架构师之路总结01

1. 互联网架构设计如何进行容量评估:

  【步骤一:评估总访问量】 -> 询问业务、产品、运营

  【步骤二:评估平均访问量QPS】-> 除以时间,一天算4w秒

  【步骤三:评估高峰QPS】 -> 根据业务曲线图来

  【步骤四:评估系统、单机极限QPS】 -> 压测很重要

  【步骤五:根据线上冗余度回答两个问题】 -> 估计冗余度与线上冗余度差值

 

2. 单点系统架构的可用性与性能优化

    1)单点系统存在的问题:可用性问题,性能瓶颈问题

    2)shadow-master是一种常见的解决单点系统可用性问题的方案

    3)减少与单点的交互,是存在单点的系统优化的核心方向,常见方法有批量写,客户端缓存

    4)水平扩展也是提升单点系统性能的好方案

 

3. 负载均衡

    负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一,它通常是指,将请求/数据【均匀】分摊到多个操作单元上执行,负载均衡的关键在于【均匀】。

    1)【客户端层】到【反向代理层】的负载均衡,是通过“DNS轮询”实现的

    2)【反向代理层】到【站点层】的负载均衡,是通过“nginx”实现的

    3)【站点层】到【服务层】的负载均衡,是通过“服务连接池”实现的

    4)【数据层】的负载均衡,要考虑“数据的均衡”与“请求的均衡”两个点,常见的方式有“按照范围水平切分”与“hash水平切分”

 

4. 如何实施异构服务器的负载均衡及过载保护?

    1)service的负载均衡、故障转移、超时处理通常是RPC-client连接池层面来实施的

    2)异构服务器负载均衡,最简单的方式是静态权重法,缺点是无法自适应动态调整

    3)动态权重法,可以动态的根据service的处理能力来分配负载,需要有连接池层面的微小改动 (权重值,成功 +1,失败 -10)

    4)过载保护,是在负载过高时,service为了保护自己,保证一定处理能力的一种自救方法()

    5)动态权重法,还可以用做service的过载保护

    动态权重是用来标识每个service的处理能力的一个值,它是RPC-client客户端连接池层面的一个东东。服务端处理超时,客户端RPC-client连接池都能够知道,这里只要实施一些策略,就能够对“疑似过载”的服务器进行降压,

    而不用服务器“抛弃请求”这么粗暴的实施过载保护。

    应该实施一些什么样的策略呢,例如:

    1)如果某一个service的连接上,连续3个请求都超时,即连续-10分三次,客户端就可以认为,服务器慢慢的要处理不过来了,得给这个service缓一小口气,于是设定策略:接下来的若干时间内,

          例如1秒(或者接下来的若干个请求),请求不再分配给这个service;

    2)如果某一个service的动态权重,降为了0(像连续10个请求超时,中间休息了3次还超时),客户端就可以认为,服务器完全处理不过来了,得给这个service喘一大口气,于是设定策略:

          接下来的若干时间内,例如1分钟(为什么是1分钟,根据经验,此时service一般在发生fullGC,差不多1分钟能回过神来),请求不再分配给这个service;

    3)可以有更复杂的保护策略…

    这样的话,不但能借助“动态权重”来实施动态自适应的异构服务器负载均衡,还能在客户端层面更优雅的实施过载保护,在某个下游service快要响应不过来的时候,给其喘息的机会。

    需要注意的是:要防止客户端的过载保护引起service的雪崩,如果“整体负载”已经超过了“service集群”的处理能力,怎么转移请求也是处理不过来的,还得通过抛弃请求来实施自我保护。

    转自:https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959601&idx=1&sn=5684c39676b1f6d9366d9d15a2cdcec3&scene=21#wechat_redirect

 

5. 主从DB与cache一致性

    在“异常时序”或者“读从库”导致脏数据入缓存时,可以用二次异步淘汰的“缓存双淘汰”法来解决缓存与数据库中数据不一致的问题,具体实施至少有三种方案:

    1)timer异步淘汰(本文没有细讲,本质就是起个线程专门异步二次淘汰缓存)

    2)总线异步淘汰

    3)读binlog异步淘汰

 

