性能调优接口平台服务性能优化

Posted sysu_lluozh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能调优接口平台服务性能优化相关的知识,希望对你有一定的参考价值。

一、问题背景

用户反馈接口平台后台服务接口处理时间慢,导致加载页面或某些操作请求时间特别长,很影响用户体验

可以看到接口请求普遍很慢,POST请求尤其明显

二、代码分析

可以分析对应几个接口的源码,发现大部分操作均是db操作和数据处理,并未发现可疑点

三、cProfiler分析

使用【调优工具】python性能分析工具cProfiler 针对耗时特别长的接口,比如修改接口的这个功能模块进行分析

@do_cprofile('./mkm_modify_iv.prof')
def modify_iv_content(self, project_id, iv_id, iv_data):
    """
    更新接口
    """

生成mkm_run.png分析图:

可以看到90%的时间在数据库连接和操作,接下来分为两个点去优化,一个是数据库的连接池的使用减少数据库连接的耗时,一个是数据库执行sql的优化

四、数据库连接池优化

主要使用【python】DBUtils数据库连接池 的方式创建数据库连接池,但是这里遇到几个问题

4.1 问题一:DBUtils版本

直接pip install DBUtil 下载包,当前最新版本是2.0.2

有个问题,即网上的资源的包的引用基本上是:

from DBUtils.PooledDB import PooledDB

这个其实是1.x版本的方式,在2.x版本中引用方式为

from dbutils.PooledDB import PooledDB

可以查看DBUtils的更新日志

4.2 问题二:Lost connection to mysql server

服务部署后,前端发起批量接口的请求,这时某些conn会报这个错误

pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query ([Errno 104] Connection reset by peer)')

如果服务启动后,单个接口连续请求,并不会引发该错误,然后出现该异常一段时间后连接也是正常

查看连接池的配置

POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=3,
    # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。
    ping=0,
    # ping MySQL服务端,检查是否服务可用。
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123456',
    database='test',
    charset='utf8'
)

故怀疑是否因为连接池初始化时:

  1. 链接池中至少创建的空闲的链接2个,连接不够用导致?
    mincached设置为5、10、甚至50该问题仍然存在

  2. 链接池中至少创建的空闲的链接2个,初始化的连接异常导致?
    mincached设置为0,该问题不再出现(但是并没有定位出这个问题的真正原因)

五、数据库执行sql优化

连接复用的问题解决了,现在还需要继续优化sql语句

5.1 定位执行耗时的sql

在这里使用统计函数运行耗时的装饰器

# 计算函数执行耗时
def time_it(func):
    def call_fun(*args, **kwargs):
        start_time = time.time()
        f = func(*args, **kwargs)
        time_spend = int(1000*(time.time() - start_time))
        if time_spend>100:
            logger.warning('*****************************************%s() run time:%s ms %s' % (func.__name__, time_spend, args))
        return f
    return call_fun

在sql的queryoperate函数中使用该装饰器,耗时超过100ms的sql语句记录下来一一分析

5.2 优化耗时的sql语句

根据EXPLAIN初步分析,执行耗时的sql语句基本上是因为索引的原因

接下来看一个有索引,但是索引未正确创建的栗子

5.2.1 sql语句

sql语句如下:

SELECT project_id FROM interface_view_exec_history WHERE iv_id = 12;

5.2.2 EXPLAIN

EXPLAIN这条sql语句:

EXPLAIN SELECT project_id FROM interface_view_exec_history WHERE iv_id = 12;

执行结果如下:

发现进行了全表扫描

5.2.3 查看索引

查看一下该表的索引:

show index from interface_view_exec_history;

执行结果如下:

5.2.4 联合索引

根据上面的表索引知道,有创建了联合索引username_iv_id,但是由于该联合索引中username的Seq_in_index为1,而iv_id为2,故上面的SELECT执行中未命中该联合索引

根据服务中对该联合索引的使用,将iv_id修改Seq_in_index为1,username为2

此时再次执行EXPLAIN

可以看到,命中索引,扫描行数为1,在正式环境中数据量8w条,sql执行时间从300ms降低为10ms以内

以上是关于性能调优接口平台服务性能优化的主要内容,如果未能解决你的问题,请参考以下文章

性能调优单接口性能调优预备阶段

性能调优单接口性能调优预备阶段

Java 应用性能调优实践

面试必备:深入 Java 应用性能调优实践

面试时这样回答 Java 应用性能调优,回报是更多 Money!

Java 应用性能调优实践