MySQL

Mysql

SQL执行过程

MySQL被分为两个部分引擎层负责执行具体的SQL数据,Server层提供MySQL剩余部分,引擎层会提供各种接口来让Server层的执行器进行调用以返回所需要数据。
客户端 --> 连接器 --> 分析器 --> 优化器 --> 执行器 --> 存储引擎 --> 查询缓存

  • Server层
    • 连接器:(管理连接,权限验证);
    • 查询缓存:(缓存SQL结果) 大部分都不建议使用,对文本修改不敏感,8.0之后彻底取消;
    • 分析器:(词法分析,语法分析) “SQL syntax”报错就是在该层报出;
    • 优化器:(生成执行计划,索引选择)优化查询条件优先使用索引字段进行查询范围的缩小;
    • 执行器:(操作引擎,返回结果)具体的执行操作,首先进行权限判断,其次根据表引擎来调用相应的接口
  • 引擎层
    • 存储引擎:(存储数据,提供读写接口)

MySQL日志

引擎层:

redo Log

MySQL在更新数据时逐条更新写磁盘会造成会提高IO成本以及磁盘压力,为了解决此现象引进redo log(WAL技术–>Write-Ahead Logging),WAL关键在于
InnoDB会先写日志再写磁盘,定时批量读写磁盘。InnoDB的redo log是固定大小,可以配置my.conf文件的组数和大小,每组文件环式结构从头写到末尾又重新到
头开始写。在写满后会进行擦除(更新磁盘),有了redo log 可以有效防止数据库异常宕机重启(crash-safe)

Service层

bin Log(归档日志),所有引擎都可以使用,主要用途与归当。
不同点:

1.redo log 是InnoDB引擎特有的,bin log是serverc层实现的所有引擎都可以使用;
2.redo log 是物理日志,记录是在某个表上修改了什么数据,bin log记录的是执行的SQL语句;
3.redo log 是循环写的,空间有固定的的大小,bin log 可以追加写,再文件大小达到阈值后会自动切换到下一个文件而之前的bin log文件不会进行覆盖。

执行SQL日志流程

update *** set *** where id = 2
1.执行器先找引擎ID=2这一行,如果这行数据就在内存中直接返回否者从磁盘中读入内存再返回;
2.执行器拿到引擎给的数据,将对应更新字段更新再调用引擎接口写入这行新数据;
3.引擎更新这行数据到内存中,同时将更新操作记录redo log,此时redo log 处于prepare状态,然后告知执行器已经完成SQL执行;
4.执行器记录已经执行的SQL语句,并将bin log写入磁盘;
5.执行器调用引擎提交事务接口,引擎将写入的redo log改成提交(commit)状态。

事务

隔离性

1.隔离级别

读未提交 < 读提交 < 可重复读 < 串心化

  • 读未提交:事务没提交,事务涉及到变更就能被别的事务看到;
  • 读提交:事务提交后其中所涉及的变更才会被别的事务读到;
  • 可重复读: 一个事务执行过程中看到的数据,总是跟这个事务在启动是看到的数据是一致的,在该级别下其他事物也不能看到该事务变更的数据;
  • 串行化:事务所涉及到的行进行操作时,读会加“读锁”写会加“写锁”,当读写发生冲突时必须等前一个事务释放锁后续事务才能继续操作
MySQL数据查询问题:
  • 脏读:事务A读取了另一个事务B修改的数据,但是事务B还未提交,假设事务B回滚那么事务A的数据就是未提交数据。只有在隔离级别是读未提交时会出现。
  • 不可重复读:事务A先进行查询,事务B再取更新该行数据并提交事务,那事务A所使用的数据就被事务B修改,造成事务A不可预知的后果。
  • 幻读:在可重复读隔离级别下,事务A查询数据,事务B进行插入/删除数据,当事务A再去查询时被插入/删除的数据不会被感知到所以出现幻读。
    隔离级别问题
2.隔离性的实现
可重复读

mysql在每条记录更新时同时会记录一套回滚操作(正常修改1->2,回滚记录2->1),更新事务(1->2->3->4)在不同时刻启动的事务都会有不同的视图,这就意味着同一条记录在同一系统中相同时间会存在多个版本(MVCC).

开启事务

begin(开启) ; commit(提交); rollback(回滚); 但是这种开启方式是等到一个操作表语句的时候事务才会真正启动,实质上这种启动方式是在执行第一个快照语句才能获取到一致性视图
start transaction with consistent snapshot(开启); commit(提交); 执行完该条命令就可以获取到一致性视图。

