分库分表索引设计:分布式环境下的 主键索引、二级索引、全局索引的最佳设计实践

news2024/11/28 16:41:42

文章目录

      • 主键选择
      • 索引设计
      • 全局表
      • 唯一索引
      • 总结
  • 结语

主键选择

对主键来说,要保证在所有分片中都唯一,它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键,就会发现存在很大的问题。

因为自增并不能在插入前就获得值,而是要通过填 NULL 值,然后再通过函数 last_insert_id()获得自增的值。所以,如果在每个分片上通过自增去实现主键,可能会出现同样的自增值存在于不同的分片上。

比如,对于电商的订单表 orders,其表结构如下(分片键是o_custkey,表的主键是o_orderkey):

CREATE TABLE `orders` (
  `O_ORDERKEY` int NOT NULL auto_increment,
  `O_CUSTKEY` int NOT NULL,
  `O_ORDERSTATUS` char(1) NOT NULL,
  `O_TOTALPRICE` decimal(15,2) NOT NULL,
  `O_ORDERDATE` date NOT NULL,
  `O_ORDERPRIORITY` char(15) NOT NULL,
  `O_CLERK` char(15) NOT NULL,
  `O_SHIPPRIORITY` int NOT NULL,
  `O_COMMENT` varchar(79) NOT NULL,
  PRIMARY KEY (`O_ORDERKEY`),
  KEY (`O_CUSTKEY`)
  ......
) ENGINE=InnoDB

如果把 o_orderkey 设计成上图所示的自增,那么很可能 o_orderkey 同为 1 的记录在不同的分片出现,如下图所示:

图片

所以,在分布式数据库架构下,尽量不要用自增作为表的主键:自增性能很差、安全性不高、不适用于分布式架构。

讲到这儿,我们已经说明白了“自增主键”的所有问题,那么该如何设计主键呢?依然还是用全局唯一的键作为主键,比如 MySQL 自动生成的有序 UUID;业务生成的全局唯一键(比如发号器);或者是开源的 UUID 生成算法,比如雪花算法(但是存在时间回溯的问题)。

总之,用有序的全局唯一替代自增,是这个时代数据库主键的主流设计标准,如果你还停留在用自增做主键,或许代表你已经落后于时代发展了。

索引设计

通过分片键可以把 SQL 查询路由到指定的分片,但是在现实的生产环境中,业务还要通过其他的索引访问表。

还是以前面的表 orders 为例,如果业务还要根据 o_orderkey 字段进行查询,比如查询订单 ID 为 1 的订单详情:

SELECT * FROM orders WHERE o_orderkey = 1

我们可以看到,由于分片规则不是分片键,所以需要查询 4 个分片才能得到最终的结果,如果下面有 1000 个分片,那么就需要执行 1000 次这样的 SQL,这时性能就比较差了。

但是,我们知道 o_orderkey 是主键,应该只有一条返回记录,也就是说,o_orderkey 只存在于一个分片中。这时,可以有以下两种设计:

  • 同一份数据,表 orders 根据 o_orderkey 为分片键,再做一个分库分表的实现;
  • 在索引中额外添加分片键的信息。

这两种设计的本质都是通过冗余实现空间换时间的效果,否则就需要扫描所有的分片,当分片数据非常多,效率就会变得极差。

而第一种做法通过对表进行冗余,对于 o_orderkey 的查询,只需要在 o_orderkey = 1的分片中直接查询就行,效率最高,但是设计的缺点又在于冗余数据量太大。

所以,改进的做法之一是实现一个索引表,表中只包含 o_orderkey 和分片键 o_custkey,如:

CREATE TABLE idx_orderkey_custkey (
  o_orderkey INT
  o_custkey INT,
  PRIMARY KEY (o_orderkey)
)

如果这张索引表很大,也可以将其分库分表,但是它的分片键是 o_orderkey,如果这时再根据字段 o_orderkey 进行查询,可以进行类似二级索引的回表实现:先通过查询索引表得到记录 o_orderkey = 1 对应的分片键 o_custkey 的值,接着再根据 o_custkey 进行查询,最终定位到想要的数据,如:

SELECT * FROM orders WHERE o_orderkey = 1

