undolog、redolog、binlog的用处

总结:
binlog 一致性。用于主从复制和数据恢复
redolog 保证持久性。log用于保证持久化,恢复在内存更新后,还没来得及刷到磁盘的数据
undolog 原子性。用于实现事务回滚和mvcc多版本并发控制

binlog(逻辑日志)#

一致性
定义:记录数据表结构和数据变更的二进制文件

redolog(物理日志)#

持久性
mysql不能每更新一条数据,就持久化到磁盘,因为会带来严重性能问题。所以是先更新到缓存,再特定时机下,再刷新到磁盘,为了防止内存数据丢失,会使用redolog作为事务日志,在内存更新完成后,写入redolog buffer,然后数据会写入到redolog file磁盘

redolog buffer写入磁盘的模式:

  • 0(延迟写)每秒刷新写入磁盘,若系统崩溃,出现1s数据丢失
  • 1(实时写)每次提交都会写入磁盘, 不会有数据丢失,但io性能差
  • 2(实时写,延迟刷新)每次提交到os buffer, 然后每秒将os buffer日志写到磁盘

注:redolog的大小是固定的,可能被覆盖

undolog(逻辑日志)#

原子性
undolog存储数据的逻辑变化日志,
参考:https://blog.csdn.net/Huangjiazhen711/article/details/127900821

多版本并发控制mvcc,
MVCC在mysql中的实现依赖的是undo log与read view
undo log记录某行数据的多个版本的数据;read view用来判断当前版本数据的可见性。
read view可以用来判断可以看到哪些事务。根据他保存的创建时活跃事务id列表,创建时的最大事务id,创建时的最小事务id。

RC在每次语句执行,都会重新创建一份ReadView
RR下,事务开始创建ReadView,一直到事务结束

undo log的删除。当该undolog没有出现在其他事务的readview时,说明事务已提交,且没有其他事务依赖,innodb后台的清除purge线程会遍历删除undolog。聚簇索引中deleted标识为1的也会被删除。均为异步操作。

注:事务隔离性是通过mvcc+排他锁实现的

Innodb 锁

Innodb支持哪些锁#

按兼容性分为 共享锁(Shared Locks,S锁)、排他锁(Exclusive Locks,X锁)
按锁范围分为:表级锁、行级锁
表锁:表共享读锁(Table Read Lock)、表排他写锁(Table Write Lock)、意向共享锁(intention shared lock, IS锁)、意向排他锁(intention exclusive lock, IX锁)
行锁:记录锁(Record Locks)、间隙锁(Gap Locks)、临键锁(Next-Key Locks)、插入意向锁(Insert Intention Locks)

共享锁(S)、排他锁(X)#

锁的模式,每种锁都有shared和exclusive两种模式。
特性:X锁与其他X,S锁不兼容,S与S锁兼容

锁定读#

SELECT … FOR SHARE 和 SELECT … FOR UPDATE
这两种锁定读在搜索时所遇到的每一条索引记录(index record)上设置共享锁或排它锁。

意向锁(IS、IX)#

意向锁,协调行锁和表锁之间的关系的,实现了“表锁是否冲突”的快速判断。协调表的读写锁和行的读写锁(不同粒度锁)之间关系。
场景:获取行的S锁,需要先获取表的IS锁;获取行的X锁,需要先获取表的IX锁。
注:与意向锁冲突的不是行的X锁,是表的X锁!
IX 与表的 X 和 S 冲突,意思当前表有行锁X锁,不能上表的X或S锁。
IS 与表的 X 冲突,意思当前表有行锁S锁,不能上表的X锁。
why意向锁:“表锁是否冲突”的快速判断。否则需要遍历表去判断行锁的存在,才能判断冲突。

索引记录锁(Record Locks)#

行锁,锁定的是索引记录。所谓的“锁定某个行”或“在某个行上设置锁”。就是在某个索引的特定索引记录(或称索引条目、索引项、索引入口)上设置锁。有shared或exclusive两种模式

间隙锁(Gap Locks)#

锁定尚未存在的记录,即索引记录之间的间隙。有shard或exclusive两种模式,但,两种模式没有任何区别,二者等价。
gap lock 之间可以共存 ! 两个事务可以共同持有。gap只阻塞插入意向锁。
why gap lock: 唯一目的就是阻止其他事务向gap中插入数据行。解决 phantom row问题。
gap lock与 插入意向锁冲突。rc 没有gap lock, rr使用gap与插入意向锁的冲突解决幻读。