MVCC 中快照的实现

InnoDB里面每个事务都存在由事务系统分发的transaction id作为事务的唯一ID,并且严格按照申请顺序递增。每行数据有多个版本,每次事务更新数据时会生成一个新的数据版本,并且把当前事务的transaction id作为这个数据版本的事务ID记为 row_trx_id,同时旧的数据版本也要保留。row_trx_id会逐渐递增这样就能保证该事务可以读到最新的回滚日志和数据版本。而新事物修改数据产生新的数据版本的时候InnoDB会根据两次数据版本的不同计算出undo log并保存。
InnoDb会为每一个事物构造一个数组来保存这个事务启动瞬间当前”活跃“(启动还没提交)的所有事务ID,这个数据里面ID最小就是这个事务的低水位也就是当前数据的倒数第二低版本,最高的ID就是最新启动的事务,高低水位构成了当前事务的一致性视图。在事务启动构成视图数据的时候,一个数据版本的row_trx_id大致会出现三种情况:(判断高低水位线非常非常重要用来判断当前事务可读数据上下限)
(1)事务启动时,其他相关事务都已经提交,说明这个时候的数据版本是最新即数据是可见的;
(2)事务启动时,其他相关事务还未开始(begin之后是不并不是立马启动,直到有语句还是执行事务才会真正的启动),表示这个版本由将来启动的事务生成的,数据不可见;
(3)事务启动时,有其他相关事务正在执行还未提交:a.该数据现在的row_trx_id在数组中,表示生成这个版本的事务还没有提交。 b.若tow_trx_id 不在数据中,证明这个数据版本已经没其他事务正在操作了,数据可见。

PPS 例子(可重复读情况下):

事务执行顺序
A先开始->B在开始->C开始更新->B开始更新->A开始查询并且提交事务->B提交事务

  1. 事务 A 开始前,系统里面只有一个活跃事务 ID 是 99;
  2. 事务 A、B、C 的版本号分别是 100、101、102,且当前系统里只有这四个事务;
  3. 三个事务开始前,(1,1)这一行数据的 row trx_id 是 90。
    这样,事务 A 的视图数组就是 [99,100], 事务 B 的视图数组是 [99,100,101], 事务 C 的视 图数组是 [99,100,101,102]。
更新逻辑:(先读后写,读当前的值称为当前读)

对于上面的例子对于C事务在执行完就立刻提交了所以再B进行更新的时候可以读取到该行数据最新版本,从而可以准确更新而不会是C事务丢失。除了update语句外还有select语句如果加锁也是当前读,所以对于事务A加上lock in share mode 或者for update 都会获取到最新的数据。但是假设如果C事务在更新完并没有立刻提交而是延迟到B事务更新完再进行操作,当B再去前读的时候该行数据的最新版本虽然已经生成了但是数据上的写锁还没有是释放所以B事务会被阻塞直到C写锁被释放掉。
事务的可重复读和核心其实就是一致性读(事务进行更新数据的时候只能用用当前读,如果记录被其他事物占用就等待至其他事务释放锁)

索引

索引实现方式

1.哈希表
2.有序数组
3.搜索树

InnoDB的索引模型

