Python + Memcached:分布式应用程序中的高效缓存

devtools/2024/11/17 7:37:32/

Python + Memcached:分布式应用程序中的高效缓存

编写 Python 应用程序时,缓存非常重要。使用缓存可以避免重新计算数据或访问速度缓慢的数据库,从而大幅提高性能。

Python 提供了内置的缓存功能,从简单的字典到更完整的数据结构,例如functools.lru_cache。后者可以使用最近最少使用算法来限制缓存大小,从而缓存任何项目。

但是,根据定义,这些数据结构对于 Python 进程而言是本地的。当应用程序的多个副本在大型平台上运行时,使用内存数据结构不允许共享缓存内容。这对于大型分布式应用程序来说可能是一个问题。

Python + Memcached系统设计图

因此,当系统分布在网络上时,它也需要一个分布在网络上的缓存。如今,有许多网络服务器提供缓存功能 。

正如您将在本教程中看到的,memcached是分布式缓存的另一个不错的选择。在快速介绍 memcached 的基本用法之后,您将了解“缓存和设置”等高级模式,以及如何使用后备缓存来避免冷缓存性能问题。

memcached">安装 memcached

Memcached适用于许多平台:

  • 如果你运行的是Linuxapt-get install memcached ,你可以使用或安装它yum install memcached。这将从预构建的包安装 memcached,但你也可以从源代码构建 memcached,如此处所述。
  • 对于macOS来说,使用Homebrew是最简单的选择。brew install memcached安装 Homebrew 包管理器后,只需运行即可。
  • Windows上,您必须自己编译 memcached 或找到预编译的二进制文件。

安装后,只需调用以下命令即可启动memcached

在从 Python 领域与 memcached 交互之前,您需要安装 memcached客户端库。您将在下一节中了解如何执行此操作以及一些基本的缓存访问操作。

python">使用 Python 存储和检索缓存

如果你从未使用过memcached,那么它很容易理解。它基本上提供了一个巨大的网络可用字典。这个字典有几个不同于传统 Python 字典的属性,主要是:

  • 键和值必须是字节
  • 键和值在过期后会自动删除

因此,与memcached交互的两个基本操作是setget。您可能已经猜到了,它们分别用于为键分配值或从键获取值。

我首选的与memcached交互的 Python 库是pymemcache—我建议使用它。你可以简单地使用 pip 安装它:

$ pip install pymemcache

以下代码展示了如何连接到memcached并将其用作 Python 应用程序中的网络分布式缓存

Python
>>> from pymemcache.client import base# Don't forget to run `memcached' before running this next line:
>>> client = base.Client(('localhost', 11211))# Once the client is instantiated, you can access the cache:
>>> client.set('some_key', 'some value')# Retrieve previously set data again:
>>> client.get('some_key')
'some value'

memcached网络协议非常简单,而且实现速度非常快,这使得它可以用来存储那些从规范数据源检索或再次计算速度很慢的数据:

虽然很简单,但此示例允许跨网络存储键/值元组并通过应用程序的多个分布式运行副本访问它们。这很简单,但功能强大。这是优化应用程序的重要第一步。

自动使缓存数据过期

将数据存储到memcached时,您可以设置过期时间 - memcached保留键和值的最大秒数。在此延迟之后, memcached会自动从其缓存中删除该键。

您应该将此缓存时间设置为多少?此延迟没有神奇的数字,它完全取决于您使用的数据类型和应用程序。它可能是几秒钟,也可能是几个小时。

缓存失效(定义何时删除与当前数据不同步的缓存)也是应用程序必须处理的事情。尤其是当要避免显示太旧或过时的数据时。

再次强调,没有神奇的秘诀;这取决于你正在构建的应用程序类型。但是,有几个异常情况需要处理——我们还没有在上面的例子中涉及。

缓存服务器无法无限增长——内存是有限资源。因此,一旦缓存服务器需要更多空间来存储其他内容,它就会刷新键。

