沈剑:数据库切分技术实践解析

GitChat

2017年2月23日周三晚8点30分,“架构师之路”公众号作者、58到家技术委员会主席沈剑带来了主题为《互联网数据库“跨库分页”架构技术实践》的分享,以下是主持人赫阳整理的实录,记录下了作者和读者问答的精彩片段。


问:目前准备做数据库水平切分,需要注意什么关键问题?目前了解需要避免跨库事务,请老师指点。

答:

  1. 需要注意分库patitionkey的选取,要保证两个均衡:数据量的均衡,请求量的均衡。

  2. 需要注意分库后,之前用SQL满足的需求是否还能满足,需要怎么改进满足,例如max、min、avg、sum都需要在服务层再做一次聚合。

  3. 夸库事务,分布式事务,在吞吐量是主要矛盾的互联网场景,目前没有能够很好解决的方案,尽量避免。


问:采用hash取模方式的表扩容策略及采用一致性hash分表的表扩容策略如何实现?

:数据库水平切分的方式,常用的有两种:

  1. hash取模:user_id%2=0为0库,user_id%2=1为1库。
  2. 数据分段:user_id属于[0, 1亿]为0库,属于[1亿, 2亿]为2库。

方案一

方案二

互联网95%使用方案一。


问:1)上述的的几种分库方案我都用过了,取模的方案考虑到大表的增长速度总是难以预料,而且一旦确定了模数,要改还要考虑一堆兼容性问题,所以线上方案改造为除数的方法,缺点是时不时要去加下表。还有个问题,全局递增唯一键有没有什么高效的方案?2)目前有两种方案,放在缓存里自增,以及数据库自增,然而感觉这些逻辑拆的比较散,如利用缓存,还要考虑缓存丢失怎么办?3)mysql等数据库没有有一个分装好的这些分表解决方案呢?另外,你们一般业务用事务的情况多吗?

:1)成倍扩容可以实现平滑,之前有撰文专门写过,非常帅气的数据库秒级扩容方案;参考《数据库秒级平滑扩容架构方案》

非成倍扩容需要进行数据迁移,如何实现不停服务,平滑的数据迁移,一言难尽,未来撰文详述。

2)对于唯一主键,一般有三类需求:

  1. 全局唯一(强需求,必须满足)。
  2. 全局趋势递增(有最好)。
  3. 全局递增(比较难)。

解决方案有这么几种:

  1. 数据库自增id,能够满足1,2,3,但性能差。

  2. 数据库自增id+上游加一个服务,批量生成,能够满足1,2,3,性能较好,但可能出现id空洞(服务挂掉后,未分配出去的id不会再被分配了)。

  3. uuid/guid,能够满足1,性能无限,但生成id很长64bit放不下,如果折半成64bit可能会出现id重复。

  4. timeMS取毫秒数,能够满足2,但并发量大会重复。

  5. 类snowflake算法,能够满足1、2,可以认为性能无限,是目前互联网圈用的最广的方法。

《细聊分布式ID生成方法》中可见更详细的做法。

3)阿里,腾讯,百度,360都有一些实践,mycat也有人用,具体可以调研一下。

实现方式一般有两种,基于客户端的(阿里cobar),基于服务端的(百度dbproxy)至于事务,前台用户侧大数据,高并发业务,几乎不用事务。 钱相关的,单库会用事务,跨库事务也很难保证一致性。

多库事务可以参考这篇文章进行优化:《多库多事务降低数据不一致概率》


问:offset(pageNo-1)*pageSizelimit pageSize。二次查询在分布不均的情况下,三页最小值远小于pageNo,比如333+0+0,极端情况下需要查询5次,如果pageNo=10000遇到这种情况怎么解决?

:如文章中第三种方案所述,“数据均衡原理”,一般数据分布是均匀的;极限情况下如果不均衡,第二次查询会返回大量的数据,性能会急剧下降。


问:可以通过搜索引擎解决分页的问题吗?

:“准确率”和“召回率”作为评估指标,潜台词是,数据是不准确的。所以一般不用搜索引擎实现分页。如果业务能接受不准确,可以使用文章中的方案三。


问:第四种不是还是需要进行内存排序么?如果单页请求书很大依然还是有问题的。

