Redis自增计数

Redis自增计数

引言

浏览量、点赞数、库存扣减、日活统计这类计数场景,用Redis的INCR原子自增比MySQL计数器性能高得多。单条命令在内存里完成加一,天然原子,并发下不会丢计数,是高并发计数的首选方案。

INCR 自增一

INCR article:1001:views

键不存在先按0初始化再加一,返回自增后的最新值,原子操作,并发安全。

INCRBY 指定步长

INCRBY score:user:2001 10

一次加指定整数,适合批量加分、累计积分场景。

DECR 与 DECRBY 递减

DECR stock:item:88
DECRBY stock:item:88 5

库存扣减用递减,扣到0前配合判断,避免出现负数库存。

读取当前计数

GET article:1001:views

返回字符串形式的数字,业务层转整型使用。

计数加过期时间

INCR day:uv:20261022:user:3001
EXPIRE day:uv:20261022:user:3001 86400

日活、每日点击这类临时计数,自增后设置过期时间,自动清理不占内存。

PHP 代码示例

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$views = $redis->incr('article:1001:views');
echo '当前浏览量:' . $views;

PHP扩展里incr对应INCR,decr对应DECR,返回自增后整数。

注意事项

  • INCR只对字符串数字生效,非数字值会报错;
  • 计数要持久化到MySQL时定时批量回写,别每次都写库;
  • 计数器长期不清理会膨胀,临时计数务必加TTL;
  • 库存扣减要先判断再递减,或用Lua脚本保证判断与扣减原子;
  • 分布式下INCR是单key原子,跨多个key计数仍需事务或Lua;
  • 计数初始值可用SET先设定,避免从0起步不符合业务。

总结

INCR原子自增一,INCRBY按步长增加,DECR/DECRBY递减,GET读取当前值。浏览量、点赞、积分、库存等高并发计数用Redis内存操作替代MySQL计数器,性能提升明显。临时日活计数自增后配EXPIRE自动清理,重要计数定时批量回写数据库。库存扣减建议用Lua脚本保证判断与扣减的原子性,避免超卖。


emer 发布于  2026-10-22 07:12 

Redis删除键命令

Redis删除键命令

引言

清理缓存、下线业务时要删Redis键。DEL是基础删除,UNLINK异步删大键不阻塞主线程,别在线上KEYS加DEL一把删,那会把Redis打挂。

删除指定键

DEL user:1001

删除一个或多个键,返回删除个数,键不存在返回0。

一次删多个

DEL user:1001 user:1002 user:1003

跟多个键名,一次命令批量删,比循环DEL省网络往返。

异步删大键:UNLINK

UNLINK bigkey:list

大key(百万级list/hash)用UNLINK,后台异步释放内存,不阻塞主线程。

判断键是否存在再删

EXISTS user:1001
DEL user:1001

EXISTS返回1存在,删除前确认避免误删。

按模式删:别用KEYS

# 危险:线上禁用
# KEYS user:* | DEL

KEYS会遍历全库阻塞生产,按模式删除用SCAN渐进扫描再UNLINK。

删除整个库

FLUSHDB      # 清当前库
FLUSHALL     # 清所有库

高危命令,线上几乎不用,执行前再三确认。

注意事项

  • DEL是同步删除,大key会阻塞,大key用UNLINK;
  • 不要在生产用KEYS模式匹配删除,会卡死Redis;
  • 删除不影响持久化AOF/RDB里已写的文件,重启后残留;
  • 删错键没有回收站,重要数据先备份;
  • 集群下DEL只删当前节点,跨槽键要逐个节点删;
  • 定期删缓存要加随机TTL,别一次删一堆热点key。

总结

DEL删一个或多个键,UNLINK异步删大key不阻塞主线程,EXISTS先判断存在。线上禁止KEYS加DEL模式删除,要用SCAN渐进扫描。FLUSHDB清当前库、FLUSHALL清全库属高危操作。删除大key优先UNLINK,批量删缓存错开时间避免雪崩,删错无回收站需提前备份。


emer 发布于  2026-10-21 07:04 

Redis设置过期时间

Redis设置过期时间

引言

缓存键必须带TTL自动失效,否则热点数据永久驻留直到内存写满。EXPIRE设置秒级过期,PEXPIRE毫秒,SET组合NX/EX可一步完成写入带过期,是防止缓存永久堆积的基本功。

写入时直接带过期

SET code:1001 123456 EX 300 NX

EX 300表示5分钟后过期,NX只在键不存在时写入,一次命令完成防重复与失效。

给已有键补过期

EXPIRE code:1001 300

键存在返回1,键不存在返回0,只设置剩余存活秒数。

查看剩余存活

TTL code:1001

返回剩余秒数;-1表示永久不过期,-2表示键已不存在。

毫秒级过期

PEXPIRE code:1001 300000
PTTL code:1001

对验证码、限流等需要毫秒精度的场景使用。

取消过期变永久

PERSIST code:1001

移除TTL让键永久保留,需要长期数据时用。

绝对值过期

EXPIREAT code:1001 1760000000

以Unix时间戳设定具体到期时刻,适合定时失效场景。

注意事项

  • EXPIRE针对键整体,hash内部field不单独过期;
  • 覆盖写值(SET同名键)会清掉旧TTL,需重新设置;
  • TTL是相对当前时间,持久化恢复后仍按剩余时间走;
  • 分布式锁用EXPIRE要配合SET NX原子写入,别先SET再EXPIRE;
  • 大量键同时过期要错开时间,避免雪崩;
  • TTL返回-1和-2含义不同,排查时别混。

总结

SET加EX一步写入带秒级TTL,EXPIRE给已有键补过期,TTL查剩余秒数,PEXPIRE/PTTL用毫秒,PERSIST取消过期变永久,EXPIREAT按时间戳定点失效。注意覆盖写会清TTL需重设,hash字段无单独过期,分布式锁要原子NX加过期,批量过期错开避免雪崩。


