前言

接上文,继续学习后续章节。细心的同学已经发现,我整理的并不一定是作者讲的内容,更多是结合自己的理解,加以阐述,所以建议结合原文一起理解。

第20章《幻读是什么,幻读有什么问题?》

先看下幻读的定义:

在一个事务中,两次执行同一个查询SQL,后一次执行结果比前一次执行结果数量变多了,称之为幻读。

在隔离级别中的定义,我们知道RR级别是无法避免幻读的?但是在innoDB中是如果做到避免幻读问题呢?其实innoDB在RR级别下解决幻读问题也并不完美。

现有一张表t,有三个字段 id主键,普通索引C,不带索引d, 目前有数据id=1,d=5,c=5;id=4,d=5,c=5;

事务A 事务B 事务C
begin;
select * from t where d =5 for update;Q1
update t set d =5 where id =4;
select * from t where d =5 for update;Q2
insert into t values (5,5,5)
select * from t where d =5 for update;Q3
commit;

首先我们知道innoDB在RR级别下,select语句是快照读,不存在幻读问题,快照读的实现方式我们之前阐述过,通过MCVV多版本并发控制解决的,本质就是通过undo log实现的,如果还需要更详细的研究,可以参考《一文讲透MVCC原理》

Q2读到事务B的更新预警,属于当前读看到的,不属于幻读.幻读指的是新插入的行。

首先我们看下,幻读不解决可能会出现哪些问题?

1、首先如果只加行锁,执行Q1时,我们希望的是所有d=5的数据都锁定,但d=5的是id=1的数据,只会对id=1加上行锁.当事务B执行时,是对id=4进行更新操作,所以可以操作.同样的事务C也可以执行,那么就违背了,Q1的语义.

2、数据一致性问题:如果事务A在Q1时候又执行了一个按d=5条件更新语句,那么在所有事务都提交后,落入到binlog中的顺序是,事务B更新一个语句把id=4的d改成了5,事务C插入了一个id=5,d=5的数据,事务A执行了一个把d=5的数据更新。此时如果按binlog的执行顺序,则会把事务B事务C的逻辑全部改掉,造成了数据不一致的问题。

所以innoDB为了解决此类幻读问题,就引入了间隙锁(Gap lock);

顾名思义就是在行与行之间也加上锁.

