Redis使用指南完整教程:从入门到生产级部署

Redis使用指南完整教程:从入门到生产级部署

摘要

本教程全面介绍Redis的使用方法,涵盖基础概念、数据结构详解、三服务器生产环境配置、常用命令实战、PHP/Laravel集成、性能优化、监控告警、故障处理等。基于真实生产环境经验,提供可直接复制的配置和代码。


第一部分:Redis基础认知

1.1 什么是Redis?

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息中间件。它由Salvatore Sanfilippo于2009年创建,现在是世界上最流行的键值存储系统之一。

核心特性

1. 高性能:基于内存操作,读写速度可达10万次/秒以上

2. 丰富数据结构:支持String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Geospatial等

3. 原子性操作:所有操作都是原子性的,要么成功要么失败

4. 持久化支持:支持RDB快照和AOF日志两种持久化方式

5. 主从复制:支持数据复制,实现读写分离和高可用

6. 集群支持:Redis Cluster提供自动分片和高可用

典型应用场景

缓存层:减轻数据库压力,提升响应速度

会话存储:分布式会话管理

排行榜:利用Sorted Set实现实时排名

计数器:文章阅读数、点赞数等

消息队列:利用List结构实现简单队列

分布式锁:实现跨服务器的同步控制

1.2 Redis vs Memcached

特性 | Redis | Memcached ——|——-|———– 数据类型 | 丰富(8种) | 仅String 持久化 | 支持 | 不支持 集群 | 支持 | 不支持 单线程 | 是 | 是 内存效率 | 较高 | 更高 性能 | 极高 | 极高

选择建议

– 简单缓存场景:Memcached

– 需要复杂数据结构、持久化:Redis

– 大多数现代应用:Redis

1.3 Redis性能基准

硬件环境:Intel Xeon E5-2680 v4 @ 2.4GHz,64GB RAM

测试结果

– GET操作:~110,000 requests/second

– SET操作:~85,000 requests/second

– INCR操作:~95,000 requests/second

– LPUSH操作:~90,000 requests/second

生产环境建议

– 预期QPS:50,000-80,000(单实例)

– 通过主从复制可扩展至100,000+ QPS

– 使用Redis Cluster可线性扩展


第二部分:数据结构详解

2.1 String(字符串)

特点

– 最基本的数据类型

– 最大值512MB

– 可以是字符串、整数、浮点数

常用命令

# 基本操作

SET user:1:name "张三"

GET user:1:name

SETEX session:abc123 3600 "user_data" # 设置3600秒过期

计数器

INCR article:123:views # 自增1

INCRBY article:123:likes 5 # 自增5

DECR article:123:dislikes # 自减1

批量操作

MGET user:1:name user:1:age user:1:email

MSET user:1:name "张三" user:1:age "25" user:1:email "test@your-domain.com"

不存在才设置(分布式锁基础)

SET lock:resource "uuid" NX EX 10

实战案例

// 文章阅读数计数器

function incrementArticleViews($articleId) {

$redis = new Redis();

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

$redis->auth('password');

$key = "article:{$articleId}:views";

$views = $redis->incr($key);

// 每增长100次,同步到数据库

if ($views % 100 == 0) {

syncViewsToDatabase($articleId, $views);

}

return $views;

}

// 分布式锁实现

function acquireLock($resource, $timeout = 10) {

$redis = new Redis();

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

$redis->auth('password');

$lockKey = "lock:{$resource}";

$lockValue = uniqid('', true);

// NX: 不存在才设置,EX: 设置过期时间

$acquired = $redis->set($lockKey, $lockValue, ['NX', 'EX' => $timeout]);

if ($acquired) {

return $lockValue; // 返回锁的唯一标识

}

return false;

}

function releaseLock($resource, $lockValue) {

$redis = new Redis();

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

$redis->auth('password');

$lockKey = "lock:{$resource}";

// Lua脚本确保只释放自己持有的锁

$script = "

if redis.call('get', KEYS[1]) == ARGV[1] then

return redis.call('del', KEYS[1])

else

return 0

end

";

return $redis->eval($script, [$lockKey, $lockValue], 1);

}

2.2 Hash(哈希)

特点

– 适合存储对象

– 一个Hash最多可存储2^32-1个键值对

– 内存优化:小于512字节的值使用ziplist编码

常用命令

# 基本操作

HSET user:1 name "张三" age "25" email "test@your-domain.com"

HGET user:1 name

HGETALL user:1 # 获取所有字段

HDEL user:1 email # 删除字段

批量操作

HMSET user:2 name "李四" age "30" city "北京"

HMGET user:2 name age

字段操作

HEXISTS user:1 name # 检查字段是否存在

HKEYS user:1 # 获取所有字段名

HVALS user:1 # 获取所有值

HLEN user:1 # 获取字段数量

计数器

HINCRBY user:1 login_count 1

实战案例

// 用户信息缓存

function cacheUserInfo($userId, $userInfo) {

$redis = new Redis();

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

$redis->auth('password');

$key = "user:{$userId}";

$redis->hmset($key, $userInfo);

// 设置1小时过期

$redis->expire($key, 3600);

}

function getUserInfo($userId) {

$redis = new Redis();

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

$redis->auth('password');

$key = "user:{$userId}";

$userInfo = $redis->hgetall($key);

if (empty($userInfo)) {

// 缓存未命中,从数据库加载

$userInfo = loadUserFromDatabase($userId);

if ($userInfo) {

cacheUserInfo($userId, $userInfo);

}

}

return $userInfo;

}

// 购物车实现

function addToCart($userId, $productId, $quantity) {

$redis = new Redis();

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

$redis->auth('password');

$key = "cart:{$userId}";

// 增加商品数量

$redis->hincrby($key, $productId, $quantity);

// 设置24小时过期

$redis->expire($key, 86400);

}

function getCart($userId) {

$redis = new Redis();

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

$redis->auth('password');

$key = "cart:{$userId}";

return $redis->hgetall($key);

}

2.3 List(列表)

特点

– 双向链表实现

– 支持头尾插入和弹出

– 最大长度2^32-1

常用命令

# 栈操作(LPUSH + LPOP)

LPUSH stack "item1"

LPUSH stack "item2"

LPOP stack # 返回"item2"

队列操作(LPUSH + RPOP)

LPUSH queue "task1"

LPUSH queue "task2"

RPOP queue # 返回"task1"

阻塞队列

BLPOP queue 0 # 永久阻塞,直到有数据

BRPOP queue 10 # 阻塞10秒

范围查询

LRANGE mylist 0 -1 # 获取所有元素

LRANGE mylist 0 9 # 获取前10个元素

LTRIM mylist 0 9 # 保留前10个,删除其余

列表操作

LLEN mylist # 获取列表长度

LINDEX mylist 0 # 获取索引为0的元素

LINSERT mylist BEFORE "item2" "item1.5"

实战案例

// 消息队列实现

class MessageQueue {

private $redis;

private $queueName;

public function __construct($queueName) {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6379);

$this->redis->auth('password');

$this->queueName = "queue:{$queueName}";

}

// 生产者

public function push($message) {

return $this->redis->rpush($this->queueName, json_encode($message));

}

// 消费者

public function pop($timeout = 0) {

$data = $this->redis->blpop($this->queueName, $timeout);

if ($data) {

return json_decode($data[1], true);

}

return null;

}

// 批量生产

public function pushBatch($messages) {

$pipeline = $this->redis->pipeline();

foreach ($messages as $message) {

$pipeline->rpush($this->queueName, json_encode($message));

}

return $pipeline->exec();

}

// 获取队列长度

public function size() {

return $this->redis->llen($this->queueName);

}

}

// 最新文章列表(时间线)

function addArticleToTimeline($articleId, $timestamp) {

$redis = new Redis();

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

$redis->auth('password');

$key = "timeline:articles";

// 添加到列表头部

$redis->lpush($key, $articleId);

// 只保留最新1000篇

$redis->ltrim($key, 0, 999);

}

function getLatestArticles($limit = 20) {

$redis = new Redis();

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

$redis->auth('password');

$key = "timeline:articles";

$articleIds = $redis->lrange($key, 0, $limit - 1);

return loadArticlesByIds($articleIds);

}

2.4 Set(集合)

特点

– 无序集合

– 元素唯一

– 支持集合运算(交集、并集、差集)

常用命令

# 基本操作

SADD myset "member1" "member2" "member3"

SMEMBERS myset # 获取所有成员

SISMEMBER myset "member1" # 检查成员是否存在

SREM myset "member2" # 删除成员

集合运算

SADD seta 1 2 3

SADD setb 2 3 4

SINTER seta setb # 交集:2 3

SUNION seta setb # 并集:1 2 3 4

SDIFF seta setb # 差集:1

随机操作

