读未提交-为什么事务没提交就可以读到别人修改的数据
疑惑
之前在思考数据库隔离级别的时候,读到"脏读"问题,也就是第一次读到的数据跟第二次读到的不一样,这种情况一般发生在数据库隔离级别Read uncommitted,这种情况下大家可能会思考为什么他事务没提交我就能读到数据了呢?这个问题理解的核心在于,数据库事务的提交和数据的修改提交根本不是一回事。
分析
首先解释一下数据库事务提交的本质,也就是commit操作:
- commit操作的作用不是把缓存或者内存中的数据更新到数据库
- 本质就是代表一次事务结束
- commit之前,数据修改已经更新到数据库缓存池中了
再来解释,修改的数据是怎么被读到的,也就是commit之前修改的数据存在哪里:
以MySQL为例
"数据未提交,那数据库应该没有改变",这个假设其实是错误的。数据库的底层设计都使用了WAL(Write-ahead**Logging)思想,只不过数据库的事务隔离技术了提供了各种方案,一个数据库请求即使事务没有提交也会发生数据库内部数据的变更,有各种技术,像Mysql InnoDB MVCC,只不过是通过一些字段进行控制。
在数据库没有提交数据的时候,你更新的数据是在缓存进行更新的,事务与事务在并发进行的时候就叫作隔离级别,只有在提交之后,数据才从日志中把数据更新到数据库里面。
目前主流的关系型数据库例如mysql、Oracle都是基于文件系统进行数据存储的,即数据是持久化到文件系统的,但基于文件系统的随机IO读写是非常慢的,故数据库都会引入内存池,完成对磁盘数据的缓存,提高读写性能。内存池是对所有线程共享的,也就是对所有的数据库事务是共享的,所谓的未提交,指的是事务未提交,但此时数据已经存储到了共享内存中,数据已经进入到了数据库服务器中,所以是可见的。
这个问题要根据不同数据库来具体分析,以MySQL为例:
MySQL的InnoDB,实现事务是根据MVCC多版本并发控制(乐观锁实现)
- InnoDB里每个事务都有自己唯一的事务ID,新来的事务会自增
- 同时每一行数据都有两个隐藏列,一个记录事务id,一个记回滚指针
- 当一行数据同时被多个事务修改,这行数据就会有n个不同事务id的""复制"
- 所以在读未提交这种隔离级别下,你可以读到别人没有提交的修改数据
解决办法
读的时候加S锁
写的时候加X锁
至于为什么,自己思考一下