一日一技:这个东西能给 Redis 插上火箭

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 一日一技:这个东西能给 Redis 插上火箭

摄影:产品经理黄金蘑菇

我们知道,用 Redis 的 Hash 可以实现一对多的映射,就像是 Python 的字典一样,例如:

但如果我们需要实现多对多怎么办?我举一个例子,我们要用 Redis 实现一个英语词典。有10000个单词,每个单词对应1-3个中文意思。例如:

  • resume: 重新开始;简历
  • close: 靠近;关闭
  • hello: 你好
  • address: 地址;致辞

如果这些词和中文是已经对应好的,那么显然我们直接用 Hash 就可以了,如下图所示:

eng_dict = {'resume': '重新开始;简历', 'close': '靠近;关闭', 'hello': '你好', 'address': '地址;致辞'}
client.hmset('eng_dict', eng_dict)

但是现在问题来了,如果我们要实现实时添加怎么办?例如一开始,close这个词只有一个意思:靠近,现在我要添加另一个意思,你觉得是否可以这样写:

new_chinese = '关闭'
chinese = client.hget('eng_dict', 'close')
if chinese:
    chinese = f'{chinese.decode()};{new_chinese}'
else:
    chinese = new_chinese
client.hmset('eng_dict', {'close': chinese})

这样写,在你一个人操作的时候,确实没有问题。但是,假如有两个人同时要修改这个词的中文意思怎么办?close这个词还有吝啬的的意思。如果两个人要添加这个词的中文意思,并且两个人的代码几乎同时运行到chinese = client.hget('eng_dict', 'close')这一行代码。此时,他们获取到的中文意思,都只有靠近这一个。但是甲先更新了关闭的意思,然后乙再更新了吝啬的的意思。此时就会导致甲的修改被覆盖。

为了解决这个问题,使用锁是一个思路。但今天我们不用锁,而是使用另一个方案。

在使用 Redis 的字符串时,我们可以使用 append 命令,原子性地在字符串末尾追加新的字符串,如下图所示:

但是,Hash 没有这个命令。如果你翻看redis-py这个库的官方文档,也许你会惊喜地发现,似乎使用Pipeline + Watch[1]可以实现你的需求:

先别高兴地太早,你仔细看一下watch命令监控的对象是什么。watch监控的是一个key,而不是 Hash 里面的field。但同一时间,可能会有其他人修改其他field。这就会导致 watch 总是失败。

在这种情况下,是时候使用 Redis 的内置 Lua 脚本了。你可以把一段 Lua 脚本发送到 Redis 中,它会被原子性地执行。

那么,如果使用redis-py这个库来执行 Lua 脚本呢?在官方文档上也给出了一个示例[2],如下图所示:

于是,我们可以仿照它的写法,来实现一个 Lua 版本的 Hash Append 命令:

import redis
client = redis.Redis()
def register_redis_lua():
    lua = '''
            local key = KEYS[1]
            local field = ARGV[1]
            local new_chinese = ARGV[2]
            local chinese_to_update = ""
            if redis.call('HEXISTS', key, field) == 1 then
                local chinese = redis.call('HGET', key, field)
                chinese_to_update = chinese .. ';' .. new_chinese
            else
                chinese_to_update = new_chinese
            end
            redis.call('HSET', key, field, chinese_to_update)
            '''
    lua_instance = client.register_script(lua)
    return lua_instance
automic_hash_append = register_redis_lua()
def hash_append(key, field, new_chinese):
    automic_hash_append(keys=[key], args=[field, new_chinese])

其中,我们调用register_lua方法,返回一个脚本实例,这个实例接收两个参数,keysargs,他们都是列表。这个脚本对象只需要注册一次,就可以在整个运行时持续使用。

我们来测试一下,首先,在 key 不存在的时候,它会把当前的值添加到 Hash 中:

现在已经close已经有一个中文意思了,我们再添加一个:

这样,就实现了 Hash 版本的 append 命令。

最后,我们简单讲讲涉及到的 Lua 命令。大部分命令大家看字面意思就能懂。只有一个chinese .. ';' .. new_chinese可能会让大家困惑一下。实际上,..在 Lua 里面就是用来连接两个字符串的符号,相当于 Python 中的+

目录
相关文章
|
Ubuntu
ubuntu 22.04 阿里源
ubuntu 22.04 阿里源
11667 0
|
7月前
|
人工智能 C语言
|
5月前
|
安全 API UED
A2A(Agent2Agent) 简介
本文主要介绍Google于2025年4月9日发布的Agent2Agent Protocol(简称“A2A”),这是一个旨在促进不同类型智能体(Agent)之间高效沟通与协作的开放协议。
3347 74
A2A(Agent2Agent) 简介
|
7月前
|
弹性计算 Ubuntu Linux
一键部署OpenWebUI+Ollama到阿里云ECS,轻松运行DeepSeek!(保姆级教程)
在当今数据驱动的时代,快速部署和管理大模型成为企业的关键需求。阿里云提供了一键部署OpenWebUI+Ollama的便捷方案,支持本地大模型运行和管理。用户也可以选择连接阿里云百炼的在线模型。
一键部署OpenWebUI+Ollama到阿里云ECS,轻松运行DeepSeek!(保姆级教程)
pywinauto教程
pywinauto教程
445 7
|
9月前
|
Ubuntu 芯片 开发者
Ubuntu 25 ARM 桌面系统抢先版发布:第一个Ubuntu ARM桌面系统
Ubuntu 25.04 将于2025年发布,首次支持ARM Desktop桌面版系统,为ARM架构设备如Mac M系列芯片、Raspberry Pi等带来全新的桌面体验。用户可通过虚拟机或双系统安装在Mac上运行Ubuntu ARM,抢先体验版已开放下载:[链接](https://www.baihezi.com/ubuntu/arm/desktop)。此版本不仅扩展了Ubuntu的硬件兼容性,还提供了丰富的功能和流畅的操作体验,适合开发者和技术爱好者尝试。
3111 9
|
NoSQL 关系型数据库 MySQL
php redis断线重连
php redis断线重连
616 0
|
存储 安全 JavaScript
XSS--概念、类型、实战--分析与详解[pikachu]
XSS--概念、类型、实战--分析与详解[pikachu]
|
Linux
CentOS yum源设置为国内aliyun yum源
CentOS yum源设置为国内aliyun yum源
8202 0