事务&锁
事务
事务,由一组 sql 语句组成的执行单元,要么都执行成功,要么回滚。事务符合 ACID 原则
ACID 原则
-
Atomicity-原子性:执行单元,要么执行成功,要么回滚
-
Consistency-一致性:事务开始和结束之后,数据库的完整性约束没有破坏
-
Isolation-隔离性:多个事务并发执行时,彼此之间并不会相互影响
-
Durability-持久性:事务对数据库所做的更改会持久的保存在数据库中
事务的隔离性
数据库中事务的隔离性分为了 4 个层级
- read uncommitted:未提交读,会引发 脏读,不可重复读,幻读
- read committed:已提交读,会引发 不可重复读,幻读
- repeatable read:可重复读,会引发 幻读
- serializable :序列化
事务的隔离性越高,并发性越低,对业务系统的影响性也就越大。
锁
悲观锁
定义:指对数据被外界修改持悲观态度,具有独占性和排他性。
分类:
- 共享锁(读锁,S 锁):允许其他进程读,但不允许修改
- 排他锁(写锁,X 锁):锁定期间,不允许其他进程读写
实现:依赖于数据库的锁机制
实例:
start transaction;
select p.productCount from product p where p.productId = 1 for update;
update product p set p.productCount=p.productCount-1 where p.productId=1 ;
commit;
乐观锁
定义:指外界对数据修改持乐观态度,认为其他进程不会影响到自己要修改的数据。
实现:使用程序便可实现
- 使用数据版本实现:为数据增加一个 version,每修改一次数据便使 version 变大,只增不减。
- 使用时间戳实现:天然递增性
实例:
start transaction;
// version = 1
select p.productCount, p.version from product p where p.productId = 1;
update product p set p.productCount=p.productCount-1, p.version=p.version+1 where p.productId = 1 and version = 1;
commit
// 不在事务中也可实现
// version = 1
select p.productCount, p.version from product p where p.productId = 1;
update product p set p.productCount=p.productCount-1, p.version=p.version+1 where p.productId = 1 and version = 1;
悲观锁 VS 乐观锁
悲观锁:依赖于数据库锁,效率低。更新失败的概率低
乐观锁:并未真正加锁,效率高。但一旦锁的粒度把握不好,更新失败的概率较大,容易发生业务失败。
在现在 3 高的要求下(高并发,高性能,高可用),悲观锁用的越来越少。
细化锁粒度
不要长时间持有不必要的锁
// 控制了只有 p.count 为 1 时才采用锁机制
update product p set p.count=p.count-1 where p.id=1 and p.count-1>0