文档章节

高可用数据库架构实践

陶邦仁
 陶邦仁
发布于 2015/11/13 10:33
字数 3067
阅读 610
收藏 11

#0 系列目录#

#1 基本概念# ##1.1 概念一“单库”##

输入图片说明

单库,不多说了,就是一个库

##1.2 概念二“分片”##

输入图片说明

分片(sharding),水平拆分,用于解决扩展性问题。

分片解决的是“数据量太大”的问题,也就是通常说的“水平切分”。一旦引入分片,势必有“数据路由”的概念,哪个数据访问哪个库

路由规则通常有3种方法:

  1. 范围:range

优点:简单,容易扩展

缺点:各库压力不均(新号段更活跃)

  1. 哈希:hash

优点:简单,数据均衡,负载均匀

缺点:迁移麻烦(2库扩3库数据要迁移)

  1. 路由服务:router-config-server

优点:灵活性强,业务与路由算法解耦

缺点:每次访问数据库前多一次查询

大部分互联网公司采用的方案二:哈希分库,哈希路由

##1.3 概念三“分组”##

输入图片说明

分组解决“可用性”问题,分组通常通过主从复制的方式实现。复制(replication)与分组(group),用于解决可用性问题

互联网公司数据库实际软件架构是:又分片,又分组(如下图)

输入图片说明

##1.4 概念四“分片+分组”##

输入图片说明

分片+分组,这是大数据量下,mysql架构的实际情况。

#2 数据库架构设计思路# 数据库软件架构师平时设计些什么东西呢?至少要考虑以下四点:

  1. 如何保证数据可用性

可用性解决思路:复制

读库可用性

从库复制多个,例如:1主2从

从库挂了读主库,例如:1主1从

写库可用性

双主模式

“双主”当“主从”用

  1. 如何提高数据库读性能(大部分应用读多写少,读会先成为瓶颈)
  2. 如何保证一致性
  3. 如何提高扩展性

##2.1 如何保证数据的可用性?## 解决可用性问题的思路是=>冗余,复制

如何保证站点的可用性?复制站点,冗余站点

如何保证服务的可用性?复制服务,冗余服务

如何保证数据的可用性?复制数据,冗余数据

数据的冗余,会带来一个副作用=>引发一致性问题(先不说一致性问题,先说可用性)

##2.2 如何保证数据库“读”高可用?## 冗余读库

输入图片说明

冗余读库带来的副作用?读写有延时,可能不一致。上面这个图是很多互联网公司mysql的架构,写仍然是单点,不能保证写高可用

##2.3 如何保证数据库“写”高可用?## 冗余写库

输入图片说明

采用双主互备的方式,可以冗余写库,带来的副作用?双写同步,数据可能冲突(例如“自增id”同步冲突),如何解决同步冲突,有两种常见解决方案:

  1. 两个写库使用不同的初始值,相同的步长来增加id:1写库的id为0,2,4,6...;2写库的id为1,3,5,7...

  2. 不使用数据的id,业务层自己生成唯一的id,保证数据不冲突

实际没有使用上述两种架构来做读写的“高可用”,实际采用的是“双主当主从用”的方式:

输入图片说明

仍是双主,但只有一个主提供服务(读+写),另一个主是“shadow-master”,只用来保证高可用,平时不提供服务。master挂了,shadow-master顶上(vip漂移,对业务层透明,不需要人工介入)。

这种方式的好处:

  1. 读写没有延时
  2. 读写高可用

不足:

  1. 不能通过加从库的方式扩展读性能
  2. 资源利用率为50%,一台冗余主没有提供服务

那如何提高读性能呢?进入第二个话题,如何提供读性能。

##2.4 如何扩展读性能?## 提高读性能的方式大致有三种,第一种是建立索引。这种方式不展开,要提到的一点是,不同的库可以建立不同的索引

输入图片说明

写库不建立索引;

线上读库建立线上访问索引,例如uid;

线下读库建立线下访问索引,例如time;

第二种扩充读性能的方式是,增加从库,这种方法大家用的比较多,但是,存在两个缺点:

  1. 从库越多,同步越慢

  2. 同步越慢,数据不一致窗口越大(不一致后面说,还是先说读性能的提高)

实际没有采用这种方法提高数据库读性能(没有从库),采用的是增加缓存。常见的缓存架构如下:

输入图片说明

上游是业务应用,下游是主库,从库(读写分离),缓存。

读写相近场景:不要使用缓存,考虑水平切分;

写多读少场景:不要使用缓存,考虑水平切分;

最终的玩法是:服务+数据库+缓存一套

输入图片说明

