---
title: 'Глибокий аналіз Redis: однопоточна архітектура, структури даних, AOF/RDB та політики витіснення | DevSense'
description: "Детальний розбір архітектури Redis. Дізнайтеся, чому однопоточний обробник працює швидко, вивчіть внутрішні структури даних, порівняйте AOF та RDB і налаштуйте політики витіснення пам'яті."
faq:
    - { question: 'Чому Redis є однопотоковим і як він забезпечує високу швидкість роботи?', answer: "Redis обробляє команди послідовно в єдиному головному циклі подій (Event Loop), що повністю виключає накладні витрати на перемикання контексту процесора та блокування конкурентного доступу (м'ютекси). Оскільки всі операції виконуються в оперативній пам'яті та займають мікросекунди, послідовне виконання виявляється швидшим за управління паралельними потоками." }
    - { question: 'У чому різниця між механізмами персистентності RDB та AOF у Redis?', answer: 'RDB (Redis Database) створює компактні бінарні знімки даних на диску на певний момент часу, забезпечуючи швидкий запуск сервера, але допускаючи втрату даних між знімками. AOF (Append Only File) записує кожну команду зміни в журнал, забезпечуючи максимальне збереження даних з мінімальними втратами.' }
    - { question: "Яку політику витіснення пам'яті використовувати для виділеного шару кешування?", answer: "Політика `allkeys-lru` є оптимальною для кешу, оскільки вона автоматично видаляє ключі, які найдовше не використовувалися (Least Recently Used), по всій базі даних при досягненні ліміту пам'яті." }
published: '2026-06-29'
---
# Глибокий аналіз Redis: однопоточна архітектура, структури даних, AOF/RDB та політики витіснення

Redis часто сприймають як звичайний сервісний кеш типу key-value. Однак у сучасній високонавантаженій архітектурі Redis виступає повноцінним сховищем структур даних в оперативній пам'яті, первинною базою даних, брокером повідомлень та рушієм потокової обробки.

Щоб ефективно використовувати Redis у продакшені, бекенд-розробник повинен розуміти його внутрішні архітектурні компроміси: чому однопотокова модель перевершує багатопотоковість при роботі з RAM, як структури даних оптимізуються в пам'яті та як механізми персистентності й витіснення захищають систему від збоїв.

**Пов'язані посібники:** [Архітектура від моноліту до мікросервісів](monolith-to-microservices-architecture) · [Моніторинг та Observability в Laravel](observability-monitoring-laravel)

## Зміст

