20191128 数据库拆分方案

任何脱离业务的架构设计都是耍流氓。

 

数据库分布式,其核心内容无非就是数据切分(Sharding),以及切分后对数据的定位、整合工作解决单一数据库或数据表因数据量过大而导致的性能瓶颈问题

已有的MySQL、ORACLE等成熟数据库系统基础上进行的一系列数据操作调度。后者分布式数据库则是集数据存储、管理以及分布式协调与计算为一体的数据库系统。

纵向拆分数据库(逻辑关系),横向拆分数据表。

如果需要拆表,如何设定数据记录的切分规则是最重要考量。一旦确定切分规则,应用对该表的操作原则基本就已确定。根据哪个字段拆分成几个库?还是按照号段进行拆表(不涉及到数据迁移,缺点:热点数据分布不均匀,网游合区,老区已经无活跃用户了)。

假设我们将Customer表根据cus_no字段来切分到4个库,如果我们所有查询条件都带有cus_no字段则可明确定位到相应库去查询,但如果我们频繁用到的查询条件中不带cus_no时,将会导致无法定位数据库,从而需要同时向4个库发起查询,最后再合并数据、取最小集返回给应用,导致分库优势反而可能成为你的拖累。

 

随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践:

1)如何来实施水平切分;(如何进行水平分表?)

2)水平切分后常见的问题;

3)典型问题的优化思路及实践;

 

一、关联查询问题

数据切分后一大难题就是关联查询,跨库关联查询的想法最好打消掉,目前没有好的解决方案。

二、分页查询问题

正如前面提到的分库并行查询时,若需要分页查询则将出现悲剧,每个库返回的结果集本身是无序的,无法确定应该如何返回数据给用户。目前像MyCat这类中间件要求分页查询时必须带上ORDER BY字段,好使得多库查询结果全部出来后,再在内存中根据排序字段单独进行排序,最后返回最小结果集。可以想像,当查询的总结果集过大时,这一排序过程对资源和时间的消耗相当可观。

三、事务一致性问题

当我们需要更新的内容同时分布在不同库时,不可避免会带来跨库事务问题,在JavaEE体系下使用XA事务进行协调解决,但XA事务目前也并非完全安全,在最后确认提交这一步若某个库失败时,并不能确保所有库都能成功roolback。目前针对此种情况尚无简单的方案,需要采用日志分析、事后补偿的方式来解决,以达到互联网类系统宣称的“最终一致性”。

四、主键避重问题

由于表同时存在于多个数据库内,主键值我们平时常用的自增序列将无用武之地,因此需要单独设计全局主键,以避免跨库主键重复问题

一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由.

五、跨节点Join的问题

只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据

六、跨节点的count,order by,group by以及聚合函数问题

这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

 

分表策略:

1、按照号段划分表;插入数据的时候,只能插入指定的表。老表与新表负载不均,老表用户活跃度不高,新表用户活跃度很高。

2、按照关键字段取模分表; 插入数据的时候,需要一个统一id生成器,生成id后,进行取模,然后决定数据存储到哪张表?避免主键重复的问题。

3、拆分表时为什么把拆分后的两张表放到不同的数据库里面?

4、在非关键业务应用中可以尝试,原则上能不分表就不分表,在核心业务上谨慎而行。

 

1、发现某些表数据达到500W+以后查询统计性能严重下降,高峰时段出现了很多SQL阻塞的情况。这种阻塞带来的灾难是滚雪球的,由于越堆越多基本上把数据库已经拖死,所以我们就面临数据库切分的问题。

2、技术选型:既然要分库分表那数据库集群是少不了的。

1)中间件:Mycat(个人建议如果使用中间件的话可以考虑Mycat)。

2)Jar形式的开源工具:例如淘宝的TDDL,以及当当开源出来的,Sharding-JDBC等。

3)动态数据源:根据自己的业务来指定数据源来完成不同库和表的操作。

之所以选择动态数据源:主要是因为技术相对简单,对于业务代码修改也比较少,可控性较高,减少了加入中间件或者第三方工具所带来的风险。

 

主键生成策略

既然要分库分表那么全局唯一主键也是我们需要考虑的问题,我所知道的和有使用经验的有如下几种技术。

添加流程

 

查询逻辑

 

分库分表后能解决我们的性能问题,但是也带来了很多其他的问题。

 

一些常见的主键生成策略。

1)UUID

使用UUID作主键是最简单的方案,但是缺点也是非常明显的。由于UUID非常的长,除占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题

2)结合数据库维护一个Sequence表

此方案的思路也很简单,在数据库中建立一个Sequence表,表的结构类似于:

CREATE TABLE `SEQUENCE` (

`table_name` varchar(18) NOT NULL,

`nextid` bigint(20) NOT NULL,

PRIMARY KEY (`table_name`)

) ENGINE=InnoDB

每当需要为某个表的新纪录生成ID时就从Sequence表中取出对应表的nextid,并将nextid的值加1后更新到数据库中以备下次使用。此方案也较简单,但缺点同样明显:由于所有插入任何都需要访问该表,该表很容易成为系统性能瓶颈,同时它也存在单点问题,一旦该表数据库失效,整个应用程序将无法工作。有人提出使用Master-Slave进行主从同步,但这也只能解决单点问题,并不能解决读写比为1:1的访问压力问题。

3)Twitter的分布式自增ID算法Snowflake

在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位 机器ID 10位 毫秒内序列12位。

* 10---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000

在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

 

数据库设计上,一般来说在业务初期,单库单表就能够搞定这个需求

当数据量越来越大时,需要对数据库进行水平切分,常见的水平切分算法有“范围法”和“哈希法”

范围法:以用户中心的主键uid为划分依据,将数据水平切分到两个数据库实例上去;

范围法的优点是:

1)切分策略简单,根据uid,按照范围,user-center很快能够定位到数据在哪个库上;

2)扩容简单,如果容量不够,只要增加user-db3即可;

 

范围法的不足是:

1)uid必须要满足递增的特性;

2)数据量不均,新增的user-db3,在初期的数据会比较少;

3)请求量不均,一般来说,新注册的用户活跃度会比较高,故user-db2往往会比user-db1负载要高导致服务器利用率不平衡

 

只要建立非uid属性(login_name / phone / email )到uid的映射关系,就能解决问题。

 

如果此时前台业务和后台业务公用一批服务和一个数据库,有可能导致,由于后台的“少数几个请求”的“批量查询”的“低效”访问,导致数据库的cpu偶尔瞬时100%,影响前台正常用户的访问.

对于这一类业务,应该采用“前台与后台分离”的架构方案.

 

https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651960212&idx=1&sn=ab4c52ab0309f7380f7e0207fa357128&pass_ticket=G8v3RrpK9Is7NJZH0fOShUfY8lp5oz9un8K5L24LeGGVtiBTXkBMc9UKkTMdQeDS

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值