性能优化原则
-
问题导向 不要过早进行优化,避免增加系统复杂度,同时也浪费研发人力
-
遵循二八原则 要抓住主要矛盾,优先优化主要的性能瓶颈
-
优化需要有数据支撑 要时刻了解你的优化让响应时间减少了多少,提升了多少的吞吐量。可以使用平均值、极值(最大/最小值)、分位值等作为统计的特征值
高可用性设计 系统设计 遵循"design for failure"的设计原则,未雨绸缪,具体优化方法有故障转移、超时控制、降级、限流 故障转移
-
对等节点直接可直接转移
-
节点分主节点和备用节点,转移时需要进行主备切换
「什么时候进行主备切换?」 一般采用某种故障检测机制,比如心跳机制,备份节点定期发送心跳包,当多数节点未收到主节点的心跳包,表示主节点故障,需要进行切换。 「如何进行切换?」 一般采用paxos、raft等分布式一致性算法,在多个备份节点中选出新主节点。 超时控制 在分布式环境下,服务响应慢可能比宕机危害更大,失败只是瞬时的,但调用延迟会导致占用的资源得不到释放,在高并发情况下会造成整个系统奔溃。 「如何合理设置超时时间?」 收集系统之间的调用日志,统计比如说 99% 的响应时间是怎样的,然后依据这个时间来指定超时时间。 降级 关闭整个流程中非核心部分,保证主流程能稳定执行(详细见后文) 限流 限制单位时间内的请求量,超过的部分直接返回错误 (详细见后文) 系统运维 灰度发布 通过线上流量观察代码变更带来的影响 故障演练 对系统中的部分节点/组件人为破坏,模拟故障,观察系统的表现。为了避免对生产系统造成影响,可以先部署另外一套与线上环境一摸一样的系统,在这上面进行故障演练 系统可用性度量指标
-
MTBF:两次故障之间的间隔,这个时间越长,系统越稳定。
-
MTTR:故障平均恢复时间,时间越短,故障对用户影响越小 可用性=MTBF / (MTBF+MTTR)
高扩展性设计 存储层 分库分表,按业务和数据纬度对库表进行水平/垂直拆分,突破单机限制。有以下两点需要注意:
-
最好一次性确定好节点/分表数量,避免频繁迁移数据
-
拆分后尽量避免使用事务,分布式事务需要协调各个模块的资源,容易出问题
业务层 按业务纬度,接口重要性纬度和请求来源等多个维度对服务进行拆分和隔离 数据库高可用设计 数据库有两个大方面的优化方向:
-
提升读写性能;
-
增强存储扩展能力,应对大数据量的存储需求
池化技术 池化是一种空间换时间的思路。预先创建好多个对象,重复使用,避免频繁创建销毁对象造成的开销 如何设计一个数据库连接池? 维护池中连接数量和保证连接可用性是连接池管理的两个关键点。 「请求获取连接流程」 初始化连接池时,需要指定最大连接数和最小连接数
-
连接池当前连接数 < 最小连接数: 创建新链接处理数据库请求
-
最小连接数 < 连接池当前连接数 < 最大连接数: 优先复用空闲连接,否则创建新连接处理请求
-
连接池连接数 > 最大连接数: 等待一段时间(自旋/线程休眠),超时还没有连接可以直接抛错
「保证连接可用性」
-
心跳机制,定期检查连接是否可用
-
每次使用连接前,先检验下连接是否可用,再进行SQL请求
如何设计一个线程池? 指定一个最大线程数量,并利用一个有限大小的任务队列,当池中线程数量较少时,直接创建新线程去处理任务,当池中线程达到设置的最大线程数量后,可以将任务放入任务队列中,等待空闲线程执行。
-
合理设置最大线程数量
❝ CPU密集型任务,保持与CPU核数相当的线程就可以了,避免过多的上下文切换,降低执行效率 IO密集型,可以适当放开数量,因为在执行IO时线程阻塞,CPU空闲下来可以去执行其他线程的任务 ❞
-
等待队列必须有界,若不限制大小可能会导致队列任务数量过多,触发Full GC,直接导致服务不可用
-
必须监控等待队列中的任务数,避免最大线程数设置不合理导致大量任务留在等待队列中得不到执行
主从读写分离 分离后,从库可以用作数据备份,也可用于处理读请求,减少单机压力;
-
注意从库数量,从库越多,主库需要越多的资源用于数据复制,同时还占用主库网络带宽,一般最多挂3-5个从库
-
主从之间存在延迟,在某些场景下从库可能读不到最新的数据会导致错误。
❝ 处理方法: 使用缓存,在更新数据后同时更新缓存,读的时候直接读缓存 写主库后发送可以发送完整数据记录到消息队列,避免后面读库操作 需要强一致的读请求直接读主库 ❞
-
需要对主从延迟进行监控
-
最好屏蔽分离后导致访问数据库方式的改变
❝以基础库中间件的方式直接引进项目代码中,访问时直接访问该中间件,主流方案有TDDL、DDB等单独部署数据库代理层,业务代码使用时访问代理层,代理层转发到指定的数据源,有Cobar、Mycat、Atlas、DBProxy等,这种方案多了一次转发,性能上有一些损耗❞ 分库分表 随着存储量变大,单机写入性能和查询性能会降低,分库分表能提高读写性能;按模块分库,实现不同模块的故障隔离 拆分方式
- <