* [Швидкість та архітектура: чому однопотоковий Event Loop швидший?](#speed-architecture)
* [Внутрішні структури даних в оперативній пам'яті](#memory-structures)
* [Механізми персистентності: AOF проти RDB](#persistence-mechanisms)
* [Управління пам'яттю та політики витіснення (Eviction Policies)](#eviction-policies)
* [Підводні камені та блокуючі операції](#pitfalls-blocking)
* [Безпечна пакетна обробка в PHP та Laravel](#php-laravel-integration)
* [⚠️ Типові помилки](#common-mistakes)
* [Чек-лист для продакшену](#checklist)
* [Резюме](#summary)
* [🧠 Питання для самоперевірки](#self-test-quiz)

---

<a id="speed-architecture"></a>
## Швидкість та архітектура: чому однопотоковий Event Loop швидший?

Redis досягає продуктивності рівня субмілісекунд (часто менше 100 мікросекунд на операцію) завдяки двом ключовим архітектурним принципам:
1. **Обслуговування даних у RAM:** Весь робочий набір даних знаходиться безпосередньо в оперативній пам'яті, минаючи затримки введення-виведення (I/O) жорстких дисків.
2. **Однопотокова модель виконання:** Команди виконуються строго послідовно всередині одного головного циклу подій (Event Loop) з використанням мультиплексування введення-виведення (`epoll` у Linux, `kqueue` у macOS/BSD).

### Чому багатопотоковість може уповільнити роботу з оперативною пам'яттю

Поширене оману стверджує, що додавання потоків завжди прискорює систему. У дискових або CPU-bound системах паралельні потоки дійсно підвищують утилізацію заліза. Однак при роботі з пам'яттю операції займають наносекунди та мікросекунди.

Якби Redis використовував кілька робочих потоків для паралельної зміни даних у пам'яті, були б потрібні механізми синхронізації:
- **Блокування та м'ютекси (Mutexes / RWLocks):** Потоки витрачали б значні ресурси процесора на очікування захоплення блокувань для ключів або хеш-таблиць.
- **Перемикання контексту процесора (Context Switching):** Часті перемикання потоків призводять до скидання кешу процесора та накладних витрат на регістри.

Виконуючи команди послідовно, Redis повністю усуває конкуренцію за блокування та перемикання контексту.

> [!NOTE]
> **Сучасна багатопотоковість у Redis:**
> Хоча виконання команд залишається строго однопотоковим, починаючи з версії Redis 6.0+ фонові потоки використовуються для асинхронного введення-виведення (читання мережевих сокетів та запису відповідей) та фонового видалення великих ключів (`UNLINK`).

---

<a id="memory-structures"></a>
## Внутрішні структури даних в оперативній пам'яті

Redis — це не просто сховище рядків; він надає ефективні типи даних, оптимізовані за пам'яттю та алгоритмічною складністю:

| Тип даних Redis | Внутрішня структура в C | Алгоритмічна складність | Сценарій використання |
| :--- | :--- | :--- | :--- |
| **String** | SDS (Simple Dynamic String) | $O(1)$ | Кешування, лічильники, бітові маски |
| **Hash** | ZipList / ListPack / HashTable (`dict`) | $O(1)$ пошук | Збереження об'єктів (користувачі, сесії) |
| **List** | QuickList (зв'язаний список ZipList'ів) | $O(1)$ push/pop | Черги завдань, логи подій |
| **Set** | IntSet / HashTable (`dict`) | $O(1)$ перевірка | Унікальні теги, чорні списки IP |
| **Sorted Set (ZSET)** | SkipList + HashTable | $O(\log N)$ вставка/пошук | Таблиці лідерів, ковзаючі лімітери |

### Оптимізація пам'яті: ZipList та ListPack

Для невеликих колекцій Redis упаковує дані в компактні байтові масиви — **ZipList** або **ListPack**. Ці безперервні блоки пам'яті усувають накладні витрати на вказівники. Як тільки колекція перевищує заданий поріг (наприклад, 512 елементів), Redis автоматично конвертує її в повноцінну хеш-таблицю або SkipList.

---

<a id="persistence-mechanisms"></a>
## Механізми персистентності: AOF проти RDB

Оперативна пам'ять є енергозалежною, тому при перезавантаженні сервера всі дані втрачаються, якщо не налаштована персистентність. Redis пропонує два основні механізми збереження даних на диск.

```
+-----------------------------------------------------------------------+
|                       Оперативна пам'ять (RAM)                        |
+-----------------------------------------------------------------------+
        |                                                 |
   Створення форку                                Дозапис команд
(Copy-On-Write)                                     (fsync everysec)
        v                                                 v
+-----------------------+                         +---------------------+
|  Знімок RDB (.rdb)    |                         |  Журнал AOF (.aof)  |
| Компактний бінарний   |                         | Журнал усіх операцій|
+-----------------------+                         +---------------------+
```

### 1. RDB (Redis Database Snapshots)

RDB створює бінарні знімки всього набору даних на диск з заданою періодичністю.

* **Як працює:** Redis викликає `fork()`, створюючи дочірній процес, який через механізм Copy-On-Write (COW) зберігає дані у компактний файл `.rdb`, поки основний процес продовжує обслуговувати запити.
* **Плюси:** Дуже компактний розмір файлу; максимальна швидкість відновлення при старті.
* **Мінуси:** Втрата даних за проміжок між знімками (наприклад, при збої через 5 хвилин після останнього збереження).

### 2. AOF (Append Only File)

AOF записує кожну команду зміни в журнал дозапису.

* **Як працює:** Команди пишуться в буфер і скидаються на диск згідно з політикою `fsync` (`always`, `everysec`, `no`).
* **Перезапис AOF (`bgrewriteaof`):** При розростанні файлу Redis автоматично переписує AOF у фоні, згортаючи історію в мінімальний набір команд.
* **Плюси:** Максимальна надійність. У режимі `appendfsync everysec` втрачається максимум 1 секунда записів.
* **Мінуси:** Більший розмір файлу та повільніший запуск порівняно з RDB.

> [!TIP]
> **Рекомендація для продакшену: Гібридний режим**
> Включайте RDB та AOF одночасно (`aof-use-rdb-preamble yes`). При старті Redis спочатку завантажує базовий знімок RDB, а потім миттєво програє короткий хвіст AOF, поєднуючи швидкість та надійність.

---

<a id="eviction-policies"></a>
## Управління пам'яттю та політики витіснення (Eviction Policies)

Коли Redis досягає ліміту пам'яті, заданого параметром `maxmemory`, при записі нових даних спрацьовує політика витіснення ключів:

```ini
# redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lru
```

### Розбір 4 основних політик витіснення

1. `noeviction` **(За замовчуванням):** Повертає помилку OOM (Out Of Memory) на будь-які операції запису (`SET`, `HSET`), але дозволяє читання. Використовується, коли Redis працює як БД.
2. `allkeys-lru` **(Рекомендується для кешу):** Видаляє ключі, які найдовше не використовувалися (Least Recently Used), по всій базі. Ідеально для кешування.
3. `allkeys-lfu` **(На основі частоти):** Видаляє рідко використовувані ключі (Least Frequently Used) на основі лічильника звернень. Ефективніше за LRU при нерівномірному навантаженні.
4. `volatile-lru` / `volatile-lfu` **:** Застосовує алгоритми LRU або LFU тільки до ключів, для яких встановлено термін життя (TTL). Ключі без TTL не чіпаються.

---

<a id="pitfalls-blocking"></a>
## Підводні камені та блокуючі операції

Зворотна сторона однопотоковості: будь-яка важка або довга команда блокує виконання всіх інших запитів клієнтів до сервера.

### ⚠️ Небезпечні операції у продакшені

* `KEYS *` **:** Сканує всю базу за один виклик з лінійною складністю $O(N)$. На мільйонах ключів вішає інстанс на секунди. **Замість цього використовуйте `SCAN`.**
* `HGETALL` / `SMEMBERS` / `LRANGE 0 -1` **:** Вивантаження гігантських колекцій за один раз перевантажує мережу та блокує Event Loop. Використовуйте `HSCAN`, `SSCAN`, `ZSCAN`.
* **Важкі Lua-скрипти:** Тривалі цикли всередині Lua-скриптів паралізують обробку запитів.

---

<a id="php-laravel-integration"></a>
## Безпечна пакетна обробка в PHP та Laravel

Замість блокуючих команд наразок `KEYS *`, продакшен-код повинен використовувати ітератори `SCAN`. Нижче наведено безпечний сервіс на PHP 8.5 для поетапного видалення ключів за шаблоном без блокування сервера:

```php
// app/Services/RedisBatchService.php
declare(strict_types=1);

namespace App\Services;

use Illuminate\Support\Facades\Redis;
use RuntimeException;

class RedisBatchService
{
    /**
     * Безпечне видалення ключів за шаблоном з використанням ітератора SCAN.
     *
     * @param string $pattern Приклад: 'users:session:*'
     * @param int $chunkSize Кількість ключів за один крок ітерації
     * @return int Загальна кількість видалених ключів
     */
    public function deleteKeysByPattern(string $pattern, int $chunkSize = 500): int
    {
        $cursor = '0';
        $totalDeleted = 0;

        do {
            // Виконання неблокуючого SCAN
            $result = Redis::scan($cursor, [
                'match' => $pattern,
                'count' => $chunkSize,
            ]);

            if ($result === false || !is_array($result)) {
                throw new RuntimeException("Помилка виконання ітерації Redis SCAN.");
            }

            $cursor = (string) $result[0];
            $keys = (array) $result[1];

            if (!empty($keys)) {
                // Видалення ключів через конвеєр (pipeline)
                $deletedCount = Redis::pipeline(function ($pipe) use ($keys): void {
                    foreach ($keys as $key) {
                        $pipe->del($key);
                    }
                });

                $totalDeleted += array_sum($deletedCount);
            }
        } while ($cursor !== '0');

        return $totalDeleted;
    }
}
```

---

<a id="common-mistakes"></a>
## ⚠️ Типові помилки

**1. Використання `KEYS *` на бойових серверах**
Виклик `KEYS *` для пошуку ключів або очищення просторів імен призводить до падіння продуктивності та таймаутів клієнтів.

**2. Відсутність обмежень `maxmemory`**
Якщо параметр `maxmemory` не заданий у `redis.conf`, при вичерпанні оперативної пам'яті системний процес Linux OOM Killer примусово завершить процес Redis.

**3. Використання Redis як БД без AOF**
Надія тільки на RDB-знімки для критичних даних. При раптовому збої сервера всі дані, записані після останнього знімка, будуть безповоротно втрачені.

---

<a id="checklist"></a>
## Чек-лист для продакшену

1. **Налаштування пам'яті:** Чи заданий параметр `maxmemory` у `redis.conf` та чи обрана політика витіснення (наприклад, `allkeys-lru`)?
2. **Неблокуючі виклики:** Чи замінені команди `KEYS *` та `HGETALL` на ітератори `SCAN` / `HSCAN` у всіх сервісах?
3. **Стратегія персистентності:** Чи включено гібридний режим (`aof-use-rdb-preamble yes`) для важливих даних?
4. **Конвеєризація (Pipelining):** Чи об'єднані масові операції запису в пайплайни для скорочення мережевих затримок?

---

<a id="summary"></a>
## Резюме

Redis забезпечує найвищу швидкість завдяки збереженню даних у RAM та однопотоковій моделі Event Loop, яка усуває затримки на м'ютекси та перемикання контексту. Комбінуючи гібридну персистентність RDB+AOF, правильні політики витіснення та неблокуючі команди `SCAN`, розробники отримують надійну та масштабовану інфраструктуру.

---

<a id="self-test-quiz"></a>
## 🧠 Питання для самоперевірки

### 1. Чому однопотокова модель робить Redis швидшим при роботі з оперативною пам'яттю?
- A) Тому що компілятори C не підтримують багатопотоковість на Linux.
- B) Тому що мікросекундні операції в RAM виконуються швидше послідовно, ніж при витраті ресурсів на м'ютекси та перемикання контексту процесора.
- C) Тому що оперативна пам'ять може читатися тільки одним ядром CPU одночасно.

<details>
<summary><b>Показати відповідь</b></summary>

**Відповідь: B**
Операції в RAM займають мікросекунди. Витрати на синхронізацію потоків (м'ютекси) та перемикання контексту створюють більше затримок, ніж послідовне виконання команд у циклічному Event Loop.
</details>

### 2. Яку команду слід використовувати замість `KEYS *` для пошуку ключів у продакшені?
- A) `FIND`
- B) `SEARCH`
- C) `SCAN`

<details>
<summary><b>Показати відповідь</b></summary>

**Відповідь: C**
`SCAN` — це курсорний неблокуючий ітератор, який повертає ключі невеликими порціями, не блокуючи сервер.
</details>

### 3. Що станеться при заповненні пам'яті Redis до `maxmemory` у режимі `allkeys-lru`?
- A) Redis поверне помилку OOM на всі нові операції запису.
- B) Redis автоматично видалить найбільш давно не використовувані ключі (LRU) по всій базі.
- C) Redis скине дані на диск і завершить роботу.

<details>
<summary><b>Показати відповідь</b></summary>

**Відповідь: B**
Політика `allkeys-lru` автоматично звільняє місце, видаляючи найменш затребувані ключі по всьому простору ключів.
</details>