学习总结
写这篇文章主要想解一下自己在大数据量下写数据或者频繁写数据的疑问,工作或者和他人闲聊中一般遇到的大数据量写的场景其实不多,多是密集读的场景,或者大部分情况下的解决方案都比较不彻底,或者比较“脏”,维护起来比较烦心,于是不如就花时间把这个问题想一想。
分库分表的原因
分库分表是一个实施的方案,我们要做分库分表要解决的问题具体来说是:
要解决数据库写压力大的问题;
或者数据量大导致读写慢的问题;
或者是MySQL集群水平扩展问题;
解决这些问题的办法并不只有分库或者分表,数据量大的问题通过做冷热数据分离也是可以解决的;业务密集写导致机器负载高的问题也是可以通过产品同学和开发同学一起商量去解决的;数据量如果不是很大的情况下,通过垂直分表也是一个解决问题的办法,例如在业务或架构上解耦,不同服务读取该服务单独的库,或者单个服务内将大表分离为多个表,业务端分散写压力,自然也都是可以解决问题的。
这里我们只是来研究讨论一下水平扩展下的分库分表方案和设计。
分布算法设计/数据分布设计
我们接下来遇到的问题是我们要怎么设计我们的数据分布,分库、分表、分区都是分散数据分布的方式,由于单纯分区还是在单台机器上的配置,所以主要讨论的是架构上分库(或是分库+分表)的实现和方案。
数据的分布一般最常见的有按范围分和按哈希分,当然能用的不止这两种,详细可以参考MyCat支持的分库分表方式。
自定义分布
存一个将ID和库的Mapping关系的单独的记录库,分布算法用另外的服务实现,分布算法服务可能会迭代,所以这种方式可能适合经常变动的业务,数据分布的关系自然就被记录在该mapping表里,实际中可能不太常见。
缺点是会引入新的单点,也就是引入了该mapping服务的高可用问题。
按范围分区间
按范围区分在实际应用中很常见,这个范围字段通常是某唯一 id 或时间字段,优点是自然增长,不需要考虑以后数据规模再次变大数据要重分布的问题;
缺点也是明显的,一般情况下密集访问的都是最近时间内的数据,密集访问其实还是集中在最近时间内产生的数据上,并发规模若再变高依旧存在密集访问的问题。
按某业务字段 Hash
通过业务字段值 Hash 处理后的值分布数据,这个数据分布方式也是很常见的实现方式,比如希望分 N 个库,可以按用户 ID 取模 N ,可以均匀地将用户相关的数据分布到不同的库上,若希望在数据库内再对表进行划分,可以再对该 ID 作 Hash 处理。Hash 算法的设计可能会业务不同而异因,我们可以学习一下美团技术团队分库分表方案上 Hash 算法的设计。
分库方案讨论
确定我们的业务的数据分布,技术选型就是接下来很重要的事情,我们了解发现现在业界现在分库分表的实现基本有在业务侧实现和通过中间件实现的方式。我可能还是偏向于用中间件的方式将分库分表的处理逻辑与业务剥离开,由运维或者 devops 去单独维护,好处是对开发完全透明,不使得已经比较复杂的业务代码变得更复杂。
分库中间件或开源库调研
现在业内使用的开源库或者中间件有多种,包括:Cobar、TDDL、Atlas、Sharding-jdbc、MyCat、kingshard。
业务端分库分表开源库
Sharding-jdbc:Sharding-jdbc 可能是 Java 系同学的福音,也是经常会看到的,已经发展到 ShardingSphere 了,有兴趣的同学自己去了解吧,我们这里不作研究了。
中间件
我们接下来对业界比较常见的,或者是我比较感兴趣的分库分表中间件作一下对比或者深入了解。
MyCat
mycat 的安装和启动方式我们这里就不做讨论了,mycat 官方文档都有描述。mycat 是用 Java 写的中间件,前身是或者参考了阿里的内部分布式中间件,在官网也能看到其功能介绍,使用于生产环境的公司等等,能看出来技术功底还是相当靠谱的,说是最常见最靠谱的中间件也不为过。与其它中间件一样,前面做一层负载均衡,比如用HAProxy等,也能作为一个常见的高可用方案。
另外社区还是相当活跃的,有大神在维护,用于生产环境比较让人安心的。具体介绍和操作都可以翻阅官方的文档,都解释得非常详细。
配置我们可以详细讨论一下,在结构上有这么几个概念:
- Schema:逻辑库,也就是逻辑意义上的数据库,像是司机信息库等。
- Table:逻辑表,表会绑定对应的一个或多个 DataNode (逻辑数据节点)。可以指定表的分片规则,不指定会使用默认规则。
- DataNode:逻辑数据节点,该逻辑数据节点还会绑定 DataHost (物理物理数据节点)。
- DataSource或DataHost:定义物理节点,同时保存物理节点信息诸如IP、最大连接数、heartbeat等。
- 分片规则(rule):也就是我们设计的数据分布方式,数据会按该分片规则分布到对应的节点上。有多种默认的分片规则如哈希、范围,也可以根据业务情况自定义。
配置文件主要有:
- schema.xml 配置逻辑表及数据节点
- rule.xml 配置分片规则
- server.xml 配置服务器权限
可以参考这个实例的 schema.xml 配置,rule.xml 和 server.xml 暂不作展示了:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 数据库配置,与server.xml中的数据库对应 -->
<schema name="db_person" checkSQLschema="false" sqlMaxLimit="100">
<table name="person" dataNode="dn1" />
<table name="vehicle" dataNode="dn1" />
<table name="images" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2" rule="mod-long" />
</schema>
<!-- 分片配置 -->
<dataNode name="dn1" dataHost="internal-01" database="db_person" />
<dataNode name="dn2" dataHost="internal-02" database="db_person" />
<!-- 物理数据库配置 -->
<dataHost name="internal-01" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user();</heartbeat>
<writeHost host="172.190.0.2" url="172.190.0.2:3306" user="root" password="123456">
</writeHost>
</dataHost>
<dataHost name="internal-02" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user();</heartbeat>
<writeHost host="172.190.0.3" url="172.190.0.3:3306" user="root" password="123456">
</writeHost>
</dataHost>
</mycat:schema>
MyCat 的官方文档描述得非常详细,包括 MyCat 使用的网络 IO 并发模型都有阐述,如果对 MyCat 有兴趣,阅读一遍官方文档是非常有益于理解 MyCat 的。
kingshard
了解 kingshard 是因为 kingshard 是用 go 写的,在学习上成本会低很多,kingshard 是一位 go 大神维护的,听说将其使用在生产环境的公司也有不少,应该也是比较好的一款分库分表中间件,后面有时间我们可以来分析学习一下 kingshard 的具体实现。
kingshard 的结构或概念相对比较简单,支持多个表放在单个库上,后面再扩容在配置上也非常友好。
配置实例(/etc/ks.yaml):
user_list:
-
user : kingshard
password : kingshard
# the web api server
web_addr: 0.0.0.0:9797
#HTTP Basic Auth
web_user: admin
web_password: admin
log_path: /Users/jerry/log
log_level: debug
log_sql: on
nodes :
-
name : node1
user : root
password : 123456
max_conns_limit : 32
master : 127.0.0.1:3307
# slave represents a real mysql salve server,and the number after '@' is-
# read load weight of this slave.
#slave : 192.168.59.101:3307@2,192.168.59.101:3307@3
down_after_noalive : 32
-
name : node2
user : root
password : 123456
max_conns_limit : 32
master : 127.0.0.1:3308
down_after_noalive: 32
-
name: node3
user: root
password: 123456
master: 127.0.0.1:3309
down_after_noalive: 32
-
name: node4
user: root
password: 123456
master: 127.0.0.1:3310
down_after_noalive: 32
# schema defines sharding rules, the db is the sharding table database.
schema_list :
-
user: kingshard
nodes: [node1,node2,node3,node4]
default: node1
shard:
-
db : db_driver
table: t_join_driver
key: driver_join_id
type: hash
nodes: [node1, node2, node3, node4]
locations: [4,4,4,4]
可以看到配置还是很简单的,定义节点( Node )信息,以及分库分表的 schema 信息,如表名、使用哪些节点、分库分表方式(如 hash )、数据分布等。接下来就是分别搭建每个数据库节点,即可启动。
不过我的客户端连接 kingshard 上一直连接不上,一直没找到问题所在,后面有时间我们可以来学习一下 MySQL 协议以及 kingshard 的实现。
Postgres-XL(pgsql)
以上都是使用 MySQL 作为后端数据库服务器的,但是数据库服务器界还有 pgsql 这么个泰斗级的存在,pgsql 功能强大,如果使用 pgsql 存储,那么也有已经使用于生产的分库分表方案,其中一个便是 Postgres-XL。
Postgres-XL是一个可横向扩展的开源数据库集群,基于Postgres-XC,而Postgres-XC又是基于PostgreSql。PostgreSQL许可证是一种自由开源许可证,类似于BSD或MIT许可证。
这里我们只展示一下 Postgres-XL 的架构,权作记录。
后结
我们这里对于分库分表的学习和讨论还是偏于表层的,对于分库分表其实一直没有非常“干净”、从容又高效的解决方案,具体对每个不同中间件的使用还是要实际中磨合才能真正合适不同的业务。事实上现在不少大型互联网公司的存储引擎或者方案都是自研的,所以我们在使用开源中间件满足业务的同时,其实更有必要的是研究底层实现,比如什么样的网络IO并发模型是最合适本业务的、底层存储引擎是否真的是我们最想要的等等,因为很有可能分布式数据水平扩展问题并没有简单粗暴的“银弹”。