Mysql+Keepalived实现双主高可用 分库分表

Mysql+Keepalived实现双主高可用

什么是Keepalived?

Mysql+Keepalived实现双主高可用 分库分表_第1张图片

Keepalived是一个工作在IP/TCP协议栈的IP层,TCP层及应用层实现交换机制的软件。

Keepalived的作用是检测服务器的状态,如果有一台服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。

原理如下:

IP层: Keepalived会定期向服务器群中的服务器发送一个ICMP的数据包(既我们平时用的Ping程序),如果发现某台服务的IP地址没有激活,Keepalived便报告这台服务器失效,并将它从服务器群中剔除。

TCP层:主要以TCP端口的状态来决定服务器工作正常与否。如web server的服务端口一般是80,如果Keepalived检测到80端口没有启动,则Keepalived将把这台服务器从服务器群中剔除。

应用层:对指定的URL执行HTTP GET。然后使用MD5算法对HTTP GET结果进行求和。如果这个总数与预期值不符,那么测试是错误的,服务器将从服务器池中移除。该模块对同一服务实施多URL获取检查。

Keepalived是基于VRRP协议的一款高可用软件。Keepailived有一台主服务器和多台备份服务器,在主服务器和备份服务器上面部署相同的服务配置,使用一个虚拟IP地址对外提供服务,当主服务器出现故障时,虚拟IP地址会自动漂移到备份服务器。

名词解释:

VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议),由IETF提出的解决局域网中配置静态网关出现单点失效现象的路由协议,1998年已推出正式的RFC2338协议标准。相类似的协议有:GLBP(思科Cisco独有), HSRP等等。

和HAProxy的区别是什么?

HAProxy请求分发
可以理解为类似于通过nginx来作后端的负载均衡,haproxy可以通过监听一个统一的端口对外提供能力,然后内部进行分发。除支持http7层处理外,还顺便为mysql支持4层转发。(更高级的可以考虑采用lvs) 。

举例说明,将两个mysql分别配置在haproxy当中,程序进行访问时,就不再访问具体的mysql机器,而是访问这个haproxy所在的机器。这里就提到需要一个额外的机器来提供服务,但是由于只为haproxy使用,其根据很低,一个最低配机器即可,费用不大。同时,相应的端口也填写haproxy所暴露的端口即可。对外即认为也只仍然只有一个mysql,即对外是透明的。

haproxy在进行处理时,将自己根据相应的策略进行转发,最简单的策略就是轮询(ribbon),当然其它加权或者是随机等,需要具体进行配置。同时,根据设定的具体时间间隔,对后端服务进行有效性检测,当mysqlA或B不能工作时,将自动从可用列表中移除。

在加上haproxy之后,负载的问题被解决,但另一个问题又来了,即服务单点的问题。如果一旦这个haproxy机器挂掉(或网络原因)。虽然,mysql服务器没挂,但整个服务也是不可用了。前端程序不会自动退回到去访问原始的mysql(甚至由于防火墙的问题,它也不能访问)。这时候就要用到另一个东西,保证haproxy不会成为单点,即KeepAlived。

保证服务不会单点的作法就是加机器,但加机器就会出现多个ip,如何保证前端程序使用单个ip又能保证后端的实际处理机器为多台,这就是KeepAlived的作用。

我们为了保证haproxy的高可用,已经又加了一个机器,即为haproxyA和haproxyB(相当于原来的2个mysql机器,又加了2台haproxy机器)。

通过KeepAlived,我们可以创造出第3个IP(也称虚拟IP),由虚拟IP来对外提供服务,但是与HaProxy的转发性质不同,这里的虚拟IP实际上就是HaProxyA和HaProxyB的一个同名映射。可以理解为HaProxyA和HaProxyB都在争抢这个ip,哪个争抢到了,就由哪个来提供服务。

因为KeepAlived不提供任何处理能力,实际上最终的处理落在能够处理信息的程序上。因此,我们可以将KeepAlived和HaProxy部署在一起,即KeepAlived负责抢ip,接收前端的请求,在接收到了请求之后,由系统自动将请求分发到同一个机器上的HaProxy上进行处理。即一个机器有2个IP,ip1负责接收请求,ip2负责实际的信息处理。

最终部署模型

a、MysqlA(ip5)和MysqlB(ip6)水平扩展,通过双主进行数据通信,并且同时提供服务能力

