事务和锁
约 1346 字大约 4 分钟
2025-01-15
1.锁分类
在MySQL中,锁是一种常见的并发事务的控制方式、用于保证数据一致性
- 锁的粒度:全局锁、表级锁、行级锁、页级锁
- 锁的级别:共享锁(读锁)、排他锁(写锁/独占锁)
- 锁的性能:乐观锁、悲观锁
- 锁的区间:间隙锁、临建锁
2.表级锁/行级锁
行级锁是指对数据库表中的某一行数据进行锁定,其他事务可以访问该表的其他行。
行级锁的分类:
共享锁(S锁):允许多个事务读取数据,但不允许修改:for lock in share mode
SELECT * FROM your_table WHERE id = 1 FOR LOCK IN SHARE MODE;
排他锁(X锁):允许一个事务对数据进行修改,其他事务不允许读取或修改:for update
SELECT * FROM your_table WHERE id = 1 FOR UPDATE;
表级锁则是对整个表进行锁定。当一个事务锁定了表,其他事务无法对该表进行任何操作
表级锁的分类:
- 写锁:LOCK TABLES your_table WRITE
- 读锁:LOCK TABLES your_table READ
3.乐观锁/悲观锁
乐观锁:认为数据在被操作时很少发生冲突,因此在访问数据时不会立即加锁,而是在更新数据时检查数据是否被其他事务修改过,如果没有则更新成功,否则进行回滚或者重试
举例:在MySQL中,可以使用乐观锁的方式是在更新数据时检查数据的版本号或者时间戳是否与当前操作一致
UPDATE table_name SET column1 = value1, version = new_version WHERE id = x AND version = old_version
悲观锁:认为数据在被操作时会发生冲突,因此在访问数据之前会先加锁,确保其他事务无法修改该数据,直到当前事务完成操作并释放锁。
举例:在MySQL中,可以使用SELECT ... FOR UPDATE语句来获取悲观锁,例如
SELECT * FROM table_name WHERE condition FOR UPDATE;
4.意向锁
- 意向锁(Intention Lock)是一种用于管理表级锁的锁机制,用于在表级别上指示事务将要在表的哪些行上获取锁
- 作用:在没有意向锁之前,如果一张表里面已经有行锁了,此时我们再添加表锁,为了防止表锁和行锁发生冲突,表锁就需要遍历整个表中的数据检查是否有行锁,效率低
- 分类:意向共享锁和意向排他锁
- 意向共享锁:表明事务将要在表的某些行上获取共享锁。当一个事务打算获取某行的共享锁时,会在表级别请求意向共享锁。
- 意向排他锁:表明事务将要在表的某些行上获取排他锁。当一个事务打算获取某行的排他锁时,会在表级别请求意向排他锁。
例子1(意向锁和表级的共享/排他锁互斥)
事务A | 事务B | |
---|---|---|
1 | 事务A成功获取第6行的排他锁,但未提交 | |
2 | 此时事务A持有该表的意向排它锁和第6行的排它锁 | |
3 | 事务B申请获取该表的读锁 | |
4 | 事务B检测到该表已有意向排它锁,因为意向锁和表级锁互斥,所以被阻塞 事务B不用去检测表中的每一行数据是否存在排他锁 |
例子2(意向锁和行级的共享锁/排他锁兼容)
事务A | 事务C | |
---|---|---|
1 | 事务A成功获取第6行的排他锁,但未提交 | |
2 | 此时事务A持有该表的意向排它锁和第6行的排它锁 | |
3 | 事务C申请获取第7行的排它锁 | |
4 | 事务C检测到该表有意向排它锁,但是意向锁和行级锁(排它锁/共享锁)兼容,即成功加锁 此时该表有事务A和事务C的两个意向排他锁,并获取到了第7行的排他锁 |
- 事务的四大特性:ACID
- A,原子性:靠undo log来保证(异常或执行失败后进行回滚)
- C,一致性:事务的最终目的,即需要数据库层面保证,又需要应用层面进行保证,并且MySQL底层通过两阶段提交事务保证了事务持久化时的一致性。
- D,持久性:靠redo log来保证(保证当MySQL宕机或停电后,可以通过redo log最终将数据保存至磁盘中)。
- I,隔离性:事务间的读写靠MySQL的锁机制来保证隔离,事务间的写操作靠MVCC机制(快照读、当前读)来保证隔离性。
- 事务带来的问题:脏读(一个事务读取到另一个事务还没有提交的数据)、不可重复读(一个事务两次快照读,读取的结果不一样)、幻读(幻读是指在一个事务范围内,由于其他事务插入或删除了满足当前查询条件的数据,导致之前的查询结果不再准确,出现了“幻影”般的数据。)
- 控制并发事务:锁、事务隔离级别