SRANDMEMBER myset # 随机返回一个成员(不删除)

SPOP myset # 随机弹出成员(删除)

集合操作

SCARD myset # 获取成员数量

SMOVE set1 set2 "member" # 移动成员

实战案例

// 标签系统

function addTagsToArticle($articleId, $tags) {

$redis = new Redis();

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

$redis->auth('password');

$key = "article:{$articleId}:tags";

$redis->sadd($key, ...$tags);

$redis->expire($key, 86400 * 30); // 30天过期

}

function getArticlesByTags($tags, $limit = 20) {

$redis = new Redis();

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

$redis->auth('password');

// 构建查询键

$keys = array_map(function($tag) {

return "tag:{$tag}:articles";

}, $tags);

// 计算交集(包含所有标签的文章)

$articleIds = $redis->sinter(...$keys);

return array_slice($articleIds, 0, $limit);

}

// 共同关注

function getCommonFollowers($userId1, $userId2) {

$redis = new Redis();

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

$redis->auth('password');

$key1 = "user:{$userId1}:following";

$key2 = "user:{$userId2}:following";

return $redis->sinter($key1, $key2);

}

// 抽奖系统

function lottery($users, $winnersCount) {

$redis = new Redis();

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

$redis->auth('password');

$key = "lottery:" . time();

// 添加所有用户到集合

$redis->sadd($key, ...$users);

// 随机抽取中奖者

$winners = [];

for ($i = 0; $i < $winnersCount; $i++) {

$winner = $redis->spop($key);

if ($winner) {

$winners[] = $winner;

}

}

return $winners;

}

2.5 Sorted Set(有序集合)

特点

– 每个成员关联一个分数(score)

– 按分数排序

– 成员唯一,分数可重复

常用命令

# 基本操作

ZADD myzset 100 "member1" 200 "member2" 150 "member3"

ZRANGE myzset 0 -1 # 获取所有成员(按分数升序)

ZREVRANGE myzset 0 -1 # 获取所有成员(按分数降序)

ZSCORE myzset "member1" # 获取成员分数

ZRANK myzset "member1" # 获取成员排名(升序)

ZREVRANK myzset "member1" # 获取成员排名(降序)

范围查询

ZRANGEBYSCORE myzset 0 150 # 获取分数0-150的成员

ZCOUNT myzset 100 200 # 统计分数范围内的成员数

删除操作

ZREM myzset "member1" # 删除成员

ZREMRANGEBYRANK myzset 0 2 # 删除排名0-2的成员

ZREMRANGEBYSCORE myzset 0 100 # 删除分数0-100的成员

集合运算

ZUNIONSTORE result 2 zset1 zset2 # 并集

ZINTERSTORE result 2 zset1 zset2 # 交集

实战案例

// 排行榜系统

class Leaderboard {

private $redis;

private $key;

public function __construct($leaderboardName) {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6379);

$this->redis->auth('password');

$this->key = "leaderboard:{$leaderboardName}";

}

// 添加或更新分数

public function addScore($member, $score) {

return $this->redis->zadd($this->key, $score, $member);

}

// 增加分数

public function incrementScore($member, $delta) {

return $this->redis->zincrby($this->key, $delta, $member);

}

// 获取排行榜(Top N)

public function getTop($limit = 10) {

return $this->redis->zrevrange($this->key, 0, $limit - 1, true);

}

// 获取用户排名和分数

public function getRank($member) {

$rank = $this->redis->zrevrank($this->key, $member);

$score = $this->redis->zscore($this->key, $member);

if ($rank === false) {

return null;

}

return [

'rank' => $rank + 1, // 排名从1开始

'score' => $score

];

}

// 获取分数范围内的用户

public function getByScoreRange($min, $max, $limit = 10) {

return $this->redis->zrevrangebyscore($this->key, $max, $min, ['withscores' => true, 'limit' => [0, $limit]]);

}

}

// 使用示例

$leaderboard = new Leaderboard('game_scores');

// 更新玩家分数

$leaderboard->addScore('player1', 1000);

$leaderboard->addScore('player2', 1500);

$leaderboard->addScore('player3', 1200);

// 获取Top 10

$top10 = $leaderboard->getTop(10);

// 获取玩家排名

$playerRank = $leaderboard->getRank('player1');

// 返回:['rank' => 3, 'score' => 1000.0]

// 延时队列

function addDelayedJob($jobData, $delaySeconds) {

$redis = new Redis();

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

$redis->auth('password');

$key = "delayed_queue";

$score = time() + $delaySeconds;

$value = json_encode($jobData);

return $redis->zadd($key, $score, $value);

}

function getReadyJobs() {

$redis = new Redis();

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

$redis->auth('password');

$key = "delayed_queue";

$now = time();

// 获取到期的任务

$jobs = $redis->zrangebyscore($key, 0, $now);

if (!empty($jobs)) {

// 删除已获取的任务

$redis->zremrangebyscore($key, 0, $now);

}

return array_map('json_decode', $jobs);

}


第三部分:三服务器生产环境配置

3.1 Redis分配策略

在我们的三服务器架构中,Redis分配策略如下:

项目 | Redis DB | 服务器 | 用途 ——|———-|——–|—— ccl-feishu | DB 0 | 主库1(106.13.74.202) | WordPress缓存、会话 ccl-openclaw | DB 1 | 主库1 | OpenClaw AI网关 ccl-openclaw | DB 2 | 主库3(8.130.67.202) | 千年MMO游戏

3.2 Redis安装配置

编译安装Redis 7.2

# 1. 下载源码

cd /tmp

wget https://github.com/redis/redis/archive/refs/tags/7.2.3.tar.gz

tar -xzf 7.2.3.tar.gz

cd redis-7.2.3

2. 编译安装

make -j$(nproc)

make test

sudo make PREFIX=/server/redis install

3. 创建目录结构

sudo mkdir -p /server/redis/{bin,etc,data,logs}

sudo mkdir -p /server/redis/data

4. 复制配置文件

sudo cp redis.conf /server/redis/etc/

sudo cp sentinel.conf /server/redis/etc/

5. 创建systemd服务

sudo cat > /etc/systemd/system/redis.service << 'EOF'

[Unit]

Description=Redis In-Memory Data Store

After=network.target

[Service]

Type=forking

ExecStart=/server/redis/bin/redis-server /server/redis/etc/redis.conf

ExecStop=/server/redis/bin/redis-cli shutdown

Restart=always

User=redis

Group=redis

[Install]

WantedBy=multi-user.target

EOF

6. 创建redis用户

sudo useradd -r -s /bin/false redis

sudo chown -R redis:redis /server/redis

7. 启动Redis

sudo systemctl daemon-reload

sudo systemctl enable redis

sudo systemctl start redis

8. 验证安装

/server/redis/bin/redis-cli ping

应该返回:PONG

3.3 Redis配置文件优化

生产环境配置(/server/redis/etc/redis.conf)

# 网络配置

bind 0.0.0.0

port 6379

protected-mode yes

tcp-backlog 511

timeout 0

tcp-keepalive 300

通用配置

daemonize yes

supervised systemd

pidfile /var/run/redis_6379.pid

loglevel notice

logfile "/server/redis/logs/redis.log"

databases 16

快照配置(RDB)

save 900 1

save 300 10

save 60 10000

stop-writes-on-bgsave-error yes

rdbcompression yes

rdbchecksum yes

dbfilename dump.rdb

dir /server/redis/data

复制配置

replica-serve-stale-data yes

replica-read-only yes

repl-diskless-sync no

repl-diskless-sync-delay 5

repl-disable-tcp-nodelay no

replica-priority 100

内存管理

maxmemory 2gb

maxmemory-policy allkeys-lru

maxmemory-samples 5

AOF配置(建议开启)

appendonly yes

appendfilename "appendonly.aof"

appendfsync everysec

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

aof-load-truncated yes

慢查询配置

slowlog-log-slower-than 10000

slowlog-max-len 128

延迟监控

latency-monitor-threshold 100

高级配置

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-size -2

list-compress-depth 0

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64

hll-sparse-max-bytes 3000

stream-node-max-bytes 4096

stream-node-max-entries 100

activerehashing yes

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit replica 256mb 64mb 60

client-output-buffer-limit pubsub 32mb 8mb 60

hz 10

dynamic-hz yes

aof-rewrite-incremental-fsync yes

rdb-save-incremental-fsync yes

3.4 Redis Sentinel高可用配置

Sentinel配置(/server/redis/etc/sentinel.conf)

# 端口

port 26379

daemonize yes

pidfile /var/run/sentinel.pid

logfile "/server/redis/logs/sentinel.log"

dir /server/redis/data

监控配置

sentinel monitor mymaster 106.13.74.202 6379 2

sentinel auth-pass mymaster your_redis_password