b、通过HaProxyA(ip3)和HaProxyB(ip4)提供mysql的负载能力,将请求路由到指定的mysql服务器,同时监控后端的mysql数据库可用性,如果取消这一层,则Mysql的两个服务器不能同时提供写服务。

c、将KeepAlivedA和KeepAlivedB分别和HaProxyA和HaProxyB部署在一起,同时绑定VIP,对外提供访问ip,同时监控本机的HaProxy的可用性

Mysql+Keepalived双主搭建说明

最关键的部分其实是Mysql的两个服务器要互为主从,所以在my.cnf的配置文件中,除了log-bin参数以外,还需要增加并设置参数logs-slave-updates=1

logs-slave-updates参数解释:

通常情况,从服务器从主服务器接收到的更新不记入它的二进制日志。该选项告诉从服务器将其SQL线程执行的更新记入到从服务器自己的二进制日志。为了使该选项生效,还必须用--logs-bin选项启动从服务器以启用二进制日志。如果想要应用链式复制服务器,应使用--logs-slave-updates。例如,可能你想要这样设置:

A -> B -> C

也就是说,A为从服务器B的主服务器,B为从服务器C的主服务器。为了能工作,B必须既为主服务器又为从服务器。你必须用--logs-bin启动A和B以启用二进制日志,并且用--logs-slave-updates选项启动B。

Mysql其余的配置同异步复制,比如:Aß--àB

那么A、B上都要配置复制用户名,都要执行CHANGE MASTER TO语句,都要执行start slave语句。

至于Keepalived的安装和配置比较复杂,在技能上至少要求可以编写sh脚本。有兴趣的同学可以参考:

https://www.cnblogs.com/ivictor/p/5522383.html

分库分表综述

Mysql+Keepalived实现双主高可用 分库分表_第2张图片

Mysql+Keepalived实现双主高可用 分库分表_第3张图片 

Mysql+Keepalived实现双主高可用 分库分表_第4张图片 

 

分库分表需求的产生和定义

分库分表, 顾名思义, 就是使用多个库和多个表甚至多个数据库实例来存储海量的数据。为了分散数据库的压力, 我们会采用分库分表的方案, 将一个表结构分为多个表, 或者将一个表的数据分片后放入多个表, 这些表可以放在同一个库里, 也可以放到不同的库里, 甚至可以放在不同的数据库实例上。

不同的业务规模阶段数据库的变化

单实例单库

单库单表是最常见的数据库设计,例如现在我们有用户表(以下简称 user表),订单表(以下简称 order表), 所有用户和订单的信息都被存储在同一个数据库中。随着业务规模的增加,我们的数据库在架构上的变化有几种可能的路径。

多实例(多机)单库,读写分离 

用户表和订单表仍然归属同一个数据库,但是数据库有多份,并放在多个实例上,同时实例之间启用了复制功能和读写分离。

多库(单实例/多实例) 

用户表和订单表分开来放到不同的数据库上,比如用户库和订单库。同时这些库分布在单个数据库实例上或者多个数据库实例上都有可能。至于是否启用读写分离则看情况而定。

多库多表 

当业务规模进一步增加,单表的数据已经超过了一定量,比如1000万,这个时候,就会进入另一个变化阶段。会将不同的业务表拆分到不同业务数据库,同时业务表也会进行拆分,也就是分库分表。

什么情况下需要分库分表?

总结一下,首先, 如果在一个库中的表数据超过了一定的数量, 例如在 MySQL的表中达到千万级别, 就需要考虑进行分表, 这样, 数据就被分散在不同的表上, 单表的索引大小得到了挠制, 会提升査询性能, 对索引及表结构的变更会很方便和高效 。 当数据库实例的吞吐量达到性能的瓶颈时,我们需要扩展数据库实例,让每个数据库实例承担其中一部分数据库的请求,分解总体的大请求量带来的压力。

所以,基本上就是两点:

如果在数据库中表的数量达到了一定的量级, 则需要进行分表, 分解单表的大数据量对索引査询带来的压力, 并方便对索引和表结构的变更 。

如果数据库的吞吐量达到了瓶颈, 就需要增加数据库实例, 利用多个数据库实例来分解大量的数据库请求带来的系统压力。

分库分表后,如何对数据进行访问?

客户端分片

