当前位置: 首页 > 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.jsqmd.com/news/928833/

相关文章:

  • 2026汇泉胶粉选购指南:纸品包装全场景裱纸胶粉权威推荐 - 速递信息
  • 《2026 年 IT 行业最有前途的 7 个方向,选错了再努力也没用》
  • 如何彻底解决Switch手柄问题:Joy-Con Toolkit完整指南
  • attention 的mask 的简单实现
  • 从Input.GetAxis到手感调优:详解Unity中移动与旋转的平滑处理与参数配置
  • ChatGPT核心原理、高阶应用与提示词实战指南
  • 2026四川绵阳江油手机店哪家好?二手手机、手机分期去哪家? - 博客万
  • 如何平衡CSP-J备赛与校内学习
  • 变更管理在软考中级系统集成项目管理工程师考试中占多少分 - 众智商学院官方
  • 【Gemini推送通知优化实战指南】:20年专家亲授5大性能瓶颈与98%送达率提升方案
  • 3步解锁经典游戏潜能:WarcraftHelper魔兽争霸III终极优化方案
  • 全国自闭症全托机构实力排行:合规与服务质量测评 - 奔跑123
  • 从ChatGPT-5到AGI:技术演进、行业重塑与个人应对指南
  • 2026沃尔玛购物卡回收避坑|别再低价贱卖!4大平台实测,差距太大了 - 资讯快报
  • 长沙二手手表回收攻略,实地走访多家门店,教你选对靠谱渠道 - 合扬奢侈品交易中心
  • MySQL 事务管理全解:从 ACID 特性、隔离级别到 MVCC 底层原理
  • MEMS 加速度计耳机敲击算法
  • 热点警示:毕业论文抽查力度加大,这8款AI毕业论文工具成毕业生“刚需” - 逢君学术-AI论文写作
  • 比特币的浩克体质:能源消耗、安全机制与AI量子计算博弈
  • 国内专业自闭症全托机构质量实测排行 核心维度对比 - 奔跑123
  • Web应用技术第二次作业
  • GetQzonehistory专业实践:掌握高效QQ空间说说备份与数据归档技巧
  • 抖音视频如何保存到相册:全场景操作方法与保存失败原因解决方案 - 科技热点发布
  • 2026年6月沈阳手表回收推荐:添价收综合服务稳定性更强 - 薛定谔的梨花猫
  • Docker和Kubernetes(K8s)的区别和联系
  • 智能客服系统进入工单管理,企业服务开始重视风险分层
  • Java变量:从“盒子”比喻到代码实战
  • 留学生无实习经验求职指南:结构性困境与系统化破局
  • 1 ROS和ROS2是什么?--读后感
  • 2026年黑龙江/哈尔滨本地门窗最新推荐榜单:厨房隔断、低碳环保、防寒保暖、防风抗压、恒温节能、极窄推拉门窗源头生产基地与工装配套之选 - 品牌企业推荐师(官方)