【请关注】关于VC++实现使用Redis不同方法,有效达到 Redis 性能优化、防击穿

news2025/6/1 11:40:25

麻烦先关注方便了解最新分享。

 

关于VC++实现使用Redis不同方法,有效达到 Redis 性能优化、防击穿。

 

一、性能优化核心方法

 

1. 使用连接池复用连接

 

// 连接池结构体

struct RedisPool {

    hiredisContext** connections;

    int size;

    sem_t sem; // 信号量控制并发

};

 

// 初始化连接池

RedisPool* create_redis_pool(const char* host, int port, int pool_size) {

    RedisPool* pool = (RedisPool*)malloc(sizeof(RedisPool));

    pool->size = pool_size;

    pool->connections = (hiredisContext**)malloc(pool_size * sizeof**iredisContext*));

    sem_init(&pool->sem, 0, pool_size); // 初始化信号量

 

    for (int i = 0; i < pool_size; i++) {

        pool->connections[i] = redisConnect(host, port);

        if (pool->connections[i]->err) {

            // 处理连接失败

            redisFree(pool->connections[i]);

            exit(1);

        }

    }

    return pool;

}

 

// 从连接池获取连接

hiredisContext* get_connection(RedisPool* pool) {

    sem_wait(&pool->sem); // 等待可用连接

    for (int i = 0; i < pool->size; i++) {

        if (pool->connections[i]->err == 0) { // 检查连接健康状态

            return pool->connections[i];

        }

    }

    return NULL; // 理论上信号量控制下不会出现

}

 

 

原理:避免频繁创建/销毁连接(单次连接耗时约 1-3ms),连接池大小建议为  CPU核心数*2+1 。

 

2. Pipeline 批量操作

 

void pipeline_example(RedisPool* pool) {

    hiredisContext* conn = get_connection(pool);

    redisBuffer* buf = redisBufferCreate(); // 创建 Pipeline 缓冲区

 

    // 批量添加命令

    redisAppendCommand(buf, "SET key1 %s", "value1");

    redisAppendCommand(buf, "SET key2 %s", "value2");

    redisAppendCommand(buf, "GET key1");

 

    // 一次性发送所有命令

    size_t len = redisBufferLength(buf);

    if (redisWrite(conn, buf->data, len) != REDIS_OK) {

        // 处理写入失败

        redisFree(conn);

        return;

    }

 

    // 读取所有结果

    redisReply* reply;

    int count = 3; // 命令数量

    for (int i = 0; i < count; i++) {

        if (redisGetReply(conn, (void**)&reply) == REDIS_OK) {

            // 处理结果

            if (reply->type == REDIS_REPLY_STRING) {

                printf("GET result: %s\n", reply->str);

            }

            freeReplyObject(reply);

        }

    }

    redisBufferFree(buf);

    sem_post(&pool->sem); // 归还连接

}

 

 

效果:1000 次单命令耗时约 80ms,Pipeline 批量处理仅需 2ms(减少网络往返开销)。

 

3. 使用异步 API(libevent 异步库)

 

#include <event2/event.h>

#include <hiredis/adapters/libevent.h>

 

void async_callback(redisAsyncContext* ctx, void* reply, void* privdata) {

    redisReply* r = (redisReply*)reply;

    if (r->type == REDIS_REPLY_STRING) {

        printf("Async GET: %s\n", r->str);

    }

}

 

void async_example() {

    struct event_base* base = event_base_new();

    redisAsyncContext* ctx = redisAsyncConnect("127.0.0.1", 6379);

    

    if (ctx->err) {

        // 处理连接失败

        return;

    }

    

    // 绑定异步事件

    redisLibeventAttach(ctx, base);

    redisAsyncCommand(ctx, async_callback, NULL, "GET key1");

    

    event_base_dispatch(base); // 进入事件循环

    event_base_free(base);

    redisAsyncFree(ctx);

}

 

 

场景:适用于非阻塞业务(如日志采集、消息队列消费),单线程可处理上万并发请求。

 

二、防击穿/雪崩核心方案

 

4. 布隆过滤器拦截无效请求

 

// 布隆过滤器(基于 Redis BitMap)

