作家
登录

MySQL引擎特性:InnoDB同步机制

作者: 来源: 2017-12-15 11:37:56 阅读 我要评论

4. get cell这个操作是大年夜sync array履行的,sync array详见帮助数据构造这一节,简单的说就是供给给监控线程应用的。

2. 当有一个写请求加锁成功时,lock_word原子递减X_LOCK_DECR。

5. 留意一下,os_event_reset和os_event_wait这两个函数的调用地位,别的,有一点必须清跋扈,就是os_event_set(锁持有者释放所后会调用这个函数通知所有等待者)可能在这整段代码履行到随便率性地位出现,有可能涌如今指导点4的地位,如许就构成了前提变量通知在前提变量等待之前,会造成无穷等待。为懂得决这个问题,才有了指导点3下面的代码,须要从新再次检测一下lock_word,别的,即使os_event_set产生在os_event_reset之后,有了这些代码,也能让当前哨程提前拿到锁,不消履行后续os_event_wait的代码,必定程度上进步了效力。

mutex_exit的伪代码就简零丁了,如下:

1. waiter是ib_mutex_t中的一个变量,用来表示当前是否有线程在等待这个锁。全部代码逻辑很简单,就是先把lock_word设置为0,然后如不雅发明有等待者,就把所有等待者给唤醒。facebook的mark callaghan在2014年测试过,比拟如今已经比较完美的pthread库,InnoDB自旋互斥锁只在并发量相对较低(小于256线程)和锁等待时光比较短的情况下有优势,在高并发且较长的锁等待时光情况下,退化比较严重,个一一个很重要的原因就是InnoDB自旋互斥锁在锁释放的时刻须要唤醒所有等待者。因为os_event_ret底层经由过程pthread_cond_boardcast来通知所有的等待者,一种改进是把pthread_cond_boardcast改成pthread_cond_signal,即只唤醒一个线程,但Inaam Rana Mark测试后发明,如不雅只唤醒一个线程的话,在高并发的情况下,这个线程可能不会急速被cpu调剂到。。由此看来,似乎唤醒一个特定命量的等待者是一个比较好的选择。

2. 伪代码中的┞封段注释笔者估计加上去的,大年夜意是因为编译器或者cpu的指令重排乱序履行,mutex->waiter这个变量的攫取可能在产生在原子操作之前,大年夜而导致一些无线等待的问题。然后还专门开了一个叫做sync_arr_wake_threads_if_sema_free的函数来做清理。这个函数是在后台线程srv_error_monitor_thread中做的,每隔1秒钟履行一次。在现代的cpu和编译器上,完全可以用内存樊篱的技巧来防止指令重排和乱序履行,这个函数可以被去掉落,官方的看法貌似是,不要这么激进,万一其他处所还须要这个函数呢。。详见BUG #79477。

总体来说,InnoDB自旋互斥锁的底层实现照样比较有意思的,异常合适进修研究。这套锁机制在如今完美的Pthread库和高达4GMHZ的cpu下,已经有点力不大年夜心了,mark callaghan研究发明,在高负载的压力下,应用这套锁机制的InnoDB,大年夜部分cpu时光都给了sys和usr,根本没有余暇,而pthread mutex在雷同情况下,却竽暌剐平均80%的余暇。同时,因为ib_mutex_t这个构造体体积比较宏大年夜,当buffer pool比较大年夜的时刻,会发明锁占用了很多的内存。最后,大年夜代码风格上来说,有不少代码没有解耦,如不雅须要把锁模块零丁打成一个函数库,比较艰苦。

