由SELECT ... FROM ... FOR UPDATE想到的

应用程序开发中有个较常见的场景, 查询某字段的值(该字段一般具有唯一性), 是否存在, 若不存在, 则插入一条记录, 反之, 就更新该记录.

常见的方法是, SELECT... FROM ... FOR UPDATE查询下, 根据SELECT返回情况, 进行相应的操作. 实践中发现, 并发量较大时, 可能会有较多死锁的情况发生,下面利用tb1表演示该问题.

tb1的表结构为:

mysql>SHOW CREATE TABLE tb1 \G

***************************1. row ***************************

Table: tb1

CreateTable: CREATE TABLE `tb1` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`category` varchar(20) NOT NULL DEFAULT ‘‘,

`quantity` bigint(20) NOT NULL DEFAULT ‘0‘,

PRIMARY KEY (`id`),

UNIQUE KEY `uk_category` (`category`)

)ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4

1 rowin set (0.00 sec)

Session1:

mysql>BEGIN;

QueryOK, 0 rows affected (0.00 sec)

mysql>SELECT * FROM tb1 WHERE category = ‘ko_007‘ FOR UPDATE;

Emptyset (0.00 sec)

Session2:

mysql>BEGIN;

QueryOK, 0 rows affected (0.00 sec)

mysql>SELECT * FROM tb1 WHERE category = ‘ko_007‘ FOR UPDATE;

Emptyset (0.00 sec)

Session1插入category为‘ko_007‘的记录, 出现了锁等待.

mysql>INSERT INTO tb1 (category) VALUES (‘ko_007‘);

Session2也插入category为‘ko_007‘的记录, 死锁的情况发生了.

mysql>INSERT INTO tb1 (category) VALUES (‘ko_007‘);

ERROR1213 (40001): Deadlock found when trying to get lock; try restartingtransaction

针对死锁的产生, 从数据库本身来看, 是否还有其它方法完成上面的需求呢 …

MySQL提供了INSERT ... ON DUPLICATE KEY UPDATE语法, 其运行方式是: 若主键(或唯一键)不存在, 就插入一条记录; 若主键存在, 则更新该记录.

对于上述需求, 单个SQL语句即可搞定: 有则更新, 无则插入.

INSERTINTO tb1 (category) VALUES (‘ko_007‘) ON DUPLICATE KEY UPDATE quantity =quantity + 1;

近日, 项目的一个分布式日志产生量统计程序, 从Kafka订阅消息处理后, 将结果写入数据库, 就使用了INSERT ... ON DUPLICATE KEY UPDATE. 其虽不像SELECT ... FROM ... FOR UPDATE容易发生死锁, 却发现了另外一个问题: 与数据表主键相关的AUTO_INCREMENT值非常大, 主键值呈跳跃式增长, 这样持续下去, 主键值要不够用了啊!

最初想到的可能是, innodb_autoinc_lock_mode设置为2引起的, 确认后, 其值是1. 哎, 原因还未找到.

测试下INSERT... ON DUPLICATE KEY UPDATE, 过程如下面所示:

tb1表中存在category 为‘ko_007‘的记录:

mysql>SELECT * FROM tb1 WHERE category = ‘ko_007‘;

+----+----------+----------+

| id| category | quantity |

+----+----------+----------+

| 10| ko_007   |     1 |

+----+----------+----------+

1 rowin set (0.00 sec)

此时查看表结构, AUTO_INCREMENT为12:

mysql>SHOW CREATE TABLE tb1 \G

***************************1. row ***************************

Table: tb1

CreateTable: CREATE TABLE `tb1` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`category` varchar(20) NOT NULL DEFAULT ‘‘,

`quantity` bigint(20) NOT NULL DEFAULT ‘0‘,

PRIMARY KEY (`id`),

UNIQUE KEY `uk_category` (`category`)

)ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4

1 rowin set (0.00 sec)

执行下面的SQL:

mysql>INSERT INTO tb1 (category) VALUES (‘ko_007‘) ON DUPLICATE KEY UPDATE quantity =quantity + 1;

QueryOK, 2 rows affected (0.00 sec)

查看category 为‘ko_007‘的记录, 和表结构, 可看到quantity 字段更新为2, AUTO_INCREMENT增加了1, 变为13, 如下面所示:

mysql>SELECT * FROM tb1 WHERE category = ‘ko_007‘;

+----+----------+----------+

| id| category | quantity |

+----+----------+----------+

| 10| ko_007   |    2 |

+----+----------+----------+

1 rowin set (0.00 sec)

mysql>SHOW CREATE TABLE tb1 \G

***************************1. row ***************************

Table: tb1

CreateTable: CREATE TABLE `tb1` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`category` varchar(20) NOT NULL DEFAULT ‘‘,

`quantity` bigint(20) NOT NULL DEFAULT ‘0‘,

PRIMARY KEY (`id`),

UNIQUE KEY `uk_category` (`category`)

)ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4

1 rowin set (0.00 sec)