下一个键锁(Next-Key Locks)#

next-key lock 是 (索引记录上的索引记录锁) + (该索引记录前面的间隙上的锁) 二者的合体,它锁定索引记录以及该索引记录前面的间隙。有shard或exclusive两种模式。
next-key lock 带的 gap lock固定锁记录前面的间隙!

插入意向锁(Insert Intention Locks)#

特殊的gap lock。
插入行之前,INSERT操作会首先在索引记录之间的间隙上设置insert intention lock,该锁的范围是(插入值, 向下的一个索引值)。有shard或exclusive两种模式,但,两种模式没有任何区别,二者等价。
插入意向锁 会和 gap锁冲突!
插入意向锁 本身多个不会冲突!插入意向锁也不会阻塞gap锁,只有先gap, 再insert Intention Lock 会阻塞。
why插入意向锁: 隔离级别为RR时正是利用插入意向锁与gap锁的冲突,来解决幻读问题。

不同索引加锁情况#

  1. 聚簇索引
    索引命中:对这个 id 聚簇索引加 X 锁
    索引未命中:对 id 这个聚簇索引加 GAP 锁,GAP的范围是两个索引的间隙
    范围条件: 对范围区间内命中的id聚簇索引加Next-Key 锁,即左开右闭的GAP锁+X锁。eg: UPDATE student SET age = 100 WHERE id < 3
  2. 唯一索引
    索引命中:二级索引的叶子节点中保存了主键索引的位置,在给二级索引加锁的时候,对应聚簇索引也会一并加锁。加两个X锁。
    索引未命中:只会在二级索引加GAP锁,不会在聚簇索引上加锁。
    范围条件:对范围区间内命中的唯一索引加Next-Key 锁(对条件范围内的索引加GAP锁+X锁),并且对命中索引对应的聚簇索引也会加 X锁。
  3. 非唯一索引
    索引命中:对命中的非唯一索引加 Next-Key锁,在非唯一索引相邻区间加 GAP 锁。并且对命中索引对应的聚簇索引加X锁。
    索引未命中: 只会在二级索引加GAP锁,不会在聚簇索引上加锁。
    范围条件:与唯一索引的范围条件加锁类似,对命中的索引加Next-Key锁(对条件范围内的索引加GAP锁+X锁),对应的聚簇索引加X锁。
  4. 无索引
    在没有索引的时候,只能走聚簇索引,对表中的记录进行全表扫描。会给所有记录加行锁,所有聚簇索引之间会加上 GAP 锁。

为什么无索引更新会锁全表?#

在没有索引的时候,只能走聚簇索引,对表中的记录进行全表扫描。会给所有记录加行锁,所有聚簇索引和聚簇索引之间还会加上 GAP 锁。
eg: UPDATE student SET age = 100 WHERE age = 33; age无索引。
why: RR 下要保证当前读时不出现幻读。需要锁全表!否则任何一个记录都可以变成age=33, 任何一个地方都可以insert一条age=33。再执行该sql的时候会发现多出记录了。

死锁场景#

死锁的可能性并不受隔离级别的影响。隔离级别改变的是读操作的行为,而死锁是由于写操作产生的。

  1. 两个行锁顺序不同的获取
    stu_no字段 是唯一索引_
    1
    2
    3
    4
    session1 : update student set age = 88 where stu_no = 1;
    session2 : update student set age = 99 where stu_no = 3;
    session1 : update student set age = 99 where stu_no = 3;
    session2 : update student set age = 88 where stu_no = 1;

死锁, 一个有stu_no=1的 X RECORD锁,一个有stu_no=3的。

  1. 两个gap锁各自阻塞另一个事务的插入意向锁
    stu_no字段 是唯一索引_

    1
    2
    3
    4
    session1 : delete from student where stu_no = 8;
    session2 : delete from student where stu_no= 7;
    session1 : insert student (stu_no) values (6);
    session2 : insert student (stu_no) values (9);
  2. 两个insert duplicate失败会加S RECORD, 若session1 commit, 两个返回duplicate-key error,若session1 rollback, 两个都想获取X锁,但对方都不释放S锁
    stu_no字段是唯一索引_

    1
    2
    3
    4
    5
    6
    session1: insert student (stu_no) values (6);
    session2: insert student (stu_no) values (6);
    session3: insert student (stu_no) values (6);
    session1: rollback;
    session2: commit;
    session3: commit;