void bloom_filter_add(RedisPool* pool, const char* key) {

    hiredisContext* conn = get_connection(pool);

    // 使用 SHA-1 生成 2 个哈希值

    unsigned char hash1[20], hash2[20];

    SHA1((unsigned char*)key, strlen(key), hash1);

    SHA1((unsigned char*)key, strlen(key), hash2);

 

    // 计算两个 bit 位置(假设布隆长度 1e6,哈希数 2)

    long bit1 = ((((uint64_t)hash1[0]) << 56) | ((uint64_t)hash1[1] << 48) | ...) % 1000000;

    long bit2 = ((((uint64_t)hash2[0]) << 56) | ...) % 1000000;

 

    // 设置 bit 位

    redisCommand(conn, "SETBIT bloom_filter %ld 1", bit1);

    redisCommand(conn, "SETBIT bloom_filter %ld 1", bit2);

    sem_post(&pool->sem);

}

 

bool bloom_filter_check(RedisPool* pool, const char* key) {

    // 类似 add 逻辑计算 bit1/bit2

    // 检查两个 bit 位是否都为 1

    long res1 = (long)redisCommand(conn, "GETBIT bloom_filter %ld", bit1);

    long res2 = (long)redisCommand(conn, "GETBIT bloom_filter %ld", bit2);

    return res1 == 1 && res2 == 1;

}

 

 

参数:误判率建议设为 0.1%,布隆长度 =  (条目数 * -ln(误判率)) / (ln2)^2 。

 

5. 互斥锁(RedLock)防止缓存击穿

 

// RedLock 实现(简化版)

#define LOCK_EXPIRE 5000 // 锁过期时间(ms)

#define LOCK_RETRY 500 // 重试间隔(ms)

 

bool redlock_acquire(RedisPool* pool, const char* lock_key, const char* client_id) {

    time_t start = time(NULL);

    while (time(NULL) - start < LOCK_EXPIRE) {

        // 尝试获取锁(NX+PX 保证原子性)

        hiredisContext* conn = get_connection(pool);

        redisReply* reply = (redisReply*)redisCommand(conn, 

            "SET %s %s NX PX %d", lock_key, client_id, LOCK_EXPIRE);

        sem_post(&pool->sem);

        

        if (reply && reply->type == REDIS_REPLY_STRING && strcmp(reply->str, "OK") == 0) {

            freeReplyObject(reply);

            return true;

        }

        freeReplyObject(reply);

        usleep(LOCK_RETRY * 1000); // 等待重试

    }

    return false;

}

 

void redlock_release(RedisPool* pool, const char* lock_key, const char* client_id) {

    hiredisContext* conn = get_connection(pool);

    // 验证客户端 ID 再释放(避免误删其他锁)

    redisCommand(conn, "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end",

        lock_key, client_id);

    sem_post(&pool->sem);

}

 

 

注意:RedLock 需至少 3 个 Redis 实例保证分布式锁可靠性,获取锁需超过半数节点成功。

 

6. 热点数据本地缓存(如 LRU 机制)

 

// 基于 std::unordered_map 的 LRU 缓存

struct LRUItem {

    std::string value;

    time_t timestamp;

};

 

class LRUCache {

private:

    std::unordered_map<std::string, LRUItem> cache;

    size_t max_size;

    time_t ttl; // 过期时间(秒)

 

public:

    LRUCache(size_t max_size, time_t ttl) : max_size(max_size), ttl(ttl) {}

 

    bool get(const std::string& key, std::string& value) {

        auto it = cache.find(key);

        if (it == cache.end()) return false;

        if (time(NULL) - it->second.timestamp > ttl) {

            cache.erase(it); // 过期淘汰

            return false;

        }

        // 更新访问时间

        it->second.timestamp = time(NULL);

        value = it->second.value;

        return true;

    }

 

    void set(const std::string& key, const std::string& value) {

        if (cache.size() >= max_size) {

            // 淘汰最久未使用项(简化实现,实际可用 list+map)

            auto oldest = std::min_element(cache.begin(), cache.end(),

                [](const auto& a, const auto& b) {

                    return a.second.timestamp < b.second.timestamp;

                });

            cache.erase(oldest);

        }

        cache[key] = {value, time(NULL)};

    }

};

 

// 使用示例

LRUCache local_cache(1000, 300); // 最多存 1000 条,5 分钟过期

std::string value;