在InnoDB中,使用B+树索引模型(B+树详解:https://www.jianshu.com/p/71700a464e97),其中索引大致分为两种,主键索引和非主键索引。逐渐索引中存放的是该行信息
而非主键索引中存放的主要是主键信息,所以在用索引查找的时候首先通过普通索引查找到对应的主键信息再去查找主键索引所构建的树从而获取指定信息。

索引维护

B+树维护索引(叶子结点)有序性时,会在每次更新数据操作时候重新维护主键索引树,在主键索引树的叶子节点上都会维护一份数据页用来保存这行数据,其中如果使用自增主键
则在新增的时候会一定程度上减少维护主键的性能损耗(B+树再维护的时候性能使用的时候会比较稳定)。当增加该表字段后超过叶子节点初始数据页的大小后会开始分裂数据页,同
一行的数据会保存在两个数据页中,但这样整体空间利用率会降低50%。(所以主键长度越小普通索引叶子节点就越小,非主键索引占用空间就越小),由于普通索引在建立树的过程中是
使用本身数据进行排序所以每个节点上会存在该索引数据在写SELECT语句的过程中建议多查索引字段会减少回表过程增加的时间。相比二叉搜索树B+树会大幅度减少磁盘读写平均次数以
及平均搜索时间。 (大量相同的元素怎么排列)

最左前缀原则

在普通索引树中,索引项中的字段顺序是由创建所以时决定的,在进行查询的时候,是从该顺序最左边开始验证(必须得通过第一个索引的验证再能验证后面的联合索引其他字段),所以
再使用和检查联合索引的时总是从联合索引的最左边索引开始进行索引。

失效条件:

1.由于联合索引在范围中只能支持一个,在执行语句时会向右直到遇到第二个索引组内的字段(>,<,between,like)就停止匹配使用单个索引;
2.=和in可以乱序都可以使用组合索引,但是!=和not in会直接使索引失效;
3.where条件中对该索引字段使用函数或者表达式;
4.or同样会使索引失效,除非or的所有条件都是索引;
5.like 以%开头
6.字符串类型的列值查询的时候不用引号会使索引失效。

索引下推

再MySQL 5.6之后引入索引下推优化, 在搜索索引树的过程中会将所以中包含的条件字段先做判断,返回有效的主键ID,在判断非索引字段,这样大大减少回表时间,之前版本再使用完索引后查询出主键ID再去逐个进行其他字段的比较增大回表次数。

组合索引在索引键排序的过程中也是遵循联合索引中的字段顺序,在order by中也是遵循最左原则的

更新索引

因为InnoDB的数据是按照数据页(默认大小16K)为单位来进行读写的,所以在更新的时候大致会分为两种情况:
1.数据页已经被读到内存中:
(1)普通索引:找到目标值位置插入,语句结束。
(2)唯一索引:找到目标值位置,判断是否该位置存在值,插入值,结束语句。
2.数据页没有被读到内存中:
(1)普通索引:更新记录在change buffer中,语句结束。其中change buffer是能被持久化的,change buffer应用到原数据页得到最新结果过程称为merge(原版本数据读入内存中应用
change buffer中的相关变化生成新版本),除了访问这个数据页会出发merger系统有后台线程会定期merge。数据库正常关闭(shutdown)过程中也会执行merge。change buffer是存放
在buffer pool中的所以大小比例只有在数据库初始化时才会被设置的。( innodb_change_buffer_max_size,来设置change buffer占用 buffer pool的内存)
(2)唯一索引: 读数据页到内存中,判断是否有冲突再插入该值。
change buffer 更适用于写多读少的业务场景,触发merge的概率减小不会额外增加数据库维护merge的成本。

索引查询异常

MySQL中Service中的优化器在选择最优执行方案的时候核心思想用最小的代价去执行语句。其中判断最小代价的条件包括扫描行数、是否使用临时表、是否排序(索引字段本来就是有序的)等因素去判断选择什么索引。
扫描行数根据统计信息来估算记录行数,统计信息就是索引的“区分度”,索引上不同的值越多索引区分度(索引上不同值的个数称为基数)越好,使用show index tableName 来查看该表上的所有索引详情,但是基数不
一定准确,基数并不是每条记录被更新时都会进行更新的。InnoDB默认会选择N个数据页来统计这些数据页上面的基数平均值再乘以数据页数就得到索引的基数(所以索引的区分度越高基数越准);
所以在优化SQL的时候可以人为指定所需索引(select * from t index(a) where a …),在优化SQL的时候可以通过analyze命令来修整某一个表内的索引基数(索引基数更新频率慢所以需要人为优化看到真实基数)。

全局锁和表锁

MySQL 锁类型

全局锁:

数据库加锁,Flush tables with read lock(只读),释放锁 unlock tables;
使用一般用于全库备份,但是Mysql可以通过自带的备份工具MySQL dump来实现备份,当MySQL dump使用参数-single-transaction时候导入数据之前就会启动一个事务来获取一致性视图。set global readonly=true 可以产生
同样的效果,是的整个数据库处于只读状态,但是备份时不建议选这种方式,一般该字段用来区分主从库,而且该参数不能自动打断或者释放,FTWRL可以通过Exception来实现中断。

表锁:

有两种表级别锁:表锁(加锁 lock tables… read/write,该语句会限制所有线程对该表的操作包括本身线程)、元数据锁(MDL 不需要显示使用,SQL语句执行的时候会自动附加上),在update的时候如果where条件索引失效会将行锁升级为表锁。

行锁

InnoDB事务中,行锁是在需要的时候才加上,但不是不需要就立刻释放而是等到事务结束才会释放锁–两阶段锁协议

死锁和死锁检测

1.设置innodb_lock_wait_timeout来设置锁等待时间
2.发起死锁检测,设置innodb_deadlock_detect = on 来开启死锁检测,当检测到死锁后会主动回滚死锁中的某一个事务

悲观锁:

假设每次获取资源都会有人进行修改或者占用,所以再获取的资源的时候都会加上锁直到使用完成后才会释放锁。共享资源同时只能有一个线程使用直到释放锁,MySQL中行锁,表锁,读锁,写锁都是使用的悲观锁

乐观锁:

假设每次获取资源的时候都没有人进行占用,所以乐观锁再获取资源的时候不会上锁,只有在更新的时候惠佳谁去判断该资源是否有修改过(比较和替换是一个原子操作)。其中乐观锁使用版本号机制和CAS算法实现,其中版本号在每次获取资源的时候会一起获取到,当修改的时候会将公共资源的版本号加一(Elastic中元数据组中version使用相同的思想),在更新的时候还会检查版本号若是不相等拒绝修改。CAS算法(compare and swap)无锁算法,其中涉及三个操作数,(需要读写的内存值V,进行比较的值A,拟写入的新值B),有且仅当V等于A时CAS通过原子操作用B来更新V否则会进行自旋操作(不断进行重试)。

乐观锁缺点
1.ABA问题

如果V的初始值是A并且在准备赋值的时候检查到它仍是A但是中间有其他的线程修改过A最后由恢复修改回来,所以A并不是没有进行过修改。

2.循环时间开销大

不断进行自旋操作会增加CPU占用时间

3.只能保证一个共享变量的原子操作

CAS只对单个共享变量有效,当操作涉及多个共享变量的时候CAS无效。

间隙锁

本质上也是排它锁,为了防止出现幻读情况。间隙锁是在事务查询数据的过程中将查询到的数据前后间隙进行加锁防止前后间隙出现插入或者删除的情况。这时如果有其他其他事务在该数据相邻间隙插入数据就会产生幻读所以在事务A读取数据的过程中就会对被查询的数据相邻的间隙(相同列数据相邻不同数据之前可插入的部分称为间隙)加锁。
间隙锁添加的条件:

  • 必须在RR隔离级别下
  • 检索条件必须有索引(否则会遍历全表造成全表的加锁)
    间隙锁
意向锁

本质上是表锁,为了解决表锁和行锁冲突问。在事务A加完行锁后但还未释放,事务B要对该行所在的表进行加表锁,而意向锁是可以让表锁快速遍历到该表上是否存在行锁,如果存在先暂时阻塞。
所以Mysql在申请数据库的行锁时会先申请该表的意向锁(排他,共享),再去申请该表或者行锁。
行锁

内存管理

MySQL中为了减少读取磁盘次数造成IO压力,实际上会在内存中缓存以数据页为单位的磁盘数据,但是只有在实际查询的时才会将磁盘中的数据页读取到内存中(数据页至少包含两行实际记录),当内存中的数据页和磁盘中数据页不一致的时候内存中的数据页称为脏页;内存中数据页写入磁盘中,数据保持了一致称该数据页为干净页。内存数据写入进磁盘的过程叫做flush。

刷新数据页条件

1.InnoDB 的redo log 写满了,这时系统会停止所有更新操作,推进redo log中checkpoint的位置,在推进过程中也会不断的将推进redo log中的脏页数据刷新至磁盘中。
2.内存满了,内存中在select的时候会去将数据页读取到内存中以减少IO压力,但是这样就会导致很多无用或者很久之前用的数据停留在内存中,从而导致内存溢出,所以该场景就是在有大量数据缓存在内存中时无法再提供新数据页就会淘汰脏页,将内存中的数据写到磁盘中。
3.系统空闲自动刷新。
4.系统shutdown。

性能影响对比

1.redo log写满要Flush脏页时,整个系统不再接受更新知道刷完checkpoint推进过程中的脏页为止。这个过程是InnoDB极力避免的。
2.这种情况下是系统的常态,InnoDB 使用Buffer pool管理内存,缓存池中的内存存在三种状态:未使用,使用并干净,使用并脏页,所以为了控制内存不足,InnoDB会控制内存中脏页的比例。

InnoDB 刷脏页控制策略

可以通过控制数据库表参数innodb_io_capacity 来控制刷新脏页IO效率,默认是75%

初始化例子:

一个内存配置为 128GB、innodb_io_capacity 设置为 20000 的大规格实例,正常会建议 你将 redo log 设置成 4 个 1GB 的文件。脏页不保存在redo log中。

MySQL 数据存储

InnoDB中的数据5.6以后默认单独存放成.ibd文件,但是允许由innodb_file_per_table来控制存放位置。该开关OFF则表数据存放在系统共享空间中(数据字典存放的位置),再删除数据后该表所占空的空间不会进行回收;该开关ON单独存放成一个文件。

数据删除流程

table里的数据实际上是以包含主键为节点的一棵B+树中,其中每个叶子节点都一个数据页,其中最少包含两行记录。
所以再删除数据的过程中实际上就是删除主键索引树中的叶子节点内的行数据,但是删除某一行数据后若该数据页内的数据页没有清空,该位置的记录可以被复用,当数据页整个被清空了则该数据页可以被复用,当相邻数据页的利用率很低时会合并数据页(B+数节点数据过多时会分裂数据页并且重新维护B+树),所以delete命令把记录的位置或者数据页标为可复用但是磁盘文件大小是不会变的,delete命令不能回收空间只会造成B+树空洞。

重建表

做表的迁移会是空间收缩,5.5之前可以使用 alter table A engine=InnoDB来重建表(实质上是创建一张临时表B再一条条冲A表读取并插入到B中),但是在5.5之后引进Online DDL对重建表进行了优化,而Online DDL过程大致如下:
1.建立一个临时文件(临时文件存放于tmp_table由server层创建),扫描表A主键所有的数据页;
2.用数据页A中的记录生成主键索引B+树,存储到临时文件中;
3.生成临时文件的过程中,将所有对A的操作记录日志文件(row log)中;
4.临时文件生成后,将日志中的操作应用到临时文件中,得到一个逻辑数据上与表A线沟通呢的数据文件;
5.临时文件替换表A文件。
对于重建表的不同算法来说(online,inplace,copy)都是需要去创建临时文件的,但是临时文件的存放位置和执行过程就会对系统造成不同程度性能损耗。

  • copy:临时表是由server层创建,而且在复制的过程中不能对表进行任务修改操作
  • online:相同模式的建立临时表,但是建立临时表的过程中会记录该表的修改操作,最后再进行应用就得到一张在不阻塞更新的前提下的一张新表
  • inplace:临时文件存放于InnoDB中,整个DDL都在InooDB中完成,减少了数据迁移(server->InnoDB)
    修改表的算法指定(alter table t engine=innodb,ALGORITHM=copy),DDL过程如果是Online的就一定是Inplace的,如果Inplace的DDL有可能不是onlined的,8.0之后添加全文索引和空间索引就是后者。

group by 排序算法

范例SQL:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`city` varchar(16) NOT NULL,
`name` varchar(16) NOT NULL,
`age` int(11) NOT NULL,
`addr` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `city` (`city`)
) ENGINE=InnoDB;

