Python:防止缓存降低我的速度

3 投票
1 回答
511 浏览
提问于 2025-04-17 03:09

我正在开发一个网页应用,使用了非常激进的缓存策略。几乎这个网页应用的每个部分:视图、部分视图、控制器输出、磁盘加载、REST-API调用、数据库查询,所有可以缓存的东西都被缓存了,都是通过装饰器来实现的。

这样做的好处是速度非常快,因为生成HTML的绝大部分都是纯函数,几乎不需要从磁盘或REST API加载数据。而且,我偶尔进行的磁盘加载、数据库查询和REST API查询也会被缓存,直到它们失效,所以除非有什么刚刚发生变化,它们的速度也很快。

总的来说,一切都很快,但有一个问题:所有这些缓存都存储在内存中,存在我的WSGI进程中的一个大字典里,因此可以直接存储而不需要序列化。一旦我开始把东西放进memcached,缓存命中时的速度变化不大,但放东西进缓存的时间却变得很长。一般来说,这没问题,但每个页面的初始“填充缓存”时间从大约900毫秒(考虑到它从磁盘读取了很多文件,这已经很快了)变成了大约9000毫秒。作为参考,缓存预热后,生成一个任意页面大约只需要10毫秒。

通过分析代码,我发现大部分时间都花在了cPickle上。那么问题来了,我该如何让这个过程更快?有没有什么内存缓存可以让我直接传递对象而不需要序列化?或者有没有什么方法可以加快缓存我这堆大对象的速度?我可以选择不使用持久的memcached,但那样我的性能(或者说缺乏性能)就会受到Apache/WSGI进程管理器的影响。

1 个回答

3

如果你在处理Python对象的序列化,而不是简单的数据类型,并且必须使用pickle,试试cPickle.HIGHEST_PROTOCOL:

my_serialized_object = cPickle.dumps(my_object, cPickle.HIGHEST_PROTOCOL)

默认的协议是为了兼容旧版本的Python,但你可能并不在意这些。

我刚做了一个简单的基准测试,用了一个包含1000个键的字典,速度快了将近十倍。

更新:看起来你已经在使用最高协议了,所以要想获得更好的性能,你需要做一些额外的工作。接下来我会这样做:

  1. 找出哪些类在序列化时最慢

  2. 在这些类里创建一对方法,来实现更快的序列化方法,比如 _to_string() 和 _from_string(s)。实际的序列化可以根据对象的内容和使用方式来定制。例如,有些对象可能只包含一个简单的字符串,比如渲染后的模板,而有些可能会作为JSON发送到浏览器,这种情况下你可以直接序列化成JSON并提供给它。使用timeit模块来确保你的方法确实更快

  3. 在你的装饰器里,检查对象是否有'_to_string'这个属性,如果有,就使用它

这种方法让你可以先处理最慢的类,并且对代码的影响最小。

撰写回答