数据库的分库分表

分库: 一个库一般最多支撑到并发2000,就要扩容了,而且一个健康的单库并发值最好保持在每秒1000左右,不要太大。那么就可以将一个库的数据拆分到多个库中,访问的时候就只访问存取该数据的库就可以了。

分表: 把一个表的数据放到多个表中,然后查询就查询一个表。比如按用户id来分表,将一个用户的数据放在其中一个表里,然后操作的时候只对那个表进行操作就好了。这样可以控制每个表的数据在可控的范围内,比如每个表就固定在200万数据以内。

水平拆分: 把一个表的数据水平切开,拆分到多个库的多个表里,但是每个库的表结构都是一样,只不过每个库表放的数据是不同的,所有的库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀的放到更多的库表里,然后用多个库来抗更高的并发,还有就是用多个库的存储容量来进行扩容。

垂直拆分: 把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表中,然后将较多访问频率很低的字段放到另外一个表里。因为数据库是有缓存的,你访问的频率高的行字段越少,就可以在缓存里存更多的行,性能就越好。这个一般在表层面做的较多。
例如:一个大表拆分成,订单表,订单支付表,订单商品表。

表面层的拆分,就是分表,将一个表变成n个表,就是让每个表的数据量控制在一定范围内,保证SQL的性能。否则单表数据量越大,SQL性能越差,一般在200万行左右。你的SQL越复杂,最好让单表的行数越少。

如何进行分库分表?
按照range来分: 每个库存一段连续的数据,比如按时间范围来分,某段时间的数据就存到一个表中。
好处:后面扩容的时候比较容易,因为你只要预备好,给每个月准备一个库,到了一个新的月份就写入新的库就好了。
缺点:容易产生热点问题,因为大部分请求都是访问最新的数据,容易造成新库抗不住高并发。
场景适用:适用于均匀的访问现在的数据以及历史的数据。

按照hash来分: 按照某个字段hash一下均匀分散,比较常用。
好处:平均分配给每个库的数据量和请求压力。
缺点:扩容起来比较麻烦,会有数据前移的过程。

如何将系统从未分库分表动态切换到分库分表上?

假设,现有一个单库单表的系统,在线上跑,单表有600万数据。
那么可以分成三个库,每个库分4个表,每个表要放50万的数据量。
方案一、停机迁移:
0点,停机,系统挺掉,没有流量写入了,此时老的单库单表数据库静止了。然后你之前得写好一个导数的一次性工具,此时直接跑起来,然后将单库单表的数据哗哗哗读出来,写到分库分表里面去。
导完了之后,就ok了,修改系统的数据库连接配置啥的,包括可能代码和SQL也许有修改,那你就用最新的代码,然后直接启动连到新的分库分表上去。

方案二、双写迁移:
在线上系统里面,之前所有写库的地方,增删改操作,都除了对老库增删改,都加上对新库的增删改,这就是所谓双写,同时写俩库,老库和新库。

然后系统部署之后,新库数据差太远,用之前说的导数工具,跑起来读老库数据写新库,写的时候要根据gmt_modified这类字段判断这条数据最后修改的时间,除非是读出来的数据在新库里没有,或者是比新库的数据新才会写。

接着导完一轮之后,有可能数据还是存在不一致,那么就程序自动做一轮校验,比对新老库每个表的每条数据,接着如果有不一样的,就针对那些不一样的,从老库读数据再次写。反复循环,直到两个库每个表的数据都完全一致为止。

接着当数据完全一致了,就ok了,基于仅仅使用分库分表的最新代码,重新部署一次。

如果扩容成6个库,每个库需要12个表,你怎么增加更多库和表?

方案一、停机扩容:
从单库单表迁移到分库分表的时候,数据量并不是很大,单表最大也就两三千万。

写个工具,多弄几台机器并行跑,1小时数据就导完了。

3个库+12个表,跑了一段时间了,数据量都1亿~2亿了。光是导2亿数据,都要导个几个小时,导完数据,还要搞后续的修改配置,重启系统,测试验证,才可以搞完。

方案二、优化:
直接一开始就分给它32个库,每个库32个表,一共1024张表。

每个库正常承载的写入并发量是1000,那么32个库就可以承载32 * 1000 = 32000的写并发,如果每个库承载1500的写并发,32 * 1500 = 48000的写并发,接近5万/s的写入并发,前面再加一个MQ,削峰,每秒写入MQ 8万条数据,每秒消费5万条数据。

1024张表,假设每个表放500万数据,在MySQL里可以放50亿条数据。

每秒的5万写并发,总共50亿条数据,对于国内大部分的互联网公司来说,其实一般来说都够了。

谈分库分表的扩容,第一次分库分表,就一次性给他分个够,32个库,1024张表,可能对大部分的中小型互联网公司来说,已经可以支撑好几年了。