sentinel down-after-milliseconds mymaster 5000

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 30000

故障通知脚本

sentinel notification-script mymaster /var/redis/notify.sh

重命名危险命令

rename-command FLUSHDB ""

rename-command FLUSHALL ""

rename-command CONFIG ""

rename-command SHUTDOWN SHUTDOWN_REDIS

启动Sentinel

# 启动Sentinel

/server/redis/bin/redis-sentinel /server/redis/etc/sentinel.conf

或使用systemd

sudo systemctl start redis-sentinel

sudo systemctl enable redis-sentinel

3.5 HAProxy本地代理配置

安装HAProxy

# CentOS/RHEL

sudo yum install haproxy -y

Ubuntu/Debian

sudo apt-get install haproxy -y

配置HAProxy(/etc/haproxy/haproxy.cfg)

frontend redis_local

bind *:6380

default_backend redis_cluster

backend redis_cluster

mode tcp

option tcp-check

tcp-check send PINGrn

tcp-check expect string +PONG

balance roundrobin

# 主库1

server redis1 127.0.0.1:6379 check inter 1s rise 2 fall 3

# 主库2(通过EIP连接)

server redis2 101.201.48.221:6379 check inter 1s rise 2 fall 3

# 主库3(通过EIP连接)

server redis3 8.130.67.202:6379 check inter 1s rise 2 fall 3

# Sentinel监控

server sentinel 106.13.74.202:26379 check inter 1s rise 2 fall 3 backup

重启HAProxy

# 验证配置

sudo haproxy -c -f /etc/haproxy/haproxy.cfg

重启服务

sudo systemctl restart haproxy

查看状态

sudo systemctl status haproxy

使用HAProxy连接

# 连接本地HAProxy代理

redis-cli -h 127.0.0.1 -p 6380 -a "password"

应用连接配置

REDIS_HOST=127.0.0.1

REDIS_PORT=6380

REDIS_PASSWORD=password


第四部分:PHP集成实战

4.1 安装Redis扩展

# PHP 8.4

sudo pecl install redis

添加到php.ini

echo "extension=redis.so" | sudo tee -a /etc/php.ini

重启PHP-FPM

sudo systemctl restart php-fpm

验证安装

php -m | grep redis

4.2 PHP Redis基础操作

<?php

/

* Redis管理类

*

* @ai-context

* - 支持连接池

* - 支持批量操作

* - 支持序列化

*/

class RedisManager {

private static $instance = null;

private $redis;

private $config;

private function __construct($config = []) {

$this->config = array_merge([

'host' => '127.0.0.1',

'port' => 6380,

'password' => 'password',

'database' => 0,

'timeout' => 5,

'persistent' => false,

], $config);

$this->connect();

}

public static function getInstance($config = []) {

if (self::$instance === null) {

self::$instance = new self($config);

}

return self::$instance;

}

private function connect() {

$this->redis = new Redis();

if ($this->config['persistent']) {

$this->redis->pconnect(

$this->config['host'],

$this->config['port'],

$this->config['timeout']

);

} else {

$this->redis->connect(

$this->config['host'],

$this->config['port'],

$this->config['timeout']

);

}

if (!empty($this->config['password'])) {

$this->redis->auth($this->config['password']);

}

if (!empty($this->config['database'])) {

$this->redis->select($this->config['database']);

}

// 设置序列化处理

$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);

$this->redis->setOption(Redis::OPT_PREFIX, 'app:');

}

/

* 缓存操作

*/

public function cache($key, $value, $ttl = 3600) {

if ($value === null) {

return $this->redis->get($key);

}

return $this->redis->setex($key, $ttl, $value);

}

/

* 批量获取

*/

public function getMulti($keys) {

return $this->redis->mget($keys);

}

/

* 批量设置

*/

public function setMulti($data, $ttl = 3600) {

$pipeline = $this->redis->pipeline();

foreach ($data as $key => $value) {

$pipeline->setex($key, $ttl, $value);

}

return $pipeline->exec();

}

/

* 删除缓存

*/

public function delete($pattern) {

if (strpos($pattern, '*') !== false) {

$keys = $this->redis->keys($pattern);

if (!empty($keys)) {

return $this->redis->del($keys);

}

return 0;

}

return $this->redis->del($pattern);

}

/

* 获取Redis实例

*/

public function getRedis() {

return $this->redis;

}

/

* 关闭连接

*/

public function close() {

if ($this->redis) {

$this->redis->close();

}

}

}

// 使用示例

$redis = RedisManager::getInstance([

'host' => '127.0.0.1',

'port' => 6380,

'password' => 'password',

'database' => 0,

]);

// 缓存数据

$redis->cache('user:123', ['id' => 123, 'name' => '张三'], 3600);

// 获取缓存

$user = $redis->cache('user:123');

// 批量操作

$redis->setMulti([

'key1' => 'value1',

'key2' => 'value2',

'key3' => 'value3',

], 3600);

$values = $redis->getMulti(['key1', 'key2', 'key3']);

4.3 Laravel集成

配置Redis(config/database.php)

'redis' => [

'client' => env('REDIS_CLIENT', 'phpredis'),

'options' => [

'cluster' => env('REDIS_CLUSTER', 'redis'),

'prefix' => env('REDIS_PREFIX', 'laravel_'),

],

'default' => [

'url' => env('REDIS_URL'),

'host' => env('REDIS_HOST', '127.0.0.1'),

'password' => env('REDIS_PASSWORD'),

'port' => env('REDIS_PORT', '6380'),

'database' => env('REDIS_DB', '0'),

'read_timeout' => 5.0,

'timeout' => 5.0,

],

'cache' => [

'url' => env('REDIS_URL'),

'host' => env('REDIS_HOST', '127.0.0.1'),

'password' => env('REDIS_PASSWORD'),

'port' => env('REDIS_PORT', '6380'),

'database' => env('REDIS_CACHE_DB', '1'),

],

'session' => [

'url' => env('REDIS_URL'),

'host' => env('REDIS_HOST', '127.0.0.1'),

'password' => env('REDIS_PASSWORD'),

'port' => env('REDIS_PORT', '6380'),

'database' => env('REDIS_SESSION_DB', '2'),

],

],

.env配置

REDIS_HOST=127.0.0.1

REDIS_PASSWORD=password

REDIS_PORT=6380

REDIS_DB=0

REDIS_CACHE_DB=1

REDIS_SESSION_DB=2

缓存配置

CACHE_DRIVER=redis

SESSION_DRIVER=redis

QUEUE_CONNECTION=redis

使用Redis缓存

<?php

namespace AppHttpControllers;

use IlluminateSupportFacadesCache;

use IlluminateSupportFacadesRedis;

class ArticleController extends Controller

{

/

* 获取文章详情(带缓存)

*/

public function show($id)

{

// 使用Cache门面

$article = Cache::remember("article:{$id}", 3600, function() use ($id) {

return Article::with('comments')->find($id);

});

return view('articles.show', compact('article'));

}

/

* 更新文章阅读数(使用Redis计数器)

*/

public function incrementViews($id)

{

$key = "article:{$id}:views";

// 使用Redis门面

Redis::incr($key);

// 每增长100次,同步到数据库

if (Redis::get($key) % 100 == 0) {

Article::where('id', $id)->update([

'views' => Redis::get($key)

]);

}

return response()->json([

'views' => Redis::get($key)

]);

}

/

* 文章列表缓存(带标签)

*/

public function index()

{

$page = request()->get('page', 1);

$articles = Cache::tags(['articles', "page:{$page}"])

->remember("articles:page:{$page}", 600, function() {

return Article::published()

->orderBy('published_at', 'desc')

->paginate(20);

});

return view('articles.index', compact('articles'));

}

/

* 清除文章缓存

*/

public function clearCache($id)

{

// 清除单个文章缓存

Cache::forget("article:{$id}");

// 清除所有文章列表缓存

Cache::tags(['articles'])->flush();

return response()->json(['message' => 'Cache cleared']);

}

}

使用Redis队列

<?php

namespace AppJobs;

use IlluminateBusQueueable;

use IlluminateContractsQueueShouldQueue;

use IlluminateFoundationBusDispatchable;

use IlluminateQueueInteractsWithQueue;

use IlluminateQueueSerializesModels;

use IlluminateSupportFacadesLog;

class SendEmailJob implements ShouldQueue

{

use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

private $email;

private $subject;

private $body;

/

* 任务尝试次数

*/

public $tries = 3;

/

* 任务超时时间(秒)

*/

public $timeout = 30;

public function __construct($email, $subject, $body)

{

$this->email = $email;

$this->subject = $subject;

$this->body = $body;

// 指定队列

$this->onQueue('emails');

}

public function handle()

{

// 发送邮件逻辑

Log::info("Sending email to {$this->email}");

// 实际发送邮件...

sleep(2);

Log::info("Email sent successfully to {$this->email}");

}

public function failed(Throwable $exception)

{

Log::error("Failed to send email to {$this->email}: " . $exception->getMessage());

}

}

