MySQL插入是并发还是串行?
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
最近和同事争辩起来,MySQL插入是并发还是串行,我记得明明是串行插入,同事非要和我杠,说MySQL可以并发插入。 我要亲自试验一下,打他的脸! 定义表结构MySQL 实验版本 8.0,首先定义 用户信息表userInfo,其中id为自增,name具有唯一索引。 验证流程默认情况下,在命令行中 MySQL会自动提交,每个SQL执行会非常快,无法验证同时执行的两个事务之间是否存在阻塞情况,所以需要显示开启事务和提交事务 。 开始验证首先,我们开启两个事务。在事务1中,首先插入一条记录,暂时不提交。然后,在事务2中开启一个新的事务,并插入一条自增记录。 如果MySQL的innodb插入是串行的,那么此时事务2的插入记录将会被阻塞。如果没有被阻塞,那就说明MySQL的innodb插入是并发执行的。 实验验证事务2 的执行记录 如上图所示,在事务1还未提交,事务2在事务1的间隙中插入一条记录,插入操作立即成功,并且事务2的自增主键ID为2。这说明在MySQL中,当一个事务正在插入记录时,并不会阻塞其他事务的插入。 在MySQL中,多个事务之间的插入操作是并发进行的,而不是串行进行的。 我感觉自己的脸热热的,小丑竟是我自己,赶紧给同事认了错…… 我的认知一直是错误的。 但是在底层存储层面,MySQL会对数据页加锁。如果两条记录在同一个数据页,实际写入是串行的,但是事务层面是并发的。 想象一下,库存扣减和新增库存流水在同一个事务中,如果新增库存流水是串行的,那将极大的降低库存事务的并发度啊。 本以为验证结束,打卡下班,结果发现 MySQL插入似乎存在幻读问题! 从下图中可以观察到,事务1在插入时似乎确实出现了幻读问题! 事务 1 的执行记录显示,事务1先于事务2开启,但是事务1期间可以查询到事务2提交的记录。这说明有幻读问题! 为什么出现幻读?所谓幻读,是指在一个事务读取记录时,另一个事务在此时插入或删除了一条记录,导致第一个事务再次读取时发现记录的数量发生了变化。 要想理解出现幻读的原因,需要先了解MySQL是如何解决幻读问题的。 为了解决幻读问题,MySQL采用了间隙锁和多版本并发控制(MVCC)的方法。间隙锁会锁定一段记录的范围,其他事务无法对这些记录进行更新或删除操作。这样,当当前事务再次进行查询时,就不会出现记录数量的新增或减少的情况了。 MySQL 插入时加了什么锁?MySQL 插入时存在幻读问题,说明MySQL 并没有加间隙锁,主要考虑也是为了提高插入时并发度,如果添加间隙锁,势必导致插入并发度降低!MySQL 在插入之前会申请 插入意向锁,而记录本身不冲突(无唯一键冲突)插入意向锁就不会冲突。 MySQL 文档中记录了 插入意向锁
插入场景MVCC 不生效?除更新场景外,查询场景也有幻读的困恼。如果第一次查询时只有3条记录,再次查询则变为4条,实在过于奇幻。 如果给普通的查询语句添加间隙锁,势必极大的降低MySQL 的并发度,如果不能使用间隙锁,还有哪些办法解决幻读呢? MySQL 通过引入MVCC解决查询场景的幻读问题。MVCC是多版本并发控制(Multiversion Concurrency Control)的缩写,在MVCC中,每个事务可以看到数据库的一个稳定的快照,而不会被其他并发事务的修改所干扰。当一个事务修改数据库时,它会创建一个新的数据版本,而不是直接在原始数据上进行修改。而其他事务仍然可以读取原始数据的旧版本或者已经提交的新版本,这样就避免了读取到未提交的数据或者被其他事务的写操作所阻塞。
转机出现了当我在苦苦思考,为什么MVCC 没有生效时,我随手重新测试发现,如果在 insert语句之前,使用select 查询一下,就不会出现幻读问题。 操作顺序如下 我在事务1,开启事务以后,新增了select 语句查询,而后第六步,就不会再有幻读问题…… 这真的实在太奇幻了。一波三折…… 由此可见 MySQL 插入并没有幻读问题,只是我的打开方式不对。我应该先 select一下 ……,终究还是我错了,但是我想问为什么?我为什么错了? ReadView 是关键!除MVCC 外,MySQL InnoDB 引擎设计了 ReadView(可读视图) 的概念。 ReadView 判断记录的可见性,ReadView 实际上是当前系统中所有活跃事务的列表,主要包含以下组成部分:
总结一下就是: 如果当前事务id的生成时间发生在 记录的更新之后,那么当前事务就可以看见这个记录,否则看不见!避免幻读问题 那 ReadView 又是何时生成的呢?在 REPEATABLE READ 隔离级别下,每个事务执行第一个 SELECT 语句时,会将当前系统中的所有的活跃事务拷贝到一个列表生成 ReadView,后续所有的 SELECT 都是复用这个 ReadView。 REPEATABLE READ 隔离级别下,只有第一次 SELECT 才会生成 ReadView,后续 SELECT 都会复用这个 ReadView,也就不存在新提交事务对这个 ReadView 的影响了。 所以 当我在 事务 1 新增select语句,会生成一个ReadView,这个ReadView 生成时间要早于 事务2的时间,所以事务1 的后续所有查询都不会看到事务2的记录,从而避免幻读问题发生。 总结
阅读原文:原文链接 该文章在 2025/5/6 11:43:46 编辑过 |
关键字查询
相关文章
正在查询... |