基础理论
ACID
- 原子性(Atomic):事务内所有操作要么全部执行,要么全不执行
- 一致性(Consistency):事务前后满足一致性,即符合一致约束条件,比如两个账户转账,两账户总金额一致
- 隔离性(Isolation): 一个事务不影响其他,没提交前不会被看到
- 持久性(Durability): 已经提交的不会丢失
事务结束方式
事务已两种形式终结
- 提交(Commit)
- 回滚(Rollback)
锁
协调事务并发访问数据,加到锁后事务结束后才会放
按目标
- 行锁:粒度小,并发强,速度慢,复杂易死锁
- 页锁:介于行锁表锁之间
- 表锁:粒度大,并发弱,速度快,简单
按行为
- 共享锁(Share Lock,S锁,读锁):加共享锁后,其他事务依然可以读取
- 独占锁(Exclusive Lock,X锁,写锁):加独占锁后,其他事务不能读写,资源上没有读写锁时才能加上
- 更新锁(Update Lock,U锁,更新锁):资源上只能加一个,和读锁兼容,其他事务依然可读,预定修改,无需等待读锁释放就可以转成写锁
一般数据库都有读写锁,部分数据库有更新锁
死锁场景
- 请求同一资源,都是先读后写
两个事务都成功加到读锁,谁都不能继续升级为写锁 - 请求不同资源,加锁顺序不同
两个事务先分别获取锁,再请求对方
隔离级别
SQL92标准
SERIALIZABLE_READ > REPEATABLE_READ > READ_COMMITTED > READ_UNCOMMITTED
SERIALIZABLE时,读也加锁,读锁会阻塞写锁,容易造成死锁,比如两个事务都是先读后写,都加读锁,写锁加不到
SERIALIZABLE严格遵从互不干涉原则,一个事务使用数据时其他事务不能使用,效果是数据相关的所有事务排队执行
隔离级别通过控制如何加锁实现,不同隔离是放宽条件来提升性能,隔离程度越高,代价越高,数据库的并发性越差
读取问题
-
脏读(Dirty Read)
一个事务读取了另一事务没提交的数据,这部分数据有可能回滚
违反隔离性,事务隔离级别应为READ_COMMITTED -
不可重复读(Unrepeatable Read)
事务内两个相同查询返回不同数据,因为这部分数据被其他事务提交导致修改
违反隔离性,事务隔离级别应为REPEATABLE_READ
加行锁,事务拿到锁在提交前,别的事务不能对行操作 -
幻读(Phantom Read)
事务内两个相同查询返回不同数据,因为这部分数据被其他事务提交导致新增或者删除
违反隔离性,事务隔离级别应为SERIALIZABLE_READ,事务串行执行
加表锁,事务拿到锁在提交前,别的事务不能对表操作
更新问题
- 第一类丢失:事务回滚,把其他事务提交的数据抹掉
SQL92所有级别都不允许这种 - 第二类丢失:事务提交,把其他事务提交的数据覆盖
比如操作同一行,初始读到金额都是1000,两个事务同时在原基础上增加100,最终先后提交后金额变为1100
一般采用局部手动悲观加锁或者乐观锁的方式维护版本号
默认级别
Oracle支持READ_COMMITED, SERIALIZABLE,默认隔离级别READ_COMMITTED
Mysql支持四种,默认级别REPEATABLE_READ
版权声明
This site by Linest is licensed under a Creative Commons BY-NC-ND 4.0 International License.
由Linest创作并维护的博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。