在Web开发中缓存是提升性能的重要手段。而Redis作为目前最流行的内存数据库和缓存系统凭借其高性能丰富的数据结构和灵活的功能被广泛应用于各种Web项目,中。

2016年Redis已经非常成熟和流行了几乎所有的中大型Web项目都会使用Redis作为缓存和数据存储。今天就来分享一下Redis缓存的入门与实战教程包括Redis简介安装配置数据结构常用命令PHP操作Redis缓存实战等希望能帮到大家。

1. Redis简介

1.1 什么是Redis

Redis全称Remote Dictionary Server是一个开源的高性能键值对内存数据库。它由Salvatore Sanfilippo开发首次发布于2009年。Redis将数据存储在内存中因此读写速度非常快每秒可以处理超过10万次操作是目前最快的数据库之一。

Redis不仅支持简单的键值对数据还支持丰富的数据结构比如字符串列表集合有序集合哈希等这使得Redis的应用场景非常广泛不仅可以作为缓存还可以作为消息队列排行榜计数器分布式锁,等。

1.2 Redis的特点

  • 高性能:数据存储在内存中读写速度极快每秒可处理10万+,次操作。
  • 丰富的数据结构:支持字符串列表集合有序集合哈希等多种数据结构。
  • 持久化:支持RDB和AOF两种持久化方式可以将内存中的数据保存到磁盘防止数据丢失。
  • 高可用:支持主从复制哨兵集群等高可用方案。
  • 原子操作:所有操作都是原子的支持事务。
  • 丰富的应用场景:可以用于缓存消息队列排行榜计数器分布式锁会话存储,等。

1.3 Redis的应用场景

  • 缓存:最常见的应用场景缓存热点数据减轻数据库压力。
  • 消息队列:使用列表类型实现简单的消息队列。
  • 排行榜:使用有序集合实现各种排行榜。
  • 计数器:使用字符串类型的INCR命令实现各种计数器比如阅读量点赞数,等。
  • 分布式锁:使用SETNX命令实现分布式,锁。
  • 会话存储:存储用户会话信息替代传统的文件会话。
  • 最新列表:使用列表类型实现最新文章最新评论,等。

2. Redis安装与配置

2.1 Linux安装

在Linux上安装Redis很简单可以通过包管理器安装也可以源码编译安装。

使用包管理器安装(以Ubuntu为例):

sudo apt-get update
sudo apt-get install redis-server

源码编译安装:

# 下载源码
wget http://download.redis.io/releases/redis-3.2.0.tar.gz
tar xzf redis-3.2.0.tar.gz
cd redis-3.2.0

# 编译
make

# 安装
sudo make install

# 启动
redis-server

2.2 Windows安装

在Windows上安装Redis可以下载微软维护的Windows版本或者使用第三方的Windows版本。

下载地址:https://github.com/MSOpenTech/redis/releases

下载后解压运行redis-server.exe即可启动Redis。

2.3 配置文件

Redis的配置文件是redis.conf主要配置项包括:

# 绑定IP
bind 127.0.0.1

# 端口
port 6379

# 密码
requirepass yourpassword

# 数据库数量
databases 16

# 持久化 - RDB
save 900 1
save 300 10
save 60 10000

# 持久化 - AOF
appendonly yes
appendfsync everysec

# 最大内存
maxmemory 256mb

# 内存淘汰策略
maxmemory-policy allkeys-lru

2.4 启动与连接

启动Redis服务:

redis-server /etc/redis/redis.conf

连接Redis:

redis-cli -h 127.0.0.1 -p 6379 -a yourpassword

测试连接:

ping
# 返回 PONG 表示连接成功

3. Redis数据结构

3.1 字符串(String)

字符串是Redis最基本的数据结构一个键对应一个字符串,值。字符串类型是二进制安全的可以存储任何数据比如字符串数字图片序列化对象,等。

常用命令:

# 设置值
SET key value

# 获取值
GET key

# 设置值并指定过期时间(秒)
SETEX key seconds value

# 设置值并指定过期时间(毫秒)
PSETEX key milliseconds value

# 只有键不存在时才设置
SETNX key value

# 批量设置
MSET key1 value1 key2 value2

# 批量获取
MGET key1 key2

# 自增
INCR key

# 自减
DECR key

# 增加指定值
INCRBY key increment

# 减少指定值
DECRBY key decrement

# 追加内容
APPEND key value

# 获取字符串长度
STRLEN key

3.2 列表(List)

列表是简单的字符串列表按照插入顺序排序可以在列表的头部或尾部添加元素。列表的底层是双向链表所以在两端操作速度很快但是通过索引访问速度较慢。

常用命令:

# 从左边添加元素
LPUSH key value1 value2

# 从右边添加元素
RPUSH key value1 value2

# 从左边弹出元素
LPOP key

