当前位置: 首页 > news >正文

PHP与Redis缓存实践完整方案

PHP与Redis缓存实践完整方案

Redis在PHP项目里太常用了。缓存、队列、计数器、排行榜,各种场景都能用上。今天写一份完整的Redis实践指南。

先从最基础的连接和基本操作说起。

```php
// 连接Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 如果有密码
// $redis->auth('password');
// 选择数据库
// $redis->select(1);

echo "连接状态: " . ($redis->ping() ? '正常' : '异常') . "\n";

// 字符串操作
$redis->set('key1', 'value1');
echo $redis->get('key1') . "\n";

// 带过期时间的设置
$redis->setex('session:123', 3600, 'user_data');
$redis->set('temp', '临时数据', 60); // 60秒过期

// 批量操作
$redis->mSet(['k1' => 'v1', 'k2' => 'v2', 'k3' => 'v3']);
$values = $redis->mGet(['k1', 'k2', 'k3']);
print_r($values);
?>
```

List可以用作队列或栈。

```php
// 列表操作
$redis->del('queue');

// 从左边推入
$redis->lPush('queue', '任务C');
$redis->lPush('queue', '任务B');
$redis->lPush('queue', '任务A');

// 从右边弹出(先进先出)
while ($task = $redis->rPop('queue')) {
echo "处理: $task\n";
}

// 队列长度
$redis->lPush('queue', 'task1', 'task2', 'task3');
echo "队列长度: " . $redis->lLen('queue') . "\n";

// 范围获取
$range = $redis->lRange('queue', 0, -1);
print_r($range);

// 阻塞弹出(等待队列有数据)
// $task = $redis->brPop(['queue'], 5); // 等待5秒
?>
```

Hash适合存储对象类型的数据。

```php
$redis->del('user:1001');

// 存储用户信息
$redis->hSet('user:1001', 'name', '张三');
$redis->hSet('user:1001', 'age', 28);
$redis->hSet('user:1001', 'email', 'zhangsan@test.com');

// 获取单个字段
echo "姓名: " . $redis->hGet('user:1001', 'name') . "\n";

// 获取所有字段
$user = $redis->hGetAll('user:1001');
print_r($user);

// 批量设置
$redis->hMSet('user:1002', [
'name' => '李四',
'age' => 35,
'email' => 'lisi@test.com',
]);

// 字段是否存在
echo "存在name: " . ($redis->hExists('user:1002', 'name') ? '是' : '否') . "\n";

// 获取所有键和值
$fields = $redis->hKeys('user:1001');
$values = $redis->hVals('user:1001');
print_r($fields);
print_r($values);

// 计数器
$redis->hIncrBy('user:1001', 'login_count', 1);
echo "登录次数: " . $redis->hGet('user:1001', 'login_count') . "\n";
?>
```

Set适合做集合运算。

```php
// 用户标签
$redis->sAdd('user:1:tags', 'PHP', 'JavaScript', 'MySQL', 'Redis');
$redis->sAdd('user:2:tags', 'PHP', 'Python', 'Docker', 'Redis');

// 共同标签(交集)
$commonTags = $redis->sInter('user:1:tags', 'user:2:tags');
echo "共同标签: " . implode(', ', $commonTags) . "\n";

// 所有标签(并集)
$allTags = $redis->sUnion('user:1:tags', 'user:2:tags');
echo "所有标签: " . implode(', ', $allTags) . "\n";

// 差异标签
$diffTags = $redis->sDiff('user:1:tags', 'user:2:tags');
echo "User1特有: " . implode(', ', $diffTags) . "\n";

// 随机获取成员
$random = $redis->sRandMember('user:1:tags', 2);
echo "随机标签: " . implode(', ', $random) . "\n";

// 集合大小
echo "User1标签数: " . $redis->sCard('user:1:tags') . "\n";
?>
```

Sorted Set(有序集合)适合排行榜场景。

```php
// 游戏排行榜
$redis->zAdd('leaderboard', 9500, '张三');
$redis->zAdd('leaderboard', 8800, '李四');
$redis->zAdd('leaderboard', 9200, '王五');
$redis->zAdd('leaderboard', 7800, '赵六');
$redis->zAdd('leaderboard', 9900, '钱七');

// 获取前3名(从高到低)
$top3 = $redis->zRevRange('leaderboard', 0, 2, true);
echo "排行榜前三:\n";
foreach ($top3 as $player => $score) {
echo " $player: $score分\n";
}

// 获取某人的排名
$rank = $redis->zRevRank('leaderboard', '李四');
echo "李四排名: 第" . ($rank + 1) . "名\n";

// 获取某人的分数
echo "张三分数: " . $redis->zScore('leaderboard', '张三') . "\n";

// 增加分数
$redis->zIncrBy('leaderboard', 100, '王五');
echo "王五加分后: " . $redis->zScore('leaderboard', '王五') . "\n";

// 统计分数区间人数
$count = $redis->zCount('leaderboard', 8000, 9500);
echo "8000-9500分人数: $count\n";
?>
```

Redis的发布订阅模式可以用于消息通知。

```php
// 发布者
function publishMessage(Redis $redis, string $channel, string $message): void
{
$redis->publish($channel, $message);
echo "已发布到频道 $channel: $message\n";
}

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
publishMessage($redis, 'notifications', '有新订单');
publishMessage($redis, 'notifications', '用户注册成功');
?>
```

