Redis实现定长队列

工作经常会碰到类似只保留最新的多少条的问题。直接删除此缓存、或者任由缓存里的数据增加,只取XX条的解决方案都过于粗糙。抽象出此问题,也就是如何维持一个固定大小的缓存。

解决方案1.0

每次添加新元素后,都会判断此缓存中的元素个数,如果大于XX则删除多余元素

$this->redis->lPush($key, $data);
$count = $this->redis->lLen($key);
$limit = 200;
if($count > $limit) {
    $this->redis->rPop($key);
}

解决方案2.0

上述方案,需要与redis通讯至少2次,至多3次,当然可以用事务把前两个redis操作合成一次通讯,但那也至多会有2次,能不能都是一次通讯呢?
我们努力的方向就是如何把元素个数判断给去掉,于是我们可以这么使用redis截取命令LTRIM与 ZREMRANGEBYRANK

/**
 * list添加元素维持固定长度,左侧入栈,右侧出栈
 * @param string $key
 * @param string $data 添加的元素
 * @param int $length 固定长度
 * @return bool/array
 */
public function lPushLimit($key, $data, $length)
{
    if(!$this->redis || empty($data) || $length <= 0){
        return false;
    }
    return $this->redis->multi()
        ->lPush($key, $data)
        ->lTrim($key, 0, $length-1)
        ->exec();
}

/**
 * list添加元素维持固定长度,右侧入栈,左侧出栈
 * @param string $key
 * @param string $data 添加的元素
 * @param int $length 固定长度
 * @return bool/array
 */
public function rPushLimit($key, $data, $length)
{
    if(!$this->redis || empty($data) || $length <= 0){
        return false;
    }
    return $this->redis->multi()
        ->rPush($key, $data)
        ->lTrim($key, -$length, -1)
        ->exec();
}

/**
 * zset添加元素时维持固定长度
 * @param string $key
 * @param array $data 添加的元素 [6, 'sixth']/[6, 'sixth', 7, 'seventh']
 * @param int $length 固定长度
 * @param bool $pop_min  出栈元素是否为最小score值
 * @return bool/array
 */
public function zAddLimit($key, $data, $length, $pop_min = true)
{
    if(!$this->redis || empty($data) || $length <= 0){
        return false;
    }

    if ($pop_min) {
        $start = 0;
        $stop = -$length-1;
    } else {
        $start = $length;
        $stop = PHP_INT_MAX;
    }
    return $this->redis->multi()
        ->zAdd($key, ...$data)
        ->zRemRangeByRank($key, $start, $stop)
        ->exec();
}

相关参考

lTrim命令
zRemRangeByRank命令

posted @ 2016-04-18 14:33  都市烟火  阅读(2917)  评论(0编辑  收藏  举报