// 分发任务

use AppJobsSendEmailJob;

SendEmailJob::dispatch('user@your-domain.com', 'Welcome', 'Welcome to our app!');


第五部分:性能优化

5.1 Pipeline批量操作

<?php

/

* 批量操作优化

*/

class RedisPipeline {

private $redis;

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 批量设置

*/

public function setBatch($data) {

$pipeline = $this->redis->pipeline();

foreach ($data as $key => $value) {

$pipeline->set($key, $value);

}

return $pipeline->exec();

}

/

* 批量获取

*/

public function getBatch($keys) {

$pipeline = $this->redis->pipeline();

foreach ($keys as $key) {

$pipeline->get($key);

}

return $pipeline->exec();

}

/

* 批量设置过期时间

*/

public function expireBatch($keys, $ttl) {

$pipeline = $this->redis->pipeline();

foreach ($keys as $key) {

$pipeline->expire($key, $ttl);

}

return $pipeline->exec();

}

}

// 性能对比:1000次操作

$redis = new Redis();

$redis->connect('127.0.0.1', 6380);

$redis->auth('password');

// 不使用Pipeline

$start = microtime(true);

for ($i = 0; $i < 1000; $i++) {

$redis->set("key:{$i}", "value:{$i}");

}

$timeWithoutPipeline = microtime(true) - $time;

// 使用Pipeline

$start = microtime(true);

$pipeline = $redis->pipeline();

for ($i = 0; $i < 1000; $i++) {

$pipeline->set("key:{$i}", "value:{$i}");

}

$pipeline->exec();

$timeWithPipeline = microtime(true) - $start;

// 结果:Pipeline快10-50倍

echo "Without Pipeline: {$timeWithoutPipeline}sn";

echo "With Pipeline: {$timeWithPipeline}sn";

5.2 合理设置过期时间

<?php

/

* 缓存过期时间策略

*/

class CacheTTL {

const TTL_SHORT = 300; // 5分钟 - 热点数据

const TTL_MEDIUM = 3600; // 1小时 - 一般数据

const TTL_LONG = 86400; // 1天 - 不常变化数据

const TTL_VERY_LONG = 604800; // 7天 - 静态数据

/

* 根据数据类型选择TTL

*/

public static function getTTL($dataType) {

switch ($dataType) {

case 'hot':

return self::TTL_SHORT;

case 'medium':

return self::TTL_MEDIUM;

case 'static':

return self::TTL_LONG;

case 'archive':

return self::TTL_VERY_LONG;

default:

return self::TTL_MEDIUM;

}

}

/

* 带随机抖动的TTL(防止缓存雪崩)

*/

public static function getTTLWithJitter($baseTTL, $jitterPercent = 10) {

$jitter = $baseTTL * ($jitterPercent / 100);

return $baseTTL + mt_rand(-$jitter, $jitter);

}

}

// 使用示例

$redis = new Redis();

$redis->connect('127.0.0.1', 6380);

$redis->auth('password');

// 热点数据(首页、排行榜)

$redis->setex('homepage:data', CacheTTL::TTL_SHORT, json_encode($homepageData));

// 一般数据(文章详情)

$redis->setex('article:123', CacheTTL::getTTLWithJitter(CacheTTL::TTL_MEDIUM), json_encode($article));

// 静态数据(配置信息)

$redis->setex('config:site', CacheTTL::TTL_LONG, json_encode($config));

5.3 避免大Key

<?php

/

* 大Key拆分策略

*/

class BigKeySplitter {

private $redis;

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 拆分大Hash

*/

public function splitHash($bigKey, $chunkSize = 1000) {

$data = $this->redis->hgetall($bigKey);

if (count($data) <= $chunkSize) {

return; // 不需要拆分

}

// 拆分成多个小Hash

$chunks = array_chunk($data, $chunkSize, true);

foreach ($chunks as $index => $chunk) {

$chunkKey = "{$bigKey}:chunk:{$index}";

$this->redis->hmset($chunkKey, $chunk);

}

// 保存元数据

$metaKey = "{$bigKey}:meta";

$this->redis->set($metaKey, json_encode([

'chunks' => count($chunks),

'chunkSize' => $chunkSize

]));

// 删除原大Key

$this->redis->del($bigKey);

}

/

* 获取拆分后的Hash数据

*/

public function getSplitHash($bigKey, $field) {

$metaKey = "{$bigKey}:meta";

$meta = json_decode($this->redis->get($metaKey), true);

if (!$meta) {

// 未拆分,直接获取

return $this->redis->hget($bigKey, $field);

}

// 查找字段在哪个chunk中

for ($i = 0; $i < $meta['chunks']; $i++) {

$chunkKey = "{$bigKey}:chunk:{$i}";

$value = $this->redis->hget($chunkKey, $field);

if ($value !== false) {

return $value;

}

}

return null;

}

}

5.4 连接池配置

<?php

/

* Redis连接池

*/

class RedisPool {

private static $pools = [];

private static $maxConnections = 10;

private static $minConnections = 2;

private $config;

private function __construct($config) {

$this->config = $config;

}

/

* 获取连接池

*/

public static function getPool($name = 'default') {

if (!isset(self::$pools[$name])) {

self::$pools[$name] = new self(self::loadConfig($name));

}

return self::$pools[$name];

}

/

* 获取连接

*/

public function getConnection() {

$redis = new Redis();

try {

$redis->connect(

$this->config['host'],

$this->config['port'],

$this->config['timeout']

);

if (!empty($this->config['password'])) {

$redis->auth($this->config['password']);

}

if (!empty($this->config['database'])) {

$redis->select($this->config['database']);

}

return $redis;

} catch (Exception $e) {

error_log("Redis connection failed: " . $e->getMessage());

return null;

}

}

private static function loadConfig($name) {

$configs = [

'default' => [

'host' => '127.0.0.1',

'port' => 6380,

'password' => 'password',

'database' => 0,

'timeout' => 5,

],

'cache' => [

'host' => '127.0.0.1',

'port' => 6380,

'password' => 'password',

'database' => 1,

'timeout' => 5,

],

];

return $configs[$name] ?? $configs['default'];

}

}

// 使用连接池

$pool = RedisPool::getPool('default');

$redis = $pool->getConnection();

if ($redis) {

$redis->set('key', 'value');

$value = $redis->get('key');

}


第六部分:监控与告警

6.1 Redis监控指标

#!/bin/bash

Redis监控脚本

REDIS_CLI="/server/redis/bin/redis-cli"

REDIS_HOST="127.0.0.1"

REDIS_PORT="6380"

REDIS_PASSWORD="password"

连接Redis并获取信息

INFO=$($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD INFO 2>/dev/null)

解析关键指标

USED_MEMORY=$(echo "$INFO" | grep "used_memory_human:" | cut -d: -f2 | tr -d 'r')

TOTAL_CONNECTIONS=$(echo "$INFO" | grep "total_connections_received:" | cut -d: -f2 | tr -d 'r')

TOTAL_COMMANDS=$(echo "$INFO" | grep "total_commands_processed:" | cut -d: -f2 | tr -d 'r')

KEYSPACE_HITS=$(echo "$INFO" | grep "keyspace_hits:" | cut -d: -f2 | tr -d 'r')

KEYSPACE_MISSES=$(echo "$INFO" | grep "keyspace_misses:" | cut -d: -f2 | tr -d 'r')

计算命中率

if [ -n "$KEYSPACE_HITS" ] && [ -n "$KEYSPACE_MISSES" ]; then

TOTAL_REQUESTS=$((KEYSPACE_HITS + KEYSPACE_MISSES))

if [ $TOTAL_REQUESTS -gt 0 ]; then

HIT_RATE=$(echo "scale=2; $KEYSPACE_HITS * 100 / $TOTAL_REQUESTS" | bc)

echo "Cache Hit Rate: ${HIT_RATE}%"

fi

fi

echo "Used Memory: $USED_MEMORY"

echo "Total Connections: $TOTAL_CONNECTIONS"

echo "Total Commands: $TOTAL_COMMANDS"

慢查询

SLOWLOG=$($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD SLOWLOG GET 10 2>/dev/null)

echo "Slow Queries (last 10):"

echo "$SLOWLOG"

6.2 PHP监控脚本

<?php

/

* Redis健康检查脚本

*/