# 从右边弹出元素
RPOP key

# 获取列表长度
LLEN key

# 通过索引获取元素
LINDEX key index

# 通过索引设置元素
LSET key index value

# 获取列表指定范围的元素
LRANGE key start stop

# 移除列表元素
LREM key count value

# 只保留列表指定范围的元素
LTRIM key start stop

3.3 集合(Set)

集合是无序的字符串集合集合中的元素是唯一的不允许重复。集合的底层是通过哈希表实现的所以添加删除查找的时间复杂度都是O(1)。

常用命令:

# 添加元素
SADD key member1 member2

# 移除元素
SREM key member1 member2

# 获取集合所有元素
SMEMBERS key

# 判断元素是否在集合中
SISMEMBER key member

# 获取集合元素数量
SCARD key

# 随机获取一个元素
SRANDMEMBER key

# 随机弹出一个元素
SPOP key

# 交集
SINTER key1 key2

# 并集
SUNION key1 key2

# 差集
SDIFF key1 key2

3.4 有序集合(Sorted Set)

有序集合和集合类似也是字符串的集合不允许重复元素。不同的是有序集合中每个元素都关联一个分数(score),通过分数对集合中的元素进行排序。

常用命令:

# 添加元素
ZADD key score1 member1 score2 member2

# 移除元素
ZREM key member1 member2

# 获取元素分数
ZSCORE key member

# 获取元素排名(从小到大)
ZRANK key member

# 获取元素排名(从大到小)
ZREVRANK key member

# 获取指定排名范围的元素(从小到大)
ZRANGE key start stop WITHSCORES

# 获取指定排名范围的元素(从大到小)
ZREVRANGE key start stop WITHSCORES

# 获取指定分数范围的元素
ZRANGEBYSCORE key min max WITHSCORES

# 获取集合元素数量
ZCARD key

# 获取指定分数范围的元素数量
ZCOUNT key min max

# 增加元素分数
ZINCRBY key increment member

3.5 哈希(Hash)

哈希是键值对的集合特别适合存储对象。哈希中每个键对应一个哈希表哈希表中包含多个字段和,值。

常用命令:

# 设置字段
HSET key field value

# 获取字段
HGET key field

# 批量设置字段
HMSET key field1 value1 field2 value2

# 批量获取字段
HMGET key field1 field2

# 获取所有字段和值
HGETALL key

# 获取所有字段
HKEYS key

# 获取所有值
HVALS key

# 删除字段
HDEL key field1 field2

# 判断字段是否存在
HEXISTS key field

# 获取字段数量
HLEN key

# 字段值自增
HINCRBY key field increment

4. PHP操作Redis

4.1 安装Redis扩展

PHP操作Redis需要安装Redis扩展。

Linux安装:

# 下载扩展
wget https://github.com/phpredis/phpredis/archive/3.0.0.tar.gz
tar xzf 3.0.0.tar.gz
cd phpredis-3.0.0

# 编译安装
phpize
./configure
make
sudo make install

# 在php.ini中添加扩展
extension=redis.so

Windows安装:

下载对应PHP版本和线程安全版本的php_redis.dll放到PHP的ext目录然后在php.ini中添加:

extension=php_redis.dll

4.2 连接Redis

<?php
// 创建Redis实例
$redis = new Redis();

// 连接Redis服务器
$redis->connect('127.0.0.1', 6379);

// 密码认证
$redis->auth('yourpassword');

// 选择数据库
$redis->select(0);

// 测试连接
echo $redis->ping(); // 返回 +PONG

4.3 字符串操作

<?php
// 设置值
$redis->set('name', '张三');

// 获取值
echo $redis->get('name'); // 张三

// 设置值并指定过期时间(秒)
$redis->setex('code', 300, '123456');

// 只有键不存在时才设置
$redis->setnx('lock', '1');

// 自增
$redis->incr('views');

// 增加指定值
$redis->incrBy('views', 10);

// 批量设置
$redis->mset(['name' => '张三', 'age' => 25]);

// 批量获取
$data = $redis->mget(['name', 'age']);

4.4 列表操作

<?php
// 从左边添加元素
$redis->lPush('articles', '文章1', '文章2');

// 从右边添加元素
$redis->rPush('articles', '文章3');

// 从左边弹出元素
$article = $redis->lPop('articles');

// 从右边弹出元素
$article = $redis->rPop('articles');

// 获取列表长度
$count = $redis->lLen('articles');

// 获取列表指定范围的元素
$articles = $redis->lRange('articles', 0, 9);

4.5 集合操作

<?php
// 添加元素
$redis->sAdd('tags', 'PHP', 'MySQL', 'Redis');

// 移除元素
$redis->sRem('tags', 'Redis');

// 获取集合所有元素
$tags = $redis->sMembers('tags');