emer 发布于  2026-10-20 07:20 

Redis查看键数量

Redis查看键数量

引言

排查内存占用、统计缓存规模、清理过期数据前,先要看清当前库有多少键、某前缀下有多少键。不同命令代价差别很大,生产环境绝对不能用KEYS全量扫描,否则会阻塞Redis。

统计当前库总键数:DBSIZE

DBSIZE

返回当前数据库的键总数,是O(1)操作,底层直接读计数器,生产环境可安全使用。

切换库再统计

SELECT 2
DBSIZE

Redis默认0到15共16个逻辑库,DBSIZE只统计当前选中的库。

统计指定前缀数量:SCAN配合

SCAN 0 MATCH user:* COUNT 100

SCAN游标式分批遍历,不会阻塞主线程,配合MATCH按前缀过滤,COUNT是每批建议数量而非精确数量。

错误做法:KEYS全扫

# 生产禁止
KEYS user:*

KEYS是O(n)一次性遍历全部键,键多时会长时间阻塞,线上直接用可能引发雪崩。

估算内存与键分布

INFO keyspace

一次性返回所有库的键数量、过期键比例、平均TTL,适合整体巡检。

按类型统计

SCAN 0 TYPE string COUNT 500

SCAN支持按数据类型过滤,筛选string、hash、list等。

脚本批量统计前缀

redis-cli --scan --pattern 'order:*' | wc -l

命令行用--scan流式遍历,管道交给wc计数,比KEYS安全。

注意事项

  • DBSIZE是O(1)放心用,KEYS是O(n)生产禁;
  • SCAN结果可能重复,业务去重;
  • DBSIZE不含已过期未清理键,近似看规模;
  • 多库要逐个SELECT统计;
  • 大库统计用INFO keyspace代替自己遍历;
  • 删前缀键也用SCAN加DEL,别配KEYS。

总结

DBSIZE O(1)看当前库总数,SELECT切库分别统计,指定前缀用SCAN游标遍历,生产禁用KEYS全扫,INFO keyspace看全库分布和TTL,命令行用--scan加wc计数。核心是别用阻塞命令,SCAN分批处理允许重复需去重,多库分别统计,清理键同样走SCAN。


emer 发布于  2026-10-20 07:13 

Redis哈希结构场景

Redis哈希结构场景

引言

Redis Hash(哈希)适合存储对象类数据,类似数据库单行记录。相比把对象序列化为JSON字符串存储,Hash可以单独读写某个字段,不用整体读取修改,适合用户资料、商品基础信息这类场景。

Hash基础命令

# 设置单个字段
HSET user:100 name "test" age 20

# 获取单个字段
HGET user:100 name

# 获取全部字段
HGETALL user:100

# 删除字段
HDEL user:100 age

PHP操作示例

<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$key = 'user:100';
$redis->hMSet($key,[
    'name'=>'admin',
    'phone'=>'13800138000'
]);
$info = $redis->hGetAll($key);
print_r($info);

适用与不适用场景

✅ 适合:用户资料、商品基础属性,需要局部更新字段

❌ 不适合:字段数量超大、海量对象;大量独立统计计数,推荐单独key

注意事项

Redis Hash有底层ziplist编码,字段少、值小的时候内存占用极低。当字段数量或者value超过阈值,会转为hash表,内存占用上升。不要在单个hash里面存放成千上万的字段。

总结

Hash是Redis高频数据结构,适合存储结构化小对象,可以局部读写字段,避免JSON字符串每次全量读写。根据业务字段数量合理选择,不要滥用。


emer 发布于  2026-10-18 07:17 

Redis分布式锁实战

Redis分布式锁实战

引言

多服务器部署项目时,多个服务实例同时操作同一资源,会出现并发争抢问题。Redis分布式锁可以保证同一时间只有一个请求执行临界代码,常用于订单扣库存、定时任务防重复执行场景。

实现原理

利用RedisSET key value NX EX命令:NX仅当key不存在才设置,EX设置过期时间。原子命令可以防止锁成功后服务宕机导致死锁。解锁时需要校验锁的值,防止误删除别人的锁。

PHP代码示例

<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$lockKey = 'order:lock:1001';
$lockVal = uniqid();
// 获取锁,过期10秒
$ok = $redis->set($lockKey,$lockVal,['NX','EX'=>10]);
if($ok){
    try{
        // 临界区业务代码
        echo "获取锁,执行业务";
    }finally{
        // lua脚本原子解锁,判断value一致才删除
        $lua = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end';
        $redis->eval($lua,[$lockKey,$lockVal],1);
    }
}else{
    echo "获取锁失败,资源被占用";
}

注意事项

  1. 锁必须设置过期时间,防止程序崩溃死锁;
  2. 解锁必须用Lua脚本,保证判断和删除原子操作;
  3. 业务执行时间不能超过锁过期时间,可增加锁续期逻辑;
  4. 单机Redis存在单点故障,高可用场景用Redis集群或Redlock。

总结

Redis分布式锁简单易用,适合中小型Web项目并发控制。理解原子操作、锁过期、安全解锁三个关键点,避免网上简易版本带来的并发漏洞。


emer 发布于  2026-10-18 07:11 

Redis缓存穿透、击穿、雪崩区别和解决

前言

用Redis做缓存,经常听到三个词:缓存穿透、缓存击穿、缓存雪崩。很多人搞不清区别,本文一次讲明白,附解决方法。

一、三个概念对比

问题 是什么 谁在访问 影响
缓存穿透 查一个不存在的数据,缓存没有,每次都去查数据库 恶意攻击/大量无效请求 数据库压力大
缓存击穿 一个热点key突然过期,大量请求同时打过去 正常用户 数据库瞬间压力大
缓存雪崩 大量key同时过期,或者Redis挂了 正常用户 数据库直接崩

