分库分表
12306为什么要分库分表
12306选择分库分表是为了应对高并发的写入请求和数据量增长带来的挑战,提高系统的写入性能和查询性能。
- 写入压力分散:在高并发场景下,单一数据库可能无法承受大量的写入请求,导致性能瓶颈。通过分库,可以将数据按照一定规则拆分到多个数据库中,每个数据库只处理部分数据的写入请求。这样,每个数据库所承受的写入压力被大大降低,从而提高了整体的写入性能。
- 存储压力分散和提高查询性能:随着12306项目的数据量的不断增加,单一数据库的存储和查询性能可能逐渐下降。此时,可以将数据按照一定的规则拆分到多个表中,每个表存储部分数据,从而分散数据的存储压力,使每个表的查询范围缩小,提高查询性能。
分库分表带来的问题
读请求扩散问题
在某些场景下(如用户登录使用电话号码登录),由于无法确定用户的分片键,不知道具体查询哪个数据库,可能导致读请求需要扫描多个数据库或表以查找数据。
这将大大增加系统的查询负载和响应时间,严重时可能导致登录超时等问题。
全局唯一性ID问题
在之前的单库环境时,对于一张表的主键通常会选用整数型字段,然后通过数据库的自增机制来保证唯一性,但在水平分库多节点的情况时,假设还是以数据库自增机制来维护主键唯一性,可能会导致多个库中出现ID相同、数据不同的情况。
用户分块分表如何实现
使用ShardingSphere-JDBC对用户表进行分库分表
引入 ShardingSphere-JDBC 依赖
在配置文件上定义分片规则后,就可以使用数据库分库分表了。
综合考虑使用username作为分库分表的分片键
从查询优化来看:username是用户登录和查询个人信息时常用的字段。将其作为分片键,可以使得相关的查询操作能够直接定位到特定的分片上,从而减少了跨分片查询的需求,提高了查询效率。
从避免热点数据来看:username`通常具有唯一性和较高的随机性,这有助于保证数据在各个分片上的均匀分布。这样可以避免出现某个分片上的数据过于集中,导致热点问题和性能瓶颈。
从稳定性来看:一旦选择了分片键,它应该是不可变的。username`在用户注册后通常不会发生变化,因此满足这一要求。这保证了分片策略的稳定性和可靠性。
使用分库分表策略对用户表进行拆分
- 计算用户username的哈希值。
- 使用哈希值对sharding-count(总分片数)取模,得到一个表分片索引。
- 根据索引选择目标表分片。
- 再除以一个库中分片数,得到要分配库的数字。
比如总分片数是32,一个库中分片数是16,可以得到分配的库的数字是0和1,加上前缀就是分到的数据库的名字。
订单号分库分表如何实现
计划订单分库分表的基本查询条件是用户要能查看自己的订单,另外,也要支持订单号精准查询。
这样的话,就需要按照两个字段当做分片键,这也就意味着每次查询时需要带着用户和订单两个字段,非常的不方便。
因此我们设计一个字段分库分表,查询时有用户id和订单号任意一个就能精准查询,而不导致读扩散问题
这里我们用基因算法解决以上问题。
当我们创建订单号时,将用户id后六位数加在订单号后面,这样订单号和用户id后六位数是一样的,
分库分表的时候,如果SQL字段有用户id,对用户id后六位数取模,得到要分配的目标表。
如果没有用户id,就用订单号后六位数字取模,得到要分配的目标表。
因为订单号和用户id后六位数是一样的,查询时, 只要带有任意这两个字段其一,都能根据后六位数找到本人订单分配表的位置。
乘车人如何查询本人订单
12306系统中,一个账号可以为多人,也就是多位乘车人,购买车票。购票时,乘车人可能还没有12306账号。
乘车人注册12306账号后,能够通过“本人车票”功能查看自己出行的车票数据记录。
因为证件号是乘车人唯一标识,因此需要通过乘车人的证件号查询本人订单。
又因为订单表分片键并非证件号ID,直接查找会导致”读请求扩散问题”,因此我们需创建路由表,通过证件号绑定订单号。
路由表的分库分表规则是按照证件号进行 HASH_MOD 。
当乘车人查询本人车票时
先按照本人证件号分库分表规则得到要查询的路由表,再根据证件号得到对应的订单号集合,
根据订单号,通过基因算法得到分配的订单表和订单明细表。
再根据订单号得到订单数据,根据订单号和乘车人证件号得到订单明细数据,最后封装返回这些数据。