Реализация метода кэширования подкачки для начинающих

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);
            }
        }
    }
}

Вопрос: ① Номер страницы можно изменить вручную. ②Изменить часть данных и изменить пакет данных.

3. Один кэш данных

Первоначальное недоразумение: сначала положить все данные в кеш? ? Нет, сначала хранить id всех данных в кеше ids.При получении данных искать его по id.Если данные не могут быть найдены в кеше, зайти в базу для проверки данных через определенный id и сохраните кеш.

1. Используйте набор для хранения информации о содержимом через ключ+идентификатор и сохранения идентификатора всех данных в других идентификаторах кеша, получения идентификаторов через интервал и получения данных через получение ключа+идентификатор для.

Поднятые вопросы:
第一个访问的人,在缓存丢失了,去查找所有记录丢进缓存这个过程中,必定很慢!!
解决方法:
所有记录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) Список запросов mysql хороший, отсортирован и помещен в список. Получить данные по интервалу.

Недостатки:

А. Вы не можете указать изменить или удалить определенный ключ.Добавления, удаления и модификации должны быть переписаны (сначала удалить, а затем вставить в цикл for) весь список.

б) одновременная запись в кэш, что может привести к дублированию данных (данные не уникальны, сбор)

Вышеупомянутое является ошибкой, ①.list может указать удалить каждое значение через lre, а в списке также хранится идентификатор, а информационные данные все еще кэшируются один за другим. ②. Дублирование данных в основном вызвано тем, что кто-то записывает данные при переворачивании страницы (в обратном порядке по идентификатору).Если текущий номер страницы и pageSize страницы используются для отображения количества подкачек, данные, изначально находившиеся на первой странице, будут появится, и будет отображаться выпадающая загрузка.Я нашел это и на второй странице! ! Поскольку данные вставляются в заголовок первой страницы, последние данные первой страницы помещаются на вторую или более позднюю страницу. . .

Решения:

Сначала осуществляется поиск данных от хвоста к голове через последовательность id rpush, а при получении данных из хвоста получается голова. Пагинация получает следующую страницу через значение индекса последней записиend值,然后通过pageSize计算出начальное значение и, наконец, передать lrange(ids,start,end), чтобы получить интервал подкачки. Ключ значения индекса передается внешнему интерфейсу, а внешний интерфейс передает его обратно; если он не существует, получить общее количество данных llen(), получить $end и затем обработать его. Добавлена ​​вставка хвоста через rpush() и удаление указанного значения через lrem(). Обновите и измените удаление соответствующего кэша информации.
例如:
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);//删除一条的缓存
    }
}

Проблема с расширением: объем данных слишком велик, он хранится в нескольких редисах по времени, и каждый месяц хранится список ридов, как я могу получить значение этого месяца, а затем получить следующий месяц, и данные должны быть на связи?

Решение: При получении значения сначала получить данные за текущий месяц.Если данные не соответствуют числу, которое будет отображаться на текущем номере страницы, получить следующее дополнение кэша.После получения одного кэша, получить следующий кэш. Внешний интерфейс передает еще один идентификатор блока кэша.

код показывает, как показано ниже:

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