Как Redis устанавливает время истечения срока действия в пакетах? Использование PIPLINE

задняя часть Архитектура
Как Redis устанавливает время истечения срока действия в пакетах? Использование PIPLINE

«Это 21-й день моего участия в ноябрьском испытании обновлений. Подробную информацию о мероприятии см.:Вызов последнего обновления 2021 г."

Разумное использование стратегии кэширования похоже на способность Сунь Укуна освоить высший навык для развития учеников~

Как Redis устанавливает время истечения срока действия в пакетах?

Не говорите, что время истечения устанавливается пакетами с помощью функции set() в foreach.

Мы представляем PIPLINE redis и устанавливаем время истечения срока действия для решения основной части проблем.

Каков принцип ТРУБОПРОВОДА?

  1. N команд не выполняются с использованием конвейера

image.png

  1. Выполнить N команд, используя конвейер

image.png

Принцип PIPLINE хорошо виден из легенды:

Клиент объединяет подкоманды через PIPLINE, и ему нужно отправить запрос только один раз.После того, как redis получает команду PIPLINE, он обрабатывает блок команд, состоящий из PIPLINE, уменьшая количество ответов на сетевой запрос.

网络延迟越大PIPLINE的优势越能体现出来

拼接的子命令条数越多使用PIPLINE的优势越能体现出来

Примечание: дело не в том, что чем больше подкоманд сплайсировано, тем лучше. Значение N также имеет верхний предел. Когда команда сплайсинга слишком длинная, клиент будет долго ждать и вызовет перегрузку сети, мы можем разобрать большое количество команд в соответствии с реальной ситуацией.Разделите на несколько реализаций PIPLINE.

инкапсуляция кода

//批量设置过期时间
public static function myPut(array $data, $ttl = 0)
{
    if (empty($data)) {
        return false;
    }

    $pipeline = Redis::connection('cache')
        ->multi(\Redis::PIPELINE);
    foreach ($data as $key => $value) {
        if (empty($value)) {
            continue;
        }
        if ($ttl == 0) {
            $pipeline->set(trim($key), $value);
        } else {
            $pipeline->set(trim($key), $value, $ttl);
        }
    }
    $pipeline->exec();
}

Боевой проект

Описание требования

  1. Откройте ПРИЛОЖЕНИЕ и отправьте мое онлайн-уведомление тем, кому я нравлюсь (во избежание прерывания повторный вход в течение 8 часов не вызовет уведомления)

  2. Все будут получать такого рода онлайн-уведомление только раз в полчаса (то есть, даже если 10 000 человек, которые мне нравятся, будут онлайн в течение получаса, я получу уведомление только о том, что мой любимый человек онлайн)

точечный анализ

  1. Разумное использование кеша для уменьшения количества операций чтения и записи БД.

  2. Я должен не только уменьшить количество операций чтения и записи БД, но также уменьшить количество операций чтения и записи повторных операций, использоватьPIPLINE

Анализ реализации кода

  1. canRecall()Изящнее написать, сначала рассудить, отправлена ​​ли отметка, а потом уже судитьHouseOpen::getCurrentOpen(),так какHouseOpen::getCurrentOpen()Это запрос к расчету БД, и этот код должен выполняться как можно реже, чтобы уменьшить запрос к БД.

  2. array_diff()Идея использования набора различий, чтобы получить людей, которым нужно подтолкнуть

пакетный инструментальный класс

<?php

namespace App\Model\House;

.
.
.

class HouseLikeRecallUser
{
    protected $_userid = '';
    protected $_availableUser = [];
    protected $_recallFlagKey = '';

    const TYPE_TTL_HOUSE_LIKE_RECALL = 60 * 30; //半小时后可以再次接收到喜欢的xxx进入通知
    const TYPE_TTL_HOUSE_LIKE_RECALL_FLAG = 60 * 60 * 8; //8小时重复登录不触发

