MySQL学习笔记-锁相关话题
在事务相关话题中,已经提到事务隔离性依靠锁机制实现的。在本篇中围绕着InnoDB与MyISAM锁机制的不同展开,进而描述锁的实现方式,多种锁的概念,以及死锁产生的原因。
MyISAM和MEMORY采用表级锁(table-level locking);
BDB采用页面锁(page-leve locking)或表级锁,默认为页面锁;
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁;
各种锁特点
表级锁(table-level locking):开销小,加锁快;不会出现死锁;锁定粒度大,发生冲突的概率最高,并发度最低
行级锁(row-level locking):开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高
页面锁(page-level locking):开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般
锁的算法
InnoDB存储引擎有3种行所算法设计,分别是:
- Record Lock:单个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next-key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身
Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnodB存储引擎会使用隐式的主键来进行锁定,在Repeatable Read隔离级别下,Next-key Lock 算法是默认的行记录锁定算法。
阻塞、系统互斥量(mutex)和信号量(event)
因为不同锁之间兼容性关系,一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源。在InnoDB存储引擎的源代码中,用Mutex数据结构来实现锁。在访问资源前需要用mutex_enter函数进行申请,在资源访问或修改完毕后立即执行mutex_exit函数。当一个资源已被一个事务占有时,另一个事务执行mutex_enter函数会发生等待,这就是阻塞。阻塞并不是一件坏事,阻塞是为了保证事务可以并发并且正常运行。
在InnoDB引擎当中,封装了操作系统提供的基本mutex(互斥量)和event(信号量),在WINDOWS下的实现暂时不做记录,主要还是对支持POSIX系统来做介绍。在POSIX系统的实现是os_fast_mutex_t和os_event_t。os_fast_mutex_t相对简单,其实就是pthread_mutex。定义如下:
- typedef pthread_mutex os_fast_mutex_t;
- typedef struct os_event_struct
- {
- os_fast_mutex_t os_mutex;
- ibool is_set;
- pthread_cond_t cond_var;
- }os_event_t;
在InnoDB的mutex(互斥量)的实现中,除了引用系统的os_mutex_t以外,还使用了原子操作来进行封装一个高效的mutex实现。在系统支持原子操作的情况下,会采用自己封装的mutex来做互斥,如果不支持,就使用os_mutex_t。
- /**
- * Atomically updates the current value with the results of
- * applying the given function, returning the previous value. The
- * function should be side-effect-free, since it may be re-applied
- * when attempted updates fail due to contention among threads.
- *
- * @param updateFunction a side-effect-free function
- * @return the previous value
- * @since 1.8
- */
- public final int getAndUpdate(IntUnaryOperator updateFunction) {
- int prev, next;
- do {
- prev = get();
- next = updateFunction.applyAsInt(prev);
- } while (!compareAndSet(prev, next));
- return prev;
- }
- /**
- * Atomically sets the value to the given updated value
- * if the current value {@code ==} the expected value.
- *
- * @param expect the expected value
- * @param update the new value
- * @return {@code true} if successful. False return indicates that
- * the actual value was not equal to the expected value.
- */
- public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
- }
InnoDB存储引擎中的锁
InnoDB是一个多线程并发的引擎,内部的读写都是用多线程来实现的,所以InnoDB内部实现了一个比较高级的并发同步机制。InnoDB并没有直接使用系统提供的锁(latch)同步结构,而是对其进行自己的封装和实现优化,但是也兼容系统的锁。我们先看一段InnoDB内部的注释(MySQL-3.23):
Semaphore operations in operating systems are slow: Solaris on a 1993 Sparc takes 3 microseconds (us) for a lock-unlock pair and Windows NT on a 1995 Pentium takes 20 microseconds for a lock-unlock pair. Therefore, we have toimplement our own efficient spin lock mutex. Future operating systems mayprovide efficient spin locks, but we cannot count on that.
浮生潦草闲愁广,一听啤酒一口尽
 
                    
                     
                    
                 
                    
                 
                
            
         
 
         浙公网安备 33010602011771号
浙公网安备 33010602011771号