索引是存储引擎用于快速找到记录的一种数据结构。可以联想到字典中的目录。
(1) Hash 索引
Hash 索引是比较常见的一种索引,他的单条记录查询的效率很高,时间复杂度为1。但是,Hash索引并不是最常用的数据库索引类型,尤其是我们常用的Mysql Innodb引擎就是不支持hash索引的。主要有以下原因:
Hash索引适合精确查找,但是范围查找不适合,因为存储引擎都会为每一行计算一个hash码,hash码都是比较小的,并且不同键值行的hash码通常是不一样的,hash索引中存储的就是Hash码,hash 码彼此之间是没有规律的;
且 Hash 操作并不能保证顺序性,所以值相近的两个数据,Hash值相差很远,被分到不同的桶中。这就是为什么hash索引只能进行全职匹配的查询,因为只有这样,hash码才能够匹配到数据。
(2) 二叉树
先来介绍下最经典的二叉树的特点:
但是在极端情况下会出现链化的情况,即节点一直在某一边增加。
平衡二叉树(Balanced Binary Tree,简称 ABT)是一种特殊的二叉树,其中每个节点的左右子树的高度之差的绝对值不超过1,并且它的左子树和右子树都是平衡二叉树。平衡二叉树的特点:
(3)B-树
B-树是一棵多路平衡查找树,对于一棵M阶的B-树有以下的性质:
可以将B-树理解为一棵更加矮胖的二叉搜索树.
二叉搜索树(Binary Search Tree,简称 BST),是一种特殊的二叉树,其中每个节点的左子树的值都小于该节点的值,而每个节点的右子树的值都大于该节点的值。
(4)B+树
MySQL 中最常用的索引的数据结构是 B+ 树。B+树是B-树的进阶版本,在B-树的基础上又做了如下的限制:
这样可以带来什么好处呢?
聚簇索引不是一种索引类型,而是一种存储数据的方式。Innodb的聚簇索引是在同一个数据结构中同时保存了索引和数据.
mysql中,主键索引页+数据页组成的B+树就是聚簇索引。聚簇索引中数据页记录的是一条记录的完整的记录。
MySQL 在存储数据的时候是以数据页为最小单位的,且数据在数据页中的存储是连续的,数据页中的数据是按照主键排序的(没有主键是由 MySQL自己维护的 ROW_ID 来排序的),数据页和数据页之间是通过双向链表来关联的,数据与数据时间是通过单向链表来关联的。
也就是说有一个在每个数据页中,他必然就有一个最小的主键,然后每个数据页的页号和最小的主键会组成一个主键目录,假设现在要查找主键为 2 的数据,通过二分查找法最后确定下主键为 2 的记录在数据页 1 中,此时就会定位到数据页 1 接着再去定位主键为 2 的记录。
假设上面的主键目录中的记录是非常非常多的,此时MySQL 会将索引里面的记录拆分到不同的索引页中,最终演变成这个样子:
来自:https://zhuanlan.zhihu.com/p/394429932
刚刚上面是说的其实可以理解为是主键索引,主键索引也是最简单的最基础的索引。所以说建立了主键查询就能变快了。
(1)索引信息
在mysql中,可以使用show create table table_name
来查看建表语句,其中包含创建索引的语句:
可以使用show index from table_name
来查看某个表上的索引,它将会有如下的输出:
可以看到,第一行默认使用了id 作为主索引。B+树在新增数据时,会根据主索引进行重整,影响性能,因此InnoDB推荐以自增id作为主索引:自增且连续,在插入的时候只需要不断的在数据后面追加即可。
后两行使用了联合索引,使用联合索引可以在多个列上进行快速的筛选和排序,特别适用于需要同时查询多个列的情况。
在创建联合索引时,需要注意以下几点:
如果需要查询的字段不在联合索引中,MySQL 就需要进行回表查询了,如:
SELECT user_id,product_name,price FROM orders WHERE user_id='2' and product_name='Apple watch';
此时 MySQL 会再次根据主键从聚簇索引的根节点开始查找,这个过程就叫回表。
另外,每建立1个索引,MySQL 就会多维护1个B+树,所以不能建立太多索引,因为索引也会占用空间。
(2)索引大小
在5.0以后的版本中,我们可以通过查看information_schema.TABLES
表中的数据来获取更加详细的数据.
该表各字段的含义如下表:
我们可以通过一些查询语句来获取详细的信息,比如:
// 查看当前MySQL服务器所有索引的大小(以MB为单位,默认是字节)
SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024), 2), ' MB') AS 'Total Index Size' FROM TABLES
// 查看某一个库的所有大小
SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024), 2), ' MB') AS 'Total Index Size' FROM TABLES WHERE table_schema = 'XXX';
// 查看某一个表的索引大小
SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024), 2), ' MB') AS 'Total Index Size' FROM TABLES WHERE table_schema = 'yyyy' and table_name = "xxxxx";
// 汇总查看一个库中的数据大小及索引大小
SELECT CONCAT(table_schema,'.',table_name) AS 'Table Name', CONCAT(ROUND(table_rows/1000000,4),'M') AS 'Number of Rows', CONCAT(ROUND(data_length/(1024*1024*1024),4),'G') AS 'Data Size', CONCAT(ROUND(index_length/(1024*1024*1024),4),'G') AS 'Index Size', CONCAT(ROUND((data_length+index_length)/(1024*1024*1024),4),'G') AS'Total'FROM information_schema.TABLES WHERE table_schema LIKE 'xxxxx';
注意:上面的表格是有缓存的,当更新数据库索引之后,最好执行analyze table xxxx
,然后再进行查看.MySQL会在表格数据发生较大的变化时才更新此表(大小变化超过1/16或者插入20亿行).
(3) 索引碎片
在索引的创建删除过程中,不可避免的会产品索引碎片,当然还有数据碎片,我们可以通过执行optimize table xxx
来重新整理索引及数据,对于不支持此命令的存储引擎来说,可以通过一条无意义的alter语句来触发整理,比如:将表的存储引擎更换为当前的引擎,alter table xxxx engine=innodb
.
硬件:使用好的硬件,更快的硬盘、大内存、多核CPU,专业的存储服务器(NAS、SAN)
架构:设计合理架构,如果 MySQL 访问频繁,考虑 Master/Slave 读写分离;数据库分表、数据库切片(分布式),也考虑使用相应缓存服务帮助 MySQL 缓解访问压力
(1)配置合理的MySQL服务器,尽量在应用本身达到一个MySQL最合理的使用
(2)针对 InnoDB 等不同引擎进行不同定制性配置
(3)针对不同的应用情况进行合理配置
华为云 RDS for MySQL参数调优建议:
https://support.huaweicloud.com/usermanual-rds/rds_08_00001.html
(1) 设计合理的数据表结构:适当的数据冗余
(2)对数据表建立合适有效的数据库索引
(3)数据查询:编写简洁高效的SQL语句
Type: 类型,是否使用了索引还是全表扫描, const,eg_reg,ref,range,index,ALL
Key: 实际使用上的索引是哪个字段
Ken_len: 真正使用了哪些索引,不为 NULL 的就是真实使用的索引
Ref: 显示了哪些字段或者常量被用来和 key 配合从表中查询记录出来
Rows: 显示了MySQL认为在查询中应该检索的记录数
Extra: 显示了查询中MySQL的附加信息,关心Using filesort 和 Using temporary,性能杀手
参考:
https://zhuanlan.zhihu.com/p/394429932
https://zhuanlan.zhihu.com/p/76355753