参考文档:
https://mp.weixin.qq.com/s/IyeiP2t1TGxZlPqSPAWNZg
https://mp.weixin.qq.com/s/dRIfbVwAJfEuZ978VlyXIA
https://mp.weixin.qq.com/s/S9Fzwu7-g81DsWgjaARHvQ

分库分表组件原理

参考文档:
https://github.com/Meituan-Dianping/Zebra/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%BB%E6%B5%81%E8%AE%BE%E8%AE%A1

  1. 数据库中间件设计方案
    典型的数据库中间件设计方案有2种:
    服务端代理(proxy:代理数据库)、
    客户端代理(datasource:代理数据源)。
    下图演示了这两种方案的架构

可以看到不论是代理数据库还是代理数据源,底层都操作了多个数据库实例。不同的是:

服务端代理(proxy:代理数据库)中: 我们独立部署一个代理服务,这个代理服务背后管理多个数据库实例。而在应用中,我们通过一个普通的数据源(c3p0、druid、dbcp等)与代理服务器建立连接,所有的sql操作语句都是发送给这个代理,由这个代理去操作底层数据库,得到结果并返回给应用。在这种方案下,分库分表和读写分离的逻辑对开发人员是完全透明的。

客户端代理(datasource:代理数据源): 应用程序需要使用一个特定的数据源,其作用是代理,内部管理了多个普通的数据源(c3p0、druid、dbcp等),每个普通数据源各自与不同的库建立连接。应用程序产生的sql交给数据源代理进行处理,数据源内部对sql进行必要的操作,如sql改写等,然后交给各个普通的数据源去执行,将得到的结果进行合并,返回给应用。数据源代理通常也实现了JDBC规范定义的API,因此能够直接与orm框架整合。在这种方案下,用户的代码需要修改,使用这个代理的数据源,而不是直接使用c3p0、druid、dbcp这样的连接池

  1. 主流的数据库中间件实现对比
    无论是代理数据库,还是代理数据源,二者的作用都是类似的。以下列出了这两种方案目前已有的实现以及各自的优缺点:

数据库代理

目前的实现方案有:阿里巴巴开源的cobar,mycat团队在cobar基础上开发的mycat,mysql官方提供的mysql-proxy,奇虎360在mysql-proxy基础开发的atlas。目前除了mycat,其他几个项目基本已经没有维护。

优点:多语言支持。也就是说,不论你用的php、java或是其他语言,都可以支持。原因在于数据库代理本身就实现了mysql的通信协议,你可以就将其看成一个mysql 服务器。mysql官方团队为不同语言提供了不同的客户端驱动,如java语言的mysql-connector-java,python语言的mysql-connector-python等等。因此不同语言的开发者都可以使用mysql官方提供的对应的驱动来与这个代理服务器建通信。

缺点:实现复杂。因为代理服务器需要实现mysql服务端的通信协议,因此实现难度较大。

数据源代理
目前的实现方案有:阿里巴巴开源的tddl,大众点评开源的zebra,当当网开源的sharding-jdbc。

优点:更加轻量,可以与任何orm框架整合。这种方案不需要实现mysql的通信协议,因为底层管理的普通数据源,可以直接通过mysql-connector-java驱动与mysql服务器进行通信,因此实现相对简单。

缺点:仅支持某一种语言。例如tddl、zebra、sharding-jdbc都是使用java语言开发,因此对于使用其他语言的用户,就无法使用这些中间件。版本升级困难,因为应用使用数据源代理就是引入一个jar包的依赖,在有多个应用都对某个版本的jar包产生依赖时,一旦这个版本有bug,所有的应用都需要升级。而数据库代理升级则相对容易,因为服务是单独部署的,只要升级这个代理服务器,所有连接到这个代理的应用自然也就相当于都升级了。

ORM框架代理
目前有hibernate提供的hibernate-shards,也可以通过mybatis插件的方式编写。相对于前面两种方案,这种方案可以说是只有缺点,没有优点。

MVCC-2021

总结#

MVCC的核心实现主要基于两部分:多事务并发操作数据与一致性读实现。

RC的本质:每一条SELECT都可以看到其他已经提交的事务对数据的修改,只要事务提交,其结果都可见,与事务开始的先后顺序无关。
RR的本质:第一条SELECT生成ReadView前,已经提交的事务的修改可见。

多事务并发操作数据#

