欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

架构师之路总结01 架构师 

程序员文章站 2024-03-16 11:59:04
...

本文转载自:http://www.itdaan.com/blog/2018/04/27/d9d4f6a074ed53401cd73c1dee33d9d9.html

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的记录,访问磁盘的概率会大大降低,数据库访问的时延会大大降低,吞吐量会大大增加。

 

相关标签: 架构师