分库分表的认知与学习

分库分表入门必读:分库分表不是银弹。分库分表不仅增加运维的复杂程度,同时数据库服务器也会成倍增加,成本激增。

最近研究了一下分库分表的一些常用技术,因为目前公司还没有这种较强的需求,所以下文更偏重一些技术上的思考。

为什么要分库分表?

分库分表对我们的开发和运维均有一定的挑战,除非必要否则不要提升我们的技术难度。

数据库瓶颈

数据热点过多,数据库负载过大

solve: 热点过多可以考虑加入多级缓存,或创建多从数据库也可提升查询效率。如有必要可以引入elasticsearch等NOSQL提升数据查询效率。

某些表数据量过大,查询延迟较大

solve: 数据量过大分数据列数过多和数据行数过多,若列数过多可考虑根据业务规则对频繁修改的数据进行分离,单独存放。若数据行数过多可以考虑对冷热数据进行分离,归类存储,针对需要查询的往期数据可以在对数据冷备时写入es等专为查询实现的数据库。

SQL比较复杂,查询较慢

solve: 简化sql,分步查询走从库,拆分后能缓存则缓存。

网络瓶颈

查询较多,单机网络带宽不足

solve: 多从可一定程度解决带宽问题。

如何分库分表

如果以上方案任无法解决问题,那么分库分表如何下手呢?

分表

垂直分表

前面提到表列数过多实际上可采用此方案,对频繁修改的列数据分离存放,根据业务进行拆分,降低单表列数量。
缺点:查询次数上升。

水平分表

水平分表一般是单表数据量较多时使用。可根据日期range进行保存(每天|周|月|年一个表),根据某个字段进行range,将一个表中的数据拆分到多个表中。
缺点:查询复杂,如果对查询有要求此方案不推荐。

分库

垂直分库

根据业务将不同业务的表拆分到不同数据库中。针对压力较大的业务同时进行分库分表。
缺点:垂直分库后意味着不同业务库结构完全不同,不同业务将无法在数据库层面join查询。

水平分库

根据表中字段(例如id),按照一定策略(hash或range),将一个库中的数据分开存储到多个库中。
缺点:如果不根据查询拆分字段查询会出现较多空查询,同时对数据的 group by、order 操作将会失效。

分库分表中间件选型

开源常见的中间件主要从两个方面进行切入:

强化JDBC sharding-jdbc

sharding-jdbc 是当当网开源的基于jdbc的开源分库分表解决方案,目前已经捐赠到Apache,项目孵化中不稳定(目前版本4.0,该版本有个坑无法使用‘Hint分片策略’功能配置后无效),如果选用此方案建议找找当当捐赠到Apache之前的最后稳定版本。

特点:基于jdbc 增加jar、添加配置即可。
缺点:使用不同的分片策略sql代码需要改动。仅java可用。

proxy层的解决方案

mycat

mycat原是基于阿里Cobar的proxy层解决方案,是一个基于java开发的中间件程序,用户可以在配置后直接连接mycat,由mycat代理访问数据库,同时由mycat来实现数据的分离存储、读取。

特点:proxy层,支持多语言,代码无感知。
缺点:单独的中间件需要维护(关键组件,承担风险),由于中间件的引入无论是在网络还是程序上性能均会有下降。

阿里云提供的基于RDS的DRDS(非开源、收费)

DRDS 兼容 MySQL 协议和语法,支持分库分表、平滑扩容、服务升降配、透明读写分离和分布式事务等特性。如果不差钱而且正准备对数据做重构,那么drds是一个不错的选择,之所以说准备做数据重构时考虑用drds,是因为drds不是一个简单的做sharding路由,即使原来使用的是rds,也无法通过drds做路由,唯一的办法新建drds实例,定义路由规则(drds支持二维路由),导入历史数据,然后就可以开心的使用drds了。

特点:专业、稳定、省心
缺点:组件付费且必须使用阿里云RDS数据库

上述几种方案均支持mysql数据库,其他数据库还需查询官方文档。

不同的分库分表中间件分片策略大致分有两种,具体请参阅不同中间件文档:

1、range方案:根据指定分片键大小将每个库多少数据。

2、hash方案:根据指定的分片键取模,分布到不同库中。

不同方案各有优劣,不同中间件也会有其他分片策略。

请注意:分库分表后某些复杂sql将执行失败,或执行结果与预期不一致。

旧数据迁移、新服务上线

引入分库分表想必都是有一定业务量的企业,因此新旧数据的回合也是一个很头疼的问题、

双写部署法

同步新旧数据库双写

在正式上线分库分表服务前上线一个版本通过新旧数据库双写来保证服务的正常运行,同时将截至到双写版本上线前的历史数据写入到新库中,等新旧库数据一致后,将服务滚动升级到新版本,至此迁移完毕。该方案代码层面改动较大。

将sql推送到队列后消化

不同ORM框架切入后将需要的sql推送到消费队列,最后订阅消化,该方案涉及改动较小。

订阅数据库的binlog日志(使用alibaba的canal)

订阅原数据库的binlog日志,通过新程序写入新库即可。该方案无需改动程序代码,开发新的binlog消费端即可。

后两种做法较为推荐,均对代码改动较小或无需改动。

关于分库分表的其他一些思考

以下为我在分库分表学习过程中一些其他一些考虑,如果大家有什么疑问也可以留言讨论哦!

索引问题

数据迁移后由于涉及到多库多表,新索引如何满足新需求,某些执行结果是在proxy层进行汇总,索引将无效。

搜索问题

分库分表后复杂的搜索将会极慢,某些场景无法沿用旧的搜索方式。

解决:搜索问题和索引问题最终都是服务与搜索的,较好的解决办法是将数据写入到es等搜索引擎工具中,专业的组件做专业的事。

事务问题

此处的事务问题之前主要是考虑到微服务之间的完全分离的多库问题,可采用阿里的seata或其他解决方案。
目前的分库分表阶段的事务均是由分库分表组件处理的,这里不需要我们手工解决事务问题。

以上便是我对分库分表的一番学习与思考。在大数据时代,拥抱newsql也是我们需要学习的。