共计 3687 个字符,预计需要花费 10 分钟才能阅读完成。
背景
索引是数据库中用于减速查问的罕用组件,它通过对数据冗余和重组织来减速 SQL 查问。通常来说,失当的索引能够晋升零碎的查问性能。对于索引存在一些误会,如:索引总是能晋升查问性能,因而索引越多越好,比方下图中的例子
只看收益,不看代价是不行的。分布式数据库系统个别反对两类索引:由分布式全局事务保护的全局索引、由本地事务保护的本地索引。这两类索引都会不同水平影响零碎的写入性能,下图展现了建设不同数量的索引时,对系统的写入性能的影响。
能够看出创立 1 个全局索引,就会使零碎的写入性能升高至原来的约 30%;单看 MySQL,在创立 8 个索引(本地索引)的状况下,写入性能会升高至原来的 85%(援用自咱们的历史文章 TiDB、OceanBase、PolarDB-X、CockroachDB 二级索引写入性能测评,感兴趣的读者可深刻浏览)。因而,在咱们享受索引带来的查问减速收益时,还需关注其引入的保护开销。特地是当引入一个索引没能带来预期收益、或者带来的开销远大于其带来的查问减速收益时,索引反而成为一种累赘。咱们称这类索引为烂索引,避开它们能够帮忙数据库取得更好的写入性能。回顾文章结尾举例的表 warehouse,你能看出其中有几个烂索引吗?咱们先讨论一下利用中常见的烂索引,而后在文末颁布答案。
低频拜访索引和许久未拜访索引
新建的索引并未依照预期目标被数据库优化器应用时,就是一个烂索引,它暗藏在数据库中,耗费着写入性能,却并未带来查问性能增益,及时发现这类索引并进行清理是十分必要的。此外,还有一些索引在一段时间内被高频应用,但随着业务的变动,这些索引不再被应用,但却始终被遗留下来,这也是烂索引。对于上述情况,PolarDB- X 提供了 INFORMATION_SCHEMA.GLOBAL_INDEXES 视图,用于查问表中全局索引被应用的状况,有了它,哪些全局索引在“磨洋工”,哪些全局索引“收工又出力”,高深莫测。
低选择性索引
索引的选择性是指不反复的索引值的个数(也常被称为基数)和数据表的记录总数 (#T) 的比值,可由定义晓得它的取值范畴在 1/#T 到 1 之间。索引的选择性越高则查问效率越高,因为选择性高的索引能够帮忙数据库在查找时过滤掉更多有效的行。一个侧面例子是主键索引,因为主键是不反复的,因而其选择性为最大值 1,数据库利用主键查找数据时效率很高。一个背面的例子是,在性别、isDelete 等属性上建索引。如何发现这些低选择性的索引呢?最间接的方法是人工查看每个索引的实在含意,排除掉“性别”“Delete 标记”之类含意的索引。此外对于全局索引,PolarDB- X 反对用 INFORMATION_SCHEMA.GLOBAL_INDEXES 视图查看全局索引的基数和记录总数,咱们能够依据这两个指标算出索引的选择性。
反复索引
反复索引是指在雷同的列上依照雷同的程序创立了同类型的索引,Polardb- X 不会禁止用户创立多个反复的索引。因为数据库在写入数据时,须要同步保护索引,因而多个反复的索引就须要数据库别离保护,此外优化器在优化查问语句时,也须要对这些反复索引一一思考,这会影响性能。刻意引入反复索引的场景不常见,但不小心引入却是可能的。如上面的 SQL 是 PolarDB- X 中的单表,
用户可能想创立一个主键,而后为其加上 unique 限度,而后再加上索引以供其查问应用,实际上上述写法会创立出 3 个互相反复的索引,其实并不需要这么做。一些索引从定义上来看是非反复索引,但从成果上来看,又是反复的。比方上面的建表语句,
一些用户可能会将查问 SQL 的 where 条件用到的列都建成索引,因而创立了索引 idx_id_name。然而通常数据库在构建索引的时候,都会在索引的 value 属性中填入主键,以不便回表。因而索引 idx_name 的数据中是蕴含了主键 id 的,idx_name 和 idx_name_id 成果雷同。请防止构建这样的索引。
冗余索引
冗余索引和反复索引有所不同,如果创立了索引 (A, B),再创立索引 (A),后者就成了冗余索引。因为 (A) 是 (A, B) 的前缀索引,优化器应用索引时存在“最左匹配准则”,即会优先应用索引中的左侧列进行匹配,索引 (A, B) 是能够当做索引 (A) 来应用的。冗余索引常常产生在为数据表增加新索引的时候,一些用户更偏向于增加新索引,而不是在现有索引上进行扩大。咱们该当优先思考在已有的索引上做扩大,而非随便增加新索引。如果确需增加新索引,也该当分外留神新引入的索引是否是一个冗余索引,又或者新索引是否会让旧有的索引变成冗余索引。当然,一味地扩大现有索引也不可取,可能会导致索引长度过长,从而影响其余应用该索引的 SQL,这是一个 trade off。除了思考“最左匹配准则”,咱们还需注意 unique 束缚。在有 unique 束缚的状况下,一些看起来冗余的索引,实际上却并不冗余。
这里索引 idx_id_name 是无奈齐全代替索引 idx_id 的,因为索引 idx_id 除了不便依照 id 进行查找的作用外,还能够束缚 id 不反复,而索引 idx_id_name 只能保障 (id, name) 不反复。
全局索引分区规定反复
像 PolarDB- X 这样的 Shared-Nothing 架构的分布式数据库个别会引入“分区”的概念,用户在建表时指定一个或若干个列为分区键,数据会在数据库外部依照分区键进行路由,从而将数据存储至不同的 DN 节点。如果一个查问语句的 where 条件中蕴含分区键,优化器就能够疾速定位到一个具体分区并进行数据查找,但如果查问语句的 where 条件不含分区键,该查问就须要扫描全副分区,这有些相似于单机 mysql 的全表扫描,全分区扫描对于分布式数据库来说开销很大。在理论数据库投入生产应用时,一个维度的分区往往不够灵便,将查问语句的 where 条件限度在必须蕴含“分区列”不够自在。分布式数据库个别会反对全局索引,它冗余了主表上的局部数据,并采纳与主表不同的分区键,查问时首先依据全局索引的分区键定位到一个分区,而后从分区中查到主表的分区键和主键,最初回表失去残缺数据。全局索引让用户的查问语句不再受到“where 条件必须蕴含主表分区列”的限度,且能防止全分区扫描的代价。从上文可知,用好全局索引的前提是设计良好的全局索引的分区形式,尤其是要防止全局索引和主表的分区形式反复,比方上面的表构造中,全局索引 g_id 和主表 tb4 的分区形式完全一致,g_id 让零碎付出了写入代价,却没有带来查问性能的增益。
全局索引分区大小不平均
全局索引须要指定分区键,它的数据是依照分区规定寄存于 PolarDB- X 的不同 DN 节点中的。构想,如果全局索引的分区规定设计的不够好,就会导致分区不均,一些 DN 节点存储大量数据,且接受大量的读写负载,而另一部分 DN 节点处于闲暇状态。这造成了资源节约,且会使数据库系统过早地达到性能瓶颈。如下图,假如有一个业务零碎建设了 seller_order 卖家订单信息表,该业务零碎的特点是绝大部分订单来自于少数几个大卖家。咱们只关注 seller_order 表上的全局索引 g_seller_id,它应用卖家的 seller_id 做分区键。咱们假如有个大卖家的订单量占全副零碎的一半,其在全局索引 g_seller_id 上的数据被路由到 P5 分区。能够看到 P5 分区会接受其它分区数倍的负载。
良好的全局索引该当保证数据尽可能均匀分布在不同分区。
全局索引中的 range 分区
在 PolarDB- X 中应用 range 分区作为全局索引的分区策略时应该额定留神,尽量避免将工夫列作为分区列。
如上建表语句所示,全局索引 g_tm 应用了 tm 作为 range 分区的分区列,其默认值为以后工夫。这里咱们只思考全局索引 g_tm,其分区 p5 是一个 catch-all 分区,在 ’2023-07-01 11:00:00’ 工夫点当前,所有待插入的新数据都会被路由到 p5 分区(这是由新数据的 tm 列的值以及全局索引 g_tm 的路由规定决定的),因而 p5 分区会成为数据写入的瓶颈,p5 分区所在 DN 上的数据量也将始终累积。将来 PolarDB- X 将针对这一场景做出优化,但目前咱们不举荐本例中的用法。
总结
咱们先来答复一下文章结尾提出的问题。warehouse 表中有 4 个烂索引,别离是:反复索引 idx_id(与主键反复)、反复索引 idx_id_order_name(和主键成果统一)、冗余索引 idx_order_id_order_name(索引 idx_order_id_order_name_item_id 能够代替它)、低选择性索引 idx_deleted_order_id。本文总结了一些常见的烂索引及其低效的起因,定期检查和清理这些烂索引,能够无效晋升数据库的写入性能。可能有读者会问,表太多、索引太多,没精力挨个查看怎么办?没关系,PolarDB- X 最新推出 inspect index 性能,反对一键主动诊断烂索引,还能给出起因和整改倡议,本文提到的烂索引都能辨认。
作者:未启
点击立刻收费试用云产品 开启云上实际之旅!
原文链接
本文为阿里云原创内容,未经容许不得转载