IO即性能

顺序主键写性能很高,由于B+树的结构,主键如果是顺序的,则磁盘页的数据会按顺序填充,减少数据移动,随机主键则可能由于记录移动产生很多io

查询二级索引时,会再根据主键id获取数据页,产生一次磁盘io,但如果在高并发场景下,二级索引不大而被整个缓存到内存时,它甚至比主键查询还快

虽然二级索引表的读是离散的,但是索引一般字段不会太多,数据量小,索引表被整个cache到内存不是难事,而如果内存中有cache页,可以直接根据id找到记录(涉及到mysql cache方式了,还不明确),可能会更快,所以利用二级索引和自增主键反而不会引起很多的直接io,而如果使用业务主键直接读,很可能数据是贯穿整个表,数据表表的数据一般比较大,想要整个cache起来非常困难,反而在并发读取下会带来大量的cache挤占,真实io更大

引用:

作者:聿明leslie

链接:https://www.zhihu.com/question/266011062/answer/310929189

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

意向锁

锁分类:共享读锁、排他写锁,主要是为了对整表加写锁时,更搞笑,当对整表加写锁后,理论上可以修改表中的任意一行

共享读锁

多个读操作可以共享这个锁,可以并发访问

排他写锁

写锁都是排他的,一旦获得了某个锁,其他线程就无法获取对应行的写锁和读锁

如果事务A先申请了行的读锁,事务B想申请整个表的写锁,则A释放行锁之前,事务B都不会成获得整表的写锁

当需要锁行时,先向整个表申请意向(共享|排他)锁,再申请行的对应锁,这样有对表加写锁的请求时,会发现表上有意向锁,会先等待阻塞,防止表锁上的冲突,实现多粒度的控制

MVCC

mysql的写操作分为

  • 修改数据区
  • redo日志

如果事务A要写一个数据,需要找到对应的数据页,load到内存中,成为cache页,然后修改cache中的数据区,再记一条日志,等待日志落盘后,就返回给客户端,而非数据落盘,而此时事务可能还未提交。

当其他事务B中有请求要查询该内存页时,而A还未提交,此时显然不能直接读脏数据(cache中的),那么该如何处理呢?

此时事务A会开辟一块undo的数据区,作用是将数据回放到上一个事务的完结状态,事务B的查询操作就会访问到undo数据区,

简单流程

  • 事务1:将一行数据a=1 改成 a=2,那么它会对这行数据先加排他锁,再将这行数据上一个已提交的事务verison数据copy到undo数据区
  • 事务2:拿到一个全局的已提交的version去读这一行,发现被事务1修改的那一行version比查询的那一行大,则去undo区里面去查

不可重复读

一个事务中,同样的查询,两次结果不相同,就叫不可重复读。

主要说的如某个事务执行过程中,前一次查询和后一次查询,数据不一样,比如某一列的值被修改了,这种情况通常为两次查询过程中,另一个事务操作了这一行数据。

解决不可重复读

通过mvcc版本比较,解决不可重复读的问题,事务会访问到另一个事务开辟undo区域,保证后一次读和前一次读的结果相同;

再加上写锁的排他特性,保证同一时刻,只会有一个事务可以操作某一行数据

幻读

幻读主要指的是两此select(count)之类的查询结果不一样,出现了幽灵行数据。

事务A第一次执行查询后,事务B通过insert插入了一行数据,事务A再次执行查询时,发现多了一行数据。

解决幻读

把mysql的读分为

  • 快照读 如两个普通的select语句
  • 当前读 如select加锁读或DML修改数据

快照读(事务中的普通select)的时候,mysql通过mvcc解决幻读

间隙锁解决当前读(select * for update或update\del\insert),不只在行上加锁,而且在行与行直接的间隙加锁

next-key锁

X锁 + 间隙锁,即锁定记录行,又锁定间隙,它是innodb ,RR隔离级别下的默认锁模式

不可重复读和幻读的区别

很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

隔离级别

https://www.cnblogs.com/windliu/p/8144202.html

如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,也就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。

上面说的,是使用悲观锁机制来处理这两种问题,但是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来避免这两种问题。

“读”与“读”的区别

可能有读者会疑惑,事务的隔离级别其实都是对于读数据的定义,但到了这里,就被拆成了读和写两个模块来讲解。这主要是因为MySQL中的读,和事务隔离级别中的读,是不一样的。

我们且看,在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。

对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:

快照读:就是select

  1. select * from table ....;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。

  1. select * from table where ? lock in share mode;
  2. select * from table where ? for update;
  3. insert;
  4. update ;
  5. delete;

事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。

解决幻读

