专栏首页Coder的技术之路高并发之存储篇:关注下索引原理和优化吧!躲得过实践,躲不过面试官!

高并发之存储篇:关注下索引原理和优化吧!躲得过实践,躲不过面试官!

文章出自本人公号,欢迎关注,后台提供高并发系列历史文章整理版下载

不管是啥业务,最终数据都要落地,数据库这一环是肯定少不了的。随着业务发展,并发越来越高,数据库很容易成为整个链路的短板。这也是大厂面试中比较常被问到的。而调优的第一步,都是从sql语句、索引入手。先得保证单个数据库执行没问题,才会有更高层次的分库分表、弹性、容灾等等。

Part1为什么Kafka不需要我们关心索引,而Mysql却需要?

Kafka 和 MySQL 虽然最终数据都是落磁盘,但是两者在用途和数据查询方式上有着很大的差异,所以决定了数据的存储结构不同,进而决定了索引的复杂程度。

我们先看下kafka的存储结构:

由于 kafka 的定位是进行稳定的高性能数据读写。所以对磁盘来说,是采用顺序读写的方式,落在了一些 .log 文件中,并以基准偏移量补0命名。

为了实现高速查找 kafka 创建了稀疏索引文件(隔一段数据创建一条,而非全量),即index文件。其中维护消息的 offset 和 .log文件的物理位置。通过二分查找快速定位log文件并顺序扫描找到目标。

所以,kafka的索引组织方式是相对简单、方案相对固定,但MySQL却不行。Mysql是关系型数据库,是为了支持复杂的业务数据查询而创建的,查询方式、数据获取需求多种多样,要求MySQL具备更加复杂的索引机制来加速复杂业务查询场景

Part2MySQL数据怎么被组织[1][2]

以InnoDB存储引擎来看mysql数据存储:

参考了三本资料,基本把最重要的部分都概括了

数据被分了多个逻辑层:行->页->区块->段->表空间

我们知道,InnoDB存储引擎表是Index organized的(数据即索引,索引即数据),他们都维护在一个B+树上,数据段就是叶子节点,索引段就是非叶子节点;