class RedisHealthCheck {

private $redis;

private $alerts = [];

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 执行健康检查

*/

public function check() {

$this->checkConnection();

$this->checkMemory();

$this->checkHitRate();

$this->checkSlowQueries();

$this->checkReplication();

return $this->alerts;

}

/

* 检查连接

*/

private function checkConnection() {

try {

$pong = $this->redis->ping();

if ($pong !== '+PONG') {

$this->alerts[] = [

'level' => 'critical',

'message' => 'Redis ping failed'

];

}

} catch (Exception $e) {

$this->alerts[] = [

'level' => 'critical',

'message' => 'Redis connection failed: ' . $e->getMessage()

];

}

}

/

* 检查内存使用

*/

private function checkMemory() {

$info = $this->redis->info('memory');

$usedMemory = $info['used_memory'];

$maxMemory = $info['maxmemory'];

if ($maxMemory > 0) {

$usagePercent = ($usedMemory / $maxMemory) * 100;

if ($usagePercent > 90) {

$this->alerts[] = [

'level' => 'critical',

'message' => sprintf('Memory usage: %.2f%%', $usagePercent)

];

} elseif ($usagePercent > 80) {

$this->alerts[] = [

'level' => 'warning',

'message' => sprintf('Memory usage: %.2f%%', $usagePercent)

];

}

}

}

/

* 检查缓存命中率

*/

private function checkHitRate() {

$info = $this->redis->info('stats');

$hits = $info['keyspace_hits'];

$misses = $info['keyspace_misses'];

$total = $hits + $misses;

if ($total > 0) {

$hitRate = ($hits / $total) * 100;

if ($hitRate < 50) {

$this->alerts[] = [

'level' => 'warning',

'message' => sprintf('Cache hit rate too low: %.2f%%', $hitRate)

];

}

}

}

/

* 检查慢查询

*/

private function checkSlowQueries() {

$slowlog = $this->redis->slowlog('get', 10);

if (count($slowlog) > 5) {

$this->alerts[] = [

'level' => 'warning',

'message' => sprintf('Too many slow queries: %d', count($slowlog))

];

}

foreach ($slowlog as $entry) {

if ($entry[1] > 10000) { // 超过10秒

$this->alerts[] = [

'level' => 'warning',

'message' => sprintf('Very slow query: %.2fs - %s', $entry[1] / 1000000, $entry[3])

];

}

}

}

/

* 检查复制状态

*/

private function checkReplication() {

$info = $this->redis->info('replication');

if (isset($info['master_link_status']) && $info['master_link_status'] !== 'up') {

$this->alerts[] = [

'level' => 'critical',

'message' => 'Master link is down'

];

}

}

/

* 发送告警

*/

public function sendAlerts() {

if (empty($this->alerts)) {

return;

}

// 发送邮件、Slack、飞书等

foreach ($this->alerts as $alert) {

$this->sendNotification($alert);

}

}

private function sendNotification($alert) {

// 实现发送逻辑(邮件、Webhook等)

error_log(sprintf("[%s] %s", strtoupper($alert['level']), $alert['message']));

}

}

// 定时执行

$healthCheck = new RedisHealthCheck();

$alerts = $healthCheck->check();

if (!empty($alerts)) {

$healthCheck->sendAlerts();

}


第七部分:故障处理

7.1 常见问题诊断

#!/bin/bash

Redis故障诊断脚本

echo "=== Redis故障诊断 ==="

echo ""

1. 检查Redis进程

echo "【1】检查Redis进程"

if pgrep -x "redis-server" > /dev/null; then

echo "✅ Redis进程运行中"

PID=$(pgrep -x "redis-server")

echo " PID: $PID"

else

echo "❌ Redis进程未运行"

echo " 修复命令: systemctl start redis"

fi

echo ""

2. 检查Redis端口

echo "【2】检查Redis端口"

if netstat -tuln | grep -q ":6379 "; then

echo "✅ Redis端口6379监听中"

else

echo "❌ Redis端口6379未监听"

fi

echo ""

3. 测试Redis连接

echo "【3】测试Redis连接"

if redis-cli -h 127.0.0.1 -p 6379 -a "password" PING 2>/dev/null | grep -q "PONG"; then

echo "✅ Redis连接正常"

else

echo "❌ Redis连接失败"

echo " 可能原因:"

echo " 1. Redis服务未启动"

echo " 2. 密码错误"

echo " 3. 防火墙阻止"

fi

echo ""

4. 检查Redis内存

echo "【4】检查Redis内存使用"

MEMORY=$(redis-cli -h 127.0.0.1 -p 6379 -a "password" INFO memory 2>/dev/null | grep "used_memory_human:" | cut -d: -f2 | tr -d 'r')

echo " 内存使用: $MEMORY"

echo ""

5. 检查慢查询

echo "【5】检查慢查询"

SLOWLOG=$(redis-cli -h 127.0.0.1 -p 6379 -a "password" SLOWLOG LEN 2>/dev/null | tr -d 'r')

echo " 慢查询数量: $SLOWLOG"

if [ "$SLOWLOG" -gt 10 ]; then

echo " ⚠️ 慢查询过多,建议优化"

echo " 查看命令: redis-cli SLOWLOG GET 10"

fi

echo ""

6. 检查持久化

echo "【6】检查持久化状态"

RDB_LAST_SAVE=$(redis-cli -h 127.0.0.1 -p 6379 -a "password" LASTSAVE 2>/dev/null | tr -d 'r')

echo " 最后一次RDB保存: $RDB_LAST_SAVE"

AOF_ENABLED=$(redis-cli -h 127.0.0.1 -p 6379 -a "password" CONFIG GET appendonly 2>/dev/null | tail -1 | tr -d 'r')

if [ "$AOF_ENABLED" = "yes" ]; then

echo " AOF: 已启用"

else

echo " AOF: 未启用"

fi

echo ""

echo "=== 诊断完成 ==="

7.2 内存优化策略

#!/bin/bash

Redis内存优化脚本

echo "=== Redis内存优化 ==="

echo ""

1. 查找大Key

echo "【1】查找大Key(Top 20)"

redis-cli -h 127.0.0.1 -p 6379 -a "password" --bigkeys --sample 1000 2>/dev/null | head -50

echo ""

2. 查找过期Key

echo "【2】查找有过期时间的Key数量"

redis-cli -h 127.0.0.1 -p 6379 -a "password" DBSIZE 2>/dev/null

echo ""

3. 查看内存使用详情

echo "【3】内存使用详情"

redis-cli -h 127.0.0.1 -p 6379 -a "password" INFO memory 2>/dev/null | grep -E "used_memory|maxmemory|mem_fragmentation_ratio"

echo ""

4. 分析内存碎片

echo "【4】内存碎片率"

FRAG=$(redis-cli -h 127.0.0.1 -p 6379 -a "password" INFO memory 2>/dev/null | grep "mem_fragmentation_ratio:" | cut -d: -f2 | tr -d 'r')

echo " 碎片率: $FRAG"

FRAG_FLOAT=$(echo "$FRAG" | bc -l)

if (( $(echo "$FRAG_FLOAT > 1.5" | bc -l) )); then

echo " ⚠️ 碎片率过高,建议执行MEMORY PURGE"

fi

echo ""

5. 执行内存整理

echo "【5】执行内存整理"

read -p "是否执行内存整理(MEMORY PURGE)?[y/N] " -n 1 -r

echo

if [[ $REPLY =~ ^[Yy]$ ]]; then

redis-cli -h 127.0.0.1 -p 6379 -a "password" MEMORY PURGE 2>/dev/null

echo "✅ 内存整理完成"

fi

echo ""

echo "=== 优化完成 ==="

7.3 数据恢复

#!/bin/bash

Redis数据恢复脚本

REDIS_DIR="/server/redis/data"

BACKUP_DIR="/backup/redis"

REDIS_CLI="/server/redis/bin/redis-cli"

echo "=== Redis数据恢复 ==="

echo ""

1. 停止Redis

echo "【1】停止Redis服务"

systemctl stop redis

echo "✅ Redis已停止"

echo ""

2. 备份当前数据

echo "【2】备份当前数据"

BACKUP_NAME="redis_backup_$(date +%Y%m%d_%H%M%S)"

mkdir -p "$BACKUP_DIR/$BACKUP_NAME"