6. DB主从一致性架构优化4种方法

    为了解决主从数据库读取旧数据的问题,常用的方案有四种:

    1)半同步复制:

          a. 系统先对DB-master进行了一个写操作,写主库

          b. 等主从同步完成,写主库的请求才返回

          c. 读从库,读到最新的数据(如果读请求先完成,写请求后完成,读取到的是“当时”最新的数据)

          方案优点:利用数据库原生功能,比较简单

          方案缺点:主库的写请求时延会增长,吞吐量会降低

    2)强制读主

          如果不使用“增加从库”的方式来增加提升系统的读性能,完全可以读写都落到主库,这样就不会出现不一致了:

          方案优点:“一致性”上不需要进行系统改造

          方案缺点:只能通过cache来提升系统的读性能,这里要进行系统改造

    3)数据库中间件

          如果有了数据库中间件,所有的数据库请求都走中间件,这个主从不一致的问题可以这么解决:

          a. 有的读写都走数据库中间件,通常情况下,写请求路由到主库,读请求路由到从库

          b. 记录所有路由到写库的key,在经验主从同步时间窗口内(假设是500ms),如果有读请求访问中间件,此时有可能从库还是旧数据,就把这个key上的读请求路由到主库

          c. 经验主从同步时间过完后,对应key的读请求继续路由到从库

          方案优点:能保证绝对一致

          方案缺点:数据库中间件的成本比较高

    4)缓存记录写key

          使用缓存,当写请求发生的时候:

           a. 将某个库上的某个key要发生写操作,记录在cache里,并设置“经验主从同步时间”的cache超时时间,例如500ms

           b. 修改数据库

           而读请求发生的时候:

            a. 先到cache里查看,对应库的对应key有没有相关数据

            b. 如果cache hit,有相关数据,说明这个key上刚发生过写操作,此时需要将请求路由到主库读最新的数据

            c. 如果cache miss,说明这个key上近期没有发生过写操作,此时将请求路由到从库,继续读写分离

            方案优点:相对数据库中间件,成本较低

            方案缺点:为了保证“一致性”,引入了一个cache组件,并且读写数据库时都多了一步cache操作

 

7. mysql并行复制降低主从同步延时的思路与启示

    从mysql并行复制缩短主从同步时延的思想可以看到,架构的思路是相同的:

    (1)多线程是一种常见的缩短执行时间的方法

    (2)多线程并发分派任务时必须保证幂等性:mysql的演进思路,提供了“按照库幂等”,“按照commit_id幂等”两种方式,思路大伙可以借鉴

     另,mysql在并行复制上的逐步优化演进:

     mysql5.5 -> 不支持并行复制,对大伙的启示:升级mysql吧

     mysql5.6 -> 按照库并行复制,对大伙的启示:使用“多库”架构吧

     mysql5.7 -> 按照GTID并行复制

 

8. 即使删了全库,保证半小时恢复

    保证数据的安全性是DBA第一要务,需要进行:

  (1)全量备份+增量备份,并定期进行恢复演练,但该方案恢复时间较久,对系统可用性影响大

  (2)1小时延时从,双份1小时延时从能极大加速数据库恢复时间

  (3)个人建议1小时延时从足够,后台只读服务可以连1小时延时从,提高资源利用率

 

9. 啥,又要为表增加一列属性?

    1)方案一:版本号+通用列

    2)方案二:通过扩展行的方式来扩展属性

   