=>

# step 1
SELECT o_custkey FROM idx_orderkey_custkey 
WHERE o_orderkey = 1

# step 2
SELECT * FROM orders 
WHERE o_custkey = ? AND o_orderkey = 1

这个例子是将一条 SQL 语句拆分成 2 条 SQL 语句,但是拆分后的 2 条 SQL 都可以通过分片键进行查询,这样能保证只需要在单个分片中完成查询操作。不论有多少个分片,也只需要查询 2个分片的信息,这样 SQL 的查询性能可以得到极大的提升。

通过索引表的方式,虽然存储上较冗余全表容量小了很多,但是要根据另一个分片键进行数据的存储,依然显得不够优雅。

因此,最优的设计,不是创建一个索引表,而是将分片键的信息保存在想要查询的列中,这样通过查询的列就能直接知道所在的分片信息。

如果我们将订单表 orders 的主键设计为一个字符串,这个字符串中最后一部分包含分片键的信息,如:

o_orderkey = string(o_orderkey + o_custkey)

那么这时如果根据 o_orderkey 进行查询:

SELECT * FROM Orders
WHERE o_orderkey = '1000-1';

由于字段 o_orderkey 的设计中直接包含了分片键信息,所以我们可以直接知道这个订单在分片1 中,直接查询分片 1 就行。

同样地,在插入时,由于可以知道插入时 o_custkey 对应的值,所以只要在业务层做一次字符的拼接,然后再插入数据库就行了。

这样的实现方式较冗余表和索引表的设计来说,效率更高,查询可以提前知道数据对应的分片信息,只需 1 次查询就能获取想要的结果。

这样实现的缺点是,主键值会变大一些,存储也会相应变大。但只要主键值是有序的,插入的性能就不会变差。而通过在主键值中保存分片信息,却可以大大提升后续的查询效率,这样空间换时间的设计,总体上看是非常值得的。

当然,这里我们谈的设计都是针对于唯一索引的设计,如果是非唯一的二级索引查询,那么非常可惜,依然需要扫描所有的分片才能得到最终的结果,如:

SELECT * FROM Orders
WHERE o_orderate >= ? o_orderdate < ?

因此,再次提醒你,分布式数据库架构设计的要求是业务的绝大部分请求能够根据分片键定位到 1 个分片上。

如果业务大部分请求都需要扫描所有分片信息才能获得最终结果,那么就不适合进行分布式架构的改造或设计。

最后,我们再来回顾下淘宝用户订单表的设计:

图片

上图是我的淘宝订单信息,可以看到,订单号的最后 6 位都是 308113,所以可以大概率推测出:

  • 淘宝订单表的分片键是用户 ID;
  • 淘宝订单表,订单表的主键包含用户 ID,也就是分片信息。这样通过订单号进行查询,可以获得分片信息,从而查询 1 个分片就能得到最终的结果。

全局表

在分布式数据库中,有时会有一些无法提供分片键的表,但这些表又非常小,一般用于保存一些全局信息,平时更新也较少,绝大多数场景仅用于查询操作。

例如 tpch 库中的表 nation,用于存储国家信息,但是在我们前面的 SQL 关联查询中,又经常会使用到这张表,对于这种全局表,可以在每个分片中存储,这样就不用跨分片地进行查询了。如下面的设计:

图片

唯一索引

最后我们来谈谈唯一索引的设计,与主键一样,如果只是通过数据库表本身唯一约束创建的索引,则无法保证在所有分片中都是唯一的。

所以,在分布式数据库中,唯一索引一样要通过类似主键的 UUID 的机制实现,用全局唯一去替代局部唯一,但实际上,即便是单机的 MySQL 数据库架构,我们也推荐使用全局唯一的设计。因为你不知道,什么时候,你的业务就会升级到全局唯一的要求了。

总结

今天介绍了非常重要的分布式数据库索引设计,内容非常干货,是分布式架构设计的重中之重,建议反复阅读,抓住本文的重点,总结来说:

  • 分布式数据库主键设计使用有序 UUID,全局唯一;
  • 分布式数据库唯一索引设计使用 UUID 的全局唯一设计,避免局部索引导致的唯一问题;
  • 分布式数据库唯一索引若不是分片键,则可以在设计时保存分片信息,这样查询直接路由到一个分片即可;
  • 对于分布式数据库中的全局表,可以采用冗余机制,在每个分片上进行保存。这样能避免查询时跨分片的查询。