客户端分片就是使用分库分表的数据库的应用层直接操作分片逻辑, 分片规则需要在同一个应用的多个节点间进行同步, 每个应用层都嵌入一个操作切片的逻辑实现 (分片规则),这一般通过依赖 Jar包来实现具体的实现方式分为三种:在应用层直接实现、通过定制JDBC协议实现、通过定制 ORM框架实现。

应用层直接实现

这是一种非常通用、 简单的解决方案, 直接在应用层读取分片规则, 然后解析分片规则, 根据分片规则实现切分的路由逻辑,从应用层直接决定每次操作应该使用哪个数据库实例、数据库及哪个数据库的表等。

这种解决方案虽然侵入了业务, 但是实现起来比较简单, 适合快速上线, 而且性能更高,切分逻辑是自己开发的,如果在生产上产生了问题,则都比较容易解决。但是,这种实现方式会让数据库保持的连接比较多。

定制JDBC协议

在应用层直接实现或多或少使切片逻辑侵入了应用的业务实现, 而且对开发人员的能力要求较高, 开发人员既需要实现业务逻辑, 也需要实现框架需求。

为了让开发人员把精力集中在业务逻辑的实现上,我们可以通过定制 JDBc协议来实现,也就是针对业务逻辑层提供与 JDBC 一致的接口,让业务开发人员不必关心分库分表的实现,让分库分表在 JDBC的内部实现,对业务层保持透明。

这种解决方案对业务透明,不侵入业务,让开发和分库分表的配置人员在一定程度上可以分离,可以让开发人员更大限度地聚焦在业务逻辑的实现上,但开发人员需要理解 JDBC协议才能实现分库分表逻辑。Sharding JDBC便采用了这种方案。

定制ORM框架

因为现在ORM框架有了大量的应用,因此有了通过定制ORM框架实现分库分表的方案,也就是把分片规则实现放到ORM框架中或者通过ORM框架支持的扩展机制来完成。

代理分片

代理分片就是在应用层和数据库层之间增加一个代理层。代理层对外提供与JDBC兼容的接口给应用层,应用层的开发人员不用关心分片规则,只需要关心业务逻辑的实现。

这种方案的优点是让应用层的开发人员专注于业务逻辑的实现, 把分库分表的配置留给代理层做;缺点是增加了代理层。尽管代理层是轻量级的转发协议,但是毕竟要实现 JDBC协议的解析, 并通过分片的路由规则来路由请求, 对每个数据库操作都增加了一层网络传输, 这对性能是有影响的,需要维护增加的代理层,也有硬件成本,还要有能够解决Bug的技术专家, 成本很高。

通过代理分片实现的主要框架有 Cobar 、Mycat和Sharding-Proxy等。

分库分表第三方产品

京东金融的ShardingSphere   

网址:http://shardingsphere.io/index_zh.html

脱胎于当当网的Sharding-JDBC,在京东金融得到重生,并且目前已经进入Apache孵化器,目前已经组成了分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能。作为京东第一个进入Apache基金会的开源产品,值得持续关注。

开源的Cobar和MyCat

Cobar 是由 Alibaba 开源的 MySQL 分布式处理中间件,目前已停止维护,它可以在分布式的环境下看上去像传统数据库一样提供海量数据服务。社区使用过程中发现存在一些比较严重的问题及使用限制,于是Mycat诞生。开源以后,最终Mycat发展成为一个由众多软件公司的实力派架构师和资深开发人员维护的社区型开源软件。

Mycat对Cobar的代码进行了彻底的重构,使用NIO重构了网络模块,并且优化了Buffer内核,增强了聚合,Join等基本特性,同时兼容绝大多数数据库成为通用的数据库中间件。1.4 版本以后完全的脱离基本Cobar内核。

目前看来,Mycat虽然市场占有率很高,但是Mycat现在呈现一个发展停滞的状态,未来前景如何还有待观察。

网址: http://www.mycat.io/

其他第三方的中间件

例如Oneproxy ,由原支付宝的首席DBA为创始人的公司开发,提供了免费版和商用版。使用C&C++开发,单个OneProxy实例可支持高达40W的QPS/TPS 。商用版则有更强的性能,更多的功能,还可以得到正式的技术支持。

有专门的运维或者DBA的公司,则可以考虑使用。最大的好处是对开发人员完全透明。

网址: http://www.onexsoft.com/

