分布式数据库集群介绍

自从谷歌提出分布式这个概念,这个玩意太火了,但是并不是所有的业务场景都适合用分布式的

什么场景适合用分布式架构?

  • 网易分布式用的最好的两个项目:网易考拉 && 网易云音乐(歌单库单表百亿以上记录、评论库)

  • 快递行业

  • 微信红包

其他业务都是有时间维度的,可能只需要存3个月的在线数据,算下来也就2kw,那为什么还要做分布式架构呢?

所以不要迷信分布式架构和分库分表

tips:
单台服务器几千qps没必要分,两三万的话可以粉

Ⅰ、分布式数据库特点

优点

  • 可用性提高
  • 可扩展性提升(扯淡,MySQL现在4个节点扩8个节点怎么办?数据库不是redis,redis里面内存数据可以丢掉,甚至redis单线程的做这些都好弄,MySQL并不是,8扩12的话,原来的数据都要打散一遍,原来8个机器,hash8个,现在变12个,数据全部打散,如果8*500G,4T数据打散到12个节点,怎么打散,这么多表)

    MySQL可扩展性的提升是很难达到的,我们借助另外一些手段来做扩展性

    为什么mongodb,redis,tidb扩展性是好的,因为他们的分片策略更好(也有缺点),是未来比较好的模式,但是在关系型数据库中很难实现,对MySQL内核和中间件的改造太大了

  • 某些情况下吞吐率提升显著 ---大大的问号,只有某些场景才会显著提升
    原来一台机器,拆成八台机器,那就性能提升八倍?这是不存在的

缺点

  • 依赖中间件(不依赖中间件就要在业务层做),业界基本上找不到一个好的中间件,mycat可以用,但是生产上不建议
  • SQL语句支持不足(中间件支持的sql非常有限,多表join,子查询,派生表这些估计玩不转,mongodb,redis,tidb不做这些)
  • 运维复杂度提高
  • 某些情况下性能下降巨大

Ⅱ、分布式数据库——shard

2.1 shard相关概念

A database shard is a horizontal partition(水平分区) of data in a database. Each individual partition is referred to as a shard or database shard. Each shard is held on a separete database server instance,to spread load.

sharding和分区的异同
相同:打散数据
不同:打散的数据可能不在同一实例上

总结:sharding的实现非常复杂

举个栗子
orders表,根据日期做分区,每个月一张订单表,还是在一台MySQL上

做分片的话,把一张表,分到四台MySQL上去

在分区中:

partition by hash(id)

这个id叫做分区键

在shard中:把这个分区键叫作均衡字段
把id 作hash,%4 来分到四个机器上,把date 作range也可以

如果根据order_key分了四个机器

select * from orders where order_key = ?

这样性能就提升了

怎么找到这个机器呢:

  • 法1:业务层控制,根据分区规则搞,如果order_key是int类型,那就%4,根据结果下发到对应的Datanode(ip)

  • 法2:中间件,client不用关心后端的分库规则,均衡字段这些
    client直接通过MySQL协议访问中间件,中间件自动帮你计算该访问哪个节点,sql的改写由中间件来做
    像我们上面这个sql就不用改写,只要转一下ip就行

两个问题:

  • 两种方法:哪个性能更好,为什么?
    法1更好,直接连数据库,法2多了一层,中间件的优势在于,开发人员更方便写代码,就和直接连数据库一样用,其他事情都交给中间件

  • 业务里面永远都是只有这么一个简单的需求嘛?如果根据其他字段查询呢?会有上面问题呢?
    那就所有的分片都要找一遍,你加N多个节点,这样还叫可扩展吗?节点越多,性能越差了都,和分区表的是一样的道理,orders_partition表中可以explain看下两种情况,走索引的话,1亿的数据量下,和1kw数据量下速度是一样的,你分十个片没什么意义