:不需要进行内存排序,排序的复杂度是n*log(n)。第四种方案,每一页返回的数据都是有序的,三个指针指向表头,比较表头,扫一遍可以得到全部顺序,复杂度为n,况且不需要全部扫一遍。


问:在做服务化后,业务会拆分不同的库和不同表中。业务需求有时候需要连表模糊查询,查询结果还要分页。这个时候我们目前是做冗余字段,我想问一下有没有更好的解决方案?

:服务化之后,业务只提需求,是否连表,是服务层需要决定的。服务内部的库,即使连表,对调用方也透明。服务外部的库,无法连表,可以通过冗余数据解决。

架构设计,本身是方案折衷,要想高可用,必须冗余,一旦冗余,必定会有一致性问题,没有十全十美的方案,看业务主要矛盾了。


问:这些分页方案的性能有多大差距?

:方案一,随着页码的增加,网络传输成n系数增加,排序性能成n*n指数增加。

方案二,性能是固定值,O(n)。

方案三,性能最好。

方案四,性能是固定值,O(n),不过要查询2次。


问:如果分库分表的情况下碰到要对一个表或多个表关联并且按多个字段为条件进行检索的情况下怎么办呢?

:所有人都在问分库后,join怎么办,我只能这么解释。

  1. 前端用户侧业务,流量大,并发大,join真的很少,58同城用户库几亿数据,帖子库300亿数据,没有join。

  2. 如果真要join,分库后冗余数据、索引表、分页,for循环低效查询 -> 总能解决的,只是看性能是不是主要矛盾、一致性是不是主要矛盾了。

拆成小sql是互联网的玩法,互联网很少用join、子查询、视图、外键、用户自定义函数、存储过程的。当然,我指面向用户侧的业务。

问:据说mycat可以解决分库的join问题,就是不知道性能如何?

:我猜测,1. 解决了部分;2. 性能不会太好。

参见《58到家数据库30条军规解读》《再议数据库军规》,我们的mysql这么玩。


问:沈老师推荐什么中间件呢?解决什么问题? “服务化+分库”能解决么?

:中间件,58同城是自研的。

服务化的第一条:不服务化(弄清楚为什么要服务化,解决什么问题)。

分库的第一条:不分库(弄清楚为什么要分库,解决什么问题)。

58到家目前没有用中间件,分库的业务很少,分库后都是服务层做聚合。


问:单表多大数据量时才考虑分库分表?

:“单表多大数据量时才考虑分库分表”,我们的经验,mysql,1000w,要考虑分了。如果查询比较简单,5000w。


问:相同的查询条件,生成不同的报表(如:按媒体类型、媒体、正负面等),数据量大的情况有哪些解决方案?

:报表这类非实时需求,让擅长大数据计算的平台搞比较合适。 每个公司应该都有BI,haddop,数据仓库等,解决报表类需求比较好。单表多大数据考虑拆分,还是看业务场景,用户表,uid查询,1亿没问题。


问:军规里面写了单库500表,请问是怎么考虑的?目前我们有分表上千了。

:1000表,单库,耦合估计很严重(特别是join多的话),未来数据量大了,不好拆。


问:我们业务使用了1000个分片表,这样可以吗?

:个人建议,一律使用分库,而不是分表。

分表:

  1. 表名不同吧?DAO层搞一个实例,还是多个实例,还是怎么trick一下?
  2. 物理上,还是在一个库文件里,还是有潜在瓶颈。
  3. 未来扩展到多机,比较麻烦。

所以,建议一律分库。

副作用是,数据库连接会比较多,但一般不是瓶颈。我发现数据库的文章,大家比较喜欢读。我花了好几天写的“搜索引擎”的文章,阅读量比较低 =_=


问:跨库后增加数据库主机,每个表实现再散列后,在保持服务的情况下,如何更新表的数据呢?

:非常好的问题,如何实现不停服务,平滑的数据迁移,至少有两种方案,未来撰文,请大伙持续关注GitChat哟。


(以上内容转自GitChat,版权归GitChat所有,转载请联系GitChat,微信号:GitChat,原文:《沈剑:数据库切分技术实践解析》