10. 这才是真正的表扩展方案

      1)常见“新表+触发器+迁移数据+rename”方案(pt-online-schema-change),这是业内非常成熟的扩展列的方案

      以user(uid, name, passwd)

      扩展到user(uid, name, passwd, age, sex)为例

      基本原理是:

     (1)先创建一个扩充字段后的新表user_new(uid, name, passwd, age, sex)

     (2)在原表user上创建三个触发器,对原表user进行的所有insert/delete/update操作,都会对新表user_new进行相同的操作

     (3)分批将原表user中的数据insert到新表user_new,直至数据迁移完成

     (4)删掉触发器,把原表移走(默认是drop掉)

     (5)把新表user_new重命名(rename)成原表user

       扩充字段完成。

       优点:整个过程不需要锁表,可以持续对外提供服务

       操作过程中需要注意

     (1)变更过程中,最重要的是冲突的处理,一条原则,以触发器的新数据为准,这就要求被迁移的表必须有主键(这个要求基本都满足)

     (2)变更过程中,写操作需要建立触发器,所以如果原表已经有很多触发器,方案就不行(互联网大数据高并发的在线业务,一般都禁止使用触发器)

     (3)触发器的建立,会影响原表的性能,所以这个操作建议在流量低峰期进行

      pt-online-schema-change是DBA必备的利器,比较成熟,在互联网公司使用广泛。

     2)哪些方案一定是不行

         (1)alter table add column

          要坚持这个方案的,也不多解释了,大数据高并发情况下,一定不可行

        (2)通过增加表的方式扩展,通过外键join来查询

         大数据高并发情况下,join性能较差,一定不可行

        (3)通过增加表的方式扩展,通过视图来对外

         一定不可行。大数据高并发情况下,互联网不怎么使用视图,至少58禁止使用视图

       (4)必须遵循“第x范式”的方案

         一定不可行。互联网的主要矛盾之一是吞吐量,为了保证吞吐量甚至可能牺牲一些事务性和一致性,通过反范式的方式来确保吞吐量的设计是很常见的,例如:冗余数据。互联网的主要矛盾之二是可用性

         为了保证可用性,常见的技术方案也是数据冗余。在互联网数据库架构设计中,第x范式真的没有这么重要

     3)哪些方案可行,但文章未提及

         (1)提前预留一些reserved字段

                  这个是可以的。但如果预留过多,会造成空间浪费,预留过少,不一定达得到扩展效果。

         (2)通过增加表的方式扩展列,上游通过service来屏蔽底层的细节

                  这个也是可以的。Jeff同学提到的UserExt(uid, newCol1, newCol2)就是这样的方案(但join连表和视图是不行的)

           转自:https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959765&idx=1&sn=b9916aa95c320e41035977e0a8098ca6&chksm=bd2d04098a5a8d1f3af38f658c05002151e621170949d2e3bb5b1bceea55c64b0477dba4c647&scene=21#wechat_redirect

 

11. 一分钟掌握数据库垂直拆分

     当一个表属性很多时,如何来进行垂直拆分呢?如果没有特殊情况,拆分依据主要有几点:

     1)将长度较短,访问频率较高的属性尽量放在一个表里,这个表暂且称为主表

     2)将字段较长,访问频率较低的属性尽量放在一个表里,这个表暂且称为扩展表

     如果1和2都满足,还可以考虑第三点:

     3)经常一起访问的属性,也可以放在一个表里

     优先考虑1和2,第3点不是必须。另,如果实在属性过多,主表和扩展表都可以有多个。

     一般来说,数据量并发量比较大时,数据库的上层都会有一个服务层。需要注意的是,当应用方需要同时访问主表和扩展表中的属性时,服务层不要使用join来连表访问,而应该分两次进行查询:

     原因是,大数据高并发互联网场景下,一般来说,吞吐量和扩展性是主要矛盾:

     1)join更消损耗数据库性能

     2)join会让base表和ext表耦合在一起(必须在一个数据库实例上),不利于数据量大时拆分到不同的数据库实例上(机器上)。毕竟减少数据量,提升性能才是垂直拆分的初衷。

     为什么要这么这么拆分

     为何要将字段短,访问频率高的属性放到一个表内?为何这么垂直拆分可以提升性能?因为:

    (1)数据库有自己的内存buffer,会将磁盘上的数据load到内存buffer里(暂且理解为进程内缓存吧)

    (2)内存buffer缓存数据是row为单位

    (3)在内存有限的情况下,在数据库内存buffer里缓存短row,就能缓存更多的数据

    (4)在数据库内存buffer里缓存访问频率高的row,就能提升缓存命中率,减少磁盘的访问

      举个例子就很好理解了:

      假设数据库内存buffer为1G,未拆分的user表1行数据大小为1k,那么只能缓存100w行数据。

      如果垂直拆分成user_base和user_ext,其中:

    (1)user_base访问频率高(例如uid, name, passwd, 以及一些flag等),一行大小为0.1k

    (2)user_ext访问频率低(例如签名, 个人介绍等),一行大小为0.9k

      那边内存buffer就就能缓存近乎1000w行user_base的记录,访问磁盘的概率会大大降低,数据库访问的时延会大大降低,吞吐量会大大增加。

 

posted @ 2018-04-27 17:40  Jtianlin  阅读(533)  评论(0编辑  收藏  举报