当前位置: 首页 > 科技观察

MySQL在大型网站的应用架构演进

时间:2023-03-18 19:43:25 科技观察

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