一、缓存

Redis最常见的用途是作为缓存。所谓缓存,就是将频繁读取、但不经常修改的数据存储在内存中,用户请求数据时优先从内存中读取,可大幅提升数据访问效率。Redis的数据结构特别适合缓存场景,并且其本身的读取速度也非常快,可以在存储海量并发访问情况下保持响应性能。

对于访问频率较高的网站,可通过将热门数据存入Redis中,降低对数据库的访问压力。例如,盛大游戏就使用Redis缓存了用户会话信息,避免了每次访问都需要读取数据库的开销,从而获得了更好的性能。

<?php
// 通过Redis缓存获取数据
$key = 'hot_user_list';
$data = $redis->get($key);
if (!$data) {
    // 缓存未命中,从数据库获取数据
    $data = $db->select("SELECT * FROM user WHERE role = 'hot'");
    $redis->set($key, $data);
}

二、分布式锁

在高并发的场景下,请求可能会同时涌入并尝试对同一个资源进行修改。例如,多个用户同时抢购同一件商品,此时需要保证只有一个用户能够成功抢购。而分布式锁就是用来解决这一问题的。

Redis可以通过SETNX(set if not exist)命令实现分布式锁。当多个请求同时尝试对同一资源加锁时,只有一个请求能够成功返回1,其他请求都会等待,直到锁被释放。由于Redis天生支持分布式,使用它实现分布式锁非常方便。

<?php
// 尝试加锁
$is_locked = $redis->setnx("resource_key", 1);
if ($is_locked) {
    // 成功加锁,进行资源操作
    operate_resource();
    // 释放锁
    $redis->del("resource_key");
} else {
    // 未获取到锁,等待重试或返回错误
    return 'Resource is locked';
}

三、消息队列

消息队列是一类特殊的数据结构,用于存储并转发消息。在高并发的场景下,将任务尽可能地异步化处理,是提高系统并发能力的关键。而Redis的LIST命令提供的队列特性,使得它成为一个好的消息队列候选者。

例如,在电商后台系统中,订单状态变化的同时需要发送通知给用户,若采用同步方式处理,则用户的请求响应速度会变慢。但若采用消息队列,将订单状态变化事件放入队列中,再使用后台任务进行异步处理,便能避免用户等待,提升整体系统的性能。

<?php
// 生产者向消息队列推送消息
$redis->lpush('order_queue', $order_id);

// 消费者从消息队列获取消息并处理
while ($order_id = $redis->rpop('order_queue')) {
    process_order($order_id);
}

四、统计分析

Redis除了能够存储数据外,还可以进行一些简单的数据分析。例如,可以利用Redis的HyperLogLog(基数统计算法)或者布隆过滤器(一种高效的数据结构,用于判断一个元素是否存在于一个集合中)实现访问用户量、UV统计等数据分析。

前段时间,某直播平台的负责人表示,如果在数据库中进行UV统计,每天都需要统计上亿条数据,成本将会非常巨大。但如果使用Redis,就能大幅降低成本,提升实时性。

<?php
// 统计日访问量
$redis->pfadd('day_visit', $user_id);
$visit_count = $redis->pfcount('day_visit');

五、分布式锁模板代码示例

<?php
class RedisLock
{
    private $redis;
    private $key;
    private $expires;
    private $token;

    public function __construct(Redis $redis, string $key, int $expires = 5)
    {
        $this->redis = $redis;
        $this->key = $key;
        $this->expires = $expires;
        $this->token = uniqid();
    }

    public function acquire(): bool
    {
        return $this->redis->set($this->key, $this->token, ['NX', 'EX' => $this->expires]);
    }

    public function release(): bool
    {
        $lua = <<redis->eval($lua, 1, $this->key, $this->token);
    }
}

以上是纯文本,可以直接复制使用。需要注意的是,该代码示例使用了Lua脚本实现了释放锁时验证锁的持有者和删除锁的操作是一个原子操作,保证了锁释放的原子性。