2.2 做shard的三个原则

  • 尽可能大部分的业务逻辑都是根据均衡字段,至少百分之80%,更改分布式数据库中的均衡字段非常麻烦,必须一开始就规划号表的均衡字段

  • 如果不能选择有效的均衡字段,这张表就不要进行Shard,作为全局表

  • 如果业务选择不出有效率的均衡子段,那么进行分布式数据库的改造也将是徒劳的

tips:shard一般用hash打散,平均的,如果没有打散就是均衡子段没选好,可以选择用多个字段作均衡字段

2.3 看看别人的业务怎么选择均衡子段的

你平时怎么逛淘宝的?

点开淘宝,所有的查询都是买家查询,你能查你老婆买了哪些东西吗?所有的查询都是根据自己的id来查(登陆是通过用户中心来登陆的)

下单了,订单里有userid,订单明细中也有userid,登陆到淘宝,查最近一些订单信息,购买记录,也是根据userid来查的

所以电商的表结构设计,有张user表,用来存用户信息的,里面有个userid

又有一个orders表,订单表里面也有个userid

还有个orderline表,一个订单里下了多少个商品,里面也有个userid

可能还有个favorite表,里面也有一个userid

coupon表,还是有个userid

综上,所有买家的数据都有一个userid的字段,这时候 上面这些表就可以根据userid放到不同的shard中

这意味着什么呢?一个用户的所有信息是在一个分片上的,是不会跨分片的,不存在一个查询要查很多个分片的

所以淘宝的业务是非常容易做扩展的,只要加机器,把数据打散,就是可扩展的

所以对于电商的业务来说,有个非常好的维度叫买家维度,所有的查询基本上都是根据买家id

那网易云音乐的歌单又是怎么弄的呢?
也是userid啊,哈哈,看自己的歌单,看别人的歌单,都是用的userid

快递行业用的是什么?
运单号

微信红包的均衡子段是什么?
也是userid,哦嚯

上面所有的都要用中间件,一般用mycat

Ⅲ、分布式最大的问题——分布式事务

3.1 微信红包案例

user<--->user<--->user
       
        中间件
       
        client

如果一台机器

begin;
update user set money = money - 100 where userid = xxx;
update user set money = money + 100 where userid = xxx;
commit;

那走分布式事务的话,很有可能这两个userid不在同一个分片里面

这个事务就变成了分布式事务,首先,mycat不支持分布式事务,但是这么写是支持的

意味着着不是一个原子操作,变成了两个事务分到两个分片上去,做不到原子性,所以千万不要把mycat用在关键的业务,特别是跟钱打交道的

现在业界的中间件,除了淘宝和网易貌似还没有什么中间件支持分布式事务的,那怎么办呢?

分布式事务改为单个事务,之前提过的补偿机制,做一个message表,update的时候往message表中插入消息,另一张表,查看消息,然后update,最后把消息置为已接收

这时候还需要中间件吗?no!这也叫一种柔性事务

3.2 用户下单案例

orders表,三个分片,userid作均衡字段,还有个表,叫stock(库存)
下单的逻辑就变得不一样了,看tpcc样例

begin;
先锁定库存
insert into orders
insert into orderline
update stock set count = count - 1 where itemid = ?
update
...
xxx
...
commit;

orders表分库分表了,stock表没有userid这东西,所以stock表叫全局表,我们该怎么放,可以放到单独的实例上,也可以放到其中的一个分片上,甚至可以根据skuid去做shard

每个MySQL是用来存分片的,但并不要求一个分布式集群中所有的表均衡字段是一样的,业务只要知道怎么存的就行,均衡子段是什么,怎么访问,自己知道就行,对于中间件来说有个元数据库来存这些均衡字段

这时候这个流程就是分布式的了,淘宝网易的中间件可以支持这个,如果stock做了分片就讨厌了,一个订单可以有很多件商品,很多个库存要修改,涉及的节点可能就多了,不像转账了,所以订单表放到消息队列(mq)中做了

