Redis 从基础到实战
文章目录
- 一、Redis 是什么?为什么要用它?
- 二、第一步:Redis 安装(不同系统指南)
- 1. Ubuntu 系统
- 2. Mac 系统
- 三、核心:Redis 5 种基本数据类型及用法
- 1. String(字符串):最基础的键值对
- 2. Hash(哈希):存 “对象” 更方便
- 3. List(列表):有序的 “队列 / 栈”
- 4. Set(集合):自动去重的 “篮子”
- 5. ZSet(有序集合):带 “分数” 的排行榜
- 四、Redis 常用操作:连接、持久化、过期设置
- 1. Ubuntu 下 Redis 配置修改(可选)
- 2. 远程连接 Redis
- 3. 数据持久化(Ubuntu 默认配置)
- 4. 查看 / 删除键(通用命令)
- 五、实战:用 Redis 存用户缓存(C++ + Ubuntu 示例)
- 1. 第一步:Ubuntu 安装 hiredis 库
- 2. 完整代码示例
- 3. 编译与运行
一、Redis 是什么?为什么要用它?
Redis(Remote Dictionary Server)是一款开源的内存数据库,简单说就是 “把数据存在内存里,取数据超快” 的工具。它的核心优势很突出:
- 速度快:内存读写,每秒能处理十几万请求;
- 用途广:能做缓存、会话存储、消息队列、排行榜等;
- 支持多数据类型:不止存键值对,还能存列表、哈希、集合等。
日常开发中,比如 “用户登录后的会话信息存储”“商品详情页缓存(减少数据库压力)”“点赞排行榜”,都能用 Redis 轻松实现~
二、第一步:Redis 安装(不同系统指南)
Ubuntu 系统下用 apt 包管理器安装更高效:
1. Ubuntu 系统
# 1. 先更新apt包索引(确保能获取最新版本)
sudo apt update# 2. 安装Redis服务器和客户端
sudo apt install -y redis-server redis-tools# 3. 启动Redis服务并设置开机自启
sudo systemctl start redis-server
sudo systemctl enable redis-server # 开机自动启动# 4. 验证Redis是否启动成功(返回PONG表示正常)
redis-cli ping# 5. 连接Redis(本地连接直接执行,默认无密码)
redis-cli
2. Mac 系统
直接用 Homebrew,一行搞定:
# 安装
brew install redis# 启动
brew services start redis# 连接
redis-cli
三、核心:Redis 5 种基本数据类型及用法
Redis 最常用的就是这 5 种数据类型,每种都有特定场景,记清 “是什么 + 怎么用 + 用在哪” 就够了。
1. String(字符串):最基础的键值对
作用:存单个值(文本、数字都可以),比如验证码、计数器。
常用命令:
# 存值:SET 键 值
SET code "123456" # 存验证码# 取值:GET 键
GET code # 返回 "123456"# 数字自增(比如统计访问量)
SET view 0
INCR view # view变成1
INCR view # view变成2# 设置过期时间(比如验证码10分钟失效)
SET code "654321" EX 600 # EX表示秒,600秒=10分钟
场景:存储短信验证码、用户 ID 对应的昵称、接口访问计数器。
2. Hash(哈希):存 “对象” 更方便
作用:类似 JSON 对象,键里套 “字段 - 值”,适合存结构化数据(比如用户信息)。
常用命令:
# 存对象:HSET 键 字段1 值1 字段2 值2
HSET user:100 name "张三" age 25 gender "男"# 取单个字段:HGET 键 字段
HGET user:100 name # 返回 "张三"# 取所有字段和值:HGETALL 键
HGETALL user:100 # 返回 ["name","张三","age","25","gender","男"]# 删字段:HDEL 键 字段
HDEL user:100 gender
场景:存储用户信息、商品详情(比如商品名、价格、库存)。
3. List(列表):有序的 “队列 / 栈”
作用:按插入顺序排序的字符串集合,可从两端操作,能当队列(先进先出)或栈(先进后出)。
常用命令:
# 从左边加元素(类似栈的push)
LPUSH msg "早安"
LPUSH msg "午安" # 此时列表是 ["午安","早安"]# 从右边加元素
RPUSH msg "晚安" # 列表变成 ["午安","早安","晚安"]# 从左边删元素(类似栈的pop)
LPOP msg # 删除并返回 "午安",列表剩 ["早安","晚安"]# 查看列表内容:LRANGE 键 起始索引 结束索引(-1表示最后一个)
LRANGE msg 0 -1 # 返回 ["早安","晚安"]
场景:消息队列(比如订单通知)、最新消息列表(比如朋友圈点赞列表)。
4. Set(集合):自动去重的 “篮子”
作用:无序的字符串集合,自动去重,还能做交集、并集(比如找共同好友)。
常用命令:
# 加元素:SADD 键 元素1 元素2
SADD user:100:tags "篮球" "游戏" "音乐"
SADD user:100:tags "游戏" # 重复元素,不会新增# 看所有元素:SMEMBERS 键
SMEMBERS user:100:tags # 返回 ["篮球","游戏","音乐"](顺序不固定)# 判重:SISMEMBER 键 元素(存在返回1,不存在0)
SISMEMBER user:100:tags "足球" # 返回 0# 交集(找共同标签,比如user100和user200的共同爱好)
SADD user:200:tags "游戏" "电影"
SINTER user:100:tags user:200:tags # 返回 ["游戏"]
场景:用户标签(去重)、共同好友 / 关注、抽奖(随机取元素)。
5. ZSet(有序集合):带 “分数” 的排行榜
作用:在 Set 基础上给每个元素加 “分数”,按分数排序,天生适合做排行榜。
常用命令:
# 加元素:ZADD 键 分数1 元素1 分数2 元素2
ZADD rank:article 100 "文章A" 200 "文章B" 150 "文章C"# 按分数升序看:ZRANGE 键 起始 结束(WITHSCORES显示分数)
ZRANGE rank:article 0 -1 WITHSCORES # 返回 ["文章A",100,"文章C",150,"文章B",200]# 按分数降序看(排行榜常用)
ZREVRANGE rank:article 0 -1 WITHSCORES # 返回 ["文章B",200,"文章C",150,"文章A",100]# 改分数(比如文章A点赞数+10)
ZINCRBY rank:article 10 "文章A" # 文章A分数变成110
场景:文章点赞排行榜、用户积分排行榜、直播礼物榜。
四、Redis 常用操作:连接、持久化、过期设置
1. Ubuntu 下 Redis 配置修改(可选)
默认 Redis 仅允许本地连接,若需远程访问,可修改配置文件:
# 1. 用vim打开Redis配置文件
sudo vim /etc/redis/redis.conf# 2. 找到并修改以下内容:
# (1)注释掉bind 127.0.0.1(允许所有IP访问)
# bind 127.0.0.1 ::1
# (2)关闭保护模式(允许远程连接)
protected-mode no
# (3)(可选)设置密码(找到requirepass,去掉#并设置密码)
requirepass your_password # 替换成你的密码# 3. 保存退出后重启Redis服务
sudo systemctl restart redis-server
2. 远程连接 Redis
# 格式:redis-cli -h 服务器IP -p 端口 -a 密码
redis-cli -h 192.168.1.100 -p 6379 -a your_password
3. 数据持久化(Ubuntu 默认配置)
Ubuntu 下安装的 Redis 默认开启 RDB 持久化,AOF 需手动开启:
- 打开配置文件
sudo vim /etc/redis/redis.conf; - 找到
appendonly no,改为appendonly yes; - 重启服务
sudo systemctl restart redis-server。
4. 查看 / 删除键(通用命令)
# 查看所有键(生产环境少用,会阻塞)
KEYS * # 返回所有键名# 查看键类型
TYPE user:100 # 返回 hash# 删除键
DEL code # 删除code这个键
五、实战:用 Redis 存用户缓存(C++ + Ubuntu 示例)
Ubuntu 下用 hiredis 库操作 Redis 更便捷,下面实现 “先查缓存、再查数据库” 逻辑,减少数据库压力。
1. 第一步:Ubuntu 安装 hiredis 库
五、实战:用 Redis 存用户缓存(C+++Ubuntu 示例)
Ubuntu 下用hiredis库操作 Redis 更便捷,下面实现 “先查缓存、再查数据库” 逻辑,减少数据库压力。
1. 第一步:Ubuntu 安装 hiredis 库
2. 完整代码示例
#include <iostream>
#include <string>
#include <unordered_map>
#include <hiredis/hiredis.h> // 引入hiredis头文件// 1. 封装Redis连接工具(自动释放连接,避免内存泄漏)
class RedisClient {
private:redisContext* conn; // Redis连接句柄
public:// 构造函数:连接Redis(支持带密码连接)RedisClient(const std::string& host, int port, const std::string& password = "") {// 建立连接conn = redisConnect(host.c_str(), port);if (conn == nullptr || conn->err) {if (conn) {std::cerr << "Redis连接失败:" << conn->errstr << std::endl;redisFree(conn); // 释放连接conn = nullptr;} else {std::cerr << "Redis连接失败:无法创建连接对象" << std::endl;}return;}// 如果有密码,执行AUTH认证if (!password.empty()) {redisReply* reply = (redisReply*)redisCommand(conn, "AUTH %s", password.c_str());if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {std::cerr << "Redis认证失败:" << (reply ? reply->str : "未知错误") << std::endl;freeReplyObject(reply); // 释放回复内存redisFree(conn);conn = nullptr;return;}freeReplyObject(reply); // 释放认证回复}std::cout << "Redis连接成功!" << std::endl;}// 析构函数:释放连接~RedisClient() {if (conn != nullptr) {redisFree(conn);conn = nullptr;}}// 执行Redis命令(返回回复,调用者需手动释放freeReplyObject)redisReply* executeCommand(const char* format, ...) {if (conn == nullptr) {std::cerr << "Redis连接未初始化,无法执行命令" << std::endl;return nullptr;}va_list args;va_start(args, format);redisReply* reply = (redisReply*)redisvCommand(conn, format, args);va_end(args);if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {std::cerr << "命令执行失败:" << (reply ? reply->str : "未知错误") << std::endl;if (reply) {freeReplyObject(reply);return nullptr;}}return reply;}// 判断连接是否有效bool isConnected() const {return conn != nullptr;}
};// 2. 模拟从数据库查询用户信息(实际项目替换为SQL查询)
std::unordered_map<std::string, std::string> get_user_from_db(int user_id) {// 模拟数据库中的用户数据std::unordered_map<std::string, std::string> user_info;user_info["name"] = "李四";user_info["age"] = "30";user_info["phone"] = "13800138000";std::cout << "从数据库获取用户信息:" << std::endl;return user_info;
}// 3. 核心逻辑:先查缓存,再查数据库,查到后存缓存
std::unordered_map<std::string, std::string> get_user(RedisClient& redis_client, int user_id) {// 缓存键名规范:业务:ID:类型(如user:101:info)std::string cache_key = "user:" + std::to_string(user_id) + ":info";std::unordered_map<std::string, std::string> user_info;// 第一步:查询缓存(HGETALL获取哈希所有字段和值)redisReply* reply = redis_client.executeCommand("HGETALL %s", cache_key.c_str());if (reply != nullptr) {// HGETALL返回的是数组,长度为2*N(字段1、值1、字段2、值2...)if (reply->type == REDIS_REPLY_ARRAY && reply->elements > 0) {for (size_t i = 0; i < reply->elements; i += 2) {std::string field = reply->element[i]->str;std::string value = reply->element[i+1]->str;user_info[field] = value;}freeReplyObject(reply); // 释放回复内存std::cout << "从缓存获取用户信息:" << std::endl;return user_info;}freeReplyObject(reply); // 释放空回复}// 第二步:缓存未命中,查询数据库user_info = get_user_from_db(user_id);// 第三步:将数据库数据存入缓存(HSET+EXPIRE设置1小时过期)if (!user_info.empty() && redis_client.isConnected()) {// 执行HSET命令:HSET user:101:info name "李四" age "30" phone "13800138000"redisReply* hset_reply = redis_client.executeCommand("HSET %s name %s age %s phone %s",cache_key.c_str(),user_info["name"].c_str(),user_info["age"].c_str(),user_info["phone"].c_str());if (hset_reply != nullptr) {freeReplyObject(hset_reply); // 释放HSET回复}// 设置缓存过期时间(3600秒=1小时,避免数据过时)redisReply* expire_reply = redis_client.executeCommand("EXPIRE %s 3600", cache_key.c_str());if (expire_reply != nullptr) {freeReplyObject(expire_reply); // 释放EXPIRE回复}std::cout << "用户信息已存入缓存(1小时过期)" << std::endl;}return user_info;
}// 4. 打印用户信息(辅助函数)
void print_user_info(const std::unordered_map<std::string, std::string>& user_info) {for (const auto& [field, value] : user_info) {std::cout << field << ": " << value << " ";}std::cout << std::endl;
}int main() {// 初始化Redis客户端(Ubuntu本地连接,默认port=6379,密码填你设置的,无则空)RedisClient redis_client("127.0.0.1", 6379, "your_password");if (!redis_client.isConnected()) {return 1; // 连接失败,退出程序}// 测试1:第一次查询(缓存未命中,走数据库)std::cout << "=== 第一次查询用户101 ===" << std::endl;auto user1 = get_user(redis_client, 101);print_user_info(user1);// 测试2:第二次查询(缓存命中,走缓存)std::cout << "\n=== 第二次查询用户101 ===" << std::endl;auto user2 = get_user(redis_client, 101);print_user_info(user2);return 0;
}
3. 编译与运行
# 1. 保存代码为redis_user_cache.cpp
# 2. 编译(链接hiredis库,-lhiredis不可少)
g++ redis_user_cache.cpp -o redis_user_cache -lhiredis
# 3. 运行程序(若设置了Redis密码,需在代码中填对)
./redis_user_cache
运行结果
Redis连接成功!
=== 第一次查询用户101 ===
从数据库获取用户信息:
用户信息已存入缓存(1小时过期)
name: 李四 age: 30 phone: 13800138000 === 第二次查询用户101 ===
从缓存获取用户信息:
name: 李四 age: 30 phone: 13800138000
可以看到:第一次查询走数据库并写入缓存,第二次直接从缓存读取,完美实现 “缓存优先” 逻辑。