    //初始化 传入setRecalled 的过期时间
    public function __construct($userid)
    {
        $this->_userid = $userid;
        //登录后给喜欢我的人推送校验:同一场次重复登录不重复发送
        $this->_recallFlagKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL_FLAG, $this->_userid);
    }

    //设置当前用户推送标示
    public function setRecalled()
    {
        Cache::put($this->_recallFlagKey, 1, self::TYPE_TTL_HOUSE_LIKE_RECALL_FLAG);
    }

    //获取当前用户是否触发推送
    public function canRecall()
    {
        $res = false;
        if (empty(Cache::get($this->_recallFlagKey))) {
            $houseOpen = HouseOpen::getCurrentOpen();
            if ($houseOpen['status'] == HouseOpen::HOUSE_STATUS_OPEN) {
                $res = true;
            }
        }
        return $res;
    }

    //获取需要推送用户
    public function getAvailableUser()
    {
        //获得最近喜欢我的用户
        $recentLikeMeUser = UserRelationSingle::getLikeMeUserIds($this->_userid, 100, Utility::getBeforeNDayTimestamp(7));

        //获得最近喜欢我的用户的 RECALL缓存标记
        foreach ($recentLikeMeUser as $userid) {
            $batchKey[] = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
        }

        //获得最近喜欢我的且已经推送过的用户
        $cacheData = [];
        if (!empty($batchKey)) {
            $cacheData = Redis::connection('cache')->mget($batchKey);
        }

        //计算最近喜欢我的用户 和 已经推送过的用户 的差集:就是需要推送的用户
        $this->_availableUser = array_diff($recentLikeMeUser, $cacheData);
        return $this->_availableUser;
    }

    //更新已经推送的用户
    public function updateRecalledUser()
    {
        //批量更新差集用户
        $recalledUser = [];
        foreach ($this->_availableUser as $userid) {
            $cacheKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
            $recalledUser[$cacheKey] = $userid;
        }
        //批量更新 设置过期时间
        self::myPut($recalledUser, self::TYPE_TTL_HOUSE_LIKE_RECALL);
    }

    //批量设置过期时间
    public static function myPut(array $data, $ttl = 0)
    {
        if (empty($data)) {
            return false;
        }

        $pipeline = Redis::connection('cache')
            ->multi(\Redis::PIPELINE);
        foreach ($data as $key => $value) {
            if (empty($value)) {
                continue;
            }
            if ($ttl == 0) {
                $pipeline->set(trim($key), $value);
            } else {
                $pipeline->set(trim($key), $value, $ttl);
            }
        }
        $pipeline->exec();
    }
}

Инструменты вызова

public function handle()
{
    $userid = $this->_userid;
    $houseLikeRecallUser = new HouseLikeRecallUser($userid);
    if ($houseLikeRecallUser->canRecall()) {
        $recallUserIds = $houseLikeRecallUser->getAvailableUser();
        $houseLikeRecallUser->setRecalled();
        $houseLikeRecallUser->updateRecalledUser();
        //群发推送消息
        .
        .
        .
    }
}

Суммировать

Разные уровни данных требуют разных методов обработки, сокращение количества сетевых запросов и рациональное использование кешей — единственный способ оптимизировать производительность.

думать дальше

Если 10 000 человек, которые мне нравятся, выходят в сеть одновременно (параллелизм второго уровня), а я получаю только одно push-сообщение, чтобы меня не засыпали уведомлениями, как я могу решить такую ​​проблему параллелизма?

Есть ли у вас какие-либо решения, вы можете обсудить их в комментариях~

Связанная рекомендация по чтению

Оптимизация производительности Отражение: не работайте в БД цикла for

Отражение оптимизации производительности: не используйте расширенную версию БД в цикле for

Наконец

👍🏻: Если вы чувствуете, что что-то приобрели, пожалуйста, поставьте лайк, чтобы поощрить это!

🌟: Любимые статьи для удобного просмотра!

💬: Обменивайтесь комментариями и развивайтесь друг с другом!