当前位置: 首页 > 科技观察

高并发存储篇:关注索引的原理与优化!你可以躲过实践,但你不能躲过面试官!

时间:2023-03-21 10:15:47 科技观察

不管是什么业务,最终的数据都要落地,数据库肯定是少不了的。随着业务的发展,并发越来越高,数据库很容易成为整个链路的短板。这是面试中最常被问到的问题之一。调优的第一步是从sql语句和索引入手。需要保证单库执行没问题,才会有更高级别的分库分表,弹性,容灾等。Part1为什么Kafka不需要我们关心索引,而Mysql做?Kafka和MySQL虽然最终的数据都是离盘的,但是它们在使用方式和数据查询方式上有很大的不同,所以数据存储结构不同,进而决定了索引的复杂度。我们先来看看kafka的存储结构:因为kafka的定位是进行稳定的高性能数据读写。因此对于磁盘,采用顺序读写的方式,落入一些.log文件,并以baseoffset补0命名。为了实现高速搜索,Kafka创建了一个稀疏索引文件(每隔一段时间创建一条数据,不是全部),也就是索引文件。它维护消息的偏移量和.log文件的物理位置。通过二分查找快速定位日志文件,顺序扫描找到目标。所以Kafka的索引组织方式比较简单,方案比较固定,而MySQL不行。Mysql是为支持复杂的业务数据查询而创建的关系型数据库。查询方式和数据获取需求多种多样。要求MySQL有更复杂的索引机制来加速复杂的业务查询场景。Part2MySQL数据是如何组织的[1][2]用InnoDB存储引擎看mysql数据存储:参考了三本资料,基本总结了最重要的部分。数据分为多个逻辑层:row->page->Block->Segment->Tablespace。我们知道InnoDB存储引擎的表都是Index组织的(数据就是索引,索引就是数据),它们都维护在一颗B+树上,数据段就是叶子节点,索引段就是非叶节点;而我们划分的segment和areaBlocks实际上是在逻辑上划分的,目的是利用操作系统的资源来实现更高效的读写(比如每次从磁盘加载到内存的数据大小是由操作系统约定的)块等`)。其中,页是MySQL与磁盘交互的最小单位。如何从页面中找到行,如何将它们聚合成块、段,然后是空间。1、数据记录的最小单位——行以单向链表的形式连接,所以这就决定了我们在记录链中查找记录时,只能按顺序遍历,这也决定了一个数据链不会太长。但是一个页面的默认大小是16K,再加上行溢出等处理,一个页面最多可以存放7992行记录,那么多记录还要顺序遍历?当然不是,让我看看页面是如何组织记录行的。2与磁盘交互的最小单位——页面,作为与磁盘交互的最小单位,用于存储实际数据(页面类型为b-treeNode,用于存储真实数据,还有其他类型如作为索引目录页面来加快查询速度)从上图可以大致看出一个页面的整体结构:我们来看几个关键的字段参数:页面目录决定了页面中记录项的查询效率为了更快query,页目录存放的是页的数据目录(slot),包含最大最小记录和包数据链最大记录的偏移量。使用二分法快速查找数据很方便,不需要从最小值开始遍历,如下图:图片来自《从根儿上理解 MySQL》FileHeaderdetermineshowpagesrelatedtoeachand记录本页的一些通用信息,主要包括<本页页码、上一页、下一页、页类型、所属表空间等>。用页码找到这个页面,用上下页连接双向链表,用类型判断是索引页还是数据页。..图片来自《从根儿上理解 MySQL》该字段决定了可以通过以上属性轻松关联页面。PageHeader决定了页面的层级,存放了本页的数据信息,主要包括**<**本页的记录数,B+树中的层级,属性的索引ID,插入方向,最大交易ID等>。有了页面数据组织的概念,如何利用这些结构实现数据的快速查询呢?Part3索引的演变可以从上面的数据组织知识看出。行记录串联起来形成一个单向链表,每一页在本页的最小记录和最大记录之间成组分布。页面通过上一页和下一页的指针串联起来,形成一个双向链表,存储在磁盘中,如下图所示:那么,查询一条记录可以做什么呢?3原文:Sequentialmethod上图所示的数据拼接方式自然提供了一种查询方式:即按照主键的先后顺序遍历每一页,以及该页中的记录行。但是,这种查询方式除了页面内的二进制优化外,效率低下。该怎么办?求改进:既然一个page中的rowrecords可以分组到slot中,为什么数据页之间不能呢?4改进:在目录法中,我们将页面向上聚集建立一个页码目录,先在目录中查找,然后再去对应的Page查找,比顺序查找快很多。求改进:这种方法需要大量的连续空间+目录会随着数据的变化而频繁变化,怎么办?5演化:主键B+树方法其实在描述行记录结构的时候,我们可以看到,除了实际的业务数据,数据行结构中还有很多额外的空间。例如,record_type用于表示记录的类型是数据还是索引。正是这些额外空间的设计,为InnoDB以更合适的方式组织索引提供了支持:图片来自《从根儿上理解 MySQL》这是一个B+树,页节点是层次区分的,页中的行记录是通过区分的类型。业务数据包含在叶节点中,目录数据包含在其他非叶节点中。这种组织的好处是它允许足够多的层级来容纳足够多的数据项(可以简单地通过假设每页数据项的大小来估计)。而这种索引方式就是我们常说的聚簇索引。也就是说,记录和页面使用主键值排序,叶节点包含所有用户数据。寻求改进:如果我想查询其他列怎么办?6Extension:secondaryindex,jointindexsecondaryindex比如用户需要根据某列(a列)的值进行查询,那么重新创建一颗B+树。这种索引树与聚簇索引树的区别在于,索引节点以a列的值作为目录,而叶子节点只包含a列的值和主键。如果用户需要查询除c列以外的更多信息,则需要取主键ID到聚簇索引中再次查看,也称为回表。联合索引二级索引是除主键之外的单列索引,而联合索引是多列的联合排序。假设用户需要使用a、b两列进行有序查询,其内在意思是当a列的值相同时判断b的值。和二级索引一样,InnoDB也需要再创建一个B+树,目录项按照先a后b的顺序排序,叶子节点的数据项只包含a、b、主键三个值。Part4类比生产实践7美团定时任务指标优化[3]系统需要定时调取特定时间段内特定状态、特定类型、特定操作员的任务进行定时处理。选择*fromtaskwherestatus=xandoperator_id=xxxxandoperate_time>xxxxxxxx01andoperate_time