cp -r $REDIS_DIR/* "$BACKUP_DIR/$BACKUP_NAME/"

echo "✅ 当前数据已备份到: $BACKUP_DIR/$BACKUP_NAME"

echo ""

3. 选择恢复方式

echo "【3】选择恢复方式"

echo "1. 从RDB文件恢复"

echo "2. 从AOF文件恢复"

read -p "请选择 [1/2]: " choice

case $choice in

1)

# 从RDB恢复

echo "请输入RDB文件路径:"

read RDB_FILE

if [ -f "$RDB_FILE" ]; then

cp "$RDB_FILE" "$REDIS_DIR/dump.rdb"

echo "✅ RDB文件已复制"

else

echo "❌ 文件不存在: $RDB_FILE"

exit 1

fi

;;

2)

# 从AOF恢复

echo "请输入AOF文件路径:"

read AOF_FILE

if [ -f "$AOF_FILE" ]; then

cp "$AOF_FILE" "$REDIS_DIR/appendonly.aof"

echo "✅ AOF文件已复制"

else

echo "❌ 文件不存在: $AOF_FILE"

exit 1

fi

;;

*)

echo "❌ 无效选择"

exit 1

;;

esac

4. 修复AOF(如果需要)

if [ -f "$REDIS_DIR/appendonly.aof" ]; then

echo ""

echo "【4】修复AOF文件"

$REDIS_CLI --appendonly fix

echo "✅ AOF文件已修复"

fi

5. 启动Redis

echo ""

echo "【5】启动Redis服务"

systemctl start redis

sleep 2

6. 验证数据

echo "【6】验证数据"

if $REDIS_CLI PING 2>/dev/null | grep -q "PONG"; then

DBSIZE=$($REDIS_CLI DBSIZE 2>/dev/null | tr -d 'r')

echo "✅ Redis启动成功"

echo " Key数量: $DBSIZE"

else

echo "❌ Redis启动失败"

systemctl status redis

exit 1

fi

echo ""

echo "=== 恢复完成 ==="


第八部分:生产环境最佳实践

8.1 安全加固

# 1. 重命名危险命令

redis-cli CONFIG SET rename-command FLUSHDB ""

redis-cli CONFIG SET rename-command FLUSHALL ""

redis-cli CONFIG SET rename-command CONFIG "CONFIG_REDIS_123"

2. 设置密码

redis-cli CONFIG SET requirepass "your_strong_password"

3. 禁用敏感命令

redis-cli CONFIG SET shutdown-command-on-noenabled yes

4. 网络隔离

- 使用防火墙限制访问

- 只允许特定IP连接

- 使用VPN或专线

8.2 备份策略

#!/bin/bash

Redis自动备份脚本

BACKUP_DIR="/backup/redis"

REDIS_CLI="/server/redis/bin/redis-cli"

REDIS_DATA_DIR="/server/redis/data"

DATE=$(date +%Y%m%d_%H%M%S)

BACKUP_NAME="redis_backup_$DATE"

创建备份目录

mkdir -p "$BACKUP_DIR/$BACKUP_NAME"

RDB备份

echo "执行RDB备份"

$REDIS-cli BGSAVE

sleep 5

等待备份完成

while [ $($REDIS_CLI LASTSAVE) -eq $($REDIS-cli -h localhost -p 6379 LASTSAVE) ]; do

sleep 1

done

复制RDB文件

cp "$REDIS_DATA_DIR/dump.rdb" "$BACKUP_DIR/$BACKUP_NAME/"

AOF备份(如果启用)

if [ -f "$REDIS_DATA_DIR/appendonly.aof" ]; then

echo "执行AOF备份"

cp "$REDIS_DATA_DIR/appendonly.aof" "$BACKUP_DIR/$BACKUP_NAME/"

fi

压缩备份

tar -czf "$BACKUP_DIR/${BACKUP_NAME}.tar.gz" -C "$BACKUP_DIR" "$BACKUP_NAME"

rm -rf "$BACKUP_DIR/$BACKUP_NAME"

删除7天前的备份

find "$BACKUP_DIR" -name "redis_backup_*.tar.gz" -mtime +7 -delete

echo "备份完成: $BACKUP_DIR/${BACKUP_NAME}.tar.gz"

8.3 监控告警配置

<?php

/

* Redis监控告警系统

*/

class RedisMonitor {

private $redis;

private $thresholds = [

'memory_usage' => 80, // 内存使用率阈值(%)

'hit_rate' => 50, // 缓存命中率阈值(%)

'slow_queries' => 10, // 慢查询数量阈值

'connections' => 10000, // 连接数阈值

];

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 执行监控检查

*/

public function monitor() {

$metrics = $this->collectMetrics();

$alerts = $this->checkThresholds($metrics);

if (!empty($alerts)) {

$this->sendAlerts($alerts);

}

return $metrics;

}

/

* 收集指标

*/

private function collectMetrics() {

$info = $this->redis->info();

return [

'memory_usage' => $this->calculateMemoryUsage($info),

'hit_rate' => $this->calculateHitRate($info),

'slow_queries' => count($this->redis->slowlog('get')),

'connections' => $info['connected_clients'],

'commands_per_sec' => $info['instantaneous_ops_per_sec'],

'keyspace_size' => $info['db0'] ?? 0,

];

}

/

* 检查阈值

*/

private function checkThresholds($metrics) {

$alerts = [];

foreach ($this->thresholds as $key => $threshold) {

if (isset($metrics[$key]) && $metrics[$key] > $threshold) {

$alerts[] = [

'metric' => $key,

'value' => $metrics[$key],

'threshold' => $threshold,

'severity' => $this->getSeverity($key, $metrics[$key], $threshold)

];

}

}

return $alerts;

}

/

* 发送告警

*/

private function sendAlerts($alerts) {

foreach ($alerts as $alert) {

$message = sprintf(

"[Redis Alert] %s: %s (threshold: %s)",

$alert['metric'],

$alert['value'],

$alert['threshold']

);

// 发送到日志

error_log($message);

// 发送到邮件

mail('admin@your-domain.com', 'Redis Alert', $message);

// 发送到Slack/飞书

$this->sendWebhook($alert);

}

}

/

* 发送Webhook通知

*/

private function sendWebhook($alert) {

$webhookUrl = 'https://open.feishu.cn/open-apis/bot/v2/hook/xxx';

$data = [

'msg_type' => 'text',

'content' => [

'text' => sprintf(

"Redis告警n指标: %sn当前值: %sn阈值: %sn级别: %s",

$alert['metric'],

$alert['value'],

$alert['threshold'],

$alert['severity']

)

]

];

$ch = curl_init($webhookUrl);

curl_setopt($ch, CURLOPT_POST, 1);

curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);

curl_exec($ch);

curl_close($ch);

}

private function calculateMemoryUsage($info) {

$used = $info['used_memory'];

$max = $info['maxmemory'];

return $max > 0 ? ($used / $max) * 100 : 0;

}

private function calculateHitRate($info) {

$hits = $info['keyspace_hits'];

$misses = $info['keyspace_misses'];

$total = $hits + $misses;

return $total > 0 ? ($hits / $total) * 100 : 0;

}

private function getSeverity($metric, $value, $threshold) {

$ratio = $value / $threshold;

if ($ratio >= 1.5) {

return 'critical';

} elseif ($ratio >= 1.2) {

return 'warning';

} else {

return 'info';

}

}

}

// 定时执行(每5分钟)

$monitor = new RedisMonitor();

$monitor->monitor();


第九部分:性能测试与调优

9.1 基准测试

#!/bin/bash

Redis性能基准测试

REDIS_CLI="/server/redis/bin/redis-cli"

RESULTS_DIR="/tmp/redis_benchmark_$(date +%Y%m%d_%H%M%S)"

mkdir -p "$RESULTS_DIR"

echo "=== Redis性能基准测试 ==="

echo ""

1. SET操作测试

echo "【1】SET操作测试(10万次)"

$REDIS_CLI --csv -h 127.0.0.1 -p 6379 -a "password" benchmark -t set -n 100000 -q | tee "$RESULTS_DIR/set_results.csv"

echo ""

2. GET操作测试

echo "【2】GET操作测试(10万次)"

$REDIS_CLI --csv -h 127.0.0.1 -p 6379 -a "password" benchmark -t get -n 100000 -q | tee "$RESULTS_DIR/get_results.csv"

echo ""

3. 混合操作测试

echo "【3】混合操作测试(10万次)"

$REDIS_CLI --csv -h 127.0.0.1 -p 6379 -a "password" benchmark -t set,get,incr,lpush,lpop -n 100000 -q | tee "$RESULTS_DIR/mix_results.csv"

echo ""

4. Pipeline性能测试

echo "【4】Pipeline性能测试(10万次,50批量)"

$REDIS_CLI --csv -h 127.0.0.1 -p 6379 -a "password" benchmark -t set,get -n 100000 -P 50 -q | tee "$RESULTS_DIR/pipeline_results.csv"

echo ""

5. 生成报告

echo "【5】生成性能报告"

cat > "$RESULTS_DIR/report.md" << EOF

Redis性能基准测试报告

测试时间: $(date)

Redis版本: $($REDIS_CLI --version | grep -oP 'v=K[0-9.]+')

测试数据量: 10万次操作

测试结果

SET操作

$(cat "$RESULTS_DIR/set_results.csv")

GET操作

$(cat "$RESULTS_DIR/get_results.csv")

混合操作

$(cat "$RESULTS_DIR/mix_results.csv")