更多的分库分表中间件:

TDDL:已经不再使用,下一代是 DDRS。淘宝根据自己的业务特点开发了 TDDL (Taobao Distributed Data Layer 外号:头都大了 )框架。 TDDL 通过部署在客户端的 jar 包,将用户的 SQL 路由到指定的数据库中。 

Heisenberg:源自Coba,目前并不活跃,由百度员工个人编写。

Atlas : 360 Web平台部基础架构团队开发维护的一个基于 MySQL 协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性

Vitess:谷歌开发的数据库中间件,集群基于 ZooKeeper 管理,通过 RPC 方式进行数据处理

DRDS:阿里分布式关系型数据库服务(Distribute Relational Database Service,简称DRDS)是一种水平拆分、可平滑扩缩容、读写分离的在线分布式数据库服务。前身为淘宝 TDDL。

还有:Mango,Amoeba等等

常用切分方式

垂直切分

把单一库中的表按业务拆分到多个库,或者把单一的表拆分成多个表,并分散到不同的数据库(主机)上。

垂直切分除了用于分解单库单表的压力, 也用于实现冷热分离, 也就是根据数据的活跃度进行拆分, 因为对拥有不同活跃度的数据的处理方式不同 。

我们可将本来可以在同一个表中的内容人为地划分为多个表。比如在微博系统的设计中, 一个微博对象包括文章标题、作者、分类、创建时间等属性字段,这些字段的变化频率低,査询次数多,叫作冷数据。而博客的浏览量、回复数、 点赞数等类似的统计信息, 或者别的变化频率比较高的数据, 叫作活跃数据或者热数据。我们把冷热数据分开存放,就叫作冷热分离,在 MysQL的数据库中,冷数据査询较多, 更新较少可以用MyISAM引擎,而热数据更新比较频繁,适合使用InnoDB存储引擎,这也是垂直拆分的一种。

其次, 对读多写少的冷数据可配置更多的从库来化解大量査询请求的压力; 对于热数据, 可以使用多个主库构建分库分表的结构,对于一些特殊的活跃数据或者热点数据,也可以考虑使用 Memcache、 Redis之类的缓存,等累计到一定的量后再更新数据库。

优点:

拆分后业务清晰,拆分规则明确。

系统之间进行整合或扩展很容易。

按照成本、应用的等级、应用的类型等将表放到不同的机器上,便于管理。

便于实现动静分离、冷热分离的数据库表的设计模式。

数据维护简单。

缺点:

部分业务表无法关联(Join),只能通过接口方式解决,提高了系统的复杂度。

事务处理复杂。

水平切分

根据表中数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上。在每个表中包含一部分数据, 所有表加起来就是全量的数据。这种切分方式根据单表的数据量的规模来切分, 保证单表的容量不会太大, 从而保证了单表的査询等处理能力,例如将用户的信息表拆分成user1、 User2等,表结构是完全一样的。我们通常根据某些特定的规则来划分表, 比如根据用户的 ID来取模划分。

优点:

单库单表的数据保持在一定的量级,有助于性能的提高。

 切分的表的结构相同,应用层改造较少,只需要增加路由规则即可。

 提高了系统的稳定性和负载能力 。

缺点:

切分后,数据是分散的,很难利用数据库的 Join操作,跨库 Join性能较差。

 分片事务的一致性难以解决。

 数据扩容的难度和维护量极大。

 我们就可以根据自己的业务需求来选择, 通常会同时使用这两种切分方式, 垂直切分更偏向于业务拆分的过程, 在技术上我们更关注水平切分的方案。

水平切分的路由和常见分片纬度

水平切分的路由过程

我们在设计表时需要确定对表按照什么样的规则进行分库分表。 例如, 当有新用户登录时, 程序需要确定将此用户的信息添加到哪个表中; 同理, 在登录时我们需要通过用户的账号找到数据库中对应的记录, 所有这些都需要按照某一规则进行路由请求, 因为请求所需要的数据分布在不同的分片表中。

通过分库分表规则査找到对应的表和库的过程叫作路由。假设,分库分表的规则是 user_id%4, 当用户新注册了一个帐号时,如果用户的 ID是123,我们就可以通过123%4=3确定此账号应该被保存在 User3表中。当 ID为123的用户登录时,我们可通过123 % 4 = 3计算后, 确定其被记录在 User3中 。