Redis的Lua脚本可以在服务端执行原子操作。

```php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// Lua脚本:限流
$rateLimitScript = '
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

local current = redis.call("GET", key)
if current and tonumber(current) >= limit then
return 0
end

redis.call("INCR", key)
redis.call("EXPIRE", key, window)
return 1
';

$redis->eval($rateLimitScript, ['rate_limit:api', 100, 60], 1);
echo "请求通过\n";
?>

分布式锁也是Redis的常见用途。

class RedisLock
{
private Redis $redis;
private string $prefix = 'lock:';
private int $defaultTTL = 10;

public function __construct(Redis $redis)
{
$this->redis = $redis;
}

public function acquire(string $key, int $ttl = null): ?string
{
$ttl = $ttl ?: $this->defaultTTL;
$lockKey = $this->prefix . $key;
$token = bin2hex(random_bytes(16));

// SET NX EX 原子操作
$result = $this->redis->set($lockKey, $token, ['NX', 'EX' => $ttl]);

if ($result) {
return $token;
}

return null;
}

public function release(string $key, string $token): bool
{
$lockKey = $this->prefix . $key;

// Lua脚本保证原子性:只有持有锁的人才能释放
$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, [$lockKey, $token], 1);
}

public function withLock(string $key, callable $callback, int $ttl = null): mixed
{
$token = $this->acquire($key, $ttl);

if ($token === null) {
throw new RuntimeException("无法获取锁: $key");
}

try {
return $callback();
} finally {
$this->release($key, $token);
}
}
}

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$lock = new RedisLock($redis);

try {
$lock->withLock('process:order:123', function () {
echo "处理订单中...\n";
sleep(1);
echo "订单处理完成\n";
return ['status' => 'success'];
});
} catch (RuntimeException $e) {
echo "错误: {$e->getMessage()}\n";
}
?>
```

Redis在实际项目中的性能优势很明显。但要注意内存管理,设置合理的过期策略。还有持久化配置,RDB和AOF各有优劣。数据结构的选用也要看具体场景,不是所有数据都适合放Redis。

http://www.gsyq.cn/news/1440167.html

相关文章:

  • 2026汇泉胶粉选购指南:纸品包装全场景裱纸胶粉权威推荐 - 速递信息
  • 如何彻底解决Switch手柄问题:Joy-Con Toolkit完整指南
  • 如何平衡CSP-J备赛与校内学习
  • MEMS 加速度计耳机敲击算法
  • 热点警示:毕业论文抽查力度加大,这8款AI毕业论文工具成毕业生“刚需” - 逢君学术-AI论文写作
  • 国内专业自闭症全托机构质量实测排行 核心维度对比 - 奔跑123
  • Docker和Kubernetes(K8s)的区别和联系
  • 2026年6月沈阳手表回收推荐:添价收综合服务稳定性更强 - 薛定谔的梨花猫
  • 2026年天津装修公司哪家口碑最好?深度测评:如何匹配最佳家装方案 - 资讯快报
  • 2026年黑龙江/哈尔滨本地门窗最新推荐榜单:厨房隔断、低碳环保、防寒保暖、防风抗压、恒温节能、极窄推拉门窗源头生产基地与工装配套之选 - 品牌企业推荐师(官方)
  • 终极Gofile下载指南:3分钟掌握高效文件获取技巧
  • KingbaseES-Windows单机实例运行机制与环境基线检查
  • 别再让VR角色穿模了!用XR Interaction Toolkit搞定CharacterController碰撞(Unity 2022 LTS实测)
  • 代理现货库存CYPD3175-24LQXQT高度集成USB Type-C PD端口控制器,综合性能优异、适配场景广,是快充电源领域的成熟方案。
  • 2026 南宁手表回收全攻略,添价收手表回收教你科学处理闲置名表 - 薛定谔的梨花猫
  • 用 ABAP CDS View 读取 SAP 表中每个采购订单行的最新记录
  • 学习C#调用OpenXml操作word文档的基本用法(39:学习表格类-1)
  • 如何轻松实现跨平台输入法词库迁移:深蓝词库转换终极指南
  • 2026年执行律师深度测评:如何为你的胜诉回款匹配最佳方案? - 资讯快报
  • 魔兽争霸3终极优化教程:5分钟免费解锁高帧率与宽屏体验
  • 告别哑巴NPC!用RT-Voice PRO 2023.1.0为你的Unity游戏注入灵魂语音(附完整代码示例)
  • 3.47 室内环境下全向成像孪生神经网络机器人定位的实验评价
  • Omniapp:AI与区块链融合的DApp平台与OMP代币经济解析
  • 避坑指南:YOLOv8训练自定义数据集时,为什么你的‘小球’总是检测不准?
  • Joy-Con Toolkit:如何快速掌握Switch手柄调试与个性化定制的终极指南
  • 智慧树刷课插件:三步实现自动化学习,告别手动刷课烦恼
  • 2026年当前,如何甄选高性价比的丽江大理私家定制小团:一份面向决策者的专业指南 - 2026年企业资讯
  • NLP内容审核中回收语言的困境与多元标注解决方案
  • 别再只会crontab -e了!Linux定时任务从入门到精通,这5个实战脚本和3个高级用法你得会
  • 小红书去水印保存图片怎么操作2026全场景高清无损操作方法汇总 - 科技热点发布