Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。
处理失效
无效消息如何发送到被追踪的客户端取决于客户端正在使用的Redis序列化协议(RESP)。早期版本的Redis使用RESP2,但是它的后续版本RESP3已经存在于Redis 6中,并且Redis 7将完全不支持旧协议。
RESP3包含许多新功能,包括服务器在客户端现有连接上“推送”除了实际回复内容的附加信息。此通道用于在使用服务器辅助的客户端缓存功能时传递无效通知。
然而,由于RESP3比较新,目前只有少数客户端支持它,因此RSACSC也可以与RESP2一起工作。由于RESP2缺乏“推送”能力,RSACSC使用Redis中对PubSub的现有支持向相关方广播失效消息。
管理器的作用是处理失效和插槽映射的键。下面是它的样子:
管理器初始化会生成一个连接池,从该连接池为PubSub创建自己的客户端以及通过应用程序请求的任何后续缓存连接。它还维护一个名为slots的字典,该字典将一个slot number映射到它所保存的一组键名。最后,它维护LRU缓存实现的Cache类。
start()方法通过在单独的线程中监听invalidate PubSub通道来启动管理器,这并不奇怪。在该通道上截获的消息由_handler()方法处理。它依次调用invalidate()方法以使请求的插槽无效:
失效是要一个接一个地从相应的插槽集合中弹出键,然后从缓存中删除它们。最后,管理器暴露了一个工厂方法get_connection(),使用它来获取新的缓存连接:
一些评估
这篇文章并不是关于基准测试或Python本身的性能,但是理解该机制的影响是很重要的。为此,我在2019 MacBook Pro上使用了benchmark.py脚本,其中一个Redis实例使用默认值在本地运行(除了我关闭了快照功能)。
在执行测试之前,基准脚本用1000个键填充数据库,并将缓存管理器的容量设置为100。然后它运行几个计时测试来测量性能。对于两种类型的连接,每个测试都重复五次:常规连接和缓存连接。
第一个测试的结果实际上证明了缓存的一个缺点:缓存未命中。在这个测试中,我们对整个数据库中的每个键只读取一次,因此每次访问本地缓存都会导致未命中:
请注意,只计算最后一次运行的平均值,因此系列测试中的每一次都被视为热身。上面的平均值显示,每1000次读取,缓存未命中导致读取增加近13ms,大约18%的延迟增加。
但是,在适合缓存的数据集上重复测试(也就是说,只有100个键显示了更令人鼓舞的结果)。虽然第一次缓存运行显示延迟增加,但随后的运行中延迟减少两个数量级:
下一个测试名为eleven_reads,因为它会将数据库中的每个键读取一次,同时读取其他10个始终相同的键。这个高度集成的用例提供了缓存好处的一个更显著的证明(尽管这本身不是目的)。
最后一个测试加入了一个额外的写入操作,这将触发部分缓存的失效。缓存的延迟略有增加,这既是因为额外的write命令,也是因为需要重新提取缓存的内容:
部分思考
花时间缓存是有用处的。您可能已经熟悉了Redis的Keyspace notifications,它们是关于keyspace的事件,例如对PubSub通道上发送的键的修改。实际上,Keyspace notifications的使用方式与Redis服务器辅助客户端缓存的使用方式几乎相同,能获得类似的结果。
由于PubSub不是一种可靠的消息传输方式,使用Keyspace notifications或基于RESP2的RSACSC都可能导致失效通知丢失和内容过时。然而,随着RESP3的出现,只要连接处于活动状态,RSACSC通知就会被发送。任何断开连接都可以通过本地缓存重置轻松处理。
将RESP3从PubSub广播移到特定于连接的通知还意味着客户端将只获得感兴趣的插槽的失效通知。这意味着用于通信的资源消耗更少。
不管使用的是不是RESP版本,客户端作者都可以使用RSACSC进行缓存,而不仅仅是获取整个字符串。该机制不用了解用于存储密钥值的实际数据结构,因此所有核心Redis类型和模块声明的任何自定义类型都可以与之一起使用。
此外,客户机不仅可以缓存键值元组,还可以缓存请求及其响应(同时追踪所涉及的键)。这样做可以缓存GETRANGE返回的子字符串、通过LRANGE获得列表元素或任何其他类型的查询。
关于Redis的CRC64函数的一点笔记
我知道我不想在这个练习中实现CRC函数。我以为Python已经为我准备好了。
要查找哪个CRC Redis正在使用,只需查看它的源代码--在src/crc64.c文件开头:
我对“Python CRC64-jones”进行了快速搜索,在浏览完文档之后,我选择pip安装crcmod,这样我就可以使用它预定义的crc-64-jones摘要。
过了一段时间,我发现了我的东西不起作用的原因。通过对文档的仔细检查发现,crcmod使用了一个不同的多项式。他们在一起,你能看出区别吗?
此外,crcmod坚决拒绝使用Redis的多项式,并声称:
当然,后来我放弃并移植了Redis CRC64实现。不是困难任务:一个拷贝粘贴,几个搜索替换,一行实际代码重写。如果要使用RSACSC,请确保使用的CRC64实现签出到0xe9c6d914c4b8d9ca。
英文原文:https://engineering.redislabs.com/posts/redis-assisted-client-side-caching-in-python/
译者:QL