并发事务更新问题

简介: 并发事务更新问题

本文的所有内容基于 mysql InnoDB 和 sequelize。



问题



多个并发的事务对同一行数据进行更新,且更新的数据是基于这一行数据更新前的数据计算的结果,造成了此行数据更新的问题。



事务与锁简述



mysql 本身并不具有事务,事务是 InnoDB 引擎所有的功能,事务的隔离级别分为四种:


1、READ_UNCOMMITTED:脏读,一个事务能读到另一个事务未提交的数据,事务的隔离级别最低。


2、READ_COMMITTED:不可重复读,一个事务对一行数据进行更新的过程中,另一个事务对同一行数据进行读取,会在此行数据更新提交前后读取到不一致的结果。避免了脏读的情况,隔离级别比脏读略高一级。


3、REPEATABLE_READ:幻读,同一个事务内读取的数据是保证相同的,但当事务非独立执行时仍然会造成读取的结果不一致。默认的事务隔离级别,比不可重复读高一级。


4、SERIALIZABLE:序列化,事务的隔离级别最高,避免了上述的问题。


两种锁:


1、共享锁:读锁,获取共享锁的事务只能读,不能修改数据,多个事务可同时获取共享锁。


2、排他锁:写锁,一个事务获取写锁后可对数据进行读写,但其他事务无法再获取到写锁直到上一个事务完成。



sequelize 示例



解决方式:使用 SERIALIZABLE 事务隔离级别,但这并不够,我们仍然需要保证多个事务并发下读取的原始数据一定是之前事务提交更新之后的数据,因此还需要使用排他锁。


以下图片使用了 async/await 的写法,包含了事务的操作和 lock 锁的使用,仅供参考,sequelize 模型的定义可参考上一篇文章 -- 数据库时间类型数据的处理 ,不必深究具体的业务实现:



需要注意的是,使用排他锁时,如果查询操作不是根据主键或索引,那么会造成表锁,这会对数据库读写性能造成很大的影响,显然这并不是我想要的,我们更需要的是行锁,所以在使用排他锁时,应该使用主键或索引进行操作。



结语



除了在数据库层面上解决这个问题之外,还有另一种方法就是将这些操作同一行数据的并发事务改为串行执行。


另一个问题是 pm2 的集群模式下的并发事务会发生什么呢?

目录
相关文章
|
1月前
|
缓存 数据库
并发修改同一记录时需要加锁
并发修改同一记录时需要加锁
|
29天前
|
数据库连接 数据库
多线程事务失效的原因
【5月更文挑战第16天】多线程事务失效的原因
82 0
|
8月前
|
数据库
【并发事务会产生哪些问题】
【并发事务会产生哪些问题】
|
10月前
|
XML Java 数据格式
五、事务操作2
五、事务操作2
49 0
|
1月前
|
SQL 关系型数据库 MySQL
MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)
MySQL事务原理分析(ACID特性、隔离级别、锁、MVCC、并发读异常、并发死锁以及如何避免死锁)
118 1
|
10月前
|
SQL 关系型数据库 MySQL
两个事务并发写,能保证数据唯一吗?
两个事务并发写,能保证数据唯一吗?
114 0
|
10月前
|
XML Java API
五、事务操作1
五、事务操作1
73 0
五、事务操作1
|
11月前
|
数据库
并发事务带来哪些问题?
并发事务带来哪些问题?
94 0
|
缓存 关系型数据库 MySQL
MySQL如何加锁避免并发事务导致的脏写?
多个事务同时并发更新一行数据时, 就有脏写问题。脏写绝对不允许,可依靠锁机制让多个事务更新一行数据的时候串行化,避免同时更新一行数据。
118 0
|
SQL Java 数据库
【事务与并发】- 不同事务读取相同数据问题
在加了事务的接口中,不同的业务或者是出现并发的时候,发现了一些SQL读取问题,两个都被事务包裹的方法,各自是隔离的,如果一方的事务延时提交,就会导致另一方读取出来的数据相同,并不是修改后的数据。