MySQL 如何在一个语句中更新一个数值后返回该值 -- 自增长种子竞态问题处理

什么是竞态问题?

  假设有一个计数器,首先当前值自增长,然后获取到自增长之后的当前值。自增长后的值有可能被有些操作用来当做唯一性标识,因此并发的操作不能允许取得相同的值。

  为什么不能使用使用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,可以有两种方法实现。

实现1:通过Session变量。

-- 计数器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();
时间: 2024-10-03 23:00:46

MySQL 如何在一个语句中更新一个数值后返回该值 -- 自增长种子竞态问题处理的相关文章

MySql的like语句中的通配符:百分号、下划线和escape

%代表任意多个字符 Sql代码 http://blog.csdn.net/yc7369/ select * from user where username like '%huxiao'; select * from user where username like 'huxiao%'; select * from user where username like '%huxiao%'; _代表一个字符 Sql代码 select * from user where username like '

Mysql 数据查询语句中between and 是包含边界值的

MySQL的sql语句中可以使用between来限定一个数据的范围,例如: select * from user where userId between 5 and 7; 查询userId为5.6,7的user,userId范围是包含边界值的,也等同如下查询: select * from user where userId >= 5 and userId <= 7; 很多地方都提到between是给定的范围是大于等第一值,小于第二个值,其实这是不对的.此前我一直也是这么认为,通过实验,结论是

模拟实现在一个字符串中查找一个字符串

在标准库中有一个函数strstr()用于在一个字符串中查找一个规定的字符串,这个函数可以模拟实现一下,代码如下: #include <stdio.h> #include <assert.h> char *my_strstr(const char str[],const char strstr[]) {  int i = 0,j = 0,k = 0;  assert(str != NULL);  assert(strstr != NULL);  for(i = 0;str[i] !=

【C语言】模拟实现strchr函数.即在一个字符串中查找一个字符第一次出现的位置并返回

//模拟实现strchr函数.即在一个字符串中查找一个字符第一次出现的位置并返回 #include <stdio.h> //#include <string.h> #include <assert.h> char* my_strchr(char *dst, char src) { assert(dst); while (*dst != '\0') { if (*dst == src) return dst; dst++; } return 0; } int main()

【c语言】模拟实现strchr函数,功能:在一个字符串中查找一个字符第一次出现的位置,如果没有出现返回NULL

// 模拟实现strchr函数,功能:在一个字符串中查找一个字符第一次出现的位置,如果没有出现返回NULL #include <stdio.h> #include <assert.h> char const* my_strchr(char const *p,char c) { assert(p != NULL); while (*p) { if (*p == c) return p; else p++; } return NULL; } int main() { char *p =

解释一下,在你往浏览器中输入一个URL后都发生了什么,要尽可能详细(转)

原文链接:解释一下,在你往浏览器中输入一个URL后都发生了什么,要尽可能详细 题目 一步一步解释一下,在你往浏览器中输入一个URL后都发生了什么,要尽可能详细. 解答 这道题目没有所谓的完全的正确答案,这个题目可以让你在任意的一个点深入下去, 只要你对这个点是熟悉的.以下是一个大概流程: 浏览器向DNS服务器查找输入URL对应的IP地址. DNS服务器返回网站的IP地址. 浏览器根据IP地址与目标web服务器在80端口上建立TCP连接 浏览器获取请求页面的html代码. 浏览器在显示窗口内渲染H

powerdesinger中建立一个表后,出现Existence of index的警告

Table Existence of index A table should contain at least one column, one index, one key, and one reference.可以不检查 Existence of index 这项,也就没有这个警告错误了.这是说没有给表建立索引,而一个表一般至少要有一个索引,这是一个警告,不用的话对执行没有影响.不用管它即可. powerdesinger中建立一个表后,出现Existence of index的警告,码迷,m

在有顺序的数列中插入一个元素后该数列仍然是有顺序的数组

/** 在有顺序的数组中插入一个元素后该数列仍然是有顺序的数组: 思路:先找到该元素的插入位置 插入数据时要先将数组中得元素后移,然后插入该元素 */ #include <stdio.h> #define  n 10 int main() { // 在有顺序的数列中插入一个元素后该数列仍然是有顺序的数列: int a[n] = {-1, 3, 6, 9, 13, 22, 27, 32, 49}; int insertVal; int insertLoc; scanf("%d"

Mysql Create Table 语句中Date类型

Mysql创建语句中的数据类型包括时间类型,有一下几类: | DATE  | TIME[(fsp)]  | TIMESTAMP[(fsp)]  | DATETIME[(fsp)]  | YEAR 这几个类型中,特别值得注意的是DATE,DATETIME,TIMESTAMP有什么区别? DATE mysql> select get_format(date,'ISO');     +------------------------+ | get_format(date,'ISO') | +-----