深入了解JUC并发编程+集合(已写)
深入了解JVM(JDK8)(已写)
深入了解Redis(已写)
深入了解MySQL(已写)
深入了解Spring(未写)
深入了解MyBatis(未写)
深入了解SpringMVC(未写)
深入了解SpringBoot(未写)
深入了解zookeeper(未写)
深入了解Dubbo(未写)
深入了解RabbitMQ(未写)
深入了解RcoketMQ(未写)
深入了解kafka(未写)
深入了解ElasticSearch(未写)
深入了解ShardingSphere(未写)
深入了解MongoDB(未写)
深入了解Netty(未写)
深入了解Eureka(未写)
深入了解OpenFeign(未写)
深入了解Nacos(未写)
深入了解Ribbon(未写)
深入了解Sentinel(未写)
深入了解Seata(未写)
深入了解GateWay(未写)
索引是帮助MySQL高效获取数据的排好序的数据结构,类似于我们字典上的目录或者看书的目录一样,需要占据内存空间。
优点:
- 可以以加快查询速度,提高系统性能
缺点:
- 创建与维护索引需要浪费时间,对数据进行增删改时,索引也要动态的维护,会降低增/改/删的执行效率
- 占据物理空间
主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
- ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
- 可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引
普通索引:基本的索引类型,没有唯一性的限制,允许为NULL值。
- ALTER TABLE table_name ADD INDEX index_name (column); 创建普通索引
- ALTER TABLE table_name ADD INDEX index_name(column1,column2, column3); 创建组合索引
全文索引:目前搜索引擎使用的一种关键技术
- ALTER TABLE table_name ADD FULLTEXT (column); 创建全文索引
- n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
- 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
- 所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
- B+ 树中,数据对象的插入和删除仅在叶节点上进行。
- B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点
1、B+
2、Hash
两者比较:Hash索引精确查询快,但是范围查询不行,还有模糊查询也不行,而B+树确可以支持范围查询和模糊查询等
创建索引的原则(重中之重)
1、全值匹配
2、最左前缀法则
3、不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
4、存储引擎不能使用索引中范围条件右边的列
5、尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少 select * 语句
6、.mysql在使用不等于(!=或者<>),not in ,not exists 的时候无法使用索引会导致全表扫描< 小于、 > 大于、 <=、>= 这些,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引
7、is null,is not null 一般情况下也无法使用索引
8、like以通配符开头(’$abc…’)mysql索引失效会变成全表扫描操作
9、字符串不加单引号索引失效
10、少用or或in,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引
11、范围查询优化总结:
创建
1、create table的时候
2、create index
3、alter index
删除
1、根据索引名删除普通索引、唯一索引、全文索引: alter table 表名 drop KEY 索引名
2、删除主键索引: alter table 表名 drop primary key (因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引),需要取消自增长再行删除:
alter table user_index MODIFY id int,drop PRIMAR YKEY
1、非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值
2、取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高
3、索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高
通过索引查询数据比全表扫描要快。
缺点:
- 索引需要空间来存储,也需要定期维护(增删改)
- 使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
- 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
- 基于非唯一性索引的检索
1、删索引
2、删无用的数据
3、重建索引
语法:
index(field(10)) ,使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。
前提:
前缀的标识度高。比如密码就适合建立前缀索引,因为密码几乎各不相同。
实操的难度:
在于前缀截取的长度。
我们可以利用 select count(*)/count(distinct left(password,prefixLen)); ,通过从调整prefixLen 的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前 prefixLen 个字符几乎能确定唯一一条记录
就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
B树:内部节点与叶子节点都可以存key和value
B+树:只有叶子节点都可以存key和value,内部节点只有key,并且叶子节点之间是一条双链表连接着
树可以在内部节点同时存储键和值,因此,把频繁访问的数据放在靠近根节点的地方将会大大提高热点数据的查询效率。这种特性使得B树在特定数据重复多次查询的场景中更加高效
由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。 B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间
底层实现原理:
hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应
的键值,之后进行回表查询获得实际数据。B+树底层实现是多路平衡查找树。对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。
区别:
1、hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询。因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。2、B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围。hash索引不支持使用索引进行排序,原理同上。
3、hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。
4、hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
5、hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。
6、因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度。而不需要使用hash索引。
1、B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
2、B+树空间利用率高,可减少IO次数,磁盘读写代价更低。一般来说,索引本、身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素
3、B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
4、B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
聚簇索引(innodb):数据存储(value)和索引(key)放在叶子节点上,找到索引就找到数据,底层文件为:frm,idb
非聚簇索引:数据存储与索引分开,索引存储的是地址,然后根据地址去文件中找数据(索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因),底层文件为:frm,myd,myi
不一定,如果查询语句中的所有字段都全部命中索引,那么就不会回表查询。比如: select age from employee where age < 30 的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。
联合索引:MySQL可以使用多个字段同时建立一个索引
在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。
MySQL使用索引时需要索引有序,假设我们在“a,b,c”的联合索引,那么索引的排序为:先按照a排序,如果a相同,则按照b排序,如果b也相同,则会按照c相同。
- 一致性
- 节省存储空间
- 防止回表
- 是否支持行级锁 : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
- 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原子性,其执行速度比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
- 是否支持外键: MyISAM不支持,而InnoDB支持。
- 是否支持MVCC :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在
READ COMMITTED
和REPEATABLE READ
两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一
1、InnoDB索引是聚簇索引(叶子节点有数据),MyISAM索引是非聚簇索引(叶子节点是地址)。
2、InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
3、MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
4、InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。
Insert Buffer是物理页 的一个组成部分,而不是缓冲池的一个部分。
在InnoDB存储引擎中,主键是行唯一的标识符。通常行记录的插入是按照主键递增的顺序进行插入的,所以Primary Key一般是顺序的。 并不是所有的主键的插入都是顺序的,例如主键类是UUID时。
一张表在拥有一个聚集索引(Primary Key)的同时,可以拥有多个非聚集的辅助索引(在聚簇索引之上创建的索引称之为辅助索引)(secondary index),在进行插入操作时,数据页的存放还是按照主键进行顺序存放,但是对于非聚集索引 叶子节点的插入不再是顺序的了,而是离散地访问非聚集索引页,由于随机读取导致插入操作性能下降。(B+树的特性决定了非聚集索引插入的离散性)
对于非聚集索引的插入(更新)过程:
判断缓冲池中是否存在插入的索引页,有则插入,否则步骤2
先将索引页放到一个Insert Buffer对象中,然后以一定的频率和情况 将 Insert Buffer和辅助索引页子节点 merge(合并),这样能够把多个插入合并到一个操作中,大大提高了插入的性能。
Insert Buffer的使用必须同时满足两个条件:
索引是辅助索引;
索引不是唯一的。(因为在插入缓冲时,数据库不会去查找索引页,来判断插入的记录的唯一性。如果需要查找唯一性的话就会遇到离散读取的情况,和不使用Insert Buffer的没什么两样)
Insert Buffer存在的问题:在写密集的情况下,插入缓冲会占用过多的缓冲池内(innodb_buffer_pool),默认最大可以占用到1/2的缓冲池内存。可以修改IBUF_POOL_PER_MAX_SIZE的值为x, 代表最大只能使用缓冲池内存的1/x。
Insert Buffer的升级,InnoDB存储引擎可以对DML操作——INSERT、DELETE、UPDATE都进行缓冲,它们分别时是:Insert Buffer、Delete Buffer、Purge Buffer。
Change Buffer使用的对象:非唯一的辅助索引。
对一条记录的UPDATE操作可能分为两个过程:
1、将记录标记为已删除;( Delete Buffer )
2、真正将记录删除。 ( Purge Buffer )
InnoDB存储引擎提供了参数
innodb_change_bufferings
来开启各种Buffer的选项(inserts, deletes, purges, changes(inserts + deletes), all, none,)。
innodb_change_buffer_max_size
控制Change Buffer最大使用内存容量,默认值为25,表示最多使用25%,最大有效值为50!SHOW VARIABLES LIKE 'innodb_change_buffer_max_size'\G ;
Insert Buffer的数据结构是一颗B+树,负责对所有的表的辅助索引进行Insert Buffer 。全局只有一颗B+树,存放在共享表空间,默认是ibdata1中。(试图通过独立表空间ibd文件恢复表中数据时,往往会导致CHECK TABLE失败,因为表的辅助索引中的数据可能还在Insert Buffer中,即共享表空间中,所以通过ibd文件进行恢复后,还需要进行REPAIR TABLE操作来重建表上的所有辅助索引)
Insert Buffer 有叶子节点和非叶子节点组成。非叶子节点存放查询的键值 search key:
space marker offset 4B 1B 4B space: 待插入记录所在表的表空间 id,每个表的 id 唯一,可以通过space id 查询是那张表。
marker:用来兼容老版本Insert Buffer。
offset:表示页所在的偏移量。
将一个辅助索引插入到页(space,offset):如果这个页不在缓冲池中,InnoDB存储引擎构造一个search key,然后查询Insert Buffer这棵树,将这条记录插入到叶子节点中。
Insert Buffer 叶子节点中的记录:
space marker page_no metadata 4B 1B 4B 4B 后面四个格子存放辅助索引的信息。
metadata 字段存储的内容
名 称 字 节 IBUF_REC_OFFSET_COUNT 2 IBUF_REC_OFFSET_TYPE 1 IBUF_REC_OFFSET_FLAGS 1 IBUF_REC_OFFSET_COUNT用来排序每个记录进入Insert Buffer的顺序。
为了保证辅助索引页(space , page_no)中的记录成功插入到树中,还需要一个Insert Buffer Bitmap来标记每个辅助索引页的可用空间。
每个Insert Buffer Bitmap页用来追踪16384个辅助索引页(4bit),也就是256个区(Extent)。
每个辅助索引页在Insert Buffer Bitmap 中存储的信息:
名 称 大小(bit) 说 明 IBUF_BITMAP_FREE 2 表示该辅助索引页中的可用空间数量。可取值为:0 表示无可用剩余空间1 表示剩余空间大于 1/32 页(512字节)2 表示剩余空间大于 1/16 页3 表示剩余空间大于 1/8 页 IBUF_BITMAP_BUFFERED 1 1 表示该辅助索引页有记录被缓存在树中 IBUF_BITMAP_IBUF 1 1 表示该页为Insert Buffer B+树的索引页
决定Insert Buffer中的记录什么时候合并到真正的辅助索引中。一共三种情况:
case 1: 辅助索引页被读到缓冲池时,
case 2: Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间。可用空间 < 1/32 页
case 3: Master Thread
对页面拷贝副本,提高数据页的可靠性,当写失效时,通过副本页还原原页,再进行redu。
doublewrite由 内存中的doublewrite buffer (2MB) 和物理磁盘上共享表空间中连续的128个页,即两个区(2MB)组成
如果建立哈希索引的检索速度比B+树检索的速度快,则建立哈希索引 AHI。
AHI 有如下要求:对页面的访问模式(长训的条件)必须是一样的
哈希索引只能用来等值查询,不能用来做范围查询。
AIO 用户可以连续发送多条IO请求,不需要等待其执行结果,直到所有请求都发送完成了再等待结果。另外可以将多个IO合并成一个IO。
InnoDB提供内核级别的AIO支持,成为Native AIO。启用Native AIO, 恢复速度提升75%。
工作原理:当刷新一个脏页时,InnoDB存储引擎会检测该页所在的区(extent)的所有项,如果有脏页,则一起刷新。
这些特性为InnoDB存储引擎带来了更好的性能、更高的可靠性。
innodb_fast_shutdown 取值为0,1,2 0:MySQL数据库关闭时,InnoDB需要完成所有的full purge 和 merge insert bufer;再将脏页刷新会磁盘。 如果MySQL升级时,必须将这个参数设为0。 1:默认值,不需要完成full purge 和 merge insert bufer,只需将脏页刷新会磁盘。 2:不需要full purge 和 merge insert bufer,也不需要将脏页刷新会磁盘。只要将日志都写入日志文件。 innodb_force_recovery: 0:默认值。当需要恢复时会执行所有的恢复操作,当不能进行有效的恢复时,会把错误写进日志。 1-6:用户可以对表进行select、create、drop操作,但insert、update、delete这类DML操作是不允许的。
例子
use user; drop table if exists student; CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(12) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB CHARSET=utf8mb4; insert into t20(name,age) values ("zhz",1),("hyf",2);
session1 | session2 |
---|---|
set session transaction_isolation=‘READ-COMMITTED’;/* 设置会话隔离级别为 RC*/ | set session transaction_isolation=‘READ-COMMITTED’;/* 设置会话隔离级别为 RC*/ |
select * from user;![]() | |
begin; | |
update user set age=21 where name=“zhz”; | |
begin; | |
select * from user;![]() | |
commit; | |
select * from user;![]() | |
commit; |
在 session1 更新了 name=“zhz” 这行记录,但还没提交的情况下,在 session2 中,满足 name=“zhz” 这条记
录, age 的值还是原始值 1 ,而不是 session 1 更新之后的 21,那么在数据库层面,这是怎么实
现的呢?
其实 InnoDB 就是通过 MVCC 和 UNDO LOG 来实现的。
什么是 MVCC 呢?
MVCC , 即多版本并发控制。 MVCC 的实现,是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不
同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
也就是上面实验第 6 步中,为什么 session2 查询的结果还是 session1 修改之前的记录。
MVCC 只在 RC 和 RR 两个隔离级别下工作。因此在上面的实验中,改成 RR 隔离级别,第 6 步中,得到的结果还是 session1 修改之前的记录(RR的操作有点差别,建议自己尝试)
3.6.1、undolog
undo log 是逻辑日志,将数据库逻辑地恢复到原来的样子,所有修改都被逻辑地取消了。 也就是如果是 insert 操作,其对应的回滚操作就是 delete ; 如果是 delete ,则对应的回滚操作是 insert ; 如果是 update ,则对应的回滚操作是一个反向的 update 操作。 除了回滚操作, undo log 的另一个作用是 MVCC , InnoDB 存储引擎中 MVCC 的实现是通过 undo 来完 成的。当用户读取一行记录时,若该记录已经被其它事务占用,当前事务可以通过 undo log 读取之前的行版 本信息,以此实现非锁定读取。
不连续,在单表的情况下,比如说一个事务,要插入3条数据,分配了自增id 123 ,然后在插入第三条的时候,回滚了 ,这个期间另一个事务 插入另一条数据 ,id值变为4了
,
引用:https://note.youdao.com/ynoteshare1/index.html?id=a6004953a0a7c80073ac74d8e76f1ebd&type=note
下面是本人的公众号:(有兴趣可以扫一下,文章会同步过去)
我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师
https://blog.csdn.net/zhouhengzhe?t=1