初學redis分頁緩存方法實現

1. 直接設置緩存,若是數據量大,操做增刪改,更新緩存頻率高和效率低。

2. 分頁設置緩存,經過頁碼設置緩存。

1.新增-刪除全部緩存(倒敘排序,第一頁插入數據,後續頁列表都改變),
2.修改-更新當前頁緩存,
3.刪除-更新當前頁以及當前頁之後的頁面的緩存。
複製代碼
<?php
class ArticleClass
{
    private $pageCount = 10;//每頁顯示
    /**
     * 獲取列表
     * @param $page_no
     * @return array
     */
    public function getList($page_no){
        $cache = getRedis();
        $cache_key = ArticleList;
        $list = $cache->get($cache_key);
        $count = $this->getCount();    //計算總數
        $page = new Page($count,$this->pageCount);
        $show = $page->show();
        if(!$list){
            $list = M('Articles')->limit($page->firstRow . ',' . $page->listRows)->order('id DESC')->select();
            $ids = [];
            foreach($list as $value){
                $ids[] = $value['id'];
                unset($value);
            }
            $oldids = $cache->get(ArticleId);
            $cache->set($cache_key,$list,3600);//緩存1小時
            $cache->set(ArticleId,$ids);
        }
        return ['page'=>$show,'list'=>$list];
    }

    /**
     * 獲取頁碼總數
     * @return mixed
     */
    public function getCount(){
        return M('Articles')->count();
    }

    /**
     * 獲取最後頁碼
     * @return float
     */
    public function getLastPage(){
        $count = $this->getCount();
        return ceil($count/$this->pageCount);
    }

    /**
     * 獲取某一條記錄信息
     * @param $id
     * @return mixed
     */
    public function getInfo($id){
        $info = M('Articles')->where('id = '.$id)->find();
        return $info;
    }

    /**
     * 保存信息
     * @param $data
     * @return mixed
     */
    public function create($data){
        return M('Articles')->add($data);
    }

    /**
     * 更新信息
     * @param $data
     * @param $id
     * @return mixed
     */
    public function updateById($data,$id){
        return M('Articles')->where('id = '.$id)->save($data);
    }

    /**
     * 刪除信息
     * @param $id
     * @return mixed
     */
    public function deleteById($id){
        return M('Articles')->where('id = '.$id)->delete();
    }

    /**
     * 刪除/修改 緩存
     * @param $type
     * @param null $page
     */
    public function delCache($type,$page=null){
        $cache = getRedis();
        $cache_key = ArticleList;
        if($type == 'add'){
            for($i=1;$i<=$this->getLastPage();$i++){
                $cache->rm($cache_key.$i);
            }
        }elseif($type == 'update'){
            $cache->rm($cache_key.$page);
        }elseif($type == 'delete'){
            for($i=$page;$i<=$this->getLastPage();$i++){
                $cache->rm($cache_key.$i);
            }
        }
    }
}
複製代碼

問題:①傳頁碼,可能人爲修改。②修改一條數據,改一批數據。php

3. 一條數據一個緩存

一開始的誤區:把全部數據先丟在緩存中??不,是把全部數據的id先存到一個ids緩存,獲取數據的時候,根據這個ids去找,緩存中找不到的在經過某個id去數據庫查這個數據並保存緩存。前端

1.用set經過key+id存儲內容信息,並存儲全部數據的id到另一個緩存ids,經過區間獲取ids,經過for用get key+id獲取數據。mysql

