当前位置: 首页 > news >正文

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

文章目录

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

主键选择

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

因为自增并不能在插入前就获得值,而是要通过填 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

结语

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

相关文章:

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

文章目录主键选择索引设计全局表唯一索引总结结语主键选择 对主键来说&#xff0c;要保证在所有分片中都唯一&#xff0c;它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键&#xff0c;就会发现存在很大的问题。 因为自增并不能在插入前就获得值&#xf…...

2023年全国最新保安员精选真题及答案

百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 一、单选题&#xff08;1-480题&#xff09;以下备选答案中只有一项最符合题目要求&a…...

计算机网络之http07 http2,http3

HTTP1.2 http1.2都做了哪些优化 (1)头部压缩 使用HPACK压缩头部 头部冗长&#xff0c;大量重复字段 &#xff08;2&#xff09;二进制帧 将报文头部和内容字符编码改为二进制格式 字符编码未压缩 &#xff08;3&#xff09;并发传输 解决h1.1 队头阻塞问题&#xff0c;多车道 …...

内网渗透(二十五)之Windows协议认证和密码抓取-使用Hashcat和在线工具破解NTLM Hash

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...

TongWeb8防止System.exit代码导致的进程停止

现象&#xff1a;当应用中存在System.exit 、Runtime.exit代码执行时&#xff0c;会导致TongWeb进程停止&#xff0c;从而产生如下日志&#xff1a;2023-02-14 09:47:36 [WARN] - The web application [webtest01] is still processing a request that has yet to finish. This…...

PMP每年考几次,费用如何?

一&#xff0c;PMP每年考几次&#xff0c;怎么准备&#xff1f; PMP项目管理证书是美国PMI发起的在全球200多个国家进行的项目管理专业人士资格认证&#xff0c;它的含金量和给认证者带来的作用已经很明显。 PMP考试是项目管理专业人士资格认证考试&#xff0c;通过PMP考试是…...

【Kubernetes】【一】Kubernetes介绍

Kubernetes介绍 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应用程序定…...

C语言:结构体

往期文章 C语言&#xff1a;初识C语言C语言&#xff1a;分支语句和循环语句C语言&#xff1a;函数C语言&#xff1a;数组C语言&#xff1a;操作符详解C语言&#xff1a;指针详解 目录往期文章前言1. 结构体的声明2. 结构体变量的定义和初始化3. 结构体成员的访问3. 结构体传参…...

搭建pclpy环境与读取pandaset数据并转换为pkl格式为pcd格式

1.搭建pclpy环境 问题&#xff1a;需要处理pcd文件&#xff0c;于是开始摸索搭建环境&#xff0c;有python-pcl&#xff0c;但是安装过程频频出现问题&#xff0c;于是转向pclpy。 参考链接&#xff1a;GitHub - davidcaron/pclpy: Python bindings for the Point Cloud Libr…...

别在用scroll去做懒加载了,交叉观察器轻松搞定

Ⅰ、前言 「懒加载」是网页中非常 常见的&#xff1b;为了减少系统的压力&#xff0c;对于一些电商系统出场频率非常高&#xff1b;那么大家一般用什么方式去实现 「懒加载」 呢 &#xff1f; ① 通过 scroll 的形式&#xff1a; 通过 滚动「scroll」事件&#xff0c;然后去判…...

工欲善其事,必先利其器,分享5款Windows效率软件

工欲善其事&#xff0c;必先利其器。作为全球最多人使用的桌面操作系统&#xff0c;Windows 的使用效率与我们的工作学习息息相关。今天&#xff0c;小编就为大家整理了5款提高效率的利器&#xff0c;让你的 Windows 更具生产力。 1.桌面自定义——Rainmeter Rainmeter是一款…...

机器学习笔记之生成模型综述(四)概率图模型 vs 神经网络

机器学习笔记之生成模型综述——概率图模型vs神经网络引言回顾&#xff1a;概率图模型与前馈神经网络贝叶斯网络 VS\text{VS}VS 神经网络表示层面观察两者区别推断、学习层面观察两者区别引言 本节将介绍概率图模型与神经网络之间的关联关系和各自特点。 回顾&#xff1a;概率…...

微信小程序 组件与页面交互 无反应的问题

使用组件 声明组件 1.在目录中右键&#xff0c;新建components 2.在页面的json&#xff0c;属性中加入"component": true, 编写组件 父 声明&#xff1a; "usingComponents": {"address": "../../components/address/address"},…...

maven相关概念以及no dependency information available错误修改

一&#xff0c;相关概念 1&#xff0c;Maven坐标 Maven定义了这样一组规则&#xff1a;世界上任何一个构件都可以使用Maven坐标唯一标识&#xff0c;Maven坐标元素包括groupId、artifactId、version、packaging、classifier&#xff0c;现在只要我们提供正确的元素坐标&#x…...

QML- 属性绑定

QML- 属性绑定一、概述二、 QML绑定使用三、从JavaScript创建属性绑定1. 调试绑定的覆盖2. 属性绑定使用 this一、概述 QML对象的属性可以被赋一个静态值&#xff0c;该值保持不变&#xff0c;直到显式地赋一个新值。但是&#xff0c;为了充分利用QML及其对动态对象行为的内置…...

MFC CObject的使用

目录1 从 CObject 派生类1.1 使用基本 CObject 功能1.2 添加运行时类信息1.3 添加动态创建支持1.4 添加序列化支持2 访问运行时类信息3 动态对象创建1 从 CObject 派生类 在 CObject 的讨论中&#xff0c;经常使用术语“接口文件”和“实现文件”。 接口文件&#xff08;通常称…...

CNI 网络流量分析(六)Calico 介绍与原理(一)

文章目录CNI 网络流量分析&#xff08;六&#xff09;Calico 介绍与原理&#xff08;一&#xff09;介绍安装Calico-node初始化Calico-node 服务Felixconfdallocate-tunnel-addrsmonitor-addressesmonitor-tokenstatus-reporterbirdcalico-kube-controllersCNI 网络流量分析&am…...

机器视觉_HALCON_示例实践_1.检测圆形

文章目录一、引言二、检测圆形三、总结一、引言 前面的文&#xff08;用户指南/快速向导&#xff09;差不多已经把HALCON的基本内容讲完了&#xff0c;并且在学习过程中还跑过一个简单示例——在单一背景下定位回形针。示例跑过&#xff0c;顿时觉得自己行了&#xff0c;但如果…...

使用yolov5训练数据集笔记

准备工作 1. 安装labelimg labelimg:主要用于目标检测的目标框绘制&#xff0c;得到关于我们训练的边框位置、类别等数据 pip install labelimg2. 下载yolov5源码 我使用的是v7.0版本&#xff0c;直接下载即可&#xff0c;下载后解压出来 2.1 安装yolov5运行依赖包 进入…...

常用类详解(三)StringBuilder

(1)一个可爱的字符序列。此类提供一个与StringBuffer兼容的API&#xff0c;但不保证同步(StringBuilder不是线程安全的)&#xff0c;该类被设计用作StringBuffer的一个简易替换&#xff0c;用在字符串缓冲区被单个线程使用的时候。如果可能&#xff0c;建议优先采用该类&#x…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...