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

常见的方法是, 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)

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

转载于:https://blog.51cto.com/coveringindex/1954051

由SELECT ... FROM ... FOR UPDATE想到的相关推荐

  1. mysql之DML(SELECT DELETE INSERT UPDATE)

    DML:数据操作语言     INSERT     DELETE     SELECT     UPDATE SELECT:     SELECT SELECT-LIST FROM TBNAME|TB ...

  2. SQLite Tutorial 3 : Working with important SqLite Queries (SELECT, INSERT, DELETE, UPDATE,WHERE...)

    1.SELECT a.选择所有数据 b.选择某列 c.选择多列 d.选择某行某列 其他WHERE语句 WHERE age between 32 and 38 WHERE gender='Male' e ...

  3. select ...... from ... for update是否会锁表?

    今天看到了一篇文件,讲到select xx form xx for update是会索表还是会锁行的问题?给我的第一个感觉就是这个还要验证吗.肯定是锁行啊,怎么可能会索表,经过验证之后就被打脸了,要看 ...

  4. sql-server基础三(select 、update、insert,delete)

    一.创建student.course.SC.表格 ,注意:sql-sever是不区分大小写的, 创建student学生信息表 create table student(Sno char(9) prim ...

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

    LR中select next row和update value on的设置 LR的参数的取值,和select next row和update value on的设置都有密不可分的关系.下表给出了sel ...

  6. sql server中同时执行select和update语句死锁问题

    原始出处 http://oecpby.blog.51cto.com/2203338/457054 最近在项目中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁.通常我们知道如 ...

  7. MySQL如何实现select into 临时表的功能

    最近在编写sql语句时,遇到两次将数据放temp表,然后将两次的temp表进行inner join,再供后续insert数据时使用的场景.写完后发现执行耗时较长,需要优化,于是将一条长长的sql语句拆 ...

  8. ABAP数据库操作系列之操作语句讲解Select

    1.select详解: Select single:这个语句从数据库中选取一条数据,如果根据查询的条件(where)可以得到多条数据的话,必须有(endselect),在这种情况下,在abap的语法检 ...

  9. Oracle 共享锁和排它锁、 DML和DDL锁、 for update 锁表的问题

    共享锁和排它锁 oracle有两种模式的锁:排他锁(exclusive lock,即X锁)和共享锁(share lock,即S锁). 共享锁:如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享 ...

最新文章

  1. 寒羽对帝国的理解(通向高手之路)(转)
  2. java将图片放进mysql中_在java代码中怎么从服务器上把图片拿来放到数据库里
  3. [网络安全提高篇] 一〇二.Metasploit技术之基础用法万字详解及防御机理
  4. 人工智能会让工作环境变得更公平,还是更压抑?
  5. CentOS7安装Docker,运行Nginx镜像、Centos镜像
  6. 机器学习中的基本概念
  7. python函数定义及调用-python函数的定义和调用 | 酷python
  8. vue富文本编辑器 Vue-Quill-Editor
  9. python画拓扑图权值是线条粗细_python—networkx:根据图的权重画图
  10. 绝对值编码器:从调研到开发
  11. c语言usb串口通信程序,C语言在RS232串行接口通信中的实现
  12. 数据分析: 线性回归分析之研究二手房价的影响因素,建立房价预测模型
  13. Bitmap、BitSet、RoaringBitmap持久化存储
  14. no matching cipher found
  15. android webview 劫持,微信webview 及第三方浏览器劫持视频问题
  16. DSP28335软件实验研究--DA_AD模块功能详解
  17. java tt-7s-d_JAVA TT-7S-D 超轻7速折叠自行车质量_参数_评价
  18. 决策树与随机森林初探
  19. 外卖店优先级(模拟)
  20. 环形队列数组展示(韩顺平)

热门文章

  1. .htaccess文件玩转Rewrite
  2. java如何用异或符号实现两个变量值的交换
  3. golang刷Leetcode系列 --- 实现strStr()
  4. EIGRP区域注入静态路由的三种方法--CCNP学习笔记
  5. Machine Learning笔记(三) 多变量线性回归
  6. 用unity3d切割图片
  7. C++ array vector 数组
  8. linux--私钥登陆
  9. Failed to connect to bitbucket.org port 443: Operation timed out
  10. Q96:PT(1.2.2):球面2D方格纹理(Sphere 2D Checker)