值班问题:insert语句插入了两条数据?
`c1` int(10) unsigned NOT NULL AUTO_INCREMENT,
`c2` int(11) DEFAULT NULL,
`c3` int(11) DEFAULT '0',
`c4` int(11) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
毫无疑问,先排查一下用户实例的binlog,发现确实一前一后有两条插入语句。
那会不会是MySQL的bug呢?即使是bug,也得要先复现出来吧。
如何复现呢?
session 1:
begin work;
这时先不commit
session 2:
begin work;
insert into aa(c2,c4) select 1002, 5 from dual where not exists(select * from aa where c2 = 1002 and c3 = 0);
commit
session1:
commit
ok,有点眉目了。在这种情况下,是可以稳定复现的。用户反映他的业务是自动提交的。如果两个insert来自不同的服务器,第一次执行的时间很长还未提交,第二个就开始执行了。也是可能出现的。后来从SQL审计日志从验证了的确如此。
不过客户去复现时,却依然复现不出来。这时想到可能是隔离级别不一样。
果然,我是在Read committed的场景下复现,客户是在Repeatable read的场景下复现。
那么,为什么两个隔离级别会有不同的效果呢?
根本原因是RC隔离级别保证对读取到的记录加锁 (记录锁);
RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (GAP锁),不存在幻读现象。
具体是如何加锁,可以直接看本文最后一个部分。
解决办法:
1. 给(c2, c3)加唯一索引进行约束。(客户的应用场景不支持,因为业务只有c2=? and c3=0的状态只能出现一条,对于c3等于其他状态值,可以允许多条记录)
2. 将这个插入语句设置为 session级别的Repeatable read。这种方式对应用的改动最小。
如果设置为全局的Repeatable read隔离级别有什么问题?
1. 锁等待的范围扩大(增加了GAP锁),可能更大概率的出现死锁。
2. 在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
一些基本知识点:
两段锁
数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)
- 加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁(共享锁,其它事务可以继续加共享锁,但不能加排它锁),在进行写操作之前要申请并获得X锁(排它锁,其它事务不能再获得任何锁)。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
- 解锁阶段:当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。
事务 | 加锁/解锁处理 |
---|---|
begin; | |
insert into test ..... | 加insert对应的锁 |
update test set... | 加update对应的锁 |
delete from test .... | 加delete对应的锁 |
commit; | 事务提交时,同时释放insert、update、delete对应的锁 |
这种方式虽然无法避免死锁,但是两段锁协议可以保证事务的并发调度是串行化(串行化很重要,尤其是在数据恢复和备份的时候)的。
事务的隔离级别
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
- 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
- 提交读(Read Committed):只能读取到已经提交的数据。针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁),存在幻读现象。
- 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。
- 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。
Read Uncommitted这种级别,数据库一般都不会用,而且任何操作都不会加锁,这里就不讨论了。
快照读VS当前读
在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:
- 快照读:简单的select操作,属于快照读,不加锁。
- select * from table where ?;
- 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
- select * from table where ? lock in share mode;
- select * from table where ? for update;
- insert into table values (…);
- update table set ? where ?;
- delete from table where ?;
所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。
不同隔离级别,以及对于不同的索引情况会如何加锁?
delete from t1 where id = 10;
- 组合一:id列是主键,RC隔离级别: 只需要将主键上id = 10的记录加上X锁即可
- 组合二:id列是二级唯一索引,RC隔离级别: 需要加两个X锁,一个对应于id unique索引上的id = 10的记录,另一把锁对应于聚簇索引上的[name=’d’,id=10]的记录。
- 组合三:id列是二级非唯一索引,RC隔离级别:对应的所有满足SQL查询条件的记录,都会被加锁。同时,这些记录在主键索引上的记录,也会被加锁。
- 组合四:id列上没有索引,RC隔离级别:SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,会在判断后放锁,最终持有的,是满足条件的记录上的锁,但是不满足条件的记录上的加锁/放锁动作不会省略。
- 聚簇索引上所有的记录,都被加上了X锁。无论记录是否满足条件,全部被加上X锁。这个锁的效果和表锁有什么区别?rc隔离级别下,有区别,记录仍旧可以插入。rr下,功能上无区别。但是innodb不会主动升级表锁。
- 为什么不是只在满足条件的记录上加锁呢?这是由于MySQL的实现决定的。如果一个条件无法通过索引快速过滤,那么存储引擎层面(innodb)就会将所有记录加锁后返回,然后由MySQL Server层进行过滤。因此也就把所有的记录,都锁上了。
- 在5.6后支持了Index Condition Pushdown, 可以在innodb层进行过滤。
- 组合五:id列是主键,RR隔离级别:加锁与组合一[id主键,Read Committed]一致。
- 组合六:id列是二级唯一索引,RR隔离级别: 与组合二[id唯一索引,Read Committed]一致。
- 组合七:id列是二级非唯一索引,RR隔离级别:首先,通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键聚簇索引上的记录X锁,然后返回;然后读取下一条,重复进行。直至进行到第一条不满足条件的记录[11,f],此时,不需要加记录X锁,但是仍旧需要加GAP锁,最后返回结束。
- 组合八:id列上没有索引,RR隔离级别:在Repeatable Read隔离级别下,如果进行全表扫描的当前读,那么会锁上表中的所有记录,同时会锁上聚簇索引内的所有GAP,杜绝所有的并发 更新/删除/插入 操作。当然,也可以通过触发semi-consistent read,来缓解加锁开销与并发影响,但是semi-consistent read本身也会带来其他问题,不建议使用。
- 组合九:Serializable隔离级别: 在MySQL/InnoDB中,所谓的读不加锁,并不适用于所有的情况,而是隔离级别相关的。Serializable隔离级别,读不加锁就不再成立,所有的读操作,都是当前读。
转载于:https://www.cnblogs.com/yuyue2014/p/4747018.html
值班问题:insert语句插入了两条数据?相关推荐
- php执行一条insert插入两条数据其中一条乱码
显然这就是编码问题,但是问题从哪来的呢, 我把文件编码以及代码的编码都设置成utf-8了,为什么还有这个问题于是我就开始写测试脚本 第一条 mysql_query('insert into table ...
- mybatis 添加语句返回对象_mybatis的insert语句插入数据时的返回值的实现
mybatis的insert语句插入数据时的返回值的实现,语句,返回值,那条,都是,站长站 mybatis的insert语句插入数据时的返回值的实现 易采站长站,站长之家为您整理了mybatis的in ...
- 疯狂试探mysql单表insert极限:已实现每秒插入8.5w条数据
很多同学都有这样的困扰: 工作中项目的数据量不大,遇不到sql优化的场景:单表就几万,我优化个der啊: 业务对性能要求不高,远远没达到性能瓶颈:咱这项目又不是不能跑,优化个der啊: 确实,如果你的 ...
- mysql单表狂 insert极限:已实现每秒插入2.5w条数据
很多同学都有这样的困扰: 工作中项目的数据量不大,遇不到sql优化的场景:单表就几万,我优化个der啊: 业务对性能要求不高,远远没达到性能瓶颈:咱这项目又不是不能跑,优化个der啊: 确实,如果你的 ...
- java insert语句_mybatis的insert语句插入数据时的返回值的实现
mybatis的sql语句一般是配置在配置文件中,现先给出一个例子, sqlMap.xml文件中的一条插入语句: insert into A(a, b, c, d) VALUE (#a#, #b#, ...
- mysql insert报错_mysql数据库使用insert语句插入中文数据报错
在mysql的命令行模式中,通过insert语句插入中文数据的时候报错,类似于下面这样: Incorrect string value: '\xE7\x8F' for column 'name' at ...
- 绝对干货,教你4分钟插入1000万条数据到mysql数据库表,快快进来
我用到的数据库为,mysql数据库5.7版本的 1.首先自己准备好数据库表 其实我在插入1000万条数据的时候遇到了一些问题,现在先来解决他们,一开始我插入100万条数据时候报错,控制台的信息如下: ...
- mysql如何快速插入一千万条数据_如何快速安全的插入千万条数据?
最近有个需求解析一个订单文件,并且说明文件可达到千万条数据,每条数据大概在20个字段左右,每个字段使用逗号分隔,需要尽量在半小时内入库. 思路 1.估算文件大小 因为告诉文件有千万条,同时每条记录大概 ...
- 插入1000万条数据到mysql数据库表
转自:https://www.cnblogs.com/fanwencong/p/5765136.html 我用到的数据库为,mysql数据库5.7版本的 1.首先自己准备好数据库表 其实我在插入100 ...
最新文章
- mxnet speech_recognition踩坑记
- esxi ntp服务器地址_NTP的工作原理以及工作模式
- 当产品上线前出了 Bug | 每日趣闻
- python嵌套循环效率_Python嵌套循环数组比较优化的可能性?
- unix:///tmp/supervisor.sock no such file
- 7安装sql cent os server_Cent OS 7 编译安装 My SQL 5.7
- python实现RSA加密解密 及 签名验签功能
- 设置servlet或action作为欢迎页面
- 2019-5-5学习心得
- 安徽省计算机一级选择题题库,计算机等级考试一级选择题题库
- 软件测试方法和技术知识点简摘
- 电子线路(线性部分)——第一章 晶体二极管
- 【DOORS】产品功能介绍
- 下面程序的功能是调用fun函数以删除字符串中指定的字符
- 数据结构,关于链表的问题,为何直接free()不会造成断链。引用的好处
- 干货|FOF资产配置方案全解析
- evak购物车--团队博客
- FireFox必备插件(二)
- 为什么我的同花顺选股服务器列表为空,同花顺选股公式,为什么我就选不出股票来呢数......
- 《统计学》胡宝珠期末复习笔记
热门文章
- influxdb 最近小时统计_用pandas快速统计学生年龄班级等分组信息
- java jint,JNI:将unsigned int转换为jint
- 【mysql】时间戳
- bat for循环_bat教程[279] reg import命令的用法
- select * 映射错误_高性能IO模型分析-浅析Select、Poll、Epoll机制(三)
- 手机电脑的芯片主要是由_苹果的自研电脑芯片终于来了!你看好么?
- django filter查询多选_Django:使用filter的pk进行多值查询操作
- 彩云小译怎么翻译网页_谷歌、百度、有道做不到的,统统交给这5款翻译工具!...
- spark2.4.5源码编译成hadoop-2.6.0-cdh5.15.1
- 一个傻瓜式构建可视化 web的 Python 神器