比如上面例子中,有id=1和id=4两个数据,那么我不仅在行上加锁,我再(- ∞ ,1](1,4],(4, + supremum】上也加上锁,那么当你再插入数据的时候,就无缝可入了就会被阻塞住。

但需要注意的是,在行锁中,读写锁,写写锁互相冲突,当一个事务加间隙锁,另外一个事务也可以对同样的范围加间隙锁。

注意:

当我们执行更新加间隙锁时,如果where条件不是索引,那么就会对所有行加行锁和行之间数值范围加间隙锁。

如果执行的where条件是等值并且是非唯一索引列,比如id=5,那么会对id=5加行锁,以及(id=5上一个id,id=5]和(id=5,id=5的下一个id值]加间隙锁。如果是唯一索引,那么就只加行锁。

第21章《为什么只改一行代码,锁这么多》

这章节,老师总结的加锁规则,非常受用:两个原则,两个优化,一个BUG

两个原则:

1、加锁的基本单位是以next-key lock。就是左开右闭。

2、查找过程中访问的到对象才会加锁。

两个优化:

1、在索引上等值查询条件,如果是唯一索引,会优化成行锁。

2、在索引上等值查询条件,会向右遍历找到第一个不符合条件的为止,并会优化成间隙锁。

一个bug:

在唯一索引上的范围查询,会访问到不满足条件为止。

带着这些规则,下面看八个案例,以表T为背景,来实践下。

CREATE TABLE`t`(
`id`int(11)NOT NULL,
`c`int(11)DEFAULT NULL,
`d`int(11)DEFAULT NULL,
PRIMARY KEY(`id`),
KEY`c`(`c`)
) ENGINE=InnoDB; insert intot values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

案例一:等值查询的情况

update t set d=d+1 where id =7;

根据主键id进行更新时,由于id=7不存在。根据规则1加锁单位next-key lock :(5,10] 根据优化2: 退化成间隙锁(5,10);

此时分别执行两个SQL:

insert into t values(8,8,8) --由于间隙锁,会block
update t set d=d+1 where id =10; --不在间隙锁范围内,执行成功

经过实验,证实如此。

案例二:非唯一索引等值查询

begin;
select id from t where c=5 lock in share mode;

根据优化2 可知会在索引C间隙锁 (5,10);

但需要注意的是:根据规则2 只有访问到的对象才会加锁,由于使用了覆盖索引,所以不会再主键id上再加锁。

update t set d=d+1 where id =5; --执行不受阻
insert into t values(7,7,7); --blocked

案例三:主键索引范围查询

select * from t where id =10 for update;
select * from t where id>=10 and id <11;

首先从结果上看,这两个SQL执行结果是一样的,但是按照上面总结的加锁规则,加锁过程是不一样的。

SQL1 唯一索引等值查询,则加的是id=10的行锁。

SQL2 首先会查询到id=10这行,加锁单位next-key lock,(5,10],根据优化1,退化成行锁,只加id=10这一行,

再查询id>10,向右找到第一个不满足条件的行id=15,加next-key lock (10,15]

案例四:非唯一索引范围锁

select * from t where c>= 10 and c<11 for update;

首先找到c=10这一行,加next-key (5,10],再根据优化2,向右查询到第一个不满足条件的值,退还成间隙锁(10,15]

案例五:唯一索引范围锁bug

select * from t where id>10 and id<=15 for update;

正常id是唯一索引,找到id=15这时 ,应该就停止扫描了,但是innoDB会依然扫描到第一个不满足条件的id=20为止。

所以就会加(10,15],(15,20],导致id=20这样也会锁上,这是没有必要的。这个bug已被官方证实,但还未修复。

案例六:未唯一索引存在多个相同值的情况

--先插入一条c=10的值,这样表中存在2个c=10的行
insert into t values(30,10,30);

当我们在c=10加锁时,加锁的范围是next-key lock (5,10] next-key lock (10,10]和间隙锁 (10,15)

案例七:limit 语句加锁

delete from t where c =10 limit2;

与案例7的加锁范围不同的时,不会再加间隙锁(10,15);

因为当扫描到c=10,id=30这一行时,已经满足limit2的条件,不会再往后扫描,也就不会再加间隙锁(10,15),从而缩小了锁的范围。

所以在删除数据的时候,我们尽量加limit ,即可以控制删除数量,又可以缩小锁的范围。

案例八:一个说死锁的例子

A执行第一句SQL时,按加锁规则,会加next-key lock (5,10]和间隙锁(10,15)。

B执行时,也会加next-key lock(5,10],会被阻塞.

A再次执行插入数据(8,8,8)时,也会被阻塞,从而导致死锁。

这是为什么了?B 不是没有加锁成功吗?但是我们要注意的时,加next-key lock 本质就是加间隙锁再加行锁。由于间隙锁加之间不是互斥的,所以B加(5,10)的间隙锁时成功了,加c=10的行锁失败。

所以当A再次插入数据时,已被间隙锁阻塞。

第23章《MySQL怎么保证数据不丢失的?》

binlog写入机制

事务执行时,先把日志写到binlog cache,事务提交后再把binlog cache 写入binlog文件。

binlog_cache_size控制了每个线程的binlog cache的内存的大小。如果超过了这个参数就临时存到磁盘中。

但注意的是,binlog cache并不一定是直接落入磁盘的,是先写入binlog files,再刷入磁盘。

这个可以通过参数sync_binlog控制:

设置0,表示每次只写入binlog files,不刷磁盘

设置1,表示每次事务提交,也会刷磁盘

设置n,表示每次都写入binlog files,但是等累计了n个事务才刷磁盘

常见设置为"100-1000",但同时也会有丢失的风险.

redo log写入机制

事务执行时,先把日志写入 redo log buffer,事务提交时,写入文件系统page cache 或者刷入磁盘.也是通过参数来设置:innoDB_flush_log_at_trx_commit

设置0,表示每次事务提交时,依然保留在redo log buffer

设置1,表示每次事务提交直接刷入磁盘

设置2,表示每次事务提交都只写到page cache.

那是不是0,2状态是不是就永不刷到磁盘了,那不是会有丢失的风险吗?

其实不是的,innoDB有一个后台线程,每隔1秒,会把redo log buffer中的日志,写到page cache,然后再调用fsync刷到磁盘.

由于这个后台线程的存在,所以也会把还未提交事务的redo log 也持久化到磁盘.

如果你的如果你的MySQLMySQL现在出现了性能瓶颈,而且瓶颈在现在出现了性能瓶颈,而且瓶颈在IO上,可以通过哪些方法来提升性能呢?上,可以通过哪些方法来提升性能呢?

针对这个问题,可以考虑以下三种方法:

1.设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。

2.将sync_binlog设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。

3.将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。

MySQL深入研究--学习总结(5)的更多相关文章

  1. MySQL深入研究--学习总结(2)

    前言 接上文,继续学习后续章节. 第四章&第五章<深入浅出索引> 这两章节主要介绍的索引结构及其如何合理建立索引,但是我觉得讲的比较简单. 总结回顾下吧,其实在我之前的文章< ...

  2. MySQL深入研究--学习总结(1)

    前言 本文是笔者学习"林晓斌"老师的<MySQL实战45讲>过程中的,对知识点的总结归纳以及对问题的思考记录,课程18年11月就出了,当时连载形式,我就上班途中一边开车 ...

  3. MySQL深入研究--学习总结(3)

    前言 接上文,继续学习后续章节.细心的同学已经发现,我整理的并不一定是作者讲的内容,更多是结合自己的理解,加以阐述,所以建议结合原文一起理解. 第九章<普通索引和唯一索引,如何选择> 从查 ...

  4. MySQL深入研究--学习总结(4)

    前言 接上文,继续学习后续章节.细心的同学已经发现,我整理的并不一定是作者讲的内容,更多是结合自己的理解,加以阐述,所以建议结合原文一起理解. 第13章<为什么表数据删除一般,表文件大小不变?& ...

  5. 【转】手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  6. 手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  7. 13本热门书籍免费送!(Python、SpingBoot、Entity Framework、Ionic、MySQL、深度学习、小程序开发等)

    七月第一周,网易云社区联合清华大学出版社为大家送出13本数据分析以及移动开发的书籍(Python.SpingBoot.Entity Framework.Ionic.MySQL.深度学习.小程序开发等) ...

  8. MySQL 定时器EVENT学习

    原文:http://blog.csdn.net/lifuxiangcaohui/article/details/6583535 MySQL 定时器EVENT学习 MySQL从5.1开始支持event功 ...

  9. 《Mysql 公司职员学习篇》 第二章 小A的惊喜

          第二章 小A的惊喜  ---- 认识数据库 吃完饭后,小Y和小A回到了家里,并打开电脑开始学习Mysql. 小Y:"小A,你平时的Excell文件很多的情况下,怎么样存放Exce ...

随机推荐

  1. 前端接收后端文件流导出excel文档遇到的问题

    先上代码: Vue.prototype.download = function(oUrl, filename) { this.axios .get(oUrl, { responseType: 'arr ...

  2. Linux 创建/编辑/查看 文件/文件夹的命令汇总

    Linux 创建/编辑/查看 文件/文件夹的命令汇总 Linux 创建文件的命令Linux,编辑文件的命令Linux 查看文件的命令,touch,vim,vi,gedit,cat,ls -a, ls ...

  3. 使用 js 实现一个简易版的 drag & drop 库

    使用 js 实现一个简易版的 drag & drop 库 具有挑战性的前端面试题 H5 DnD js refs https://www.infoq.cn/article/0NUjpxGrqRX ...

  4. react-parent-child-lifecycle-order

    react-parent-child-lifecycle-order react parent child lifecycle order live demo https://33qrr.csb.ap ...

  5. vue & template & v-else & v-for bug

    vue & template & v-else & v-for bug nested table bug https://codepen.io/xgqfrms/pen/wvaG ...

  6. [转]【视觉 SLAM-2】 视觉SLAM- ORB 源码详解 2

    转载地址:https://blog.csdn.net/kyjl888/article/details/72942209 1 ORB-SLAM2源码详解 by 吴博 2 https://github.c ...

  7. [Python学习笔记]正则表达式总结

    常用缩写字符及其含义表格查询 缩写字符分类 含义 \d 0-9的任意数字 \D 除0-9的数字以外的任何字符 \w 任何字母.数字或下划线字符(可以认为是匹配"单词"字符) \W ...

  8. Java进阶专题(二十六) 数据库原理研究与优化

    前言 在一个大数据量的系统中,这些数据的存储.处理.搜索是一个非常棘手的问题. 比如存储问题:单台服务器的存储能力及数据处理能力都是有限的, 因此需要增加服务器, 搭建集群来存储海量数据. 读写性能问 ...

  9. Go的包

    目录 go的包 一.包的创建规则 二.包的导入规则 三.包的函数调用 go的包 一.包的创建规则 一个包就是一个文件夹. 同一个包(文件夹)下,所有go文件都只能用同一个package,也就是每个文件 ...

  10. dpi dp px 换算关系

    getResources().getDisplayMetrics().densityDpi 就是屏幕密度.getResources().getDisplayMetrics().density 也可以理 ...