if (!local_cache.get("hot_key", value)) {

    // 从 Redis 加载并写入本地缓存

    hiredisContext* conn = get_connection(pool);

    redisReply* reply = (redisReply*)redisCommand(conn, "GET hot_key");

    if (reply && reply->type == REDIS_REPLY_STRING) {

        value = reply->str;

        local_cache.set("hot_key", value);

    }

    freeReplyObject(reply);

    sem_post(&pool->sem);

}

 

 

场景:适合 QPS > 1w 的热点数据(如商品详情页),本地缓存命中率需维持在 80% 以上。

 

三、其他优化手段(附代码片段)

 

7. 避免大 Key(控制 Value 大小 < 100KB)

 

// 写入前校验 Value 大小

void check_big_value(const char* value, size_t max_size) {

    size_t len = strlen(value);

    if (len > max_size) {

        throw std::runtime_error("Value exceeds 100KB limit");

    }

}

 

 

8. 使用压缩编码(LZ4 压缩大 Value)

 

#include <lz4.h>

 

std::string compress_value(const std::string& raw) {

    char* compressed = (char*)malloc(LZ4_compressBound(raw.size()));

    int compressed_size = LZ4_compress_default(raw.data(), compressed, raw.size());

    std::string result(compressed, compressed_size);

    free(compressed);

    return result;

}

 

std::string decompress_value(const std::string& compressed, size_t original_size) {

    char* decompressed = (char*)malloc(original_size);

    int decompressed_size = LZ4_decompress_safe(compressed.data(), decompressed, compressed.size(), original_size);

    std::string result(decompressed, decompressed_size);

    free(decompressed);

    return result;

}

 

 

9. 异步淘汰过期 Key(定期删除 + 惰性删除)

 

// 定期删除任务(后台线程执行)

void async_expire_cleanup(RedisPool* pool) {

    while (true) {

        hiredisContext* conn = get_connection(pool);

        redisReply* reply = (redisReply*)redisCommand(conn, "SCAN 0 MATCH key:* EXIT 1000"); // 每次扫描 1000 个 Key

        if (reply->type == REDIS_REPLY_ARRAY && reply->elements == 2) {

            redisReply* keys_reply = reply->element[1];

            for (int i = 0; i < keys_reply->elements; i++) {

                std::string key = keys_reply->element[i]->str;

                redisReply* ttl_reply = (redisReply*)redisCommand(conn, "TTL %s", key.c_str());

                if (ttl_reply->integer < 0) continue; // 无过期时间

                if (ttl_reply->integer < 60) { // 临近过期时主动淘汰

                    redisCommand(conn, "DEL %s", key.c_str());

                }

                freeReplyObject(ttl_reply);

            }

        }

        freeReplyObject(reply);

        sem_post(&pool->sem);

        sleep(60); // 每分钟执行一次

    }

}

 

 

四、完整优化配置示例(redis.conf 对应项)

 

# 性能优化配置

tcp-backlog 511 # 优化 TCP 三次握手队列

tcp-keepalive 300 # 保持连接活性(秒)

hz 100 # 提高事件循环频率(默认 10,高负载设为 100)

aof-rewrite-incremental-fsync yes # AOF 重写期间优化 fsync

 

# 防击穿配置

maxmemory 2gb # 限制内存使用

maxmemory-policy allkeys-lru # 内存不足时淘汰策略

notify-keyspace-events Ex # 开启 Key 过期通知

 

 

关键指标监控代码

 

// 实时监控 Redis 状态

void monitor_redis(RedisPool* pool) {

    hiredisContext* conn = get_connection(pool);

    redisReply* reply = (redisReply*)redisCommand(conn, "INFO stats");

    if (reply->type == REDIS_REPLY_STRING) {

        std::string info = reply->str;

        // 解析 QPS:

        size_t pos = info.find("instantaneous_ops_per_sec:");

        if (pos != std::string::npos) {

            int qps = std::stoi(info.substr(pos + 21));

            if (qps > 10000) { // 阈值告警

                send_alert("Redis QPS 过高: " + std::to_string(qps));

            }

        }

    }

    freeReplyObject(reply);

    sem_post(&pool->sem);

}

 

 

注意事项

 

1. 线程安全: hiredis  非线程安全,连接池需配合锁/信号量使用。