二、缓存穿透

是什么

黑客故意查一个数据库里根本不存在的数据,缓存里也没有,每次请求都打到数据库。

例子: 查用户ID=-1,数据库里没有,缓存里也没有,每次都查数据库。

危害

数据库被大量无效请求打挂。

解决方法

方法1:缓存空值

数据库查不到,也往缓存里写一个空值,设个短过期时间。

// 先查缓存
$user = $redis->get("user:$id");

if ($user === false) {
    // 查数据库
    $user = mysqli_query("SELECT * FROM users WHERE id = $id");

    if (!$user) {
        // 查不到也缓存,设60秒过期
        $redis->setex("user:$id", 60, "null");
    } else {
        $redis->setex("user:$id", 3600, $user);
    }
}

方法2:布隆过滤器

在缓存前面加一层布隆过滤器,快速判断这个ID存在不存在。

不存在的直接返回,不用查数据库。

方法3:接口校验

在接口层就做参数校验,比如用户ID必须大于0,不合法的直接拒绝。

三、缓存击穿

是什么

一个热点key(比如首页热点新闻),突然过期了,这时候刚好有大量用户同时访问,所有请求都打到数据库。

和穿透的区别: 这个key本来是存在的,只是过期了。

危害

数据库瞬间压力暴增。

解决方法

方法1:互斥锁

只让一个线程去查数据库,其他线程等一下。

$lockKey = "lock:user:$id";

// 尝试加锁
if ($redis->setnx($lockKey, 1)) {
    // 加锁成功,查数据库
    $user = mysqli_query("SELECT * FROM users WHERE id = $id");
    $redis->setex("user:$id", 3600, $user);
    $redis->del($lockKey);
} else {
    // 加锁失败,等一下再读缓存
    sleep(100);
    $user = $redis->get("user:$id");
}

方法2:热点key不过期

热点数据不设过期时间,后台定时更新。

方法3:提前预热

在热点数据快过期前,主动后台更新缓存。

四、缓存雪崩

是什么

大量key在同一时间过期,或者Redis整个挂了,所有请求都打到数据库。

和击穿的区别:

  • 击穿:一个key过期
  • 雪崩:大量key同时过期

危害

数据库直接被打挂,整个服务不可用。

解决方法

方法1:过期时间加随机值

不要所有key都设3600秒过期,加个随机数错开。

// 基础3600秒,加0-600秒随机
$expire = 3600 + rand(0, 600);
$redis->setex("user:$id", $expire, $data);

方法2:Redis集群

Redis挂了还有备用的,不要单点。

方法3:服务降级

数据库扛不住的时候,直接返回默认数据或者报错,不要让请求都打过去。

方法4:热点数据永不过期

核心数据不设过期,后台异步更新。

五、总结对比

问题 原因 解决
缓存穿透 查不存在的数据 缓存空值 + 布隆过滤器
缓存击穿 热点key过期 互斥锁 + 热点永不过期
缓存雪崩 大量key同时过期 过期时间加随机 + Redis集群

六、常见坑

坑1:把穿透和击穿搞混

  • 穿透:数据本来就不存在
  • 击穿:数据存在,但缓存过期了

坑2:缓存空值不设过期时间

如果不设过期时间,以后这个数据真的有了,缓存还是空的。

解决: 空值也要设过期时间(比如60秒)。

坑3:互斥锁死锁

加了锁没释放,其他线程一直等。

解决: 锁也要设过期时间。

坑4:随机值加太小

所有key还是差不多同时过期。

建议: 随机范围至少几百秒。

总结

记住:

  1. 穿透:查不存在的数据 → 缓存空值 + 布隆过滤器
  2. 击穿:热点key过期 → 互斥锁 + 热点永不过期
  3. 雪崩:大量key同时过期 → 过期时间加随机 + Redis集群

最简单的防雪崩:

$expire = 3600 + rand(0, 600);
$redis->setex($key, $expire, $data);

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:47 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,恶意请求能把数据库打挂。

1. 什么是缓存穿透

现象: 请求的数据在缓存和数据库中都不存在,每次请求都直接打到数据库。

危害:

  • 恶意攻击时数据库压力大
  • 正常请求被拖慢
  • 数据库CPU飙升

2. 为什么会穿透

  • 查询一个不存在的ID
  • 缓存没有命中
  • 数据库也查不到
  • 请求直接穿透到数据库

3. 解决方案一:缓存空值

思路: 即使数据库查不到,也在Redis存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";
    $user = $redis->get($key);

    if ($user !== false) {
        // 空值标记
        if ($user === 'null') {
            return null;
        }
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 存在就缓存1小时
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 不存在也缓存,5分钟过期
        $redis->setex($key, 300, 'null');
    }

    return $user;
}

优点: 简单有效
缺点: 占内存,可能被恶意key刷爆

4. 解决方案二:布隆过滤器

思路: 在缓存前面加一层布隆过滤器,不存在的key直接拦截。

原理:

  • 所有存在的数据先存到布隆过滤器
  • 请求先过布隆过滤器
  • 不存在直接返回,不查缓存和数据库

PHP 示例(用 Redis 的布隆过滤器模块):

# 加载 RedisBloom 模块
redis-cli BF.ADD user:1
redis-cli BF.EXISTS user:1
function getUser($id) {
    $key = "user:$id";

    // 先过布隆过滤器
    if (!$redis->rawCommand('BF.EXISTS', 'user_filter', $id)) {
        return null; // 一定不存在
    }

    // 布隆说可能存在,再查缓存
    $user = $redis->get($key);
    if ($user) {
        return json_decode($user, true);
    }

    // 查数据库...
}

优点: 内存占用小,效率高
缺点: 有误判率,实现复杂

5. 解决方案三:参数校验