水平切分的分片维度

1)按照哈希切片

对数据的東个字段求哈希, 再除以分片总数后取模, 取模后相同的数据为一个分片的将数据分成多个分片的方法叫作哈希分片,合希分片常常应用于数据没有时效性的情况,这种切片方式的好处是数据切片比较均匀, 对数据压力分散的效果较好, 缺点是数据分片后, 对于査询需求需要进行聚合处理,

2)按照时间切片

按照哈希切片不同, 这种方式是按照时间的范围将数据分布到不同的分片上的, 例如, 我们可以将交易数据按照月进行切片, 或者按照季度进行切片, 由交易数据的多少来决定按照什么样的时间周期对数据进行切片,,这种切片方式适用于有明显时间特点的数据,例如,距离现在1个季度的数据访问频繁,距离现在2个季度的数据不需要更新,距离现在3个季度的数据没有查询要求。就可以按时间进行分片,用不同的介质进行存放。

在实践中,按照哈希切片和按时间切片都很常见,而且有时会两种结合起来使用。

分库分表引发的问题

 扩容与迁移

在分库分表后, 如果涉及的分片已经达到了承载数据的最大值, 就需要对集群进行扩容。 扩容是很麻烦的, 一般会成信地扩容。

分库分表维度导致的查询间题

在分库分表以后, 如果査询的标准是分片的主键, 则可以通过分片规则再次路由并查询。

但是对于其他主键的查询、范围査询、关联査询、查询结果排序等,并不是按照分库分表维度来査询的。例如,用户购买了商品,需要将交易记录保存下来,那么如果按照买家的纬度分表,则每个买家的交易记录都被保存在同一表中, 我们可以很快、 很方便地査到某个买家的购买情况, 但是某个商品被购买的交易数据很有可能分布在多张表中, 査找起来比较麻烦 。 反之, 按照商品维度分表, 则可以很方便地査找到该商品的购买情况, 但若要査找到买家的交易记录, 则会比较麻烦 。

所以常见的解决方式如下。

( 1 ) 在多个分片表查询后合并数据集, 这种方式的效率很低。

( 2 ) 记录两份数据, 一份按照买家纬度分表, 一份按照商品维度分表,,

( 3 ) 通过搜索引擎解决, 但如果实时性要求很高, 就需要实现实时搜索

实际上,在高并发的服务平台下,交易系统是专门做交易的,因为交易是核心服务,所以需要和査询系统分离, 査询一般通过其他系统进行, 数据也可能是冗余存储的。

在某电商交易平台下, 可能有买家査询自己在某一时间段的订单, 也可能有卖家査询自已在某一时间段的订单, 如果使用了分库分表方案, 则这两个需求是难以满足的, 因此, 通用的解决方案是, 在交易生成时生成一份按照买家分片的数据副本和一份按照卖家分片的数据副本,查询时分别满足之前的两个需求,因此,查询的数据和交易的数据可能是分别存储的,并从不同的系统提供接口。

另外,在电商系统中,在一个交易订单生成后,一般需要引用到订单中交易的商品实体如果简单地引用,若商品的金额等信息发生变化,则会导致原订单上的商品信息也会发生变化这样买家会很疑惑。因此,通用的解决方案是在交易系统中存储商品的快照,在査询交易时使用交易的快照,因为快照是个静态数据,永远都不会更新,所以解决了这个问题。可见查询的问题最好在单独的系统中使用其他技术来解决,而不是在交易系统中实现各类查询功能。当然当然,也可以通过对商品的变更实施版本化,在交易订单中引用商品的版本信息,在版本更新时保留商品的旧版本,这也是一种不错的解决方案。

3.跨库事务难以实现

要避免在一个事务中同时修改数据库db0和数据库dbl中的表,因为操作起来很复杂,对效率也会有一定的影响。

4.同组数据跨库问题

要尽量把同一组数据放到同一台数据库服务器上,不但在某些场景下可以利用本地事务的强一致性,还可以使这组数据自治。以电商为例,我们的应用有两个数据库db0和dbl,分库分表后,按照id维度,将卖家A的交易信息存放到db0中。当数据库dbl挂掉时,卖家A的交易信息不受影响,依然可以正常使用。也就是说,要避免数据库中的数据依赖另一数据库中的数据。

 

你可能感兴趣的:(mysql,数据库)