数据库水平切分及问题

简介

前面一篇文章说到,当遇到数据存储层的高并发的时候,会首先想到读写分离,同时高并发有可能意味着数据量大,大量的查询或更新操作集中在一张大表中,锁的频繁使用,会导致访问速度的下降,而且数据量可能超过了单机的容量,所以我们想到了分库分表。

但是在分库分表之前,我还是想多说几句,除非使用那些透明的分库分表方案,否则分库分表是一个大工程。 所以在分库分表前,我建议尽可能先升级数据库的硬件,SSD/NVMe硬盘 + 大容量内存基本可以满足一个小型互联网公司大部分的应用, 对于中型互联网公司需要使用到分库分表的场景也不会太多,用户,订单,交易可能会用到的。但是即使是这些场景,现在也有了不同的解决方案,在以前,大部分的应用还是基于web的,如果一些操作需要用到用户校验,就要进行登录,对数据库操作比较频繁,随着移动互联网的兴起以及Nosql的出现,如今大部分的应用都迁移到了App端,基本都是通过accessToken+NoSql来进行用户校验的,大家可以想想你有多久没有进行过登录操作了。对于订单交易,通过冷热数据分离,也可以解决大部分场景。

数据库切分

数据库切分分为水平切分,垂直切分, 垂直切分一般在拆分系统的时候使用,这里不再赘述,下面主要说数据的水平切分。

水平切分方式

1. 只分表:在一个数据库下面,分成10张表,表名 user_0 ,user_1, user_2.....

数据集中在一台服务器上,当单机性能瓶颈的时候,后续扩展困难。

2. 只分库,分成10个库,每个库一张表, 表名都是一样的。 db0-user  db1-user db2-user......

出现跨库,没有办法使用事务。不过在分布式系统中,一般不会使用事务,保证数据最终一致性即可

3. 分表分库,10X10这种方式。 先分10个库, 每个库10张表。

分表算法

确定了表切分的方式,接下来就是要根据一定的规则把数据落入到指定表中,这里介绍两种形式,

取模:即找到某个字段,这个字段通过取模后的值尽可能平均,根据字段取模的方式决定数据落在哪张表,比如,我们订单根据用户Id来决定落在哪张表。

日期:这种方法在最开始的时候,并没有决定要分多少张表,只是指定日期的数据落到指定的表中,比如用户注册,在2017-5-30日注册,则可以将数据落入user_201705,这张表中。

这两种分表方法各有优劣,按照取模分,每张表的数据可以尽可能的平均,但是如果后面表扩展则比较麻烦,按照日期来分,虽然可以解决表扩展问题,但是数据有可能不平均,比如在5月,举办了促销,结果那个月注册的人很多。

Id

当数据开始写数据库,Id就是要考虑的,这里有几个解决方案:

1. Id自增,步进=分表数。比如分了3张表,每张表的起始Id不一样,并且自增的幅度=表数量,这样Id就是1,4,7  2,5,8  3,6,9 ,但是如果以后分表数增加,Id的步进还要变。

2. 单独的表,用于记录自增,可以单独一张表,里面有一列,记录的是当前的id, 可以获取下一个Id,  只是这样每次都要访问这张表,会有性能瓶颈。

3. Guid

4. snowflake算法

关于Id有几个注意的地方,如果你的Id有可能出现在页面上, 比如订单Id, 用户Id ,尽量不要使用自增的,因为别人可以根据这些Id,看出你现在的用户规模,订单量。通过在两天中相同时间下单,可以看出你的交易量,如果权限没有控制好,通过遍历可以查出所有的信息。

我们这里以用户表为例,看把用户信息写入数据库的一些方法,以及这些方法的一些问题。

当一个新用户注册,并没有Id,可以通过分布式Id获取到一个Id ,并对这个Id取模,得到具体的表,然后将Id连同用户信息写入表。 后面如果要根据Id得到用户信息,只要对Id采用相同的方法,即可查到对应的信息。