查询语句:
select city,name,age from t where city='杭州' order by name limit 1000  ;

全字段排序

通过执行计划可以看出SQL的执行时的优化器为SQL所做出的选择。Extra字段下可以通过描述看出是否需要排序,
上面范例查询语句中:
1、首选会初始化sort_buffer,确定防御name、city、age三个字段;
2、从索引树中查找出主键ID;
3、从主键ID索引树中取出对应ID的三个字段放入sort_buffer(这阵缓存的数据是按照city进行排序的,相对name是乱序);
4、将sort_buffer中的数据进行排序;
5、向执行器返回所需数据结果。

注意

因为sort_buffer大小有限(sort_buffer_size参数来设置排序缓存的大小)所以在所选字段比较多的情况或者字段太长的情况下就需要额外的磁盘临时文件(tmp)来支持排序。外部排序一
般使用归并排序将分成若 干分的数据文件(sort_buffer_size越小分成的文件数量越多)进行排序生成汇总一个有序的文件。

rowid排序

当单行大小超过阈值时(max_length_for_sort_data 控制启用rowid算法的启用大小),更换算法不在将所选字段全部放进内存中进行排序,而是将排序字段和主键ID放入sort_buffer中进行排序:
1、初始化sort_buffer,确认只放入排序字段和主键ID;
2、通过查询条件查询出满足条件的主键ID和所选字段放入sotr_buffer中;
3、sort_buffer排序;
4、取满足条件的行数再到主键索引树中取出所查询的字段;

