表锁-读写锁-偏读-共享锁
讨论mysql的读写锁,先讨论读锁,有 ab 两个线程,有 AB两张表,讨论在 a 对 A 加了读锁后,a 对A读写,a对B读写,b 对A读写,b对B读写的情况
读锁(共享锁)的特点:
- 允许多个事务同时获得同一个资源的读锁。
- 持有读锁的事务只能读取数据,不能修改数据。
- 其他事务可以获取该资源的读锁,但不能获取写锁。
情况分析
- 线程a对表A加读锁后,可以读取表A,但不能写入表A(报错)。
- 线程a对表B的读写操作不受影响。
- 线程b可以读取表A,但不能写入表A(阻塞,直到读锁释放)。
- 线程b对表B的读写操作不受影响
a 对 A 加写锁
写锁(排他锁)的特点如下:
- 一次只允许一个事务获得写锁。
- 持有写锁的事务可以读取和修改数据。
- 其他事务既不能获取该资源的读锁,也不能获取写锁。
情况分析
- 线程a对表A加写锁后,可以自由地读取和写入表A。
- 线程a对表B的读写操作不受影响。
- 线程b不能读取或写入表A,这些操作会被阻塞,直到线程a释放写锁。
- 线程b对表B的读写操作不受影响。
读锁会阻塞写,但是不会阻塞读,而写锁则会把读和写都阻塞
看看哪些表被锁了
show open tables;
如何分析表锁定
查看 Table_locks_immediate Table_locks_waited 这两个参数
show status like ‘table%’;
行锁-偏写-排他锁
锁定粒度最低,发生锁冲突的概率最低,并发度也最高
行锁支持事务
事务及其acid特性
- 原子性
事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全部不执行
- 一致性
在事务开始和完成时,数据都必须保持一致状态,
- 隔离性
数据处理过程的中间状态对外是不可见的
- 持久性
对于数据的修改是永久性的
并发事务带来的问题
- 更新丢失
版本覆盖
- 脏读
事务A读取到事务B已修改但尚未提交的数据,如果事务B回滚,A读取的数据无效,不符合一致性要求
- 不可重复读
事务A读取到事务B修改的数据
- 幻读
读到了新增的数据
事务隔离级别
读数据一致性及允许的并发副作用隔离级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读 | 最低级别,只能保证不读取物理上损坏的数据 | 是 | 是 | 是 |
已提交读 | 语句级 | 否 | 是 | 是 |
可重复读 | 事务级 | 否 | 否 | 是 |
可序列化 | 最高级别,事务级 | 否 | 否 | 否 |
查看当前数据库的事务隔离级别
show variables like ‘transaction_isolation’;
演示行锁定
一个事务不提交修改某一行,另外一个事务修改同一行会被阻塞
无索引行锁升级为表锁
注意update的时候,varchar没有使用单引号的隐式类型转换导致的索引失效,会把行锁升级为表锁
间隙锁的危害
在操作一个范围数据的更新的时候,会导致中间数据的插入被锁住
比如操作 1 到 9,插入 2,这个插入就被锁住
如何锁定一行
select for update where id = xxx 锁住这一行,阻塞其他会话的操作