其他分享
首页 > 其他分享> > 高并发系统设计思考笔记

高并发系统设计思考笔记

作者:互联网

一、性能度量的指标

如何衡量系统接口的响应时间?

二、高并发性能优化

要想提高qps(query per seconds),先看示例:假设一次请求任务耗时100ms,一个线程1秒钟能处理10个请求任务。qps=(处理线程数)/(单次任务响应时间ms)/1000。由于单次任务的响应时间是以毫秒计,因此除以1000转化为秒。

  1. 提高处理线程数
    提高处理线程数可以提升 qps,但是一个系统的线程数不可能无限地增加。它受限于阿姆达尔定律(Amdahl’s law)。这可以通过压测方式知道系统的核心线程数到多少时出现性能瓶颈。

  2. 降低单次任务的响应时间
    要完成一次请求执行完所有的业务逻辑的耗时,就是响应时间。而要降低响应时间,这主要靠各种优化,比如:引入缓存、减少系统间的RPC调用、优化代码(减少加锁次数、优化算法)等。这就需要对系统的业务逻辑比较熟悉。

三、高可用

  1. 核心系统一般需要保证4个9的可用性(年故障时间52分钟)。

  2. 发现故障
    业务指标监控打点(业务逻辑失败)、系统指标监控打点(缓存、数据库访问异常)。节点之间则是通过“heartbeat”心跳包检测异常

  3. 处理故障
    对于业务系统而言,一般是无状态的节点,可以无限地扩容。在引入灰度发布和弹性伸缩后,可应对大部分的故障场景。业务系统依赖的底层存储而言,是有状态的节点,当QPS突增时,业务系统机器可借助弹性伸缩无限地扩容,但是当底层存储扛不住时,扩容就比较困难了(可能需要迁移数据、重新部署一套存储……)此时可采取的手段有:限流、降级、调整超时时间的配置。
    对于业务系统之间的 RPC 调用而言,一般会有 thrift 线程池,客户端调用服务端时,会有一个超时时间,若超时时间设置得不合理(比如默认值30秒),当调用的下游服务出现慢查询时,这些慢请求会占用客户端 worker 线程,从而导致调用方没有 worker 线程来处理其他请求了,而如果设置合理的超时时间(比如200ms),那么 200ms 之后请求超时,客户端 worker 线程就释放了,从而能够处理其他请求。
    总结一下:当出现QPS流量突增时,业务系统开始出现一些超时请求的故障了,第一件事就是先扩容。如果扩容达到了底层存储再也扛不住时(默认下游服务能扛住流量。在真实的线上系统中,如果上游系统扩容了能扛住高QPS但是调用的下游服务有可能扛不住高QPS,就需要评估是否保护下游?),这时候就要开始限流、降级了。与此同时,联系DBA申请更多的存储资源。如果扩容未生效,这时候看是否能够适当调大一点超时时间(有风险,因此流量是不断增长的,更大的超时时间可能会占用更多的处理线程,从而导致其他请求无线程可用),需要确保有足够的可用线程且超时时间是合理的。因此,更保险的做法是限流、降级。

  4. failover
    业务系统(无状态)的 failover 就是根据配置的 qps 或者 cpu 使用率等指标自动触发弹性扩容。有状态的系统的 failover 比较复杂,因此有状态的节点一般有 master 和 slave 之分。通过心跳检测到 master 宕机后,需要发起选主,选主需要保证其余节点一致认可 master(需要一致性算法 raft/paxos),然后由新的 master 来负责数据同步并恢复故障。

  5. 路由与负载均衡
    服务之间通过 RPC 调用时,需要选择一台合适的下游机器将请求发送过去。如何选择?
    路由是从下游节点集合中筛选出符合要求的一部分节点;负载均衡是从符合要求的节点中选择出一个节点。通常是先执行路由,再执行负载均衡,路由的输出是负载均衡的输入。
    路由解决:如何从下游选择出一组机器,作为请求候选机器。常见的路由策略有:同机房优先、同地域/城市优先。
    负载均衡解决:从候选集机器中,选择一台,将请求发送过去。常见的负载均衡策略有:按权随机负载(每个机器有个权重,权重高的分配到的请求多,权重一样时,则随机)、RoundRobin负载。

四、读写分离与分库分表

读写分离

访问量变大的情况下,写请求走主库,从请求走读库。

  1. 负载均衡,读能力水平扩展
  2. 避免单点故障
  3. 需要对SQL进行判断,如果是 select 走从库,是 update/insert/delete 走主库
  4. 主从同步延迟
  5. 事务问题,如果一个事务中同时包含了读和写,那么读不从走从库,所有操作都得走主库,避免跨库事务
  6. 高可用性,新增slave节点,需要及时被 client 感知,将读请求发送到此 slave;slave宕机,需要检测出来并隔离,后续读请求转发到其他正常的 slave
  7. master 宕机,需要主从切换,将某个 slave 提升为master,写请求走新的 master

分库分表

数据量变大的情况下,将单表的数据做拆分。

  1. 负载均衡,写能力水平扩展
  2. 单表数据量太大,水平分区(sharding),将一张表的数据分配给N个表维护,有三种分法:只分表、只分库、分库分表。示例如下:
  3. SQL的增删改需要分发到不同的DB上执行。假设有 user 大表,拆分成四张 user 子表:user_1、user_2、user_3、user_4

insert into user(id,name) values (1,"a"),(2,"b"), (3,"c"),(4,"d")

需要改写成:

insert into user_1(id,name) values (1,"a")
insert into user_2(id,name) values (2,"b")
insert into user_3(id,name) values (3,"c")
insert into user_0(id,name) values  (4,"d")

经过分库分表后,原来单表下执行的SQL需要:SQL解析、SQL路由、SQL改写、SQL执行、结果集合并,最终得到执行结果。
4. 分布式事务
分库分表后不可避免地遇到跨库事务的问题,一般使用“柔性事务”来解决。
5. 分布式id
不能再使用mysql的自增主键,需要分布式唯一ID生成器,参考:Snowflake

数据库中间件

  1. 数据库代理
    在DB和应用程序之间单独部署数据库代理。对于应用而言通过一个普通的数据源(c3p0、druid、dbcp等)与代理服务器建立连接,然后由代理服务访问DB

  2. 数据源代理

五、NoSQL 数据库

HBase

淘宝 Tair

标签:负载,请求,笔记,并发,线程,user,思考,超时,节点
来源: https://www.cnblogs.com/hapjin/p/16007599.html