思路: 从源头过滤非法请求。

function getUser($id) {
    // 参数校验
    if ($id <= 0 || !is_numeric($id)) {
        return null;
    }

    // 正常查询逻辑...
}

适用: ID类查询,不合法直接拦截

6. 三种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 占内存 数据量不大
布隆过滤器 高效 误判,复杂 数据量大
参数校验 最直接 只防非法 ID类请求

7. 生产建议

1)组合使用

  • 先做参数校验
  • 再缓存空值
  • 大流量用布隆过滤器

2)空值过期时间要短

  • 5分钟到10分钟
  • 不要太长

3)监控异常请求

  • 发现大量404要警惕
  • 及时加防护

4)接口限流

  • 用 Nginx 或网关限流
  • 防止恶意刷

8. 缓存雪崩和击穿

缓存穿透: 查不存在的数据
缓存击穿: 热点key过期,大量请求打数据库
缓存雪崩: 大量key同时过期

区分清楚: 三个问题现象类似但原因不同,解决方案也不同。

总结

Redis 缓存穿透解决要点:

  • 查不存在的数据穿透到数据库
  • 缓存空值是最简单方案
  • 布隆过滤器适合大数据量
  • 参数校验从源头拦截
  • 空值过期时间要短
  • 生产建议组合使用
  • 注意和雪崩、击穿区分开
  • 监控异常请求
  • 接口加限流

缓存穿透是面试高频题,也是生产常见问题,一定要掌握。


emer 发布于  2026-10-9 07:36 

Redis 持久化机制

Redis 持久化机制

Redis 是内存数据库,断电数据就没了,所以需要持久化。

1. 为什么要持久化

  • Redis 数据在内存中
  • 服务器断电/重启数据丢失
  • 持久化把数据存到磁盘
  • 重启后可以恢复数据

2. RDB 快照

原理: 定时把内存数据全量保存到磁盘。

优点:

  • 恢复速度快
  • 文件小
  • 适合备份

缺点:

  • 可能丢失最后一次快照后的数据
  • fork子进程可能阻塞

配置:

# redis.conf
save 900 1      # 900秒内1个key变化就保存
save 300 10     # 300秒内10个key变化
save 60 10000   # 60秒内10000个key变化

手动触发:

# 前台保存(阻塞)
redis-cli save

# 后台保存(不阻塞)
redis-cli bgsave

3. AOF 日志

原理: 记录每个写命令,重启时重放。

优点:

  • 数据更安全(最多丢1秒)
  • 日志可读

缺点:

  • 文件比 RDB 大
  • 恢复速度慢

配置:

# redis.conf
appendonly yes              # 开启AOF
appendfilename "appendonly.aof"

# 同步策略
appendfsync everysec       # 每秒同步(推荐)
# appendfsync always       # 每次写都同步
# appendfsync no           # 交给操作系统

AOF 重写:

# 手动触发AOF重写
redis-cli bgrewriteaof

4. RDB vs AOF 对比

对比项 RDB AOF
数据安全 可能丢数据 更安全
文件大小 小 大
恢复速度 快 慢
性能影响 fork阻塞 较小
适用场景 备份/容灾 数据安全要求高

5. 混合持久化

Redis 4.0+ 支持混合持久化:

  • AOF 重写时前半段用 RDB 格式
  • 后半段增量用 AOF 格式
  • 结合两者优点

配置:

aof-use-rdb-preamble yes

6. 生产建议

1)数据不敏感用 RDB

  • 缓存场景
  • 丢点数据没关系

2)数据重要用 AOF

  • 每秒同步
  • 最多丢1秒数据

3)生产环境推荐

  • 同时开 RDB + AOF
  • 混合持久化
  • 定期备份 RDB 文件

7. 数据恢复

RDB 恢复:

# 把 dump.rdb 放到数据目录
# 重启 Redis 自动加载

AOF 恢复:

# 开启 appendonly yes
# 重启自动重放AOF日志

8. 常见问题

1)fork 阻塞怎么办

  • 数据量大 fork 慢
  • 用 AOF 代替
  • 减少单实例数据量

2)AOF 文件太大

  • 定期触发重写
  • 设置自动重写阈值

3)恢复慢怎么办

  • 用 RDB 恢复快
  • 混合持久化更优

总结

Redis 持久化要点:

  • RDB 是快照,恢复快但可能丢数据
  • AOF 是日志,数据安全但文件大
  • 生产建议同时开 RDB + AOF
  • 混合持久化结合两者优点
  • 缓存用 RDB,重要数据用 AOF
  • 定期备份持久化文件
  • fork 阻塞要关注数据量

持久化是 Redis 生产必备配置,一定要重视。


emer 发布于  2026-10-9 07:28 

Redis 过期策略详解

Redis 过期策略详解

Redis 设置了过期时间后,怎么删除过期的 key?这就涉及过期策略。

1. 设置过期时间

# 设置过期时间(秒)
set name "张三" ex 60

# 给已有key设置过期时间
expire name 60

# 查看剩余时间
ttl name

# 移除过期时间
persist name

2. 两种过期策略

1)惰性删除

  • 等到访问这个 key 的时候才检查是否过期
  • 过期了就删除,返回 nil
  • 优点:对 CPU 友好
  • 缺点:对内存不友好,很多过期 key 没被访问就占着内存

2)定期删除

  • 每隔一段时间,随机检查一些 key
  • 发现过期的就删除
  • 优点:定期清理,控制内存
  • 缺点:随机抽查可能漏

Redis 实际是两种策略结合用。

3. 内存淘汰策略

当内存满了,写入新数据时怎么办?

# 查看当前淘汰策略
config get maxmemory-policy