引出的問題:
第一個訪問的人,在緩存丟失了,去查找全部記錄丟進緩存這個過程當中,一定很慢!!
複製代碼
解決方法:
全部記錄ids緩存先永久保存,各條記錄緩存加上expire時間,每一個記錄緩存丟失在查找指定的數據寫入緩存。
【這個說法是有問題的!!永久緩存 x,仍是要設置有效時長。】
【第一我的訪問慢的問題,後續經過list實現,redis列表通常不設失效時間】
複製代碼
public function getList(){
    $cache_ids = $this->Cache->get(ArticleId);
    $count = $this->getCount();//計算總數
    $page = new Page($count,$this->pageCount);
    $show = $page->show();
    if(!$cache_ids){//這個在後臺跑一次,永久記錄緩存
        $lists = M('Articles')->order('id DESC')->select();//tp3.2不能直接
查找某個字段全部數據
        $ids = [];
        foreach($lists as $value){
            $ids[] = $value['id'];
            unset($value);
        };
        unset($lists);
        $this->Cache->set(ArticleId,$ids);//記錄全部id到一個緩存
        //當前須要獲取的數據
        $list = M('Articles')->limit($page->firstRow.','.$page->listRows)->order('id DESC')->select();
        foreach($list as $value){
            $this->Cache->set($this->code_key.$value['id'],$value,3600);//
每條記錄一個緩存
            unset($value);
        }
    }else{
        $start = $page->firstRow;
        $end = $start+$page->listRows;
        for($i=$start;$i<$end;$i++){
            if(empty($cache_ids[$i])){
                break;
            }
            $temp = $this->Cache->get(ArticleList.$cache_ids[$i]);
            if(!$temp){//某個緩存不存在,從庫裏拿
                $temp = $this->getInfo($cache_ids[$i]);
                $this->Cache->set($this->code_key.$cache_ids[$i],$temp,3600);//記錄丟失的緩存
            }
            $list[] = $temp;
            unset($temp);
        }
    }
    return ['page'=>$show,'list'=>$list];
}
複製代碼
問題2:
獲取全部id的時候經過foreach獲取??
複製代碼
解決方法:
1.經過數組函數array_column獲取數組某一列的值,返回一個一維數組。
2.其實當時查代碼的時候只是要id,能夠加個filed(‘id’),而後存就存一個二維數組,子數組只有一個字段id,拿的時候經過array_slice獲取一段要的數據。
舒適提示:不要經過foreach獲取緩存!!查每一個緩存,不存在,查數據庫by id,再存緩存。
複製代碼
public function getList(){
    $cache_ids = $this->Cache->get(ArticleId);
    $count = $this->getCount();//計算總數
    $page = new Page($count,$this->pageCount);
    $show = $page->show();
    if(!$cache_ids){//這個在後臺跑一次,永久記錄緩存
        $lists = M('Articles')->order('id DESC')->select();//tp3.2不能直接
查找返回一維數組爲某個字段的全部數據
        $ids = [];
        $ids = array_column($lists,'id');
        unset($lists);
        $this->Cache->set(ArticleId,$ids);//記錄全部id到一個緩存
        $cache_ids = $this->Cache->get(ArticleId);
    }
    $start = $page->firstRow;
    $end = $start+$page->listRows;
    for($i=$start;$i<$end;$i++){
        if(empty($cache_ids[$i])){
            break;
        }
        $temp = $this->Cache->get(ArticleList.$cache_ids[$i]);
        if(!$temp){//某個緩存不存在,從庫裏拿
            $temp = $this->getInfo($cache_ids[$i]);
            $this->Cache->set($this->code_key.$cache_ids[$i],$temp,3600);//記錄丟失的緩存
        }
        $list[] = $temp;
        unset($temp);
    }
    
    return ['page'=>$show,'list'=>$list];
}
複製代碼
  • 增刪改時,更新緩存
如下代碼存在的問題:
1. 新增和修改沒有考慮ids緩存不存在的狀況!!
2. 且存在隱患:當用戶新增數據併發量大,插入數據庫先的人存入緩存慢!!
致使列表排序有問題!!如:當前數據1,2,3;a先插入4,可是還沒來得及保存到緩存的那一個節點!
b插入了5,且插入了緩存,致使本來應該1,2,3,4,5的緩存列表變成1,2,3,5,4
一勞永逸的解決方案:增刪改涉及到的緩存直接刪除!!由於獲取的時候找不到緩存時會到數據庫查並存入緩存!!
複製代碼
/**
 * 新增一個緩存
 * @param $id
 */
public function addCache($id){
    $info = $this->getInfo($id);
    $this->Cache->set($this->code_key.$id,$info,3600);//記錄一條的緩存
    $ids = $this->Cache->get($this->code_ids);
    array_unshift($ids,$id);
    $this->Cache->set($this->code_ids,$ids);
}

/**
 * 修改一個緩存
 * @param $id
 */
public function updateCache($id){
    $info = $this->getInfo($id);
    $this->Cache->set($this->code_key.$id,$info,3600);//記錄一條的緩存
}

/**
 * 刪除一個緩存
 * @param $id
 */
public function delCache($id){
    $this->Cache->rm($this->code_key.$id);//記錄一條的緩存
    $ids = $this->Cache->get($this->code_ids);
    array_splice($ids,array_search($id,$ids),1);
    $this->Cache->set($this->code_ids,$ids);
}
複製代碼
  • 最終代碼
