Python + Memcached: 在分布式应用程序中实现高效缓存
2018-03-05 14:27
453 查看
作者 Julien Danjou中文翻译:首席IT民工原文地址:https://realpython.com/blog/python/python-memcache-efficient-caching译者:首席IT民工在开发Python程序时,实现缓存是重要的一环。缓存技术能够在很大程度上提升性能,从而避免数据的重复计算,或是数据库访问过慢的问题。Python内置了实现缓存的技术,包括简单的字典和诸如functools.lru_cache之类更加完整的数据结构。后者可以利用“最近最少使用”算法限制缓存的大小,做到任意缓存。然而,依照定义,这些数据结构仅限于Python进程内部使用。这可能对大规模分布式应用导致问题,因为当你的程序的多个副本在大规模平台上运行时,使用这种本地内存中的数据结构将不允许对所缓存的内容进行共享。
因此,如果你的系统是基于分布式网络的,就需要缓存也基于分布式网络。现如今大量的网络服务器提供了缓存能力–我们在how to use Redis for caching with Django (如何使用Redis实现Django缓存)一文中已经提到过。在这篇教程中你将看到,memcached是实现分布式缓存的一个很好的选择。首先是对基本的memcached使用方法的简介,然后你会学习到一些高级方法,比如检查和设置(cache and set),以及使用后备缓存以避免传统的缓存性能问题。
对于macOS,使用Homebrew安装是最简单的。只要有Homebrew包管理器,直接运行 brew install memcached
对于Windows, 你需要自行编译memcached,或者寻找预编译的版本
安装完毕后,运行memecached命令即可启动它:$ memcached在正式使用memcached的功能之前,你还需要安装一个memcached的客户端库。下面介绍安装的方法,以及基本的缓存访问操作。
到了过期时间后,键和值将被自动删除
因此,与memcached交互的两个基本操作是:set和get。正如你可能猜到了,他们分别表示给某个键赋值,和从某个键取值。我个人偏爱的与memcached交互的Python库是 pymemcache,推荐使用。它用pip就可以安装:$ pip install pymemcache以下代码显示了如何连接memcached和如何在你的Python程序中将它用作网络分布式缓存:
memcached网络协议十分简单,其实现运行起来也非常快,使得它在数据存储上很管用。换作其他技术,这些数据要么访问起来很慢,要么需要重新计算。这个例子简单明了,却演示了键值的跨网络存储,以及程序的多个分布式副本如何来访问键值。很简单,却很强大。这是很棒的迈向程序优化的第一步。
注:由于存在清理操作,处理丢失的键是一定要做的。此外,像memcached刚刚启动这种冷缓存的情况也必须进行处理。在这种情况下,缓存是全空状态,需要一次载入一个请求,直至充分填满。这意味着说,任何缓存的数据都应视作是临时性的。而且,千万不要期望缓存中包含你先前写入的值。
FallbackClient类能够依照顺序,查询传递给构造函数的老的缓存。这种情况下,总是优先查询新的缓存服务器。如果未命中,则查询老的缓存,从而避免来回查询主数据源的可能。键会被设置到新的缓存中。一段时间后,可以淘汰老的缓存,然后用new_cache客户端直接替换FallbackClient
然而,如果这个程序的两个实例尝试同时更新这个计数器,情况会怎样呢?第一个client.get(‘visitors’)调用会返回相同的用户数,比方说是42。两个实例同时加一,结果是43,但是43是错的,正确的结果应该是42+1+1 = 44。通过CAS操作解决并发问题,就很方便。以下的片段实现了一个正确的方案。
类似于get方法,gets方法既返回键值,又返回CAS值。值的内容是无关的,但在下一次cas方法的调用中会用得着。除了值的内容自gets操作后发生了变化就会导致运行失败,这个方法等同于set操作。运行成功时,循环是中断的,否则操作会从头开始。当程序的两个实例尝试同时更新计数器时,只有一个实例可以成功地将计数器从42加到43。另一个实例会由client.cas调用返回一个“假”值,只能再次走一遍循环。这次它会得到43,加1得到44,且cas调用会成功完成,从而解决了我们的问题增加计数器这个例子的有趣之处在于,简单地解释了CAS的工作原理。其实,memcached其实提供了incr和decr方法,可以在单个请求中递增或递减,而无需多次调用gets / cas 。 现实中的程序中使用gets/cas来实现更加复杂的数据类型或者操作。大多数的远程缓存服务器和数据存储都提供了类似的方法,以防止出现并发性问题。了解和合理的使用这些方法至关重要。
因此,如果你的系统是基于分布式网络的,就需要缓存也基于分布式网络。现如今大量的网络服务器提供了缓存能力–我们在how to use Redis for caching with Django (如何使用Redis实现Django缓存)一文中已经提到过。在这篇教程中你将看到,memcached是实现分布式缓存的一个很好的选择。首先是对基本的memcached使用方法的简介,然后你会学习到一些高级方法,比如检查和设置(cache and set),以及使用后备缓存以避免传统的缓存性能问题。
安装memcached
Memcached提供了[很多平台的版本](https://github.com/memcached/memcached/wiki/Install) :如果你运行的是Linux,安装命令是 apt-get install memcached 或 yum install memcached 你既可以通过预构造的安装包安装,也可以从源文件安装对于macOS,使用Homebrew安装是最简单的。只要有Homebrew包管理器,直接运行 brew install memcached
对于Windows, 你需要自行编译memcached,或者寻找预编译的版本
安装完毕后,运行memecached命令即可启动它:$ memcached在正式使用memcached的功能之前,你还需要安装一个memcached的客户端库。下面介绍安装的方法,以及基本的缓存访问操作。
使用Python存储和检索缓存值
即便你从未使用过memcached,也很容易理解。基本来说,memcached提供了一个超大的网络字典。这个字典的几个属性不同于经典的Python字典:键和值都必须是bytes(字节)到了过期时间后,键和值将被自动删除
因此,与memcached交互的两个基本操作是:set和get。正如你可能猜到了,他们分别表示给某个键赋值,和从某个键取值。我个人偏爱的与memcached交互的Python库是 pymemcache,推荐使用。它用pip就可以安装:$ pip install pymemcache以下代码显示了如何连接memcached和如何在你的Python程序中将它用作网络分布式缓存:
memcached网络协议十分简单,其实现运行起来也非常快,使得它在数据存储上很管用。换作其他技术,这些数据要么访问起来很慢,要么需要重新计算。这个例子简单明了,却演示了键值的跨网络存储,以及程序的多个分布式副本如何来访问键值。很简单,却很强大。这是很棒的迈向程序优化的第一步。
缓存数据的自动过期
在memcached中存储数据时,你可以设置过期时间,即mecached保存键和值的最长时间,单位是秒。到期以后,memcached会自动从缓存中删除他们。过期时间应该设多长呢?没有一个直接的答案,过期时间完全取决于你处理的数据和程序的类型。可能是几秒钟,也可能是几个小时。“缓存失效”定义了当缓存中的数据与当前数据不一致时,什么时候删除缓存数据,这也是你的程序必须处理的问题,尤其是在不得不呈现已经过时或过于陈旧的数据的情况下。重申一下,没有直接的答案;过期时间取决于你构造的程序类型。不过,有几类外围情况需要处理,我在上面的例子中尚未涉及。缓存服务器不能无限增长–内存是有限资源。因此,服务器一旦需要更多的空间来存储其他东西,就会立即清空已有的键。有些键由于到了过期时间(有时叫做生存周期,或TTL),会变无效。在这些情况下,数据会丢失,并且必须再次重新从标准数据源查询。听上去比实际情况更加复杂。通常来说,你在Python中使用memcached可以遵循以下方式:注:由于存在清理操作,处理丢失的键是一定要做的。此外,像memcached刚刚启动这种冷缓存的情况也必须进行处理。在这种情况下,缓存是全空状态,需要一次载入一个请求,直至充分填满。这意味着说,任何缓存的数据都应视作是临时性的。而且,千万不要期望缓存中包含你先前写入的值。
冷缓存的预热
有些冷缓存的情况是无法避免的,比如memcached发生了崩溃。而有些冷缓存情况,比如迁移到一个新的memcached服务器,是可以避免的。当你预见会发生冷缓存的情况时,最好避开它。一个需要重新填满的缓存。意味着突然之间,所有缺少缓存数据的用户会大量地访问标准数据源。这种现象也称作“惊群效应”。pymemcache库提供了一个名为FallbackClient的类来实现这种场景,如下所述:FallbackClient类能够依照顺序,查询传递给构造函数的老的缓存。这种情况下,总是优先查询新的缓存服务器。如果未命中,则查询老的缓存,从而避免来回查询主数据源的可能。键会被设置到新的缓存中。一段时间后,可以淘汰老的缓存,然后用new_cache客户端直接替换FallbackClient
检查和设置
在与缓存进行远程通信时,可能发生常见的并发性问题,即有多个客户端尝试同时访问同一个键。Memcached提供了一个叫做检查和设置(check and set)的操作,缩写CAS,来解决这一问题。最简单的例子比如,有个程序希望统计它的用户数。每当有用户连接时,计数器加一。用memcached,简单实现起来是这样子的:然而,如果这个程序的两个实例尝试同时更新这个计数器,情况会怎样呢?第一个client.get(‘visitors’)调用会返回相同的用户数,比方说是42。两个实例同时加一,结果是43,但是43是错的,正确的结果应该是42+1+1 = 44。通过CAS操作解决并发问题,就很方便。以下的片段实现了一个正确的方案。
类似于get方法,gets方法既返回键值,又返回CAS值。值的内容是无关的,但在下一次cas方法的调用中会用得着。除了值的内容自gets操作后发生了变化就会导致运行失败,这个方法等同于set操作。运行成功时,循环是中断的,否则操作会从头开始。当程序的两个实例尝试同时更新计数器时,只有一个实例可以成功地将计数器从42加到43。另一个实例会由client.cas调用返回一个“假”值,只能再次走一遍循环。这次它会得到43,加1得到44,且cas调用会成功完成,从而解决了我们的问题增加计数器这个例子的有趣之处在于,简单地解释了CAS的工作原理。其实,memcached其实提供了incr和decr方法,可以在单个请求中递增或递减,而无需多次调用gets / cas 。 现实中的程序中使用gets/cas来实现更加复杂的数据类型或者操作。大多数的远程缓存服务器和数据存储都提供了类似的方法,以防止出现并发性问题。了解和合理的使用这些方法至关重要。
更多缓存知识
这篇教程演示了使用memcached来提高Python程序的性能是多么的容易。只要执行两个基本的”set”和”get”操作,你就可以加速数据检索,或者避免反复的计算结果。有了memcache,你可以在大量的分布式节点间实现缓存的共享。另外,类似CAS操作更高级的方法允许你跨多个Python线程/进程并发地更新缓存中的数据,同时不破坏这些数据。如果你有兴趣学习更多的高级技巧,写出更快、扩展性更好的Python程序,可以访问Scaling Python。它包含了许多高级主题,比如网络分布,排队系统,分布式哈希算法,和代码性能分析。相关文章推荐
- WSWP(用 python写爬虫) 笔记四:实现缓存功能
- 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写
- 实现自己的O/R Mapping组件-高效缓存的思考
- 【原创】python实现清理本地缓存垃圾
- Python实现高效求解素数代码实例
- Python 按行读取文本文件 缓存 和 非缓存实现
- 如何高效使用和管理Bitmap--图片缓存管理模块的设计与实现
- python用装饰器实现缓存函数执行结果
- python高效编程技巧13(如何在线程之间实现事件通知)
- Python 按行读取文本文件 缓存 和 非缓存实现
- 如何高效使用和管理Bitmap--图片缓存管理模块的设计与实现
- Python&Auto.js:实现蚂蚁森林自动收能量(懒人的高效生活)
- [置顶] 如何高效使用和管理Bitmap--图片缓存管理模块的设计与实现
- 8种方法用Python实现线性回归,为你解析最高效选择
- 转 如何高效使用和管理Bitmap--图片缓存管理模块的设计与实现
- Python之——实现高效的端口扫描
- php动态内容文件缓存的设计和高效实现
- [Python高效编程] - 实现用户历史记录功能
- Python实现以时间换空间的缓存替换算法
- python 斐波那契数列缓存方式实现