安装配置 连接方式 数据类型操作 函数速查 扩展用法 最佳实践
一、Redis 概述
Redis(Remote Dictionary Server)是一个开源的、基于内存的键值对(Key‑Value)数据库,使用 ANSI C 语言编写。它不仅支持简单的字符串键值对,还提供了丰富的数据结构,包括字符串、哈希、列表、集合、有序集合、位图、HyperLogLog、流(Stream)等。Redis 将数据存储在内存中,因此读写速度极快,单机 QPS 可达十万级别,同时支持持久化(RDB / AOF)将内存数据写入磁盘,防止重启后数据丢失。
Redis 的核心特性包括:
- 高性能:纯内存操作,读写延迟通常在亚毫秒级,单实例 QPS 可达 10 万以上。
- 丰富数据结构:支持 String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream 等。
- 持久化:支持 RDB(快照)和 AOF(追加日志)两种持久化方式,可组合使用。
- 主从复制:支持一主多从架构,从节点自动同步主节点数据,实现读写分离。
- 哨兵与集群:Sentinel 提供自动故障转移;Cluster 提供数据分片和高可用。
- 过期机制:支持为键设置 TTL,到期自动删除,天然适合缓存场景。
- 原子操作:单条命令保证原子性,支持事务(MULTI/EXEC)和 Lua 脚本。
- 发布订阅:内置 Pub/Sub 消息模型,可用于实时消息推送。
Redis 在 PHP 项目中的典型应用场景:
- 缓存:缓存数据库查询结果、页面片段、API 响应等,降低数据库压力。
- 会话存储:将用户会话(Session)存储在 Redis 中,支持多服务器共享。
- 计数器/限流:利用 INCR 实现访问计数、点赞数、接口限流。
- 排行榜:利用 Sorted Set 实现实时排行榜功能。
- 消息队列:利用 List 的 BLPOP/BRPOP 或 Stream 实现轻量级消息队列。
- 分布式锁:利用 SET NX EX 实现分布式锁,保证并发安全。
- 排行榜/延时队列:利用 Sorted Set 的 score 实现延时任务调度。
二、PHP Redis 扩展安装
PHP 中操作 Redis 有两种主流方式:
- phpredis 扩展(推荐):C 语言编写的 PHP 扩展,性能最高,由 PECL 维护,提供了面向对象的 API,是生产环境的首选。
- predis 客户端:纯 PHP 编写的 Redis 客户端库,通过 Composer 安装,无需编译扩展,但性能略低于 phpredis。
2.1 安装 phpredis 扩展
Linux 环境下通过 PECL 安装:
pecl install redis
安装后在 php.ini 中添加扩展:
extension=redis.so
安装完成后验证:
php -m | grep redis
php --ri redis
2.2 安装 predis 客户端(备选方案)
如果无法安装 C 扩展,可使用 Composer 安装 predis:
composer require predis/predis
本文所有代码示例均基于 phpredis 扩展(C 扩展版),即 Redis 类。predis 的 API 接口与 phpredis 高度相似但不完全一致,使用时需注意差异。
三、Redis 连接方式
3.1 基本连接(短连接)
使用 connect() 方法创建 Redis 连接,脚本结束时自动关闭:
<?php
$redis=new Redis();
$redis->connect('127.0.0.1',6379,2.5);//主机、端口、超时时间(秒)
$redis->auth('your_password');//有密码时需要认证
$redis->select(0);//选择数据库(默认0)
$redis->set('test_key','HelloRedis');
echo $redis->get('test_key');//输出:HelloRedis
$redis->close();//显式关闭连接
connect() 方法参数说明:
| 参数 | 功能说明 |
|---|---|
| host | Redis 服务器地址,可为 IP 或域名,如 '127.0.0.1' 或 /var/run/redis/redis.sock(Unix 套接字) |
| port | Redis 端口,默认 6379。使用 Unix 套接字时端口设为 0 |
| timeout | 连接超时时间(秒),默认 0 表示不限制。浮点数表示毫秒级,如 2.5 秒 |
| retry_interval | 重连间隔(毫秒),连接失败时的重试等待时间,默认 0 |
| read_timeout | 读取超时时间(秒),默认 0 表示不限制 |
3.2 持久连接(长连接)
使用 pconnect() 创建持久连接,连接在脚本结束后不会关闭,后续请求可复用该连接,减少 TCP 握手开销:
<?php
$redis=new Redis();
$redis->pconnect('127.0.0.1',6379,2.5);
//pconnect也可接受persistent_id参数,用于区分不同的连接池
$redis->pconnect('127.0.0.1',6379,2.5,'pool_1');
生产环境推荐使用 pconnect() 持久连接,配合 PHP‑FPM 进程常驻,可显著降低连接建立开销。注意每个 PHP‑FPM 进程会维持一个独立连接。
3.3 带认证的连接
Redis 6.0+ 推荐使用 ACL 用户认证,旧版本使用 requirepass 密码认证:
<?php
$redis=new Redis();
$redis->connect('127.0.0.1',6379);
//方式一:旧版密码认证
$redis->auth('your_password');
//方式二:ACL用户名+密码认证(Redis6.0+)
$redis->auth(['user'=>'myuser','pass'=>'mypass']);
//方式三:连接时直接带认证信息
$redis->connect('127.0.0.1',6379,2.5,NULL,0,0,['auth'=>['user','pass']]);
3.4 连接参数数组方式(推荐)
phpredis 5.3+ 支持用数组参数一次性传入所有连接配置:
<?php
$redis=new Redis();
$redis->connect([
'host'=>'127.0.0.1',
'port'=>6379,
'timeout'=>2.5,
'auth'=>['myuser','mypass'],
'db'=>0,//选择数据库
'retry_interval'=>100,//重连间隔(ms)
'read_timeout'=>3.0,//读取超时
]);
3.5 SSL/TLS 加密连接
连接远程 Redis 时可启用 TLS 加密传输:
<?php
$redis=new Redis();
$redis->connect('tls://redis.example.com',6379,2.5,
NULL,0,0,
['stream'=>[
'verify_peer'=>true,
'verify_peer_name'=>true,
'cafile'=>'/etc/ssl/certs/ca‑bundle.crt',
]
]);
3.6 predis 连接方式
<?php
require 'vendor/autoload.php';
//单节点连接
$redis=new Predis\Client([
'scheme'=>'tcp',
'host'=>'127.0.0.1',
'port'=>6379,
'password'=>'your_password',
'database'=>0,
]);
//集群连接
$redis=new Predis\Client([
['host'=>'10.0.0.1','port'=>6379],
['host'=>'10.0.0.2','port'=>6379],
['host'=>'10.0.0.3','port'=>6379],
],['cluster'=>'redis','parameters'=>['password'=>'secret']]);
3.7 连接相关函数汇总
| 方法 | 频率 | 说明 |
|---|---|---|
| connect(host, port, timeout, ...) | 常用 | 创建 Redis 短连接,脚本结束自动断开 |
| pconnect(host, port, timeout, ...) | 常用 | 创建 Redis 持久连接,跨请求复用 |
| close() | 常用 | 关闭当前连接 |
| auth(password) | 常用 | Redis 密码认证 |
| select(db) | 常用 | 切换 Redis 数据库(0~15) |
| ping() | 常用 | 发送 PING 命令测试连接,返回 true 或 '+PONG' |
| echo(msg) | 非常用 | 发送 ECHO 命令,原样返回消息字符串 |
| getAuth() | 非常用 | 获取当前设置的认证密码 |
| getDBNum() | 非常用 | 获取当前选中的数据库编号 |
| getHost() | 非常用 | 获取当前连接的主机地址 |
| getPort() | 非常用 | 获取当前连接的端口号 |
| isConnected() | 非常用 | 判断当前是否已连接 |
| getPersistentID() | 非常用 | 获取持久连接的唯一标识 |
四、字符串(String)操作
String 是 Redis 最基本的数据类型,一个键对应一个值,值最大支持 512MB。String 可以存储文本、数字、序列化数据(JSON、PHP serialize)甚至二进制数据。它是 Redis 中使用频率最高的数据类型。
4.1 常用字符串函数
4.1.1 set() --- 设置键值
将字符串值关联到指定键。如果键已存在则覆盖旧值。
<?php
$redis->set('name','Redis');
//带选项参数(phpredis5.0+)
$redis->set('user:1','Alice',['ex'=>3600]);//3600秒后过期
$redis->set('lock:1','owner1',['nx']);//仅当键不存在时设置
$redis->set('lock:2','owner2',['xx','ex'=>30]);//仅当键存在时设置,30秒过期
| 方法 | 功能说明 |
|---|---|
| set(key, value) | 设置键值,存在则覆盖。返回 true |
| set(key, value, 'ex' =\> seconds) | 设置键值并指定过期时间(秒) |
| set(key, value, 'px' =\> ms) | 设置键值并指定过期时间(毫秒) |
| set(key, value, 'nx') | 仅当键不存在时设置(等同于 setnx) |
| set(key, value, 'xx') | 仅当键已存在时设置 |
| set(key, value, 'keepttl') | 设置新值但保留原有 TTL |
4.1.2 get() --- 获取值
<?php
$redis->set('name','Redis');
echo $redis->get('name');//输出:Redis
var_dump($redis->get('not_exist'));//输出:bool(false)
4.1.3 del() --- 删除键
<?php
$redis->set('key1','val1');
$redis->set('key2','val2');
$redis->del('key1');//删除单个键,返回删除数量
$redis->del(['key1','key2']);//批量删除
$count=$redis->del('k1','k2','k3');//返回实际删除数量
4.1.4 incr() / decr() --- 自增自减
原子操作,适合计数器场景。值必须为整数,否则报错。
<?php
$redis->set('counter',100);
$redis->incr('counter');//101
$redis->decr('counter');//100
$redis->incrBy('counter',10);//110
$redis->decrBy('counter',5);//105
//浮点数自增
$redis->set('price','10.50');
$redis->incrByFloat('price',0.25);//10.75
4.1.5 setex() / psetex() --- 带过期的设置
<?php
$redis->setex('token',3600,'abc123');//3600秒后过期
$redis->psetex('token2',5000,'xyz789');//5000毫秒(5秒)后过期
4.1.6 setnx() --- 不存在时设置
常用于分布式锁。键不存在时设置成功返回 true,已存在返回 false。
<?php
$ok=$redis->setnx('lock:order:1001','client_1');
if($ok){
$redis->expire('lock:order:1001',30);//设置过期防止死锁
//执行业务逻辑...
}else{
echo '锁已被占用';
}
4.1.7 mset() / mget() --- 批量操作
<?php
//批量设置
$redis->mset([
'user:name'=>'Alice',
'user:age'=>'28',
'user:city'=>'Beijing',
]);
//批量获取
$values=$redis->mget(['user:name','user:age','user:city']);
print_r($values);//['Alice','28','Beijing']
4.1.8 append() --- 追加字符串
<?php
$redis->set('greeting','Hello');
$redis->append('greeting','World');//'HelloWorld'
echo $redis->strlen('greeting');//11
4.1.9 getSet() --- 设置新值并返回旧值
<?php
$redis->set('counter','100');
$old=$redis->getSet('counter','200');//$old='100',当前值为'200'
4.2 非常用字符串函数
4.2.1 getRange() / setRange() --- 范围操作
<?php
$redis->set('text','HelloWorld');
echo $redis->getRange('text',0,4);//'Hello'
$redis->setRange('text',6,'Redis');//'HelloRedis'
4.2.2 位操作函数
<?php
$redis->set('bitmap','abc');
$redis->setBit('bitmap',10,1);//设置第10位为1
$bit=$redis->getBit('bitmap',10);//获取第10位
$count=$redis->bitCount('bitmap');//统计被设为1的位数
位操作适用于用户签到、在线状态等需要高效存储布尔标记的场景。
4.3 字符串函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| set(key, value, opts) | 常用 | 设置键值,支持过期、NX/XX 等选项 |
| get(key) | 常用 | 获取键的值,不存在返回 false |
| del(key1, key2, ...) | 常用 | 删除一个或多个键,返回删除数量 |
| exists(key) | 常用 | 判断键是否存在(存在返回1,可传多个键返回总数) |
| incr(key) | 常用 | 值自增1,返回新值 |
| decr(key) | 常用 | 值自减1,返回新值 |
| incrBy(key, int) | 常用 | 值增加指定整数 |
| decrBy(key, int) | 常用 | 值减少指定整数 |
| incrByFloat(key, float) | 非常用 | 值增加指定浮点数 |
| setex(key, ttl, val) | 常用 | 设置键值并指定过期时间(秒) |
| psetex(key, ttl, val) | 非常用 | 设置键值并指定过期时间(毫秒) |
| setnx(key, value) | 常用 | 仅当键不存在时设置值 |
| getSet(key, value) | 常用 | 设置新值并返回旧值 |
| mset(array) | 常用 | 批量设置多个键值 |
| mget(array) | 常用 | 批量获取多个键的值 |
| msetnx(array) | 非常用 | 批量设置,仅当所有键都不存在时成功 |
| append(key, value) | 常用 | 在值末尾追加字符串 |
| strlen(key) | 常用 | 获取值的字符串长度 |
| getRange(key, start, end) | 非常用 | 获取值的子字符串 |
| setRange(key, offset, val) | 非常用 | 从指定偏移量覆写值 |
| getBit(key, offset) | 非常用 | 获取值指定位置的比特位 |
| setBit(key, offset, val) | 非常用 | 设置值指定位置的比特位 |
| bitCount(key) | 非常用 | 统计值中被设为1的比特数 |
| bitOp(op, dest, key1, ...) | 非常用 | 位运算(AND/OR/XOR/NOT)并存入 dest |
五、哈希(Hash)操作
Hash 是一个键对应多个字段‑值(field‑value)对的结构,类似于 PHP 中的关联数组。Hash 非常适合存储对象,例如用户信息(姓名、年龄、邮箱等),相比将对象序列化为 String 存储,Hash 可以单独修改某个字段而无需读写整个对象。
5.1 常用哈希函数
5.1.1 hSet() / hGet() --- 设置和获取字段
<?php
//设置单个字段
$redis->hSet('user:1','name','Alice');
$redis->hSet('user:1','age','28');
//获取单个字段
echo $redis->hGet('user:1','name');//Alice
var_dump($redis->hGet('user:1','email'));//false(字段不存在)
5.1.2 hMSet() / hMGet() --- 批量操作
<?php
//批量设置多个字段
$redis->hMSet('user:1',[
'name'=>'Alice',
'age'=>'28',
'email'=>'alice@example.com',
'city'=>'Beijing',
]);
//批量获取多个字段
$fields=$redis->hMGet('user:1',['name','age','email']);
print_r($fields);
//['name'=>'Alice','age'=>'28','email'=>'alice@example.com']
5.1.3 hGetAll() --- 获取所有字段和值
<?php
$all=$redis->hGetAll('user:1');
print_r($all);
//['name'=>'Alice','age'=>'28','email'=>'alice@example.com','city'=>'Beijing']
5.1.4 hDel() --- 删除字段
<?php
$redis->hDel('user:1','email');//删除单个字段
$redis->hDel('user:1','email','city');//删除多个字段
5.1.5 hExists() / hLen() --- 判断和计数
<?php
var_dump($redis->hExists('user:1','name'));//true
echo $redis->hLen('user:1');//字段数量,如3
5.1.6 hIncrBy() --- 字段自增
<?php
$redis->hSet('user:1','login_count',0);
$redis->hIncrBy('user:1','login_count',1);//1
$redis->hIncrBy('user:1','login_count',5);//6
$redis->hIncrByFloat('user:1','score',1.5);//浮点数自增
5.1.7 hKeys() / hVals() --- 获取所有字段名/值
<?php
print_r($redis->hKeys('user:1'));//['name','age','email']
print_r($redis->hVals('user:1'));//['Alice','28','alice@example.com']
5.2 非常用哈希函数
5.2.1 hSetNx() --- 不存在时设置字段
<?php
//仅当字段不存在时设置,已存在则不修改
$redis->hSetNx('user:1','created_at',date('Y‑m‑d H:i:s'));
5.2.2 hStrLen() --- 获取字段值长度
<?php
echo $redis->hStrLen('user:1','name');//字段值的字节长度
5.2.3 hScan() --- 迭代哈希字段
<?php
$iterator=null;
while($keys=$redis->hScan('user:1',$iterator)){
foreach($keys as $field=>$value){
echo "$field => $value\n";
}
}
5.3 哈希函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| hSet(key, field, val) | 常用 | 设置哈希字段的值,字段已存在则覆盖 |
| hGet(key, field) | 常用 | 获取哈希字段的值 |
| hMSet(key, array) | 常用 | 批量设置多个字段 |
| hMGet(key, array) | 常用 | 批量获取多个字段的值 |
| hGetAll(key) | 常用 | 获取所有字段和值(关联数组) |
| hDel(key, field1, ...) | 常用 | 删除一个或多个字段,返回删除数 |
| hExists(key, field) | 常用 | 判断字段是否存在 |
| hLen(key) | 常用 | 获取字段数量 |
| hIncrBy(key, field, int) | 常用 | 字段值增加指定整数 |
| hIncrByFloat(key, field, fl) | 非常用 | 字段值增加指定浮点数 |
| hKeys(key) | 常用 | 获取所有字段名 |
| hVals(key) | 常用 | 获取所有字段值 |
| hSetNx(key, field, val) | 非常用 | 仅当字段不存在时设置值 |
| hStrLen(key, field) | 非常用 | 获取字段值的字节长度 |
| hScan(key, iter, opts) | 非常用 | 增量迭代哈希字段,支持 MATCH/COUNT |
六、列表(List)操作
List 是一个有序的字符串链表,支持从头部(左)和尾部(右)插入/弹出元素。List 常用于消息队列、最新动态列表、操作日志等场景。
6.1 常用列表函数
6.1.1 lPush() / rPush() --- 入队
lPush 从左端(头部)插入,rPush 从右端(尾部)插入。返回列表长度。
<?php
$redis->lPush('queue:tasks','task3');//[task3]
$redis->lPush('queue:tasks','task2');//[task2,task3]
$redis->lPush('queue:tasks','task1');//[task1,task2,task3]
$redis->rPush('queue:tasks','task4');//[task1,task2,task3,task4]
6.1.2 lPop() / rPop() --- 出队
<?php
echo $redis->lPop('queue:tasks');//'task1',剩余[task2,task3,task4]
echo $redis->rPop('queue:tasks');//'task4',剩余[task2,task3]
6.1.3 lRange() --- 获取范围元素
获取列表指定范围的元素,0 表示第一个,‑1 表示最后一个。
<?php
//获取全部元素
$all=$redis->lRange('queue:tasks',0,-1);
//获取前3个
$top3=$redis->lRange('queue:tasks',0,2);
6.1.4 lLen() --- 获取列表长度
<?php
echo $redis->lLen('queue:tasks');//列表元素数量
6.1.5 lRem() --- 移除元素
<?php
//count>0:从头到尾移除最多count个等于value的元素
//count<0:从尾到头移除
//count=0:移除所有等于value的元素
$redis->lRem('queue:tasks','task2',1);
6.1.6 lTrim() --- 裁剪列表
<?php
//只保留索引0~2的元素,其余删除
$redis->lTrim('queue:tasks',0,2);
6.2 非常用列表函数
6.2.1 lIndex() / lSet() --- 按索引操作
<?php
echo $redis->lIndex('queue:tasks',0);//获取第一个元素
$redis->lSet('queue:tasks',0,'new_task1');//设置第一个元素的值
6.2.2 lInsert() --- 插入元素
<?php
//在pivot元素之前/之后插入新元素
$redis->lInsert('queue:tasks',Redis::BEFORE,'task2','task1.5');
$redis->lInsert('queue:tasks',Redis::AFTER,'task2','task2.5');
6.2.3 阻塞弹出 blPop / brPop
阻塞式弹出,列表为空时等待直到有元素或超时。适合消费者长轮询。
<?php
//阻塞等待最多5秒
$result=$redis->blPop(['queue:tasks'],5);
//返回['queue:tasks','task_value']或超时返回空
if($result){
$queue=$result[0];
$value=$result[1];
}
6.2.4 rpoplpush() --- 移动元素
<?php
//从source尾部弹出并推入destination头部
$redis->rpoplpush('queue:src','queue:dst');
6.3 列表函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| lPush(key, val1, ...) | 常用 | 从列表左端插入一个或多个元素 |
| rPush(key, val1, ...) | 常用 | 从列表右端插入一个或多个元素 |
| lPop(key) | 常用 | 弹出并返回列表左端第一个元素 |
| rPop(key) | 常用 | 弹出并返回列表右端第一个元素 |
| lRange(key, start, end) | 常用 | 获取指定范围的元素列表 |
| lLen(key) | 常用 | 获取列表长度(元素数量) |
| lRem(key, val, count) | 常用 | 移除指定数量等于 val 的元素 |
| lTrim(key, start, end) | 常用 | 裁剪列表,只保留指定范围 |
| lIndex(key, index) | 非常用 | 获取指定索引的元素 |
| lSet(key, index, val) | 非常用 | 设置指定索引的元素值 |
| lInsert(key, pos, piv, v) | 非常用 | 在 pivot 元素前/后插入值 |
| blPop(keys, timeout) | 非常用 | 阻塞式从左端弹出 |
| brPop(keys, timeout) | 非常用 | 阻塞式从右端弹出 |
| rpoplpush(src, dst) | 非常用 | 从 src 尾部弹出推入 dst 头部 |
| brpoplpush(src, dst, t) | 非常用 | 阻塞式 rpoplpush |
| lPushx(key, val) | 非常用 | 仅当列表存在时从左端插入 |
| rPushx(key, val) | 非常用 | 仅当列表存在时从右端插入 |
七、集合(Set)操作
Set 是一个无序的、不重复的字符串集合。支持集合运算(交集、并集、差集),常用于标签管理、共同好友、去重等场景。
7.1 常用集合函数
7.1.1 sAdd() / sRem() --- 添加和移除成员
<?php
$redis->sAdd('tags:article:1','php','redis','database');
$redis->sAdd('tags:article:1','redis');//重复添加返回0
$redis->sRem('tags:article:1','database');//移除成员
7.1.2 sMembers() --- 获取所有成员
<?php
$members=$redis->sMembers('tags:article:1');
print_r($members);//['php','redis']
7.1.3 sIsMember() --- 判断成员是否存在
<?php
var_dump($redis->sIsMember('tags:article:1','php'));//true
var_dump($redis->sIsMember('tags:article:1','java'));//false
7.1.4 sCard() --- 获取成员数量
<?php
echo $redis->sCard('tags:article:1');//2
7.1.5 sPop() / sRandMember() --- 随机操作
<?php
//随机弹出一个成员
$member=$redis->sPop('tags:article:1');
//随机获取3个成员(不弹出)
$random=$redis->sRandMember('tags:article:1',3);
7.2 集合运算函数
集合运算是 Set 类型的核心能力:
<?php
$redis->sAdd('set:A','a','b','c','d');
$redis->sAdd('set:B','c','d','e','f');
//交集:{c,d}
$inter=$redis->sInter('set:A','set:B');
//并集:{a,b,c,d,e,f}
$union=$redis->sUnion('set:A','set:B');
//差集(A有B没有):{a,b}
$diff=$redis->sDiff('set:A','set:B');
//将交集结果存入新集合
$redis->sInterStore('result:inter','set:A','set:B');
$redis->sUnionStore('result:union','set:A','set:B');
$redis->sDiffStore('result:diff','set:A','set:B');
7.3 sMove() --- 移动成员
<?php
//将成员从source集合移动到destination集合
$redis->sMove('set:A','set:B','a');
7.4 集合函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| sAdd(key, member1, ...) | 常用 | 向集合添加一个或多个成员 |
| sRem(key, member1, ...) | 常用 | 从集合移除一个或多个成员 |
| sMembers(key) | 常用 | 获取集合所有成员 |
| sIsMember(key, member) | 常用 | 判断成员是否在集合中 |
| sCard(key) | 常用 | 获取集合成员数量 |
| sPop(key) | 非常用 | 随机弹出一个成员 |
| sRandMember(key, count) | 非常用 | 随机获取成员(不弹出) |
| sInter(key1, key2, ...) | 常用 | 获取多个集合的交集 |
| sUnion(key1, key2, ...) | 常用 | 获取多个集合的并集 |
| sDiff(key1, key2, ...) | 常用 | 获取多个集合的差集 |
| sInterStore(dst, k1, ...) | 非常用 | 交集结果存入指定集合 |
| sUnionStore(dst, k1, ...) | 非常用 | 并集结果存入指定集合 |
| sDiffStore(dst, k1, ...) | 非常用 | 差集结果存入指定集合 |
| sMove(src, dst, member) | 非常用 | 将成员从源集合移到目标集合 |
| sScan(key, iter, opts) | 非常用 | 增量迭代集合成员 |
八、有序集合(Sorted Set)操作
Sorted Set 是 Redis 最具特色的数据类型之一。每个成员关联一个分数(score),集合按分数自动排序。非常适合排行榜、延时队列、带权重的数据等场景。
8.1 常用有序集合函数
8.1.1 zAdd() --- 添加成员
<?php
//添加成员及分数
$redis->zAdd('leaderboard',100,'Alice');
$redis->zAdd('leaderboard',85,'Bob');
$redis->zAdd('leaderboard',92,'Charlie');
//批量添加(phpredis5.0+支持选项参数)
$redis->zAdd('leaderboard',
['nx'],//仅当成员不存在时添加
100,'Alice',
85,'Bob',
92,'Charlie'
);
8.1.2 zRange() / zRevRange() --- 范围获取
zRange 按分数升序,zRevRange 按分数降序。
<?php
//升序获取全部
$asc=$redis->zRange('leaderboard',0,-1);
//['Bob','Charlie','Alice']
//降序获取前3名(含分数)
$top3=$redis->zRevRange('leaderboard',0,2,true);
//['Alice'=>100,'Charlie'=>92,'Bob'=>85]
8.1.3 zRangeByScore() --- 按分数范围获取
<?php
//获取分数在80~95之间的成员
$mid=$redis->zRangeByScore('leaderboard',80,95);
//['Bob','Charlie']
//限制返回数量,带分数
$mid=$redis->zRangeByScore('leaderboard',80,95,
['withscores'=>true,'limit'=>[0,10]]);
//降序按分数范围
$desc=$redis->zRevRangeByScore('leaderboard',100,80);
8.1.4 zScore() --- 获取成员分数
<?php
echo $redis->zScore('leaderboard','Alice');//100
8.1.5 zIncrBy() --- 增加分数
<?php
$redis->zIncrBy('leaderboard',10,'Bob');//Bob分数变为95
8.1.6 zRank() / zRevRank() --- 获取排名
zRank 返回升序排名(0 开始),zRevRank 返回降序排名。
<?php
echo $redis->zRank('leaderboard','Alice');//2(升序第3位)
echo $redis->zRevRank('leaderboard','Alice');//0(降序第1位)
8.1.7 zRem() / zCard() / zCount()
<?php
//删除成员
$redis->zRem('leaderboard','Bob');
//获取成员总数
echo $redis->zCard('leaderboard');//2
//获取分数范围内的成员数
echo $redis->zCount('leaderboard',90,100);//1(Alice)
8.2 非常用有序集合函数
8.2.1 zRemRangeByRank() / zRemRangeByScore()
<?php
//按排名范围删除
$redis->zRemRangeByRank('leaderboard',0,2);
//按分数范围删除
$redis->zRemRangeByScore('leaderboard',0,50);
8.2.2 zUnion() / zInter() --- 集合运算
<?php
//并集:将多个有序集合合并
$redis->zUnion('result',['set1','set2']);
//带权重和聚合方式
$redis->zUnion('result',['set1','set2'],
['weights'=>[2,1],'aggregate'=>'MAX']);
//交集
$redis->zInter('result',['set1','set2']);
8.3 有序集合函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| zAdd(key, score, member, ...) | 常用 | 添加成员及分数 |
| zRem(key, member1, ...) | 常用 | 移除一个或多个成员 |
| zRange(key, start, end, w) | 常用 | 按分数升序获取成员 |
| zRevRange(key, start, end, w) | 常用 | 按分数降序获取成员 |
| zRangeByScore(key, min, max) | 常用 | 按分数范围升序获取成员 |
| zRevRangeByScore(key, max, min) | 常用 | 按分数范围降序获取成员 |
| zScore(key, member) | 常用 | 获取成员的分数 |
| zIncrBy(key, incr, member) | 常用 | 增加成员的分数 |
| zRank(key, member) | 常用 | 获取成员升序排名 |
| zRevRank(key, member) | 常用 | 获取成员降序排名 |
| zCard(key) | 常用 | 获取成员总数 |
| zCount(key, min, max) | 常用 | 获取分数范围内的成员数 |
| zRemRangeByRank(key, s, e) | 非常用 | 按排名范围删除成员 |
| zRemRangeByScore(key, min, max) | 非常用 | 按分数范围删除成员 |
| zUnion(dst, keys, opts) | 非常用 | 多个有序集合并集存入 dst |
| zInter(dst, keys, opts) | 非常用 | 多个有序集合交集存入 dst |
| zRangeByLex(key, min, max) | 非常用 | 按字典序范围获取成员 |
| zScan(key, iter, opts) | 非常用 | 增量迭代有序集合成员 |
九、键(Key)操作
Key 操作不限于特定数据类型,适用于所有 Redis 数据类型。包括过期设置、类型查询、模式匹配查找等通用操作。
9.1 常用键操作函数
9.1.1 keys() --- 按模式查找键
支持通配符:* 匹配任意字符,? 匹配单个字符,[] 匹配范围。
<?php
$keys=$redis->keys('user:*');//匹配所有user:开头的键
$keys=$redis->keys('user:?:??');//匹配user:1ab等模式
keys() 命令在大数据量下会阻塞 Redis,生产环境应使用 scan() 替代。scan() 是增量式遍历,不会阻塞服务器。
9.1.2 scan() --- 增量遍历键(推荐)
<?php
$iterator=null;
$result=[];
do{
$keys=$redis->scan($iterator,'user:*',100);
if($keys!==false){
$result=array_merge($result,$keys);
}
}while($iterator>0);
print_r($result);
9.1.3 expire() / ttl() --- 过期与查询
<?php
$redis->set('token','abc123');
$redis->expire('token',3600);//设置3600秒后过期
echo $redis->ttl('token');//剩余秒数,如3598
$redis->persist('token');//移除过期,变为永久键
echo $redis->ttl('token');//‑1(永不过期)
$redis->pExpire('token',5000);//5000毫秒后过期
echo $redis->pttl('token');//剩余毫秒数
9.1.4 exists() / type() / rename()
<?php
var_dump($redis->exists('token'));//true/false
echo $redis->type('token');//返回类型常量
echo $redis->type('user:1');//Redis::REDIS_HASH
$redis->rename('old_key','new_key');//重命名键
9.1.5 object() --- 获取键的内部信息
<?php
echo $redis->object('encoding','user:1');//编码方式如'hashtable'
echo $redis->object('refcount','user:1');//引用计数
echo $redis->object('idletime','user:1');//空闲时间(秒)
9.2 键操作函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| keys(pattern) | 常用 | 按模式匹配查找所有键(生产慎用) |
| scan(iter, pat, count) | 常用 | 增量式遍历键(推荐替代 keys) |
| del(key1, key2, ...) | 常用 | 删除一个或多个键 |
| exists(key) | 常用 | 判断键是否存在 |
| expire(key, seconds) | 常用 | 设置键的过期时间(秒) |
| pExpire(key, ms) | 非常用 | 设置键的过期时间(毫秒) |
| expireAt(key, timestamp) | 非常用 | 设置键在指定 Unix 时间戳过期 |
| pExpireAt(key, timestamp) | 非常用 | 设置键在指定毫秒级时间戳过期 |
| ttl(key) | 常用 | 获取键的剩余过期时间(秒),‑1为永不过期 |
| pTtl(key) | 非常用 | 获取键的剩余过期时间(毫秒) |
| persist(key) | 常用 | 移除键的过期时间,变为永久键 |
| type(key) | 常用 | 获取键的数据类型 |
| rename(key, newkey) | 非常用 | 重命名键,新键已存在则覆盖 |
| renameNx(key, newkey) | 非常用 | 仅当新键不存在时重命名 |
| randomKey() | 非常用 | 随机返回一个键名 |
| object(subcmd, key) | 非常用 | 获取键的内部信息(编码/引用数/空闲时间) |
| move(key, db) | 非常用 | 将键移动到指定数据库 |
| dump(key) | 非常用 | 序列化键的值(用于迁移) |
| restore(key, ttl, val) | 非常用 | 反序列化恢复键值 |
| touch(key1, ...) | 非常用 | 修改键的最后访问时间,返回存在键数 |
十、事务(Transaction)操作
Redis 事务通过 MULTI/EXEC 实现:MULTI 开启事务,后续命令进入队列,EXEC 原子性执行队列中的所有命令。WATCH 可实现乐观锁,当被监视的键在事务执行前被修改,事务自动中止。
10.1 基本事务
<?php
$redis->multi();
$redis->set('key1','val1');
$redis->set('key2','val2');
$redis->incr('counter');
$result=$redis->exec();//返回每条命令的执行结果数组
//['val1','val2',新计数]
10.2 链式事务调用
<?php
$ret=$redis->multi()
->set('a','1')
->set('b','2')
->incr('counter')
->exec();
10.3 WATCH 乐观锁
<?php
//监视键,如果事务执行前被修改则中止
$redis->watch('balance');
$balance=$redis->get('balance');
if($balance>=100){
$redis->multi();
$redis->decrBy('balance',100);
$redis->incrBy('paid',100);
$result=$redis->exec();
if($result===false){
echo '事务被中止(数据已被修改)';
}
}else{
$redis->unwatch();
echo '余额不足';
}
10.4 事务函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| multi() | 常用 | 开启事务,后续命令进入队列 |
| exec() | 常用 | 执行事务队列中的所有命令,返回结果数组 |
| discard() | 非常用 | 取消事务,清空命令队列 |
| watch(key1, ...) | 常用 | 监视一个或多个键(乐观锁) |
| unwatch() | 常用 | 取消所有键的监视 |
十一、发布订阅(Pub/Sub)
Redis Pub/Sub 是一种消息通信模式:发布者将消息发送到频道,所有订阅了该频道的客户端都能收到消息。适合实时消息推送、聊天室等场景。
11.1 发布消息
<?php
//向频道发布消息,返回收到消息的订阅者数量
$count=$redis->publish('news','Breaking:PHP8.4 released!');
echo "收到消息的订阅者数:$count\n";
11.2 订阅频道
subscribe() 会阻塞当前进程,持续监听频道消息:
<?php
$redis=new Redis();
$redis->connect('127.0.0.1',6379);
//订阅频道,有消息时触发回调
$redis->subscribe(['news','alerts'],function($redis,$channel,$msg){
echo "[$channel] $msg\n";
});
//此行以下的代码不会执行,subscribe会阻塞
11.3 模式订阅
<?php
//按模式匹配频道名
$redis->psubscribe(['news.*','log.*'],function($redis,$pattern,$channel,$msg){
echo "[pattern=$pattern][channel=$channel] $msg\n";
});
11.4 发布订阅函数速查表
| 方法 | 频率 | 说明 |
|---|---|---|
| publish(channel, msg) | 非常用 | 向频道发布消息,返回订阅者数 |
| subscribe(channels, cb) | 非常用 | 订阅频道,阻塞监听消息 |
| psubscribe(patterns , cb) | 非常用 | 按模式订阅频道 |
| unsubscribe(channels) | 非常用 | 取消订阅频道 |
| punsubscribe(patterns) | 非常用 | 取消模式订阅 |
| pubsub(subcmd, args) | 非常用 | 查询 Pub/Sub 状态(频道/订阅者数) |
十二、常用函数汇总
以下是 Redis 在 PHP 项目中实际使用频率最高的函数分类汇总,掌握这些函数即可覆盖绝大多数日常开发需求。
12.1 连接类常用函数
| 方法 | 功能说明 |
|---|---|
| connect(host, port, timeout) | 创建 Redis 连接 |
| pconnect(host, port, timeout) | 创建持久连接(PHP‑FPM 推荐) |
| auth(password) | 密码认证 |
| select(db) | 选择数据库 |
| close() | 关闭连接 |
| ping() | 测试连接 |
12.2 字符串类常用函数
| 方法 | 功能说明 |
|---|---|
| set(key, value, opts) | 设置键值,可带过期/NX/XX 选项 |
| get(key) | 获取键值 |
| del(key1, key2, ...) | 删除键 |
| exists(key) | 判断键是否存在 |
| incr(key) / decr(key) | 自增/自减1 |
| incrBy(key, n) / decrBy(key, n) | 自增/自减指定整数 |
| setex(key, ttl, value) | 设置带过期的键值 |
| setnx(key, value) | 仅当键不存在时设置 |
| mset(array) / mget(array) | 批量设置/获取 |
| getSet(key, value) | 设置新值返回旧值 |
| append(key, value) | 追加字符串 |
| strlen(key) | 获取值长度 |
12.3 哈希类常用函数
| 方法 | 功能说明 |
|---|---|
| hSet(key, field, value) | 设置字段值 |
| hGet(key, field) | 获取字段值 |
| hMSet(key, array) | 批量设置字段 |
| hMGet(key, array) | 批量获取字段 |
| hGetAll(key) | 获取所有字段和值 |
| hDel(key, field1, ...) | 删除字段 |
| hExists(key, field) | 判断字段是否存在 |
| hLen(key) | 获取字段数量 |
| hIncrBy(key, field, n) | 字段值自增 |
| hKeys(key) / hVals(key) | 获取所有字段名/值 |
12.4 列表类常用函数
| 方法 | 功能说明 |
|---|---|
| lPush(key, val) / rPush(key, val) | 从左/右入队 |
| lPop(key) / rPop(key) | 从左/右出队 |
| lRange(key, start, end) | 获取范围元素 |
| lLen(key) | 获取列表长度 |
| lRem(key, val, count) | 移除指定元素 |
| lTrim(key, start, end) | 裁剪列表 |
12.5 集合类常用函数
| 方法 | 功能说明 |
|---|---|
| sAdd(key, member1, ...) | 添加成员 |
| sRem(key, member1, ...) | 移除成员 |
| sMembers(key) | 获取所有成员 |
| sIsMember(key, member) | 判断成员是否存在 |
| sCard(key) | 获取成员数量 |
| sInter(k1, k2, ...) | 交集 |
| sUnion(k1, k2, ...) | 并集 |
| sDiff(k1, k2, ...) | 差集 |
12.6 有序集合类常用函数
| 方法 | 功能说明 |
|---|---|
| zAdd(key, score, member) | 添加成员及分数 |
| zRem(key, member) | 移除成员 |
| zRange(key, s, e, w) | 升序获取成员 |
| zRevRange(key, s, e, w) | 降序获取成员 |
| zRangeByScore(key, min, max) | 按分数范围获取 |
| zScore(key, member) | 获取分数 |
| zIncrBy(key, incr, member) | 增加分数 |
| zRank(key, member) | 获取升序排名 |
| zRevRank(key, member) | 获取降序排名 |
| zCard(key) / zCount(key, min, max) | 获取成员数/范围内成员数 |
12.7 键操作常用函数
| 方法 | 功能说明 |
|---|---|
| keys(pattern) | 按模式查找键(慎用) |
| scan(iterator, pattern, count) | 增量遍历键(推荐) |
| expire(key, seconds) | 设置过期时间 |
| ttl(key) | 查询剩余过期时间 |
| persist(key) | 移除过期时间 |
| type(key) | 获取数据类型 |
12.8 事务常用函数
multi()、exec()、watch(key1, ...)、unwatch()
十三、非常用函数汇总
以下函数在实际开发中使用频率较低,但在特定场景下非常有用。了解这些函数有助于在遇到特殊需求时选择合适的工具。
13.1 字符串非常用函数
| 方法 | 功能说明 |
|---|---|
| incrByFloat(key, float) | 浮点数自增 |
| psetex(key, ttl, value) | 毫秒级过期设置 |
| msetnx(array) | 批量设置(全部不存在时才成功) |
| getRange(key, start, end) | 获取子字符串 |
| setRange(key, offset, value) | 按偏移覆写值 |
| getBit(key, offset) | 获取比特位 |
| setBit(key, offset, val) | 设置比特位 |
| bitCount(key) | 统计值为1的比特数 |
| bitOp(op, dest, key1, ...) | 位运算(AND/OR/XOR/NOT) |
13.2 哈希非常用函数
| 方法 | 功能说明 |
|---|---|
| hIncrByFloat(key, field, float) | 字段浮点数自增 |
| hSetNx(key, field, value) | 字段不存在时设置 |
| hStrLen(key, field) | 字段值字节长度 |
| hScan(key, iterator, opts) | 增量迭代哈希字段 |
13.3 列表非常用函数
| 方法 | 功能说明 |
|---|---|
| lIndex(key, index) | 获取指定索引元素 |
| lSet(key, index, value) | 设置指定索引元素 |
| lInsert(key, pos, pivot, val) | 在 pivot 前/后插入 |
| blPop(keys, timeout) | 阻塞式左弹出 |
| brPop(keys, timeout) | 阻塞式右弹出 |
| rpoplpush(src, dst) | 尾弹出推入头部 |
| brpoplpush(src, dst, t) | 阻塞式 rpoplpush |
| lPushx(key, val) / rPushx(key, val) | 列表存在时插入 |
13.4 集合非常用函数
| 方法 | 功能说明 |
|---|---|
| sPop(key) | 随机弹出成员 |
| sRandMember(key, count) | 随机获取成员 |
| sInterStore(dst, k1, ...) | 交集存入新集合 |
| sUnionStore(dst, k1, ...) | 并集存入新集合 |
| sDiffStore(dst, k1, ...) | 差集存入新集合 |
| sMove(src, dst, member) | 移动成员到另一集合 |
| sScan(key, iterator, opts) | 增量迭代集合成员 |
13.5 有序集合非常用函数
| 方法 | 功能说明 |
|---|---|
| zRemRangeByRank(key, s, e) | 按排名范围删除 |
| zRemRangeByScore(key , min, max) | 按分数范围删除 |
| zUnion(dst, keys, opts) | 有序集合并集 |
| zInter(dst, keys, opts) | 有序集合交集 |
| zRangeByLex(key, min, max) | 按字典序获取 |
| zScan(key, iterator, opts) | 增量迭代成员 |
13.6 键操作非常用函数
| 方法 | 功能说明 |
|---|---|
| pExpire(key, ms) | 设置毫秒过期 |
| expireAt(key, timestamp) | 时间戳秒级过期 |
| pExpireAt(key, timestamp) | 毫秒级时间戳过期 |
| pTtl(key) | 毫秒级剩余时间 |
| rename(key, newkey) | 重命名 |
| renameNx(key, newkey) | 不存在时重命名 |
| randomKey() | 随机返回一个键 |
| object(subcmd, key) | 获取键内部信息 |
| move(key, db) | 移动到其他数据库 |
| dump(key) | 序列化键值 |
| restore(key, ttl, val) | 反序列化恢复 |
| touch(key1, ...) | 修改最后访问时间 |
13.7 服务器管理非常用函数
| 方法 | 功能说明 |
|---|---|
| info(section) | 获取 Redis 服务器信息 |
| config(cmd, key) | 获取/设置配置参数 |
| dbSize() | 获取当前数据库键数量 |
| flushDB() | 清空当前数据库 |
| flushAll() | 清空所有数据库 |
| time() | 获取 Redis 服务器时间 |
| bgSave() | 后台异步保存(RDB) |
| save() | 同步保存(会阻塞) |
| lastSave() | 获取上次保存时间戳 |
| slaveOf(host, port) | 设置主从复制 |
| slowlog(subcmd, arg) | 慢查询日志 |
| client(cmd, args) | 客户端管理 |
| eval(script, keys, args) | 执行 Lua 脚本 |
| rawCommand(...) | 发送原始 Redis 命令 |
十四、Redis 扩展用法
14.1 Pipeline 管道
Pipeline 允许一次性发送多条命令,减少网络往返延迟。适合批量写入或读取场景。管道内的命令不保证原子性。
<?php
$redis=new Redis();
$redis->connect('127.0.0.1',6379);
//方式一:数组形式
$redis->pipeline();
for($i=0;$i<1000;$i++){
$redis->set("key:$i","val:$i");
}
$results=$redis->exec();//返回1000条结果
//方式二:回调形式(推荐)
$results=$redis->pipeline(function($pipe){
for($i=0;$i<1000;$i++){
$pipe->set("key:$i","val:$i");
}
});
Pipeline 与 multi() 的区别:Pipeline 是客户端行为,减少网络往返;multi/exec 是服务端事务,保证原子性。两者可以组合使用。
14.2 分布式锁
利用 SET NX EX 实现分布式锁,确保在分布式环境中同一资源只有一个客户端能操作。
<?php
class RedisLock
{
private Redis $redis;
private string $lockKey;
private string $token;//唯一标识,防止误解锁
public function __construct(Redis $redis,string $key,int $ttl=10)
{
$this->redis=$redis;
$this->lockKey="lock:{$key}";
$this->token=uniqid('',true);
//SETNX EX实现原子加锁
$ok=$redis->set($this->lockKey,$this->token,['nx','ex'=>$ttl]);
if(!$ok){
throw new RuntimeException('获取锁失败');
}
}
public function release():bool
{
//用Lua脚本保证原子解锁(验证token后删除)
$script="
if redis.call('get',KEYS[1])==ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end
";
return(bool)$this->redis->eval($script,
[$this->lockKey,$this->token],1);
}
}
//使用示例
try{
$lock=new RedisLock($redis,'order:1001',30);
//执行业务逻辑...
$lock->release();
}catch(RuntimeException $e){
echo $e->getMessage();
}
14.3 计数器与限流
利用 INCR 的原子性实现接口限流(固定窗口算法):
<?php
function rateLimit(Redis $redis,string $key,int $max,int $period):bool
{
$count=$redis->incr($key);
if($count===1){
$redis->expire($key,$period);//首次访问设置过期窗口
}
return $count<=$max;
}
//每分钟最多100次请求
if(!rateLimit($redis,'api:limit:user:1001',100,60)){
http_response_code(429);
echo '请求过于频繁,请稍后再试';
exit;
}
14.4 缓存模式(Cache‑Aside)
<?php
function getCachedData(Redis $redis,string $cacheKey,callable $callback,int $ttl=3600)
{
//1.先从缓存读取
$data=$redis->get($cacheKey);
if($data!==false){
return json_decode($data,true);
}
//2.缓存未命中,从数据源获取
$data=$callback();
//3.写入缓存
$redis->setex($cacheKey,$ttl,json_encode($data));
return $data;
}
//使用示例
$users=getCachedData($redis,'users:active',function(){
//从数据库查询
return Db::table('users')->where('status',1)->limit(100)->select();
},300);//缓存5分钟
14.5 Session 存储
将 PHP Session 存储到 Redis,支持多台服务器共享会话:
//php.ini配置
session.save_handler=redis
session.save_path="tcp://127.0.0.1:6379?auth=yourpass&database=1"
<?php
//或在代码中动态设置
ini_set('session.save_handler','redis');
ini_set('session.save_path','tcp://127.0.0.1:6379?auth=yourpass&database=1');
session_start();
14.6 延时队列(Sorted Set 实现)
利用 Sorted Set 的 score 存储执行时间戳,实现延时任务:
<?php
//投递延时任务
function delayPush(Redis $redis,string $queue,$data,int $delay):void
{
$execTime=time()+$delay;
$redis->zAdd($queue,$execTime,json_encode($data));
}
//消费延时任务
function delayPop(Redis $redis,string $queue)
{
$now=time();
//获取score<=当前时间的第一个成员
$items=$redis->zRangeByScore($queue,0,$now,['limit'=>[0,1]]);
if(empty($items)){
return null;
}
$data=$items[0];
//原子删除(防止并发重复消费)
$removed=$redis->zRem($queue,$data);
if($removed>0){
return json_decode($data,true);
}
return null;
}
14.7 位图实现签到统计
<?php
//用户签到:将每月的某天对应位设为1
function checkIn(Redis $redis,int $userId,int $year,int $month,int $day):void
{
$key="signin:{$userId}:{$year}{$month}";
$offset=$day‑1;
$redis->setBit($key,$offset,1);
}
//查询本月签到天数
function getCheckInCount(Redis $redis,int $userId,int $year,int $month):int
{
$key="signin:{$userId}:{$year}{$month}";
return $redis->bitCount($key);
}
//判断某天是否签到
function isCheckedIn(Redis $redis,int $userId,int $year,int $month,int $day):bool
{
$key="signin:{$userId}:{$year}{$month}";
return(bool)$redis->getBit($key,$day‑1);
}
14.8 Lua 脚本执行
通过 eval() 执行 Lua 脚本,保证多条命令的原子性:
<?php
//原子性扣库存脚本
$script="
local stock=tonumber(redis.call('get',KEYS[1]))
if stock==nil or stock<tonumber(ARGV[1])then
return 0--库存不足
end
redis.call('decrby',KEYS[1],ARGV[1])
return 1--扣减成功
";
$result=$redis->eval($script,['product:1001:stock',1],1);
echo $result?'扣减成功':'库存不足';
14.9 Redis 集群连接
<?php
//RedisCluster连接(phpredis3.0+)
$redis=new RedisCluster(null,
['10.0.0.1:7000','10.0.0.2:7000','10.0.0.3:7000'],
2.5,//连接超时
3.0,//读取超时
true//启用持久连接
);
//带认证的集群连接
$redis=new RedisCluster(
'my_persistent_id',
['10.0.0.1:7000','10.0.0.2:7000','10.0.0.3:7000'],
2.5,3.0,true,
['auth'=>['myuser','mypass']]
);
$redis->set('key','value');
echo $redis->get('key');
十五、Redis 最佳实践
15.1 键命名规范
使用冒号分隔的层级命名,形成命名空间:
user:1001:profile//用户资料user:1001:settings//用户设置cache:article:200//文章缓存queue:email:pending//邮件队列lock:order:5001//订单分布式锁
15.2 合理选择数据结构
- 缓存单个值:使用 String + set/get
- 存储对象/结构化数据:使用 Hash + hSet/hGet,可单独修改字段
- 消息队列:使用 List + lPush/rPop 或 BLPOP
- 排行榜:使用 Sorted Set + zAdd/zRevRange
- 标签/去重:使用 Set + sAdd/sIsMember
- 签到/布尔标记:使用 Bitmap + setBit/bitCount
- 计数器:使用 String + incr/decr
- 延时队列:使用 Sorted Set + score 存储执行时间
15.3 过期策略
- 缓存键必须设置 TTL,避免内存无限增长。
- 热点数据 TTL 可适当延长,冷数据设置较短 TTL。
- 避免大量键同时过期(缓存雪崩),可在 TTL 上加随机偏移。
- 使用 setex 一步完成设置值和过期,避免两次命令。
15.4 连接管理
- PHP‑FPM 环境使用 pconnect() 持久连接,减少 TCP 连接开销。
- 避免在循环中反复创建 Redis 连接,应在脚本入口创建一次。
- 连接超时和读取超时应根据业务合理设置,防止阻塞。
- 生产环境应设置 Redis 密码,不要暴露默认端口到公网。
15.5 批量操作优化
- 大量写入使用 pipeline() 管道,减少网络往返。
- 批量获取使用 mget/hmget 替代循环 get/hget。
- 遍历键使用 scan() 替代 keys(),防止阻塞 Redis。
- 避免大 key 操作(如 hGetAll 大哈希),考虑分页或 hScan。
15.6 内存优化
- 使用合适的数据结构编码:小 Hash/Set 会自动使用 ziplist 压缩编码。
- 控制单个键的体积,避免大 Hash(字段过多)和大 List(元素过多)。
- 定期清理无用键和过期键,监控 used_memory 指标。
- 使用 expire 设置 TTL,让 Redis 自动回收内存。
- 对象存储优先用 Hash 而非序列化为 String,可节省内存。
15.7 安全建议
- 设置 requirepass 或使用 ACL 限制访问。
- 绑定 bind 127.0.0.1 或内网 IP,禁止外网直接访问。
- 禁用危险命令:rename‑command FLUSHALL "" 等方式屏蔽。
- 使用 SSL/TLS 加密跨网络传输。
- 定期备份 RDB 文件,开启 AOF 持久化。
15.8 错误处理
<?php
try{
$redis=new Redis();
$redis->connect('127.0.0.1',6379,2.5);
$redis->auth('yourpass');
$redis->set('key','value');
$val=$redis->get('key');
}catch(RedisException $e){
//Redis连接或操作异常
error_log('Redis error:'.$e->getMessage());
//降级处理:从数据库读取或返回默认值
$val=null;
}
本教程涵盖了 Redis 在 PHP 中的安装配置、连接方式、五大数据类型的完整操作函数、事务、发布订阅、常用与非常用函数分类、扩展用法及最佳实践。掌握这些内容,即可在实际项目中高效使用 Redis 解决缓存、计数、队列、锁等问题。