常用淘汰策略:

  • noeviction: 不淘汰,写入报错(默认)
  • allkeys-lru: 所有 key 中淘汰最久没用的
  • allkeys-random: 所有 key 中随机淘汰
  • volatile-lru: 只在设置了过期时间的 key 中淘汰最久没用的
  • volatile-random: 只在设置了过期时间的 key 中随机淘汰
  • volatile-ttl: 淘汰即将过期的 key

生产推荐:allkeys-lru

4. LRU 和 LFU

  • LRU(最近最少使用): 淘汰最久没访问的
  • LFU(最不经常使用): 淘汰访问次数最少的

Redis 4.0 开始支持 LFU。

5. 实际使用建议

  1. 设置合理的 maxmemory

    • 不要让 Redis 把内存占满
    • 一般设为服务器内存的 70%
  2. 选择合适的淘汰策略

    • 纯缓存场景:allkeys-lru
    • 有持久化数据:volatile-lru
  3. key 一定要设过期时间

    • 重要数据不要设过期
    • 缓存数据都要设过期时间
  4. 监控内存使用

    • 用 info memory 查看
    • 关注 used_memory 指标

6. 查看内存信息

# 查看内存使用
info memory

# 查看key数量
dbsize

# 查看大key
--bigkeys

总结

Redis 过期策略要点:

  • 惰性删除:访问时才删
  • 定期删除:定期抽查清理
  • 两种策略结合使用
  • 内存满了要配置淘汰策略
  • 生产推荐 allkeys-lru
  • 缓存数据都要设过期时间
  • 监控内存使用情况

搞懂过期策略,Redis 用起来才不踩坑。


emer 发布于  2026-10-9 07:11 

Redis 常用数据类型

Redis 常用数据类型

Redis 不只是简单的 key-value,有5种常用数据类型,每种适合不同场景。

1. String(字符串)

最基础的类型,存字符串、数字都可以。

# 设置
set name "张三"

# 获取
get name

# 自增
incr age

# 设置过期时间
set token "abc" ex 3600

适用场景: 缓存、计数器、Session。

2. Hash(哈希)

存对象,比如用户信息。

# 设置单个字段
hset user:1 name "张三"

# 获取单个字段
hget user:1 name

# 设置多个字段
hmset user:1 name "张三" age 20 city "北京"

# 获取所有字段
hgetall user:1

适用场景: 存储对象(用户信息、商品信息)。

3. List(列表)

有序列表,可以做消息队列。

# 左侧添加
lpush list "a" "b" "c"

# 右侧添加
rpush list "d"

# 左侧弹出
lpop list

# 查看范围
lrange list 0 -1

适用场景: 消息队列、最新列表。

4. Set(集合)

无序,不重复,做去重。

# 添加
sadd set "a" "b" "c"

# 查看所有
smembers set

# 判断是否存在
sismember set "a"

# 交集
sinter set1 set2

适用场景: 去重、共同好友、标签。

5. ZSet(有序集合)

有序,带分数排序。

# 添加
zadd rank 100 "张三" 90 "李四" 80 "王五"

# 查看排名(从高到低)
zrevrank rank "张三"

# 查看分数
zscore rank "张三"

# 查看排行榜
zrevrange rank 0 9

适用场景: 排行榜、分数排序。

总结

Redis 5种数据类型:

  • String:缓存、计数器
  • Hash:存对象
  • List:消息队列
  • Set:去重、标签
  • ZSet:排行榜

根据场景选对数据类型,用起来才高效。


emer 发布于  2026-10-8 07:29 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是指查询不存在的数据,请求都打到数据库,可能被攻击。

1. 什么是缓存穿透

  • 缓存里没有这个数据
  • 数据库里也没有
  • 每次请求都查数据库
  • 有人恶意攻击数据库就挂了

2. 和其他问题的区别

  • 穿透: 查不存在的数据
  • 击穿: 热点key过期
  • 雪崩: 大量key同时过期

3. 解决方案

方案一:缓存空值

function getUser($id) {
    global $redis, $db;

    $key = "user:".$id;
    $user = $redis->get($key);

    if ($user !== false) {
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id=$id");

    if ($user) {
        // 存正常数据
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 存空值,短时间过期
        $redis->setex($key, 60, "null");
    }

    return $user;
}

方案二:布隆过滤器

  • 所有存在的数据存在布隆过滤器里
  • 查询前先过布隆过滤器
  • 不存在直接返回,不查数据库

方案三:接口校验

  • 前端做参数校验
  • 比如id必须大于0
  • 不合法的直接拒绝

4. 代码示例

// 简单防穿透
if ($id <= 0) {
    return null;
}

// 先查缓存
$user = $redis->get("user:".$id);

if ($user === "null") {
    return null;
}

if ($user) {
    return json_decode($user, true);
}

// 查数据库
$user = $db->query("SELECT * FROM users WHERE id=$id");

// 存缓存
if ($user) {
    $redis->setex("user:".$id, 3600, json_encode($user));
} else {
    $redis->setex("user:".$id, 60, "null");
}

return $user;

5. 最佳实践

  • 空值缓存,短时间过期
  • 重要接口加布隆过滤器
  • 前端做参数校验
  • 数据库做限流保护

总结

缓存穿透解决要点:

  • 查不到也存空值
  • 空值过期时间短
  • 用布隆过滤器
  • 做好接口校验

高并发系统,缓存穿透一定要防。


emer 发布于  2026-10-8 07:23 

Redis 缓存雪崩解决

Redis 缓存雪崩解决

缓存雪崩是指大量缓存同时失效,请求全部打到数据库,数据库直接挂掉。

1. 什么是缓存雪崩

原理:

  • 大量 key 同时过期
  • 请求都查数据库
  • 数据库压力瞬间暴增
  • 数据库扛不住挂掉

和缓存穿透的区别:

  • 穿透:查询不存在的数据
  • 雪崩:大量缓存同时失效

2. 产生原因

1)缓存大面积过期:

  • 大量数据设置了相同过期时间
  • 到时间一起失效