解决幻读:通过行锁 + 间隙gap锁,这里说的行锁是通过mvcc实现的乐观锁,比较cache页中数据行的version和当前事务的version,如果当前version比较小,则去undo区域,在普通select读这种快照读情况下,实际上是不会有锁的,而在dml中,涉及到数据变更,会锁住被修改的行,同时,加gap锁,防止其他事务插入数据导致的幻读

唯一索引的唯一搜索(非范围查询,如t_key = 1)不用加gap锁,因为其他事务在尝试获取index-record lock时会失败

设table_a 有非唯一索引t_key,上一个已提交最新版本的全局version为10000

  1. CREATE TABLE `table_a` (
  2. `id` bigint(11) NOT NULL AUTO_INCREMENT,
  3. `t_key` int(11) NOT NULL,
  4. PRIMARY KEY (`id`),
  5. KEY `idx_t_key` (`t_key`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8

事务1:

事务id:10001

  1. select * from table_a where t_key = '1'; //load page into cache/memory
  2. update table_a set t_key = '2' where t_key = '1'; //对t_key = 1 的数据行加锁,为了防止其他事务insert t_key(1),加间隙锁,并将t_key='1'的上一个版本数据行copy到undo区域,undo区域中t_key还是1,cache页中数据行版本变为10001,此时事务还没有提交,因此cache页是一个脏数据页

(事务2此时介入,查询sql select * from table_a where t_key = '1';)

....

end

事务2:

事务id:10002

  1. select * from table_a where t_key = '1'; //查询cache页,发现上一个最新已提交version为10000,小于10001,去undo区域获取数据行
  2. insert(t_key) table_a values(1) //事务1对table_a加了间隙锁,如索引节点 [-无穷,1]和[1,2]中间被锁住(假设有多条记录,t_key分别为1,2,3),不包括2,获取锁时都需要等待,如果table_a的t_key字段没有索引,在innodb会锁定整个表,锁住整个表的方式,通过实验,确定为锁住primary索引,包括不存在的记录

(等待事务1 end)

(没有间隙锁释放,可以插入)

间隙锁的定位方式为最近锁定行的左右区间,避免锁定没有必要的行,假如有索引数据(1,1)(3,4),(5,5)(8,5)(9,7),(主键,t_key)

update where t_key = 4时,会锁住(1)(4)之间,(4)(5)之间,而不是锁住(1) ~ (5),所以才叫间隙锁,通过分析,间隙锁应该是发生了B+树的叶子节点上,并且已经对应到行记录上了

事务1锁定了where t_key = 4 时,间隙锁t_key(1,1)(3,4)之间,(3,4)(5,5)之间,此时还需要关注主键的位置

则insert(2,3)时,它在(1,1)(3,4)之间,会阻塞

insert(4,5)时,它在(3,4)~(5,5)之间,会阻塞

insert(6,5)时,成功,因为它没在(1,1)(3,4)之间,(3,4)(5,5)之间,同样t_key都是5,一个可以成功一个不可以

如果一个事务通过update 主键id锁定了行(3,4),则另一个事务此时也无法通过t_key索引更新(3,4),因为都对应到了主键id为3的数据行

假如table_a还有一个name字段

  1. select * from table_a where t_key=5 and name =‘5 for update,将会锁住t_key的索引;
  2. select * from table_a where name ='5' for update,会锁住primary索引

事务1:

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

  1. mysql> select * from table_a where name='5';
  2. Empty set (0.00 sec)
  3. mysql> select * from table_a where name='6';
  4. +----+-------+------+
  5. | id | t_key | name |
  6. +----+-------+------+
  7. | 5 | 5 | 6 |
  8. | 8 | 5 | 6 |
  9. +----+-------+------+
  10. 2 rows in set (0.00 sec)
  11. mysql> update table_a set name='5' where t_key=5;
  12. Query OK, 2 rows affected (0.00 sec)
  13. Rows matched: 2 Changed: 2 Warnings: 0
  14. mysql> commit;
  15. Query OK, 0 rows affected (0.01 sec)

事务1 update操作执行,但没有commit时,执行事务2

事务2:

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

  1. mysql> select * from table_a where t_key =5;
  2. +----+-------+------+
  3. | id | t_key | name |
  4. +----+-------+------+
  5. | 5 | 5 | 6 |
  6. | 8 | 5 | 6 |
  7. +----+-------+------+
  8. 2 rows in set (0.00 sec)
  9. mysql> update table_a set name='8' where t_key=5 and name='6';
  10. Query OK, 0 rows affected (4.64 sec)
  11. Rows matched: 0 Changed: 0 Warnings: 0
  12. mysql> commit;
  13. Query OK, 0 rows affected (0.00 sec)
  14. 最终name的值为
  15. mysql> select * from table_a where t_key =5;
  16. +----+-------+------+
  17. | id | t_key | name |
  18. +----+-------+------+
  19. | 5 | 5 | 5 |
  20. | 8 | 5 | 5 |
  21. +----+-------+------+
  22. 2 rows in set (0.00 sec)

mysql幻读、MVCC、间隙锁、意向锁(IX\IS)的更多相关文章

  1. Mysql加锁过程详解(2)-关于mysql 幻读理解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  2. Mysql加锁过程详解(3)-关于mysql 幻读理解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  3. mysql 隔离级别与间隙锁等

    数据库隔离级 SQL标准中DB隔离级别有: read uncommitted:可以读到其它transaction 未提交数据 read committed:可以读到其它transaction 已提交数 ...

  4. 浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题

    本文出处:http://www.cnblogs.com/wy123/p/7501261.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...

  5. mysql 快照读MVCC

    mysql的读分快照读和当前读 快照读 是指写的同时,读不阻塞,达到并发的作用 这时候的读 是 记录的历史版本,存在于undo里,当然回滚时就的也是这个undo 当执行一条update语句时,记录本身 ...

  6. 《Mysql - 幻读》

    一:准备 - 为了深入了解幻读,准备数据. CREATE TABLE `t` ( `id` ) NOT NULL, `c` ) DEFAULT NULL, `d` ) DEFAULT NULL, PR ...

  7. 关于MySQL幻读的实验

    该实验基于 CentOS 7 + MySQL 5.7 进行 打开两个窗口连接到MySQL 第一个连接的事务我们命名为  T1 第二个连接的事务我们命名为 T2 T2 发生在 T1 的 O1 操作结束以 ...

  8. mysql幻读问题

    转载:https://blog.csdn.net/u013067756/article/details/90722490 关于间隙锁:https://blog.csdn.net/sinat_27143 ...

  9. mysql 幻读

    幻读(Phantom Read) 是指当用户读取某一范围的数据行时,B事务在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行.InnoDB和Falcon存储引擎通 过多版本并发 ...

