分库分表索引设计:二级索引、全局索引的最佳设计实践 DATE: 2024-04-29 08:07:17
主键选择
对主键来说,分库分表要保证在所有分片中都唯一,索引设计设计实践它本质上就是索局索一个全局唯一的索引。如果用大部分同学喜欢的引全引自增作为主键,就会发现存在很大的最佳问题 。
因为自增并不能在插入前就获得值,分库分表而是索引设计设计实践要通过填 NULL 值,然后再通过函数 last_insert_id()获得自增的索局索值。所以,引全引如果在每个分片上通过自增去实现主键 ,最佳可能会出现同样的分库分表自增值存在于不同的分片上 。
比如,索引设计设计实践对于电商的索局索订单表 orders ,其表结构如下(分片键是引全引o_custkey,表的最佳主键是o_orderkey):
CREATE TABLE `orders` (n `O_ORDERKEY` int NOT NULL auto_increment,n `O_CUSTKEY` int NOT NULL,n `O_ORDERSTATUS` char(1) NOT NULL,n `O_TOTALPRICE` decimal(15,2) NOT NULL,n `O_ORDERDATE` date NOT NULL,n `O_ORDERPRIORITY` char(15) NOT NULL,n `O_CLERK` char(15) NOT NULL,n `O_SHIPPRIORITY` int NOT NULL,n `O_COMMENT` varchar(79) NOT NULL,n PRIMARY KEY (`O_ORDERKEY`),n KEY (`O_CUSTKEY`)n ......n) ENGINE=InnoDBn
如果把 o_orderkey 设计成上图所示的自增 ,那么很可能 o_orderkey 同为 1 的记录在不同的分片出现 ,如下图所示:
所以,在分布式数据库架构下,尽量不要用自增作为表的主键 :自增性能很差 、安全性不高、不适用于分布式架构。
讲到这儿,我们已经说明白了“自增主键”的所有问题 ,那么该如何设计主键呢?依然还是用全局唯一的键作为主键,比如 MySQL 自动生成的有序 UUID;业务生成的全局唯一键(比如发号器);或者是开源的 UUID 生成算法 ,比如雪花算法(但是存在时间回溯的问题)。
总之,用有序的全局唯一替代自增 ,是这个时代数据库主键的主流设计标准 ,如果你还停留在用自增做主键,或许代表你已经落后于时代发展了 。
索引设计
通过分片键可以把 SQL 查询路由到指定的分片 ,但是在现实的生产环境中,业务还要通过其他的索引访问表 。
还是以前面的表 orders 为例,如果业务还要根据 o_orderkey 字段进行查询 ,比如查询订单 ID 为 1 的订单详情:
SELECT * FROM orders WHERE o_orderkey = 1n
我们可以看到 ,由于分片规则不是分片键,所以需要查询 4 个分片才能得到最终的结果,如果下面有 1000 个分片,那么就需要执行 1000 次这样的 SQL ,这时性能就比较差了。
但是 ,我们知道 o_orderkey 是主键 ,应该只有一条返回记录,也就是说,o_orderkey 只存在于一个分片中。这时 ,可以有以下两种设计 :
- 同一份数据,表 orders 根据 o_orderkey 为分片键 ,再做一个分库分表的实现;
- 在索引中额外添加分片键的信息 。
这两种设计的本质都是通过冗余实现空间换时间的效果,否则就需要扫描所有的分片 ,当分片数据非常多 ,效率就会变得极差。
而第一种做法通过对表进行冗余 ,对于 o_orderkey 的查询,只需要在 o_orderkey = 1的分片中直接查询就行,效率最高 ,但是设计的缺点又在于冗余数据量太大。
所以 ,改进的做法之一是实现一个索引表