Skip to content

2177866/cache_optimization_example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Пример работы оптимизации и кеша

⚠️ Данный репозиторий предназначен исключительно для учебных целей.

Он демонстрирует влияние индексации полей в таблицах на скорость позучения результатов запросов.

Демонстрация №1

Вначале мы можем видеть сравнение выборки из неоптимизированной таблицы и работу Redis-кеша с этой таблицей:

Description

Демонстрация №2

И во втором случае мы видим значительный прирост в производительности выборки из таблицы и влияние на скорость работы кеша (данные в кеш загружаются из БД):

Description

Описание

Что именно сравнивается?

В этом проекте мы имитируем таблицу объёмом ~3 миллиона записей. В примере это информация о "лайках" разных пользователей. Предполагается, что для получения информации о "постах", мы получаем список из 20 постов и должны найти записи о том лайкали их или нет. Для тестирования выполняем 250 таких запросов для 10 пользователей (словно 10 пользователей прокручивают одинаковую ленту за одну загрузку 20 эелементов загружается).

Метод 1 - прямая выборка из БД

Делается прямая выборка через Eloquent. В примере мы просто добавляем 2 условия user_id = $1 AND model_id in (...$2)

use \App\Models\Like;

public static function testSqlQuery($user, array $articles) {
    return Like::forUser($user)
        ->whereIn('model_id', $articles)
        ->pluck('model_id')
        ->toArray();
}

Метод 2 - Redis кеш

Тут 2 части логики.

  1. Первая это проверка наличия кеша пользователя
    • если его нет, то его созданем (sadd)
    • устанавливаем таймер (expire)
  2. Поиск в кеше множества
    • запись искомого множества в кеш (sadd)
    • Поиск пересечения искомого множества в кеше пользователя (sinter)
    • удаление временного множества из кеша

Получается что при обработке первого запроса оба этапа обрабатываются и делается запрос к БД, а при повторном вызове обращение к БД не происходит, а происходит только вторая часть логики, что делает его очень быстрым.

Важно определить срок хранения кеша.

use Illuminate\Support\Facades\Redis;

public static function testRedisQuery($user, array $articles) {
    $redisKey = "user:likes:$user";

    if (!Redis::exists($redisKey)) {
        $userLikes = Like::forUser($user)
            ->pluck('model_id')
            ->toArray();
        Redis::sadd($redisKey, ...$userLikes);
        Redis::expire($redisKey, 86400);
    }

    $tempKey = "temp:check:" . Str::uuid()->toString();
    Redis::sadd($tempKey, ...$articles);
    $intersection = Redis::sinter($redisKey, $tempKey);
    Redis::del($tempKey);

    return $intersection;
}

Таблица

В тестах используется простая таблица.

Schema::create('likes', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->uuid('user_id');
    $table->uuid('model_id');
    $table->string('model_type');
    $table->timestamps();
});

для второго теста производится оптимизация таблицы

Schema::table('likes', function (Blueprint $table) {
    $table->index('user_id'); // для выборки `forUser`
    $table->index(['model_id', 'model_type']); //
    $table->unique(['user_id', 'model_id', 'model_type']);
});

About

Project for Demonstrating the Efficiency of Caching

Topics

Resources

Stars

Watchers

Forks

Languages