随机推荐

  1. 用 Java 做个“你画手机猜”的小游戏

    本文适合有 Java 基础的人群 作者:DJL-Lanking HelloGitHub 推出的<讲解开源项目>系列.有幸邀请到了亚马逊 + Apache 的工程师:Lanking( htt ...

  2. [De1CTF 2019]Giftbox 分析&&TPOP学习

    [De1CTF 2019]Giftbox 刚进来我以为是直接给了shell,恐怖如斯. 随便扔了个命令,之后就没然后了,hhh,截包发现可能存在sql注入. 然后我就不会了... what i lea ...

  3. Bulldog1靶机渗透

    Bulldog1靶机渗透 扫描一下内网存活主机,发现192.168.114.144这个存活主机. 进行端口扫描:23,80,8080端口均开放. 进行网页访问,暂时没有什么发现,扫一下网站的目录. 发 ...

  4. python中的三大流程

    三大流程又叫程序控制流程 在我们编写代码时,程序是怎么运行的,要用到程序控制流程.在普通代码中,运行的方向是从上到下,从左到右 这就是顺序(逐行扫描). 第二个是分支(又叫选择)结构,例如在编写代码时 ...

  5. vue-cli3.0 vue组件发布到npm

    一.创建项目 1.vue create xxx (使用的版本是3.1.0) 2.把src目录名称改为examples 3.新建文件夹packages 用来存放组件 4.比如:新建Button组件 in ...

  6. 关于对象的行为、数组、继承和类的高级概念(Java)

    1.对象的行为:                   (1)方法调用栈:所有的方法调用都维护在一个称为调用栈的结构中. 第一个被调用的方法就是main(),该方法是Jvm调用的,因此main()方法总 ...

  7. 【奇淫巧技】sqlmap绕过过滤的tamper脚本分类汇总

    sqlmap绕过过滤的tamper脚本分类汇总

  8. 【漏洞复现】Office远程代码执行漏洞(CVE-2017-11882)

    昨晚看到的有复现的文章,一直到今天才去自己复现了一遍,还是例行记录一下. POC: https://github.com/Ridter/CVE-2017-11882/ 一.简单的生成弹计算器的doc文 ...

  9. STM32之旅2——按键

    STM32之旅2--按键     几乎每个项目都有用到按键,为了避免以后在做大项目的时候还在琢磨按键怎么写,现在写一个,方便以后使用.这里是最简单的独立按键驱动方法,和学习51单片机是的一样,更好的方 ...

  10. yii2框架路径相关

    调用YII框架中jquery:Yii::app()->clientScript->registerCoreScript('jquery'); framework/web/js/source ...