业务层不直接面向db和cache,服务层屏蔽了底层db、cache的复杂性。为什么要引入服务层,今天不展开,采用了“服务+数据库+缓存一套”的方式提供数据访问,用cache提高读性能。

不管采用主从的方式扩展读性能,还是缓存的方式扩展读性能,数据都要复制多份(主+从,db+cache),一定会引发一致性问题

##2.5 如何保证一致性?## 主从数据库的一致性,通常有两种解决方案:

  1. 中间件

输入图片说明

如果某一个key有写操作,在不一致时间窗口内,中间件会将这个key的读操作也路由到主库上。这个方案的缺点是,数据库中间件的门槛较高。

  1. 强制读主

输入图片说明

上面实际采用的“双主当主从用”的架构,不存在主从不一致的问题。

第二类不一致,是db与缓存间的不一致:

输入图片说明

常见的缓存架构如上,此时写操作的顺序是:

  1. 淘汰cache

  2. 写数据库

读操作的顺序是:

  1. 读cache,如果cache hit则返回

  2. 如果cache miss,则读从库

  3. 读从库后,将数据放回cache

在一些异常时序情况下,有可能从【从库读到旧数据(同步还没有完成),旧数据入cache后】,数据会长期不一致。

解决办法是“缓存双淘汰”,写操作时序升级为:

  1. 淘汰cache

  2. 写数据库

  3. 在经过“主从同步延时窗口时间”后,再次发起一个异步淘汰cache的请求

这样,即使有脏数据如cache,一个小的时间窗口之后,脏数据还是会被淘汰。带来的代价是,多引入一次读miss(成本可以忽略)

除此之外,最佳实践之一是:建议为所有cache中的item设置一个超时时间

说完一致性,最后一个话题是扩展性。

##2.6 如何提高数据库的扩展性?##

原来用hash的方式路由,分为2个库,数据量还是太大,要分为3个库,势必需要进行数据迁移,介绍一个很帅气的“数据库秒级扩容”方案

首先,我们不做2库变3库的扩容,我们做2库变4库(库加倍)的扩容(未来4->8->16)

输入图片说明

服务+数据库是一套(省去了缓存),数据库采用“双主”的模式。

秒级扩容步骤:

  1. 第一步,将一个主库提升

  2. 第二步,修改配置,2库变4库(原来MOD2,现在配置修改后MOD4),扩容完成

输入图片说明

原MOD2为偶的部分,现在会MOD4余0或者2;

原MOD2为奇的部分,现在会MOD4余1或者3;

数据不需要迁移,同时,双主互相同步,一遍是余0,一边余2,两边数据同步也不会冲突,秒级完成扩容!

  1. 最后,要做一些收尾工作:

(1)将旧的双主同步解除

(2)增加新的双主(双主是保证可用性的,shadow-master平时不提供服务)

(3)删除多余的数据(余0的主,可以将余2的数据删除掉)

输入图片说明

#3 数据库拆库实战#

四类场景覆盖99%拆库业务:

  1. “单key”场景,用户库如何拆分: user(uid, XXOO)
  2. “1对多”场景,帖子库如何拆分: tiezi(tid, uid, XXOO)
  3. “多对多”场景,好友库如何拆分: friend(uid, friend_uid, XXOO)
  4. “多key”场景,订单库如何拆分:order(oid, buyer_id, seller_id, XXOO)

##3.1 用户库如何拆分##

用户库,10亿数据量:

user(uid, uname, passwd, age, sex, create_time);

业务需求如下:

a)1%登录请求 => where uname=XXX and passwd=XXX

b)99%查询请求 => where uid=XXX

结论:“单key”场景使用“单key”拆库

##3.2 帖子库如何拆分##

帖子库,15亿数据量:

tiezi(tid, uid, title, content, time);

业务需求如下:

a)查询帖子详情(90%请求)=> SELECT * FROM tiezi WHERE tid=$tid

b)查询用户所有发帖(10%请求)=> SELECT * FROM tiezi WHERE uid=$uid

结论:“1对多”场景使用“1”分库,例如帖子库1个uid对应多个tid,则使用uid分库,tid生成时加入分库标记

##3.3 好友库如何拆分##

好友库,1亿数据量:

friend(uid, friend_uid, nick, memo, XXOO);

业务需求如下:

a)查询我的好友(50%请求) => 用于界面展示 SELECT friend_uid FROM friend WHERE uid=$my_uid

b)查询加我为好友的用户(50%请求) => 用户反向通知 SELECT uid FROM friend WHERE friend_uid=$my_uid

结论:“多对多”场景,使用数据冗余方案,多份数据使用多种分库手段