2)Redis 宕机:

  • Redis 服务挂了
  • 所有请求都走数据库

3. 解决方案

方案一:过期时间加随机值

// 不要设置相同过期时间
$expire = 3600 + rand(0, 600);
$redis->setex("key", $expire, $value);

加随机值,让 key 分散过期,不会同时失效。

方案二:缓存永不过期

  • 热点数据不设置过期时间
  • 更新数据时主动更新缓存
  • 异步更新缓存

方案三:Redis 高可用

  • 搭建 Redis 集群
  • 主从复制
  • 哨兵模式
  • 一台挂了还有其他的

方案四:熔断降级

  • 请求量太大时直接返回默认值
  • 不让请求打到数据库
  • 保护数据库

4. 代码示例

function getData($id) {
    global $redis, $db;

    $key = "data:".$id;
    $data = $redis->get($key);

    if ($data) {
        return json_decode($data, true);
    }

    // 查数据库
    $data = $db->query("SELECT * FROM table WHERE id=$id");

    // 缓存,过期时间加随机值
    $expire = 3600 + rand(0, 600);
    $redis->setex($key, $expire, json_encode($data));

    return $data;
}

5. 最佳实践

  • 过期时间加随机值
  • 搭建 Redis 集群
  • 热点数据永不过期
  • 加熔断降级机制
  • 数据库做限流

总结

缓存雪崩解决要点:

  • 过期时间加随机值,不要同时失效
  • Redis 做主从高可用
  • 热点数据不设置过期
  • 做好降级兜底

做高并发系统,缓存雪崩一定要防。


emer 发布于  2026-10-8 07:16 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,大量请求查询不存在的数据,直接打到数据库。这篇讲怎么解决。

1. 什么是缓存穿透

用户查询一个不存在的数据,Redis 里没有,每次都去查数据库。恶意攻击的话,数据库扛不住。

2. 解决方案一:缓存空值

查询数据库也没结果,就缓存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";

    // 先查缓存
    $user = $redis->get($key);
    if ($user !== false) {
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 缓存有数据的结果
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 缓存空值,短时间过期
        $redis->setex($key, 60, '');
    }

    return $user;
}

优点: 简单粗暴
缺点: 浪费内存,可能导致不一致

3. 解决方案二:布隆过滤器

在缓存前面加一层布隆过滤器,不存在的 key 直接拦截。

// 使用 RedisBloom 模块
// 添加到布隆过滤器
$redis->rawCommand("BF.ADD", "user_filter", "1001");

// 查询是否存在
$exists = $redis->rawCommand("BF.EXISTS", "user_filter", "1001");
if (!$exists) {
    return null; // 直接返回,不查数据库
}

原理: 用二进制数组和多个哈希函数,判断一个 key 一定不存在或者可能存在。

优点: 内存占用小,效率高
缺点: 有一定误判率,不支持删除

4. 两种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 浪费内存 数据量小
布隆过滤器 高效省内存 有误差 数据量大

5. 其他注意事项

  • 接口增加参数校验,不合法的 id 直接拒绝
  • 增加接口访问限流,防止恶意攻击
  • 黑名单机制,频繁查不存在的 IP 封掉

总结

缓存穿透解决思路:

  1. 缓存空值: 简单,适合小数据量
  2. 布隆过滤器: 高效,适合大数据量
  3. 加限流和校验: 从入口防护

生产环境推荐布隆过滤器 + 缓存空值组合使用。


emer 发布于  2026-10-8 07:06 

Redis 常用数据类型

Redis 常用数据类型

Redis 不只是缓存,它有 5 种常用数据类型,用对了能解决很多问题。

1. String(字符串)

最基本的类型,一个 key 对应一个 value。

# 设置
SET name "张三"

# 读取
GET name

# 自增(计数器)
INCR page_view

# 设置过期时间(秒)
EXPIRE token 3600

# 查看过期时间
TTL token

适用场景: 缓存、计数器、分布式锁。

2. Hash(哈希)

适合存对象。

# 设置字段
HSET user:1 name "张三" age 25

# 获取字段
HGET user:1 name

# 获取所有字段
HGETALL user:1

# 删除字段
HDEL user:1 age

适用场景: 用户信息、商品信息。

3. List(列表)

有序队列,先进先出。

# 从左边插入
LPUSH messages "消息1"
LPUSH messages "消息2"

# 从右边插入
RPUSH messages "消息3"

# 从左边弹出
LPOP messages

# 查看列表
LRANGE messages 0 -1

适用场景: 消息队列、最新消息列表。

4. Set(集合)

无序不重复。

# 添加元素
SADD tags "PHP" "MySQL" "Linux"

# 查看所有元素
SMEMBERS tags

# 判断是否存在
SISMEMBER tags "PHP"

# 交集
SINTER set1 set2

适用场景: 标签、共同好友、去重。

5. ZSet(有序集合)

每个元素带分数,按分数排序。

# 添加元素
ZADD rank 100 "张三"
ZADD rank 90 "李四"
ZADD rank 80 "王五"

# 查看排行(从高到低)
ZREVRANGE rank 0 -1 WITHSCORES

# 获取排名
ZREVRANK rank "张三"

适用场景: 排行榜、热度排序。

总结

类型 特点 适用场景
String 简单键值 缓存、计数器
Hash 存对象 用户信息
List 有序队列 消息队列
Set 不重复 标签、去重
ZSet 带分数排序 排行榜

Redis 用熟了,能解决很多业务问题。


emer 发布于  2026-10-7 07:15 

Redis 缓存雪崩解决

Redis 缓存雪崩解决方案

缓存雪崩是 Redis 最常见的线上事故之一,搞懂怎么预防,关键时刻能救命。

1. 什么是缓存雪崩