2. 序列化:复杂对象需先序列化为 JSON/Protocol Buffers 再存入 Redis。

3. 压测工具:用  redis-benchmark  验证优化效果(如单实例 QPS 从 5w 提升至 8w)。

 

如需某方案的深度优化或特定场景扩展(如 Redis Cluster 分布式锁)。

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

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

相关文章

20250529-C#知识:索引器

C#知识&#xff1a;索引器 索引器给对象添加了索引访问的功能&#xff0c;实际访问的是对象的成员&#xff0c;感觉不太常用。 1、主要内容及代码示例 索引器中类似属性&#xff0c;也包含get和set方法索引器能够使像访问数组一样访问对象一般当类中有数组类型的成员变量时&am…

【笔记】suna部署之获取 Tavily API key

#工作记录 Tavily 注册 Tavily 账号5&#xff1a; 打开浏览器&#xff0c;访问 Tavily 官网Tavily AI。点击页面上的 “注册” 按钮&#xff0c;按照提示填写注册信息&#xff0c;如邮箱地址、设置密码等&#xff0c;完成注册流程。也可以选择使用 Google 或 GitHub 账号授权登…

06-Web后端基础(java操作数据库)

1. 前言 在前面我们学习MySQL数据库时&#xff0c;都是利用图形化客户端工具(如&#xff1a;idea、datagrip)&#xff0c;来操作数据库的。 我们做为后端程序开发人员&#xff0c;通常会使用Java程序来完成对数据库的操作。Java程序操作数据库的技术呢&#xff0c;有很多啊&a…

什么是单片机?

众所周知&#xff0c;人类行为受大脑调控&#xff0c;正如视觉、听觉、味觉、嗅觉、触觉及运动功能等感官与肢体活动均受其指挥&#xff1b;换言之&#xff0c;大脑作为人体的中枢神经系统&#xff0c;负责管理所有可控制的生理功能。 在电子设备领域&#xff0c;单片机…

Ubuntu的shell脚本

关于shell脚本 • shell脚本是文本的一种。 • shell脚本是可以运行的文本。 • shell脚本的内容是由说辑和数据组成。 • shell 脚本是解释型语言。 shell脚本存在的意义 Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具 Linux/UNIX系统…

从抄表到节能,电费管理系统如何重构公寓运营场景——仙盟创梦IDE

租房公寓电费管理系统是集智能计量、自动化计费、线上缴费、数据管理于一体的综合性解决方案&#xff0c;旨在解决传统电费管理中人工抄表误差大、收费效率低、纠纷频发等痛点。系统通过部署智能电表实时采集用电数据&#xff0c;结合云计算与大数据分析技术&#xff0c;实现电…

记一次前端逻辑绕过登录到内网挖掘

前言 在测试一个学校网站的时候&#xff0c;发现一个未授权访问内网系统&#xff0c;但是这个未授权并不是接口啥的&#xff0c;而是对前端 js 的审计和调试发现的漏洞&#xff0c;这里给大家分享一下这次的漏洞的过程。 进入内网的过程 可以看到是一个图书馆的网站&#xff…

Springboot 整合 WebSocket 实现聊天室功能

目录 前言一、WebSocket原理二、Spring Boot集成WebSocket2.1. 引入依赖2.2 配置类WebSocketConfig2.3 WebSocketServer 类2.4 前端代码 index.html2.5 Controller访问首页 前言 WebSocket概述&#xff1a; 在日常的web应用开发中&#xff0c;常见的是前端向后端发起请求&…

用 Trae IDE 打造一个桌面小爬虫:从 PyQt5 开始,轻松采集掘金首页内容

很多程序员都有这样的经历&#xff1a;刷掘金、看文章、找灵感、追热点。但你有没有想过&#xff0c;有一天让“爬虫”代替你去浏览这些内容&#xff1f;自动提取标题、作者、点赞数、评论数&#xff0c;一键生成你的专属“技术热点日报”。 今天我们就用 Trae IDE PyQt5 来完…

python和风api获取天气(JSON Web Token)

下载安装openssl 默认安装目录&#xff0c;添加C:\Program Files\OpenSSL-Win64\bin到用户Path环境变量 打开cmd&#xff0c;执行命令&#xff0c;会生成两个文件ed25519-private.pem&#xff0c;ed25519-public.pem openssl genpkey -algorithm ED25519 -out ed25519-privat…