一些密钥也可能过期,因为它们已经到达了过期时间(有时也称为“生存时间”或 TTL)。在这些情况下,数据会丢失,必须再次查询规范数据源。

这听起来比实际要复杂得多。在 Python 中使用 memcached 时,通常可以使用以下模式:

Python
from pymemcache.client import basedef do_some_query():# Replace with actual querying code to a database,# a remote REST API, etc.return 42# Don't forget to run `memcached' before running this code
client = base.Client(('localhost', 11211))
result = client.get('some_key')if result is None:# The cache is empty, need to get the value# from the canonical source:result = do_some_query()# Cache the result for next time:client.set('some_key', result)# Whether we needed to update the cache or not,
# at this point you can work with the data
# stored in the `result` variable:
print(result)

注意:由于正常的刷新操作,必须处理丢失的键。还必须处理冷缓存场景,即memcached刚刚启动时。在这种情况下,缓存将完全为空,需要一次一个请求地完全重新填充缓存

这意味着您应该将任何缓存数据视为短暂的。并且您永远不应该期望缓存中包含您之前写入的值。

预热冷缓存

有些冷缓存场景无法避免,例如memcached崩溃。但有些可以避免,例如迁移到新的memcached服务器。

当可以预测冷缓存情况将会发生时,最好避免这种情况。需要重新填充的缓存意味着,突然之间,所有缺少缓存数据的缓存用户将大量访问缓存数据的规范存储(也称为惊群问题)。

pymemcache提供了一个名为的类FallbackClient,它有助于实现这种场景,如下所示:

Python
from pymemcache.client import base
from pymemcache import fallbackdef do_some_query():# Replace with actual querying code to a database,# a remote REST API, etc.return 42# Set `ignore_exc=True` so it is possible to shut down
# the old cache before removing its usage from 
# the program, if ever necessary.
old_cache = base.Client(('localhost', 11211), ignore_exc=True)
new_cache = base.Client(('localhost', 11212))client = fallback.FallbackClient((new_cache, old_cache))result = client.get('some_key')if result is None:# The cache is empty, need to get the value # from the canonical source:result = do_some_query()# Cache the result for next time:client.set('some_key', result)print(result)

查询FallbackClient传递给其构造函数的旧缓存,并遵守顺序。在这种情况下,将始终首先查询新缓存服务器,如果发生缓存未命中,则将查询旧缓存服务器 — 避免可能返回到主要数据源。

如果设置了任何键,则只会将其设置为新缓存。一段时间后,旧缓存可以退役,并FallbackClient可以直接通过客户端进行替换new_cache

检查并设置

与远程缓存通信时,常见的并发问题又出现了:可能有多个客户端同时尝试访问同一个键。memcached提供了检查和设置操作(缩写为CAS)可帮助解决此问题。

最简单的例子是某个应用程序想要计算其用户数。每次有访问者连接时,计数器就加 1。使用memcached,一个简单的实现如下:

Python
def on_visit(client):result = client.get('visitors')if result is None:result = 1else:result += 1client.set('visitors', result)

但是,如果应用程序的两个实例同时尝试更新该计数器会发生什么情况?

第一次调用client.get('visitors')将返回两个函数相同的访客数量,比如说 42。然后两个函数都会加 1,计算出 43,并将访客数量设置为 43。这个数字是错误的,结果应该是 44,即 42 + 1 + 1。

为了解决这个并发问题, memcached的 CAS 操作很方便。以下代码片段实现了一个正确的解决方案:

Python
def on_visit(client):while True:result, cas = client.gets('visitors')if result is None:result = 1else:result += 1if client.cas('visitors', result, cas):break

gets方法返回值,就像get方法一样,但它还返回一个CAS 值

此值中的内容无关紧要,但它可用于下一次方法cas调用。此方法相当于set操作,但如果操作后值已更改,则该方法会失败gets。如果成功,则循环中断。否则,操作将从头开始重新启动。

