目前互联网公司多数都在使用,MySQL的数据结构和一些设计思想很值得学习,面试必问...
思考:生产上遇到的慢查询/慢SQL查询,解决方法通常是添加索引,添加索引后查询速度可以提升几个数量级怎么做到的?正常一条SQL执行速度是几十毫秒或者几百毫秒,慢SQL执行几秒甚至几十秒肯定是不合理的。
想要学好MySQL首先要理解索引
的概念,索引可以帮助MySQL高效查找数据,是一个排好序的数据结构(排好序体现在从左到右依次递增)。
国外的一个数据结构图示化网址:Data Structure Visualization
索引的数据结构有很多:
二叉树
也叫折半查找,可以快速定位到数据。特点是右子节点的数据一定大于父节点的数据,左子节点的数据一定小于父节点的数据。缺点是由于每个节点只保存单个数据,所以数据量越大树的高度就会越高,查询次数越多查询效率会变低,极端情况例如所有数据都在节点的同一侧,查询类似逐行查找效率很低。
红黑树
也叫二叉平衡树,在二叉树的基础上做了优化。当节点左右两侧树的高度相差较多时自动做平衡,可以提高一定的查询性能,同时在平衡过程中会带来一定的消耗(左旋右旋等)。数据量大时,同样有树的高度变高导致查询次数多而效率低的问题。Hashmap在1.8版本做了优化,数据结构改为数组+链表和红黑树,后续再做详细总结。
hash
对于等值查询支持良好,不支持范围查找。对索引键做hash运算后维护到hash表(hash表中保存磁盘文件的地址)中,并关联到数据在磁盘中的地址,可以一次快速查找到目标数据。MySQL实现了自己的hash算法(常见的MD5、CRC16、CRC32等都是hash算法),发生hash冲突时数据在hash桶后面追加。
B-树
思考:在红黑树的基础上,横向保存的节点越多,是不是就意味着树的高度越低?
单个节点可以保存更多的数据。数据量大时,可以明显的降低树的高度,减少查询次数从而提高性能。
特点:叶子节点具有相同的深度,且指针为空;所有索引元素不重复;节点的数据索引左到右依次递增。
MySQL的B-树:
B+树
MySQL索引没有选择纯粹的B-树,而是对其进行优化,使用B+树(B-树变种)作为底层的数据结构,B+树这种数据结构数据量越大性能提升的越明显。
B+树的叶子节点(磁盘页)保存着多条索引和数据,把每个叶子节点上的第一个索引提取出来作为冗余保存在非叶子节点中,非叶子节点大小固定且只保存索引(严格上还有和叶子节点关联的指针),非叶子节点保存更多的索引理论上就代表可以保存更多的数据。
特点:非叶子节点不存储data,只保存冗余索引,可保存更多索引;叶子节点包含所有索引字段;叶子节点用指针连接,提高区间访问性能。
MySQL的B+树:
MySQL对B+树同样做了优化:叶子节点间使用的是双向指针,B+树原本使用的是单向指针
B+树相对B-树做了哪些优化?
a)B+树的非叶子节点只保存索引,而B-树的非叶子节点保存索引和数据。B+树的叶子节点包含所有的索引元素
b)B+树相邻的叶子节点之间有双向的指针,保存相邻节点的磁盘地址,范围查找更友好
补充:MySQL的数据是保存在磁盘中,存储位置在磁盘中随机,取决于写数据时通过磁道写入的磁盘位置。查询数据时的性能消耗主要发生在磁盘IO,当查找数据时从根节点开始,将磁盘页load到内存中和查询条件比较,把满足条件的下一个磁盘页load到内存中再次比较查找,重复操作最终找到目标数据。
个人理解:树的高度越低,磁盘页load到内存的次数越少,IO少了所以性能会更高。
优化:所有的根节点作为常驻内存保存。非叶子节点也可以作为内存进行保存。
每个节点(磁盘页)大小的选择是有讲究的,默认为16kb大小(16384b),大小可以修改但是不推荐。
数据表中每一行的数据通常不会超过1kb(按照表中几十个字段估算,没有文本格式大文件的情况),这样每个叶子节点可以保存16条数据
使用B-树时:非叶子节点同样保存data数据,数据总量 = 16 ^ n
,其中n代表树的高度
使用B+树时:B+树的非叶子节点保存的是索引(按大点算bigint格式占8字节)和指向下一个磁盘页的指针地址(C语言实现约占6字节),这样非叶子节点可以保存的索引个数约为:16384 / (8+6) = 1170
,三层高度的树可以保存数据总量约为:1170 * 1170 * 16
,两千万+数据
B+树的高度是由非叶子节点存放的元素个数决定的,存放的越多树的高度越低,查询效率也就越高(load到内存的I/O次数少)。显然相同数据量的情况下,B+树的高度远远小于B-树,性能更高
InnoDB存储引擎保存的索引和数据在同一个文件中 .ibd
MyISAM存储引擎保存的索引和数据在不同的文件中 .MYD 和 .MYI
注意:存储引擎描述的是数据表而不是描述数据据,最终生效的肯定是修饰数据表的存储引擎
存储引擎和磁盘保存文件格式的关系:
开发人员在设计InnoDB表的时候,ibd文件必须要用一个B+树的数据文件来组织
表自带主键索引的情况,直接用主键索引来组织整张表的所有数据
不创建主键时,从表的第一列开始选择所有数据都不相等的列来组织B+树
如果选不到,MySQL会创建一个隐藏列来组织整张表的数据
为了减少MySQL的工作量,索引应该由我们程序员提前创建好
索引定位查找数据时,都是从根节点开始查找数据并比较
整型数字的比较速度比字符串(逐个字符比较ASCII码)比较速度快
另外整型和字符串或者uuid相比,更能节省磁盘空间(通常比较贵SSD)
索引维护到B+树中会排好序,自增每次新增都添加到树的最后,减少树的分裂和平衡
主键索引树的叶子节点保存表中所有的数据
二级索引树的叶子节点保存组织整张表数据的聚集索引的值(通常是主键,也可能是MySQL选择的列或者隐藏列rowid)
MySQL这样设计可以减少数据的冗余节省存储空间,也保证了数据的一致性减少复杂度
二级索引查找数据时,是先找到主键索引,再通过主键索引回表到聚簇索引上面找到目标数据
二级索引其实也是稀疏的,是非聚簇索引
联合索引:多个字段共同组织成一个索引
理想情况是通过1到3个联合索引把业务上80%的查询覆盖到,在加上几个单值索引覆盖剩余的业务