【请关注】关于VC++实现使用Redis不同方法,有效达到 Redis 性能优化、防击穿
麻烦先关注方便了解最新分享。
关于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 分布式锁)。
相关文章:
【请关注】关于VC++实现使用Redis不同方法,有效达到 Redis 性能优化、防击穿
麻烦先关注方便了解最新分享。 关于VC实现使用Redis不同方法,有效达到 Redis 性能优化、防击穿。 一、性能优化核心方法 1. 使用连接池复用连接 // 连接池结构体 struct RedisPool { hiredisContext** connections; int size; sem_t sem; // 信号量控制并发 }; //…...
【加密算法】
计算机网络加密算法详解 在计算机网络中,加密算法是保障数据安全的核心技术,用于防止数据在传输过程中被窃听、篡改或伪造。本文将详细介绍常见的加密算法分类、工作原理及其在网络中的应用。 1. 加密算法分类 加密算法主要分为以下三类: 类型特点典型算法对称加密加密和解…...

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

【笔记】suna部署之获取 Tavily API key
#工作记录 Tavily 注册 Tavily 账号5: 打开浏览器,访问 Tavily 官网Tavily AI。点击页面上的 “注册” 按钮,按照提示填写注册信息,如邮箱地址、设置密码等,完成注册流程。也可以选择使用 Google 或 GitHub 账号授权登…...

06-Web后端基础(java操作数据库)
1. 前言 在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。 我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库的技术呢,有很多啊&a…...
JavaScript性能优化实战的技术文-——仙盟创梦IDE
JavaScript性能优化实战技术文章大纲 性能优化的核心原则 减少代码执行时间降低内存消耗优化网络请求提升用户体验 代码层面的优化 减少全局变量使用,避免命名冲突和内存泄漏使用节流(throttle)和防抖(debounce)优…...
GitHub Copilot 使用手册与原理解析
一、Copilot 简介 GitHub Copilot 是由 GitHub 联合 OpenAI 推出的 AI 编程助手。它能在你编程时,基于上下文智能补全代码、自动生成函数、编写测试用例、解释代码含义等,大幅提高编程效率。 即便有 Google、Amazon、Meta 等强劲竞争对手推出免费工具&…...
vllm 2080TI ubuntu环境安装
#TOC 欢迎使用Markdown编辑器安装gcc 9 sudo add-apt-repository ppa:ubuntu-toolchain-r/testsudo apt-get install gcc-9 g-9sudo apt-get install gcc-9 g-9sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g g /usr/bin/g-9gcc …...
【C语言】指针详解(接)
前言: 文接上章,在上章节讲解了部分指针知识点,在本章节为大家继续提供。 六指针与字符串:C 语言字符串的本质 在 C 语言中,字符串实际上是一个以\0结尾的字符数组。字符串常量本质上是指向字符数组首元素的指针&…...
【C】箭头运算符
在C语言中,p_tone->power_off 是一种通过指针访问结构体成员的方法,称为箭头运算符(->)。它主要用于以下场景: 1. 语法解释 p_tone:是一个指向结构体(或联合体)的指针。powe…...
HTTP Accept简介
一、HTTP Accept是什么 HTTP协议是一个客户端和服务器之间进行通信的标准协议,它定义了发送请求和响应的格式。而HTTP Accept是HTTP协议中的一个HTTP头部,用于告诉服务器请求方所期望的响应格式。这些格式可以是媒体类型、字符集、语言等信息。 HTTP A…...

什么是单片机?
众所周知,人类行为受大脑调控,正如视觉、听觉、味觉、嗅觉、触觉及运动功能等感官与肢体活动均受其指挥;换言之,大脑作为人体的中枢神经系统,负责管理所有可控制的生理功能。 在电子设备领域,单片机…...
联通专线加持!亿林网络 24 核 32G 裸金属服务器,千兆共享带宽适配中小型企业 IT 架构
在当今数字化时代,企业的业务运营越来越依赖高效、稳定的 IT 架构。对于中小型企业而言,如何在有限的预算内构建强大且可靠的 IT 基础设施,是一项关键挑战。亿林网络推出的 24 核 32G 裸金属服务器,搭配联通专线和千兆共享带宽&am…...

Ubuntu的shell脚本
关于shell脚本 • shell脚本是文本的一种。 • shell脚本是可以运行的文本。 • shell脚本的内容是由说辑和数据组成。 • shell 脚本是解释型语言。 shell脚本存在的意义 Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具 Linux/UNIX系统…...
微信小程序页面嵌套web-view点击系统导航返回时进行弹窗处理
实现效果:微信小程序页面嵌套web-view点击系统导航返回时进行弹窗处理 首先在web-view里是不可实现的(据我了解下来) 参考小程序文档:page-container 大致逻辑: 1、page-container可实现页面离开前拦截 2、由于web-vie…...

从抄表到节能,电费管理系统如何重构公寓运营场景——仙盟创梦IDE
租房公寓电费管理系统是集智能计量、自动化计费、线上缴费、数据管理于一体的综合性解决方案,旨在解决传统电费管理中人工抄表误差大、收费效率低、纠纷频发等痛点。系统通过部署智能电表实时采集用电数据,结合云计算与大数据分析技术,实现电…...
Rust 学习笔记:关于闭包的练习题
Rust 学习笔记:关于闭包的练习题 Rust 学习笔记:关于闭包的练习题问题 1问题 2以下程序能否通过编译?若能,输出是?以下程序能否通过编译?若能,输出是?考虑该 API,空白处填…...