<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/10
 * Time: 14:41
 */
namespace Api\Controller;
use Think\Controller;
use Think\Page;

class ArticleClass
{
    private $pageCount = 10;//每頁顯示
    private $Cache;//存儲緩存對象
    public function __construct()
    {
        $this->Cache = getRedis();
    }

    /**
     * 獲取列表
     * @return array
     */
    public function getList(){
        $cache_ids = $this->getIdList();//獲取數據
        //分頁
        $count = count($cache_ids);//計算總數
        $page = new Page($count,$this->pageCount);
        $show = $page->show();
        $start = $page->firstRow;
        $end = $start+$page->listRows;
        $list = [];
        for($i=$start;$i<$end;$i++){
            if(empty($cache_ids[$i])){
                break;
            }
            $temp = $this->getInfo($cache_ids[$i]);
            $list[] = $temp;
            unset($temp);
        }
        return ['page'=>$show,'list'=>$list];
    }

    /**
     * 獲取ID列表
     * @return mixed
     */
    public function getIdList(){
        $cache_ids = $this->Cache->get(ArticleId);
        if(!$cache_ids){//id緩存不存在,進來
            $lists = M('Articles')->order('id DESC')->select();//tp3.2不能直接返回一維數組爲某個字段全部數據
            $ids = [];
            $ids = array_column($lists,'id');
            $this->Cache->set(ArticleId,$ids,24*3600);//記錄全部id到一個緩存
            $cache_ids = $this->Cache->get(ArticleId);
        }
        return $cache_ids;
    }

    /**
     * 獲取某一條記錄信息
     * @param $id
     * @return mixed
     */
    public function getInfo($id){
        $info = $this->Cache->get(ArticleList.$id);
        if(!$info){//某個緩存不存在,從庫裏拿
            $info = M('Articles')->where('id = '.$id)->find();
            $this->Cache->set(ArticleList.$id,$info,3600);//記錄丟失的緩存
        }
        return $info;
    }

    /**
     * 新增 by id
     * @param $data
     * @return mixed
     */
    public function create($data){
        $id = M('Articles')->add($data);
        $this->killActicleIdsCache();
        return $id;
    }

    /**
     * 更新by id
     * @param $data
     * @param $id
     * @return mixed
     */
    public function updateById($data,$id){
        $res = M('Articles')->where('id = '.$id)->save($data);
        $this->kellActicleInfoCache($id);
        return $res;
    }

    /**
     * 刪除by id
     * @param $id
     * @return mixed
     */
    public function deleteById($id){
        $res = M('Articles')->where('id = '.$id)->delete();
        $this->kellActicleInfoCache($id);
        $this->killActicleIdsCache();
        return $res;
    }

    /**
     * 刪除內容緩存
     * @param $id
     */
    public function kellActicleInfoCache($id){
        $this->Cache->rm(ArticleList.$id);//刪除一條的緩存
    }

    /**
     * 清除ids緩存
     */
    public function killActicleIdsCache(){
        $ids = $this->Cache->get(ArticleId);
        if($ids){
            $this->Cache->rm(ArticleId);
        }
    }
}
複製代碼

2)list mysql查詢好,排好序,都丟在一個列表裏面。經過區間獲取數據。redis

弊端:sql

a.不能指定修改、刪除某個key,增刪改都要重寫(先刪除,後for循環插入)整個list.數據庫

b.併發的寫緩存,可能致使數據重複(數據不是惟一性,集合)數組

上面是想岔了,①.list能夠經過lre指定刪除每一個值的,並且list也是存id,信息數據仍是一條一個緩存。②.數據重複主要是在翻頁的時候恰好有人寫入數據(經過id倒序排),若是經過page當前頁碼和pageSize每頁顯示個數分頁,就會出現本來在第一頁的數據,下拉加載第二頁的時候發現也在!!由於在第一頁頭插入了數據把第一頁最後的數據頂到第二頁或者更後面的頁碼。。。

解決思路:

首先,把數據經過id順序rpush從尾查到頭,獲取數據時就從尾拿到頭。分頁經過最後一條記錄的索引值獲得下一頁end值,而後經過pageSize計算出start值,最後經過lrange(ids,start,end)獲取分頁區間。索引值key傳給前端,前端再傳回來;若是不存在,則獲取數據總數llen(),獲得$end,再處理。新增經過rpush()插入尾部,刪除經過lrem()刪除指定值。更新和修改刪除對應的info緩存。
例如:
1-----5-----10-----15 有15條數據,每頁5條數據分頁。

