⚠️ Данный репозиторий предназначен исключительно для учебных целей.
Он демонстрирует влияние индексации полей в таблицах на скорость позучения результатов запросов.
Вначале мы можем видеть сравнение выборки из неоптимизированной таблицы и работу Redis-кеша с этой таблицей:
И во втором случае мы видим значительный прирост в производительности выборки из таблицы и влияние на скорость работы кеша (данные в кеш загружаются из БД):
В этом проекте мы имитируем таблицу объёмом ~3 миллиона записей. В примере это информация о "лайках" разных пользователей. Предполагается, что для получения информации о "постах", мы получаем список из 20 постов и должны найти записи о том лайкали их или нет. Для тестирования выполняем 250 таких запросов для 10 пользователей (словно 10 пользователей прокручивают одинаковую ленту за одну загрузку 20 эелементов загружается).
Делается прямая выборка через 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 части логики.
- Первая это проверка наличия
кеша пользователя
- если его нет, то его созданем (sadd)
- устанавливаем таймер (expire)
- Поиск в кеше множества
- запись
искомого множества
в кеш (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']);
});