// 判断元素是否在集合中
$exists = $redis->sIsMember('tags', 'PHP');

// 获取集合元素数量
$count = $redis->sCard('tags');

// 交集
$inter = $redis->sInter('tags1', 'tags2');

4.6 有序集合操作

<?php
// 添加元素
$redis->zAdd('ranking', 100, 'user1', 90, 'user2', 80, 'user3');

// 获取指定排名范围的元素(从大到小)
$ranking = $redis->zRevRange('ranking', 0, 9, true);

// 获取元素排名
$rank = $redis->zRevRank('ranking', 'user1');

// 获取元素分数
$score = $redis->zScore('ranking', 'user1');

// 增加元素分数
$redis->zIncrBy('ranking', 5, 'user1');

4.7 哈希操作

<?php
// 设置字段
$redis->hSet('user:1', 'name', '张三');
$redis->hSet('user:1', 'age', 25);

// 获取字段
$name = $redis->hGet('user:1', 'name');

// 批量设置字段
$redis->hMset('user:1', ['name' => '张三', 'age' => 25, 'email' => 'zhangsan@example.com']);

// 批量获取字段
$user = $redis->hMget('user:1', ['name', 'age', 'email']);

// 获取所有字段和值
$user = $redis->hGetAll('user:1');

// 删除字段
$redis->hDel('user:1', 'email');

// 判断字段是否存在
$exists = $redis->hExists('user:1', 'name');

5. Redis缓存实战

5.1 文章详情缓存

在博客系统中文章详情页是访问量比较大的页面可以使用Redis缓存文章详情数据减轻MySQL压力。

<?php
function getArticle($id) {
    global $redis, $db;
    
    $cacheKey = "article:{$id}";
    
    // 先从缓存获取
    $article = $redis->get($cacheKey);
    if ($article !== false) {
        return json_decode($article, true);
    }
    
    // 缓存未命中,从数据库查询
    $stmt = $db->prepare("SELECT * FROM blog_posts WHERE id = ?");
    $stmt->execute([$id]);
    $article = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($article) {
        // 写入缓存,过期时间1小时
        $redis->setex($cacheKey, 3600, json_encode($article));
    }
    
    return $article;
}

// 更新文章时清除缓存
function updateArticle($id, $data) {
    global $redis, $db;
    
    // 更新数据库
    // ...
    
    // 清除缓存
    $cacheKey = "article:{$id}";
    $redis->del($cacheKey);
}

5.2 文章阅读量统计

使用Redis的INCR命令统计文章阅读量比直接更新MySQL性能好很多。

<?php
function incrementViews($id) {
    global $redis;
    
    $cacheKey = "article:views:{$id}";
    
    // 阅读量自增
    $views = $redis->incr($cacheKey);
    
    // 设置过期时间,防止冷数据占用内存
    $redis->expire($cacheKey, 86400);
    
    return $views;
}

// 定时将阅读量同步到数据库
function syncViewsToDB() {
    global $redis, $db;
    
    // 获取所有阅读量缓存键
    $keys = $redis->keys('article:views:*');
    
    foreach ($keys as $key) {
        $id = str_replace('article:views:', '', $key);
        $views = $redis->get($key);
        
        // 更新数据库
        $stmt = $db->prepare("UPDATE blog_posts SET views = views + ? WHERE id = ?");
        $stmt->execute([$views, $id]);
        
        // 删除缓存
        $redis->del($key);
    }
}

5.3 热门文章排行榜

使用Redis的有序集合实现热门文章排行榜。

<?php
// 文章被访问时增加分数
function addHotScore($id) {
    global $redis;
    
    $cacheKey = 'hot:articles';
    
    // 增加文章热度分数
    $redis->zIncrBy($cacheKey, 1, $id);
    
    // 设置过期时间
    $redis->expire($cacheKey, 86400);
}

// 获取热门文章排行榜
function getHotArticles($limit = 10) {
    global $redis, $db;
    
    $cacheKey = 'hot:articles';
    
    // 从有序集合获取排名靠前的文章ID
    $ids = $redis->zRevRange($cacheKey, 0, $limit - 1);
    
    if (empty($ids)) {
        // 缓存未命中,从数据库查询
        $stmt = $db->query("SELECT id FROM blog_posts ORDER BY views DESC LIMIT {$limit}");
        $ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
    }
    
    // 根据ID查询文章详情
    $articles = [];
    foreach ($ids as $id) {
        $articles[] = getArticle($id);
    }
    
    return $articles;
}

5.4 缓存防穿透

缓存穿透是指查询一个数据库中不存在的数据导致每次请求都会查询数据库失去了缓存的意义。

解决方案: 对查询结果为空的数据也进行缓存设置较短的过期时间。

