分布式系统的性能优化方法

旧时模样 提交于 2019-12-28 17:46:38

本文主要记录在分布式系统下优化的常用方法。

找到系统的性能瓶颈

这句话似乎是废话, 但是确实至关重要的。因为, 整个系统的性能瓶颈, 满足木桶效应, 最短的那根木板决定了桶里面能够容纳水的量。类似的, 系统的整体性能或者叫吞吐量, 取决于系统中性能最差的那个模块或者某个部分的代码, 可能是几个接口。
如果我们没有找到正确的性能瓶颈, 盲目的凭借经验或者感觉去定位性能瓶颈点, 很有可能忙乎了好长一阵, 但是没有任何提升, 因为最短板的地方没有任何修复。

定位性能瓶颈的方法
  • pstack
    通常, 可以通过pstack查看正在运行中的进程的callstack, 如果多次都显示在某个位置, 那么很有可能相应的函数有问题, 可以重点分析一下。

  • 借助工具
    不同类型的问题有不同的工具, 需要具体问题具体分析。

    类型 问题 工具
    网络 网络连接 ping, netstat, traceroute, tcpdump
    磁盘io 读写性能 iotop, iostat
    内存 内存泄漏 valgrind, purify
    内存 内存交换 free, top
  • 给系统关键点埋点
    有时候我们从工具无法分析出, 那个模块有性能问题, 或者我们刚刚接受一个新系统, 代码还不熟悉, 无法知道各个模块的具体交互量或者数据流量, 可以顺着main函数, 在各个调用函数的开头和结尾处加上性能埋点, 带引出某个函数执行的时间。
    这样将系统运行起来跑一会儿, 就能看出系统的主要时间花在哪里。

  • 查看系统日志文件
    很多系统本身都带有日志可以查看, 有些还可以设定日志的级别, 这些信息能够帮助我们定位问题。
    如果系统有慢日志查询, 那就更加有帮助了。

性能问题解决

简单的代码优化

这个是在比较新的系统中常出现, 之前知识关注与功能, 在第一次性能优化的时候, 会发现经常很小的改动就能提升很多:

  • SQL语句导致的慢查询;
  • 大对象的频繁的创建, 复制等;
  • 大的集合算法导致的性能问题;
单线程的性能问题

在很多时候, 单线程意味着任务需要一个接一个的执行, 如果能够将任务并行的分配到不同的线程来执行, 并且这些任务没有相关性, 是能够大幅度提升性能的, 这也是在单机系统下最常用的性能优化手段。

由锁引起的性能问题

多线程是提升性能的利器, 但是如果线程的设计不合理的时候, 很可能会造成死锁, 或者性能的严重下降。死锁的问题相对容易发现, 因为一旦死锁, 系统就完全卡住了。由互斥锁引起的性能问题, 不是很容易发现, 因为他只是造成系统间过多的同步或者通知, 并不是太明显。
因此设计多线程系统的时候, 一定要根据具体的业务场景, 设计合理的任务分发机制, 并且尽量减少线程之间的同步, 尽量染一个线程完成整改任务。

模块间的通信造成的性能问题

跨机器的程序一般通过TCP或者HTTP协议来进行通信, 网络通信有很多的因素会影响到传输的效率, 比如,socket的设定, 我们需要尽可能的了解协议本身的机制, 然后根据我们的业务特征去选择不同的通信方式。

数据库引起的性能问题

一般的数据库, 像MySQL, MongoDB, 他们本身的吞吐量是比较低的, 在对相应要求比较高的系统中, 尽可能采用redis-Mysql或者redis-MongoDB的方式来提升性能, 当然, 需要重点关注数据一致性的问题。
如果,数据量没有非常庞大的情况, 也可以考虑redis 集群作为存储, 而不是缓存, 来提升系统性能。
另外, 当前的NoSQL也出现了好多年, 为数据库的存储提供了比较多的选择, NoSQL通常去掉了RDB的很多限制条件, 性能相对于关系数据库普遍高一些。

client-server模式, 尽量提升server端的性能

在client-server模式下, 系统的吞吐量, 很大程度上取决于server端处理的速度。 提升server的处理速度, 是一个比较直观地想法, 通常尽可能的实现异步调用,异步调用的好处就是可以尽可能快的响应客户端,将调用本身的时间变成异步。 但是, 可能的问题就是前段发过来的待处理消息太多, 后端来不及处理, 因此通常需要评估后端的处理能力, 进行必要的流量控制。
还有一种提升后端的处理能力, 就是后端的并发处理能力, 可能是想Nginx那样的启动多个进程, 并发处理不同的请求, 也可以是多线程的模式来并行处理。

缓存方式解决重复的调用

还有一种问题是需要重复地调用来获得一些信息, 比如文件的属性信息, 而这些信息其实变化很小, 大部分时间是相同的, 为了获得实时的数据, 需要一次次的频繁调用, 这种情况下, 可以将属性信息分成可变部分与不可变部分, 我们可以缓存不变部分, 来减少调用的次数。
缓存的使用, 分为本地内存缓存和分布式缓存, 比如redis。 本地缓存,速度快简单, 但是容量有限易丢失。

总之, 性能问题, 需要因地制宜, 问题不同处理的方式也不相同。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!