在应用程序的两个实例尝试同时更新计数器的情况下,只有一个实例成功将计数器从 42 移动到 43。第二个实例获取False调用返回的值client.cas,并且必须重试循环。它这次将检索 43 作为值,将其增加到 44,并且它的cas调用将成功,从而解决了我们的问题。

增加计数器作为解释 CAS 工作原理的一个有趣示例,因为它很简单。但是,memcached还提供了incrdecr方法来在单个请求中增加或减少整数,而不是进行多次gets/cas调用。在实际应用中gets,和cas用于更复杂的数据类型或操作

大多数远程缓存服务器和数据存储都提供此类机制来防止并发问题。了解这些情况对于正确使用其功能至关重要。

最后

本文介绍的简单技巧向您展示了利用memcached来加速 Python 应用程序的性能是多么容易。

只需使用两个基本的“set”和“get”操作,您通常就可以加速数据检索或避免一遍又一遍地重新计算结果。使用 memcached,您可以在大量分布式节点之间共享缓存

您在本教程中看到的其他更高级的模式,例如检查和设置(CAS)操作,允许您跨多个 Python 线程或进程同时更新存储在缓存中的数据,同时避免数据损坏。


http://www.ppmy.cn/devtools/134650.html

相关文章

NDNF-RNASeq

数据来源:https://ncbi.nlm.nih.gov/geo/query/acc.cgi?accGSE226291 下载数据(3小时) #!/bin/bash for i in 1 2 3 4 5 6 do prefetch SRR2364187${i} donewget https://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_mouse/release_M25/…

UI自动化测试|XPath元素定位实践

前言 自动化测试元素定位是指在自动化测试过程中,通过特定的方法或策略来准确识别和定位页面上的元素,以便对这些元素进行进一步的操作或断言。这些元素可以是文本框、按钮、链接、图片等HTML页面上的任何可见或不可见的组件。 在自动化测试中&#xf…

开源模型应用落地-qwen模型小试-Qwen2.5-7B-Instruct-tool usage入门-串行调用多个tools(三)

一、前言 Qwen-Agent 是一个利用开源语言模型Qwen的工具使用、规划和记忆功能的框架。其模块化设计允许开发人员创建具有特定功能的定制代理,为各种应用程序提供了坚实的基础。同时,开发者可以利用 Qwen-Agent 的原子组件构建智能代理,以理解和响应用户查询。 本篇将介绍如何…

细粒度集群

直觉是 贡献相同频率的标记的日志消息 更有可能有相同的模版, 具体来说,我们首先对每条日志消息进行标记, 然后计算所有标记的评率。 期间上述过程中, 在 Scipy库里 stop 单词是被排除在外以消除不相关的标记。 对于每条日志,选择使用top-K频率标记, 分类到不同的 粗粒度集群。…

速通前端篇 —— HTML

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏:速通前端 目录 HTML的介绍 如何创建HTML文件 HTML 文件基本结构 HTML常用标签 title标签 标题标签 h1-h6 段落标签 p 换行标签 b…

Spring boot + Vue2小项目基本模板

Spring boot Vue2小项目基本模板 基本介绍基本环境安装项目搭建最终效果展示 基本介绍 项目来源哔哩哔哩的青戈,跟着学习搭建自己的简单vue小项目;看别人的项目总觉得看不懂,需要慢慢打磨 这里目前只简单的搭建了菜单导航和表格页面&#x…

vue2和vue3:diff算法的区别?

Vue 2 和 Vue 3 在 diff 算法方面的主要区别是: Vue 2 使用普通的 diff 算法,它会遍历所有的节点进行比对。 Vue 3 引入了 patch flag 的概念,并且对 diff 算法进行了优化,比如在相同层级的节点间不会去递归比对已经被移除的节点…

服务器数据恢复——Ext4文件系统使用fsck后mount不上的数据恢复案例

关于Ext4文件系统的几个概念: 块组:Ext4文件系统的全部空间被划分为若干个块组,每个块组结构基本上相同。 块组描述符表:每个块组都对应一个块组描述符,这些块组描述符统一放在文件系统的前部,称为块组描述…