MySQL 如何在一个语句中更新一个数值后返回该值 -- 自增长种子竞态问题处理
2017-09-07 23:33
477 查看
什么是竞态问题?
假设有一个计数器,首先当前值自增长,然后获取到自增长之后的当前值。自增长后的值有可能被有些操作用来当做唯一性标识,因此并发的操作不能允许取得相同的值。
为什么不能使用使用UPDATE语句更新计数器,然后SELECT语句获取自增长后的当前值?问题在于并发的操作有可能获取到相同的计数器值。
如何避免竞态问题?
方法一:使用Transaction和SELECT FOR UPDATE
如果一个Transaction中执行SELECT FOR UPDATE,该步操作会锁住该行记录。其它对该行记录的并发操作会被阻塞,直到当前SELECT FOR UPDATE所在Transaction提交或超时。
方法二:在一个语句中完成UPDATE和SELECT
方案一虽然可行并且可靠,但是加上了锁后一定程度上可能会影响一些性能。幸运的是我们可以使用方案二,在一个语句中完成UPDATE和SELECT,可以有两种方法实现。
[b]实现1:通过Session变量。[/b]
实现2:通过MySQL自带的LAST_INSERT_ID方法
LAST_INSERT_ID方法常用的场景是获取自增长列最后一次插入的值。它还有另外一个用法,当传入一个值时它会返回传入的值,并且在下一次调用不含参数的LAST_INSERT_ID()方法时,还是会返回先前传入的值。
假设有一个计数器,首先当前值自增长,然后获取到自增长之后的当前值。自增长后的值有可能被有些操作用来当做唯一性标识,因此并发的操作不能允许取得相同的值。
为什么不能使用使用UPDATE语句更新计数器,然后SELECT语句获取自增长后的当前值?问题在于并发的操作有可能获取到相同的计数器值。
CREATE TABLE counters ( id INT NOT NULL UNIQUE, -- 计数器ID,多个计数器可以存在一个表中, value INT -- 计数器当前值 ); -- 初始化计数器1,从10开始计数 INSERT INTO counters VALUES (1, 10); -- 计数器1自增步长1 UPDATE counters SET value = value + 1 WHERE id = 1; -- 获取计数器1自增长后的当前值 SELECT value FROM counters WHERE id = 1;
如何避免竞态问题?
方法一:使用Transaction和SELECT FOR UPDATE
如果一个Transaction中执行SELECT FOR UPDATE,该步操作会锁住该行记录。其它对该行记录的并发操作会被阻塞,直到当前SELECT FOR UPDATE所在Transaction提交或超时。
START TRANSACTION; -- 锁定计数器1 SELECT value FROM counters WHERE id = 1 FOR UPDATE; -- 计数器1自增长步长1 UPDATE counters SET value = value + 1 WHERE id = 1; -- 获取自增长后的当前值 SELECT value FROM counters WHERE id = 1; COMMIT;
方法二:在一个语句中完成UPDATE和SELECT
方案一虽然可行并且可靠,但是加上了锁后一定程度上可能会影响一些性能。幸运的是我们可以使用方案二,在一个语句中完成UPDATE和SELECT,可以有两种方法实现。
[b]实现1:通过Session变量。[/b]
-- 计数器1当前值自增长步长1 UPDATE counters SET value = (@newValue := value + 1) WHERE id = 1; -- 获取自增长之后的值 SELECT @newValue;
实现2:通过MySQL自带的LAST_INSERT_ID方法
LAST_INSERT_ID方法常用的场景是获取自增长列最后一次插入的值。它还有另外一个用法,当传入一个值时它会返回传入的值,并且在下一次调用不含参数的LAST_INSERT_ID()方法时,还是会返回先前传入的值。
-- 计数器1自增长步长1,并通过LAST_INSERT_ID(Num)方法记录插入的值 UPDATE counters SET value = LAST_INSERT_ID(value + 1) WHERE id = 1; -- 获取最后一次插入的值 (自增长之后的当前值) SELECT LAST_INSERT_ID();
相关文章推荐
- 记录:java执行mysql语句查询字段类型:timestamp返回页面显示会多出个 .0,自己如何处理的
- 如何在mysql存储过程中处理select语句返回的多行结果(用游标)
- 如何解决ACCESS中SELECT TOP语句竟然返回多条记录的问题?
- NHibernate问题 flush-mode 一个事务中重复提交更新的SQL语句
- 如何解决ADO.NET访问Access数据库出现"操作必须使用一个可更新的查询"的问题
- 执行SQL语句时出现问题操作必须使用一个可更新的查询错误的解决方法
- 如何解决ADO.NET访问Access数据库出现"操作必须使用一个可更新的查询"的问题
- 对一个DROP MV 语句长时间无返回的处理步骤。
- ASP.Net下如何解决关于Access数据库“操作必须使用一个可更新的查询”问题
- 如何解决ACCESS中SELECT TOP语句竟然返回多条记录的问题?
- 在数据库查询语句中 要返回 一个集合的数据 的处理方式。
- 如何解决ADO.NET访问Access数据库出现"操作必须使用一个可更新的查询"的问题(非原创,摘自书中)
- MySQL MYSQL_ROW 返回的字段若是 float 如何在 C++程序中 把 字段值赋给 一个 float变量
- 不用存储过程,如何在执行一个INSERT语句后,返回新记录的主键值。
- 如何解决ACCESS中SELECT TOP语句竟然返回多条记录的问题?
- 如何解决ACCESS中select TOP语句返回全部记录问题?(转)
- SQL语句更新一个表的上下行的问题(急)
- 如何处理多页面重定向到同一页面后的返回问题
- 如何在一个update语句中,用一个表的数据更新另一个表的数据
- 如何解决access 中 SELECT TOP 1语句竟然返回多条记录的问题?