1.基本概念2.数据库架构设计思路(1)可用性(2)读取性能(3)一致性(4)可扩展性1.基本概念概念1“单库”概念2“分片””分片解决了“数据太多”的问题,也就是通常所说的“水平分片”。一旦引入分片,就必须有一个“数据路由”的概念,哪些数据访问哪个库。路由规则通常有3种方式:(1)Range:range优点:简单,易扩展缺点:各数据库压力不均(新的号段比较活跃)(2)Hash:散列优点:简单,数据均衡,和负载均匀性缺点:迁移麻烦(数据从2个数据库迁移到3个数据库)(3)路由服务:router-config-server优点:灵活性强,业务和路由算法解耦缺点:每次访问前查询大部分互联网公司采用的数据库解决方案2:哈希分库,哈希路由概念3“分组”分组解决“可用性”问题,分组通常通过主从复制来实现。互联网公司数据库的实际软件架构是:分片和分组(如下图所示)二.数据库架构设计思路数据库软件架构师通常设计什么?至少应该考虑以下四点:(1)如何保证数据的可用性(2)如何提高数据库的读性能(大部分应用读多写少,读首先会成为瓶颈)(3)如何保证一致性(四)如何提高可扩展性2.1如何保证数据的可用性?解决可用性问题的思路是=>冗余如何保证站点的可用性?复制站点,冗余站点如何保证服务的可用性?复制服务,冗余服务如何保证数据的可用性?重复数据,冗余数据数据冗余会带来一个副作用=>导致一致性问题(先不说一致性问题,先说可用性)如何保证数据库“读”的高可用?冗余阅读库冗余阅读库的副作用?读写有延迟,可能不一致。上图是很多互联网公司的mysql架构。写入还是单点,高可用无法保证。如何保证数据库“写”的高可用?冗余读库采用双主互备的方式,可以避免冗余写库带来的副作用吗?双写同步,数据可能会冲突(比如“自增id”同步冲突),如何解决同步冲突,常见的解决方法有两种:(1)两个写库使用不同的初值,相同的步长增加id:1写库的id为0,2,4,6...;2写库的id为1,3,5,7...(2)数据的id没有使用,业务层自己生成一个唯一的id,保证数据不冲突。58同城并没有使用以上两种架构进行读写。”,58同程采用“双主为主从”的方式:依然是双主,但只有一个主提供服务(读+写),另一个主是“影子主”,仅供使用保证高可用,一般不提供服务。master挂掉,shadow-master在上面(vip漂移,对业务层透明,不需要人工干预)。这种方式的好处:1)有是读写没有延迟2)读写的高可用不足:1)不能增加库的读性能扩展方式2)资源利用率50%,一个冗余的master不提供服务,那么如何提高读性能呢?进入第二个话题,如何提供读性能。2.2如何扩展读性能?提高读性能的方法大致有3种,第一种是建立索引,这种方法做不展开。需要说明的是不同的库可以创建不同的索引。写数据库不创建索引;在线读取数据库建立在线访问索引,如uid;离线读取数据库建立离线访问索引,例如时间;第二种扩展读性能的方法是增加从库。用的很多,但是有两个缺点:(1)从库越多,同步越慢(2)同步越慢,数据不一致的窗口越大(不一致后面会讲到,先说先提高读性能)58同城这个方法不是用来提高数据库读性能的(没有从库),而是增加缓存。常见的缓存架构如下:上游是业务应用,下游是主库、从库(读写分离)、缓存。58同城的玩法是:服务+数据库+缓存。业务层不直接面对db和cache,service层屏蔽了底层db和cache的复杂性。为什么要引入服务层?我们今天不会展开它。58采用“服务+数据库+缓存”的方式提供数据访问,利用缓存提高读取性能。不管是用master-slave的方式来扩展读性能,还是用cache的方式来扩展读性能,数据都要复制多份(master+slave,db+cache),肯定会造成一致性问题问题。2.3如何保证一致性?主从数据库的一致性通常有两种解决方案:(1)如果中间件对某个key有写操作,中间件也会在不一致的时间窗口内将这个key的读操作路由到主库.这种方案的缺点是数据库中间件门槛高(百度、腾讯、阿里、360等公司都有,当然58也有)。不存在主从不一致的问题。第二种不一致是db和cache的不一致。常见的缓存架构如上。此时写操作的顺序是:(1)清除缓存(2)写库读操作的顺序是:(1)读缓存,如果缓存命中返回(2)如果缓存未命中,读从库(3)读取从库后,将数据放回缓存中在一些异常的时序情况下,有可能从[从库(尚未同步完成)]中读取旧数据,旧数据进入后cache],数据会长期不一致。解决方案是“缓存双淘汰”,写入操作顺序升级为:(1)淘汰缓存(2)写入数据库(3)在经历“主从同步延迟窗口时间”后,发起异步像这样再次进行缓存淘汰请求,即使有缓存等脏数据,经过一个小的时间窗口后,脏数据仍然会被淘汰。代价是多引入了一次readmiss(代价可以忽略)。另外,58同城的一个最佳实践是:建议为缓存中的所有条目设置一个超时时间。说完一致性,最后一个话题就是可扩展性。2.4如何提高数据库的可扩展性?它曾经是通过哈希路由并分为2个数据库。数据量还是太大了。如果分成3个数据库,势必需要进行数据迁移。58同城有个很帅的“数据库二级扩容”方案。如何秒扩容?首先,我们不是从2库扩容到3库,我们是从2库扩容到4库(翻倍数据库)(未来4->8->16)服务+数据库是一个集合(保存缓存)数据库采用“双主”模式。扩容步骤:第一步,升级一个主库到第二步,修改配置,将配置从2改为4(原来的MOD2,现在配置修改为MOD4),将原来的MOD2扩容到偶数部分,现在MOD4中会多出0个或0个2原来MOD2是奇数部分,现在MOD4剩下的1个或3个数据不用迁移了。同时双主同步,一次留0,一边留2,两边数据同步不会冲突,秒扩容完成!***,做一些收尾工作:(1)去掉老双主的同步(2)增加一个新的双主(双主是为了保证可用性,shadow-master一般不提供服务)(3)删除冗余数据(对于剩余0个的master,可以删除剩余2个数据)这样,几秒钟之内,我们就完成了从2个数据库扩容到4个数据库。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】