作者:让我来搞这个bug
链接:https://www.jianshu.com/p/071c4ee7a532

结语

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TDengine 3.0.2.5 查询再优化!揭秘索引文件的工作原理

TDengine 3.0 虽然对底层做了大规模的优化重构&#xff0c;但是相对于数据文件的工作逻辑和 2.0 相比是整体保持不变的。本系列文章的主旨在于帮助用户深入理解产品&#xff0c;并且拥有基本的性能调试思路&#xff0c;从而获得更好的产品体验。本期文章会在讲解 TDengine 时序…

今天是情人节呐,我利用Python制作了好多表白的东西,快来吧~

今天是情人节那&#xff0c;有没有现在没有对象的宝子&#xff0c;评论里扣个111哈哈 目录 玫瑰 爱心树 丘比特 多彩气球 阿玥的小课堂 一、情人节的由来 二、情人节的来历和意义 玫瑰 局部代码实现如下&#xff1a; # 花瓣1 turtle.left(150) turtle.circle(-90, 70) …

【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

【STM32笔记】低功耗模式下的RTC唤醒&#xff08;非闹钟唤醒&#xff0c;而是采用RTC_WAKEUPTIMER&#xff09; 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置&#xff08;ADC唤醒无法使用、低功耗模式无法烧录…

C语言思维导图大总结 可用于期末考试 C语言期末考试题库

目录 一.C语言思维导图 二.C语言期末考试题库 一.C语言思维导图 导出的图可能有点糊&#xff0c;或者查看链接&#xff1a;https://share.weiyun.com/uhf1y2mp 其实原图是彩色的不知道为什么导出时颜色就没了 部分原图&#xff1a; 也可私信我要全图哦。 图里的链接可能点不…

软件测试工程师,月薪8000是一个什么样的工作状态?

这个工资得看是在什么地方&#xff0c;如果是在北上深&#xff0c;在互联网圈子里面&#xff0c;这个工资偏低&#xff0c;而且8000的话要解决生活各种开销&#xff0c;基本最后都是所剩无几。但是如果是在生存压力没有那么大的城市&#xff0c;8000基本算是很不错的一个薪资待…

IP多播技术详解

文章目录前言IP多播技术的相关基本概念IP多播地址和多播组在局域网上进行硬件多播IP多播地址和多播MAC地址映射关系在因特网上进行IP多播网际组管理协议IGMP多播路由选择协议前言 随着计算机网络的发展和个人计算机的普及&#xff0c;人们能够方便的在网络上畅游&#xff0c;进…

【大数据】YARN节点标签Node Label特性

简介 YARN 的 Node-label 特性能够将不同的机器类型进行分组调度&#xff0c;也可以根据不同的资源要求进行分区调度。运维人员可以根据节点的特性将其分为不同的分区来满足业务多维度的使用需求。YARN的Node-label功能将很好的试用于异构集群中&#xff0c;可以更好地管理和调…

使用 RGB 值设置颜色

使用 RGB 值设置颜色 另一种可以在 CSS 中表示颜色的方法是使用 RGB 值。 RGB 即红色、绿色、蓝色&#xff08;英语&#xff1a;Red, Green, Blue&#xff09;。 ● 红色&#xff08;R&#xff09;0 到 255 间的整数&#xff0c;代表颜色中的红色成分。。 ● 绿色&#xff08;G…

【数据结构】基础:图的最小生成树(附C++源代码)

【数据结构】基础&#xff1a;图的最小生成树&#xff08;附C源代码&#xff09; 摘要&#xff1a;将会在数据结构专题中开展关于图论的内容介绍&#xff0c;其中包括四部分&#xff0c;分别为图的概念与实现、图的遍历、图的最小生成树以及图的最短路径问题。本文主要介绍Krus…

机器学习经典算法总结