但是上面的方法没有覆盖到一个场景,用户登录都是通过用户名登录的,怎么查呢,这个时候,一般的方法就是并行查所有的表,好一点的方法就是,在注册的时候,额外建立一个映射,是username->表名的映射,当用户登录的时候,可以根据这个映射找到对应的表名,然后根据表名,再插到具体的用户,这个映射可以放到数据库中,也可以放到redis/mongodb中。

上面的方法可以解决问题,但是需要引入映射,如果不想要映射, 这个时候可以采用复合Id , 我们使用用户名进行取模,算出具体的表索引,然后通过分布式Id+表索引作为新的Id(NewId)插入到表中,这样我们对用户名进行计算可以得到表索引,通过对NewId进行计算也可以得到表索引(NewId包含了分布式Id和表索引,通过分离,可以得到表索引)。

我们上面没有考虑到扩展,比如现在我们有10张表,过段时间,发现10张表的存储也快满了, 这个时候就要再扩展10张表,变成20张表,那取模算法也要改,这个时候再对以前的NewId进行计算可能就得不到准备的表了,所以在NewId中,我们还要包含一个算法版本号。

事务

当所有的操作都在一个数据库的时候, 可以很方便的使用事务,比如Java下spring的声明式事务,但是当出现跨库操作,事务的使用就不怎么方便了,对于互联网公司,大多数系统都是分布式的,会选择柔性事物。

扩容方式

随着业务的发展,数据量增多,我们需要再次对表进行扩展,扩展方式如下:

1. 需要迁移数据

1. 首次扩展:假如我们要开始分库分表,原来有一个库一张表,现在要扩展为10个,可以新建9个slave库,等9个slave库数据都和主库都同步了,然后修改路由算法。

2. 扩容后再扩容:比如现在3个表,要改为5个表,采用Hash算法,当数据扩容,则整体数据都要做迁移。 新增和删除都要做。 技巧:选择2个倍数,可以只做新表新增和删除,这个可能需要用到双写。

3. 划分树形组,比如,之前一个数据库,不需要Hash,现在新增9个,同步数据,完成后,修改路由算法(321%10),再删除数据,如果10个数据库不够,则再次扩容,原来的10个库,每个库各自再扩展9个库(可根据实际需要扩展),同步数据,方法和上面第一种类似,然后通过 321%10   321/10%10, 算出具体的数据库。如果不够,可以用同样的方式扩容。

2. 不迁移数据

1. 根据时间定义,比如一个月一张表。

2. 定义mapping关系,可以放入到分布式缓存中,写入的时候,写入缓存,读取的时候读取缓存, 如果缓存失效,全量读取。

3. 增量和Hash同时使用,定义策略,1-1000W一张组,1000-3000W一张组,组内,根据Hash避免热点数据。 新数据都会到新的数据库中。

4. 生成的ID具有自描述性。 ID+DBIndex,比如Id为321,Hash命中到第一张表,变为32101,最后两位表示具体的数据库索引,老的数据库也会写数据,如果我们的表增多,则修改hash算法,只命中到新表。

分库分表解决方案

目前分库分表的解决方案主要集中在这几层:

1. dao层,在dao层根据指定的分表键决定要操作那个数据库。

2. ORM层

2. JDBC层,比较有名的是sharding-jdbc.

3. 代理层,比如mycat.

总结

  分库分表是一个大工程, 如何分, ID怎么取, 事物怎么做都是要考虑 的。另外大家不要用操作单表的思维去操作多表,有些人一接触分表就去考虑gourp by 怎么做, join怎么做。 从我接触的一些分表来看,如果牵扯到分表,操作都比较简单,基本在执行sql前都已经确定了要操作哪张表。 当然如果真的遇到要join的操作或者没有办法确定数据在哪个表中,像sharding-jdbc,mycat这种透明分表分库的都会帮你处理。

时间: 2024-10-31 12:05:37

数据库水平切分及问题的相关文章

多key业务,数据库水平切分架构一次搞定