<?php
function getArticle($id) {
    global $redis, $db;
    
    $cacheKey = "article:{$id}";
    
    // 先从缓存获取
    $article = $redis->get($cacheKey);
    if ($article !== false) {
        // 空值缓存标记
        if ($article === 'NULL') {
            return null;
        }
        return json_decode($article, true);
    }
    
    // 缓存未命中,从数据库查询
    $stmt = $db->prepare("SELECT * FROM blog_posts WHERE id = ?");
    $stmt->execute([$id]);
    $article = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($article) {
        // 写入缓存,过期时间1小时
        $redis->setex($cacheKey, 3600, json_encode($article));
    } else {
        // 空值缓存,过期时间5分钟,防止缓存穿透
        $redis->setex($cacheKey, 300, 'NULL');
    }
    
    return $article;
}

5.5 缓存防雪崩

缓存雪崩是指大量缓存在同一时间过期导致大量请求同时查询数据库造成数据库压力过大甚至宕机。

解决方案:

  1. 给缓存过期时间加上随机值避免大量缓存同时过期。
  2. 使用互斥锁或队列避免大量请求同时查询数据库。
  3. 做二级缓存一级缓存失效后查询二级缓存。
<?php
function getArticle($id) {
    global $redis, $db;
    
    $cacheKey = "article:{$id}";
    
    // 先从缓存获取
    $article = $redis->get($cacheKey);
    if ($article !== false) {
        return json_decode($article, true);
    }
    
    // 使用互斥锁防止缓存雪崩
    $lockKey = "lock:article:{$id}";
    $lock = $redis->setnx($lockKey, '1');
    
    if ($lock) {
        // 获取到锁,设置锁过期时间
        $redis->expire($lockKey, 10);
        
        // 从数据库查询
        $stmt = $db->prepare("SELECT * FROM blog_posts WHERE id = ?");
        $stmt->execute([$id]);
        $article = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($article) {
            // 过期时间加上随机值,防止缓存雪崩
            $expire = 3600 + rand(0, 300);
            $redis->setex($cacheKey, $expire, json_encode($article));
        }
        
        // 释放锁
        $redis->del($lockKey);
        
        return $article;
    } else {
        // 未获取到锁,等待后重试
        usleep(100000); // 等待100毫秒
        return getArticle($id);
    }
}

6. Redis持久化

Redis支持两种持久化方式:RDB和AOF。

6.1 RDB持久化

RDB是在指定的时间间隔内将内存中的数据生成快照保存到磁盘。

优点:

  • 文件紧凑适合备份和恢复。
  • 恢复速度,快。
  • 对性能影响,小。

缺点:

  • 可能会丢失最后一次快照之后的数据。
  • 数据量大时保存可能会卡顿。

配置:

save 900 1    # 900秒内至少1个键修改则保存
save 300 10   # 300秒内至少10个键修改则保存
save 60 10000 # 60秒内至少10000个键修改则保存

dbfilename dump.rdb
dir /var/lib/redis

6.2 AOF持久化

AOF是将每一条写命令记录到日志文件中恢复时重新执行这些命令。

优点:

  • 数据安全性高最多丢失1秒数据。
  • 日志文件易读可手动修改。

缺点:

  • 文件体积,大。
  • 恢复速度,慢。
  • 对性能影响稍大。

配置:

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec  # 每秒同步一次
# appendfsync always  # 每次写都同步,最安全但最慢
# appendfsync no      # 不同步,由操作系统决定

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

6.3 如何选择

  • 如果能容忍少量数据丢失追求性能和恢复速度选择RDB。
  • 如果要求数据安全性高选择AOF。
  • 一般建议同时开启RDB和AOF结合两者的优点。

写在最后

以上就是Redis缓存入门与实战教程。Redis是一个非常强大和实用的工具掌握Redis的使用能大大提升我们的Web项目性能和开发效率。

Redis的功能还有很多比如发布订阅事务Lua脚本管道集群哨兵等这些高级功能需要在实际使用中慢慢学习和掌握。对于日常开发来说掌握上面的基本使用和缓存实战就已经足够,了。

如果你还没有使用Redis强烈建议你开始使用,它。它会改变你对缓存和数据存储的认识让你的Web项目性能更上一层,楼。

希望这篇教程能帮到大家快速入门Redis。如果有什么问题或者建议欢迎在评论区留言和我一起讨论。

最后提醒大家几句:

  1. 合理设置过期时间:缓存都应该设置过期时间避免冷数据占用内存。
  2. 注意缓存穿透和雪崩:使用空值缓存防止穿透使用随机过期时间和互斥锁防止雪崩。
  3. 做好持久化:根据需求选择合适的持久化方式避免数据丢失。
  4. 注意内存使用:Redis数据存储在内存中要注意内存使用情况设置合理的最大内存和淘汰策略。

祝大家都能用好Redis打造高性能的Web应用!