SQL中的Isolation
实验SQL语句案例:
-- SELECT @@session.tx_isolation; 查看当前的isolation
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET autocommit=0;
START transaction;
-- 修改不同的值,配合 select * from test_acid where id=1; 对读写情况进行观察
update test_acid set score=100 where id=1;
COMMIT;
-- 其他事务的测试,最后执行SET autocommit=1; 恢复缺省设置
上面是我画的一个便于理解四个isolation级别的图,没有研究过数据库具体的实现,所以只是一个理解示意图。
Isolation | select 读 | update 写(一般处理) | update 写(存在SERIALIZABLE事务设置的行锁,即读过或者写过) |
---|---|---|---|
READ UNCOMMITTED | 获得提交数据(如存在)。但这个风险,可能会rollback,但这是最新的数据。 | 如果存在提交数据,要排队等待,有阻塞。 | 必须等该SERIALIZABLE事务结束,然后按一般处理。 |
READ COMMITTED | 获取磁盘数据,除非是自己的提交。 | 如果存在提交数据,要排队等待,有阻塞。 | 必须等该SERIALIZABLE事务结束,然后按一般处理。 |
REPEATABLE READ | 第一次读,则获取磁盘数据;后续的读,则返回第一次读的数据,除非被自己提交。 无论在事务的过程中,数据是否发生变化,保持本事务内视图一致。 | 如果存在提交数据,要排队等待,有阻塞。 | 必须等该SERIALIZABLE事务结束,然后按一般处理。 |
SERIALIZABLE | 如果存在提交数据,等待其写入磁盘,有阻塞。 | 只要在事务期间,数据存在修改,无论是在提交数据还是磁盘,均报错。 | 报错 |
我建议能够要前面给的测试SQL来进行检验,这样可以加强理解。最要直接用 mysql client 连接数据库,而不是使用什么 workbench 之类的工具。每个mysql client就是一个session,所设置的isolation在本session内生效。
基于Hibernate的Spring Data
使用JAVA代码,其实和SQL没有什么区别,最终都是要将SQL语句传递给mysql server来执行,但是通过上一学习,我们需要注意,传递的sql并不一定按照代码的时序:
- Spring Data的写SQL是在最后commit前发出,这最大程度地缩短了写操作和commit之间的时间。
- 对相同的ID的读,JPA只从数据库中读取一次。
- 从数据库中获取entity,修改entity的数据,即使最后没有执行save,在commit之前,发送update。
isolation | read | 并发 非SERIALIZABLE 的写 | 并发 SERIALIZABLE 事务(无论读写) |
---|---|---|---|
Isolation.READ_UNCOMMITTED | 读取提交内容(如果有) | 等待提交内容commit,阻塞 | |
Isolation.READ_COMMITTED | 读取磁盘存储内容 | 等待提交内容commit,阻塞 | |
Isolation.REPEATABLE_READ | JPA中只读一次,很难重新现象。 | 等待提交内容commit,阻塞 | |
Isolation.SERIALIZABLE | 如果有其他事务的写锁,等待其他事务完成,阻塞 | 对于Spring Data,update操作先会触发读,读会先等等其他完成。一旦serializable读,其他的写就必须等其commit完成。这个测试环境不存在。 如果采用JPA,直接进行写操作,或报告异常。和SQL语句测试一样,只要在事务期间,数据存在修改,无论是在提交数据还是磁盘,均报错。 |
对于SERIALIZABLE,只要其对数据进行操作,即使是只读,其他的事物都必须等待其完成。也就是对于数据享有唯一处理的权限。