分库分表索引设计:分布式环境下的 主键索引、二级索引、全局索引的最佳设计实践
文章目录
- 主键选择
- 索引设计
- 全局表
- 唯一索引
- 总结
- 结语
主键选择
对主键来说,要保证在所有分片中都唯一,它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键,就会发现存在很大的问题。
因为自增并不能在插入前就获得值,而是要通过填 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 INTo_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
结语
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。
相关文章:
分库分表索引设计:分布式环境下的 主键索引、二级索引、全局索引的最佳设计实践
文章目录主键选择索引设计全局表唯一索引总结结语主键选择 对主键来说,要保证在所有分片中都唯一,它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键,就会发现存在很大的问题。 因为自增并不能在插入前就获得值…...
2023年全国最新保安员精选真题及答案
百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 一、单选题(1-480题)以下备选答案中只有一项最符合题目要求&a…...
计算机网络之http07 http2,http3
HTTP1.2 http1.2都做了哪些优化 (1)头部压缩 使用HPACK压缩头部 头部冗长,大量重复字段 (2)二进制帧 将报文头部和内容字符编码改为二进制格式 字符编码未压缩 (3)并发传输 解决h1.1 队头阻塞问题,多车道 …...
内网渗透(二十五)之Windows协议认证和密码抓取-使用Hashcat和在线工具破解NTLM Hash
系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...
TongWeb8防止System.exit代码导致的进程停止
现象:当应用中存在System.exit 、Runtime.exit代码执行时,会导致TongWeb进程停止,从而产生如下日志:2023-02-14 09:47:36 [WARN] - The web application [webtest01] is still processing a request that has yet to finish. This…...
PMP每年考几次,费用如何?
一,PMP每年考几次,怎么准备? PMP项目管理证书是美国PMI发起的在全球200多个国家进行的项目管理专业人士资格认证,它的含金量和给认证者带来的作用已经很明显。 PMP考试是项目管理专业人士资格认证考试,通过PMP考试是…...
【Kubernetes】【一】Kubernetes介绍
Kubernetes介绍 应用部署方式演变 在部署应用程序的方式上,主要经历了三个时代: 传统部署:互联网早期,会直接将应用程序部署在物理机上 优点:简单,不需要其它技术的参与 缺点:不能为应用程序定…...
C语言:结构体
往期文章 C语言:初识C语言C语言:分支语句和循环语句C语言:函数C语言:数组C语言:操作符详解C语言:指针详解 目录往期文章前言1. 结构体的声明2. 结构体变量的定义和初始化3. 结构体成员的访问3. 结构体传参…...
搭建pclpy环境与读取pandaset数据并转换为pkl格式为pcd格式
1.搭建pclpy环境 问题:需要处理pcd文件,于是开始摸索搭建环境,有python-pcl,但是安装过程频频出现问题,于是转向pclpy。 参考链接:GitHub - davidcaron/pclpy: Python bindings for the Point Cloud Libr…...
别在用scroll去做懒加载了,交叉观察器轻松搞定
Ⅰ、前言 「懒加载」是网页中非常 常见的;为了减少系统的压力,对于一些电商系统出场频率非常高;那么大家一般用什么方式去实现 「懒加载」 呢 ? ① 通过 scroll 的形式: 通过 滚动「scroll」事件,然后去判…...
工欲善其事,必先利其器,分享5款Windows效率软件
工欲善其事,必先利其器。作为全球最多人使用的桌面操作系统,Windows 的使用效率与我们的工作学习息息相关。今天,小编就为大家整理了5款提高效率的利器,让你的 Windows 更具生产力。 1.桌面自定义——Rainmeter Rainmeter是一款…...
机器学习笔记之生成模型综述(四)概率图模型 vs 神经网络
机器学习笔记之生成模型综述——概率图模型vs神经网络引言回顾:概率图模型与前馈神经网络贝叶斯网络 VS\text{VS}VS 神经网络表示层面观察两者区别推断、学习层面观察两者区别引言 本节将介绍概率图模型与神经网络之间的关联关系和各自特点。 回顾:概率…...
微信小程序 组件与页面交互 无反应的问题
使用组件 声明组件 1.在目录中右键,新建components 2.在页面的json,属性中加入"component": true, 编写组件 父 声明: "usingComponents": {"address": "../../components/address/address"},…...
maven相关概念以及no dependency information available错误修改
一,相关概念 1,Maven坐标 Maven定义了这样一组规则:世界上任何一个构件都可以使用Maven坐标唯一标识,Maven坐标元素包括groupId、artifactId、version、packaging、classifier,现在只要我们提供正确的元素坐标&#x…...
QML- 属性绑定
QML- 属性绑定一、概述二、 QML绑定使用三、从JavaScript创建属性绑定1. 调试绑定的覆盖2. 属性绑定使用 this一、概述 QML对象的属性可以被赋一个静态值,该值保持不变,直到显式地赋一个新值。但是,为了充分利用QML及其对动态对象行为的内置…...
MFC CObject的使用
目录1 从 CObject 派生类1.1 使用基本 CObject 功能1.2 添加运行时类信息1.3 添加动态创建支持1.4 添加序列化支持2 访问运行时类信息3 动态对象创建1 从 CObject 派生类 在 CObject 的讨论中,经常使用术语“接口文件”和“实现文件”。 接口文件(通常称…...
CNI 网络流量分析(六)Calico 介绍与原理(一)
文章目录CNI 网络流量分析(六)Calico 介绍与原理(一)介绍安装Calico-node初始化Calico-node 服务Felixconfdallocate-tunnel-addrsmonitor-addressesmonitor-tokenstatus-reporterbirdcalico-kube-controllersCNI 网络流量分析&am…...
机器视觉_HALCON_示例实践_1.检测圆形
文章目录一、引言二、检测圆形三、总结一、引言 前面的文(用户指南/快速向导)差不多已经把HALCON的基本内容讲完了,并且在学习过程中还跑过一个简单示例——在单一背景下定位回形针。示例跑过,顿时觉得自己行了,但如果…...
使用yolov5训练数据集笔记
准备工作 1. 安装labelimg labelimg:主要用于目标检测的目标框绘制,得到关于我们训练的边框位置、类别等数据 pip install labelimg2. 下载yolov5源码 我使用的是v7.0版本,直接下载即可,下载后解压出来 2.1 安装yolov5运行依赖包 进入…...
常用类详解(三)StringBuilder
(1)一个可爱的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全的),该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类&#x…...
快速学C语言——第 4 章:运算符与表达式
第 4 章:运算符与表达式 在编程中,我们经常需要对数据进行各种计算和比较,就像在数学中一样。C 语言提供了一系列运算符,让我们能够对变量和值进行赋值、计算、比较、逻辑判断等操作。将变量、常量和运算符按照语法规则…...
保姆级教程:在Win10上从零配置OpenSSH服务器,并用Termius实现iPad远程连接(含防火墙和用户权限避坑指南)
从零构建Win10 SSH服务:用Termius实现iPad远程开发的完整指南 当你躺在沙发上用iPad突然想修改一段代码,或是出差时急需访问家中电脑的文件,Win10自带的OpenSSH服务配合Termius这款优雅的SSH客户端,能让你摆脱物理距离的限制。但官…...
Switch游戏文件管理的终极解决方案:5步掌握NSC_BUILDER批量处理技巧
Switch游戏文件管理的终极解决方案:5步掌握NSC_BUILDER批量处理技巧 【免费下载链接】NSC_BUILDER Nintendo Switch Cleaner and Builder. A batchfile, python and html script based in hacbuild and Nuts python libraries. Designed initially to erase titleri…...
Spring Statemachine详解底层和落地
一、什么是状态机?为什么 Spring 要专门封装它 1.1 从“if-else 海啸”说起 在任何一个具有多状态的生命周期管理场景中,这种代码非常常见: if (order.getStatus() == OrderStatus.CREATED) {if (event == Event.PAY) {// 支付逻辑order.setStatus(OrderStatus.PAID);} e…...
视频加速控制器:如何用2倍速度看完一天的学习内容
视频加速控制器:如何用2倍速度看完一天的学习内容 【免费下载链接】videospeed HTML5 video speed controller (for Google Chrome) 项目地址: https://gitcode.com/gh_mirrors/vi/videospeed 还在为视频播放速度太慢而烦恼吗?每天面对海量的在线…...
音乐无界:解锁网易云音乐灰色歌曲的智能方案
音乐无界:解锁网易云音乐灰色歌曲的智能方案 【免费下载链接】UnblockNeteaseMusic Revive unavailable songs for Netease Cloud Music 项目地址: https://gitcode.com/gh_mirrors/un/UnblockNeteaseMusic 你是否曾经打开网易云音乐,发现心爱的歌…...
Neat Bookmarks:重构浏览器书签管理的技术架构与实践方案
Neat Bookmarks:重构浏览器书签管理的技术架构与实践方案 【免费下载链接】neat-bookmarks A neat bookmarks tree popup extension for Chrome [DISCONTINUED] 项目地址: https://gitcode.com/gh_mirrors/ne/neat-bookmarks 开篇:数字信息过载时…...
技术写作如何‘破圈’?从周志明《智慧的疆界》聊聊给非技术朋友讲AI的实用技巧
技术写作如何‘破圈’?从周志明《智慧的疆界》聊聊给非技术朋友讲AI的实用技巧 技术写作的本质是信息传递的艺术,但当受众从同行专家变成产品经理、运营人员甚至完全不懂技术的朋友时,这项艺术就变成了需要刻意练习的"翻译"技能。周…...
3步完成Android Studio中文界面汉化:提升Android开发效率的终极指南
3步完成Android Studio中文界面汉化:提升Android开发效率的终极指南 【免费下载链接】AndroidStudioChineseLanguagePack AndroidStudio中文插件(官方修改版本) 项目地址: https://gitcode.com/gh_mirrors/an/AndroidStudioChineseLanguagePack 你…...
STM32F103 Flash读写避坑大全:从解锁失败到数据丢失,我踩过的坑你别再踩
STM32F103 Flash读写避坑大全:从解锁失败到数据丢失,我踩过的坑你别再踩 第一次在STM32F103上操作内部Flash时,我以为按照手册步骤就能轻松完成。直到调试灯疯狂闪烁、数据神秘消失、芯片莫名锁死,才意识到这片存储区域远没有想象…...
