MST

星途 面试题库

面试题:Redis命令请求执行的数据验证与过滤之复杂场景

假设你正在开发一个基于Redis的高并发缓存系统,要求对所有SET命令进行数据验证,确保设置的键值对中的值大小不超过10KB,并且键名必须符合特定的命名规范(例如以'cache:'开头)。请详细说明如何设计数据验证与过滤机制,包括可能用到的脚本或模块。
49.5万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 使用Redis Lua脚本
    • Lua脚本可以在Redis服务器端原子性地执行多个命令,这对于确保数据验证的一致性非常有用。
    • 以下是一个简单的Lua脚本示例,用于验证SET命令的数据:
-- 获取键名
local key = ARGV[1]
-- 获取值
local value = ARGV[2]
-- 检查键名是否以'cache:'开头
if not key:sub(1, 6) == 'cache:' then
    return {false, "键名不符合规范,必须以'cache:'开头"}
end
-- 检查值的大小是否超过10KB(10 * 1024字节)
if string.len(value) > 10 * 1024 then
    return {false, "值大小超过10KB"}
end
-- 如果验证通过,执行SET命令
redis.call('SET', key, value)
return {true, "SET操作成功"}
  • 在客户端代码中,使用Redis客户端库调用这个Lua脚本。例如在Python中使用redis - py库:
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)

def set_with_validation(key, value):
    script = """
    local key = ARGV[1]
    local value = ARGV[2]
    if not key:sub(1, 6) == 'cache:' then
        return {false, "键名不符合规范,必须以'cache:'开头"}
    end
    if string.len(value) > 10 * 1024 then
        return {false, "值大小超过10KB"}
    end
    redis.call('SET', key, value)
    return {true, "SET操作成功"}
    """
    result = r.eval(script, 0, key, value)
    return result
  1. 自定义中间件(如果是应用层开发)
    • 如果是在应用层开发(例如使用Node.js、Python等语言构建的应用),可以在应用层实现一个中间件。
    • 以Node.js为例,使用express - redis - cache等缓存中间件结合自定义逻辑:
const express = require('express');
const redis = require('redis');
const app = express();
const client = redis.createClient();

function validateSet(key, value) {
    if (!key.startsWith('cache:')) {
        throw new Error("键名不符合规范,必须以'cache:'开头");
    }
    if (Buffer.byteLength(value) > 10 * 1024) {
        throw new Error("值大小超过10KB");
    }
    return true;
}

app.use((req, res, next) => {
    if (req.method === 'SET') {
        const key = req.body.key;
        const value = req.body.value;
        try {
            validateSet(key, value);
            client.set(key, value, (err, reply) => {
                if (err) {
                    return next(err);
                }
                res.send(reply);
            });
        } catch (error) {
            return next(error);
        }
    } else {
        next();
    }
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});
  1. Redis模块(高级方式)
    • Redis模块是一种更高级的扩展Redis功能的方式。可以用C或Rust等语言编写Redis模块。
    • 在模块中,可以定义一个新的命令,例如ValidatedSet,在这个命令内部实现键名和值大小的验证逻辑。以下是一个简单的用C语言编写Redis模块的示例框架(实际完整代码会更复杂):
#include "redismodule.h"

int ValidatedSet(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    if (argc != 3) {
        return RedisModule_WrongArity(ctx);
    }
    size_t key_len;
    const char *key = RedisModule_StringPtrLen(argv[1], &key_len);
    if (strncmp(key, "cache:", 6) != 0) {
        return RedisModule_ReplyWithError(ctx, "键名不符合规范,必须以'cache:'开头");
    }
    size_t value_len;
    const char *value = RedisModule_StringPtrLen(argv[2], &value_len);
    if (value_len > 10 * 1024) {
        return RedisModule_ReplyWithError(ctx, "值大小超过10KB");
    }
    int reply = RedisModule_Call(ctx, "SET", "ss", key, value);
    return reply;
}

int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    if (RedisModule_Init(ctx, "validatedset", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
        return REDISMODULE_ERR;
    }
    if (RedisModule_CreateCommand(ctx, "validatedset", ValidatedSet, "write", 0, 0, 0) == REDISMODULE_ERR) {
        return REDISMODULE_ERR;
    }
    return REDISMODULE_OK;
}
  • 然后将编写好的模块编译并加载到Redis服务器中,使用validatedset命令来替代普通的SET命令,从而实现数据验证。