lrange(ids,14,14-5+1) 即索引爲10,11,12,13,14的數據。(索引從0開始)
複製代碼

完整代碼以下緩存

class ArticleClass
{
    private $pageCount = 5;//每頁顯示
    private $Cache;//存儲緩存對象
    public function __construct()
    {
        $this->Cache = getRedis();
    }

    /**
     * 獲取列表
     * @param null $key 列表索引
     * @return array
     */
    public function getList($key=null){
        //分頁
        if($key == null){
            $end = $this->getArticleIdLen();
        }else{
            $end = $key;
        }
        $end = $end - 1;
        if($end < 0){
            return ['list'=>[],'key'=>0];
        }
        $start = $end - $this->pageCount + 1;
        if($start <= 0){
            $start = 0;
        }
        $cache_ids = $this->getRangeId($start,$end);//獲取數據
        $list = [];
        foreach($cache_ids as $id){
            $info = $this->getInfo($id);
            $list[] = $info;
        }
        rsort($list);
        return ['list'=>$list,'key'=>$start];
    }

    /**
     * 獲取ids緩存長度
     * @return mixed
     */
    public function getArticleIdLen(){
        if(!$this->Cache->exists(ArticleId)){//緩存不存在,獲取
            $this->setIdList();
        }
        $count = $this->Cache->llen(ArticleId);
        return $count;
    }

    /**
     * 獲取ID區間緩存
     * @param $start
     * @param $end
     * @return mixed
     */
    public function getRangeId($start,$end){
        if(!$this->Cache->exists(ArticleId)){//緩存不存在,獲取
            $this->setIdList();
        }
        $cache_ids = $this->Cache->lrange(ArticleId,$start,$end);
        return $cache_ids;
    }

    /**
     * 設置ID列表緩存
     * @return mixed
     */
    public function setIdList(){
        $lists = M('Articles')->order('id ASC')->select();
        $ids = [];
        $ids = array_column($lists,'id');
        foreach($ids as $value){
            $this->Cache->rpush(ArticleId,$value);
        }
    }

    /**
     * 獲取某一條記錄信息
     * @param $id
     * @return mixed
     */
    public function getInfo($id){
        $info = $this->Cache->get(ArticleList.$id);
        if(!$info){//某個緩存不存在,從庫裏拿
            $info = M('Articles')->where('id = '.$id)->find();
            $this->Cache->set(ArticleList.$id,$info,3600);//記錄丟失的緩存
        }
        return $info;
    }

    /**
     * 新增 by id
     * @param $data
     * @return mixed
     */
    public function create($data){
        $id = M('Articles')->add($data);
        $this->Cache->rpush(ArticleId,$id);//插到列表尾巴
        return $id;
    }

    /**
     * 更新by id
     * @param $data
     * @param $id
     * @return mixed
     */
    public function updateById($data,$id){
        $res = M('Articles')->where('id = '.$id)->save($data);
        $this->kellActicleInfoCache($id);
        return $res;
    }

    /**
     * 刪除by id
     * @param $id
     * @return mixed
     */
    public function deleteById($id){
        $res = M('Articles')->where('id = '.$id)->delete();
        $this->kellActicleInfoCache($id);
        $this->Cache->lrem(ArticleId,$id);//刪除id列表緩存
        return $res;
    }

    /**
     * 刪除內容緩存
     * @param $id
     */
    public function kellActicleInfoCache($id){
        $this->Cache->rm(ArticleList.$id);//刪除一條的緩存
    }
}
複製代碼

擴展問題:數據量太大,經過時間分多個redis存儲,每月存一個reids列表,怎麼拿完這個月的值再拿上一個月的,而且要數據銜接好?bash

解決思路:獲取值時先獲取當前月份的數據,若數據不知足當前頁碼要顯示的個數,則獲取下一個緩存補充,一個緩存獲取完,獲取下一個緩存。前端傳多一個緩存塊id。併發

代碼以下:

class ArticleClass
{
    private $pageCount = 5;//每頁顯示
    private $Cache;//存儲緩存對象
    private $cache_block = ['2018-04','2018-03'];
    public function __construct()
    {
        $this->Cache = getRedis();
    }

