最近工作上在做基础服务的性能优化,分库分表自然是大数据表常用优化利器之一,除此之外还有针对查多增少的场景进行缓存优化也是常见方法,下面
主要总结下分库分表时候不同业务表的一些处理思路。
注意,文中分库分表和分表 并不做特别区分,两者在本文讨论场景下的处理思路都是一样的。
除非公司初期的工程师经验丰富(这很难),对于大多数不断壮大的公司来说,工程师一波一波的更替,总会遇到一些高速堆需求时候,前同事们一时不小心 考虑不周,遗留下的技术债。 尤其是表结构,一旦最初设计不合理,后面很难改正过来,比代码层面换编程语言还难。相反每次建表时候,多迟疑下,更合理些,后面开发会顺利很多。
| token | user_id | x | y | z |
|---|---|---|---|---|
| asc893ffdads | 6210983232 | 字段x | 字段y | 字段z |
假如入有上面一个表,其中token 是个字符串且是这个表的主键。
现在需要对这个表进行水平分表,同时不影响现有对外暴露的接口业务:
- 根据token查询,getByToken()
- 根据user_id查询,getByUserId()
经过调研发现80%请求都是byToken.所以 对token取hash值 ,然后取模。这时候getByToken可以直接定位到对应的分表,进行查询。 而对于user_id,由于其可能散落在任何一个分表中,那么就需要并行把所有分表都查一次,然后聚合。 如果一个user_id只对应一个token,岂不是n-1次查询都是无效的(n是分表数量),本来需要查一次的,分完后居然多查了很多次,而且 大多是无效的消耗,虽然其只占了20%的流量。
还想优化怎么办? 再次回归到业务,分析发现业务getByUserId接口都是查询user_id>0 的,而此类数据3~5年内也就千万级别,所以再次优化如下: 建立一个映射表userid_token_map,表中只存有user_id--->token 两个字段的映射关系,其他业务字段不要。然后当getByUserId时候,先从映射表查询到对应的token, 然后再通过token直接定位到对应表,获取其他业务字段。这时候就上面n从请求降低为2次了。可以接受。
还有一些思考:
- 覆盖索引在映射表中那些业务下可以应用。
- 怎么判断衡量添加map映射方式是一种治标不治本处理方式 or 完善解决
- 万一若干年后map映射表需要分表,是否可行。
- 根本上如何解决,最初表该怎样设计。