##3.4 订单库如何拆分##

订单库,10亿数据量:

order(oid, buyer_id, seller_id, order_info, XXOO);

业务需求如下:

a)查询订单信息(80%请求)=> SELECT * FROM order WHERE oid=$oid

b)查询我买的东东(19%请求)=> SELECT * FROM order WHERE buyer_id=$my_uid

c)查询我卖出的东东(1%请求)=> SELECT * FROM order WHERE seller_id=$my_uid

结论:“多key”场景一般有两种方案:

a)方案一,使用3.2和3.3综合的方案

b)方案二,1%的请求采用多库查询

#4 分库后业务实战#

分库后出现的问题:单库时mysql的SQL功能不再支持了。

##4.1 分库后,IN查询怎么玩##

用户库如何进行uid的IN查询:

user(uid, uname, passwd, age, sex, photo, create_time, ...);

Partition key:uid

查询需求:IN查询:WHERE uid IN(1,2,3,4,5,6)

输入图片说明

解决方案:服务做MR

方案一:直接分发

方案二:拼装成不同SQL,定位不同的库

##4.2 分库后,非Partition key的查询怎么玩##

方案一:业务方不关心数据来自哪个库,可以只定位一个库

输入图片说明

方案二:结果集只有一条数据,业务层做分发,只有一条记录返回就返回

输入图片说明

##4.3 分库后,夸库分页怎么玩##

问题的提出与抽象:ORDER BY xxx OFFSET xxx LIMIT xxx

a)按时间排序

b)每页100条记录

c)取第100页的记录

分库后的难题:如何确认全局偏移量

分库后传统解决方案,查询改写+内存排序

a)ORDER BY time OFFSET 0 LIMIT 10000+100

b)对20200条记录进行排序

c)返回第10000至10100条记录

优化方案一:增加辅助id,以减少查询量

a)技术上,引入特殊id,作为查询条件(或者带入上一页的排序条件)

b)业务上,尽量禁止跨页查询

单库情况:

输入图片说明

a)第一页,直接查

b)得到第一页的max(id)=123(一般是最后一条记录)

c)第二页,带上id>123查询:WHERE id>123 LIMIT 100

多库情况:

输入图片说明

a)将WHERE id>xxx LIMIT 100分发

b)将300条结果排序

c)返回前100条

优点:避免了全局排序,只对小量记录进行排序

优化方案二:模糊查询

a)业务上:禁止查询XX页之后的数据

b)业务上:允许模糊返回 => 第100页数据的精确性真这么重要么?

优化方案三:终极方案,查询改写与两段查询

方案一和方案二在业务上都有所折衷,前者不允许跨页查询,后者数据精度有损失,解决夸库分页问题的终极方案是,将order by + offset + limit进行查询改写,分两段查询。

© 著作权归作者所有

共有 人打赏支持
陶邦仁
粉丝 1649
博文 420
码字总数 1483887
作品 0
海淀
技术主管
私信 提问
加载中

评论(1)

qwfys
qwfys
很详细
jeesz分布式架构-分布式高可用

什么是高可用 高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。 常见互联网分布式架构如上,分为: (1)客户端...

壹玖
2018/08/09
0
0
究竟啥才是互联网架构“高可用”

原创 2016-12-05 58沈剑 架构师之路 一、什么是高可用 高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。 假设系...

毛爷爷夸我帅
2016/12/06
10
0
互联网高可用架构技术实践

一、什么是高可用 高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。 假设系统一直能够提供服务,我们说系统的可...

LionelShen
2016/12/06
45
0
MySQL高可用在网易的最佳应用与实践

本文根据DBAplus社群第102期线上分享整理而成。 主题简介: 1、常见的MySQL高可用架构 2、分布式数据库高可用实践 3、基于keepalive的MySQL高可用改造 大家好,我是来自杭州研究院的潘威,今...

潘威
2017/05/08
0
0
MySQL高可用在网易的最佳应用与实践

本文根据DBAplus社群第102期线上分享整理而成。 主题简介: 1、常见的MySQL高可用架构 2、分布式数据库高可用实践 3、基于keepalive的MySQL高可用改造 大家好,我是来自杭州研究院的潘威,今...

潘威
2017/05/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
44分钟前
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
2
0
微信网页授权获取用户信息(ThinkPHP5)+ 微信发送客服消息(一)

以thinkphp5为实例,创建控制器 class Kf extends Controller { /** * [protected description]微信公众号appid * @var [type] */ protected $appid = "xxxxxxxxxxxxxxx"; /** * [protected......

半缘修道半缘君丶
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部