    /**
     * 獲取列表
     * @param null $key 列表索引
     * @return array
     */
    public function getList($key=null,$block=0){
        //分頁
        if($key == null){
            $end = $this->getArticleIdLen($this->cache_block[$block]);
        }else{
            $end = $key;
        }
        $end = $end - 1;
        if($end < 0){//若是沒有值,拿它下一個緩存
            $block++;
            $end = $this->getArticleIdLen($this->cache_block[$block]);
            $end = $end - 1;
            if($end < 0){
                $block--;
                return ['list'=>[],'key'=>0,'block'=>$block];
            }
        }
        $start = $end - $this->pageCount + 1;
        if($start <= 0){
            $start = 0;
        }
        $cache_ids = $this->getRangeId($start,$end,$this->cache_block[$block]);//獲取數據

        if(count($cache_ids) < $this->pageCount){//最後返回數量不足,拿下一個補充
            $block++;
            $end = $this->getArticleIdLen($this->cache_block[$block]);
            $end = $end - 1;
            if($end > 0){
                $start = $end - $this->pageCount + 1 + count($cache_ids);
                $block_ids = $this->getRangeId($start,$end,$this->cache_block[$block]);
                $cache_ids = array_merge($cache_ids,$block_ids);
            }else{
                $block--;
            }
        }
        $list = [];
        foreach($cache_ids as $id){
            $info = $this->getInfo($id);
            $list[] = $info;
            unset($id);
        }
        rsort($list);
        return ['list'=>$list,'key'=>$start,'block'=>$block];
    }

    /**
     * 獲取ids緩存長度
     * @return mixed
     */
    public function getArticleIdLen($block){
        if(!$this->Cache->exists(ArticleId.$block)){
            $this->setIdList($block);
        }
        $count = $this->Cache->llen(ArticleId.$block);
        return $count;
    }

    /**
     * 獲取ID區間緩存
     * @param $start
     * @param $end
     * @return mixed
     */
    public function getRangeId($start,$end,$block){
        if(!$this->Cache->exists(ArticleId.$block)){
            $this->setIdList($block);
        }
        $cache_ids = $this->Cache->lrange(ArticleId.$block,$start,$end);
        return $cache_ids;
    }

    /**
     * 設置ID列表緩存
     * @return mixed
     */
    public function setIdList($block){
        $lists = M('Articles')->where("DATE_FORMAT(created_time,'%Y-%m') = '".$block."'")->order('id ASC')->select();
        $ids = [];
        if(count($lists)){
            $ids = array_column($lists,'id');
            foreach($ids as $value){
                $this->Cache->rpush(ArticleId.$block,$value);
            }
        }
    }

    /**
     * 獲取某一條記錄信息
     * @param $id
     * @return mixed
     */
    public function getInfo($id){
        $info = $this->Cache->get(ArticleList.$id);
        if(!$info){//某個緩存不存在,從庫裏拿
            $info = M('Articles')->where('id = '.$id)->find();
            $this->Cache->set(ArticleList.$id,$info,3600);//記錄丟失的緩存
        }
        return $info;
    }

    /**
     * 新增 by id
     * @param $data
     * @return mixed
     */
    public function create($data){
        $id = M('Articles')->add($data);
        $this->Cache->rpush(ArticleId.substr($data['created_time'],0,7),$id);//插到列表尾巴
        return $id;
    }

    /**
     * 更新by id
     * @param $data
     * @param $id
     * @return mixed
     */
    public function updateById($data,$id){
        $res = M('Articles')->where('id = '.$id)->save($data);
        $this->kellActicleInfoCache($id);
        return $res;
    }

    /**
     * 刪除by id
     * @param $id
     * @return mixed
     */
    public function deleteById($id){
        $data = M('Articles')->field('created_time')->where('id = '.$id)->find();
        $res = M('Articles')->where('id = '.$id)->delete();
        $this->kellActicleInfoCache($id);
        $this->Cache->lrem(ArticleId.substr($data['created_time'],0,7),$id);//刪除id列表緩存
        return $res;
    }

    /**
     * 刪除內容緩存
     * @param $id
     */
    public function kellActicleInfoCache($id){
        $this->Cache->rm(ArticleList.$id);//刪除一條的緩存
    }
}
複製代碼

3)zset

相關文章
相關標籤/搜索