一&#xff0c;KNN 算法 K 近邻算法&#xff08;KNN&#xff09;是一种基本分类和回归方法。KNN 算法的核心思想是如果一个样本在特征空间中的 k 个最相邻的样本中的大多数属于一个类别&#xff0c;那该样本也属于这个类别&#xff0c;并具有这个类别上样本的特性。该方法在确…

知识图谱表示

知识图谱表示 用某种语言对知识图谱进行建模&#xff0c;从而方便知识运算。 符号知识表示 &#xff1a;以符号逻辑为基础的知识表示方法。 特点&#xff1a;易于刻画显示、离散的知识。具有内生的可解释性。 不足&#xff1a;部分隐性知识不易符号化表示&#xff0c;故知识表…

Pr LOGO片头动画

哈喽&#xff0c;各位小伙伴&#xff01;今天我们来学习一下如何制作简约文字LOGO片头动画&#xff1f; 新建序列 新建一个1920*1080的序列&#xff0c;在字幕上打上我们的文字&#xff08;例&#xff1a;Ultraman&#xff09; 调出基本面板&#xff0c;调整文字的位置&…

全栈项目【尚医通】预约挂号系统项目介绍

&#x1f43c;个人主页&#xff1a;爪哇斗罗 &#x1f43c;博主介绍&#xff1a;一名打工人 &#x1f43c;签名&#xff1a;圣人之道&#xff0c;为而不争。 &#x1f43c;一起交流&#xff0c;一起进步&#xff0c;一起互动。 ​ 目录 一.项目介绍 二.技术架构 2.1 架构选…

SpringBoot集成Swagger3(powernode document)(内含源代码)

SpringBoot集成Swagger3&#xff08;powernode document&#xff09;&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87449720 目录SpringBoot集成Swagger3&#xff08;powernode document&#xff0…

数据库锁的12连问,你顶得了嘛?

前言 金三银四很快就要来啦&#xff0c;准备了数据库锁的12连问&#xff0c;相信大家看完肯定会有帮助的。 1. 为什么需要加锁 在日常生活中&#xff0c;如果你心情不好想静静&#xff0c;不想被比别人打扰&#xff0c;你就可以把自己关进房间里&#xff0c;并且反锁。这就是…

Simulink自动代码生成:如何标准化的建模?以MAB,MISRA C 2012建模规范为例

目录 为什么要规范建模 MAB&#xff0c;MISRA C2012建模规范步骤 常用的规范总结 生成代码配置 总结 为什么要规范建模 MathWorks 咨询委员会 (MAB) 规范规定了在 Simulink 和 Stateflow 中建模的重要基本规则。这些建模规范的总体目的是让控制系统模型的建模者和使用方能…

自定义ESLint规则和修复功能

这是接上一篇自定义ESLint规则开发与使用的后续扩展&#xff0c;之前文章中详细讲述了怎么创建一个自定义的规则&#xff0c;这篇文章讲述怎么实现ESLint在检测出有问题的代码时&#xff0c;怎么自动fix问题。 比如我们要检测项目中所有http的协议&#xff0c;将其替换为https协…

Power BI 存储模式介绍(导入、DirectQuery、双)

本系列的文章&#xff1a; 《Power BI windows下载安装流程&#xff09;》《Power BI 11个必学官方示例数据案例&#xff08;附下载链接&#xff09;》《Power BI 数据导入&#xff08;SQL Server、MySQL、网页数据&#xff09;》 一、背景原因 一般情况下&#xff0c;我们是…

测试开发面试基础题

1.对测试开发的理解 测试开发首先离不开测试&#xff0c;而软件测试是指&#xff0c;在规定的条件下对程序进行操作&#xff0c;以发现程序错误&#xff0c;衡量软件质量&#xff0c;并对其是否能满足设计要求进行评估的过程。 而且&#xff0c;现在不仅仅是通过手工测试来发…

安装less-loader5出现webpack版本不兼容

今天遇到一个问题&#xff1a; 安装less-loader5之后其它包提示peerDependencies WARNING&#xff0c;意思是包版本不兼容。 【难题】 虽然NPM已经很自动化了&#xff0c;但依赖问题真的是一个难题&#xff0c;无法自动解决&#xff0c;需要人工干预调整。 【解决办法】 去查…