索引是帮助MySQL高效获取数据的排好序的数据结构
索引数据结构,主要包含以下几类,我们来对比下
**定义:**每个结点最多有两个子树,左子树比父节点小,右子树比父节点大。
缺点:会出现极端情况导致整棵树只有左子树或只有右子树。
定义:平衡二叉树又称AVL树,是一种特殊的二叉查找树,其左右子数都是平衡二叉树,且左右子树高度差的绝对值不超过1。
缺点:AVL树是高度平衡的,频繁的插入和删除,会引起频繁的rebalance,导致效率下降。
定义:
缺点:数据量大会导致树层数比较多,这样就会造成查找数据慢。
定义:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 对目标值进行hash运算得到hash值和数据磁盘指针地址保存到hash表,这样就达到快速定位数据位置。
缺点:精确查找十分快速,但范围查找就碰壁了。
定义:
缺点:节点里面数组数据:每个数据的结构=索引数据+数据记录(即叶子节点存储键值和数据记录)。
每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。
模拟查找关键字29的过程:
根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
比较关键字29在区间(17,35),找到磁盘块1的指针P2。
根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
比较关键字29在区间(26,30),找到磁盘块3的指针P2。
根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
在磁盘块8中的关键字列表中找到关键字29。
分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。
定义:B+Tree是在B-Tree基础上的一种优化。节点里面数组数据:每个数据只存储键信息,这样不存数据可以腾出空间放更多的键信息,让树层数越小
将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找
可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为 INT(占用4个字节)或 BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值。(因为是估值,为方便计算,这里的K取值为〖10〗3)。**也就是说一个深度为3的B+Tree索引可以维护103 * 10^3 * 10^3 = 10亿 条记录**。
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在24层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要13次磁盘I/O操作。
数据库中的B+Tree索引可以分为聚集索引(clustered index)和 辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。
辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。
数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据引擎进行创建、查询、更新和删除数据操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎还可以获得特定的功能。
现在许多数据库管理系统都支持多种不同的存储引擎。MySQL 的核心就是存储引擎。
MySQL 提供了多个不同的存储引擎,包括处理事务安全表的引擎和处理非事务安全表的引擎。在 MySQL 中,不需要在整个服务器中使用同一种存储引擎,针对具体的要求,可以对每一个表使用不同的存储引擎。
可以利用 SHOW ENGINES 语句来显示可用的数据库引擎和默认引擎。
可以根据以下的原则来选择 MySQL 存储引擎:
使用下面的语句可以修改数据库临时的默认存储引擎 SET default_storage_engine&#61;<存储引擎名 > 注意&#xff1a;将 MySQL 数据库的临时默认存储引擎修改为 其他的存储引擎时 &#xff0c;但是当再次重启客户端时&#xff0c;默认存储引擎仍然是 InnoDB。
MyISAM采用的是索引与数据分离的形式&#xff0c;将数据保存在三个文件中.frm 、.MYD、.MYI。
MyISAM不支持行锁&#xff0c;所以读取时对表加上共享锁&#xff0c;在写入是对表加上排他锁。由于是对整张表加锁&#xff0c;相比InnoDB&#xff0c;在并发写入时效率很低。
MyISAM不支持事务
MyISAM是基于非聚簇索引进行存储的。
MyISAM索引文件和数据文件是分离的(非聚集)
MyISAM提供了大量的特性&#xff0c;包括全文索引&#xff0c;压缩&#xff0c;空间函数&#xff0c;延迟更新索引键等。
使用InnoDB时&#xff0c;会将数据表分为.frm 和 .ibd 两个文件进行存储。
innodb的所有数据文件&#xff08;后缀为ibd的文件&#xff09;&#xff0c;他的大小始终都是16384&#xff08;16k&#xff09;的整数倍
InnoDB采用**MVCC(多版本并发控制)**来支持高并发&#xff0c;InnoDB实现了四个隔离级别&#xff0c;默认级别是REPETABLE READ&#xff0c;并通过间隙锁策略防止幻读的出现。它的锁粒度是行锁。【MVCC在稍后会进行介绍】
InnoDB是典型的事务型存储引擎&#xff0c;并且通过一些机制和工具&#xff0c;支持真正的热备份。
InnoDB表是基于聚簇索引建立的&#xff0c;聚簇索引对主键的查询有很高的性能&#xff0c;不过他的二级索引&#xff08;非主键索引&#xff09;必须包含主键列&#xff0c;索引其他的索引会很大。
InnoDB索引实现(聚簇)
底层数据结构&#xff1a;
比较相等时&#xff0c;先比较第一列的值&#xff0c;如果相等&#xff0c;再继续比较第二列&#xff0c;以此类推。
最左前缀原理&#xff1a;
使用联合索引时&#xff0c;索引列的定义顺序将会影响到最终查询时索引的使用情况。例如联合索引&#xff08;a,b,c&#xff09;&#xff0c;mysql会从最左边的列优先匹配&#xff0c;如果最左边的带头大哥没有使用到&#xff0c;在未使用覆盖索引的情况下&#xff0c;就只能全表扫描。 联合底层数据结构思考&#xff0c;mysql会优先以联合索引第一列匹配&#xff0c;此后才会匹配下一列&#xff0c;如果不指定第一列匹配的值&#xff0c;也就无法得知下一步查询哪个节点。 另外还有一种情况&#xff0c;如果遇到 > 这个问题的简单回答是&#xff1a;约2千万。为什么是这么多呢&#xff1f;因为这是可以算出来的。 这里我们先假设B&#43;树高为2&#xff0c;即存在一个根节点和若干个叶子节点&#xff0c;那么这棵B&#43;树的存放总记录数为&#xff1a;根节点指针数单个叶子节点记录行数。 上文我们已经说明单个叶子节点&#xff08;页&#xff09;中的记录数&#61;16K/1K&#61;16。&#xff08;这里假设一行记录的数据大小为1k&#xff0c;实际上现在很多互联网业务数据记录大小通常就是1K左右&#xff09;。 那么现在我们需要计算出非叶子节点能存放多少指针&#xff0c;其实这也很好算&#xff0c;我们假设主键ID为bigint类型&#xff0c;长度为8字节&#xff0c;而指针大小在InnoDB源码中设置为6字节&#xff0c;这样一共14字节&#xff0c;我们一个页中能存放多少这样的单元&#xff0c;其实就代表有多少指针&#xff0c;即16384/14&#61;1170。那么可以算出一棵高度为2的B&#43;树&#xff0c;能存放1170*16&#61;18720条这样的数据记录。 根据同样的原理我们可以算出一个高度为3的B&#43;树可以存放&#xff1a;1170* 1170 *16&#61;21902400条这样的记录。所以在InnoDB中B&#43;树高度一般为1-3层&#xff0c;它就能满足千万级的数据存储。在查找数据时一次页的查找代表一次IO&#xff0c;所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。 那么每次插入新的记录&#xff0c;记录就会顺序添加到当前索引节点的后续位置&#xff0c;主键的顺序按照数据记录的插入顺序排列&#xff0c;自动有序。当一页写满&#xff0c;就会自动开辟一个新的页 由于每次插入主键的值近似于随机&#xff0c;因此每次新纪录都要被插到现有索引页得中间某个位置&#xff0c;此时MySQL不得不为了将新记录插到合适位置而移动数据&#xff0c;甚至目标页面可能已经被回写到磁盘上而从缓存中清掉&#xff0c;此时又要从磁盘上读回来&#xff0c;这增加了很多开销&#xff0c;同时频繁的移动、分页操作造成了大量的碎片&#xff0c;得到了不够紧凑的索引结构&#xff0c;后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。 聚簇索引的数据的物理存放顺序与索引顺序是一致的&#xff0c;即&#xff1a;只要索引是相邻的&#xff0c;那么对应的数据一定也是相邻地存放在磁盘上的。如果主键不是自增id&#xff0c;那么可以想象&#xff0c;它会干些什么&#xff0c;不断地调整数据的物理地址、分页&#xff0c;当然也有其他一些措施来减少这些操作&#xff0c;但却无法彻底避免。但&#xff0c;如果是自增的&#xff0c;那就简单了&#xff0c;它只需要一页一页地写&#xff0c;索引结构相对紧凑&#xff0c;磁盘碎片少&#xff0c;效率也高。问题总结
InnoDB一颗B&#43;树可以存放多少行数据&#xff1f;
聚簇索引和非聚簇索引的区别
为什么InnoDB表必须有主键&#xff0c;并且推荐使用整型的自增主键&#xff1f;
MySQL为什么用自增作为索引比较好。而UUID索引效率比较低&#xff1f;
为什么非主键索引结构叶子节点存储的是主键值&#xff1f;&#xff08;一致性和节省存储空间&#xff09;