分表分库
提升数据库处理能力方案
读写分离方案
海量数据的存储及访问,通过对数据库进行读写分离,来提升数据的处理能力。读写分离它的方案特点是数据库产生多个副本,数据库的写操作都集中到一个数据库上,而一些读的操作呢,可以分解到其它数据库上。这样,只要付出数据复制的成本,就可以使得数据库的处理压力分解到多个数据库上,从而大大提升数据处理能力。
· 优点:由于所有的数据库副本,都有数据的全拷贝,因此所有的数据库特性都可以实现,部分机器当机不影响系统的使用。
· 缺点:数据的复制同步是一个问题,要么采用数据库自身的复制方案,要么自行实现数据复制方案。需要考虑数据的迟滞性,一致性方面的问题。
数据分区方案
原来所有的数据都是在一个数据库上的,网络IO及文件IO都集中在一个数据库上的,因此CPU、内存、文件IO、网络IO都可能会成为系统瓶颈。而分区的方案就是把某一个或某几张相关的表的数据放在一个独立的数据库上,这样就可以把CPU、内存、文件IO、网络IO分解到多个机器中,从而提升系统处理能力。
· 优点:不存在数据库副本复制,性能更高。
· 缺点:分区策略必须经过充分考虑,避免多个分区之间的数据存在关联关系,每个分区都是单点,如果某个分区宕机,就会影响到系统的使用。
数据分表方案
不管是上面的读写分离方案还是数据分区方案,当数据量大到一定程度的时候,都会导致处理性能的不足,这个时候就没有办法了,只能进行分表处理。也就是把数据库当中数据根据按照分库原则分到多个数据表当中,这样,就可以把大表变成多个小表,不同的分表中数据不重复,从而提高处理效率。
· 优点:数据不存在多个副本,不必进行数据复制,性能更高。
· 缺点:分表之间的数据很少进行集合运算;分表都是单点,如果某个分表宕机,如果使用的数据不在此分表,不影响使用。
分表也有两种方案:
1.同库分表:所有的分表都在一个数据库中,由于数据库中表名不能重复,因此需要把数据表名起成不同的名字。
· 优点:由于都在一个数据库中,公共表,不必进行复制,处理更简单
· 缺点:由于还在一个数据库中,CPU、内存、文件IO、网络IO等瓶颈还是无法解决,只能降低单表中的数据记录数。表名不一致,会导后续的处理复杂。
2.不同库分表:由于分表在不同的数据库中,这个时候就可以使用同样的表名。
· 优点:CPU、内存、文件IO、网络IO等瓶颈可以得到有效解决,表名相同,处理起来相对简单
· 缺点:公共表由于在所有的分表都要使用,因此要进行复制、同步。
混合方案
通过上面的描述,我们理解了读写分离,数据分区,数据分表三个解决方案,实际上都各有优点,也各有缺,因此,实践当中,会把三种方案混合使用。由于数据不是一天长大的,实际上,在刚开始的时候,可能只采用其中一种方案,随着应用的复杂,数据量的增长,会逐步采用多个方案混合的方案。以提升处理能力,避免单点。
实现路线分析
正所谓条条大路通罗马,解决这个问题的方案也有多种,但究其深源,都可以归到两种方案之上,一种是对用户透明的方案,即用户只用像普通的JDBC数据源一样访问即可,由框架解决所有的数据访问问题。另外一种是应用层解决,具体一般是在Dao层进行封装。
JDBC层方案
· 优点:开发人员使用非常方便,开发工作量比较小;可以实现数据库无关。
· 缺点:框架实现难度比较大,性能不一定能做到最优。
同样是JDBC方案,也有两种解决方案,一种是有代理模式,一种是无代理模式。
有代理模式,有一台专门的代理服务器,来接收用户请求,然后发送请求给数据库集群中的数据,并对数据进行汇集后再提交给请求方。
无代理模式,就是说没有代理服务器,集群框架直接部署在应用访问端。
有代理模式,能够提供的功能更强大,甚至可买提供中间库进行数据处理,无代理模式处理性能较强有代理模式少一次网络访问,相对来说性能更好,但是功能性不如有代理模式。
DAO层方案
· 优点:开发人员自由度非常大,性能调优更精准。
· 缺点:开发人员在一定程度上受影响,与具体的Dao技术实现相关,较难做到数据库无关。
由于需要对SQL脚本进行判断,然后进行路由,因此DAO层优化方案一般都是选用iBatis或Spring JdbcTemplate等方案进行封装,而对于Hibernate等高度封装的OR映射方案,实现起来就非常困难了。
需求
需求决定了后续的解决方案及问题领域:
· 采用JDBC层解决方案:对于最终用户来说,要完全透明
· 采用无代理解决方案:数据库集群框架代码直接放在应用层
· 支持读写分离、分区、分表三种方式及其混合使用方式:三种方式可以混用可以提供极大的灵活性及对未来的扩展性
· 需要提供灵活的分区及分表规则支持
· 对于读写分离的方案,需要提供灵活的路由规则,比如:平均路由规则、加权路由规则,可以提供写库的备用服务器,即主写入服务器当机之后,即可写入备用服务器当中。
· 支持高性能分布式主键生成器
· 有良好的集群事务功能
· 可以通过扩展点来对框架进行扩展,以便于处理分区、分表相关的操作。
· 支持各种类型支持JDBC驱动的数据库
· 支持异构数据库集群
· 支持count、sum、avg、min、max等统计函数
· 支持排序
· 支持光标移动
· 支持结果集合并
· 支持数据库自增长主键
· 支持数据库分页指令
· 支持DDL语句处理
· 支持Grovy脚本定义规则
· 支持JDK1.5~1.8
· 支持连接延迟获取--只到使用时才申请数据库连接,提升性能降低资源战胜
明确不支持的内容或限定条件:
· 不支持分区之间的联合查询
特性说明
上面的红色部分特性是最新添加的功能特性,这里简单解释一下:
支持数据库自增长主键:比如:Mysql,SqlServer等数据库支持 auto increase主键,原来不支持,现在可以完美支持了
支持数据库分页指令:比如,有些数据库支持start limit或类似的分页指令,原来不支持,现在可以完美支持了
支持DDL语句:原来对数据库结构方面的操纵,原来不支持,现在可以完美的支持了,比如:可以一次修改所有的分片的表结构
支持Grovy脚本定义分区分片规则:原来是只能采用实现接口的方式进行分区和分片规则的实现,现在可以用Grovyy脚本来定义了
支持JDK1.5~1.8:原来是是分成两个版本,一个jdbc3,一个jdbc4的,框架开发和维护非常麻烦,现在合并成一个工程,两个可以同步支持了
支持连接延迟获取:原来是整个分区分片中需要的连接一次申请到,现在优化为需要时才申请,这样会大大降低对数据库连接资源的需要,同时由于事务变小,也可以显著提升处理效率。
结构设计
框架采用三层设计:最上层是Cluster,一个Cluster相当于我们常规的一个数据库;一个Cluster当中可以包含一到多个Partition,也就是分区;而一个Partition中可以包含一到多个Shard,也就是分片。
所以一个就形成了一个树状结构,通过Cluster->Partion->Shard就构成了整个数据库集群。但是对于开发人员来说,实际上并不知道这个内部结构,他只是连接上了一个JDBC数据源,然后做它应该做的事情就可以了。
Cluster
以完整的形态对外提供服务,它封装了Cluster当中所有Partition及其Shard的访问。把它打开是一个数据库集群,对于使用者来说是一个完整的数据库。
属性名 | 类型 | 说明 |
id | String | 集群标识 |
userName | String | 连接集群时的用户名 |
Password | String | 连接集群时的密码 |
dataSources | List<DataSourceConfig> | 集群中需要访问的数据源列表 |
partitions | List<Partition>; | 集群中包含的分区列表 |
Partition
分区,分区有两种模式,一种是主从模式,用于做读写分离;另外一种模式是分片模式,也就是说把一个表中的数据分解到多个表中。一个分区只能是其中的一种模式。但是一个Cluster可以包含多个分区,不同的分区可以是不同的模式。
属性名 | 类型 | 说明 |
id | String | 分区标识 |
mode | int | 分区类型,可以是主从,也可以是分表 |
Password | String | 连接集群时的密码 |
shards | List<Shard> | 分区中包含的分片列表 |
partitionRules | List<PartitionRule> | 分区规则,当进行处理的时候,路由到哪个分区执行 |
Shard
Shard与一个物理的数据源相关联。
属性名 | 类型 | 说明 |
id | String | 分区标识 |
dataSourceId | String | 实际访问的数据库配置ID |
readWeight | int | 读权重,仅用于主从读写分离模式 |
writeWeight | int | 写权重,仅用于主从读写分离模式 |
shardRules | List<ShardRule> | 分片规则,当进行处理的时候,路由到哪个分片执行,仅用于分模式 |
tableMappings | List<TableMapping>; | 表名映射列表,仅用于同库不同表名分表模式 |
分布式主键接口
来源:https://my.oschina.net/tinyframework/blog/617417