要下单的话,先放到mq中,后面很多worker线程,拿到这个消息然后去消费

所以,现在打开京东,天猫这些,下一个单之后,状态显示为出库中,什么叫出库中?

出库中就表示,我现在这个下单,只是把业务逻辑丢到消息队列中,至于消息队列后面能不能成功并不知道,如果库存不够会告诉你出库失败,然后把你这个下单过程回滚掉

所以经常下单成功,出库失败,不是1111,一般都不会有问题,1111的时候就不一定了,实时库存代价太大了

3.3 小结

到现在为止,还用得着中间件嘛?不是可以不用,而是建议不用,因为用了中间件,你避免不了分布式事务,很多核心业务有风险的

根据userid来扩展,做查询没问题啊,但是对于数据库来说,分库分表最难的点在于事务化和持久化,而不是select,甚至select放到redis里都可以

分布式事务到最后都用了消息表和消息队列,业务逻辑都丢到消息里,后面消费线程慢慢消费,出错了,就回滚,关闭订单,订单表中有个状态字段,可见,不可见,成功,没成功,都是通过业务来控制的

微信红包用的就不是中间件,淘宝的下单过程也没有用中间件做,存在分布式事务,性能不好

用分布式事务的协调器来做的,淘宝最早提出tcc,现在基于这个封装了一堆东西了都

所以中间件这东西很危险不要想的太多了,如果用了中间件,那也是个很简单的东西,淘宝,微信核心业务都不用中间件,所有的访问都是业务控制的。

建议:
如果分库分表,最好让业务控制,而不要用中间件,用了中间件这个活儿就是你的,说难听点,就是甩锅,以后出了问题就跟我没关系,第二点,核心业务,中间件不支持分布式事务,保证不了事务性和持久性,第三点,中间件性能真的不行,即使用了ddb,一个订单三个商品垮了好多个节点,双十一能接受吗?不要迷信中间件

但是,快递行业真的很适合中间件,因为快递行业没有很强的事务性要求,就是一个个运单,这个运单要周转几次,现在的中转状态,所以快递行业是中间件使用的最好的场景,如果互联网金融用了中间件,碰到了分布式事务就很也容易呵呵

Ⅳ、理解分库和分表呢

shard是一个标准的说法,但是分库分表根本没有完整的定义,很模糊

最早的时候:

  • 分表:orders ---> orders00,orders01,,,,
  • 分库:db1,db2中都有orders00,orders01,当然也可以分到其他实例中的db中

这样表名改了,不好维护,网易的中间件就不支持分表,那就只分库吧,最好是同一个实例上建很多个不同的db,扩展起来不用打散数据

所以这里又有个淘宝的规范,一开始可能就会分1024个shard,不管多少个机器,一开始放在100个机器上,双十一放到1000个机器上,具体落到哪个shard还是用hash

不能2048个shard,MySQL数据库扩展是不做reshard的,重分布的代价太大,所以就拆,在一开始在同一个机器上做很多个shard

一开始就把shard就划好,扩展的时候不用每次都去打散数据,换句话说,这样做的是垂直扩展,虽然说分布式数据库可扩展,但是因为hash的扩展很麻烦,涉及到数据的重分布,所以尽量垂直扩展,数据打散水平打散,扩展的话垂直扩展

mongodb的扩展方便,是基于chunck的,先把所有的数据放到chunck里,如果超过32M会做split,拆成两个chunck,然后做一个balance的操作,把两个chunck放到两台服务器上,所以这个chunck是可以移动的,mysql的hash不可移动

mongodb的balance也有问题,牵涉到一台服务器的chunck拷贝到另一台服务器

各有优缺点,扩展方便,新加一台机器,可以把各个机器上的chunck移过来,不用hash那样整个集群的重分布,只拿一部分chunck过来,但是io会很大

猜你喜欢

转载自www.cnblogs.com/---wunian/p/9266135.html