而我们划分的段、区块 其实都是为了利用操作系统的资源(比如每次从磁盘加载到内存的数据大小按区块来约定等等`)来达到更高效读写的目的,逻辑划分的。

其中页是MySQL和磁盘交互的最小单位,怎么从页找到行,怎么聚合到块、到段再到空间呢。

1数据记录最小单位-- 行

从上面总图中摘出一条记录的结构如下图:

我们可以看到,记录头中除了行号,还有下一条记录的标识next_record,所以,我们可以通过next_record将记录连接起来,以单向链表的形式,所以这就决定了,当我们在记录链中寻找某记录时,只能顺序遍历,这也决定了一条数据链不会太长。

但一个页默认是16K,加上行溢出等处理,一页最多存放7992行记录,这么多的记录,必须顺序遍历么?当然不需要,让我看看页是怎么组织记录行的。

2与磁盘最小交互单位-- 页

作为与磁盘交互的最小单位,是用来存放实际数据的(页类型是b-tree Node存真实数据,还有其他类型如索引目录页等用来加速查询)从上面的大图中可以大致看到一个页的整体结构:

让我们来看几个关键的字段参数:

Page Directory 决定着记录项在页内的查询效率

为了更快速的查询,页目录存储的本页的数据目录(),包含最大最小记录分组数据链的最大记录的偏移量。方便使用二分法快速查找数据,不需要再从最小值开始遍历,如下图:

图片来自《从根儿上理解 MySQL》

File Header决定页和页之间怎样关联

记录本页的一些通用信息,主要包含< 本页页号上一页下一页页类型所属表空间等等>。

通过页号来找到本页、通过上下页进行双向链表串联、通过类型判断是索引页还是数据页。。。

图片来自《从根儿上理解 MySQL》

此字段决定了页和页之间可以很方便的通过上述属性进行关联。

Page Header决定页的层级

存储的本页的数据信息,主要包含**<** 本页记录数量在B+树中的层级归属的索引ID插入方向最大事务ID等等 >

有了页面的数据组织概念,那么,怎么利用这些结构来实现的数据快速查询呢?

Part3索引的演进思路

从上面的数据组织的知识里可以看到,行记录之间串联成单向链表,在每页中都按分组方式分布在此页的最小记录和最大记录之间。

页面之间通过上一页、下一页的指针,串联成双向链表,在磁盘中进行存储,如下图:

那么,要查询一条记录,可以怎么做?

3原始:顺序方式

如上图所示的数据串联方式,自然的提供了一种查询方式:即按主键顺序遍历每页和页中的记录行。

但是,这样的查询方式,除了在页内有二分优化,再无效率可言。怎么办?

寻求改进:既然页内的行记录可以分组入槽,那数据页之间为什么不行呢?

4改进:目录方式

我们将页向上聚蔟,构建一个页号目录,先在目录中查找,再到对应页中查找,就比顺序查找要快很多了。

寻求改进:这样的方式所需大量连续空间 + 目录会随数据变动而频繁变动,怎么办?

5演进:主键B+树方式

其实,在叙述行记录结构的时候,我们就看到,数据行的结构中,除了实际业务数据外,还有很多额外空间。

record_type用来表示该记录的类型是数据还是索引。正是这些额外的空间的设计,给InnoDB以更加适合的方式组织索引提供了支持:

图片来自《从根儿上理解 MySQL》

这就是一棵B+树,页节点有层级区分,页中的行记录有类型区分。

业务数据都包含在叶子节点中,目录数据都包含在其他非叶节点中。

这样组织方式的优势,是允许足够少的层级容纳足够多的数据项(可以简单的假设每一页的数据项大小来预估)。

而这个索引方式就是我们常说的聚蔟索引。即使用主键值进行记录和页的排序,且叶子节点含有全部用户数据。

寻求改进:如果我想用其他列来查询,怎么办?

6扩展:二级索引、联合索引

二级索引

比如用户需要根据某一列(a列)的值来查询,那就再重新创建一个B+树。此索引树和聚蔟索引树的差别在于,索引节点是以a列的值为目录,且叶子节点只包含a列的值和主键两个值

如果用户需要查询除c列以外的更多信息,则需要拿主键ID再去聚蔟索引查一次,也叫回表

联合索引

二级索引是除主键外的单列索引,而联合索引则是多个列共同排序。假设用户需要用a 、b 两个列进行有序查询,那内在含义是,在a列值相同的情况下,再判断b的值。

同二级索引一样,InnoDB也需要再创建一棵B+树,且目录项的排序按先a,后b进行排序串联,叶子节点的数据项只包含 a 、b、主键三个值。

Part4生产实践之触类旁通

7美团定时任务索引优化[3]

系统需要定时的捞取特定时间段内特定状态、特定类型、特定操作者的任务进行定时处理。

select * from task 
where
   status=x
   and operator_id=xxxx 
   and operate_time>xxxxxxxx01
   and operate_time<xxxxxxxx99
   and type=x;

开发发现此sql运行的越来越慢,希望给每个字段加二级索引,被优化师叫停,而是考虑的该表所有查询方式后,创建了一个联合索引:

(status,operator_id,type,operate_time)

为什么不建多个的二级索引?为什么范围查询的字段要放在最后?

分析: (1)从前面的原理部分我们知道,索引是要占内存的,不是越多越好,能起作用就行。

(2)用于范围匹配的字段的索引位置要严谨。因为创建索引的时候,根据索引字段的顺序来进行排序,如果把time字段放在type字段前面建索引,在查询时,因为time是一个范围值,那么多个time值延续到type字段,整体是无序的,无法用到type索引。

8蚂蚁分布式主事务表的索引运用

蚂蚁的分布式事务中的主事务表起到了维护整体事务状态的作用,其中包含了整体事务状态、操作时间等字段。而在业务支付发生异常,且实时回滚失败时,需要事务恢复系统从远程捞取前1分钟的异常数据,并捞取对应的分支记录表发起异步回滚。

考虑查询效率,查询sql会限定业务发生时间在[前10分钟,前1分钟],是有范围查询,所以,针对其他字段,业务时间的索引顺序需要置于联合索引的最后。此操作的原理和上一部分美团定时任务的原理是一样的。

9阿里开发手册中几条典型的规范[4]

【强制】 在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。 原理关联:字段越长,索引占内存越多,只要其长度可以保证区分度即可 【强制】 字符搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 原理关联:左模糊的字段不是有序的,无法用到索引 【推荐】 如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。 原理关联:如果条件中有范围查询,则后续字段是无序的,order by时无法用到索引 【推荐】 建组合索引的时候,区分度最高的在最左边。 原理关联:区分度越高,查询路径越短,效率越高

等等,参见阿里Java开发手册

Part5总结

本文从MySQL的存储结构、索引的设计思路演进、美团阿里大型系统索引使用等几个部分,阐述了索引的原理和对业务系统的重要作用。

当然,对于高并发下的数据库的优化远不止索引优化这一个方面,本文只从索引这一个点出发,让大家对其优化原理和优化方向有一个大致的概念,在业务发展遇到数据库瓶颈时能有所帮助。

附:高并发系列历史文章目录

  1. 垂直性能提升 1.1. 架构优化:集群部署,负载均衡 1.2. 万亿流量下负载均衡的实现 1.3. 架构优化:消息中间件的妙用

参考资料

[1]MySQL技术内幕: 机械工业出版社

[2]MySQL 是怎样运行的: 掘金小册

[3]MySQL索引原理及慢查询优化: https://tech.meituan.com/2014/06/30/mysql-index.html

[4]阿里Java开发手册: 阿里巴巴

本文分享自微信公众号 - Coder的技术之路(gh_1b3189982966),作者:Coder的技术之路

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 吹弹牛皮之Unity 性能分析-锁定内存泄漏

    承载千万家庭的高考莘莘学子刚刚为10年的寒窗苦拉上帷幕。鼓励我们的学子多多选择计算机专业。这里有拉格朗日函数的求解,有傅里叶级数的转换。奥秘万千,从笛卡尔坐标系...

    用户7698595
  • 公司web安全等级提升

    公司的一个web数据展示系统,本来是内网的,而且是一个单独的主机,不存在远程控制的问题,所以之前并没有考虑一些安全相关的测试.但是国调安全检查的需要添加这样子的...

    @坤的
  • 安全科普:流量劫持能有多大危害?

    作者 gethostbyname 上一篇文章,介绍了常见的流量劫持途径。然而无论用何种方式获得流量,只有加以利用才能发挥作用。 不同的劫持方式,获得的流量也有所...

    FB客服
  • Webshell免杀研究

    有时候我们在做攻防对抗时经常会碰到可以上传webshell的地方,但是经常会被安全狗、D盾、护卫神、云锁等安全软件查杀,在本篇文章中将会介绍一些常用的木马免杀技...

    Al1ex
  • MySQL 是怎样运行的:从根儿上理解 MySQL

    MySQL凭借着它还不错的性能、还不错的稳定性常年稳居老二宝座,当然最大的优势就是它不要钱,还开源,这让它成为大部分中小型公司,尤其是互联网公司首选的数据库(近...

    老钱
  • 有什么经验教训,是面试很多次之后才知道的?

    帮助了近千名小伙伴进行面试复盘,成功入职的小伙伴占了99%,而且很多小伙伴是在本身能力,年龄和学历占弱势的情况下拿到了心仪的offer。

    互联网老辛
  • 【原创】从地图到线路规划 (六)

    假如一定要找一个用高德的理由,刚好你身处物流行业,那我会说,高德最新推出了货运解决方案。

    物流IT圈
  • 漫谈中国SaaS:从圆桌理论到降维打击

    十年前,我是魔兽世界的忠实玩家。当时我玩的是战士,防战。 玩过防战的都知道,这个职业唯一的作用就是下副本的时候抗 BOSS,身后站着 5 个以上的治疗和几十个 ...

    静一
  • 近5亿次捉迷藏游戏中,AI玩家策略轮番升级,花式使用工具!

    在生命诞生初期,生命体是非常简单的。它们是微小的单细胞生物,几乎没有协调能力。然而,经过数十亿年的竞争和自然选择,这些简单的生命体逐渐演化成为我们今天所拥有的复...

    大数据文摘
  • Vue3 封装出让后来者难以理解的组件,让你变得不再随时可替代

    https://juejin.cn/post/6964954862991704094

    coder_koala
  • 深入理解JVM - 分代的基本概念

    本次讲述jvm分代模型的基础概念,这个专栏会由浅入深的不断构建起来,循序渐进,和上一篇一样,是非常基础的内容

    阿东
  • 当小爱同学遇到Blinker与WiFiduino能碰出怎样的火花?

    2020年 注定是一个不平凡的年 新年伊始 疫情肆虐! 武汉告急! 华中告急! 全国告急! 为了躲避疫情 我们不约而同的无聊起来 但无论怎样 学习不能耽搁 想当...

    聪明的瓦肯人
  • 嗨,IT小哥哥,我想问问你,我们有爱一点好不好?

    IT男,普遍会被贴上“智商极高,情商却极低”的标签,其实小宁同学也觉得IT小哥哥这个特殊的群体有些超乎寻常的高智商。

    用户1257393
  • 2019年一线大厂最全JVM面试100问!你能答对多少?

    JVM(Java虚拟机)简单来说就是运行Java代码的解释器,作为螺丝钉程序员JVM其实了解下就差不多啦,不懂JVM内部细节照样能写出优质的代码!但是一到造火箭...

    程序员追风
  • 文科妹子都会用 GitHub,你这个工科生还等什么

    点赞人数还不少,这说明还真有不少工科生不会用 GitHub,你看大小写都没有区分(手动狗头)。所以我就想写篇文章科普下,“新手如何使用 GitHub?”

    沉默王二
  • 为什么Mongodb索引用B树,而Mysql用B+树?

    好久没写文章了,今天回来重操旧业。 今天讲的这个主题,是《面试官:谈谈你对mysql索引的认识》,里头提到的一个坑。

    帅地
  • 曾经名噪一时的7个搜索引擎:现在都在哪里?

    对某些特定时期的人而言,搜索领域只代表着一件事情:Google。但是对很多人来说,他们还记得那样一个时代——搜索引擎数不胜数,新奇的品牌备受瞩目。 AltaVi...

    CSDN技术头条
  • 学Linux运维自动化无头绪?这21个学习资源值得看

    运维工种对于自动化的强烈需求已经显露无疑——作为一个古老的技术工种,在几台、几十台服务器时尚可人肉维护,面对云计算时代动辄上百上千的服务器,单凭人肉维护显然束手...

    小小科
  • 关于新型的无文件网络攻击,你需要知道的5件事

    在今年早些时候 ,在Equifax漏洞事件和全球性的WannaCry勒索软件爆发之后,关于目前的网络安全环境情况已经达到了刻不容缓的时候。企业需要面临着提供更多...

    未来守护者

扫码关注云+社区

领取腾讯云代金券