一个实践是利用32 * 32来分库分表,即分为32个库,每个库里一个表分为32张表。一共就是1024张表。根据某个id先根据32取模路由到库,再根据32取模路由到库里的表。

刚开始的时候,可能没用到这么多库,比如一个服务器可能建了16个库,后面如果要拆分,就不断在库和服务器之间做迁移就好了,然后系统修改一下配置。

比如说最多可以扩展到32个数据库服务器,每个数据库服务器是一个库。如果还是不够?最多可以扩展到1024个数据库服务器,每个数据库服务器上面一个库一个表。因为最多是1024个表么。

这么搞,是不用自己写代码做数据迁移的,都交给dba来搞好了,但是dba确实是需要做一些库表迁移的工作,但是总比你自己写代码,抽数据导数据来的效率高得多了。

哪怕是要减少库的数量,也很简单,其实说白了就是按倍数缩容就可以了,然后修改一下路由规则。

对2 ^ n取模

orderId 模 32 = 库
orderId / 32 模 32 = 表
259 3 8
1189 5 5
352 0 11
4593 17 15

1、设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是32库 * 32表,对于大部分公司来说,可能几年都够了

2、路由的规则,orderId 模 32 = 库,orderId / 32 模 32 = 表

3、扩容的时候,申请增加更多的数据库服务器,装好mysql,倍数扩容,4台服务器,扩到8台服务器,16台服务器

4、由dba负责将原先数据库服务器的库,迁移到新的数据库服务器上去,很多工具,库迁移,比较便捷

5、我们这边就是修改一下配置,调整迁移的库所在数据库服务器的地址

6、重新发布系统,上线,原先的路由规则变都不用变,直接可以基于2倍的数据库服务器的资源,继续进行线上系统的提供服务。

分库分表后全局id如何生成?

1)数据库自增id
只能适用于单库单表,方便简单。
可以适用redis的自增id来实现唯一性。

2)uuid
uuid太长了,作为主键性能太差了,不适合做主键。

3)获取系统当前时间
如果单单是取当前时间,在高并发情况下,同一秒并发几千,不可避免就会出现重复id。
可以是当前的业务字段进行拼接形成唯一性:时间戳 + 用户id + 业务含义编码

4)雪花snowflake算法
twitter开源的分布式id生成算法,就是把一个64位的long型的id,1个bit是不用的,用其中的41 bit作为毫秒数,用10 bit作为工作机器id,12 bit作为序列号。

1 bit:不用,为啥呢?因为二进制里第一个bit为如果是1,那么都是负数,但是我们生成的id都是正数,所以第一个bit统一都是0

41 bit:表示的是时间戳,单位是毫秒。41 bit可以表示的数字多达2^41 - 1,也就是可以标识2 ^ 41 - 1个毫秒值,换算成年就是表示69年的时间。

10 bit:记录工作机器id,代表的是这个服务最多可以部署在2^10台机器上哪,也就是1024台机器。但是10 bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2 ^ 5个机房(32个机房),每个机房里可以代表2 ^ 5个机器(32台机器)。

12 bit:这个是用来记录同一个毫秒内产生的不同id,12 bit可以代表的最大正整数是2 ^ 12 - 1 = 4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id

例如:
64位的long型的id,64位的long -> 二进制
0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000

2018-01-01 10:00:00 -> 做了一些计算,再换算成一个二进制,41bit来放 -> 0001100 10100010 10111110 10001001 01011100 00

机房id,17 -> 换算成一个二进制 -> 10001

机器id,25 -> 换算成一个二进制 -> 11001

snowflake算法服务,会判断一下,当前这个请求是否是,机房17的机器25,在2175/11/7 12:12:14时间点发送过来的第一个请求。
假设,在2175/11/7 12:12:14时间里,机房17的机器25,发送了第二条消息,snowflake算法服务,会发现说机房17的机器25,在2175/11/7 12:12:14时间里,在这一毫秒,之前已经生成过一个id了,此时如果你同一个机房,同一个机器,在同一个毫秒内,再次要求生成一个id,此时我只能把加1

0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000001

他这个算法生成的时候,会把当前毫秒放到41 bit中,然后5 bit是机房id,5 bit是机器id,接着就是判断上一次生成id的时间如果跟这次不一样,序号就自动从0开始;要是上次的时间跟现在还是在一个毫秒内,他就把seq累加1,就是自动生成一个毫秒的不同的序号。

这个算法那,可以确保说每个机房每个机器每一毫秒,最多生成4096个不重复的id。

利用这个snowflake算法,你可以开发自己公司的服务,甚至对于机房id和机器id,反正给你预留了5 bit + 5 bit,你换成别的有业务含义的东西也可以的。

这个snowflake算法相对来说还是比较靠谱的,所以你要真是搞分布式id生成,如果是高并发啥的,那么用这个应该性能比较好,一般每秒几万并发的场景,也足够你用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值