PS

Mysql的设计思想就是如果内存大小够尽量使用内存,尽量减少磁盘访问减少IO压力,但是使用联合索引会对固定业务的排序有很大的优化,因为联合索引是针对固定字段的顺序,所以对多字段排序可以省去
sort_buffer排序的步骤和回表的步骤。

order by rang()排序

对于取随机数Mysql需要使用临时表来进行排序和取数,(执行计划中 extra字段显示 Using temporary表示使用临时表,但是临时表分为内存和磁盘下面会详细说明):

内存临时表(可以理解为内存数组,因为就两列,R值可以看成下标):

1、先在内存中创建一个使用memory引擎的临时表,该表只有两个字段double的R(实际存放小于一的随机数)、varchar字段的W(存放该行数据word);
2、从目标表中扫描出符合条件的数据行,生成一个0<random<1的R值存入临时表;
3、按照R值对临时表进行排序;
4、初始化sort_buffer,只放入两个字段R值和位置信息(rowid(系统自动生成长度为6字节的数据)或者主键);
5、取出临时表内的数据存放到sort_buffer中;
6、sort_buffer按照R字段排序;
7、取出sort_buffer中前三个位置信息以次到内存表中取出word值返给客户端。

磁盘临时表

理解不了 待续 17

SQL 执行效率