Pipeline操作

$(cat "$RESULTS_DIR/pipeline_results.csv")

性能分析

- 单次操作QPS: 约10万+

- Pipeline操作QPS: 约50万+

- Pipeline性能提升: 5倍左右

优化建议

1. 使用Pipeline批量操作

2. 合理设置过期时间

3. 避免大Key

4. 使用连接池

EOF

echo "✅ 测试完成,结果保存在: $RESULTS_DIR"

echo " 报告文件: $RESULTS_DIR/report.md"

9.2 压力测试

<?php

/

* Redis压力测试脚本

*/

class RedisStressTest {

private $redis;

private $results = [];

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 执行压力测试

*/

public function test($operations = 100000, $threads = 10) {

echo "=== Redis压力测试 ===n";

echo "操作数: {$operations}n";

echo "并发数: {$threads}nn";

$this->testSet($operations);

$this->testGet($operations);

$this->testIncr($operations);

$this->testPipeline($operations);

$this->testConcurrent($threads);

$this->printResults();

}

/

* 测试SET操作

*/

private function testSet($operations) {

$start = microtime(true);

for ($i = 0; $i < $operations; $i++) {

$this->redis->set("key:{$i}", "value:{$i}");

}

$time = microtime(true) - $start;

$qps = $operations / $time;

$this->results['set'] = [

'operations' => $operations,

'time' => $time,

'qps' => $qps

];

}

/

* 测试GET操作

*/

private function testGet($operations) {

// 先填充数据

for ($i = 0; $i < $operations; $i++) {

$this->redis->set("key:{$i}", "value:{$i}");

}

$start = microtime(true);

for ($i = 0; $i < $operations; $i++) {

$this->redis->get("key:{$i}");

}

$time = microtime(true) - $start;

$qps = $operations / $time;

$this->results['get'] = [

'operations' => $operations,

'time' => $time,

'qps' => $qps

];

}

/

* 测试INCR操作

*/

private function testIncr($operations) {

$start = microtime(true);

for ($i = 0; $i < $operations; $i++) {

$this->redis->incr("counter:{$i}");

}

$time = microtime(true) - $start;

$qps = $operations / $time;

$this->results['incr'] = [

'operations' => $operations,

'time' => $time,

'qps' => $qps

];

}

/

* 测试Pipeline

*/

private function testPipeline($operations) {

$start = microtime(true);

$pipeline = $this->redis->pipeline();

for ($i = 0; $i < $operations; $i++) {

$pipeline->set("pipe:key:{$i}", "value:{$i}");

}

$pipeline->exec();

$time = microtime(true) - $start;

$qps = $operations / $time;

$this->results['pipeline'] = [

'operations' => $operations,

'time' => $time,

'qps' => $qps

];

}

/

* 测试并发

*/

private function testConcurrent($threads) {

$start = microtime(true);

$pids = [];

for ($i = 0; $i < $threads; $i++) {

$pid = pcntl_fork();

if ($pid == 0) {

// 子进程

$redis = new Redis();

$redis->connect('127.0.0.1', 6380);

$redis->auth('password');

for ($j = 0; $j < 10000; $j++) {

$redis->set("concurrent:{$i}:{$j}", "value");

}

exit(0);

} else {

$pids[] = $pid;

}

}

// 等待所有子进程

foreach ($pids as $pid) {

pcntl_waitpid($pid, $status);

}

$time = microtime(true) - $start;

$totalOps = $threads * 10000;

$qps = $totalOps / $time;

$this->results['concurrent'] = [

'threads' => $threads,

'operations' => $totalOps,

'time' => $time,

'qps' => $qps

];

}

/

* 打印结果

*/

private function printResults() {

echo "n=== 测试结果 ===nn";

foreach ($this->results as $name => $result) {

echo "【{$name}】n";

foreach ($result as $key => $value) {

if ($key == 'time') {

echo " {$key}: " . number_format($value, 4) . "sn";

} elseif ($key == 'qps') {

echo " {$key}: " . number_format($value) . "n";

} else {

echo " {$key}: {$value}n";

}

}

echo "n";

}

// 性能对比

if (isset($this->results['set']['qps']) && isset($this->results['pipeline']['qps'])) {

$speedup = $this->results['pipeline']['qps'] / $this->results['set']['qps'];

echo "【性能提升】n";

echo " Pipeline vs SET: " . number_format($speedup, 2) . "xnn";

}

}

}

// 执行测试

$test = new RedisStressTest();

$test->test(100000, 10);


第十部分:实战案例

10.1 构建高性能计数器系统

<?php

/

* 高性能计数器系统

*

* @ai-context

* - 支持多维度计数

* - 支持自动归档

* - 支持实时查询

*/

class CounterSystem {

private $redis;

private $prefix = 'counter';

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 增加计数

*

* @param string $key 计数键

* @param int $delta 增量

* @param array $dimensions 维度标签

*/

public function increment($key, $delta = 1, $dimensions = []) {

$redisKey = $this->buildKey($key, $dimensions);

// 增加计数

$this->redis->incrby($redisKey, $delta);

// 设置过期时间(30天)

$this->redis->expire($redisKey, 86400 * 30);

// 记录到时间序列

$this->recordToTimeseries($key, $delta, $dimensions);

return $this->redis->get($redisKey);

}

/

* 获取计数

*/

public function get($key, $dimensions = []) {

$redisKey = $this->buildKey($key, $dimensions);

return (int)$this->redis->get($redisKey);

}

/

* 获取排行榜

*/

public function getRanking($key, $limit = 10) {

$pattern = "{$this->prefix}:{$key}:*";

$keys = $this->redis->keys($pattern);

$ranking = [];

foreach ($keys as $redisKey) {

$value = (int)$this->redis->get($redisKey);

$dimensions = $this->parseKey($redisKey);

$ranking[] = [

'dimensions' => $dimensions,

'value' => $value

];

}

// 按值排序

usort($ranking, function($a, $b) {

return $b['value'] - $a['value'];

});

return array_slice($ranking, 0, $limit);

}

/

* 获取时间序列数据

*/

public function getTimeseries($key, $startTime, $endTime, $dimensions = []) {

$timeseriesKey = $this->buildTimeseriesKey($key, $dimensions);

$startTimestamp = strtotime($startTime);

$endTimestamp = strtotime($endTime);

return $this->redis->zrangebyscore(

$timeseriesKey,

$startTimestamp,

$endTimestamp,

['withscores' => true]

);

}

/

* 批量增加计数

*/

public function incrementBatch($counters) {

$pipeline = $this->redis->pipeline();

foreach ($counters as $counter) {

$redisKey = $this->buildKey($counter['key'], $counter['dimensions'] ?? []);

$delta = $counter['delta'] ?? 1;

$pipeline->incrby($redisKey, $delta);

$pipeline->expire($redisKey, 86400 * 30);

}

return $pipeline->exec();

}

/

* 构建Redis键

*/

private function buildKey($key, $dimensions = []) {

if (empty($dimensions)) {

return "{$this->prefix}:{$key}";

}

ksort($dimensions);

$dimensionStr = http_build_query($dimensions);

return "{$this->prefix}:{$key}:{$dimensionStr}";

}

/

* 解析Redis键

*/

private function parseKey($redisKey) {

$parts = explode(':', $redisKey);

array_shift($parts); // 移除prefix

array_shift($parts); // 移除key

if (empty($parts)) {

return [];

}

$dimensionStr = implode(':', $parts);

parse_str($dimensionStr, $dimensions);

return $dimensions;

}

/

* 记录到时间序列

*/

private function recordToTimeseries($key, $delta, $dimensions = []) {

$timeseriesKey = $this->buildTimeseriesKey($key, $dimensions);

$timestamp = time();

$value = json_encode([

'delta' => $delta,

'timestamp' => $timestamp

]);

$this->redis->zadd($timeseriesKey, $timestamp, $value);

// 设置过期时间(90天)

$this->redis->expire($timeseriesKey, 86400 * 90);

}

/

* 构建时间序列键

*/

private function buildTimeseriesKey($key, $dimensions = []) {

$dimensionStr = empty($dimensions) ? '' : ':' . md5(json_encode($dimensions));

return "{$this->prefix}:timeseries:{$key}{$dimensionStr}";

}

}

// 使用示例

$counter = new CounterSystem();

// 简单计数

$counter->increment('page_views');

echo "Page Views: " . $counter->get('page_views') . "n";

// 带维度的计数

$counter->increment('article_views', 1, ['article_id' => 123]);

$counter->increment('article_views', 1, ['article_id' => 456]);

echo "Article 123 Views: " . $counter->get('article_views', ['article_id' => 123]) . "n";

// 多维度计数

$counter->increment('downloads', 1, [

'product_id' => 1,

'version' => '2.0',

'platform' => 'ios'

]);