基于上述几个缺点,MySQL 5.7及后续的版本中,对互斥锁进行了大年夜量的从新,包含以下几点(WL#6044):

1. 应用了C++中的类持续关系,体系互斥锁和InnoDB本身实现的自旋互斥锁都是一个父类的子类。

2. 因为bool pool的锁对机能请求比较高,是以应用静态持续(也就是模板)的方法来削减持续中虚指针造成的开销。

3. 保存旧的InnoDB自旋互斥锁,并实现了一种基于futex的锁。简单的说,futex锁与上述的原子操作类似,能削减用户态和内核态切换的开销,但同时保存类似mutex的应用办法,大年夜大年夜降低了法度榜样编写的难度。

InnoDB读写锁

与前提变量、互斥锁不合,InnoDB琅绫擎没有Pthread库的读写锁的包装,其完全依附依附于原子操作和InnoDB的前提变量,甚至都不须要依附InnoDB的自旋互斥锁。此外,读写锁还实现了写操作的递归锁,即同一个线程可以多次获得写锁,然则同一个线程依然不克不及同时获得读锁和写锁。InnoDB读写锁的核心数据构造rw_lock_t中,并没有等待队列的信息,是以不克不及包管先到的请求必定会先辈入临界区。这与体系互斥量用PTHREAD_MUTEX_ADAPTIVE_NP来初始化有异曲同工之妙。

InnoDB读写锁的核心实如今源文件sync0rw.cc和sync0rw.ic中,核心数据构造rw_lock_t定义在sync0rw.h中。应用办法与InnoDB自旋互斥锁很类似,只不过读要乞降写请求要调用不合的函数。加读锁调用rw_lock_s_lock, 加写锁调用rw_lock_x_lock,释放读锁调用rw_lock_s_unlock, 释放写锁调用rw_lock_x_unlock,创建读写锁调用rw_lock_create,释放读写锁调用rw_lock_free。函数rw_lock_x_lock_nowait和rw_lock_s_lock_nowait表示,当加读写锁掉败的时刻,直接返回,而不是自旋等待。

核心计心境制

rw_lock_t中,核心的成员有以下几个:lock_word, event, waiters, wait_ex_event,writer_thread, recursive。

与InnoDB自旋互斥锁不合,InnoDB读写锁还有wait_ex_event和recursive两个变量。wait_ex_event也是一个InnoDB前提变量,然则它用来等待第一个写锁(因为写请求可能会被先前的读请求堵住),当先前达到的读请求都读完了,就会经由过程这个event来唤醒这个写锁的请求。

因为InnoDB读写锁实现了写锁的递归,是以须要保存当前写锁被哪个线程占用了,后续可以经由过程这个值来断定是否是这个线程的写锁请求,如不雅是则加锁成功,不然掉败,须要等待。线程的id就保存在writer_thread这个变量中。

recursive是个bool变量,用来表示当前的读写锁是否支撑递归写模式,在某些情况下,例如须要别的一个线程来释放这个读写锁(insert buffer须要这个功能)的时刻,就不要开启递归模式了。

接着我们来说说大年夜于两个线程下可能会产生的问题。线程A和C是等待线程,等待同一个前提变量,B是通知线程,通知A和C停止等待。推敲一个乱序C:线程A履行os_event_reset(步调1),线程B立时就履行os_event_set(步调2)了,接着线程C履行了os_event_reset(步调3),最后线程A履行os_event_wait(步调4),线程C履行os_event_wait(步调5)。乍一眼看,似乎看不出啥问题,然则实际上你会发明A和C线程在无穷等待了。原因是,步调2,把is_set这个变量设置为false,然则在步调3,线程C经由过程reset又把它给从新设回false了。。然后线程A和C在os_event_wait中误认为还没有产生过前提通知,就开端无穷等待了。为懂得决这个问题,InnoDB在核心数据构造os_event中惹人64位整形变量signal_count,用来记录已经发出前提旌旗灯号的次数。每次发出一个前提通知,这个变量就递增1。os_event_reset的返回值就把当前的signal_count值掏出来。os_event_wait如不雅发明有这个参数的传入,就会断定传入的参数与当前的signal_count值是否雷同,如不雅不雷同,表示这个已经通知过了,就不会进入等待了。举个例子,假设乱序C,一开端的signal_count为100,步调1把这个参数传给了步调4,在步调4中,os_event_wait会发明传入值100与当前的值101(步调2中递增了1)不合,所以线程A认为旌旗灯号已经产生过了,就不会再等待了。。。然而。。线程C呢?步调3返回的值应当是101,传给步调5后,产生于当前值一样。。持续等待。。。细心分析可以发明,线程C是属于前提变量通知产生在等待之前(步调2,步调3,步调5),上一段已经说过了,针对这种通知提前发出的,今朝InnoDB没有异常好的解法,只能调用者本身控制。


  推荐阅读

  海尔王养浩:让IT成为业务和创新的赋能者

数字化转型已成为趋势,然而既然是转型则必定会带来阵痛,新技巧在带来新的颠覆的同时,也给IT运维治理带来新的挑衅,包含稳定性、扩大性、灵活性、成本、投资回报率等等。对于IT治理者而言,若何经由过程大年夜数据>>>详细阅读


本文标题:MySQL引擎特性:InnoDB同步机制

地址:http://www.17bianji.com/lsqh/39799.html

关键词: 探索发现

乐购科技部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与乐购科技进行文章共享合作。

网友点评
自媒体专栏

评论

热度

精彩导读
栏目ID=71的表不存在(操作类型=0)