数据库-02-锁与事务

142 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

锁与事务

上一节介绍了数据库的索引,本节将围绕数据库的锁与事务。包括不同粒度的锁,比如全局锁,表级锁和行级锁。事务包括四种不同的事务等级。

锁是协调多个进程或线程访问同一资源的方式。在数据库中自带有不同粒度的锁。

全局锁

一般在需要备份全局数据使用全局锁。

加锁:flush tables with read lock

释放锁:unlock tables

表锁

1.表锁:最常用的,也就是自己手动加锁的方式,一般用于整表迁移的场景

加读锁:lock table xxx read
当前和其它Session都可以读这张表
当前Session插入或更新会报错,其它Session则会等待

加写锁:lock table xxx write
当前Session可以对表进行增删改查,其它Session则会被阻塞

2.元数据锁(MDL):这种锁是防止在CRUD时有线程对表结构进行修改。因此在CRUD时加的是MDL读锁,更改表结构时加MDL写锁。

3.意向锁:如果没有意向锁的话,我们要去加表排他锁就需要去遍历所有的行有没有排他锁,效率比较低。在给行加共享锁时加上表意向共享锁,给行加排他锁时加上表意向排他锁。意向锁之间不会冲突,也不会和行级的锁冲突,只会和表级的共享锁和排他锁冲突。

4.自增锁:当把某字段设置成自增就是靠自增锁实现的,自增锁不是在事务完成后释放,而是在插入语句后立即释放。由于这会阻塞其它事务的插入,因此可以采取更轻量级的方式,在值赋值完成后立即释放锁。

行锁

每次锁住一行数据。开销大,加锁慢,粒度最小因此并发度也是最高的。加行级锁需要满足几个条件,select语句的where字段需要是索引并且在语句后加上for update,并且行级锁只能在事务中使用。

Record Lock(记录锁):仅将该记录上锁。

Gap Lock(间隙锁):锁定一个范围,不包含记录本身,前开后开区间。

Next-Key Lock:对记录加锁时,加锁的基本单位,前开后闭区间。

在加临键锁时,不同的情况加的锁不同。

主键索引:如果是等值查询,命中纪录会加Record Lock,否则退化为Gap Lock。如果是范围查询,不同的版本不同8.0.17前加的是前开后闭区间的Next-Key Lock,往后的版本则是Gap Lock。

非主键唯一索引:如果是等值查询,for update命中纪录会在主键索引上加锁,for share在覆盖索引时仅在自己的索引上加锁。如果是范围查询,不是覆盖索引下,会对相应的范围加前开后闭区间锁,如果存在数据会在对应主键行加行锁。如果是覆盖索引,会对所有后闭区间内的所有主键行加行锁。

普通索引:由于不唯一,因此会向后查询知道查询到不为该值的记录,锁定该值区间,如果命中了数据会对对应的主键行加行锁。

事务

事务是由一组SQL语句组成的逻辑处理单元。事务通常含有四大属性,也就是常说的ACID

ACID

原子性(Atomicity):事务是一个原子操作,要么全部成功,要么全部失败。

一致性(Consistent):在事务的开始和结束,数据的状态要保持一致。也就是说所有相关的数据规则都应用于事务的修改,保证数据的完整性。

隔离性(Isolation):一个事务在提交修改前,对于其它事务是不可见的。

持续性(Durable):事务提交后,修改的数据将永久留在数据库中。

并发事务问题

同时并发事务处理会带来一系列的问题

脏读:一个事务读取到另一个事务尚未提交的数据。

不可重复读:一个事务前后读取的数据不一致。

幻读:事务A读取到了事务B的新增数据。

隔离级别

不同的隔离级别产生的并发事务问题不同

隔离级别脏读不可重复读幻读
读未提交(Read Uncommitted)可能可能可能
读已提交(Read Committed)不可能可能可能
可重复读(Repeatable Read)不可能不可能可能
串行化(Serializable)不可能不可能不可能

MySQL的默认隔离机制是RR可重复度读

查看当前数据库的隔离级别:show variables like 'tx_isolation';
设置事务隔离级别:set tx_isolation = 'REPEATABLE-READ';
set session transaction isolation level read committed;

总结

本节介绍了数据库不同粒度的锁,以及四种数据库的隔离级别,不同的隔离级别可能产生的问题也不一样,下一节将介绍数据库隔离级别的实现方式,以及三种不同的数据库日志undo,redo和binlog。

感谢观看!