От стартапа до highload: Эволюция веб-проекта, или Как победить нагрузку Антон Терехов Ведущий инженер - программист отдела фотосервисов ООО "Медиа Мир" (РБК) PhotoFile. Начало. • Проект получен в 2005 году • Проект состоял из 2-х серверов: www + DB, fileserver • Количество записей в БД: – Пользователи: – Альбомы: – Фото: ~80 000 ~290 000 ~6 500 000 • Нагрузка: хиты ~2 000 000 хосты: ~55 000 • Файловый сервер заполнен на 3/4 • Что делать? PhotoFile. Начало. Улучшили, то, что можно улучшить быстро: • Вынос DB на отдельный сервер • Регистрация пользователей на новом fileserver’е • Включение Smarty caching • И, конечно же, начали делать версию 2 Новый движок • Это – фотохостинг. Мы не меняем URL изображения. (Только если пользователь сам не сделает что-то с фото) • • • • • URL вида photofile.ru/photo/userlogin/albumid/photoid.jpg ЧПУ – залог удобства ресурса photofile.ru/users/userlogin/… Все фото пользователя хранятся на 1 сервере, что исключает работы по переносу файлов между машинами При отказе сервера внешние ссылки не изменяются nginx разпределяет запросы к fileserver’ам (regexp) Появляется отдельный proxy-server Реклама – зло! • HTTP 1.1 500 Нагрузка растет • Введение MySQL Master-Slaves • Код не готов - введение отдельного сервера для авторизованных пользователей http://a.photofile.ru/ – – – + Временное неправильное решение Нет отказоустойчивости Разные ссылки для авторизованного и анонимного пользователей Но зато оно работает! Новый движок. Часть 2. • Старые URL фото вида ff01.photofile.ru/photo/056/290/1239173/128169725442ea9efd115a.jpg • Новые URL фото вида photofile.ru/photo/UserLogin/AlbumID/Photo.jpg + Уменьшается количество полей в таблице + Упрощение ссылок + Более четкая дисковая структура photo/U/Us/UserLogin/ + Мы можем выделить на подмножество пользователей (Us) отдельный fileserver! – URL фото в альбоме можно подобрать (Bruteforce). Нужна новая система паролирования альбомов. ngnix: X-Accel-Redirect и md5(то, что меняется между клиентами) Проблемы с версией 2 (которые можно было предусмотреть) • Smarty cache – «неидеальное» решение для нескольких frontends + fileservers – Некогерентен – Невозможно удаление «по событию» – NFS на файлы Smarty cache является SPOF (и не используется) • БД «не держит» нагрузку – Слишком большая таблица альбомов – Слишком много сортировок и условий + Таблица типа MRG + Cron script (album_new – за последние 2 месяца, album_old – все остальные) These Aren't the Droids You're looking For • «Старые» ссылки на файлы при переносе = symlinks • «Старые» ссылки на страницы и файлы = денормализованные таблицы + «правильные» индексы • При кажущейся непервоочередности проблемы – пользователи не почувствовали себя «лишними» Что делать? • Cache Smarty не эффективен • Очевидное решение – memcached – Данные не связаны между собой – Не гарантирует хранение • Необходим дополнительный уровень абстракции метаданные Memcached with METAinformation + Реализация SQL limit + Уменьшение данных для сериализации + Единовременное устаревание коллекции данных + Самостоятельная обработка устаревания − Дополнительные операции get/set Файловые транзакции • Выросший объем файлов в альбоме • Необходимы real-time операции • Обернем каждую файловую операцию в транзакцию (SQL и диск) • Сократим дисковые операции путем введения флага «удален» • nginx: set $flag /...../.deleted if ( -f $flag ) { return 404; } Предел MySQL Master-Slaves • Данных меньше не становится • Эффективное количество Slaves конечно • Решение – разделить данные на несколько порций – – – – Управляющая часть (список пользователей, сессии) Данные пользователя (альбомы, фотографии) Общие данные (форум, конкурсы) Справочники (пункты печати) • Вопросы: – Как сохранить существующий формат ID – Как организовать Autoincrement – Балансировка нагрузки Сквозой autoincrement по нескольким mysqld 4 294 967 295 : например M = 50 частей, по 85 899 345 20 INSERT/сек Заполнение части: 49 дней Работа с разделенными данными • На каждую порцию данных есть 2 Slaves • При подключении указываем режим работы R или RW • Балансировка путем анализа нагрузки и размещения на сервере нескольких баз или же демонов (разные порты, репликация) Распределение файлов по серверам • • • • • Более 40 fileservers x 1.4Tb REGEXP слишком сложен Часть данных превышает 1.4Tb Dynamic DNS – наш выбор Nginx.conf: resolver 127.0.0.1; resolver_timeout 1s; … set $username $1; proxy_pass http://$username.photofile$request_uri; Photofile в цифрах и фактах Сервера: Proxy: www (php): MySQL: Fileserver Backup Прочие Итого: 2 7 13 55 55 3 135 Посещаемость: Хиты: 4 500 000 Хосты: 200 000 В базе: Пользователей: 1 000 000 Альбомов: 2 100 000 Фото: 70 000 000 Вместо итогов • Что еще используется: – CARP – разделение IP для резервного проксирования – mod_accel – для кеширования частых URL – nginx кеширует популярные фото – MySQL Master-Slaves для разделения запросов на «запись-чтение» – Cron & rsync – для генерации статического контента • Что планируется: – Ввод дополнитетельных файлов-флагов «запаролен», «защищен» – Переработка отдачи картинок (вынос preview на отдельные сервера) + 55 fileservers, +55 backup От стартапа до highload: Эволюция веб-проекта, или Как победить нагрузку Вопросы? Антон Терехов, ведущий инженер-программист [email protected]