前言
以下内容源自A minor
仅供学习交流使用
推荐
MySQL笔记
【MySQL】基础使用(一):支持的数据类型
【MySQL】基础使用(二):常用 SQL 语句大全
【MySQL】基础使用(三):视图操作及其作用
【MySQL】基础使用(四):用户及权限操作
【MySQL】left join、right join、inner join 区别对比
【MySQL】SQL 查询中 in 和 exists 的区别分析
select from A where op B in适用于B表较小 因为会把符号条件的B表中的数据存储到内存中构成临时表 再存A表中注意匹配临时表 exists适用于A表较小 把A表数据缓存成临时表,与B表数据逐一匹配
【MySQL】基础使用(五):几条运行监控命令
【MySQL】运行原理(一):整体架构
【MySQL】运行原理(二):查询 SQL 的执行过程分析
【MySQL】运行原理(三):增删改 SQL 执行过程分析
【MySQL】存储引擎(一):存储引擎介绍
InnoDB 支持事务具有外键 行锁 MyISAM 查询。插入效率 表锁 行数
【MySQL】存储引擎(二):InnoDB 内存结构
1.Buffer Pool 缓存池 当Buffer Pool中有所需页,直接修改 等待后续刷脏处理 当Buffer Pool中没有所需页,磁盘调入内存 此时至少有一次磁盘IO,Change Buffer优化一下 1.1Change Buffer(写缓存) : 针对写操作 Buffer Pool没有所需页 当修改字段是普通索引,直接存入Change Buffer 当下次改页调入内存中,把Change Buffer 记录到数据页的操作merge。 当修改字段是唯一索引,就只能将数据页从磁盘读入到内存,返回给执行 1.2Adaptive Hash Index InnoDB存储引擎会监控对表上各个索引页的查询,如果观察到建立哈希索引可以带来速度上的提升,则建立哈希索引,所以叫做自适应哈希索引。 2.Log Buffer(Redo log) WAL 技术(Write-Ahead Logging),它的关键点就是先写日志,再写磁盘。 顺序IO和随机IO的区别 操作系统内存-页 磁盘-块 Log Buffer的刷盘机制 写到os buffer,写到磁盘的 0 延迟写,延迟刷 1秒 1 实时写,实时刷 事务提交 默认 2 实时写,延迟刷 事务提交 操作系统1秒 redo log(重做日志)有什么特点 物理日志 记录的数据信息 √ 逻辑日志 记录的操作信息 二个日志文件 logfile0 logfile1 write pos check point
【MySQL】存储引擎(三):InnoDB 磁盘结构
MySQL的存储结构分为5级:表空间、段、簇、页、行。 表空间:下篇讲 2^32个页 64TB 段:索引段、数据段 簇:区 1MB 64个连续的页 页:16KB
【MySQL】存储引擎(四):InnoDB 磁盘结构(表空间)
1.system tablespace(系统表空间) UndoLogs 在后面介绍 InnoDB数据字典 由内部系统表组成,存储表和索引的元数据(定义信息) 双写缓冲区 InnoDB的页和操作系统的页大小不一致,InnoDB页大小一般为16K,操作系统页大小为4K,InnoDB的页写入到磁盘时,一个页需要分4次写。 部分写失效(partial page write) 页的副本就是 double write,InnoDB的双写技术。通过它实现了数据页的可靠性。 ChangeBuffer 已介绍 2.file-per-table tablespaces(独占表空间) 3.general tablespaces(通用表空间) 4.temporary tablespaces(临时表空间) 5.undo log tablespace undo log(撤销日志或回滚日志) 逻辑格式的日志
【MySQL】存储引擎(五):重做日志(redo log),回滚日志(undo log),二进制日志(binlog)
1.重做日志(redo log) MySQL更新数据--为了减少磁盘IO,有了Buffer Pool缓存页数据--在内存中--防止丢失--redo log 确保事务的持久性 物理日志 2.回滚日志(undo log) 原子性底层就是通过undo log实现的 作用 回滚数据 MVCC 逻辑日志 3.二进制日志(binlog) 主从同步 还原 STATMENT、ROW、和MIXED => undo log 和 redo log => redo/undo log 和 binlog
【MySQL】索引原理(一):索引的存储结构是什么?数组?链表?树?
1.数组/链表 2.二叉查找树(BST Binary Search Tree) 查找效率与树的深度有个 斜树 O(n) 3.平衡二叉树(AVL Tree)(左旋、右旋) 左右子树的深度差不超过1 4.多路平衡查找树(B Tree)(分裂、合并) 二叉效率低 5.B+树(加强版多路平衡查找树) 数据都在叶子结点上 6.为什么不用红黑树? BST 黑平衡 1、只有两路;2、不够平衡 7.哈希表也能作为索引? 哈希冲突(采用拉链法解决) Memcache 支持 Mysql InnoDB 自适应哈希
【MySQL】索引原理(二):B+Tree索引的实现,MyISAM 和 InnoDB
1.MyISAM .MYD 数据文件 .MYI 索引文件 都是非聚簇索引 存的是地址 都需回表操作 2.InnoDB .ibd 聚簇索引 完整数据 非聚簇索引 主键 回表到聚簇索引 B+Tree是通过不断的分叉和合并操作使树保持平衡的,而这个时候键值的地址会发生变化,所以在辅助索引里面不能存储地址。 隐藏地址 rowid select _rowid name from user_info;
【MySQL】索引原理(三):联合索引(最左前缀原则),覆盖索引,索引条件下推
1.联合索引与最左前缀原则 全值匹配我最爱,最左前缀要遵守 带头大哥不能死,中间兄弟不能断 索引列上少计算,范围之后全失效 like百分写最右,覆盖索引不写星 不等空值还有or,索引失效要少用 2.覆盖索引 不用回表 3.索引条件下推(ICP) 先筛选,最后回表 只适用于二级索引。ICP 的目标是减少访问表的完整行的读数量从而减少 I/O 操作。
【MySQL】索引原理(四):创建索引的几点建议,列的离散度,前缀索引
1.列的离散度 建立索引,要使用离散度(选择度)更高的字段。 2.前缀索引 我们可以通过截取字段的前面一部分内容建立索引,这个就叫前缀索引。
【MySQL】事务与锁(一):详解数据库事务及并发时可能出现的问题
1.什么是事务? 不可以再分 2.事务的四大特性 原子性 undo log 一致性 其余三个保证 隔离性 持久性 redo log和double write双写缓冲保证 3.数据库如何操作事务? 显示开启 begin 隐式开启 autocommit=0 4.事务并发会带来什么问题? 脏读:事务A读取到了事务B已经修改但尚未提交的数据 不可重复读:事务A读取到了事务B已经提交的修改数据 导致自己读的和上一次不一样 幻读:事务A读取到了事务B提交的新增数据,不符合隔离性 5.如何解决并发处理事务带来的问题? 5.1 SQL92标准 隔离级别 读未提交 读已提交 可重复读 串行化 5.2 InnoDB 对隔离级别的支持 可重复读可以解决幻读
【MySQL】事务与锁(二):事务并发控制方案(LBCC + MVCC)
1.LBCC 方案 基于锁的并发控制 Lock Based Concurrency Control(LBCC)。 读未提交(Read Uncommitted) 解决更新丢失问题。如果一个事务已经开始写操作,那么其他事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现,即事务需要对某些数据进行修改必须对这些数据加 X 锁,读数据不需要加 S 锁。 读已提交(Read Committed) 解决了脏读问题。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。这可以通过“瞬间共享读锁”和“排他写锁”实现, 即事务需要对某些数据进行修改必须对这些数据加 X 锁,读数据时需要加上 S 锁,当数据读取完成后立刻释放 S 锁,不用等到事务结束。 可重复读取(Repeatable Read) 禁止不可重复读取和脏读取,但是有时可能出现幻读数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。 Mysql默认使用该隔离级别。这可以通过“共享读锁”和“排他写锁”实现,即事务需要对某些数据进行修改必须对这些数据加 X 锁,读数据时需要加上 S 锁,当数据读取完成并不立刻释放 S 锁,而是等到事务结束后再释放。 串行化(Serializable) 解决了幻读的问题的。提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。 不支持并发的读写操作 问题 如果仅仅是基于锁来实现事务隔离,一个事务读取的时候不允许其他时候修改,那就意味着不支持并发的读写操作,而我们的大多数应用都是读多写少的,这样会极大地影响操作数据的效率。 所以我们还有另一种解决方案,如果要让一个事务前后两次读取的数据保持一致,那么我们可以在修改数据的时候给它建立一个备份或者叫快照,后面再来读取这个快照就行了。这种方案我们叫做多版本的并发控制 Multi Version Concurrency Control(MVCC)。 2.MVCC 优化 多版本并发控制(Multi-Version Concurrency Control, MVCC)是MySQL中基于乐观锁理论实现隔离级别的方式,用于实现读已提交和可重复读取隔离级别的实现。 最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。 以 InnoDB 为例,每一行中都冗余了两个字断。一个是行的创建版本,一个是行的删除(过期)版本。具体的版本号(trx_id)存在 information_schema.INNODB_TRX 表中。版本号(trx_id)随着每次事务的开启自增。 事务每次取数据的时候都会取创建版本小于当前事务版本的数据,以及过期版本大于当前版本的数据。 PS:在 InnoDB 中,MVCC 是通过 Undo log 实现的;Oracle、Postgres等等其他数据库都有MVCC的实现。 3.最终策略:LBCC+MVCC 在 InnoDB 中,MVCC 和锁是协同使用的,这两种方案并不是互斥的。 问题一:各个隔离级别如何实现? 1)Read Uncommited:RU 隔离级别,不做并发控制 2)Read Commited:RC 隔离级别 快照读:MVCC,每次快照读时都生成 ReadView,读完就销毁 当前读:记录锁(RC 不支持 Gap Lock),直到事务结束才释放 隐式:insert、delete、update 默认使用 显式:select for update / lock in share mode 小结: 正因为是 ReadView 是一致性视图是会变化的,所以快照读下 RC 会出现不可重复读问题 正因为是记录锁,锁的只是单条记录,所以 RC 在当前读下会出现幻读的问题。 注:RC 不管单行还是范围查找,都不会用到gap lock;除了两种特殊情况——外键约束检查(foreign-key constraint checking)以及重复键检查(duplicate-key checking)时会使用间隙锁封锁区间。 3)Repeatable Read:RR 隔离级别 快照读:MVCC,第一次快照读时生成 ReadView,事务提交时销毁 当前读:当前元素临键锁(左开右闭) + 相邻间隙锁,直到事务结束才释放 隐式:insert、delete、update 默认使用 显示:select for update / lock in share mode 小结: 正因为视图的一致性,所以快照读下 RR 解决了不可重复读问题 正因为间隙锁,锁住了元素及左右区间(无法添加系元素),所以当前读下 RR 解决了幻读问题(InnoDB,有争议) 4)Serializable:串行化 所有的 select 语句都会被隐式的转化为 select … in share mode,会和 insert、update、delete 互斥。 问题二:所以,事务隔离级别怎么选? RU 和 Serializable 肯定不能用。为什么有些公司要用 RC,或者说网上有些文章推荐有 RC? RC 和 RR 主要有几个区别: RR 的间隙锁会导致锁定范围的扩大 条件列未使用到索引,RR 锁表,RC 锁行 RC的“半一致性”(semi-consistent)读可以增加 update 操作的并发性。 在RC中,一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足 update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。 实际上,如果能够正确地使用锁(避免不使用索引去加锁),只锁定需要的数据,用默认的RR级别就可以了。
【MySQL】事务与锁(三):事务并发控制方案(MVCC)
1.MVCC是什么 MVCC的核心思想是: 我可以查到在我这个事务开始之前已经存在的数据,即使它在后面被修改或者删除了。在我这个事务之后新增的数据,我是查不到的。 这个快照什么时候创建?读取数据的时候,怎么保证能读取到这个快照而不是最新的数据?这个怎么实现呢? InnoDB为每行记录都实现了两个隐藏字段: DB_TRX_ID,6字节:插入或更新行的最后一个事务的事务ID,事务编号是自动递增的(我们把它理解为创建版本号,在数据新增或者修改为新数据的时候,记录当前事务ID)。 DB_ROLL_PTR,7字节:回滚指针(我们把它理解为删除版本号,数据被删除或记录为旧数据的时候,记录当前事务ID)。我们把这两个事务ID理解为 版本号。 MVCC的查找规则:只能查找创建时间小于等于当前事务ID的数据,且删除时间大于当前事务ID的行(或未删除)。
【MySQL】事务与锁(四):事务并发控制方案(LBCC)
1.锁的粒度 行锁 表锁 加锁效率 冲突的概率 2.锁的基本模式 2.1 共享锁/排他锁(行级别读/写锁) 2.2 意向共享锁/意向排他锁(表级别读/写锁) 问题一:锁的作用是什么?它跟Java里面的锁是一样的,是为了解决资源竞争的问题,Java里面的资源是对象,数据库的资源就是数据表或者数据行。所以锁是用来解决事务对数据的并发访问的问题的。 问题二:锁到底锁住了什么呢?当一个事务锁住了一行数据的时候,其他的事务不能操作这一行数据,那它到底是锁住了这一行数据,还是锁住了这一个字段,还是锁住了别的什么东西呢?锁的其实是索引,详情见 行锁到底锁住的是什么? 3.行锁的算法 3.1 记录锁 第一种情况,当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。 3.2 间隙锁 第二种情况,当我们查询的记录不存在,没有命中任何一个record,无论是用等值查询还是范围查询的时候,它使用的都是间隙锁。 注意,间隙锁主要是阻塞插入insert,而没有阻塞select。相同的间隙锁之间不冲突。 Gap Lock 只在隔离级别 RR 中存在 3.3 临键锁 第三种情况,当我们使用了范围查询,不仅仅命中了 Record 记录,还包含了 Gap 间隙,在这种情况下我们使用的就是临键锁,它是MySQL里面默认的行锁算法,相当于记录锁加上间隙锁。 临键锁与间隙锁不同的是,它除了锁住原本的临键区间,还会锁住最后一个key的下一个左开右闭的区间 为什么要锁住下一个左开右闭的区间?——为了解决幻读的问题。所以,我们看下MySQL InnoDB里面事务隔离级别的实现。为什么 InnoDB 的RR 级别能够解决幻读的问题,就是用临键锁实现的。
【MySQL】事务与锁(五):行锁到底锁住的是什么?记录?字段?索引?
在没有索引或者没有用到索引的情况下,会锁住整张表 隐藏字段ROWID做索引了 为什么锁表,是因为查询没有使用索引,会进行全表扫描,然后把每一个隐藏的聚集索引都锁住了。 InnoDB 的行锁,就是通过锁住索引来实现的。 总结一下: 行锁在 InnoDB 中是基于索引实现的,所以一旦某个加锁操作没有使用索引,那么该锁就会退化为表锁 除了直接在主键索引加锁,我们还可以通过辅助索引找到相应主键索引后再加锁
【MySQL】事务与锁(六):死锁问题分析
【MySQL】性能调优(一):配置。连接配置优化
【MySQL】性能调优(二):表结构。建表及存储引擎几点建议
【MySQL】性能调优(三):SQL。慢查询日志及SQL优化建议
1.慢日志 2.SQL 优化建议
【MySQL】性能调优(四):SQL。EXPLAIN 执行计划详解
【MySQL】性能调优(五):架构。集群及分库分表
【MySQL】性能调优(六):业务层。减轻数据库压力
最后
2022/11/20 23:22
这篇博客能写好的原因是:站在巨人的肩膀上
这篇博客要写好的目的是:做别人的肩膀
开源:为爱发电
学习:为我而行