高性能高并发系统的稳定性保障

wotian 7年前
   <p>本文是2015年肖飞在内部分享的《高性能高并发系统的稳定性保障》PPT内容。</p>    <p><strong>性能、并发、稳定性三者关系</strong></p>    <ul>     <li> <p>高性能:高吞吐量、低延时</p> </li>     <li> <p>公式:吞吐量(并发)=单位时间/平均延时</p> </li>     <li> <p>N-th% Latency:TP99, TP999</p> </li>     <li> <p>稳定性:低延时的稳定性标准为TP99/TP999是隐含的必要条件;系统的稳定性标准:高+可用;用户标准</p> </li>    </ul>    <p>吞吐量:QPS, TPS,OPS等等,并发。并不是越高越好,需要考虑TP99。用户角度:系统是个黑盒,复杂系统中的任何一环到会导致稳定性问题。SLA:在某种吞吐量下能提供TP99为n毫秒的服务能力。降低延时,会提高吞吐量,但是延时的考核是TP99这样的稳定的延时。</p>    <p><strong>如何改善延时</strong></p>    <p>你应该知道如下表格</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f3a313159fce3130851e314573d36ef7.jpg"></p>    <p>JeffDean</p>    <p>Disk random read IOPS:</p>    <p>IOPS = 1000 / (4 + 60000/7200/2)  = 122</p>    <p>IOPS = 1000 / (4 + 60000/10000/2) = 142</p>    <p>IOPS = 1000 / (4 + 60000/15000/2) = 166</p>    <p>SSD random read IOPS:</p>    <p>IOPS = 1000000/16=62500</p>    <p><strong>数字的启示</strong></p>    <ul>     <li> <p>高速缓存的威力;</p> </li>     <li> <p>线程切换代价cache miss</p> </li>     <li> <p>顺序写优于随机写</p> </li>     <li> <p>局域网络快于本地HDD</p> </li>     <li> <p>大块读优于小块读</p> </li>     <li> <p>SSD解决随机读写</p> </li>     <li> <p>跨地域IDC网络是最大的延时</p> </li>    </ul>    <p><strong>策略</strong></p>    <ul>     <li> <p>关键路径: “28原则”(20%的代码影响了80%的性能问题,抓重点)、“过早优化是万恶之源”。不同解读;</p> </li>     <li> <p>优化代码:空间换时间: 各级缓存 ;时间换空间: 比如传输压缩,解决网络传输的瓶颈 ;多核并行: 减少锁竞争 ; lesscode; 各类语言、框架、库的trick; 算法+数据结构,保持代码的清晰、可读、可维护和扩展;</p> </li>     <li> <p>通过性能测试和监控找出瓶颈</p> </li>    </ul>    <p><strong>metric</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/336cb8a52e2bc4d7b813f0d269654ca7.jpg"></p>    <p>通过性能测试和监控:</p>    <ul>     <li> <p>单系统operf/jprofiler etc;</p> </li>     <li> <p>Java的一系列工具:jstat, jstack, jmap, jvisualvm,HeapAnalyzer, mat</p> </li>     <li> <p>分布式跟踪系统:Dapper,鹰眼等</p> </li>    </ul>    <p><strong>benchmark</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/74b1e9c6ff7570bf019b651ad56fec8c.jpg"></p>    <p><strong>微观</strong></p>    <ul>     <li> <p>内存分配</p> </li>    </ul>    <p>吞吐量和利用率的权衡</p>    <p>显式分配器:jemalloc/tcmalloc代替默认的ptmalloc</p>    <p>隐式分配器:JVM GC的各种调优</p>    <p>是否使用hugepagen预分配和重用:Netty的Pooled ByteBuf</p>    <p>减少拷贝:new ArrayList(int), new StringBuilder(int)</p>    <p>内存分配器利用率:减少内部或外部碎片;Page Table(页表), TLB(页表寄存器缓冲),减少TLB miss,pin cache。增加COW的开销, 与内存分配器的实现冲突。JVM的GC调优是很多Java应用的关注重点。</p>    <ul>     <li> <p>减少系统调用</p> </li>    </ul>    <p>批处理: buffer io,pipeline</p>    <p>使用用户态的等价函数: gettimeofday ->clock_gettime</p>    <p>减少锁竞争</p>    <p>RWMutex</p>    <p>CAS</p>    <p>Thread local</p>    <p>最小化锁范围</p>    <p>最小化状态,不变类</p>    <p>批处理增加了内存拷贝的开销,但是减少了系统调用开销,减少了上下文切换的影响。bufferio的例子:日志、网络读写。pipeline的例子:redis。</p>    <ul>     <li> <p>减少上下文切换</p> </li>    </ul>    <p>触发:中断、系统调用、时间片耗尽、IO阻塞等</p>    <p>危害:L1/L2 Cache Missing,上下文保存/恢复</p>    <p>单线程:基于状态机redis和Master/Worker的nginx</p>    <p>CPU亲和性绑定</p>    <p>ThreadPool的配置,不同任务类型不同的ThreadPool</p>    <p>几个例子:1、docker中线程池大小的核数自动设定;2、CPU节能模式;3、CENTOS-7.1内核BUG。</p>    <ul>     <li> <p>网络</p> </li>    </ul>    <p>内核TCP Tuning参数和SocketOption:net.ipv4.tcp_*</p>    <p>TCP Socket连接池</p>    <p>网络I/O模型</p>    <p>传输压缩</p>    <p>编解码效率</p>    <p>超时、心跳和重试机制</p>    <p>网卡:多队列中断CPU绑定;增加带宽:万兆、Bonding;Offload特性:ethtool -k eth0;UIO Driver: DPDK</p>    <p>连接池:减少握手、减少服务端session创建消耗。网络I/O模型:BIO、Non-Blocking IO、AIO;select/poll、epoll/kqueue、aio;netty使用nativetransport。Offload特性:ethtool-k eth0。   将数据包分组、重组、chksum等从内核层放到硬件层做。</p>    <p><strong>如何提高吞吐量</strong></p>    <p>改善和降低单机的延时,一般就能提高我们的吞吐量。从集群化上讲,因素就比较多。</p>    <p><strong>宏观</strong></p>    <ul>     <li> <p>提升系统扩展能力</p> </li>     <li> <p>应用的无状态架构</p> </li>     <li> <p>缓存/存储的集群架构:冗余复制(负载均衡、异构解除系统依赖 ); 分布式(数据sharding , 副本,路由,数据一致性 ) ;切换</p> </li>     <li> <p>微服务/SOA</p> </li>     <li> <p>扩容</p> </li>     <li> <p>异步化</p> </li>     <li> <p>缓存</p> </li>    </ul>    <p><strong>复制</strong></p>    <ul>     <li> <p>通过复制提高读吞吐量、容灾、异构</p> </li>     <li> <p>通过数据分片,提高写吞吐量</p> </li>     <li> <p>程序双写:一致性难以控制,逻辑复杂,幂等性要求。完全把控复制和切换时机。异构系统唯一选择。 同步双写(数据一致性高,影响性能,不适合多个复制集 ); 异步双写(数据一致性差,性能高,适合多个复制集 ); CDC[Change Data Capture](canal,databus等 )</p> </li>     <li> <p>底层存储复制机制:一致性由底层控制,对应用端透明。程序和底层存储配合切换</p> </li>    </ul>    <p><strong>扩容</strong></p>    <ul>     <li> <p>每年大促前的核心工作:该扩容了吗?现状分析;扩容规划(关键系统峰值20倍吞吐量);扩容依据(架构梳理、线上压测 ) ;</p> </li>     <li> <p>扩容checklist:前(部署、DB授权.... ); 后(配置更新、LB更新、接入日志、接入监控.... )</p> </li>     <li> <p>应用扩容、数据扩容、写扩容、读扩容</p> </li>     <li> <p>垂直扩容:加内存、升级SSD、更换硬件。数据复制、切换</p> </li>     <li> <p>水平扩容:数据迁移或初始化</p> </li>    </ul>    <p>现状分析:去年双十一到目前,峰值时的性能数据;软硬件性能指标;数据存储容量。</p>    <p>扩容规划;流量规划:核心系统20倍吞吐量 ; 数据增长量规划;扩容依据;架构梳理;线上压测。</p>    <p>读扩容比写扩容难;读写分离。</p>    <p><strong>异步化</strong></p>    <ul>     <li> <p>解耦利器</p> </li>     <li> <p>削峰填谷</p> </li>     <li> <p>页面异步化</p> </li>     <li> <p>系统异步化</p> </li>     <li> <p>JMQ</p> </li>     <li> <p>状态机(worker)+DB</p> </li>     <li> <p>本地队列</p> </li>     <li> <p>集中式缓存队列</p> </li>    </ul>    <p>本地内存队列:实时价格回源服务响应之后,通过BlockingQueue异步更新前端缓存。本地日志队列:库存预占。集中式缓存队列:商品变更任务下发系统。</p>    <p>异步化的一些例子:</p>    <p>1、操作系统内核的高速缓存队列,磁盘延迟刷盘;</p>    <p>2、mysql数据库复制、redis复制;</p>    <p>异步化需要注意的是:</p>    <p>1、任务要落地;</p>    <p>2、不可避免的重复执行,需要幂等;</p>    <p>3、是否需要保证顺序、如何保证顺序。</p>    <p><strong>缓存</strong></p>    <ul>     <li> <p>久经考验的局部性原理</p> </li>     <li> <p>多级缓存:浏览器browser cache、cdn、nginx本地redis缓存、本地JVM缓存、集中式缓存...</p> </li>     <li> <p>缓存前置:2/8原则、单品页、实时价格、库存状态</p> </li>     <li> <p>一致性、延迟权衡</p> </li>     <li> <p>缓存主节点负责写,和最重要的校验</p> </li>     <li> <p>通过CDC监听数据库binlog主动更新缓存</p> </li>     <li> <p>CPU不是瓶颈,网络才是</p> </li>     <li> <p>优化编码,减少尺寸</p> </li>     <li> <p>优化操作</p> </li>     <li> <p>优化拓扑</p> </li>    </ul>    <p><strong>如何保障稳定性</strong></p>    <p><strong>宏观</strong></p>    <ul>     <li> <p>提高可用性</p> </li>     <li> <p>分组和隔离</p> </li>     <li> <p>限流</p> </li>     <li> <p>降级</p> </li>     <li> <p>监控和故障切换</p> </li>    </ul>    <p><strong>可用性</strong></p>    <ul>     <li> <p>可用性衡量指标:几个9</p> </li>     <li> <p>可用性度量:A = MTBF / (MTBF + MTTR)</p> </li>     <li> <p>减少故障、加长可用时间</p> </li>     <li> <p>减少故障修复时间(发现、定位、解决)</p> </li>     <li> <p>冗余复制、灾备切换,高可用的不二法门</p> </li>     <li> <p>如何快速切换?</p> </li>     <li> <p>切换的影响</p> </li>     <li> <p>监控、ThoubleShooting、软件质量的影响</p> </li>    </ul>    <p>可行性指标:999,一周10分钟;9999,一周1分钟不可用。可用性:从客户角度。可用性度量:A = MTBF / (MTBF + MTTR) ,其中MTBF表示mean time betweenfailures,而MTTR表示maximum time to repair or resolve。</p>    <p>高可用行性的成本和收益,好钢用在刀刃上。</p>    <p>如何快速切换:有可以切换的?可以不重启应用么? 操作快捷么?演练过么?</p>    <p>切换的影响:切换目标资源能否承受新增的压力;切换是否影响状态(数据的一致性、丢失问题)。</p>    <p>监控到位、即时,减少故障发现时间;监控全面,增加故障分析时可以参考的数据。</p>    <p>troubleshooting的能力,踩坑的精力, COE,问题本质、根源的追查。</p>    <p>软件质量:编码是否健壮、(异常处理、防御性、2/8原则)超时处理、日志是否全面合理、线程名称等等。</p>    <p>测试:case是否全面、自动回归。</p>    <p>上线:是否灰度:N+1, N+2;回滚方案、数据回滚。</p>    <p><strong>分组和隔离</strong></p>    <ul>     <li> <p>网络流量隔离:大数据单独部署,QOS;</p> </li>     <li> <p>业务系统隔离:秒杀系统独立出主交易;</p> </li>     <li> <p>流量分组:对使用者按照重要程度、请求量、SLA要求等因素分级</p> </li>     <li> <p>存储的分组:按照使用者重要程度、实时性要求等因素,将数据库的复制集分组</p> </li>    </ul>    <p>传统世界的例子:道路被划分为高速道路、自行道、人行道等,各行其道。</p>    <p>流量分组</p>    <p>举例:商品基础信息读服务。对使用者按照重要程度、请求量、SLA要求等因素分级,将服务实例和存储分组:交易、生产、网站、移动、promise、ERP...</p>    <p>读写分离</p>    <p>举例:商品主数据服务。按照使用者重要程度、实时性要求等因素,将数据库分组:ERP、POP、网站、大数据平台...</p>    <p><strong>限流</strong></p>    <ul>     <li> <p>限流原则:影响到用户体验,谨慎使用</p> </li>     <li> <p>区分正常流量和超预期流量:限流标准来自压力测试、折算</p> </li>     <li> <p>读少限,写多限</p> </li>     <li> <p>客户端配合限流</p> </li>     <li> <p>不同分组的限流阈值</p> </li>     <li> <p>各层限流手段</p> </li>    </ul>    <p>前置限流,快速失败:比如通过提供给调用方的JSF客户端,封装限流逻辑。</p>    <p>Nginx层限流:自主研发的模块;几个规则:账户,IP,系统调用流程。</p>    <p>应用限流:减少并发数线程数;读少限,写多限;DB限流;连接数。</p>    <p><strong>降级</strong></p>    <ul>     <li> <p>保证用户的核心需求</p> </li>     <li> <p>降级需要有预案和开关:确定系统和功能级别,是否可降,影响如何;降级需要有开关</p> </li>     <li> <p>非关键业务屏蔽:购物车的库存状态</p> </li>     <li> <p>业务功能模块降级:实时价格更新不及时;peking库,保订单管道、生产,暂停统计相关</p> </li>     <li> <p>数据降级:动态降级到静态;远程服务降级到本地缓存:采销岗服务</p> </li>    </ul>    <p><strong>监控和切换</strong></p>    <ul>     <li> <p>无所不在的监控:网络流量;操作系统指标;服务接口调用量、TP99、错误率...;日志;业务量变化;太多监控了,如何提高监控的质量</p> </li>     <li> <p>切换:切换开关;成熟的流程可自动化;数据的重要性、一致性,要求强一致的,可以人工介入;系统的指标没法判断、监控点不全的,需人工判断决定</p> </li>    </ul>    <p><strong>review</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f7796d166e8639548e0f915a1b33f434.png"></p>    <p>Nginx层限流:自主研发的模块;几个规则:账户,IP,系统调用流程。</p>    <p>应用限流:减少并发数线程数;读少限,写多限;DB限流;连接数。</p>    <p><strong>如何验证性能和稳定性</strong></p>    <ul>     <li> <p>线上压测:两类压力测试场景(读业务压测、写业务压测 ) ;压力测试方案(从集群中缩减服务器、复制流量、模拟流量、憋单)</p> </li>     <li> <p>全流程演练:降级、切换等</p> </li>    </ul>    <p>读业务压力测试:是将线上业务隔离后,压测至系统临界点,通过分析系统在临界点时软硬件指标定位系统短板并优化。</p>    <p>写逻辑压力测试,如果数据具有不可恢复性,一定要提前做好数据隔离保护,如订单号压测,为避免影响线上业务,压测前后都要做好“跳号”以隔离线上数据。</p>    <p>从集群中缩减服务器。加大单台服务器的压力。大概估算出正常的集群规模能够承载的流量。</p>    <p>复制流量。主要通过 Tcpcopy 复制端口流量,多层翻倍放大流。</p>    <p>模拟流量。模拟流量主要脚本攻击工具和压测工具结合,主要用ab,siege,webbench,loadruner通过多台机器压测。分机房,按分支进行压测。</p>    <p>憋单。主要针对后续的订单生产系统压测。通过在管道积压一批订单,然后快速释放,形成对后续生产系统持续快速的冲击,达到压测的目的。</p>    <p> </p>    <p>来自:https://mp.weixin.qq.com/s/YMgIwaz8YC_zNPh_Jf98HA</p>    <p> </p>