在where条件中虽然目标表可能已经创建了索引但是因为SQL语句写法存在问题导致优化器并不能准确的使用更高效的索引搜索而去扫描全表大大降低
效率。对索引字段做韩束操作可能会破坏优化器对于索引树的搜索,隐式类型转换、隐式字符编码转换都属于函数操作(CAST()、CONVERT())。

隐式类型转换

在字符串和数字类型作比较的时候会把字符串转换成数字类型进行比较,所以在转换时不要使用驱动表字段进行转换尽量使用条件进行函数操作。

隐式字符编码转换

在不同表之间进行连表查询时因为不同表之间的编码格式不同所以在where条件查询的时候会进行字符集的转换,所以在查询的时候原则上尽量不要在
被驱动表上的字段进行函数操作,将条件转换成目标格式。
MySQL的优化器不会对隐式进行语句重写,例如:where id +=10 查询id=9时也不会使用索引。

SQL 分析

show processlist

如果有SUPER权限可以看到所有线程,否则只能看到自己发起的线程。其中各个字段解释:

  • id: 线程ID
  • user: 线程发起用户名称
  • host: 线程发起的IP和端口
  • db: 线程连接的数据库
  • command: 当前连接执行的命令 sleep(资源未释放,但连接没有中断),query,connect
  • time: 状态持续时间,单位:秒
  • state: https://blog.csdn.net/dhfzhishi/article/details/81263084(状态,基本直译)
  • info: 当前线程执行的SQL语句
    用 show processlist 可以看到当前所有的线程执行情况,但是无法知道具体是哪个线程发起的资源占用也就无法杀死线程疏通堵塞,需要查看sys.schema_table_lock_waits表查看造成阻塞的process_id(select blocking_pid from sys.schema_table_lock_waits ),再直接kill进程。

mysql 锁

BST和RBT区别

二叉树搜索树:左小右大 最差情况树高n,平均Log(n)
红黑树(平衡二叉树):任何一个节点都有颜色(黑或红),根节点是黑色,父子节点之间不能出现连续两个红节点,任何一个根节点遍历到他的子孙节点经过的黑节点数量必须相同,空节点被认为黑色节点。
左旋
左旋图解(x左边节点逆时针旋转):
a
/ \
b x -> x
/ \ /
c d a d
/ \
b c
如图以x为中心,其父节点为a,左边兄弟节点为b,左子节点为c,右子节点为d

左旋就是保证x和右子节点d不变,逆时针旋转与x节点的直接相关的左边节点,也就是a,b,c
左旋的过程是将x的父节点a,左边兄弟节点b 逆时针旋转,
原来的x左节点c被x的父节点替代,
原来x的左子节点c逆时针方向平移后变成x的原父节点a的右子节点。

右旋
右旋图解(x右边节点顺时针旋转):
a
/ \
x b -> x
/ \ / \
d c d a
/
c b

聚簇索引

聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据,不仅保存主键值还保存该行数据还保存事务ID,以及事务版本信息等等。
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行.

B-树和B+树

https://blog.csdn.net/u013411246/article/details/81088914
b+树相比于b树的查询优势:
b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢);
对于范围查找来说,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历,如下两图:

###死锁
死锁案例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值