52、C# 泛型 (Generics)

泛型是 C# 2.0 引入的一项强大功能&#xff0c;它允许你编写可以处理多种数据类型的代码&#xff0c;而无需为每种类型重复编写相同的逻辑。泛型提高了代码的重用性、类型安全性和性能。 基本概念 泛型类 public class GenericClass<T> {private T _value;public Gene…

Allegro X PCB设计小诀窍--05.如何在Allegro X中实现隐藏电源飞线效果

背景介绍&#xff1a;在PCB设计过程中&#xff0c;布线初期印制板上的飞线错综复杂&#xff0c;信号线和电源线混合交错&#xff0c;但是实际上对于多层板来说&#xff0c;电源的网络一般是通过电源层铺铜连接的&#xff0c;很少需要走线&#xff0c;这样混乱的情况会严重影响设…

一篇文章教会你ESP8266串口WIFI无线模块实现物联网无线收发,附STM32代码示例

目录 一、ESP-01S无线模块: &#xff08;1&#xff09;特点&#xff1a; &#xff08;2&#xff09;管脚定义&#xff1a; &#xff08;3&#xff09;启动模式&#xff1a; 二、ESP-01S出厂固件烧录&#xff1a; &#xff08;1&#xff09;引脚接线&#xff1a; &#xff0…

算法-基础算法

一、枚举算法 也称为穷举算法&#xff0c;指的是按照问题本身的性质&#xff0c;一一列举出该问题所有可能的解&#xff0c;并在逐一列举的过程中&#xff0c;将它们逐一与目标状态进行比较以得出满足问题要求的解。在列举的过程中&#xff0c;既不能遗漏也不能重复 1. 问题 …

Reactor模式详解:高并发场景下的事件驱动架构

文章目录 前言一、Reactor模式核心思想二、工作流程详解2.1 服务初始化阶段2.2 主事件循环2.3 子Reactor注册流程2.4 IO事件处理时序2.5 关键设计要点 三、关键实现技术四、实际应用案例总结 前言 在现代高性能服务器开发中&#xff0c;如何高效处理成千上万的并发连接是一个关…

项目日记 -Qt音乐播放器 -设置任务栏图标与托盘图标

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【Qt音乐播放器】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 代码仓库&#xff1a;MusicPlayer v1.0版视频展示&#xff1a;Qt -音乐播放器(仿网易云)V1.0 前言 本文的目标&#xff1a; 一是设置任务栏的图标&#xff0c; 二…

国产 BIM 软件万翼斗拱的技术突破与现实差距 —— 在创新与迭代中寻找破局之路

万翼斗拱在国产BIM领域迈出重要一步&#xff0c;凭借二三维一体化、参数化建模及AI辅助设计等功能形成差异化竞争力&#xff0c;在住宅设计场景中展现效率优势&#xff0c;但与国际主流软件相比&#xff0c;在功能完整性、性能稳定性和生态成熟度上仍有显著差距&#xff0c;需通…

Golang|etcd服务注册与发现 策略模式

etcd 是一个开源的 分布式键值存储系统&#xff08;Key-Value Store&#xff09;&#xff0c;主要用于配置共享和服务发现。 ETCD是一个键值&#xff08;KV&#xff09;数据库&#xff0c;类似于Redis&#xff0c;支持分布式集群。ETCD也可以看作是一个分布式文件系统&#xff…

STM32的OLED显示程序亲测可用:适用于多种场景的稳定显示解决方案

STM32的OLED显示程序亲测可用&#xff1a;适用于多种场景的稳定显示解决方案 【下载地址】STM32的OLED显示程序亲测可用 这是一套专为STM32设计的OLED显示程序&#xff0c;经过实际测试&#xff0c;运行稳定可靠。支持多种OLED屏幕尺寸和类型&#xff0c;提供丰富的显示效果&am…

【AI News | 20250529】每日AI进展

AI Repos 1、WebAgent 阿里巴巴通义实验室近日发布了WebDancer&#xff0c;一款旨在实现自主信息搜索的原生智能体搜索推理模型。WebDancer采用ReAct框架&#xff0c;通过分阶段训练范式&#xff0c;包括浏览数据构建、轨迹采样、监督微调和强化学习&#xff0c;赋予智能体自主…