面试题答案
一键面试EVAL命令执行涉及的关键机制
- 脚本缓存:
- 原理:Redis使用一个字典来缓存Lua脚本。当执行
EVAL
命令时,首先会计算脚本的SHA1摘要。如果该摘要对应的脚本已经在缓存中,就直接执行缓存中的脚本,而无需重新解析和编译。这在源码中,redisCommand
函数处理EVAL
命令时,会调用scriptLoadOrEval
函数,该函数会先检查脚本是否已缓存(通过dictFind
在脚本字典中查找)。 - 作用:减少脚本的解析和编译开销,提高重复执行相同脚本时的效率。
- 原理:Redis使用一个字典来缓存Lua脚本。当执行
- 内存分配:
- 原理:在执行Lua脚本过程中,Redis需要为脚本执行环境分配内存。Lua脚本执行时会涉及到创建Lua状态机(
lua_State
),以及为脚本中定义的变量、函数等分配内存。在Redis源码中,luaCreateState
函数用于创建Lua状态机,在这个过程中会分配相应的内存。此外,Lua脚本操作Redis数据结构(如哈希表、列表等)时,也可能触发Redis数据结构自身的内存分配操作,例如向哈希表中添加新元素时可能需要重新分配哈希表的内存。 - 作用:确保Lua脚本能够正常执行,同时合理的内存分配策略能避免频繁的内存分配和释放操作,提高性能。
- 原理:在执行Lua脚本过程中,Redis需要为脚本执行环境分配内存。Lua脚本执行时会涉及到创建Lua状态机(
- 原子性:
- 原理:Redis保证
EVAL
命令执行的Lua脚本是原子性的。这是通过在执行脚本期间,阻塞其他客户端的命令请求来实现的。在源码中,server.lua_caller
字段用于标记当前是否在执行Lua脚本,当执行脚本时,该字段被设置,其他客户端命令会被放入队列等待,直到脚本执行完毕。 - 作用:保证脚本执行过程中数据的一致性,避免并发访问导致的数据不一致问题。
- 原理:Redis保证
应用层面优化
- 脚本复用:
- 做法:在应用中尽量复用相同的Lua脚本。例如,对于一些通用的业务逻辑,如批量删除符合某种规则的键,可以编写一个Lua脚本,并在需要的地方重复使用。通过计算脚本的SHA1摘要,使用
EVALSHA
命令执行脚本,这样可以直接利用脚本缓存,减少解析和编译开销。 - 示例:在Python中使用
redis - py
库,先计算脚本的SHA1摘要:
import hashlib import redis r = redis.Redis(host='localhost', port=6379, db = 0) script = "return redis.call('GET', KEYS[1])" sha1 = hashlib.sha1(script.encode()).hexdigest() result = r.evalsha(sha1, 1, 'key1')
- 做法:在应用中尽量复用相同的Lua脚本。例如,对于一些通用的业务逻辑,如批量删除符合某种规则的键,可以编写一个Lua脚本,并在需要的地方重复使用。通过计算脚本的SHA1摘要,使用
- 减少内存使用:
- 做法:在Lua脚本中尽量减少不必要的变量声明和数据结构创建。避免在脚本中创建大量临时的大数组或哈希表,尤其是在循环中。如果需要处理大量数据,可以分批次处理,减少单个脚本执行时占用的内存。
- 示例:如果要处理一个大的哈希表,不要一次性将所有数据加载到Lua脚本中处理,而是每次只获取一部分数据进行处理:
local cursor = "0" local keys = {} repeat local res = redis.call('HSCAN', KEYS[1], cursor, 'COUNT', 100) cursor = res[1] for i = 2, #res do table.insert(keys, res[i]) end -- 处理keys中的数据 for _, key in ipairs(keys) do -- 业务逻辑 end keys = {} until cursor == "0"
Redis配置层面优化
- 调整脚本缓存大小:
- 做法:可以通过修改Redis配置文件中的
lua - script - cache - max - memory
参数来调整脚本缓存占用的最大内存。如果应用中会频繁使用Lua脚本,适当增大这个值可以提高脚本缓存命中率。但如果设置过大,可能会占用过多内存,影响其他Redis功能。 - 示例:在
redis.conf
文件中添加或修改:
lua - script - cache - max - memory 10mb
- 做法:可以通过修改Redis配置文件中的
- 优化内存分配策略:
- 做法:Redis使用jemalloc作为默认的内存分配器。可以根据实际应用场景调整jemalloc的参数,如
MALLOC_ARENA_MAX
等。例如,如果应用中Lua脚本执行频繁且内存分配操作较多,可以适当增大MALLOC_ARENA_MAX
的值,以提高内存分配效率。不过,调整这些参数需要谨慎,因为不同的设置可能对性能产生不同的影响,需要通过实际测试来确定最优值。 - 示例:可以通过环境变量设置jemalloc参数,在启动Redis前执行:
export MALLOC_ARENA_MAX = 4 redis - server
- 做法:Redis使用jemalloc作为默认的内存分配器。可以根据实际应用场景调整jemalloc的参数,如