HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>При работе над бэкэндом сколько-нибудь крупного проекта перед разработчиком рано или поздно встает вопрос выбора хранилища. На определенном этапе РСУБД перестает удовлетворять потребности проекта:</p>
1 <p>При работе над бэкэндом сколько-нибудь крупного проекта перед разработчиком рано или поздно встает вопрос выбора хранилища. На определенном этапе РСУБД перестает удовлетворять потребности проекта:</p>
2 <ol><li>Требование на быструю запись/чтение. При высоких нагрузках скорость ответа от базы растет. РСУБД можно ускорять разными путями: оптимизировать настройки, профилировать и улучшать запросы, шардировать и реплицировать. Однако эти оптимизации все равно не позволят стабильно отвечать на запросы за несколько мс.</li>
2 <ol><li>Требование на быструю запись/чтение. При высоких нагрузках скорость ответа от базы растет. РСУБД можно ускорять разными путями: оптимизировать настройки, профилировать и улучшать запросы, шардировать и реплицировать. Однако эти оптимизации все равно не позволят стабильно отвечать на запросы за несколько мс.</li>
3 <li>Строгая структура таблиц. Сегодня в любом бизнесе важна скорость доставки ценности до конечного пользователя. Вследствие этого на проектах происходят частые изменения, которые затрагивают схемы таблиц. Сперва может показаться, что это не проблема, но, во-первых, составление миграций занимает время, во-вторых, в таблице может храниться слишком много записей для изменений "в бою".</li>
3 <li>Строгая структура таблиц. Сегодня в любом бизнесе важна скорость доставки ценности до конечного пользователя. Вследствие этого на проектах происходят частые изменения, которые затрагивают схемы таблиц. Сперва может показаться, что это не проблема, но, во-первых, составление миграций занимает время, во-вторых, в таблице может храниться слишком много записей для изменений "в бою".</li>
4 </ol><p>В вышеперечисленных случаях можно использовать key-value (далее "KV") хранилища.</p>
4 </ol><p>В вышеперечисленных случаях можно использовать key-value (далее "KV") хранилища.</p>
5 <h2>Redis как key-value хранилище</h2>
5 <h2>Redis как key-value хранилище</h2>
6 <p>Рассмотрим стандартный пример использования Redis как KV: хранение информации о сессиях пользователей. Когда пользователь авторизуется на сайте, генерируется идентификатор сессии и используется как ключ. В значении сохранятся идентификатор пользователя (user_id) и любая нужная персональная информация. Обычно сессия записывается в cookie браузера. После этого backend моментально определяет, от какого пользователя пришел запрос.</p>
6 <p>Рассмотрим стандартный пример использования Redis как KV: хранение информации о сессиях пользователей. Когда пользователь авторизуется на сайте, генерируется идентификатор сессии и используется как ключ. В значении сохранятся идентификатор пользователя (user_id) и любая нужная персональная информация. Обычно сессия записывается в cookie браузера. После этого backend моментально определяет, от какого пользователя пришел запрос.</p>
7 <h3>Запись новой сессии</h3>
7 <h3>Запись новой сессии</h3>
8 <p>Попробуем реализовать пример на практике. Представим, что пользователь с идентификатором<em>120</em>авторизовался, и серверу нужно создать для него сессию. Обычно идентификатор сессии состоит из большой рандомизированной строки. В данном уроке для простоты будет использоваться числовое представление.</p>
8 <p>Попробуем реализовать пример на практике. Представим, что пользователь с идентификатором<em>120</em>авторизовался, и серверу нужно создать для него сессию. Обычно идентификатор сессии состоит из большой рандомизированной строки. В данном уроке для простоты будет использоваться числовое представление.</p>
9 <p>Подключаемся к Redis серверу с помощью redis-cli и записываем значение<em>user_id</em>по ключу сессии:</p>
9 <p>Подключаемся к Redis серверу с помощью redis-cli и записываем значение<em>user_id</em>по ключу сессии:</p>
10 <p>Команда записи в Redis имеет простую конструкцию:</p>
10 <p>Команда записи в Redis имеет простую конструкцию:</p>
11 <p>Сразу стоит обратить внимание на пару важных моментов:</p>
11 <p>Сразу стоит обратить внимание на пару важных моментов:</p>
12 <ol><li>в ключах рекомендуется использовать двоеточие как разделитель слов (хотя и встречаются другие подходы к наименованию). В примере с сессией ключ имеет вид session:{session_id}.</li>
12 <ol><li>в ключах рекомендуется использовать двоеточие как разделитель слов (хотя и встречаются другие подходы к наименованию). В примере с сессией ключ имеет вид session:{session_id}.</li>
13 <li>команда set записывает значение как строку. Одиночное слово пишется без кавычек, но предложение придется писать в одинарных или двойных кавычках set key1 'hello world'.</li>
13 <li>команда set записывает значение как строку. Одиночное слово пишется без кавычек, но предложение придется писать в одинарных или двойных кавычках set key1 'hello world'.</li>
14 </ol><h3>Получение существующей сессии</h3>
14 </ol><h3>Получение существующей сессии</h3>
15 <p>Сессия сформирована и записана в Redis и cookie. Теперь при следующем запросе от клиента сервер увидит сессию и пойдет в Redis, чтобы определить пользователя:</p>
15 <p>Сессия сформирована и записана в Redis и cookie. Теперь при следующем запросе от клиента сервер увидит сессию и пойдет в Redis, чтобы определить пользователя:</p>
16 <p>Redis вернул строку, так как сессия существовала. Но что будет, если клиент пришлет несуществующую сессию?</p>
16 <p>Redis вернул строку, так как сессия существовала. Но что будет, если клиент пришлет несуществующую сессию?</p>
17 <p>Вернется значение (nil), означающее отсутствие ключа.</p>
17 <p>Вернется значение (nil), означающее отсутствие ключа.</p>
18 <p>Если необходимо проверить сессию на существование без получения идентификатора пользователя, то используется соответствующая команда:</p>
18 <p>Если необходимо проверить сессию на существование без получения идентификатора пользователя, то используется соответствующая команда:</p>
19 <p>Если ключ существует, Redis возвращает целое число<em>1</em>. В противном случае вернется<em>0</em>.</p>
19 <p>Если ключ существует, Redis возвращает целое число<em>1</em>. В противном случае вернется<em>0</em>.</p>
20 <h3>Удаление сессии</h3>
20 <h3>Удаление сессии</h3>
21 <p>При выходе из аккаунта сессия пользователя должна удаляться:</p>
21 <p>При выходе из аккаунта сессия пользователя должна удаляться:</p>
22 <p>Ответ Redis идентичен команде exists:</p>
22 <p>Ответ Redis идентичен команде exists:</p>
23 <ul><li><em>1</em>, если ключ существовал, и он был успешно удален</li>
23 <ul><li><em>1</em>, если ключ существовал, и он был успешно удален</li>
24 <li><em>0</em>, если ключа не было, поэтому ничего не было удалено</li>
24 <li><em>0</em>, если ключа не было, поэтому ничего не было удалено</li>
25 </ul><h3>Хранение дополнительной информации</h3>
25 </ul><h3>Хранение дополнительной информации</h3>
26 <p>Допустим, в сессии нужно хранить не только идентификатор (user_id), но и электронную почту пользователя (email). Для этого достаточно в значение положить JSON-объект, содержащий всю необходимую информацию:</p>
26 <p>Допустим, в сессии нужно хранить не только идентификатор (user_id), но и электронную почту пользователя (email). Для этого достаточно в значение положить JSON-объект, содержащий всю необходимую информацию:</p>
27 <p>Этот метод имеет некоторые недостатки:</p>
27 <p>Этот метод имеет некоторые недостатки:</p>
28 <ol><li>логика по конвертированию в JSON выносится на уровень приложения. В следующих уроках мы рассмотрим стандартный тип данных в Redis, который позволяет хранить несколько значений по одному ключу</li>
28 <ol><li>логика по конвертированию в JSON выносится на уровень приложения. В следующих уроках мы рассмотрим стандартный тип данных в Redis, который позволяет хранить несколько значений по одному ключу</li>
29 <li>для получения одного поля<em>user_id</em>нужно достать весь JSON и декодировать его.</li>
29 <li>для получения одного поля<em>user_id</em>нужно достать весь JSON и декодировать его.</li>
30 </ol><p>В Redis существует модуль<a>RedisJSON</a>для решения этой проблемы.</p>
30 </ol><p>В Redis существует модуль<a>RedisJSON</a>для решения этой проблемы.</p>
31 <h2>Резюме</h2>
31 <h2>Резюме</h2>
32 <ul><li>БД в проекте выбираются под конкретные задачи.</li>
32 <ul><li>БД в проекте выбираются под конкретные задачи.</li>
33 <li>в Redis все значения хранятся по ключам. Абстрактно можно представить в виде хэш-таблицы или словаря. Из-за этого любое чтение/запись имеет очень высокую производительность. Например, Redis без труда выполняет запрос за 3мс при нагрузке в 5000+ RPS (запросов в секунду).</li>
33 <li>в Redis все значения хранятся по ключам. Абстрактно можно представить в виде хэш-таблицы или словаря. Из-за этого любое чтение/запись имеет очень высокую производительность. Например, Redis без труда выполняет запрос за 3мс при нагрузке в 5000+ RPS (запросов в секунду).</li>
34 <li>в Redis хранятся неструктурированные значения. Если необходимо добавить новое поле в существующую структуру, то изменения вносятся только на уровне кода.</li>
34 <li>в Redis хранятся неструктурированные значения. Если необходимо добавить новое поле в существующую структуру, то изменения вносятся только на уровне кода.</li>
35 <li>основные команды для работы со значениями: set, get, del, exists.</li>
35 <li>основные команды для работы со значениями: set, get, del, exists.</li>
36 <li>Хранение данных в JSON-формате удобно, но требует дополнительных усилий для обработки и доступа к отдельным полям, что можно упростить с помощью специализированных решений, таких как RedisJSON.</li>
36 <li>Хранение данных в JSON-формате удобно, но требует дополнительных усилий для обработки и доступа к отдельным полям, что можно упростить с помощью специализированных решений, таких как RedisJSON.</li>
37 </ul>
37 </ul>