由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, 过程如下面所示:
mysql>SELECT * FROM tb1 WHERE category = 'ko_007';
mysql>SHOW CREATE TABLE tb1 \G
***************************1. row ***************************
CreateTable: CREATE TABLE `tb1` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`category` varchar(20) NOT NULL DEFAULT '',
`quantity` bigint(20) NOT NULL DEFAULT '0',
UNIQUE KEY `uk_category` (`category`)
)ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4
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想到的相关推荐
- mysql之DML(SELECT DELETE INSERT UPDATE)
DML:数据操作语言 INSERT DELETE SELECT UPDATE SELECT: SELECT SELECT-LIST FROM TBNAME|TB ...
- 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 ...
- select ...... from ... for update是否会锁表?
今天看到了一篇文件,讲到select xx form xx for update是会索表还是会锁行的问题?给我的第一个感觉就是这个还要验证吗.肯定是锁行啊,怎么可能会索表,经过验证之后就被打脸了,要看 ...
- sql-server基础三(select 、update、insert,delete)
一.创建student.course.SC.表格 ,注意:sql-sever是不区分大小写的, 创建student学生信息表 create table student(Sno char(9) prim ...
- 转-LR中select next row和update value on的设置
LR中select next row和update value on的设置 LR的参数的取值,和select next row和update value on的设置都有密不可分的关系.下表给出了sel ...
- sql server中同时执行select和update语句死锁问题
原始出处 http://oecpby.blog.51cto.com/2203338/457054 最近在项目中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁.通常我们知道如 ...
- MySQL如何实现select into 临时表的功能
最近在编写sql语句时,遇到两次将数据放temp表,然后将两次的temp表进行inner join,再供后续insert数据时使用的场景.写完后发现执行耗时较长,需要优化,于是将一条长长的sql语句拆 ...
- ABAP数据库操作系列之操作语句讲解Select
1.select详解: Select single:这个语句从数据库中选取一条数据,如果根据查询的条件(where)可以得到多条数据的话,必须有(endselect),在这种情况下,在abap的语法检 ...
- Oracle 共享锁和排它锁、 DML和DDL锁、 for update 锁表的问题
共享锁和排它锁 oracle有两种模式的锁:排他锁(exclusive lock,即X锁)和共享锁(share lock,即S锁). 共享锁:如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享 ...
最新文章
- 寒羽对帝国的理解(通向高手之路)(转)
- java将图片放进mysql中_在java代码中怎么从服务器上把图片拿来放到数据库里
- [网络安全提高篇] 一〇二.Metasploit技术之基础用法万字详解及防御机理
- 人工智能会让工作环境变得更公平,还是更压抑?
- CentOS7安装Docker,运行Nginx镜像、Centos镜像
- 机器学习中的基本概念
- python函数定义及调用-python函数的定义和调用 | 酷python
- vue富文本编辑器 Vue-Quill-Editor
- python画拓扑图权值是线条粗细_python—networkx:根据图的权重画图
- 绝对值编码器:从调研到开发
- c语言usb串口通信程序,C语言在RS232串行接口通信中的实现
- 数据分析: 线性回归分析之研究二手房价的影响因素,建立房价预测模型
- Bitmap、BitSet、RoaringBitmap持久化存储
- no matching cipher found
- android webview 劫持,微信webview 及第三方浏览器劫持视频问题
- DSP28335软件实验研究--DA_AD模块功能详解
- java tt-7s-d_JAVA TT-7S-D 超轻7速折叠自行车质量_参数_评价
- 决策树与随机森林初探
- 外卖店优先级(模拟)
- 环形队列数组展示(韩顺平)
热门文章
- .htaccess文件玩转Rewrite
- java如何用异或符号实现两个变量值的交换
- golang刷Leetcode系列 --- 实现strStr()
- EIGRP区域注入静态路由的三种方法--CCNP学习笔记
- Machine Learning笔记(三) 多变量线性回归
- 用unity3d切割图片
- C++ array vector 数组
- linux--私钥登陆
- Failed to connect to bitbucket.org port 443: Operation timed out
- Q96:PT(1.2.2):球面2D方格纹理(Sphere 2D Checker)