多事务并发操作数据核心基于Undo log进行实现,Undo log可以用来做事务的回滚操作,保证事务的原子性。
同时可以用来构建数据修改之前的版本,支持多版本读。

InnoDB中,每一行记录都有两个隐藏列:DATA_TRX_ID和DATA_ROLL_PTR。(若没有主键,则还有一个隐藏主键)
DATA_TRX_ID:记录最近更新这条记录的事务ID(6字节)
DATA_ROLL_PTR:指向该行回滚段的指针,通过指针找到之前版本,通过链表形式组织(7字节)
DB_ROW_ID:行标识(隐藏单增ID),没有主键时主动生成(6字节)
当存在多个事务进行并发操作数据时,不同事务对同一行的更新操作产生多个版本,通过回滚指针将这些版本链接成一条Undo Log链。

操作过程如下:
1、将待操作的行加排他锁。
2、将该行原本的值拷贝到Undo Log中,DB_TRX_ID和DB_ROLL_PTR保持不变。(形成历史版本)
3、修改该行的值,更新该行的DATA_TRX_ID为当前操作事务的事务ID,将DATA_ROLL_PTR指向第二步拷贝到Undo Log链中的旧版本记录。(通过DB_ROLL_PTR可以找到历史记录)
4、记录Redo Log,包括Undo Log中的修改。
INSERT操作:产生新的记录,其DATA_TRX_ID为当前插入记录的事务ID。
DELETE操作:软删除,将DATA_TRX_ID记录下删除该记录的事务ID,真正删除操作在事务提交时完成。

一致性读实现#

在InnoDB中,对于不同的事务隔离级别,一致性读实现均不相同,具体如下:
READ UNCOMMITED隔离级别:直接读取版本的最新记录。
SERIALIZABLE隔离级别:通过加锁互斥访问数据实现。
READ COMMITED和REPEATABLE READ隔离级别:使用版本链实现。(ReadView,可读性视图)
对于RC与RR隔离级别,实现一致性读都是通过ReadView,也就是今天的重点,什么是ReadView?

MVCC ReadView
ReadView是事务开启时,当前所有活跃事务(还未提交的事务)的一个集合,ReadView数据结构决定了不同事务隔离级别下,数据的可见性。

up_limit_id:最先开始的事务,该SQL启动时,当前事务链表中最小的事务id编号,也就是当前系统中创建最早但还未提交的事务
low_limit_id:最后开始的事务,该SQL启动时,当前事务链表中最大的事务id编号,也就是最近创建的除自身以外最大事务编号
m_ids:当前活跃事务ID列表,所有事务链表中事务的id集合
注:ID越小,事务开始的越早;ID越大,事务开始的越晚

1、下面所说的db_trx_id,是来自于数据行中的db_trx_id字段,并非开启了一个事务分配的ID,分配的事务ID只有操作了数据行,才会更新数据行中的db_trx_id字段
2、ReadView是与SQL绑定的,而并不是事务,所以即使在同一个事务中,每次SQL启动时构造的ReadView的up_trx_id和low_trx_id也都是不一样的
up_limit_id表示“低水位”,即当时活跃事务列表的最小事务id(最早创建的事务),如果读取出来的数据行上的的db_trx_id小于up_limit_id,则说明这条记录的最后修改在ReadView创建之前,因此这条记录可以被看见。

low_limit_id表示“高水位”,即当前活跃事务的最大id(最晚创建的事务),如果读取出来的数据行上的的db_trx_id大于low_limit_id,则说明这条记录的最后修改在ReadView创建之后,因此这条记录肯定不可以被看见。

如果读取出来的数据行上的的db_trx_id在low_limit_id和up_limit_id之间,则查找该数据上的db_trx_id是否在ReadView的m_ids列表中:

如果存在,则表示这条记录的最后修改是在ReadView创建之时,被另外一个活跃事务所修改,所以这条记录也不可以被看见。
如果不存在,则表示这条记录的最后修改在ReadView创建之前,所以可以看到。

REPEATABLE READ下的ReadView生成
每个事务首次执行SELECT语句时,会将当前系统所有活跃事务拷贝到一个列表中生成ReadView。

每个事务后续的SELECT操作复用其之前生成的ReadView。

UPDATE,DELETE,INSERT对一致性读snapshot无影响。

示例:事务A,B同时操作同一行数据