// 获取排行榜

$ranking = $counter->getRanking('article_views', 10);

print_r($ranking);

// 获取时间序列

$timeseries = $counter->getTimeseries('page_views', '-7 days', 'now');

print_r($timeseries);

10.2 构建实时排行榜系统

<?php

/

* 实时排行榜系统

*

* @ai-context

* - 支持多种排名规则

* - 支持历史排名查询

* - 支持排名变化通知

*/

class RealtimeLeaderboard {

private $redis;

private $prefix = 'leaderboard';

public function __construct() {

$this->redis = new Redis();

$this->redis->connect('127.0.0.1', 6380);

$this->redis->auth('password');

}

/

* 添加或更新分数

*/

public function addScore($leaderboard, $member, $score, $memberData = []) {

$key = "{$this->prefix}:{$leaderboard}";

// 更新分数

$this->redis->zadd($key, $score, $member);

// 保存成员数据

if (!empty($memberData)) {

$dataKey = "{$this->prefix}:data:{$leaderboard}:{$member}";

$this->redis->hmset($dataKey, $memberData);

$this->redis->expire($dataKey, 86400 * 30);

}

// 记录历史

$this->recordHistory($leaderboard, $member, $score);

// 检查排名变化

$this->checkRankChange($leaderboard, $member);

return $this->getMemberRank($leaderboard, $member);

}

/

* 批量添加分数

*/

public function addScores($leaderboard, $scores) {

$key = "{$this->prefix}:{$leaderboard}";

$pipeline = $this->redis->pipeline();

foreach ($scores as $member => $score) {

$pipeline->zadd($key, $score, $member);

}

return $pipeline->exec();

}

/

* 获取排行榜(Top N)

*/

public function getTop($leaderboard, $limit = 10, $withData = false) {

$key = "{$this->prefix}:{$leaderboard}";

$members = $this->redis->zrevrange(

$key,

0,

$limit - 1,

['withscores' => true]

);

if (!$withData) {

return $members;

}

// 获取成员数据

$result = [];

foreach ($members as $member => $score) {

$dataKey = "{$this->prefix}:data:{$leaderboard}:{$member}";

$memberData = $this->redis->hgetall($dataKey);

$result[] = [

'member' => $member,

'score' => $score,

'data' => $memberData

];

}

return $result;

}

/

* 获取成员排名和分数

*/

public function getMemberInfo($leaderboard, $member) {

$key = "{$this->prefix}:{$leaderboard}";

$rank = $this->redis->zrevrank($key, $member);

$score = $this->redis->zscore($key, $member);

if ($rank === false || $score === false) {

return null;

}

$dataKey = "{$this->prefix}:data:{$leaderboard}:{$member}";

$memberData = $this->redis->hgetall($dataKey);

return [

'member' => $member,

'rank' => $rank + 1,

'score' => $score,

'data' => $memberData

];

}

/

* 获取分数范围

*/

public function getByScoreRange($leaderboard, $minScore, $maxScore, $limit = 10) {

$key = "{$this->prefix}:{$leaderboard}";

return $this->redis->zrevrangebyscore(

$key,

$maxScore,

$minScore,

['withscores' => true, 'limit' => [0, $limit]]

);

}

/

* 获取排名范围

*/

public function getByRankRange($leaderboard, $minRank, $maxRank) {

$key = "{$this->prefix}:{$leaderboard}";

return $this->redis->zrevrange(

$key,

$minRank - 1,

$maxRank - 1,

['withscores' => true]

);

}

/

* 获取历史排名

*/

public function getHistory($leaderboard, $member, $limit = 10) {

$historyKey = "{$this->prefix}:history:{$leaderboard}:{$member}";

$history = $this->redis->zrevrange(

$historyKey,

0,

$limit - 1,

['withscores' => true]

);

$result = [];

foreach ($history as $data => $timestamp) {

$record = json_decode($data, true);

$record['timestamp'] = $timestamp;

$result[] = $record;

}

return $result;

}

/

* 记录历史

*/

private function recordHistory($leaderboard, $member, $score) {

$historyKey = "{$this->prefix}:history:{$leaderboard}:{$member}";

$timestamp = time();

$data = json_encode([

'member' => $member,

'score' => $score,

'rank' => $this->getMemberRank($leaderboard, $member)

]);

$this->redis->zadd($historyKey, $timestamp, $data);

// 只保留最近100条记录

$this->redis->zremrangebyrank($historyKey, 0, -101);

// 设置过期时间(90天)

$this->redis->expire($historyKey, 86400 * 90);

}

/

* 检查排名变化

*/

private function checkRankChange($leaderboard, $member) {

$currentRank = $this->getMemberRank($leaderboard, $member);

$previousRank = $this->getPreviousRank($leaderboard, $member);

if ($previousRank !== null && $currentRank !== $previousRank) {

$change = $previousRank - $currentRank;

// 排名上升超过10位,发送通知

if ($change >= 10) {

$this->sendRankUpNotification($leaderboard, $member, $currentRank, $change);

}

}

// 保存当前排名

$this->setPreviousRank($leaderboard, $member, $currentRank);

}

/

* 获取成员排名

*/

private function getMemberRank($leaderboard, $member) {

$key = "{$this->prefix}:{$leaderboard}";

$rank = $this->redis->zrevrank($key, $member);

return $rank !== false ? $rank + 1 : null;

}

/

* 获取上一次排名

*/

private function getPreviousRank($leaderboard, $member) {

$key = "{$this->prefix}:prev_rank:{$leaderboard}:{$member}";

$rank = $this->redis->get($key);

return $rank !== false ? (int)$rank : null;

}

/

* 保存当前排名

*/

private function setPreviousRank($leaderboard, $member, $rank) {

$key = "{$this->prefix}:prev_rank:{$leaderboard}:{$member}";

$this->redis->set($key, $rank);

$this->redis->expire($key, 86400 * 7);

}

/

* 发送排名上升通知

*/

private function sendRankUpNotification($leaderboard, $member, $currentRank, $change) {

// 实现通知逻辑(邮件、推送等)

$message = sprintf(

"恭喜!您在%s排行榜中上升了%d位,当前排名:第%d名",

$leaderboard,

$change,

$currentRank

);

error_log($message);

}

}

// 使用示例

$leaderboard = new RealtimeLeaderboard();

// 添加分数

$leaderboard->addScore('game', 'player1', 1000, ['name' => '张三', 'avatar' => 'avatar1.jpg']);

$leaderboard->addScore('game', 'player2', 1500, ['name' => '李四', 'avatar' => 'avatar2.jpg']);

$leaderboard->addScore('game', 'player3', 1200, ['name' => '王五', 'avatar' => 'avatar3.jpg']);

// 获取Top 10

$top10 = $leaderboard->getTop('game', 10, true);

print_r($top10);

// 获取玩家信息

$playerInfo = $leaderboard->getMemberInfo('game', 'player1');

print_r($playerInfo);

// 获取历史排名

$history = $leaderboard->getHistory('game', 'player1', 10);

print_r($history);


最后一些建议

本教程全面介绍了Redis的使用方法,从基础概念到生产环境部署,从简单操作到高级应用。主要内容包括:

核心知识点

1. Redis基础:数据结构、性能特点、应用场景

2. 数据结构:String、Hash、List、Set、Sorted Set的详细使用

3. 三服务器配置:生产环境部署、Sentinel高可用、HAProxy代理

4. PHP集成:基础操作、Laravel集成、连接池管理

5. 性能优化:Pipeline、缓存策略、大Key拆分

6. 监控告警:健康检查、性能监控、故障告警

7. 故障处理:问题诊断、数据恢复、内存优化

8. 实战案例:计数器系统、排行榜系统

最佳实践

1. 使用Pipeline批量操作:性能提升5-10倍

2. 合理设置过期时间:避免内存溢出

3. 避免大Key:拆分大Hash、大List

4. 使用连接池:减少连接开销

5. 启用持久化:RDB + AOF组合

6. 配置高可用:Sentinel自动故障切换

7. 监控关键指标:内存使用、命中率、慢查询

8. 定期备份:自动化备份策略

性能指标

单实例QPS:50,000-80,000

Pipeline QPS:200,000-500,000

内存使用:根据数据量合理配置maxmemory

缓存命中率:建议保持在80%以上

Redis是提升应用性能的利器,合理使用可以显著改善系统响应速度和用户体验。希望本教程能帮助您在实际项目中更好地使用Redis!


参考资料

– Redis官方文档:https://redis.io/documentation

– PHP Redis扩展:https://github.com/phpredis/phpredis

– Laravel Redis文档:https://laravel.com/docs/redis

相关文章

Redis缓存命中率统计工具

三服务器健康检查脚本完整实现

发表评论