美团点评集团统一使用的MySQL数据库访问层的中间件。主要提供对业务开发透明、读写分库、分库分表能力,并提供了端到端SQL监控的集成方案。
介绍
Zebra是一个基于JDBC API协议上开发出的高可用、高性能的数据库访问层解决方案,是美团点评内部使用的数据库访问层中间件。具有以下的功能点:
核心
模块
Zebra总体设计
zebra客户端架构
在阅读过上述基础知识之后,下面来介绍zebra的客户端架构:
其中:
1、最上层的是ShardDataSource,用于进行分库分表。ShardDataSource包含了若干个GroupDataSource,每个连接的数据库集群相当于1个分片(Shard)。
2、中间一层是GroupDataSource,主要用于读写分离。下面通过一组SingleDataSource连接一个数据库服务组集群,分为一个主和若干个从。
3、最下一层是SingleDataSource,主要用于和mysql集群中的单个mysql实例直接建立连接。支持6种连接池:dbcp、dbcp2、druid、tomcat-jdbc、c3p0,hikaricp。用户无需直接使用SingleDataSource。
ShardDataSource、GroupDataSource都实现了JDBC协议的javax.sql.DataSource接口,因此你可以把二者都当做一个普通的数据库连接池来使用。所有读写分离、分库分表的底层实现逻辑,都对用户进行了屏蔽。
数据库中间件主流设计
数据库中间件的作用
在未进行读写分离/分库分表的情况下,我们是直接在应用中通过数据源(c3p0、druid、dbcp2等)与数据库建立连接,进行读写操作,架构如下所示:
可以看到在操作单库单表的情况下,我们是直接在应用中通过数据源(c3p0、druid、dbcp等)与数据库建立连接,进行读写操作。
大部分开发人员对于访问单库的应用的架构都是很熟悉的。但是在进行读写分离/分库分表后,底层的数据库实例就会有多个,读写分离情况下一个master多个slave;分库分表的情况下,有多个不同的分库。
从应用的角度来说,除了要与多个不同的数据库建立连接,还需要处理分库分表/读写分离特定场景下的问题:
这无疑是个复杂的工作,而数据库中间件的作用,是让开发人员可以像操作单库单表那样去操作数据库,屏蔽底层复杂的实现。
2 数据库中间件设计方案
典型的数据库中间件设计方案有2种:服务端代理(proxy:代理数据库)、客户端代理(datasource:代理数据源)。下图演示了这两种方案的架构:
可以看到不论是代理数据库还是代理数据源,底层都操作了多个数据库实例。不同的是:
服务端代理(proxy:代理数据库)中: 我们独立部署一个代理服务,这个代理服务背后管理多个数据库实例。而在应用中,我们通过一个普通的数据源(c3p0、druid、dbcp等)与代理服务器建立连接,所有的sql操作语句都是发送给这个代理,由这个代理去操作底层数据库,得到结果并返回给应用。在这种方案下,分库分表和读写分离的逻辑对开发人员是完全透明的。
客户端代理(datasource:代理数据源): 应用程序需要使用一个特定的数据源,其作用是代理,内部管理了多个普通的数据源(c3p0、druid、dbcp等),每个普通数据源各自与不同的库建立连接。应用程序产生的sql交给数据源代理进行处理,数据源内部对sql进行必要的操作,如sql改写等,然后交给各个普通的数据源去执行,将得到的结果进行合并,返回给应用。数据源代理通常也实现了JDBC规范定义的API,因此能够直接与orm框架整合。在这种方案下,用户的代码需要修改,使用这个代理的数据源,而不是直接使用c3p0、druid、dbcp这样的连接池。
3 主流的数据库中间件实现对比
无论是代理数据库,还是代理数据源,二者的作用都是类似的。以下列出了这两种方案目前已有的实现以及各自的优缺点:
数据库代理
目前的实现方案有:阿里巴巴开源的cobar,mycat团队在cobar基础上开发的mycat,mysql官方提供的mysql-proxy,奇虎360在mysql-proxy基础开发的atlas。目前除了mycat,其他几个项目基本已经没有维护。
优点:多语言支持。也就是说,不论你用的php、java或是其他语言,都可以支持。原因在于数据库代理本身就实现了mysql的通信协议,你可以就将其看成一个mysql 服务器。mysql官方团队为不同语言提供了不同的客户端驱动,如java语言的mysql-connector-java,python语言的mysql-connector-python等等。因此不同语言的开发者都可以使用mysql官方提供的对应的驱动来与这个代理服务器建通信。
缺点:实现复杂。因为代理服务器需要实现mysql服务端的通信协议,因此实现难度较大。
数据源代理
目前的实现方案有:阿里巴巴开源的tddl,大众点评开源的zebra,当当网开源的sharding-jdbc。
优点:更加轻量,可以与任何orm框架整合。这种方案不需要实现mysql的通信协议,因为底层管理的普通数据源,可以直接通过mysql-connector-java驱动与mysql服务器进行通信,因此实现相对简单。
缺点:仅支持某一种语言。例如tddl、zebra、sharding-jdbc都是使用java语言开发,因此对于使用其他语言的用户,就无法使用这些中间件。版本升级困难,因为应用使用数据源代理就是引入一个jar包的依赖,在有多个应用都对某个版本的jar包产生依赖时,一旦这个版本有bug,所有的应用都需要升级。而数据库代理升级则相对容易,因为服务是单独部署的,只要升级这个代理服务器,所有连接到这个代理的应用自然也就相当于都升级了。
ORM框架代理
目前有hibernate提供的hibernate-shards,也可以通过mybatis插件的方式编写。相对于前面两种方案,这种方案可以说是只有缺点,没有优点。
4 Zebra的设计方式
zebra采用客户端代理的方式来实现读写分离和分库分表的功能,需要引入zebra-client的相关依赖。
因为是Java语言的客户端代理,所以基本上按照Java中的JDBC协议去实现的代理。
GroupDataSource:用于读写分离,请参考:Zebra读写分离接入指南
ShardDataSource:用于分库分表,接入请参考:Zebra分库分表接入指南
1 读写分离介绍
在单台mysql实例的情况下,所有的读写操作都集中在这一个实例上。当读压力太大,单台mysql实例扛不住时,此时DBA一般会将数据库配置成集群,一个master(主库),多个slave(从库),master将数据通过binlog的方式同步给slave,可以将slave节点的数据理解为master节点数据的全量备份。关于如何配置mysql主从同步,可以参考mysql官方文档:https://dev.mysql.com/doc/refman/5.7/en/replication.html
从应用的角度来说,需要对读(select、show、explain等)、写(insert、update、delete等)操作进行区分。如果是写操作,就走主库,主库会将数据同步给从库;之后有读操作,就走从库,从多个slave中选择一个,查询数据。上述流程如下图所示:
2 读写分离优点
避免单点故障。
3 读写分离挑战
对sql类型进行判断。如果是select等读请求,就走从库,如果是insert、update、delete等写请求,就走主库。
4 zebra与读写分离
zebra提供了GroupDataSource来完成读写分离功能,解决了上述所有问题,且对业务方透明。开发人员可以像操作单个库那样,去访问mysql数据库集群,底层细节完全由zebra屏蔽。GroupDataSource还额外提供了就近路由、限流等多种功能。
关于读写分离功能接入,请参考:https://github.com/Meituan-Dianping/Zebra/wiki/Zebra%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB%E6%8E%A5%E5%85%A5
Zebra分库分表介绍
1 分库分表介绍
读写分离,主要是为了数据库读能力的水平扩展(参考:Zebra读写分离介绍)
一旦业务表中的数据量大了,从维护和性能角度来看,无论是任何的 CRUD 操作,对于数据库而言都是一件极其耗费资源的事情。即便设置了索引, 仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实 ,这个时候就该对数据库进行 水平分区 (sharding,即分库分表 ),将原本一张表维护的海量数据分配给 N 个子表进行存储和维护。
水平分表从具体实现上又可以分为3种:只分表、只分库、分库分表,下图展示了这三种情况:
只分表:将db库中的user表拆分为2个分表,user_0和user_1,这两个表还位于同一个库中。
只分库:将db库拆分为db_0和db_1两个库,同时在db_0和db_1库中各自新建一个user表,db_0.user表和db_1.user表中各自只存原来的db.user表中的部分数据。
分库分表:将db库拆分为db_0和db_1两个库,db_0中包含user_0、user_1两个分表,db_1中包含user_2、user_3两个分表。
下图演示了在分库分表的情况下,数据是如何拆分的:假设db库的user表中原来有4000W条数据,现在将db库拆分为2个分库db_0和db_1,user表拆分为user_0、user_1、user_2、user_3四个分表,每个分表存储1000W条数据。
2 分库分表优点
分库的好处: 降低单台机器的负载压力,提升写入性能
分表的好处: 提高数据操作的效率。举个例子说明,比如user表中现在有4000w条数据,此时我们需要在这个表中增加(insert)一条新的数据,insert完毕后,数据库会针对这张表重新建立索引,4000w行数据建立索引的系统开销还是不容忽视的。但是反过来,假如我们将这个表分成4 个table呢,从user_0一直到user_3,4000w行数据平均下来,每个子表里边就只有1000W行数据,这时候我们向一张 只有1000W行数据的table中insert数据后建立索引的时间就会下降,从而提高DB的运行时效率,提高了DB的并发量。除了提高写的效率,更重要的是提高读的效率,提高查询的性能。当然分表的好处还不止这些,还有诸如写操作的锁操作等,都会带来很多显然的好处。
3 Zebra与分库分表
zebra提供了ShardDataSource来完成分库分表功能,主要解决的是分库分表的基本增删改查问题。
关于分库分表功能接入,请参考:Zebra分库分表接入指南
https://github.com/Meituan-Dianping/Zebra/wiki/Zebra%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E6%8E%A5%E5%85%A5
Mysql 高可用架构 https://blog.csdn.net/qq_23864697/article/details/79465772