若事务A的第一个SELECT在事务B提交之前进行,则即使事务B修改记录后先于事务A进行提交,事务A后续的SELECT操作也无法读到事务B修改后的数据。
若事务A的第一个SELECT在事务B修改数据并提交事务之后,则事务A能读到事务B的修改。
针对RR隔离级别,在第一次创建ReadView后,这个ReadView就会一直持续到事务结束,也就是说在事务执行过程中,数据的可见性不会变,所以在事务内部不会出现不一致的情况。

READ COMMITED下的ReadView生成
每次SELECT执行,都会重新将当前系统中的所有活跃事务拷贝到一个列表中生成ReadView。

针对RC隔离级别,事务中的每个查询语句都单独构建一个ReadView,所以如果两个查询之间有事务提交了,两个查询读出来的结果就不一样。

innodb事务隔离级别

一、定义#

未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
提交读(Read Committed):只能读取到已经提交的数据. 可以阻止脏读,但是可能发生幻读或不可重复读
可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的. 可以阻止脏读和不可重复读,幻读通过mvcc解决了快照读,next-key锁解决了当前读. mysql默认隔离级别.
串行读(Serializable):读加共享锁,写加排他锁,读写互斥.

可重复读指的是同一行在同一个事务下无论怎么读取都是同一个结果(除非自己把它改了).

幻读指的是在同一事务下,连续执行两次同样的SQL语句第二次的SQL语句可能返回之前不存在的行;

二、什么是快照读,什么是当前读#

快照读:就是select

1
select * from table ….;

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

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

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

三、rr级别下mvcc解决不可重复读和快照读之间的幻读#

一致非锁定读,也可以称为快照读,即普通SELECT语句,既然是快照读,故 SELECT 的时候,会生成一个快照。

生成快照的时机:事务中第一次调用SELECT语句的时候才会生成快照,在此之前事务中执行的update、insert、delete操作都不会生成快照

REPEATED READ 隔离级别下,快照会在事务中第一次SELECT语句执行时生成,只有在本事务中对数据进行更改才会更新快照,因此,只有第一次SELECT之前其它已提交事务所作的更改你可以看到,但是如果已执行了SELECT,那么其它事务commit数据,你SELECT是看不到的。

四、rr级别下next-key锁解决当前读之间的幻读#

在当前读下rr级别使用了next-key锁(临键锁),临键锁包括行锁+间隙锁, 来避免两个当前读时有其它事务插入数据,所以当前读使用next-key锁解决的幻读。

最后备注下:如果是先快照读再当前读,影响行数不一致是否属于幻读,是有争议的但大多认为并不是幻读。

五、rr和rc区别#

rc级别下的mvcc总是读取数据行的最新快照,而rr级别下的mvcc,会在事务第一次select的时候,为数据行生成一个快照,后面每次都读这个快照,除非自己更新

如果问,rr为什么是默认的隔离级别,就说rr相比rc来说没有不可重复读和幻读问题. 后面再深入研究

六、todo学习#

  1. rr为什么是默认的隔离级别
  2. mvcc在rc隔离级别下,读最新的快照,为什么不直接读行记录呢,rc级别是怎么解决脏读的

参考文章#

https://tech.meituan.com/2014/08/20/innodb-lock.html

innodb事务隔离级别、MVCC

隔离级别#

未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)

可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读

串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

MVCC#

InnoDB是一个多版本的存储引擎:为了支持事务的一些特性诸如并发和回滚,它保持着被修改行的旧版本信息。这些信息被存储在一个被叫做回滚段(rollback segment)的表空间中。InnoDB在回滚段中用这些信息来执行undo操作,以此支持事务回滚。它也用这些信息来构造行的更早的版本,以此支持一致性读(快照读)。

在内部,InnoDB为数据库中存储的每一行添加三个隐藏字段。

DB_TRX_ID:表明插入或者修改这一行的最后一个事务的事务标识符。如何查看行事务ID

DB_ROLL_PTR:指向回滚段中的一个undo log记录,如果行被修改了,那么这个undo log记录包含的信息必须先于行修改被重新修改。

DB_ROW_ID:单调递增的行ID。如果InnoDB自动生成了一个聚集索引,那么这个索引包含行ID值,否则DB_ROW_ID列不会出现在任何索引中。

锁类型#

共享锁(读锁,S锁):若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放S锁。

排他锁(写锁,X锁):若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他事务不能再对A加作何类型的锁,直到T释放A上的X锁。

