Зачем боту база данных
Разбираемся, почему наш «Цыплёнок-помощник» забывает всё на свете после перезапуска и как научить его помнить тебя месяцами.
База данных — это файл (или отдельная программа), куда бот складывает информацию так, чтобы она пережила перезапуск, выключение сервера и обновление кода.
Хук: бот, у которого амнезия
Представь: ты весь вечер настраивал «Цыплёнка-помощника». Написал ему своё имя, выбрал город для погоды, накидал список напоминаний про домашку на неделю вперёд. Бот радостно всё подтвердил: «Записал, не переживай!» Ты довольный закрыл ноутбук.
Утром ты перезапустил бота, чтобы добавить новую команду. Пишешь ему «привет», а он отвечает: «Здравствуй! Как тебя зовут?» — будто видит тебя впервые. Город забыл. Напоминания испарились. Весь вчерашний вечер — как и не было.
Знакомое чувство? Это как если бы любимая игра сбрасывала прогресс каждый раз, когда ты выходишь в главное меню. Или как если бы друг в переписке забывал всё, что ты ему рассказал, стоило тебе закрыть чат. Бесит, правда?
В прошлом уроке (Хранение состояния) мы научили бота помнить, на каком шаге диалога находится пользователь — через FSM. Но это память «на пару минут». Сегодня разберёмся, почему она исчезает, чем временные данные отличаются от постоянных и что вообще такое база данных, к которой мы будем идти весь этот раздел. К концу урока ты будешь понимать, какие данные бот обязан хранить надолго и почему обычная переменная для этого не годится.
Откуда у бота амнезия
Чтобы понять проблему, надо вспомнить, где живут переменные в твоей программе. Когда ты пишешь:
users = {}
users[12345] = "Цыплёнок"
print(users)Вывод:
{12345: 'Цыплёнок'}Этот словарь users хранится в оперативной памяти (её ещё называют RAM). Оперативная память — это как доска для заметок маркером: пока программа работает, на доске всё видно. Но как только программа останавливается, доску стирают начисто. Перезапустил бота — доска снова чистая, словарь снова пустой.
А бот останавливается чаще, чем кажется:
- ты сам нажал «стоп», чтобы поправить код;
- сервер перезагрузился после обновления;
- программа упала из-за ошибки;
- закончился интернет или Telegram временно недоступен.
Каждый такой случай стирает доску. И всё, что бот держал «в голове» — в переменных, словарях, списках — пропадает безвозвратно.
И вот что коварно: пока ты тестируешь бота у себя на компьютере, проблему почти не видно. Ты запускаешь его, играешься десять минут, всё работает — данные-то лежат в памяти и никуда не делись, ведь программа всё это время не останавливалась. Но настоящий бот живёт на сервере неделями. Сервер обновляют, перезагружают, иногда он падает сам. И каждый такой момент — это потеря всего, что бот не успел сохранить на диск. Чем дольше работает бот и чем больше у него пользователей, тем больнее бьёт амнезия.
Метафора: записная книжка против головы
Представь человека, который держит все номера телефонов в голове. Пока он бодрствует и помнит — всё хорошо. Но стоит ему заснуть (а сон для программы — это перезапуск), и наутро он не помнит ни одного номера. Чтобы такого не было, люди придумали записную книжку: записал номер на бумаге — и он останется там завтра, через неделю, через год, даже если ты сто раз поспишь.
База данных — это и есть записная книжка для бота. Информация в ней лежит на диске (в файле), а не в оперативной памяти. А диск, в отличие от «доски с маркером», не стирается при выключении. Перезапустил бота — он открывает свою записную книжку и читает оттуда всё, что записал вчера.
А чем база лучше обычного файла?
Тут у тебя может возникнуть честный вопрос: «Если всё дело в том, чтобы писать на диск, я же могу просто складывать данные в обычный текстовый файл или в JSON. Зачем какая-то отдельная база?» Вопрос отличный, и ответ показывает, ради чего базы вообще придумали.
Представь, что твоя записная книжка — это не аккуратная книжка с алфавитом по краю, а одна длиннющая лента бумаги, куда ты записываешь подряд всё что попало. Чтобы найти номер одного друга, тебе придётся прочитать всю ленту от начала до конца. Пока друзей пятеро — терпимо. Когда их тысяча — это мучение. А если ты захочешь поменять одну цифру в середине ленты, придётся переписывать всё заново.
Обычный файл ведёт себя именно так. База данных — это «умная» записная книжка: она хранит данные в виде таблиц (строки и столбцы, как в Excel), умеет молниеносно находить нужную запись по ключу, менять одну строку, не трогая остальные, и аккуратно работать, даже когда к ней одновременно обращаются несколько пользователей бота. Всё это база берёт на себя — тебе остаётся только сказать ей, чего ты хочешь.
Временное состояние против постоянных данных
Тут важно не запутаться. Не всё подряд нужно хранить вечно. Есть данные «на минутку» и данные «навсегда», и для них нужны разные инструменты.
Временное состояние — это FSM
Помнишь анкету из прошлого урока? Бот спрашивает имя, потом возраст, потом город. Пока пользователь заполняет анкету, бот держит в FSM-хранилище промежуточные ответы и текущий шаг. Но как только анкета заполнена — это состояние больше не нужно, его можно стирать. Если бот перезапустится посреди анкеты, ничего страшного: пользователь просто начнёт её заново.
Временное состояние — это как черновик контрольной на отдельном листочке: ты решаешь задачу, черкаешь, а потом переписываешь ответ в чистовик и черновик выбрасываешь.
Постоянные данные — это база
А вот чистовик — то, что бот должен помнить долго — это уже постоянные данные. Сравни:
| Временное (FSM, память) | Постоянное (база данных) |
| На каком шаге анкеты пользователь | Готовое имя пользователя |
| Какую кнопку нажали 5 секунд назад | Выбранный город для погоды |
| Незаконченный ввод напоминания | Список всех сохранённых напоминаний |
| Можно потерять без последствий | Потеря = бот всё забыл и расстроил человека |
Простое правило: если потеря данных огорчит пользователя — это постоянные данные, им место в базе. Если данные нужны только «прямо сейчас, в этом диалоге» — это временное состояние, ему хватит памяти или FSM.
Кстати, временное и постоянное часто работают в паре. Возьми ту же анкету: пока человек её заполняет, бот держит ответы во временном FSM-хранилище (черновик). А как только анкета дозаполнена и пользователь нажал «Готово» — бот переписывает финальные данные из FSM в базу (чистовик) и очищает временное состояние. Так что FSM из прошлого урока никуда не девается: он по-прежнему ведёт пользователя по шагам, просто в конце пути результат отправляется на долгое хранение.
Что именно боту хранить надолго
Давай на примере нашего «Цыплёнка-помощника» разберём, что попадёт в базу данных по мере роста бота.
Пример 1. Пользователи
Как только в чат приходит новый человек, бот хочет его запомнить: кто он, как его зовут, когда впервые написал. Без базы каждый перезапуск превращает старых друзей в незнакомцев.
@dp.message(CommandStart())
async def cmd_start(message: Message):
user_id = message.from_user.id
name = message.from_user.first_name
# сейчас мы просто отвечаем, но НЕ запоминаем человека
await message.answer(f"Привет, {name}! Будем знакомы.")Результат: в чате бот ответит «Привет, Аня! Будем знакомы.» — но если Аня напишет завтра после перезапуска, он снова поздоровается как с новенькой, потому что нигде не сохранил, что они уже знакомы. Чтобы это исправить, нам и понадобится база: при /start бот будет записывать user_id и имя в таблицу пользователей.
Пример 2. Настройки
Цыплёнок умеет показывать погоду — но для какого города? Это личная настройка каждого пользователя, и она должна жить между сессиями.
user_city = {} # словарь в памяти — плохая идея для настроек!
@dp.message(Command("city"))
async def set_city(message: Message):
city = message.text.replace("/city", "").strip()
user_city[message.from_user.id] = city
await message.answer(f"Запомнил город: {city}")Результат: в чате бот ответит «Запомнил город: Казань». Но слово «запомнил» здесь — обман: словарь user_city живёт в оперативной памяти, и после перезапуска он снова пустой. Пользователю придётся заново вводить город каждый раз, когда ты обновляешь бота. В базе данных эта настройка хранилась бы надёжно.
Пример 3. История и напоминания
Бот-напоминалка про домашку обязан помнить список дел, даже если его выключили на ночь. Список напоминаний — это классические постоянные данные: их много, они нужны долго, и терять их недопустимо.
reminders = [] # пропадёт при перезапуске
@dp.message(Command("add"))
async def add_reminder(message: Message):
text = message.text.replace("/add", "").strip()
reminders.append({"user": message.from_user.id, "text": text})
await message.answer(f"Добавил напоминание: {text}")Результат: в чате бот ответит «Добавил напоминание: сдать реферат по физике». Звучит надёжно, но список reminders — всё та же доска с маркером. Уснул сервер — и реферат «забыт». Именно ради таких данных мы и идём к базе.
Маленький разбор: как мы будем «искать» данные в базе
База данных хороша не только тем, что не теряет данные. Она ещё умеет быстро находить нужное среди тысяч записей. Чтобы прочувствовать идею поиска по ключу, посмотри на чистый Python без всякого бота:
users = {
111: {"name": "Аня", "city": "Казань"},
222: {"name": "Игорь", "city": "Пермь"},
}
def find_city(user_id):
user = users.get(user_id)
if user is None:
return "Пользователь не найден"
return user["city"]
print(find_city(222))
print(find_city(999))Вывод:
Пермь Пользователь не найден
База данных делает примерно то же самое — находит запись по ключу (например, по user_id), — только хранит данные на диске и справляется с миллионами записей, не теряя их при перезапуске. А обращаться к ней мы будем не через словарь, а через небольшие команды на специальном языке SQL. Вот как, забегая вперёд, выглядит «достань город пользователя 222»:
SELECT city FROM users WHERE user_id = 222;Результат: база вернёт одну строку со значением Пермь. Не пугайся незнакомого синтаксиса — в следующих уроках мы разберём SQL по косточкам. Сейчас важно лишь увидеть: данные лежат в таблице (как в Excel: строки и столбцы), и к ним можно обращаться запросами.
Частые ошибки и подводные камни
Вот на чём спотыкаются почти все новички, когда впервые сталкиваются с хранением данных.
- «Я же написал — запомнил!» Сообщение «запомнил» в чате не значит, что данные сохранены навсегда. Если данные легли в обычную переменную или словарь — это память, и она исчезнет. Слово в ответе бота и реальное сохранение на диск — разные вещи.
- Путают FSM-storage с базой. FSM-хранилище создано для временного состояния диалога. Складывать туда имена, города и историю на годы — неправильно: при
MemoryStorageвсё так же теряется при перезапуске, а само хранилище не предназначено для долгого поиска по данным. - Думают, что глобальный словарь «и так работает». На твоём компьютере во время теста словарь действительно держит данные — пока ты не остановил программу. Иллюзия надёжности рушится ровно в момент первого перезапуска на сервере. А сервер перезагружают регулярно.
- Хранят секреты вместе с данными. Токен и другие переменные окружения — это не «данные пользователей», их нельзя класть в базу и тем более в код. База — для информации о пользователях и их вещах, а секреты живут отдельно (об этом был отдельный разговор про токен).
- Боятся, что база — это «что-то огромное и серверное». На старте база данных — это просто один файл рядом с
bot.py(мы будем использоватьSQLite). Никакого отдельного сервера поднимать не нужно. Это не страшнее, чем создать.txt-файл.
Мини-практика: разложи данные по полочкам
Закрепим понимание без единой строчки кода — на бумаге или в заметках. Возьми нашего «Цыплёнка-помощника» и список того, что он мог бы хранить. Реши для каждого пункта: это временное состояние (память/FSM) или постоянные данные (база)?
- Текущий шаг анкеты «как тебя зовут → сколько лет».
- Дата рождения пользователя, чтобы поздравлять каждый год.
- Какую inline-кнопку человек нажал секунду назад.
- Список друзей из игрового клана, которых бот оповещает о рейде.
- Черновик ещё не отправленного сообщения-рассылки.
- Счётчик «сколько раз пользователь вызвал /weather» за всё время.
Подсказка-критерий: спроси себя «если бот перезапустится прямо сейчас — пользователь расстроится, что это пропало?» Если да — данные постоянные, им место в базе. Ответы: 1 — временное, 2 — постоянное, 3 — временное, 4 — постоянное, 5 — временное, 6 — постоянное. Если хотя бы один пункт ты сначала отнёс не туда — это нормально, именно ради этого навыка и был урок.
Бонус-задание: выпиши, какие три таблицы ты бы завёл для Цыплёнка прямо сейчас. Например: users (кто с ботом общается), settings (город, язык), reminders (список дел). Это и будет черновик схемы нашей будущей базы — в следующих уроках мы её создадим по-настоящему.
Итоги и что дальше
Давай соберём главное:
- Переменные и словари живут в оперативной памяти и стираются при каждом перезапуске бота — это «доска с маркером».
- База данных хранит информацию в файле на диске, поэтому она переживает перезапуски — это «записная книжка».
- Временное состояние (шаг диалога, незаконченный ввод) можно терять без последствий — для него хватает памяти и FSM.
- Постоянные данные (пользователи, настройки, история, напоминания) терять нельзя — им место в базе.
- Главный критерий: огорчит ли пользователя потеря этих данных. Огорчит — значит, в базу.
- На старте база — это всего лишь один файл
SQLiteрядом сbot.py, без отдельных серверов.
Теперь у тебя есть карта: ты понимаешь, что и зачем боту хранить. В следующем уроке мы перестанем теоретизировать и наконец создадим эту самую базу: подключим SQLite, заведём первую таблицу users и заставим Цыплёнка по-настоящему запоминать каждого, кто нажал /start. Записная книжка бота вот-вот откроется на первой странице — увидимся там!