Redis协议详解及其异步应用

news2024/10/27 23:22:31

目录

    • 一、Redis Pipeline(管道)
      • 概述
      • 优点
      • 使用场景
      • 工作原理
      • Pipeline 的基本操作步骤
      • C++ 示例(使用 [hiredis](https://github.com/redis/hiredis) 库)
    • 二、Redis 事务
      • 概述
      • 事务的前提
      • 事务特征(ACID 分析)
      • WATCH 命令
        • 示例 1:事务实现 `zpop`
        • 示例 2:事务实现加倍操作
    • 三、Lua 脚本
      • Lua 脚本的事务特性分析
      • 基本命令
      • 应用示例
        • 示例:执行加倍操作
    • 四、Redis 发布订阅(Pub/Sub)
      • 主要命令
      • 应用场景
      • C++ 示例(使用 [hiredis](https://github.com/redis/hiredis) 库)
        • 发布者示例
        • 订阅者示例
    • 5. Redis 异步方式
    • 移步:[Redis异步实现解析](https://blog.csdn.net/weixin_43925427/article/details/142876618?fromshare=blogdetail&sharetype=blogdetail&sharerId=142876618&sharerefer=PC&sharesource=weixin_43925427&sharefrom=from_link)
    • 六、Redis 的缺点
    • 七、总结
      • Redis 的优势
      • Redis 的局限性
      • 应用建议
      • 参考


一、Redis Pipeline(管道)

概述

Redis Pipeline(管道) 是一种客户端机制,允许一次性发送多个Redis命令到服务器,而无需等待每个命令的响应。通过管道,可以显著减少网络延迟,提高命令执行的吞吐量。

优点

  • 减少网络延迟:批量发送命令,减少网络往返次数(RTT)。
  • 提高吞吐量:一次性处理多个命令,提升操作效率。

使用场景

  • 批量插入或更新数据:如批量存储用户信息或日志数据。
  • 批量读取数据:如同时获取多个键的值。

工作原理

在这里插入图片描述

传统上,每个Redis命令在客户端和服务器之间都需要一次网络往返通信。当需要执行大量命令时,这种通信开销会成为性能瓶颈。使用Redis Pipeline,可以将多个命令打包成一个网络请求一次性发送给服务器,减少网络开销,并更充分地利用服务器的处理能力。
在这里插入图片描述

Pipeline 的基本操作步骤

  1. 创建 Pipeline 对象:在客户端中创建一个 Pipeline 对象,用于存储要执行的多个命令。
  2. 向 Pipeline 中添加命令:使用 Pipeline 对象的方法(如 pipeline.set(key, value))向其中添加要执行的Redis命令。可以添加任意多个命令。
  3. 执行 Pipeline:调用 Pipeline 对象的 execute() 方法,将 Pipeline 中的所有命令一次性发送给Redis服务器执行。
  4. 获取结果:通过遍历 Pipeline 中的命令结果,或使用 execute() 方法的返回值来获取执行结果。

C++ 示例(使用 hiredis 库)

以下示例展示了如何使用 hiredis 库在C++中实现Redis Pipeline:

#include <hiredis/hiredis.h>
#include <iostream>
#include <vector>
#include <string>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    // 启动管道:使用 MULTI 开启事务
    redisAppendCommand(c, "MULTI");
    redisAppendCommand(c, "SET key1 value1");
    redisAppendCommand(c, "SET key2 value2");
    redisAppendCommand(c, "SET key3 value3");
    redisAppendCommand(c, "EXEC");

    // 读取响应
    for (int i = 0; i < 5; ++i) {
        redisReply *reply;
        if (redisGetReply(c, (void**)&reply) == REDIS_OK) {
            // 处理每个回复
            if (reply->type == REDIS_REPLY_STATUS) {
                std::cout << "Reply " << i << ": " << reply->str << std::endl;
            } else if (reply->type == REDIS_REPLY_ARRAY) {
                std::cout << "Reply " << i << ": [";
                for (size_t j = 0; j < reply->elements; ++j) {
                    if (reply->element[j]->str)
                        std::cout << reply->element[j]->str;
                    else
                        std::cout << "nil";
                    if (j != reply->elements -1) std::cout << ", ";
                }
                std::cout << "]" << std::endl;
            } else {
                std::cout << "Reply " << i << ": " << (reply->str ? reply->str : "") << std::endl;
            }
            freeReplyObject(reply);
        } else {
            std::cerr << "Failed to get reply" << std::endl;
            break;
        }
    }

    // 关闭连接
    redisFree(c);
    return 0;
}

输出示例

Reply 0: OK
Reply 1: OK
Reply 2: OK
Reply 3: OK
Reply 4: [OK, OK, OK]

解释

  1. redisAppendCommand:将多个命令(包括事务命令 MULTIEXEC)添加到管道中。
  2. redisGetReply:逐个获取命令的响应。
  3. 处理回复:根据回复类型处理并输出每个命令的响应。

注意:在实际应用中,可以根据需求添加更多命令到管道中,以实现批量操作。


二、Redis 事务

概述

Redis 事务 允许客户端一次性执行一系列命令,确保这些命令按顺序执行且没有其他客户端的干扰。事务通过以下命令实现:

  • MULTI:标记事务的开始。
  • EXEC:执行事务中的所有命令。
  • DISCARD:取消事务,清空事务队列。
  • WATCH:监视一个或多个键,如果这些键在事务执行前被修改,事务将被中断。

事务的前提

在有并发连接的情况下,不同连接异步执行命令可能会导致不可预期的冲突。Redis 是单线程的,但在事务执行期间,如果不加以控制,仍可能出现数据不一致的问题。比如:我们希望顺序执行命令1、2、3。但是如果Redis是请求回应模型,若在命令1和命令2之间的空档期,命令3插入执行,那么最后的结果就会出错。
在这里插入图片描述

事务特征(ACID 分析)

ACID(原子性、一致性、隔离性和持久性)是关系型数据库管理系统确保事务正确执行的四个基本特性。以下分析Redis事务在ACID方面的支持情况:

  1. 原子性(Atomicity)

    • 部分支持:Redis中的单个命令是原子性的。然而,Redis事务(通过 MULTIEXEC 实现)不支持回滚机制。如果在事务中执行多个命令,其中一个命令失败,之前的命令依然会执行,无法回滚。

    • 示例

      MULTI
      SET key1 value1
      INCR key2
      EXEC
      

      如果 INCR key2 对应的键类型不是整数,INCR 命令会失败,但 SET key1 value1 已经执行,无法回滚。

  2. 一致性(Consistency)

    • 部分支持:Redis引擎本身不提供严格的一致性保证。例如,在主从复制模式下,当主节点出现故障时,从节点可能无法立即更新,导致数据的部分丢失。
    • 类型一致性
      • 示例

        SET count 1000
        TYPE count  // 返回 string
        LPUSH count 2000  // 返回 (error) WRONGTYPE Operation against a key holding the wrong kind of value
        
  3. 隔离性(Isolation)

    • 支持:Redis使用单线程模型,一个客户端的命令在执行期间不会被其他客户端的命令中断,因此天然具备隔离性。
    • 注意:在多线程环境下,临界资源仍需要加锁来确保数据一致性。
  4. 持久性(Durability)

    • 部分支持:Redis提供了RDB(快照)和AOF(Append-Only File)两种持久化机制。特别是在AOF持久化策略为 appendfsync=always 时,Redis能够确保事务提交后数据被永久保存。
    • 实际情况:在实际项目中,通常不会将AOF配置为 always,因为这会影响性能。

WATCH 命令

WATCH 命令用于监视一个或多个键,并在事务执行期间检测这些键是否被修改。如果被监视的键在事务执行前被修改,事务将被取消执行。这是一种乐观锁机制。

事务实现示例

示例 1:事务实现 zpop

假设需要从一个有序集合中弹出最高分的成员,并将其添加到另一个集合中,这是一个需要原子性操作的场景。

#include <hiredis/hiredis.h>
#include <iostream>
#include <string>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    // 监视zset
    redisReply *watch_reply = (redisReply*)redisCommand(c, "WATCH myzset");
    freeReplyObject(watch_reply);

    // 获取zset的最高分成员
    redisReply *reply = (redisReply*)redisCommand(c, "ZREVRANGE myzset 0 0 WITHSCORES");
    if (reply->elements == 0) {
        std::cout << "myzset is empty" << std::endl;
        freeReplyObject(reply);
        redisFree(c);
        return 0;
    }

    std::string member = reply->element[0]->str;
    double score = atof(reply->element[1]->str);
    freeReplyObject(reply);

    // 开始事务
    redisReply *multi_reply = (redisReply*)redisCommand(c, "MULTI");
    freeReplyObject(multi_reply);

    // 弹出成员
    redisAppendCommand(c, "ZREM myzset %s", member.c_str());
    // 添加到另一个集合
    redisAppendCommand(c, "ZADD anotherzset %f %s", score, member.c_str());

    // 执行事务
    redisReply *exec_reply;
    if (redisGetReply(c, (void**)&exec_reply) == REDIS_OK) {
        if (exec_reply->type == REDIS_REPLY_ARRAY) {
            std::cout << "Transaction executed successfully" << std::endl;
        } else {
            std::cout << "Transaction failed" << std::endl;
        }
        freeReplyObject(exec_reply);
    } else {
        std::cerr << "EXEC failed" << std::endl;
    }

    redisFree(c);
    return 0;
}

解释

  1. WATCH:监视 myzset,如果在事务执行前 myzset 被其他客户端修改,事务将被取消。
  2. ZREVRANGE:获取 myzset 中分数最高的成员。
  3. MULTI:开始事务。
  4. ZREM & ZADD:在事务中移除成员并添加到另一个集合。
  5. EXEC:执行事务。如果在事务执行前 myzset 被修改,EXEC 将返回 null,表示事务被中断。
示例 2:事务实现加倍操作

假设需要对某个键的值进行加倍操作,这需要确保读取和写入的原子性。

#include <hiredis/hiredis.h>
#include <iostream>
#include <string>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    std::string key = "counter";

    while (true) {
        // 监视键
        redisReply *watch_reply = (redisReply*)redisCommand(c, "WATCH %s", key.c_str());
        freeReplyObject(watch_reply);

        // 获取当前值
        redisReply *reply = (redisReply*)redisCommand(c, "GET %s", key.c_str());
        if (reply->type == REDIS_REPLY_STRING) {
            int value = atoi(reply->str);
            freeReplyObject(reply);

            // 开始事务
            redisReply *multi_reply = (redisReply*)redisCommand(c, "MULTI");
            freeReplyObject(multi_reply);

            // 设置新值
            redisAppendCommand(c, "SET %s %d", key.c_str(), value * 2);

            // 执行事务
            redisReply *cmd_reply;
            if (redisGetReply(c, (void**)&cmd_reply) == REDIS_OK) {
                freeReplyObject(cmd_reply);
                redisReply *exec_reply;
                if (redisGetReply(c, (void**)&exec_reply) == REDIS_OK) {
                    if (exec_reply->type == REDIS_REPLY_ARRAY) {
                        std::cout << "Counter doubled to " << value * 2 << std::endl;
                        freeReplyObject(exec_reply);
                        break;
                    } else {
                        std::cout << "Transaction aborted" << std::endl;
                        freeReplyObject(exec_reply);
                        continue;
                    }
                }
            }
        } else {
            freeReplyObject(reply);
            std::cerr << "Failed to get key" << std::endl;
            break;
        }
    }

    redisFree(c);
    return 0;
}

解释

  1. WATCH:监视 counter 键,确保在事务执行期间没有其他客户端修改该键。
  2. GET:获取 counter 当前值。
  3. MULTI:开始事务。
  4. SET:在事务中设置新值为当前值的两倍。
  5. EXEC:执行事务。如果 counter 在事务执行前被修改,EXEC 将返回 null,事务被取消,循环继续尝试。

三、Lua 脚本

Lua 脚本的事务特性分析

Lua 脚本 是Redis提供的一种在服务器端执行复杂操作的机制。Lua脚本在Redis服务器上运行,能够将多个命令组合成一个原子性操作,减少网络往返次数。

ACID 特性分析

  1. 原子性(Atomicity)

    • 部分满足:Lua脚本通过一个命令将所有脚本中的语句一起执行,但不具备回滚机制。如果脚本中某个命令执行失败,之前成功的命令依然会生效。
  2. 一致性(Consistency)

    • 部分不满足:Lua脚本本身不具备严格的一致性。如果脚本执行过程中发生错误,无法回滚已执行的命令,可能导致数据不一致。
  3. 隔离性(Isolation)

    • 满足:Redis使用单线程模型,Lua脚本作为单个数据包执行期间,其他命令或脚本不会被打断,确保隔离性。
  4. 持久性(Durability)

    • 部分不满足:只有在AOF持久化策略为 appendfsync=always 时,Lua脚本的修改才具备持久性。否则,可能会在系统故障时丢失。

基本命令

  • EVAL:执行一个Lua脚本。

    EVAL script numkeys [key ...] [arg ...]
    
  • EVALSHA:根据脚本的SHA1哈希值执行脚本。

    EVALSHA sha1 numkeys [key ...] [arg ...]
    
  • SCRIPT LOAD:将Lua脚本加载到Redis并返回其SHA1哈希值。

    SCRIPT LOAD script
    
  • SCRIPT EXISTS:检查脚本缓存中是否存在指定的SHA1哈希值的Lua脚本。

    SCRIPT EXISTS sha1 [sha1 ...]
    
  • SCRIPT FLUSH:清除所有脚本缓存。

    SCRIPT FLUSH
    
  • SCRIPT KILL:强制停止正在运行的脚本(如死循环)。

    SCRIPT KILL
    

应用示例

示例:执行加倍操作

测试使用

SET jack 100
EVAL 'local key = KEYS[1]; local val = redis.call("GET", key); if val then redis.call("SET", key, 2 * val); return 2 * val; end; return 0;' 1 jack

输出

(integer) 200

实际使用

  1. 加载脚本到Redis

    SCRIPT LOAD 'local key = KEYS[1]; local val = redis.call("GET", key); if val then redis.call("SET", key, 2 * val); return 2 * val; end; return 0;'
    

    输出

    "f76a2571acb0452ef1a0ba3b0bbd6c46a321cbe1"
    
  2. 执行缓存的脚本

    EVALSHA "f76a2571acb0452ef1a0ba3b0bbd6c46a321cbe1" 1 jack
    

    输出

    (integer) 200
    

C++ 示例(使用 EVAL 和 EVALSHA)

以下示例展示了如何使用 hiredis 库在C++中加载并执行Lua脚本:

#include <hiredis/hiredis.h>
#include <iostream>
#include <string>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    // Lua 脚本:key3 = key1 + key2
    const char *script = "local v1 = tonumber(redis.call('GET', KEYS[1])) " \
                         "local v2 = tonumber(redis.call('GET', KEYS[2])) " \
                         "local sum = v1 + v2 " \
                         "redis.call('SET', KEYS[3], sum) " \
                         "return sum";

    // 加载脚本到Redis
    redisReply *reply = (redisReply*)redisCommand(c, "SCRIPT LOAD %s", script);
    std::string sha = "";
    if (reply->type == REDIS_REPLY_STRING) {
        sha = reply->str;
        std::cout << "Script SHA1: " << sha << std::endl;
    }
    freeReplyObject(reply);

    // 设置初始值
    redisCommand(c, "SET key1 15");
    redisCommand(c, "SET key2 25");

    // 使用 EVALSHA 执行脚本
    std::string evalsha_cmd = "EVALSHA " + sha + " 3 key1 key2 key3";
    reply = (redisReply*)redisCommand(c, evalsha_cmd.c_str());
    if (reply->type == REDIS_REPLY_INTEGER || reply->type == REDIS_REPLY_STRING) {
        std::cout << "Sum via EVALSHA: " << (reply->type == REDIS_REPLY_INTEGER ? std::to_string(reply->integer) : reply->str) << std::endl;
    }
    freeReplyObject(reply);

    // 获取结果
    reply = redisCommand(c, "GET key3");
    if (reply->type == REDIS_REPLY_STRING) {
        std::cout << "key3 = " << reply->str << std::endl;
    }
    freeReplyObject(reply);

    redisFree(c);
    return 0;
}

输出示例

Script SHA1: f76a2571acb0452ef1a0ba3b0bbd6c46a321cbe1
Sum via EVALSHA: 40
key3 = 40

解释

  1. SCRIPT LOAD:将Lua脚本加载到Redis,并获取其SHA1哈希值。
  2. EVALSHA:使用脚本的SHA1哈希值执行脚本,实现 key3 = key1 + key2
  3. GET:获取执行结果 key3 的值。

四、Redis 发布订阅(Pub/Sub)

主要命令

为了支持消息的多播机制,Redis的发布订阅(Pub/Sub)是一种消息传递模式,允许消息发布者将消息发送到特定频道,订阅者订阅这些频道以接收消息。消息不一定可达;分布式消息队列;stream的方式确保一定可达;主要命令包括:

  • PUBLISH:发布消息到指定频道。

    PUBLISH channel message
    
  • SUBSCRIBE:订阅一个或多个频道。

    SUBSCRIBE channel [channel ...]
    
  • UNSUBSCRIBE:取消订阅频道。

    UNSUBSCRIBE channel [channel ...]
    
  • PSUBSCRIBE:按模式订阅一个或多个频道。

    PSUBSCRIBE pattern [pattern ...]
    
  • PUNSUBSCRIBE:取消按模式订阅的频道。

    PUNSUBSCRIBE pattern [pattern ...]
    

示例命令

# 订阅频道
SUBSCRIBE news.game news.tech news.school

# 订阅模式频道
PSUBSCRIBE news.*

# 发布消息
PUBLISH news.game 'EDG wins S12 championship'

# 取消订阅频道
UNSUBSCRIBE news.game

# 取消订阅模式频道
PUNSUBSCRIBE news.*

客户端接收消息示例

当订阅者订阅了 news.gamenews.technews.school 频道后,发布者发布消息到 news.game 频道,所有订阅该频道的客户端都会收到消息。

message news.game EDG wins S12 championship

应用场景

发布订阅功能通常需要重新开启一个连接,因为订阅连接会进入阻塞模式接收消息,无法继续执行其他命令。因此,实际项目中支持Pub/Sub时,通常需要另开一条连接用于处理发布订阅。

常见应用场景

  • 实时聊天系统:用户通过频道发送和接收消息。
  • 实时通知:系统事件通过频道通知相关服务。
  • 分布式系统的消息传递:不同服务之间的通信。

C++ 示例(使用 hiredis 库)

发布者和订阅者通常需要在不同的连接上进行操作,因为订阅连接会进入阻塞模式接收消息。

发布者示例
#include <hiredis/hiredis.h>
#include <iostream>
#include <thread>
#include <chrono>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    // 发布消息
    for (int i = 1; i <= 5; ++i) {
        std::string message = "Hello " + std::to_string(i);
        redisReply *reply = (redisReply*)redisCommand(c, "PUBLISH mychannel %s", message.c_str());
        if (reply->type == REDIS_REPLY_INTEGER) {
            std::cout << "Published message to " << reply->integer << " subscribers." << std::endl;
        }
        freeReplyObject(reply);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    redisFree(c);
    return 0;
}

输出示例

Published message to 1 subscribers.
Published message to 1 subscribers.
Published message to 1 subscribers.
Published message to 1 subscribers.
Published message to 1 subscribers.
订阅者示例
#include <hiredis/hiredis.h>
#include <iostream>
#include <thread>

int main() {
    // 连接到Redis服务器
    redisContext *c = redisConnect("127.0.0.1", 6379);
    if (c == nullptr || c->err) {
        if (c) {
            std::cerr << "Connection error: " << c->errstr << std::endl;
            redisFree(c);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
        return 1;
    }

    // 订阅频道
    redisReply *reply = (redisReply*)redisCommand(c, "SUBSCRIBE mychannel");
    freeReplyObject(reply);

    // 持续接收消息
    while (redisGetReply(c, (void**)&reply) == REDIS_OK) {
        if (reply->type == REDIS_REPLY_ARRAY && reply->elements == 3) {
            std::cout << "Received message from " << reply->element[1]->str << ": " << reply->element[2]->str << std::endl;
        }
        freeReplyObject(reply);
    }

    redisFree(c);
    return 0;
}

输出示例

Received message from mychannel: Hello 1
Received message from mychannel: Hello 2
Received message from mychannel: Hello 3
Received message from mychannel: Hello 4
Received message from mychannel: Hello 5

注意:订阅者和发布者通常需要在不同的进程或线程中运行,因为订阅者连接会进入阻塞状态,无法执行其他命令。


当然可以!以下内容详细介绍了Redis 异步连接,包括同步连接异步连接的概念、优缺点、实现方法以及相应的C代码示例。通过这些内容,您可以更好地理解和应用Redis的异步连接功能,以优化应用性能。


5. Redis 异步方式

移步:Redis异步实现解析

六、Redis 的缺点

尽管Redis在许多场景下表现出色,但也存在一些缺点和限制:

  1. 内存限制

    • 描述:Redis是内存数据库,所有数据存储在内存中。对于大数据量应用,内存成本较高。
    • 影响:高内存消耗限制了Redis在大规模数据存储上的应用。
    • 解决方案:通过分片(Sharding)和使用更高效的数据结构来优化内存使用。
  2. 单线程模型

    • 描述:Redis的大部分命令是单线程执行的,虽然通过IO多路复用实现高性能,但在多核CPU上无法充分利用多线程优势。
    • 影响:在CPU密集型操作或需要高并发处理时,性能可能受限。
    • 解决方案:通过集群部署,分散负载到多个Redis实例。
  3. 数据持久化风险

    • 描述:虽然Redis支持RDB和AOF持久化,但在极端情况下可能会丢失部分数据。
    • 影响:数据的可靠性和持久性在某些应用场景下可能无法满足要求。
    • 解决方案:合理配置持久化策略,结合主从复制和高可用架构,增强数据安全性。
  4. 缺乏复杂查询能力

    • 描述:Redis主要支持键值操作,不适合需要复杂查询和关联的应用场景。
    • 影响:对于需要复杂数据关系和查询的应用,Redis无法直接满足需求。
    • 解决方案:结合其他数据库(如SQL数据库)使用,Redis用于缓存和快速访问。
  5. 有限的事务支持

    • 描述:Redis的事务不支持回滚机制,无法像传统数据库那样处理复杂的事务逻辑。
    • 影响:在需要严格事务控制的场景下,Redis无法提供足够的支持。
    • 解决方案:使用Lua脚本实现复杂的原子操作,或结合其他数据库的事务功能。
  6. 数据结构局限

    • 描述:尽管Redis支持多种数据结构,但在某些特定场景下可能不如专用数据库高效。
    • 影响:对于特定类型的数据处理(如图数据),需要额外的实现工作。
    • 解决方案:使用专用的数据库(如图数据库)来处理特定类型的数据。
  7. 安全性

    • 描述:Redis的默认配置安全性较低,需额外配置才能满足生产环境的安全要求。
    • 影响:未经配置的Redis实例可能容易受到未经授权的访问和攻击。
    • 解决方案:配置密码认证、限制网络访问、启用TLS等安全措施。
  8. 缺乏多版本并发控制(MVCC)

    • 描述:Redis不支持复杂的并发控制机制,可能导致竞争条件和数据一致性问题。
    • 影响:在高并发环境下,可能会出现数据冲突和不一致。
    • 解决方案:使用 WATCH、Lua脚本等机制实现乐观锁,或通过应用层控制并发。

七、总结

Redis 是一个高性能的内存数据库,适用于多种场景,如缓存、实时数据处理、消息队列等。通过深入了解其Pipeline(管道)事务Lua 脚本发布订阅(Pub/Sub)异步连接等功能,开发者可以充分利用Redis的优势来优化应用性能。

Redis 的优势

  • 高性能:基于内存,支持快速的数据读写。
  • 丰富的数据结构:支持字符串、哈希、列表、集合、有序集合等多种数据类型。
  • 多样的功能:支持事务、发布订阅、Lua脚本、持久化等功能。
  • 简单易用:Redis命令直观,易于学习和使用。

Redis 的局限性

  • 内存消耗高:对于大规模数据存储,内存成本较高。
  • 事务支持有限:缺乏回滚机制,事务控制不如关系型数据库完善。
  • 安全性需额外配置:默认配置不够安全,需手动加强安全措施。
  • 单线程模型:在某些高并发或CPU密集型场景下,性能可能受限。

应用建议

在选择使用Redis时,需综合考虑应用需求和Redis的特性:

  • 适合场景

    • 高频访问的数据缓存。
    • 实时数据分析和处理。
    • 实现分布式锁和消息队列。
    • 会话存储和排行榜系统。
  • 不适合场景

    • 需要复杂事务控制的应用。
    • 大规模数据存储,超过内存容量。
    • 需要复杂查询和数据关联的应用。

通过合理设计架构,结合Redis的优势和其他数据库的功能,可以构建高性能、可靠的应用系统。

参考

0voice · GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【HarmonyOS】HMRouter使用详解(二)路由跳转

路由跳转 HMRouter中使用HMRouterMgr的静态方法push()和replace()来实现路由跳转。使用pop()方法来实现页面返回 push &#xff1a;目标页面不会替换当前页&#xff0c;而是插入页面栈。可以使用pop实现页面的返回操作。replace&#xff1a;目标页面会替换当前页&#xff0c;并…

西门子828d的plc一些信息记录

1、虽然是200的plc但是引入了DB的形式替代原来的V存储区。 2、用户自定义DB块范围&#xff0c;DB9000-DB9063,共64个DB块。 可用地址范围如上图 机床MCP483面板地址表&#xff0c;其它类型的面板地址自己在828d简明调试手册里查看。 如何上载828d的plc程序&#xff1a; 1.通…

web-105linux权限提升

rsync未授权本地覆盖 Rsync 是 linux 下一款数据备份工具&#xff0c;默认开启 873 端口 https://vulhub.org/#/environments/rsync/common/ 借助 Linux 默认计划任务调用/etc/cron.hourly&#xff0c;利用 rsync 连接覆盖 前提条件就是需要知道rsync的密码或者存在未授权 -提…

【成品设计】基于Arduino平台的物联网智能灯

《基于Arduino平台的物联网智能灯》 整体功能&#xff1a; 这个任务中要求实现一个物联网智能灯。实际测试环境中要求设备能够自己创建一个热点&#xff0c;连接这个热点后能自动弹出控制界面&#xff08;强制门户&#xff09;。 功能点 基础功能 (60分) 要求作品至少有2个灯…

发布-订阅模式(Publisher-Subscriber)

实际上&#xff0c;发布-订阅模式只是观察者模式的一个别称。 但是经过时间的沉淀&#xff0c;似乎他已经强大了起来&#xff0c;已经独立于观察者模式&#xff0c;成为另外一种不同的设计模式。在现在的发布订阅模式中&#xff0c;称为发布者的消息发送者不会将消息直接发送给…

Linux下基本指令

Linux下基本指令 登录系统输入ssh root&#xff0c;在后面输入ip公用地址&#xff0c;按下enter键&#xff0c;会弹出一个密码框&#xff0c;输入密码即可登录成功。 Xshell下Altenter全屏&#xff0c;再重复操作是取消全屏。 clear清理屏幕。 01. ls 指令&#xff08;用来…

[红队apt]文件捆绑攻击流程

免责声明:本文用于了解攻击者攻击手法&#xff0c;切勿用于不法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理黑客通过文件捆绑进行攻击的流程思路 文件捆绑原理 废话只多说这一句。 1.exe和2.exe被你捆绑为3.exe。 那么你点击了3.exe就等于点…

信息安全工程师(45)入侵检测系统组成与分类

前言 入侵检测系统&#xff08;IDS&#xff09;是一种网络安全设备或软件&#xff0c;能够监控和分析网络或系统活动&#xff0c;以检测和响应潜在的入侵行为。 一、入侵检测系统的组成 根据互联网工程任务组&#xff08;IETF&#xff09;的定义&#xff0c;一个典型的入侵检测…

文科类考研答题规范与卷面整洁度提升:高效备考的秘诀

随着考研竞争的日益激烈&#xff0c;考生们为了在众多竞争者中脱颖而出&#xff0c;纷纷寻求提升自己的备考策略&#xff0c;答题规范和卷面整洁度在文科类考研中显得尤为重要&#xff0c;本文将从答题规范和卷面整洁度两个方面&#xff0c;为广大文科类考研学子提供一些建议&a…

LeetCode刷题日记之回溯算法(一)

目录 前言组合组合总和III电话号码的字母组合总结 前言 今天开始学习回溯算法啦&#xff0c;虽然直接递归学习的时候有涉及到回溯但是没有系统性的学习&#xff0c;希望博主记录的内容能够对大家有所帮助 &#xff0c;一起加油吧朋友们&#xff01;&#x1f4aa;&#x1f4aa;…

飞腾X100适配Ubuntu说明

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

实践体验密集小目标检测,以小麦麦穗颗粒为基准,基于嵌入式端超轻量级模型LeYOLO全系列【n/s/m/l】参数模型开发构建智能精准麦穗颗粒检测计数系统

对于常规的目标检测任务来说&#xff0c;诸如&#xff1a;COCO、VOC这类基础的数据场景&#xff0c;涌现出来了一些列性能初衷的检测模型&#xff0c;YOLO系列就是其中的佼佼者&#xff0c;不断地刷榜取得了越来越好的效果&#xff0c;不过这些评测指标是基于COCO、VOC这类公开…

基于Python+sqlite3实现(Web)图书管理系统

项目名称&#xff1a;LibraryManagementSystem 一、系统目标 使用了Python作为语言,以django为后台&#xff0c;sqlite3作为数据库&#xff0c;UI基于bootstrap的图书管理系统&#xff0c;模拟图书管理的真实场景&#xff0c;考虑客观需求&#xff0c;界面简洁、操作方便&…

解决Kali直接使用root用户密码ssh远程登录不上问题

一、问题描述 当我们直接使用root用户和密码ssh远程登录时&#xff08;ssh rootKali主机的IP地址&#xff09;直接提示“SSH服务器拒绝了密码&#xff0c;请再试一次”或者“Permission denied, please try again.&#xff08;权限被拒绝&#xff0c;请重试&#xff09;”信息&…

腾讯云视立方Electron 相关问题

安装相关 trtc-electron-sdk 是否兼容官方 Electron v12.0.1 版本? 兼容的&#xff0c;trtc-electron-sdk 没有特别依赖 elecron 自身的 SDK&#xff0c;所以没有相关的版本依赖。 Electron 下载慢甚至卡住不动&#xff1f; 当开始下载tmp-3320-1-SHASUMS256.txt-6.1.9文件…

考研C语言程序设计_语法相关习题(持续更新)

目录 一、语法题strlen转义字符内置数据类型字符串结束标志局部变量和全局变量名字冲突 局部优先switch语句中的关键字数组初始化是否正确注意define不是关键字C语言中不能用连等判断switch( )的括号里可以是什么类型? 二、程序阅读题有关static有关continue说明代码的功能for…

Postgresql JSON操作符

postgresql json数据 参考文章&#xff1a; JSON数据操作 操作符 1、-> 表示获取一个JSON数组元素&#xff0c;支持下标值(下标从0开始)、Key获取。 2、->> 表示获取一个JSON对象字符串。 3、#> 表示获取指定路径的一个JSON对象。 4、#>>表示获取指定路径…

脉冲目标检测网络模型SpikeYOLO——基于整值训练和脉冲驱动推理的高性能节能目标检测神经网络

最近看到目标检测领域又出新作&#xff0c;ECCV2024 满分Oral论文——《Integer-Valued Training and Spike-Driven Inference Spiking Neural Network for High-performance and Energy-efficient Object Detection》论文地址在这里&#xff0c;如下所示&#xff1a; 感兴趣的…

浪潮信息领航边缘计算,推动AI与各行业深度融合

在9月20日于安徽盛大召开的浪潮信息边缘计算合作伙伴大会上&#xff0c;浪潮信息指出&#xff0c;未来的计算领域将全面融入AI技术&#xff0c;特别是在企业边缘侧&#xff0c;智能应用特别是生成式人工智能应用正在迅速普及&#xff0c;这一趋势正引领边缘计算向边缘智算的方向…

R语言机器学习算法实战系列(三)lightGBM算法(Light Gradient Boosting Machine)

文章目录 介绍原理:应用方向:教程下载数据加载R包导入数据数据预处理数据描述数据切割设置数据对象调节参数训练模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性模型SHAP值解释保存模型总结系统信息介绍 LightGBM(Light Gradient B…