大量缓存同时过期,或者 Redis 整个挂了,所有请求都打到数据库,数据库瞬间压力爆炸,直接崩溃。

就像雪崩一样,一层压一层,全完了。

2. 和缓存穿透、击穿的区别

穿透:查不存在的数据,缓存挡不住,一直打数据库。

击穿:一个热点 key 突然过期,瞬间大量请求打数据库。

雪崩:大量 key 同时过期,或者 Redis 挂了,整个数据库被打垮。

3. 解决方案一:过期时间加随机值

最常用的方法。不要所有 key 都设一样的过期时间,在基础时间上加随机数:

// 不好:所有 key 都 1 小时过期
$redis->setex('product:1', 3600, $data);

// 好:基础 1 小时,加 0-600 秒随机
$ttl = 3600 + rand(0, 600);
$redis->setex('product:1', $ttl, $data);

这样过期时间分散开,不会一起失效。

4. 解决方案二:热点数据永不过期

热点数据不要主动设过期,后台异步更新:

// 商品详情这种热点数据,不设过期
$redis->set('product:1', $data);

// 后台定时任务更新
// 不依赖过期时间,由程序控制刷新

5. 解决方案三:Redis 高可用

Redis 自己要高可用,不能单点:

  • 主从架构 + 哨兵,主挂了自动切从
  • Redis Cluster 集群,分片存储,一个节点挂了不影响全局
# Redis Sentinel 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000

6. 解决方案四:熔断降级

数据库撑不住的时候,要能自动降级:

  • 限流:用令牌桶、漏桶算法,限制请求量
  • 降级:返回默认数据或者"系统繁忙"
  • 熔断:数据库连续报错多了,直接不访问数据库

常用工具:Sentinel、Hystrix。

7. 解决方案五:多级缓存

不要只靠 Redis 一层缓存:

用户请求 → 浏览器缓存 → CDN → Nginx 本地缓存 → Redis → 数据库

多级缓存,就算 Redis 挂了,前面还有 Nginx 缓存挡着。

8. 应急处理

真出问题了怎么办:

  1. 先限流,别让数据库被打死
  2. 恢复 Redis 服务
  3. 重新预热热点数据
  4. 慢慢放开流量

总结

缓存雪崩解决方案:

  • 过期时间加随机值,别一起失效
  • 热点数据永不过期,后台更新
  • Redis 做高可用集群
  • 加熔断限流兜底
  • 多级缓存层层防护

记住这几点,缓存雪崩就不怕了。


emer 发布于  2026-10-6 07:39 

Redis 数据结构详解

Redis 五种数据结构详解

Redis 不只是缓存,它的五种数据结构各有各的用处,搞懂了才能用对地方。

1. String(字符串)

最基础的数据结构,存字符串、数字都行。

SET name "张三"
GET name
INCR count
SET token "abc123" EX 3600

应用场景:缓存用户信息、计数器、分布式锁、会话存储。

2. Hash(哈希)

存键值对,适合存对象。

HSET user:1 name "张三" age 25 city "北京"
HGET user:1 name
HGETALL user:1

应用场景:存用户信息、商品信息,比 String 存 JSON 更省空间。

3. List(列表)

有序列表,可以从头或尾加元素。

LPUSH messages "hello"
LPUSH messages "world"
LRANGE messages 0 -1

应用场景:消息队列、最新消息列表、朋友圈时间线。

4. Set(集合)

无序不重复集合。

SADD tags "PHP"
SADD tags "MySQL"
SMEMBERS tags
SISMEMBER tags "PHP"

应用场景:标签、共同好友、去重。

5. ZSet(有序集合)

每个元素带分数,按分数排序。

ZADD rank 100 "张三"
ZADD rank 90 "李四"
ZREVRANGE rank 0 9 WITHSCORES

应用场景:排行榜、计分板、优先级队列。

总结

五种数据结构各有用处:

  • String:简单缓存、计数器
  • Hash:存对象
  • List:队列、时间线
  • Set:去重、标签
  • ZSet:排行榜

选对数据结构,性能才能发挥出来。


emer 发布于  2026-10-4 10:50 

Redis 缓存穿透解决

Redis 缓存穿透解决方案

缓存穿透是指查询一个数据库里根本不存在的数据,因为缓存里也没有,所以每次请求都打到数据库。如果有人恶意攻击,数据库可能被打挂。

什么是缓存穿透

正常流程:先查缓存,缓存有就直接返回,没有再查数据库,然后写入缓存。

如果查询的数据数据库里也没有,那缓存永远都不会命中,每次请求都会查数据库,这就是缓存穿透。

解决方案一:缓存空值

最简单的办法,数据库查不到也往缓存里写一个空值,设个短过期时间。

$data = $redis->get($key);
if ($data === null) {
    $data = $db->query($sql);
    if ($data === null) {
        // 缓存空值,60秒过期
        $redis->setex($key, 60, '');
    } else {
        $redis->setex($key, 3600, $data);
    }
}

优点:简单直接
缺点:会占用更多缓存空间,大量空值缓存会浪费内存

解决方案二:布隆过滤器

在缓存前面加一层布隆过滤器,先判断数据是否存在,不存在直接返回。

布隆过滤器的原理是用一个大的位数组和多个哈希函数,把存在的数据标记为1。查询时如果所有哈希位置都是1,说明可能存在;只要有一个位置是0,说明一定不存在。

优点:内存占用小,效率高
缺点:有一定误判率(说存在可能不存在,说不存在一定不存在)

两种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 浪费内存 数据量小,穿透不严重
布隆过滤器 内存效率高 有误差 数据量大,穿透严重

总结

缓存穿透是常见的缓存问题,小项目用缓存空值就够了,大项目数据量大就上布隆过滤器。两种方案都能解决问题,根据实际场景选就行。


emer 发布于  2026-10-4 10:43 