其实查看MySQL官方文档对于INSERT ... ON DUPLICATE KEY UPDATE的说明时,细心的话可发现文档早已说明了该语法UPDATE唯一记录时, AUTO_INCREMENT会增加的.

大致的原因是, MySQL在执行INSERT ... ON DUPLICATE KEY UPDATE时, 首先要AUTO_INCREMENT+1获取可用的自增值, 继续后面的逻辑时, MySQL发现这是个UPDATE操作, 继而更新相应的记录.

既然整数型的自增主键值有用完的风险, 那使用VARCHAR数据类型的字段作为主键, 就避开该问题呀, 确实是, 调整后的表结构如下:

mysql>SHOW CREATE TABLE tb2 \G

***************************1. row ***************************

Table: tb2

CreateTable: CREATE TABLE `tb2` (

`category` varchar(20) NOT NULL DEFAULT ‘‘,

`quantity` bigint(20) NOT NULL DEFAULT ‘0‘,

PRIMARY KEY (`category`)

)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

1 rowin set (0.00 sec)

但这也可能带来某些副作用,如数据表存储空间变大, 主从复制效率降低等等…

时间: 2024-11-03 21:25:50

由SELECT ... FROM ... FOR UPDATE想到的的相关文章

【单条记录锁】select single for update

DATA: wa_t001 type t001. select single for update * into wa_t001 from t001 where bukrs = '1000'. 解释: select single for update,where条件必须涵盖所有主键. 执行时,该语句会锁定满足条件的单条数据(在SM12中是看不到的). 锁定退出条件:执行到commit或rollback. 补充: 如果有其他的select single for update访问到同样的数据,会一直

select 时进行update的操作,在高并发下引起死锁

场景:当用户查看帖子详情时,把帖子的阅读量:ReadCount+1 select title,content,readcount from post where id='xxxx'   --根据主键查询帖子 update post set readcount=readcount+1 where id='xxxx' ------------------------------为什么会出现死锁呢?--------------------------------- 在网上找到一种说法: 就是说,某个q

mysql之DML(SELECT DELETE INSERT UPDATE)

DML:数据操作语言    INSERT    DELETE    SELECT    UPDATE SELECT:    SELECT SELECT-LIST FROM TBNAME|TBNAMES|SELECT ACTION QUALIFICATION SELECT-LIST 搜索清单            DISTINCT 去重,相同的值只显示一次.            AS ALIAS  字段别名            * 所有内容            FIELD 字段名      

MyBatis XML 映射器 select、insert update 和 delete、参数

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在. 如果跟JDBC 代码进行对比,省掉了将近 95% 的代码. 1 selectCREATE TABLE person (id int(11) NOT NULL AUTO_INCREMENT,username varchar(100) DEFAULT NULL,password varchar(100) DEFAULT NULL,full_name varchar(100) DEFAULT NULL,first_name varchar

mysql update ...select的使用 & update 遇到 disable safe 的解决方法

use `testdb`; update dtable d INNER JOIN new_table n ON d.details = n.details set d.email = n.email, d.cellphone =n.cellphone,d.contact = n.contact,d.address = n.address update 遇到 disable safe 使用 以下语句解决 SET SQL_SAFE_UPDATES=0; 原文地址:https://www.cnblog

select from update row的实现

DTCC大会上,阿里江疑的演讲中提到一个:select from update hot row; 不明白如何在Oracle中实现的,他的意思是在一条SQL中实现update和select这条update的字段信息. 经dbsnake指点,了解到这是模仿了Oracle的returning into子句,可以将使用的DML语句影响的行记录的指定列的值select出来. 官方文档中有示例: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/tu

sql server中高并发情况下 同时执行select和update语句死锁问题 (一)

 最近在项目上线使用过程中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁.通常我们知道如果两个事务同时对一个表进行插入或修改数据,会发生在请求对表的X锁时,已经被对方持有了.由于得不到锁,后面的Commit无法执行,这样双方开始死锁.但是select语句和update语句同时执行,怎么会发生死锁呢?看完下面的分析,你会明白的- 首先看到代码中使用的查询的方法Select <span style="font-size:18px;"> /// &

LR中select next row和update value on的设置

LR的参数的取值,和select next row和update value on的设置都有密不可分的关系.下表给出了select next row和update value on不同的设置,对于LR的参数取值的结果将不同,给出了详细的描述. Select next row Update Value on 实际运行结果 sequential each iteration 在某次循环中所有用户取值相同.所有用户第一次循环取第一行值,第二次循环取第二行值 each occurrence 在某次循环中

转-LR中select next row和update value on的设置

LR的参数的取值,和select next row和update value on的设置都有密不可分的关系.下表给出了select next row和update value on不同的设置,对于LR的参数取值的结果将不同,给出了详细的描述. Select next row Update Value on 实际运行结果 sequential each iteration 在某次循环中所有用户取值相同.所有用户第一次循环取第一行值,第二次循环取第二行值 each occurrence 在某次循环中