记一次前端逻辑绕过登录到内网挖掘
前言 在测试一个学校网站的时候,发现一个未授权访问内网系统,但是这个未授权并不是接口啥的,而是对前端 js 的审计和调试发现的漏洞,这里给大家分享一下这次的漏洞的过程。 进入内网的过程 可以看到是一个图书馆的网站ÿ…...
有什么excel.js支持IE11,可以显示EXCEL单元格数据,支持单元格合并,边框线,单元格背景
有什么excel.js支持IE11,可以显示EXCEL单元格数据,支持单元格合并,边框线,单元格背景 在 IE11 环境下操作 Excel 且需要支持单元格合并、边框、背景等格式时,需考虑兼容性较强的库。以下是符合需求的工具及对比分析: 1. SheetJS(js-xlsx&am…...
x86 与 ARM 汇编深度对比:聚焦 x86 汇编的独特魅力
一、引言 汇编语言是硬件与软件的桥梁,x86 和 ARM 作为两大主流架构,其汇编语言在设计理念、指令集、编程风格上差异显著。本文以 x86 汇编为核心,结合与 ARM 的对比,解析 x86 汇编的技术细节与应用场景,助力开发者深…...

Springboot 整合 WebSocket 实现聊天室功能
目录 前言一、WebSocket原理二、Spring Boot集成WebSocket2.1. 引入依赖2.2 配置类WebSocketConfig2.3 WebSocketServer 类2.4 前端代码 index.html2.5 Controller访问首页 前言 WebSocket概述: 在日常的web应用开发中,常见的是前端向后端发起请求&…...

用 Trae IDE 打造一个桌面小爬虫:从 PyQt5 开始,轻松采集掘金首页内容
很多程序员都有这样的经历:刷掘金、看文章、找灵感、追热点。但你有没有想过,有一天让“爬虫”代替你去浏览这些内容?自动提取标题、作者、点赞数、评论数,一键生成你的专属“技术热点日报”。 今天我们就用 Trae IDE PyQt5 来完…...

python和风api获取天气(JSON Web Token)
下载安装openssl 默认安装目录,添加C:\Program Files\OpenSSL-Win64\bin到用户Path环境变量 打开cmd,执行命令,会生成两个文件ed25519-private.pem,ed25519-public.pem openssl genpkey -algorithm ED25519 -out ed25519-privat…...
模板应用更新同步到所有开发中的应用
需求是为多个 Vue 3 应用方便地同步模板更新,并且模板自身也可能演进,采用 Git 上游仓库 (Upstream) 策略。这种方法在操作上相对直观,更贴近常规的 Git 工作流,并且能较好地处理模板更新中可能涉及到的配置文件、依赖项以及 Vue …...
git和gitee的常用语句命令
Git 和 Gitee 常用命令及语法规则 一、Git 基础配置与初始化 在使用 Git 进行版本控制之前,需要进行用户签名的配置。此操作只需执行一次即可生效。 git config --global user.name "用户名" # 设置用户名 git config --global user.email "邮箱…...

52、C# 泛型 (Generics)
泛型是 C# 2.0 引入的一项强大功能,它允许你编写可以处理多种数据类型的代码,而无需为每种类型重复编写相同的逻辑。泛型提高了代码的重用性、类型安全性和性能。 基本概念 泛型类 public class GenericClass<T> {private T _value;public Gene…...
理解 Vue 2 的响应式原理:数据劫持与依赖收集的背后
在Vue2中,响应式系统是一切魔法的源头,无论是模板中的数据绑定,还是computed,watch的精准监听,都离不开Vue背后的响应式机制,本文将从源码角度出发,结合实例,深入剖析vue2是如何通过数据劫持(Object.defineProperty)和依赖收集实现响应式的 一.Vue2响应式系统基本原理 vue2中…...
深入理解 Pinia:Vue 状态管理的革新与实践
深入理解 Pinia:Vue 状态管理的革新与实践 一、引言 在 Vue.js 应用开发中,状态管理是构建复杂应用的关键环节。Pinia 作为新一代 Vue 状态管理库,凭借其简洁的 API 设计、强大的开发体验和良好的性能表现,逐渐成为 Vue 开发者的…...
Dubbo高频面试题
引言 作为分布式服务框架的标杆,Dubbo凭借其高性能RPC通信、灵活的服务治理能力和丰富的容错机制,成为Java技术栈中微服务领域的核心考点。本文系统梳理Dubbo高频面试核心知识点,涵盖容错策略、负载均衡、注册中心原理、服务上下线感知等关键…...

Allegro X PCB设计小诀窍--05.如何在Allegro X中实现隐藏电源飞线效果
背景介绍:在PCB设计过程中,布线初期印制板上的飞线错综复杂,信号线和电源线混合交错,但是实际上对于多层板来说,电源的网络一般是通过电源层铺铜连接的,很少需要走线,这样混乱的情况会严重影响设…...