地址:联系地址联系地址联系地址
电话:020-123456789
传真:020-123456789
邮箱:admin@aa.com
对主键来说,分库分表要保证在所有分片中都唯一,索引设计设计实践它本质上就是索局索一个全局唯一的索引 。如果用大部分同学喜欢的引全引自增作为主键 ,就会发现存在很大的最佳问题 。
因为自增并不能在插入前就获得值 ,分库分表而是索引设计设计实践要通过填 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 只存在于一个分片中。这时 ,可以有以下两种设计:
这两种设计的本质都是通过冗余实现空间换时间的效果 ,否则就需要扫描所有的分片,当分片数据非常多,效率就会变得极差。
而第一种做法通过对表进行冗余 ,对于 o_orderkey 的查询 ,只需要在 o_orderkey = 1的分片中直接查询就行 ,效率最高,但是设计的缺点又在于冗余数据量太大。
所以 ,改进的做法之一是实现一个索引表