Чому я не використовую реляційні СУБД
«Rather than making my app easy to deploy, i'll just do a bunch of gnarly shit in Docker»Some CEO
Якщо ви стартап і початківець бізнес, то одна з перших завдань, яке буде стояти перед вами, — викотити на вчора хоч що-небудь. Так як ~90% стартапів вмирають в перший же рік (цифра 90% різна в залежності від типу дослідження і джерела), ця вимога є досить розумним і очевидним. Потрібно якомога швидше перевірити гіпотези, зрозуміти, чи потрібен ваш продукт хоч кому-небудь і спробувати підписати перших клієнтів вже хоча б на стадії MVP, загалом продати хоч що-небудь. В умовах постійного поспіху і постійно мінливих вимог немає ніякого сенсу проектувати круте і гнучке ядро. Ця інвестиція ніколи не окупиться, крім випадку, коли це ядро є самим продуктом.
В аутсорс світі розробка нового проекту зазвичай починається так: знаходиться клієнт з грошима, він дає вимоги, бізнес-аналітики перетравлюють вимоги і віддають технічну специфікацію кінцевої команді розробників, роботу яких потім перевіряє команда тестерів і деплоем якої займається команда девопсов. Це відмінно працює. Особливо, коли у клієнта є ресурси на все це.
У світі стартапів і початківця бізнесу грошей немає. Є тільки трохи людського ресурсу. Спека? Пффф... Це недозволена розкіш. Тестери? Пфф... Девопсы? Ну ви зрозуміли.
Все доводиться робити самому. І бази даних тут суцільна перешкода. І відразу з кількох причин.
СУБД повільні
Впевнений, що кожному досвідченому розробнику доводилося тюнить який-небудь запит або саму базу. Перемикати движки з InnoDB на MyISAM, змінювати розмір кеша під конкретне залізо, дивитися на план виконання запитів, думати про правильних індексах і шукати компроміс між нормалізацією, дубликацией даних і швидкістю.
Вже давно не секрет: сучасна СУБД 90% часу витрачає на обслуговування своїх підсистем — логування, блокування, менеджмент пам'яті, планувальник запитів, парсер, лог транзакцій і т. д. І лише 10% власне на виконання потрібного клієнту запиту. То є величезна кількість ресурсів витрачається марно. Просто тому, що для більшості додатків з минулого це було необхідним функціоналом.
Кожен раз, коли ви робите:
userDao.save(user); //insert into users (id, name) values (?, ?);
Я роблю:
try (BufferedWriter writer = Files.newBufferedWriter(fileTo, UTF_8)) { writer.write(user.toJson()); }
Я зберігаю всі дані у файловій системі, як простий json файл. Це дуже швидко, просто, і це відмінно працює.
Запити — це дорого
Якщо вирішити питання з повільними СУБД ще можливо переходом на заточені під конкретну задачу рішення, то з виконанням самого запиту вже важче. Навіть якщо у вас є пул з'єднань, і ви не витрачаєте ресурси на їх відкриття.
Тільки уявіть собі довжину шляху всього запиту в типовому java додатку: request ? ORM ? jdbc driver ? network ? DB, і у зворотний бік.
Є лише два часткових виходу з ситуації: не виконувати запити до БД на кожен реквест від користувача, наприклад, за допомогою такого додатка, або робити батчинг, щоб виключити оверхед на постійні раундтрипы по мережі.
Кожен раз, коли ви робите:
user.setName("'Peter'"); userDao.update(user); //update users set name = 'Peter' where id = ?;
Я роблю:
user.name = "'Peter'";
Я не зберігаю кожна зміна моделі на диск в момент, коли модель змінюється. Замість цього я зберігаю змінені об'єкти на диск разів у 1 хвилину в окремому потоці. Всі апдейти я роблю виключно батчами, бо поодинокі запити — дорогі. Я зберігаю дані безпосередньо на диск, тому що не хочу передавати дані по мережевому інтерфейсу кожен раз, коли змінилася модель. Це дорого, повільно, і потрібно хендлить помилки з'єднання.
RAMа так багато, що диск потрібен лише меншості
Люди інертні, але світ змінюється дуже швидко. І він вже змінився. Змиріться з цим. У той час, коли Amazon запускає сервера з 2 ГБ RAM, а середній java сервер — це 32 ГБ RAM, люди додають в проекти СУБД просто, щоб зберегти там 1-2 ГБ даних.
Ви можете зберігати все в пам'яті. Для багатьох додатків цього буде більш ніж достатньо. Як мінімум, цього буде достатньо для ~90% стартапів, які помруть, так і не ставши зіркою єдинорогом. Прикрутити базу ви завжди встигнете — коли потрапите в ті 10%.
Кожен раз, коли ви робите:
User user = userDao.get(name); //select * from users where name = ?;
Я роблю:
Map<String User> users ...; User user = users.get(name);
Всі дані я зберігаю в пам'яті. Звичайно, даних може бути дуже багато, і всі не вмістити. Наприклад, в моєму випадку в пам'яті зберігається все, крім даних репортингу — так як їх дійсно багато. Їх дешевше писати/читати прямо з диска. 100К активних користувачів — рівно стільки я можу обслуговувати з 1 ГБ RAM на виртуалке за 10.е.
Схеми, схеми, схеми
Щоб почати роботу з СУБД, мені потрібна схема. Без схеми я нічого не можу зробити. Я не сідаю писати бізнес-логіку — я сідаю і описую мапінг, залежність між класами і на основі мапинга генера схему (ну хоч на цьому спасибі ORM). Але бізнес не цікавить схема. Йому потрібно зробити на вчора. Я міг би спроектувати круту нормалізовану схему як на малюнку нижче. Але навіщо?
Схему потрібно постійно підтримувати. Коли у вас немає продакшену — це легко. Але як тільки у вас з'явився перший клієнт, ви в глухому куті. Luiquibase, flyway — класні тули. Але ви їх використовуєте, коли ви вже в глухому куті. Ви не можете зробити зміни в моделі, нічого не поламавши. І ще вам потрібні ролі, користувачі, розмежування прав доступу, різні бази даних. А бізнесу потрібно на вчора.
Коли ви пишете:
@Entity @Table(name = "Users") public class User { private Long id; @Id @GeneratedValue(generator="increment") @GenericGenerator(name="increment", strategy = "increment") public Long getId() { return id; } }
Я пишу
public class User { public long id; }
Немає БД? Це несерйозно
«Це несерйозно» — фраза, яку я постійно чую від досвідчених сениоров. І це забавно, адже в більшості випадків сеніор навіть і близько не підозрює про потреби бізнесу.
Несерйозно два роки робити продукт, який так і не потрапить у продакшн. Несерйозно робити системи, які помирають при навантаженні 10 річок-с. Несерйозно будувати системи, де половину можна просто викинути, бо фабрики фабрик, інтерфейс від інтерфейсу, і вся ця універсальна гнучкість нікому не потрібна.
Якщо бізнес запитує: «Яку БД ви використовуєте?» — ви спілкуєтеся не з бізнесом. Бізнесу це не цікаво. Клієнта цікавить лише те, чи вирішує система його проблему. І якщо вирішує, то за якою ціною. Інше нікого не цікавить. Ні один наш бізнес-клієнт не запитав: «Яка у Вас БД?» І це при тому, що всі наші клієнти — технічні компанії!
Коли Ви робите:
create table users;
Я пишу бізнес-логіку.
Бази даних потрібно підтримувати
У аутсорсе було добре. Мій код — моя відповідальність. Все що далі — не моя справа. Мені легко було додавати postgres, redis і sharding для них. Сьогодні, коли я все роблю сам, я розумію, що у кожного додаткового модуля системи є ціна деплоя та вартість підтримки. Я не буду додавати БД в проект, поки що без неї можу вирішити бізнес-завдання мінімальною ціною.
Додати БД в проект легко. Але потім її потрібно розгорнути локально, розгорнути в середовищі, розгорнути на стейджинге, розгорнути на продакшені. А це все робота.
БД потрібно моніторити. Вона може впасти, там може закінчитися диск, що виконання запиту може «подвиснуть», процес може з'їсти 100% CPU, БД може почати гальмувати, delete може не очищати диск і потрібно виконувати vacuum, дані можуть бути зіпсовані і т. д.
Якщо ви продаєте не тільки хмара як сервіс, але і послугу розгортання приватних серверів, як це робимо ми, то кожна додаткова інструкція збільшує вартість і час розгортання. Так, є докер, але проект теж не стоїть на місці. Потрібно постійно оновлювати, підтримувати і стежити за контейнерами.
Поки ви робите:
https://github.com/docker-library/postgres/blob/e4942cb0f79b61024963dc0ac196375b26fa60dd/9.6/Dockerfile
Я роблю:
java -jar server.jar
Про проект. Наш проект Blynk — IoT платформа з мобільними додатками. 30К MAU. Поточна навантаження на систему — 4500 річок-с. Майже 3000 девайсів постійно в мережі. Всього періодично підключається близько 10К девайсів. Аптайм 99,99% за півтора року. Вся система обходиться в 120$ (50$ з них Geo DNS) в місяць. Запас міцності — 10х на поточному залозі; + 10х можливість вертикального зростання. Проект опенсорс, глянути можна тут .
P. S. Я не закликаю вас відмовлятися від БД. Є клас задач, де без БД ніяк, і вони дійсно спрощують життя. Але не варто бездумно ліпити БД в проект лише тому, що вам потрібно зберегти дані на диск.
P. P. S. Стаття пролежала в чернетках півроку. Зараз ми вже використовуємо Postgres (backup систему) і Redis для лоад балансинга (не бізнес-логіка). Все це входить у 120$. Тим не менш, система успішно проіснувала без баз даних 2,5 року.
Опубліковано: 24/11/16 @ 08:00
Розділ Різне
Рекомендуємо:
3 типу посилань, які дають максимальний SEO ефект
DOU Ревізор в Lucky Labs: «Триповерхова лабораторія ігор» + ВІДЕО
Як я переїхав працювати до Франції
Ринок праці 2016: 100 тис. програмістів, бум ІТ-шкіл, надлишок PM
Front-Еnd дайджест #19: Yarn, Node v7 і Fiber