pull/1/head
595208882@qq.com 3 years ago
parent b07147b93f
commit e7634d0838

@ -959,7 +959,7 @@ kill trx_mysql_thread_id;
事务是一系列对系统中数据进行访问与更新的操作组成的一个程序逻辑单元。即不可分割的许多基础数据库操作。
## 事务特性ACID
## 事务特性(ACID)
### 原子性Atomicity
@ -1311,219 +1311,120 @@ SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;
## 索引结构
### 平衡多路查找树(B-Tree)
### Hash索引
二叉查找树查询的时间复杂度是O(logN)查找速度最快和比较次数较少。但用于数据库索引当数据量过大不可能将所有索引加载进内存使用二叉树会导致磁盘IO过于频繁最坏的情况下磁盘IO的次数由树的高度来决定。
![Hash索引](images/Database/Hash索引.png)
B-Tree对二叉树进行了横向扩展使树结构更加**矮胖**使得一次IO能加载更多关键字对比在内存中完成减少了磁盘IO次数更适用于大型数据库但是为了保持自平衡插入或者删除元素都会导致节点发生裂变反应有时候会非常麻烦。
![索引机制-B-树](images/Database/索引机制-B-树.jpg)
B-树的特性:
- 关键字集合分布在整颗树中
- 任何一个关键字出现且只出现在一个结点中
- 搜索有可能在非叶子结点结束
- 其搜索性能等价于在关键字全集内做一次二分查找
- 自动层次控制
### B+Tree
B+树是B-树的变体也是一种多路搜索树。其定义基本与B-树相同,除了:
- 非叶子结点的子树指针与关键字个数相同
- 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树B-树是开区间)
- 为所有叶子结点增加一个链指针
- 所有关键字都在叶子结点出现
![索引机制-B+树](images/Database/索引机制-B+树.jpg)
B+的搜索与B-树也基本相同区别是B+树只有达到叶子结点才命中B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找。
B+的特性:
- 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的
- 不可能在非叶子结点命中
- 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层
- 更适合文件索引系统
### B*Tree
是B+树的变体在B+树的非根和非叶子结点再增加指向兄弟的指针;
![索引机制-B星树](images/Database/索引机制-B星树.jpg)
B*树定义了非叶子结点关键字个数至少为(2/3)M即块的最低使用率为2/3代替B+树的1/2
**B+树的分裂**
当一个结点满时分配一个新的结点并将原结点中1/2的数据复制到新结点最后在父结点中增加新结点的指针B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针。
**B*树的分裂**
当一个结点满时如果它的下一个兄弟结点未满那么将一部分数据移到兄弟结点中再在原结点插入关键字最后修改父结点中兄弟结点的关键字因为兄弟结点的关键字范围改变了如果兄弟也满了则在原结点与兄弟结点之间增加新结点并各复制1/3的数据到新结点最后在父结点增加新结点的指针所以B*树分配新结点的概率比B+树要低,空间使用率更高。
**原理**
- 事先将索引通过 hash算法后得到的hash值(即磁盘文件指针存到hash表中
- 在进行查询时将索引通过hash算法得到hash值与hash表中的hash值比对。通过磁盘文件指针只要**一次磁盘IO**就能找到要的值
例如在第一个表中要查找col=6的值。hash(6) 得到值比对hash表就能得到89。性能非常高。
### 二叉树
**特点**
1. 左子节点值 < 节点值
2. 右子节点值 > 节点值
3. 当数据量非常大时,要查找的数据又非常靠后,和没有索引相比,那么二叉树结构的查询优势将非常明显
**存在问题**
如下图,可以看出,二叉树出现单边增长时,二叉树变成了“链”,这样查找一个数的时候,速度并没有得到很大的优化。
![索引结构-二叉树](images/Database/索引结构-二叉树.png)
- 但是hash表索引存在问题如果要查询带范围的条件时hash索引就歇菜了
### 红黑树
**特点**
1. 节点是红色或者黑色
2. 根节点是黑色
3. 每个叶子的节点都是黑色的空节点NULL
4. 每个红色节点的两个子节点都是黑色的
5. 从任意节点到其每个叶子的所有路径都包含相同的黑色节点
**优点**
![索引结构-红黑树](images/Database/索引结构-红黑树.png)
- 快速查询参与索引的字段只要进行Hash运算之后就可以快速定位到该记录时间复杂度约为1
**存在的问题**
**缺点**
**红黑树虽然和二叉树相比,一定程度上缓解了单边过长的问题,但是它依旧存储高度问题。** 
- 哈希索引只包含哈希值和行指针,所以不能用索引中的值来避免读取行
- 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序和范围查询
- 哈希索引也不支持部分索引列查询,因为哈希索引始终是使用索引列的全部数据进行哈希计算的
- 哈希索引只支持等值比较查询,如=IN()<=>操作
- 如果哈希冲突较多,一些索引的维护操作的代价也会更高
假设现在数据量有100万那么红黑树的高度大概为 100,0000 = 2^n n大概为 20。那么至少要20次的磁盘IO这样性能将很受影响。如果数据量更大IO次数更多性能损耗更大。**所以红黑树依旧不是最佳方案。**
### B-Tree索引
**思考:针对上面的红黑树结构,我们能否优化一下呢?**
**背景**二叉查找树查询的时间复杂度是O(logN)查找速度最快和比较次数较少。但用于数据库索引当数据量过大不可能将所有索引加载进内存使用二叉树会导致磁盘IO过于频繁最坏的情况下磁盘IO的次数由树的高度来决定。
上述红黑树默认一个节点就存了一个 (索引+磁盘地址),我们设想一个节点存多个 (索引+磁盘地址),这样就可以降低红黑树的高度了。 **实际上我们设想的这种结构就是 B-Tree**
B-Tree(平衡多路查找树)对二叉树进行了横向扩展,能很好解决红黑树中遗留的高度问题,使树结构更加**矮胖**使得一次IO能加载更多关键字对比在内存中完成减少了磁盘IO次数更适用于大型数据库但是为了保持自平衡插入或者删除元素都会导致节点发生裂变反应有时候会非常麻烦。
![索引结构-B-Tree](images/Database/索引结构-B-Tree.png)
**案例分析**模拟下查找key为29的data的过程
### Hash
- 根据根结点指针读取文件目录的根磁盘块1。【磁盘IO操作第**1次**】
- 磁盘块1存储1735和三个指针数据。我们发现17<29<35p2
- 根据p2指针我们定位并读取磁盘块3。【磁盘IO操作**2次**】
- 磁盘块3存储2630和三个指针数据。我们发现26<29<30p2
- 根据p2指针我们定位并读取磁盘块8。【磁盘IO操作**3次**】
- 磁盘块8中存储2829。我们找到29获取29所对应的数据data
**原理**
1. 事先将索引通过 hash算法后得到的hash值(即磁盘文件指针存到hash表中
2. 在进行查询时将索引通过hash算法得到hash值与hash表中的hash值比对。通过磁盘文件指针只要**一次磁盘IO**就能找到要的值
例如在第一个表中要查找col=6的值。hash(6) 得到值比对hash表就能得到89。性能非常高。
**存在问题**
但是hash表索引存在问题如果要查询带范围的条件时hash索引就歇菜了。
```mysql
select *from t where col1>=6;
```
### B-Tree
**特点**
B-Tree索引能很好解决红黑树中遗留的高度问题B-Tree 是一种平衡的多路查找又称排序在文件系统中和数据库系统有所应用主要用作文件的索引其中的B就表示平衡Balance
为了描述B-Tree首先定义一条数据记录为一个二元组 [key, data]key为记录的键值key对于不同数据记录key是互不相同的**data为数据记录除以key外的数据 (这里指的是聚集索引)**。那么B-Tree是满足下列条件的数据结构
1. d 为大于1的一个正整数称为BTree的度
2. h为一个正整数称为BTree的高度
3. key和指针互相间隔节点两端是指针
4. 叶子节点具有相同的深度,叶子节点的指针为空,节点中数据索引(下图中的key)从左往右递增排列
- **对范围查找没有更简单的方法**。可以用B+Tree解决
**说明**:下图都是以主键索引为例,至于非主键索引(非聚集索引)无非就是data里存的内容不同。
- **每行数据量很大时会导致B-Tree深度较大进而影响查询效率**。可以用B+Tree解决
![索引结构-B-Tree指针](images/Database/索引结构-B-Tree指针.png)
![索引结构-B-Tree](images/Database/索引结构-B-Tree.png)
**分析**
模拟下查找key为29的data的过程
### B+Tree索引
1. 根据根结点指针读取文件目录的根磁盘块1。【磁盘IO操作第**1次**】
2. 磁盘块1存储1735和三个指针数据。我们发现17<29<35p2
3. 根据p2指针我们定位并读取磁盘块3。【磁盘IO操作**2次**】
4. 磁盘块3存储2630和三个指针数据。我们发现26<29<30p2
5. 根据p2指针我们定位并读取磁盘块8。【磁盘IO操作**3次**】
6. 磁盘块8中存储2829。我们找到29获取29所对应的数据data
**存在问题**
1. 比如,下面查询语句,那么不但需要叶子节点>20的值也需要非叶子节点在右边节点的值。即下图画圈的两部分 **B-Tree似乎在范围查找没有更简便的方法为了解决这一问题。我们可以用B+Tree。**
```mysql
select *from t where col1 > 20;
```
![索引结构-B-Tree](images/Database/索引结构-B-Tree问题.png)
2. 深度问题
从图上可以看到每个节点中不仅包含数据的key值还有data值。而每一个节点的存储空间是有限的(mysql默认设置一个节点的大小为16K)如果data中存放的数据较大时将会导致每个节点即一个页能存储的key的数量索引的数量很小所以当数据量很多且每行数据量很大的时候同样会导致B-Tree的深度较大增大查询时的磁盘I/O次数进而影响查询效率。所以引入B+Tree
B+树是B-树的变体也是一种多路搜索树。其定义基本与B-树相同,除了:
- 非叶子结点的子树指针与关键字个数相同
- 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树B-树是开区间)
- 为所有叶子结点增加一个链指针
- 所有关键字都在叶子结点出现
![索引结构-B+Tree](images/Database/索引结构-B+Tree.png)
### B+Tree
**剖析**如上图在叶子节点上注意是MySQL已经有成双向箭头原生B+Tree是单向的而且从左到右是递增顺序的所以很好的解决了 > 和 < 这类查找问题。
**特点**
B+的搜索与B-树也基本相同区别是B+树只有达到叶子结点才命中B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找。
`B+Tree`是在`B-Tree`基础上的一种优化使其更适合实现外存储索引结构。在B+Tree中所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上而非叶子节点上只存储key值信息这样可以大大加大每个节点存储的key值数量降低B+Tree的高度。
B+的特性:
1. 非叶子节点不存储data只存储索引可以存放更多索引
2. 叶子节点不存储指针
3. 顺序访问指针,提高区间访问性能
4. 非叶子节点中的索引最终还是会在叶子节点上存储一份,也就是叶子节点会包含非叶子节点上的所有索引
5. 一个父节点,它的**左侧**子节点都**小于**父节点的值,**右侧**的子节点都**大于等于**父节点的值
6. 每一层节点从左往右都是递增排列,无论是数值型还是字符型
- 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的
- 不可能在非叶子结点命中
- 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层
- 更适合文件索引系统
**注意**MySQL索引默认的存储结构使用的就是B+Tree。
**![索引结构-B+Tree指针](images/Database/索引结构-B+Tree指针.png)**
![索引结构-B+Tree](images/Database/索引结构-B+Tree.png)
**优点**
**剖析**如上图在叶子节点上注意是MySQL已经有成双向箭头原生B+Tree是单向的而且从左到右是递增顺序的所以很好的解决了 > 和 < 这类查找问题。
- 单次请求涉及的磁盘IO次数少出度d大且非叶子节点不包含表数据树的高度小
- 查询效率稳定(任何关键字的查询必须走从根结点到叶子结点,查询路径长度相同)
- 遍历效率高(从符合条件的某个叶子节点开始遍历即可)
**分析**
**缺点**
假如:**以一个高度为3的B+Tree为例**B+Tree的表都存满了能存储多少数据
B+树最大的性能问题在于会产生大量的随机IO主要存在以下两种情况
**首先,**查看MySQL默认一个节点页的大小
- 主键不是有序递增的,导致每次插入数据产生大量的数据迁移和空间碎片
- 即使主键是有序递增的,大量写请求的分布仍是随机的
```mysql
SHOW GLOBAL STATUS like 'Innodb_page_size';
```
如下图大小为16K。
![索引结构-B+Tree案例](images/Database/索引结构-B+Tree案例.png)
### B*Tree
然后假设主键Id为bigint类型那么长度就是8B指针在Innodb源码中大小为6B所以一共就是14B再假设最后一层存放的数据data为1k 大小(能存很多内容了),那么:
是B+树的变体在B+树的非根和非叶子结点再增加指向兄弟的指针;
1. 第一层最大节点数为: 16k / (8B + 6B) = 1170 (个)
2. 第二层最大节点数也应为1170个
3. 第三层最大节点数为16k / 1k = 16 (个)
![索引机制-B星树](images/Database/索引机制-B星树.jpg)
一张B+Tree的表最多存放 1170 * 1170 * 16 = 21902400 ≈ 2千万。所以通过分析我们可以得出B+Tree结构的表可以容纳千万数据量的查询。而且**一般来说MySQL会把 B+Tree 根节点放在内存中**,那只需要**两次磁盘IO第二层1次第三层1次**就行
B*树定义了非叶子结点关键字个数至少为(2/3)M即块的最低使用率为2/3代替B+树的1/2
**扩展**
**B+树的分裂**
数据库中的B+Tree索引可以分为聚集索引clustered index也叫主键索引和辅助索引secondary index也叫非聚集索引。上面的B+Tree示例图在数据库中的实现对应的是聚集索引聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据(除主键以外的所有数据)辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据而是存储相应行数据的对应的聚集索引键即主键。当通过辅助索引来查询数据时InnoDB存储引擎会遍历辅助索引找到主键然后再通过主键在聚集索引中找到完整的行记录数据
当一个结点满时分配一个新的结点并将原结点中1/2的数据复制到新结点最后在父结点中增加新结点的指针B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针
**B*树的分裂**
当一个结点满时如果它的下一个兄弟结点未满那么将一部分数据移到兄弟结点中再在原结点插入关键字最后修改父结点中兄弟结点的关键字因为兄弟结点的关键字范围改变了如果兄弟也满了则在原结点与兄弟结点之间增加新结点并各复制1/3的数据到新结点最后在父结点增加新结点的指针所以B*树分配新结点的概率比B+树要低,空间使用率更高。
@ -2282,7 +2183,7 @@ redo log 实际的触发 fsync 操作写盘包含以下几个场景:
# SQL优化
## SQL优化步骤
## 优化步骤
**第1步通过慢查日志等定位那些执行效率较低的SQL语句**
**第2步explain分析SQL的执行计划**

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Loading…
Cancel
Save