外观
为什么要加锁
1.保证数据一致性
防止脏读
- 脏读概念:指一个事务读取到了另一个未提交事务修改的数据。例如,事务 A 修改了某条记录,但还未提交,此时事务 B 读取了这条被修改但未提交的数据。如果事务 A 随后回滚,那么事务 B 读取到的数据就是无效的,这就产生了脏读问题。
- 加锁解决方式:通过加锁可以避免脏读。例如,当事务 A 对某条记录进行修改时,对该记录加写锁,其他事务在事务 A 提交或回滚之前无法读取该记录,从而防止了脏读的发生。
避免不可重复读
- 不可重复读概念:一个事务在执行过程中,多次读取同一数据,但由于其他事务对该数据进行了修改并提交,导致该事务每次读取到的数据不一致。比如,事务 A 第一次读取某条记录的值为 X,在事务 A 还未结束时,事务 B 修改了该记录的值为 Y 并提交,此时事务 A 再次读取该记录时,得到的值就变成了 Y,这就是不可重复读。
- 加锁解决方式:可以通过加锁来保证可重复读。在可重复读隔离级别下,事务 A 在读取某条记录时对其加读锁,在事务 A 结束之前,其他事务无法对该记录加写锁进行修改,从而保证事务 A 多次读取同一数据时结果一致。
防止幻读
- 幻读概念:一个事务在执行过程中,按照某个条件查询到了一些记录,在该事务还未结束时,另一个事务插入或删除了符合该查询条件的记录,导致该事务再次按照相同条件查询时,结果集发生了变化,就好像出现了 “幻影” 一样。例如,事务 A 第一次查询年龄大于 20 岁的员工数量为 10 个,在事务 A 未结束时,事务 B 插入了一个年龄大于 20 岁的员工记录,此时事务 A 再次查询年龄大于 20 岁的员工数量就变成了 11 个,这就是幻读。
- 加锁解决方式:可以通过加范围锁等方式来解决幻读问题。例如,事务 A 在查询年龄大于 20 岁的员工时,对这个年龄范围加锁,其他事务在事务 A 结束之前无法插入或删除符合该年龄范围的记录。
2.保证数据完整性
维护数据约束
- 数据库中存在各种数据约束,如主键约束、唯一约束、外键约束等。加锁可以确保在并发操作时这些约束不被破坏。例如,在插入一条新记录时,对相关的唯一索引加锁,防止其他事务同时插入具有相同唯一值的记录,从而保证唯一约束的完整性。
保证操作原子性 一些数据库操作需要保证原子性,即要么全部执行成功,要么全部不执行。加锁可以确保在一个事务执行过程中,其他事务不会干扰该事务的操作,从而保证操作的原子性。例如,在进行转账操作时,需要同时更新转出账户和转入账户的余额,通过加锁可以保证这两个操作作为一个原子操作执行,不会出现部分操作成功而部分操作失败的情况。
3.支持并发控制
协调并发事务 在多用户并发访问数据库的环境中,多个事务可能同时对同一数据进行操作。加锁可以协调这些并发事务,避免它们之间的冲突。例如,通过共享锁和排他锁的机制,多个事务可以同时对同一数据进行读操作(共享锁),但当一个事务需要对数据进行写操作时,需要获取排他锁,此时其他事务无法对该数据进行读或写操作,直到排他锁被释放。
提高并发性能 合理的加锁策略可以在保证数据一致性的前提下,提高数据库的并发性能。例如,采用行级锁而不是表级锁,可以减少锁的粒度,使得不同事务可以同时对不同的行进行操作,从而提高并发度。