没有什么是静止的,包括我们的理想和生活!MySQL作为一款免费开源的关系型数据库,深受大家的喜爱,从最初的无人问津到现在IOE的下架,体现了MySQL举足轻重的作用。今天我们就从淘宝的发展说起大型网站下MySQL架构的演进史!MySQL的可扩展架构的可扩展性往往与并发性密切相关。没有并发增长,就没有必要做高扩展架构,这里简单介绍下可扩展性。常用的扩容方式有以下两种Scale-up:垂直扩容,通过更换更好的机器和资源来实现扩容,提升服务能力Scale-out:水平扩容,通过增加节点(机器)来实现Scaling,提升服务能力.对于高并发的互联网应用,横向扩展无疑是出路。垂直购买更高端的机器一直是我们避免的问题,也不是长久之计,scaleout理论下,理想的可扩展性状态是什么?可扩展性的理想状态一个服务,当面临更高的并发时,可以通过简单的增加机器来增加服务支持的并发量,并且增加机器过程中对在线服务没有影响(没有停机时间),这就是理想状态可扩展性状态!MySQL架构演进MySQL简易网站架构(V1.0)一个简单的小网站或应用背后的架构可以很简单,数据存储只需要一个mysql实例即可满足数据读写需求(忽略数据备份实例这里),这个时间段的网站一般都是把所有的信息都存储在一个数据库实例中。在这样的架构下,我们来看看数据存储的瓶颈是什么?1.数据量的总大小一台机器放不下。2.数据的索引(B+Tree)在机器的内存中放不下。3.访问量大(读写混合)一个实例承受不了。只有满足以上三点中的任意一个或多个,才需要考虑进化到下一个层次。由此我们可以看出,其实对于很多小公司和小应用来说,这种架构已经足够满足他们的需求了。准确评估初始数据量是防止过度设计的重要部分。浪费一个人的经验。这是我的一个简单例子。像用户信息这样的表(3个索引),16G内存可以存储2000W行左右的数据索引,简单的混合读写访问量3000/s左右是没有问题的。你的应用场景是MySQL的垂直架构(V2.0)一般当V1.0遇到瓶颈时,第一个也是最简单的拆分方式就是垂直拆分。什么是垂直?从业务的角度,将不强相关的数据分配给不同的实例,以达到消除瓶颈的目的。以图中为例,将用户信息数据和业务数据拆分为三个不同的实例。对于重复读类型比较多的场景,我们还可以加一层缓存,减轻DB的压力。在这样的架构下,我们来看看数据存储的瓶颈是什么?单实例单服务还是有V1.0描述的瓶颈。遇到瓶颈时,可以考虑升级到本文的高V版本。性能瓶颈可以考虑升级到V3.0,其他瓶颈可以考虑将MySQL的主从架构(V3.0)升级到V4.0。该架构主要解决V2.0架构下的读取问题。想法是迁移阅读压力。在Mysql场景下,采用的是主从结构。主库抗写压力,读压力由从库分担。对于写少读多的应用,V3.0主从架构完全有能力在这样的架构下,我们来看看数据存储的瓶颈是什么?主库无法承受写入量。当V2.0和V3.0解决方案遇到瓶颈时,可以使用MySQL的路由多集群架构(V4.0)。它通过水平拆分来解决。水平拆分和垂直拆分有很大的区别。垂直拆分的结果是一个实例上的全量数据。水平拆分后,任何instance都只有全量的1/n数据,以下图中Userinfo的拆分为例,将userinfo拆分成3个cluster,每个cluster持有总数据的1/3,总和3个集群的数据等于一个完整的数据(注:这里不再叫单实例而是集群,代表一个包含主从的小型mysql集群)数据如何路由?1.Range按照连续的区间拆分shardingkey和routes,一般用在对ID自增要求比较严格的场景,比如Userid,UseridRange的一个小例子:使用userid3000W作为Range来拆分1号clusteruserid1-3000WNo.2clusteruserid3001W-6000W2.ListSplittingList拆分与Range拆分相同。使用不同的shardingkey路由到不同的集群,但具体方法有些不同。List主要用于shardingkey不是落入一个cluster的连续区间序列的情况,比如下面的场景:假设有20家音像店分布在4个有分销权的区域,如图下表:业务希望将一个区域内的所有数据组织在一起进行搜索。这个场景Listsplitting可以轻松完成。3、hash分裂是通过对shardingkey进行hash来进行分裂,常用的hash方法有除法、stringhash等,除法是根据userid%n的值来决定读写数据的cluster,其他hash计算类型数据拆分后引入的问题这里就不细说了:数据水平拆分引入的问题主要是读写操作只能通过shardingkey进行。获取信息首先要知道userid,这样才能算出查询哪个集群。假设我需要通过username检索用户信息,需要引入额外的反向索引机制(类似HBASE二级索引),比如在redis上存储username->userid的映射,用username查询的例子就变成了先查询username->userid,再通过userid查询相应信息。其实这个方法很简单,但是我们不能忽视一个额外的隐患,那就是数据不一致的隐患。redis中存储的username->userid和mysql中存储的userid->username必须保持一致。这通常是一件很难保证的事情。比如修改用户名的场景,需要同时修改redis和mysql,这两个东西很难做到事务保证,比如mysql操作成功,redis操作失败(分布式事务引入成本高),对于互联网应用来说,可用性是最重要的,一致性是其次的,所以可以容忍少量的不一致。毕竟从比例上来说,这种不一致的比例可以忽略不计(一般都是用mq来保证更新不成功,直到成功停止重试操作)在这样的架构下,我们来看看是什么数据存储的瓶颈?基于这种拆分理念构建的架构理论上没有瓶颈(前提是shardingkey可以保证各个集群的流量相对均衡下),但是有一个很恶心的地方,就是集群时重做数据的成本扩大。比如我以前有3个集群,现在我的数据增长比较快,需要6个集群。然后我们需要将每个集群拆分成两个。一般的方法是移除一个slave,停止同步,写入增量日志(实现上,业务方可以写持久化mq或者mysqlmaster创建写操作的触发记录等方法)开始在数据上做数据staticslave,拆分成2个回放增量写,直到赶上所有增量,基本保持和原来cluster的同步写切换,从原来的3个cluster切换到6个cluster。有没有类似飞机空中加油的东西?我觉得这是一份又脏又累又容易出问题的工作。为了避免这种情况,我们通常在一开始就设计足够多的分片集群,以防止可能的集群膨胀。MySQL的云计算架构(Clouddatabase)V5.0云计算现在是各大IT企业节省成本的突破口。对于数据存储mysql,如何使其成为saas(SoftwareasaService)是重点。在MS的官方文档中,将构建足够成熟的SAAS面临的三大挑战(MS简单列出了SAAS应用的4级成熟度):可配置性、可扩展性和多用户存储结构设计,称之为“三头怪兽”。可配置性和多用户存储结构的设计在Mysqlsaas中并不是特别困难的事情,所以这里我们重点关注可扩展性。Mysql作为一个saas服务,在架构演进到V4.0之后,依赖于良好的shardingkey设计,不再存在可扩展性问题,但是面对扩容和缩容,它还有一些脏活累活,并且作为saas,无法避免扩容和缩容的问题,所以只要将V4.0的脏工作改成1即可。扩容和缩容对前端APP是透明的(业务代码不不需要任何改动)2.扩容和缩容完全自动化,在线服务对架构实现的关键点没有影响,需要对业务透明,扩容和缩容不需要对业务做任何改动,那么我们必须吃我们自己的狗粮,在你的mysqlsaas里面解决这个问题。一般的做法是我们需要引入AProxy,Proxy来分析sql协议,根据shardingkey找到cluster,判断是读操作还是写操作来请求master或者slave,所有内部细节都是被代理屏蔽。这里附一张淘宝图,列出proxy需要做的事情。架构实现关键点,扩缩容完全自动化,对线上业务无影响;扩容和缩容对应的数据操作是数据拆分和数据合并。有许多不同的实现方法可以实现完全自动化。大体思路与V4.0引入的瓶颈部分有关。目前解决这个问题比较好的办法是实现一个syncslave,伪装成slave,分析mysql同步协议,然后实现dataSplit逻辑,对全量数据进行拆分。具体架构如下图所示:对于OriginalMaster来说,Syncslave和普通的MysqlSlave没有区别,不需要额外的区别对待。当需要扩容/缩容时,挂掉一个Syncslave,开始全量同步+增量同步,等待一段时间赶上数据。以扩容为例,如果扩容后的服务与扩容前的数据基本同步,此时的切换怎么可能不影响业务呢?其实重点还是proxy的引入。这个问题改成了如何使代理热切换终端的问题。这已经成为一个非常容易处理的问题。另一件值得注意的事情是:2014年5月28日——为了满足当前Web和云应用的需求,甲骨文宣布推出MySQLFabric。在相应的资料部分,我也放上了我看了很多关于Fabric的资料。如果你有兴趣,你可以看看。或许会成为未来云数据库扩缩容的解决方案。