意向共享锁(IS锁):事务T在对表中数据对象加S锁前,首先需要对该表加IS(或更强的IX)锁。

意向排他锁(IX锁):事务T在对表中的数据对象加X锁前,首先需要对该表加IX锁。

意向锁补充#
  • InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
  • 意向锁之间互不排斥,但除了 IS 与 S 兼容外,意向锁会与 共享锁 / 排他锁 互斥。
  • IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
  • 意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。

2pl 两阶段锁#

两段锁协议,先随便加,最后commit才能一起释放.

GAP锁有何用#

其实这个多出来的GAP锁,就是RR隔离级别,相对于RC隔离级别,不会出现幻读的关键。

确实,GAP锁锁住的位置,也不是记录本身,而是两条记录之间的GAP。

所谓幻读,就是同一个事务,连续做两次当前读 (例如:select * from t1 where id = 10 for update;),那么这两次当前读返回的是完全相同的记录 (记录数量一致,记录本身也一致),第二次的当前读,不会比第一次返回更多的记录 (幻象)。

如何保证两次当前读返回一致的记录,那就需要在第一次当前读与第二次当前读之间,其他的事务不会插入新的满足条件的记录并提交。为了实现这个功能,GAP锁应运而生

快照读 & 当前读#

快照读:就是select

1
select * from table ….;

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

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

聊天讨论#

全快照、全当前,不幻读, 先快照,再当前,幻读

「 球球: 其实产生幻读的原因,就是以为读走了mvcc的副本, 」


读如果也加gap锁,就真的不会幻读了,但代价太大

「 宏伟: 张三账户余额有10000元;要转账给李四9000元,转给王五5000元;这两次转账肯定有一次因余额不足转账失败;如果在 rr 下,可重复读, 两次update 张三都会成功? 」


这个如果两个事务并发,都先读的1w元快照读,然后第1个事务直接update张三减钱, commit, 第2个事务 接着update张三减钱,都会成功,除非
1 用数据库乐观锁
2 自己搞个分布式锁,将读写操作原子化
3 用kafka让同一用户操作串行化

敏: 交易相关的就不要快照读了, 交易要算钱,select的时候就要锁,不然这条数据之后未必可用

总结#

RR级别下,通过MVCC, 解决了两个快照读的幻读问题,
通过next-key锁,解决了两个当前读的幻读问题,但是快照读后的当前读是会幻读的.

mysql创建100张表

使用存储过程,创建100张表

Innodb索引原理及其优化

一、目的

了解索引结构,explain方法,优化原理

二、索引名词

2.1 聚簇索引

1

  • 聚簇索引也称为聚集索引,聚类索引,簇集索引,聚簇索引的顺序就是数据的物理存储顺序。
  • 由于聚簇索引规定数据在表中的物理存储顺序,因此一个表只能包含一个聚簇索引。
  • Innodb聚簇索引根据每张表的主键构造一棵B+树,叶子节点中存放的即为整张表的行记录数据,也称为数据页。
  • 对于主键的排序查找和范围查找速度非常快。叶子节点的数据就是用户所要查询的数据

mysql技术内幕第二章、Innodb存储引擎

Innodb

Innodb体系结构

存储引擎结构图

InnoDB 存储引擎有多个内存块,这些内存块组成了一个大的内存池,主要负责如下工作:

  • 维护所有进程/线程需要访问的多个内部数据结构
  • 缓存磁盘上的数据, 方便快速读取, 同时在对磁盘文件修改之前进行缓存
  • 重做日志(redo log)缓冲

mysql技术内幕第一章、mysql体系结构和存储引擎

Mysql体系结构

mysql体系结构

MySQL由以下几部分组成:

  • 连接池组件。
  • 管理服务和工具组件。
  • SQL接口组件。
  • 查询分析器组件。
  • 优化器组件。
  • 缓冲(Cache)组件。
  • 插件式存储引擎。
  • 物理文件。

从图1-1还可以看出,MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎。MySQL插件式的存储引擎架构提供了一系列标准的管 理和服务支持,这些标准与存储引擎本身无关,可能是每个数据库系统本身都必需的,如SQL分析器和优化器等,而存储引擎是底层物理结构的实现,每个存储引 擎开发者都可以按照自己的意愿来进行开发。

注意:存储引擎是基于表的,而不是数据库。请牢牢记住图1-1所示的MySQL体系结构图,它对于你以后深入了解MySQL有极大的帮助

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×