数据库事物隔离级别
一:网上参考资料
1.http://www.cnblogs.com/wajika/p/6680200.html (一般情况)
2.http://www.cnblogs.com/zhaoyl/p/4121010.html (mysql)
3.http://www.cnblogs.com/digdeep/p/4947694.html (mysql)
4.https://dev.mysql.com/doc/refman/5.6/en/innodb-transaction-isolation-levels.html (官网)
二:事物隔离级别
4种隔离级别的相应原理总结如下:
READ_UNCOMMITED 的原理:
- 事务对当前被读取的数据不加锁;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。
表现:
- 事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,能读到事务2对该记录的修改版本,即使该修改尚未被提交。
- 事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
READ_COMMITED 的原理:
- 事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
表现:
- 事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的只能是事务2对其更新前的版本,要不就是事务2提交后的版本。
- 事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
REPEATABLE READ 的原理:
- 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
表现:
- 事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的仍然是第一次读取的那个版本。
- 事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
SERIALIZABLE 的原理:
- 事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
- 事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
表现:
- 事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。
- 事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。
三:疑问
(1)两条并发请求,都是先查询--更新--再查询,总是第一条先执行更新语句的请求在第二次查询中查到数据库真实结果(非快照读),第二条执行更新语句的第二次查询结果是第一次查询快照读的结果。
@Transactional(rollbackFor={Exception.class}) public int saveHelpFluxForTest(HelpSponsor helpSponsor, Bw30GzUserInfo gzUserInfo, HelpActivity activityInfo) throws Exception { int result = 0; if(helpSponsor.getId() == 1){ activityInfo = helpActivityMapper.findById(helpSponsor.getActivityId()); logger.info("first:1 PrizeSendNum=" + activityInfo.getPrizeSendNum()); int affectSendCount = helpActivityMapper.updatePirzeSendNum1(activityInfo); logger.info("first:affectSendCount=" + affectSendCount); HelpActivity activityInfoNew1 = helpActivityMapper.findById(helpSponsor.getActivityId()); logger.info("first:2 PrizeSendNum=" + activityInfoNew1.getPrizeSendNum()); }else{ activityInfo = helpActivityMapper.findById(helpSponsor.getActivityId()); int affectSendCount = helpActivityMapper.updatePirzeSendNum1(activityInfo); logger.info("two:affectSendCount=" + affectSendCount); HelpActivity activityInfoNew2 = helpActivityMapper.findById(helpSponsor.getActivityId()); logger.info("two:PrizeSendNum=" + activityInfoNew2.getPrizeSendNum()); } return result; }
mysql官网有这么一条描述:A consistent read means that InnoDB
uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction. This exception causes the following anomaly: If you update some rows in a table, a SELECT
sees the latest version of the updated rows, but it might also see older versions of any rows. If other sessions simultaneously update the same table, the anomaly means that you might see the table in a state that never existed in the database.