转发自:原创 2017-08-29 58沈剑 架构师之路 数据库水平切分是一个很有意思的话题,不同业务类型,数据库水平切分的方法不同. 本篇将以"订单中心"为例,介绍"多key"类业务,随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践. 一.什么是"多key"类业务 所谓的"多key",是指一条元数据中,有多个属性上存在前台在线查询需求. 订单中心业务分析 订单中心是一个非常常见的"多key&q

数据库水平切分的实现原理解析

数据库水平切分的实现原理解析---分库,分表,主从,集群,负载均衡器 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式.水平切分数据库,可以降低单台机器的负载,同时最大限度的降低了了宕机造成的损失.通过负载均衡策略,有效的降低了单台机器的访问负载,降低了宕机的可能性:通过集群方案,解决了

数据库水平切分的原理探讨、设计思路--数据库分库,分表,集群,负载均衡器

本文转载:http://www.cnblogs.com/olartan/archive/2009/12/02/1615131.html 第1章  引言 数据量巨大时,首先把多表分算到不同的DB中,然后把数据根据关键列,分布到不同的数据库中.库分布以后,系统的查询,io等操作都可以有多个机器组成的群组共同完成了.本文主要就是针对,海量数据库,进行分库.分表.负载均衡原理,进行探讨,并提出解决方案. 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的互联网应用,每

数据库水平切分的实现原理解析——分库,分表,主从,集群,负载均衡器(转)

第1章 引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于 一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向 扩展数据层已经成为架构研发人员首选的方式. 水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失: 负载均衡策略:可以降低单台机器的访问负载,降低宕机的可能性: 集群方案:解决了数据库宕机带来的单点数据库不能访问的问题: 读写分离策略:最大

数据库水平切分的实现原理解析---分库,分表,主从,集群,负载均衡器

原文来自:http://zhengdl126.iteye.com/blog/419850 第1章  引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的 互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层 已经成为架构研发人员首选的方式.水平切分数据库,可以降低单台机器的负载,同时最大限度的降低了了宕机造成的损失.通过负载均衡策略,有效的降低了单台 机器的访问负载

数据库水平切分的实现原理解析——分库,分表,主从,集群,负载均衡器(转)

申明:此文为转载(非原创),文章分析十分透彻,已添加原文链接,如有任何侵权问题,请告知,我会立即删除. 第1章 引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式. 水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失: 负载均衡策略:可以降低单台机器的访问负载

数据库水平切分方法

在大中型项目中,在数据库设计的时候,考虑到数据库最大承受数据量,通常会把数据库或者数据表水平切分,以降低单个库,单个表的压力.我这里介绍两个我们项目中常用的数据表切分方法.当然这些方法都是在程序中使用一定的技巧来路由到具体的表的.首先我们要确认根据什么来水平切分?在我们的系统(SNS)中,用户的UID贯穿系统,唯一自增长,根据这个字段分表,再好不过. 方法一:使用MD5哈希 做法是对UID进行md5加密,然后取前几位(我们这里取前两位),然后就可以将不同的UID哈希到不同的用户表(user_xx

数据库水平切分(拆库拆表)的实现原理解析(转)

第1章  引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当 高的负载.对于系统的稳定性和扩展性造成了极大的问题.通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式.水平切分数据库,可 以降低单台机器的负载,同时最大限度的降低了了宕机造成的损失.通过负载均衡策略,有效的降低了单台机器的访问负载,降低了宕机的可能性:通过集群方案, 解决了数据库宕机带来的单点数据库不能访问的问题:通过读

(转)数据库水平切分的两个思路 数据库最大承受数据量所必需考虑的问题

在大中型项目中,在数据库设计的时候,考虑到数据库最大承受数据量,通常会把数据库或者数据表水平切分,以降低单个库,单个表的压力.这里介绍两个 项目中常用的数据表切分方法.当然这些方法都是在程序中?使用一定的技巧来路由到具体的表的.首先我们要确认根据什么来水平切分?在我们的系统(SNS) 中,用户的UID贯穿系统,唯一自增长,根据这个字段分表,再好不过. 方法一:使用MD5哈希 做法是对UID进行md5加密,然后取前几位(我们这里取前两位),然后就可以将不同的UID哈希到不同的用户表(user_xx