[{"data":1,"prerenderedAt":1067},["ShallowReactive",2],{"content:\u002F09-redis\u002F01-redis-overview":3},{"title":4,"description":5,"path":6,"body":7},"Redis: быстрые данные рядом с приложением","Redis часто называют \"кешем\", но это слишком узкое описание. Redis - это in-memory data store: сервер, который хранит данные в оперативной памяти и даёт к ним доступ через сетевой протокол. Он умеет работать как кеш, очередь, хранилище с TTL, счётчик, rate limiter, pub\u002Fsub broker, backend для сессий и координатор для некоторых распределённых сценариев.","\u002F09-redis\u002F01-redis-overview",{"type":8,"value":9,"toc":1045},"minimark",[10,14,17,20,31,34,37,42,45,48,68,75,77,81,84,90,93,107,110,128,131,133,137,140,143,149,152,158,161,163,167,170,262,265,271,274,280,283,285,289,292,298,301,327,330,332,336,341,344,350,353,357,360,366,370,380,384,387,391,394,400,402,406,409,412,418,422,425,497,500,504,507,521,524,528,531,559,562,564,568,603,605,609,628,630,634,665,859,1041],[11,12,4],"h1",{"id":13},"redis-быстрые-данные-рядом-с-приложением",[15,16,5],"p",{},[15,18,19],{},"Главная идея Redis: держать горячие данные рядом с приложением и выполнять простые операции очень быстро.",[21,22,28],"pre",{"className":23,"code":25,"language":26,"meta":27},[24],"language-text","HTTP API\n   │\n   ├── PostgreSQL: источник истины, транзакции, долговечность\n   │\n   └── Redis: горячие данные, TTL, счётчики, блокировки, очереди\n","text","",[29,30,25],"code",{"__ignoreMap":27},[15,32,33],{},"Redis не заменяет PostgreSQL или другую основную базу данных в большинстве backend-систем. Он закрывает другой класс задач: низкая задержка, временное состояние, быстрая агрегация простых структур и снижение нагрузки на основное хранилище.",[35,36],"hr",{},[38,39,41],"h2",{"id":40},"что-значит-in-memory","Что значит in-memory",[15,43,44],{},"Redis хранит рабочий набор данных в RAM. Поэтому доступ к данным обычно измеряется микросекундами на стороне сервера и миллисекундами с учётом сети, клиента и сериализации.",[15,46,47],{},"Цена такой скорости:",[49,50,51,55,62,65],"ul",{},[52,53,54],"li",{},"память дороже диска;",[52,56,57,58,61],{},"объём данных ограничен RAM и настройкой ",[29,59,60],{},"maxmemory",";",[52,63,64],{},"при неправильной persistence-настройке можно потерять последние изменения;",[52,66,67],{},"big keys и hot keys быстро превращаются в production-проблемы.",[15,69,70,71,74],{},"Важно: \"Redis быстрый\" не означает \"любой Redis-запрос бесплатный\". Команда с асимптотикой ",[29,72,73],{},"O(N)",", ключ на сотни мегабайт или тысяча маленьких round-trip'ов подряд легко убьют latency.",[35,76],{},[38,78,80],{"id":79},"single-threaded-event-loop","Single-threaded event loop",[15,82,83],{},"Классический Redis исполняет команды в одном основном потоке. Это не значит, что Redis вообще не использует дополнительные потоки: современные версии могут использовать I\u002FO threads для сетевого ввода-вывода и фоновые процессы\u002Fпотоки для persistence. Но логика выполнения команд над структурами данных остаётся последовательной.",[21,85,88],{"className":86,"code":87,"language":26,"meta":27},[24],"client A ─┐\nclient B ─┼── socket I\u002FO ─► event loop ─► execute command ─► response\nclient C ─┘                         │\n                                     └── данные Redis\n",[29,89,87],{"__ignoreMap":27},[15,91,92],{},"Почему это работает:",[49,94,95,98,101,104],{},[52,96,97],{},"операции обычно короткие;",[52,99,100],{},"нет тяжёлой синхронизации между worker'ами;",[52,102,103],{},"структуры данных живут в памяти;",[52,105,106],{},"Redis использует неблокирующий I\u002FO и быстро переключается между клиентами.",[15,108,109],{},"Что из этого следует для разработчика:",[49,111,112,115,125],{},[52,113,114],{},"одна медленная команда задерживает остальные;",[52,116,117,120,121,124],{},[29,118,119],{},"KEYS *",", огромный ",[29,122,123],{},"LRANGE",", большой Lua-скрипт или удаление гигантского ключа могут создать latency spike;",[52,126,127],{},"Redis нужно проектировать так, чтобы каждая команда была маленькой и предсказуемой.",[15,129,130],{},"Redis удобен не потому, что \"однопоточный быстрее многопоточного\", а потому что выбранная модель убирает много накладных расходов и делает поведение команд более понятным.",[35,132],{},[38,134,136],{"id":135},"resp-как-клиенты-говорят-с-redis","RESP: как клиенты говорят с Redis",[15,138,139],{},"Redis использует RESP - Redis Serialization Protocol. Это простой текстово-бинарный протокол: клиент отправляет массив аргументов, сервер возвращает строку, число, bulk string, массив или ошибку.",[15,141,142],{},"Команда:",[21,144,147],{"className":145,"code":146,"language":26,"meta":27},[24],"SET user:42:name \"Pavel\"\n",[29,148,146],{"__ignoreMap":27},[15,150,151],{},"В RESP выглядит примерно так:",[21,153,156],{"className":154,"code":155,"language":26,"meta":27},[24],"*3\n$3\nSET\n$12\nuser:42:name\n$5\nPavel\n",[29,157,155],{"__ignoreMap":27},[15,159,160],{},"Обычно вы не пишете RESP руками. Go-клиент сам сериализует команды и парсит ответы. Но понимание протокола полезно: Redis-команда - это не HTTP-запрос с JSON, а компактный набор аргументов. Поэтому Redis хорошо чувствует себя в сценариях с большим количеством маленьких операций, особенно если использовать pipelining.",[35,162],{},[38,164,166],{"id":165},"структуры-данных","Структуры данных",[15,168,169],{},"Redis - не key-value store только со строками. Ключ указывает на значение определённого типа:",[171,172,173,186],"table",{},[174,175,176],"thead",{},[177,178,179,183],"tr",{},[180,181,182],"th",{},"Тип",[180,184,185],{},"Для чего используется",[187,188,189,198,206,214,222,230,238,246,254],"tbody",{},[177,190,191,195],{},[192,193,194],"td",{},"String",[192,196,197],{},"кеш JSON, счётчики, флаги, токены, простые значения",[177,199,200,203],{},[192,201,202],{},"Hash",[192,204,205],{},"объект с полями: профиль, настройки, агрегат",[177,207,208,211],{},[192,209,210],{},"List",[192,212,213],{},"очередь, лог последних событий, стек",[177,215,216,219],{},[192,217,218],{},"Set",[192,220,221],{},"уникальные элементы: роли, подписки, лайки",[177,223,224,227],{},[192,225,226],{},"Sorted Set",[192,228,229],{},"рейтинг, leaderboard, расписание по score",[177,231,232,235],{},[192,233,234],{},"Stream",[192,236,237],{},"append-only log, consumer groups, обработка событий",[177,239,240,243],{},[192,241,242],{},"Bitmap",[192,244,245],{},"компактные boolean-флаги по индексам",[177,247,248,251],{},[192,249,250],{},"HyperLogLog",[192,252,253],{},"приближённый подсчёт уникальных значений",[177,255,256,259],{},[192,257,258],{},"Geospatial",[192,260,261],{},"координаты и поиск рядом",[15,263,264],{},"Из-за структур данных Redis часто позволяет не забирать объект в приложение, менять его и сохранять обратно. Например, счётчик можно увеличить атомарной командой:",[21,266,269],{"className":267,"code":268,"language":26,"meta":27},[24],"INCR course:42:views\n",[29,270,268],{"__ignoreMap":27},[15,272,273],{},"А участника добавить во множество:",[21,275,278],{"className":276,"code":277,"language":26,"meta":27},[24],"SADD course:42:students user:1001\n",[29,279,277],{"__ignoreMap":27},[15,281,282],{},"Это уменьшает race condition'ы и сетевые round-trip'ы.",[35,284],{},[38,286,288],{"id":287},"latency-где-теряется-время","Latency: где теряется время",[15,290,291],{},"Redis может выполнить команду очень быстро, но конечная задержка в приложении складывается из нескольких частей:",[21,293,296],{"className":294,"code":295,"language":26,"meta":27},[24],"Go handler\n  │\n  ├─ сериализация команды клиентом\n  ├─ ожидание свободного connection из pool\n  ├─ сеть до Redis\n  ├─ очередь команд внутри Redis\n  ├─ выполнение команды\n  ├─ сеть обратно\n  └─ парсинг ответа и десериализация payload\n",[29,297,295],{"__ignoreMap":27},[15,299,300],{},"Типичные причины плохой задержки:",[49,302,303,306,309,312,315,321,324],{},[52,304,305],{},"Redis далеко от приложения по сети;",[52,307,308],{},"слишком маленький connection pool;",[52,310,311],{},"команда работает с большим количеством элементов;",[52,313,314],{},"ключ содержит огромный JSON;",[52,316,317,318,61],{},"нет timeout'ов в ",[29,319,320],{},"context.Context",[52,322,323],{},"приложение делает 20 последовательных команд вместо pipeline или Lua-скрипта;",[52,325,326],{},"включённая persistence даёт периодические паузы из-за диска или fork.",[15,328,329],{},"На практике Redis лучше держать в той же зоне\u002Fкластере, что и backend, измерять p95\u002Fp99 latency, следить за slowlog и не использовать Redis как свалку больших документов.",[35,331],{},[38,333,335],{"id":334},"распространённые-backend-сценарии","Распространённые backend-сценарии",[337,338,340],"h3",{"id":339},"cache-aside","Cache-aside",[15,342,343],{},"Приложение сначала смотрит в Redis. Если данных нет - читает PostgreSQL, кладёт результат в Redis с TTL и возвращает клиенту.",[21,345,348],{"className":346,"code":347,"language":26,"meta":27},[24],"GET \u002Fcourses\u002F42\n   │\n   ├─ Redis GET course:42\n   │     ├─ hit  -> вернуть данные\n   │     └─ miss -> SELECT из PostgreSQL -> SETEX в Redis -> вернуть данные\n",[29,349,347],{"__ignoreMap":27},[15,351,352],{},"Это снижает нагрузку на базу, но требует аккуратно работать с TTL, инвалидацией и cache stampede.",[337,354,356],{"id":355},"sessions-и-auth-state","Sessions и auth state",[15,358,359],{},"Redis часто используют для серверных сессий, refresh token blacklist, одноразовых кодов и временных auth-состояний. TTL здесь естественно ложится на доменную модель:",[21,361,364],{"className":362,"code":363,"language":26,"meta":27},[24],"SETEX otp:login:user:42 300 \"123456\"\n",[29,365,363],{"__ignoreMap":27},[337,367,369],{"id":368},"counters-и-rate-limits","Counters и rate limits",[15,371,372,375,376,379],{},[29,373,374],{},"INCR",", ",[29,377,378],{},"EXPIRE",", sorted sets и Lua-скрипты позволяют делать счётчики просмотров, лимиты запросов, anti-spam и token bucket.",[337,381,383],{"id":382},"очереди-и-события","Очереди и события",[15,385,386],{},"Для простых задач можно использовать Lists, для более надёжной обработки - Streams с consumer groups. Но Redis не равен Kafka: retention, replay, масштабирование и delivery semantics у них разные.",[337,388,390],{"id":389},"leaderboards-и-ранжирование","Leaderboards и ранжирование",[15,392,393],{},"Sorted Set идеально подходит для рейтингов:",[21,395,398],{"className":396,"code":397,"language":26,"meta":27},[24],"ZADD leaderboard 1840 user:42\nZREVRANGE leaderboard 0 9 WITHSCORES\n",[29,399,397],{"__ignoreMap":27},[35,401],{},[38,403,405],{"id":404},"redis-как-часть-архитектуры","Redis как часть архитектуры",[15,407,408],{},"Redis нужно воспринимать как отдельную зависимость со своими отказами. Он может быть недоступен, медленен, перегружен, очищен eviction-политикой или отставать в репликации.",[15,410,411],{},"Хороший backend не должен превращать кеш в единственную точку истины без осознанного решения. Если Redis используется как кеш, приложение должно уметь пережить miss или временную недоступность. Если Redis используется как очередь или lock service, требования к durability и consistency нужно проговорить заранее.",[21,413,416],{"className":414,"code":415,"language":26,"meta":27},[24],"Можно потерять данные?        Нет -> Redis только с persistence\u002Fрепликацией или другой брокер\nДанные можно пересчитать?     Да  -> Redis cache\u002Fcounter подходит\nНужна строгая транзакционность? Да -> основная БД, Redis как вспомогательный слой\nНужна задержка \u003C 5 ms?        Redis может помочь, если сеть и команды спроектированы правильно\n",[29,417,415],{"__ignoreMap":27},[337,419,421],{"id":420},"политика-деградации","Политика деградации",[15,423,424],{},"Перед добавлением Redis нужно явно ответить на вопрос: что делает сервис, если Redis недоступен или возвращает медленные ответы?",[171,426,427,440],{},[174,428,429],{},[177,430,431,434,437],{},[180,432,433],{},"Сценарий",[180,435,436],{},"Обычно безопасная политика",[180,438,439],{},"Почему",[187,441,442,453,464,475,486],{},[177,443,444,447,450],{},[192,445,446],{},"кеш публичного урока",[192,448,449],{},"fail-open: читать PostgreSQL",[192,451,452],{},"Redis ускоряет, но не владеет истиной",[177,454,455,458,461],{},[192,456,457],{},"кеш результата конверсии",[192,459,460],{},"fail-open или stale-if-error",[192,462,463],{},"зависит от freshness SLA и допустимой старости курса",[177,465,466,469,472],{},[192,467,468],{},"login rate limit",[192,470,471],{},"fail-closed или stricter local fallback",[192,473,474],{},"лучше временно ограничить злоупотребление, чем открыть brute force",[177,476,477,480,483],{},[192,478,479],{},"distributed lock для rebuild cache",[192,481,482],{},"best-effort fallback",[192,484,485],{},"ошибка lock не должна ломать бизнес-инвариант",[177,487,488,491,494],{},[192,489,490],{},"Stream с важным событием",[192,492,493],{},"fail\u002Fretry, не терять молча",[192,495,496],{},"событие уже стало частью workflow",[15,498,499],{},"Эта политика должна быть видна в коде, метриках и документации. Иначе Redis outage превращается в спор во время инцидента.",[337,501,503],{"id":502},"stale-data-policy","Stale data policy",[15,505,506],{},"Для кеша недостаточно выбрать TTL. Нужно решить:",[49,508,509,512,515,518],{},[52,510,511],{},"какая максимальная старость данных допустима для пользователя;",[52,513,514],{},"кто удаляет или обновляет ключ после записи в source of truth;",[52,516,517],{},"можно ли отдавать stale value, если PostgreSQL или Redis временно недоступны;",[52,519,520],{},"как клиент поймёт, что ответ был рассчитан по устаревшим данным, если это важно для домена.",[15,522,523],{},"Например, карточка урока может жить в кеше 10 минут и инвалидироваться при публикации новой версии. Курс валют для RateDesk может иметь отдельный freshness threshold: если курс старше допустимого окна, лучше вернуть доменную ошибку freshness, а не тихо посчитать неверную сумму.",[337,525,527],{"id":526},"минимальная-наблюдаемость-redis","Минимальная наблюдаемость Redis",[15,529,530],{},"Даже для кеша нужны метрики:",[49,532,533,547,550,556],{},[52,534,535,375,538,375,541,375,544,61],{},[29,536,537],{},"cache_hit",[29,539,540],{},"cache_miss",[29,542,543],{},"cache_error",[29,545,546],{},"cache_stale_served",[52,548,549],{},"latency Redis-команд по operation name, без raw key как label;",[52,551,552,555],{},[29,553,554],{},"evicted_keys",", memory usage, connected clients, slowlog;",[52,557,558],{},"pool stats клиента: ожидание соединения, timeouts, retries.",[15,560,561],{},"Не добавляйте user id, полный key, request id или текст ошибки как Prometheus labels: cardinality быстро убьёт мониторинг.",[35,563],{},[38,565,567],{"id":566},"вопросы-на-собеседовании","Вопросы на собеседовании",[49,569,570,573,576,579,582,585,588,594,597,600],{},[52,571,572],{},"Почему Redis часто быстрее дисковой базы данных?",[52,574,575],{},"Что означает single-threaded event loop в Redis?",[52,577,578],{},"Почему одна тяжёлая команда может повлиять на всех клиентов?",[52,580,581],{},"Что такое RESP и зачем понимать, что Redis-команда - это набор аргументов?",[52,583,584],{},"Чем Redis как кеш отличается от Redis как источника истины?",[52,586,587],{},"Какие структуры данных Redis вы использовали бы для счётчика, рейтинга, очереди и сессии?",[52,589,590,591,593],{},"Почему ",[29,592,119],{}," опасна в production?",[52,595,596],{},"Откуда берётся latency между Go-приложением и Redis?",[52,598,599],{},"Что такое fail-open\u002Ffail-closed policy для Redis-зависимости?",[52,601,602],{},"Чем TTL отличается от freshness policy?",[35,604],{},[38,606,608],{"id":607},"практика","Практика",[610,611,612,615,622,625],"ol",{},[52,613,614],{},"Опишите, какие данные учебной платформы можно держать в Redis: профиль пользователя, прогресс уроков, список курсов, счётчики просмотров, сессии. Для каждого пункта решите: Redis - кеш или источник истины.",[52,616,617,618,621],{},"Нарисуйте схему cache-aside для эндпоинта ",[29,619,620],{},"GET \u002Fapi\u002Flessons\u002F{slug}"," и укажите, где возможен cache stampede.",[52,623,624],{},"Подберите структуры данных Redis для задач: leaderboard студентов, одноразовый код входа, множество купленных курсов, последние 100 событий пользователя.",[52,626,627],{},"Для двух Redis-сценариев RateDesk сформулируйте fail-open\u002Ffail-closed и stale data policy без описания конкретных issue проекта.",[35,629],{},[38,631,633],{"id":632},"интерактивная-практика","Интерактивная практика",[635,636,640,643,660],"quiz",{"answer":637,"id":638,"xp":639},"2","redis-overview-q1","10",[15,641,642],{},"Для какого сценария Redis чаще всего подходит лучше всего?",[644,645,646],"template",{"v-slot:options":27},[49,647,648,651,654,657],{},[52,649,650],{},"Как единственный источник истины для финансовых операций",[52,652,653],{},"Как быстрый слой для горячих или временных данных рядом с приложением",[52,655,656],{},"Как замена PostgreSQL для сложных SQL-запросов",[52,658,659],{},"Как публичный storage без авторизации, потому что данные в памяти",[644,661,662],{"v-slot:explanation":27},[15,663,664],{},"Redis силён в горячих данных, TTL, счётчиках, rate limits и временном состоянии. Для durable business facts обычно нужен основной источник истины.",[666,667,671,674,854],"predict",{"answer":668,"id":669,"xp":670},"fail-open\\nfail-closed","redis-overview-p1","15",[15,672,673],{},"Что выведет программа?",[644,675,676],{"v-slot:code":27},[21,677,681],{"className":678,"code":679,"language":680,"meta":27,"style":27},"language-go shiki shiki-themes github-dark","package main\n\nimport \"fmt\"\n\nfunc dependencyPolicy(required bool) string {\n    if required {\n        return \"fail-closed\"\n    }\n    return \"fail-open\"\n}\n\nfunc main() {\n    fmt.Println(dependencyPolicy(false))\n    fmt.Println(dependencyPolicy(true))\n}\n","go",[29,682,683,696,703,719,724,753,762,771,777,786,792,797,808,831,849],{"__ignoreMap":27},[684,685,688,692],"span",{"class":686,"line":687},"line",1,[684,689,691],{"class":690},"snl16","package",[684,693,695],{"class":694},"svObZ"," main\n",[684,697,699],{"class":686,"line":698},2,[684,700,702],{"emptyLinePlaceholder":701},true,"\n",[684,704,706,709,713,716],{"class":686,"line":705},3,[684,707,708],{"class":690},"import",[684,710,712],{"class":711},"sU2Wk"," \"",[684,714,715],{"class":694},"fmt",[684,717,718],{"class":711},"\"\n",[684,720,722],{"class":686,"line":721},4,[684,723,702],{"emptyLinePlaceholder":701},[684,725,727,730,733,737,741,744,747,750],{"class":686,"line":726},5,[684,728,729],{"class":690},"func",[684,731,732],{"class":694}," dependencyPolicy",[684,734,736],{"class":735},"s95oV","(",[684,738,740],{"class":739},"s9osk","required",[684,742,743],{"class":690}," bool",[684,745,746],{"class":735},") ",[684,748,749],{"class":690},"string",[684,751,752],{"class":735}," {\n",[684,754,756,759],{"class":686,"line":755},6,[684,757,758],{"class":690},"    if",[684,760,761],{"class":735}," required {\n",[684,763,765,768],{"class":686,"line":764},7,[684,766,767],{"class":690},"        return",[684,769,770],{"class":711}," \"fail-closed\"\n",[684,772,774],{"class":686,"line":773},8,[684,775,776],{"class":735},"    }\n",[684,778,780,783],{"class":686,"line":779},9,[684,781,782],{"class":690},"    return",[684,784,785],{"class":711}," \"fail-open\"\n",[684,787,789],{"class":686,"line":788},10,[684,790,791],{"class":735},"}\n",[684,793,795],{"class":686,"line":794},11,[684,796,702],{"emptyLinePlaceholder":701},[684,798,800,802,805],{"class":686,"line":799},12,[684,801,729],{"class":690},[684,803,804],{"class":694}," main",[684,806,807],{"class":735},"() {\n",[684,809,811,814,817,819,822,824,828],{"class":686,"line":810},13,[684,812,813],{"class":735},"    fmt.",[684,815,816],{"class":694},"Println",[684,818,736],{"class":735},[684,820,821],{"class":694},"dependencyPolicy",[684,823,736],{"class":735},[684,825,827],{"class":826},"sDLfK","false",[684,829,830],{"class":735},"))\n",[684,832,834,836,838,840,842,844,847],{"class":686,"line":833},14,[684,835,813],{"class":735},[684,837,816],{"class":694},[684,839,736],{"class":735},[684,841,821],{"class":694},[684,843,736],{"class":735},[684,845,846],{"class":826},"true",[684,848,830],{"class":735},[684,850,852],{"class":686,"line":851},15,[684,853,791],{"class":735},[644,855,856],{"v-slot:hint":27},[15,857,858],{},"Кеш чтения обычно может деградировать в основное хранилище, а security-sensitive limiter часто безопаснее закрывать строже.",[860,861,865,883,1028],"code-task",{"expected":862,"id":863,"xp":864},"postgres\\nredis\\npostgres","redis-overview-ct1","20",[15,866,867,868,871,872,875,876,879,880,882],{},"Реализуй ",[29,869,870],{},"StoreFor",": durable business facts должны идти в ",[29,873,874],{},"postgres",", временные данные можно хранить в ",[29,877,878],{},"redis",", а неопределённый сценарий по умолчанию оставь в ",[29,881,874],{},".",[644,884,885],{"v-slot:template":27},[21,886,888],{"className":678,"code":887,"language":680,"meta":27,"style":27},"package main\n\nimport \"fmt\"\n\nfunc StoreFor(durable bool, temporary bool) string {\n    return \"todo\"\n}\n\nfunc main() {\n    fmt.Println(StoreFor(true, false))\n    fmt.Println(StoreFor(false, true))\n    fmt.Println(StoreFor(false, false))\n}\n",[29,889,890,896,900,910,914,941,948,952,956,964,984,1004,1024],{"__ignoreMap":27},[684,891,892,894],{"class":686,"line":687},[684,893,691],{"class":690},[684,895,695],{"class":694},[684,897,898],{"class":686,"line":698},[684,899,702],{"emptyLinePlaceholder":701},[684,901,902,904,906,908],{"class":686,"line":705},[684,903,708],{"class":690},[684,905,712],{"class":711},[684,907,715],{"class":694},[684,909,718],{"class":711},[684,911,912],{"class":686,"line":721},[684,913,702],{"emptyLinePlaceholder":701},[684,915,916,918,921,923,926,928,930,933,935,937,939],{"class":686,"line":726},[684,917,729],{"class":690},[684,919,920],{"class":694}," StoreFor",[684,922,736],{"class":735},[684,924,925],{"class":739},"durable",[684,927,743],{"class":690},[684,929,375],{"class":735},[684,931,932],{"class":739},"temporary",[684,934,743],{"class":690},[684,936,746],{"class":735},[684,938,749],{"class":690},[684,940,752],{"class":735},[684,942,943,945],{"class":686,"line":755},[684,944,782],{"class":690},[684,946,947],{"class":711}," \"todo\"\n",[684,949,950],{"class":686,"line":764},[684,951,791],{"class":735},[684,953,954],{"class":686,"line":773},[684,955,702],{"emptyLinePlaceholder":701},[684,957,958,960,962],{"class":686,"line":779},[684,959,729],{"class":690},[684,961,804],{"class":694},[684,963,807],{"class":735},[684,965,966,968,970,972,974,976,978,980,982],{"class":686,"line":788},[684,967,813],{"class":735},[684,969,816],{"class":694},[684,971,736],{"class":735},[684,973,870],{"class":694},[684,975,736],{"class":735},[684,977,846],{"class":826},[684,979,375],{"class":735},[684,981,827],{"class":826},[684,983,830],{"class":735},[684,985,986,988,990,992,994,996,998,1000,1002],{"class":686,"line":794},[684,987,813],{"class":735},[684,989,816],{"class":694},[684,991,736],{"class":735},[684,993,870],{"class":694},[684,995,736],{"class":735},[684,997,827],{"class":826},[684,999,375],{"class":735},[684,1001,846],{"class":826},[684,1003,830],{"class":735},[684,1005,1006,1008,1010,1012,1014,1016,1018,1020,1022],{"class":686,"line":799},[684,1007,813],{"class":735},[684,1009,816],{"class":694},[684,1011,736],{"class":735},[684,1013,870],{"class":694},[684,1015,736],{"class":735},[684,1017,827],{"class":826},[684,1019,375],{"class":735},[684,1021,827],{"class":826},[684,1023,830],{"class":735},[684,1025,1026],{"class":686,"line":810},[684,1027,791],{"class":735},[644,1029,1030],{"v-slot:hints":27},[49,1031,1032,1035,1038],{},[52,1033,1034],{},"Долговечность важнее скорости.",[52,1036,1037],{},"Redis хорош для временного состояния.",[52,1039,1040],{},"Если непонятно, где source of truth, выбирай более надёжное хранилище.",[1042,1043,1044],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":27,"searchDepth":698,"depth":698,"links":1046},[1047,1048,1049,1050,1051,1052,1059,1064,1065,1066],{"id":40,"depth":698,"text":41},{"id":79,"depth":698,"text":80},{"id":135,"depth":698,"text":136},{"id":165,"depth":698,"text":166},{"id":287,"depth":698,"text":288},{"id":334,"depth":698,"text":335,"children":1053},[1054,1055,1056,1057,1058],{"id":339,"depth":705,"text":340},{"id":355,"depth":705,"text":356},{"id":368,"depth":705,"text":369},{"id":382,"depth":705,"text":383},{"id":389,"depth":705,"text":390},{"id":404,"depth":698,"text":405,"children":1060},[1061,1062,1063],{"id":420,"depth":705,"text":421},{"id":502,"depth":705,"text":503},{"id":526,"depth":705,"text":527},{"id":566,"depth":698,"text":567},{"id":607,"depth":698,"text":608},{"id":632,"depth":698,"text":633},1781022064355]