Redis 持久化详解

Redis 两种持久化机制对比

Redis 是内存数据库,重启数据就没了,所以持久化很重要。Redis 有两种持久化方式:RDB 和 AOF。

1. RDB 快照

RDB 就是把整个内存数据快照存成文件。

优点:

  • 文件小,恢复快
  • 适合备份和灾难恢复

缺点:

  • 可能丢数据,两次快照之间的写入会丢

配置:

save 900 1        # 900秒内至少1个key变化就快照
save 300 10       # 300秒内至少10个key变化
save 60 10000     # 60秒内至少10000个key变化

2. AOF 日志

AOF 把每个写操作都记录到日志文件,重启时重放日志恢复数据。

优点:

  • 数据更安全,最多丢1秒
  • 日志可读,误删可以手动修复

缺点:

  • 文件比 RDB 大
  • 恢复慢

配置:

appendonly yes
appendfsync everysec   # 每秒刷盘一次

3. 混合持久化

Redis 4.0 支持混合持久化,RDB 做全量,AOF 做增量。

aof-use-rdb-preamble yes

生产环境推荐用这个,兼顾性能和数据安全。

4. 怎么选

  • 纯缓存场景:不要持久化,性能最好
  • 数据不能丢:用 AOF 或混合持久化
  • 需要快速恢复:用 RDB

总结

Redis 持久化没有完美方案,根据业务场景选。生产环境建议混合持久化,兼顾性能和数据安全。


emer 发布于  2026-10-4 10:30 

Redis 数据结构:五种常用类型和应用场景实战

前言

Redis 不只是简单的缓存,它有 5 种常用数据结构,每种都有特定的应用场景。用对了能解决很多问题,用错了就是浪费内存。本文讲清楚这 5 种数据结构。

一、String(字符串)

是什么

最简单的 key-value 结构,value 可以是字符串、数字、二进制。

常用命令

# 设置值
SET name "张三"

# 获取值
GET name

# 数字自增
INCR count
INCRBY count 10

# 设置过期时间
SETEX session:token 3600 "abc123"

# 批量操作
MSET name "张三" age 18 city "北京"
MGET name age city

应用场景

  1. 缓存:缓存用户信息、配置
  2. 计数器:文章阅读数、点赞数
  3. 分布式锁:SETNX 命令
  4. Session 存储:用户登录状态

二、Hash(哈希)

是什么

一个 key 对应一个 map,适合存储对象。

常用命令

# 设置单个字段
HSET user:1 name "张三"
HSET user:1 age 18

# 获取单个字段
HGET user:1 name

# 获取所有字段
HGETALL user:1

# 批量设置
HMSET user:1 name "张三" age 18 city "北京"

# 字段自增
HINCRBY user:1 age 1

应用场景

  1. 存储对象:用户信息、商品信息
  2. 购物车:key 是用户 ID,field 是商品 ID,value 是数量
# 购物车
HSET cart:1001 商品A 2
HSET cart:1001 商品B 1

三、List(列表)

是什么

有序列表,底层是双向链表,可以从左右两边 push/pop。

常用命令

# 左边插入
LPUSH messages "hello"
LPUSH messages "world"

# 右边插入
RPUSH messages "end"

# 左边弹出
LPOP messages

# 右边弹出
RPOP messages

# 查看列表范围
LRANGE messages 0 -1

# 列表长度
LLEN messages

应用场景

  1. 消息队列:LPUSH + RPOP
  2. 最新消息:比如最新评论、最新文章
  3. 朋友圈时间线:LPUSH 新内容,LRANGE 分页查看

四、Set(集合)

是什么

无序且不重复的集合,自动去重。

常用命令

# 添加元素
SADD tags "PHP" "MySQL" "Redis"

# 查看所有元素
SMEMBERS tags

# 判断元素是否存在
SISMEMBER tags "PHP"

# 集合运算
SINTER set1 set2  # 交集
SUNION set1 set2  # 并集
SDIFF set1 set2   # 差集

# 集合大小
SCARD tags

应用场景

  1. 标签系统:一篇文章的标签
  2. 共同好友:SINTER 算交集
  3. 去重:UV 统计、防止重复提交
  4. 抽奖:SRANDMEMBER 随机抽取

五、ZSet(有序集合)

是什么

每个元素都有一个 score,按 score 排序,不重复。

常用命令

# 添加元素
ZADD rank 100 "张三"
ZADD rank 90 "李四"
ZADD rank 95 "王五"

# 按分数从低到高排序
ZRANGE rank 0 -1 WITHSCORES

# 按分数从高到低排序
ZREVRANGE rank 0 -1 WITHSCORES

# 查看元素排名
ZREVRANK rank "张三"

# 查看元素分数
ZSCORE rank "张三"

# 分数范围查询
ZRANGEBYSCORE rank 90 100

应用场景

  1. 排行榜:直播间送礼排行、游戏排行
  2. 延迟队列:score 存时间戳,到点的任务处理
  3. 加权排序:搜索结果排序

六、怎么选数据结构

场景 用什么
简单 key-value String
存对象 Hash
有序列表/队列 List
去重/标签 Set
排序/排行 ZSet

七、常见坑

  1. List 太多元素:链表太长,范围查询慢
  2. Hash 字段太多:小对象还好,大对象不适合
  3. Set 当 List 用:Set 无序,要顺序用 List 或 ZSet
  4. 大 key:一个 key 存几百万数据,会阻塞

总结

Redis 5 种数据结构的核心要点:

  1. String:最简单,缓存、计数器
  2. Hash:存对象,用户信息、购物车
  3. List:有序队列,消息、时间线
  4. Set:去重集合,标签、共同好友
  5. ZSet:排序集合,排行榜、延迟队列

选对数据结构,Redis 才能发挥最大威力。


emer 发布于  2026-10-4 10:14 
'); })();