mysql-锁
mysql-锁
jwang锁
数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
1.读写锁
1.1 共享锁:对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后。
语法为:select * from table lock in share mode
1.2 排他锁:对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。
语法为:select * from table for update --增删改自动加了排他锁
1.3 数据库规定同一资源上不能同时共存共享锁和排他锁
命令窗1:select * from table lock in share mode
命令窗2:update table set column1='hello'
在1未完成前 2无法运行:T2 之所以要等,是因为 T2 在执行 update 前,试图对 table 表加一个排他锁,而数据
库规定同一资源上不能同时共存共享锁和排他锁。所以 T2 必须等 T1 执行完,释放了全部的共享锁,才能加上排他
锁,然后才能开始执行 update 语句。
2.锁的大小(颗粒度)
2.1 表级锁定(table-level)
表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
2.2 行级锁定(row-level)
行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。使用行级锁定的主要是InnoDB存储引擎。
2.3 页级锁定(page-level)
页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。使用页级锁定的主要是BerkeleyDB存储引擎。
3.myisam的锁
由于MyISAM存储引擎使用的锁定机制完全是由MySQL提供的表级锁定实现,所以我们以MyISAM存储引擎作为示例存储引擎。
3.1 MySQL表级锁的锁模式
MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
锁模式的兼容性:
1.对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
2.对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;
3.MyISAM表的读操作与写操作之间,以及写操作之间是串行的。当一个线程获得对一个表的写锁后,只有持有锁的线
程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。
3.2 MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。
4.innodb的锁
行级锁定不是MySQL自己实现的锁定方式,而是由其他存储引擎自己所实现的,如广为大家所知的InnoDB存储引擎,以及MySQL的分布式存储引擎NDBCluster等都是实现了行级锁定。
4.1 InnoDB锁定模式及实现机制
InnoDB的锁定机制和Oracle数据库有不少相似之处。InnoDB的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。
| 共享锁s | 排他锁x | 意向共享锁is | 意向拍他锁 | |
|---|---|---|---|---|
| S | 兼容 | 冲突 | 兼容 | 冲突 |
| x | 冲突 | 冲突 | 冲突 | 冲突 |
| IS | 兼容 | 冲突 | 兼容 | 兼容 |
| IX | 冲突 | 冲突 | 兼容 | 兼容 |
如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要
等待锁释放。
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排
他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
4.2 InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁
1.在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。
2.由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的
索引键,是会出现锁冲突的。
3.当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普
通索引,InnoDB都会使用行锁来对数据加锁。
4.即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果
MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行
锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引
5.乐观锁 vs 悲观锁(重点)
5.1 乐观锁
总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新
时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到
的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
5.2 悲观锁
总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁. 所以说我们上面所说的读锁,写锁都是属于悲观锁
5.3 优缺点
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
6.gap和record lock
6.1 Gap lock 间隙锁
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身
mysql> select * from emp where empid > 100 for update;
是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并
不存在)的“间隙”加锁。
InnoDB使用间隙锁的目的: 防止幻读,以满足相关隔离级别的要求。对于上面的例子,要是不使用间隙锁,如果其他
事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;
6.2 Record lock
单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。
6.3 Next-Key
就是Record lock和gap lock的结合,即除了锁住记录本身,还要再锁住索引之间的间隙。
7.死锁
概述
所谓死锁DeadLock: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等带的进程称为死锁进程.表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB.
死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。那么对应的解决死锁问题的关键就是:让不同的
session加锁有次序
加锁方法
select .. from
不加任何类型的锁
select...from lock in share mode
共享锁
select...from lock for update
排它锁
update..where
delete from..where
排他锁


