MySQL в качестве NoSQL базы данных

Идея, на самом деле, витала в воздухе. Уже не раз для хранения данных с неопределенной структурой приходилось использовать единственное поле, в которое эти данные помещались в сериализованном виде. Оставалось сделать один шаг: убрать все остальные поля. Нет ничего проще. Засунули все кроме первичного ключа в то же самое поле — и готово. В таком варианте остается вопрос с поиском, ведь потребность в индексах пока никто не отменял. Для этой цели можно использовать вспомогательные таблицы c примерно такой структурой:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

Т.е. получается, что мы выносим индексы из основной таблицы во вспомогательные, что дает нам выигрыш в скорости записи в основную таблицу, а ведь она может быть огромной.

Больше подробностей о подобном подходе можно прочитать в блоге у CEO FriendFeed. Мне кажется, тема заслуживает внимания.

MySQL, InnoDB и TEXT

Совершенно неожиданная проблема вылезла со стороны InnoDB. При записи реальных данных в таблицу сервер начал выдавать ошибку 1118 — «Row size too large».

Если кто не знает, MySQL хранит все данные вместе в одной строке-блоке, кроме данных типов TEXT и BLOB. Вместо них хранится ссылка на местоположение реальных данных. Вот и считаем сумму всех данных в строке — она не должна превышать некоторого значения. Если посмотреть в документации, можно увидеть, что максимальная длина строки для MyISAM — 64 килобайта, а для InnoDB — около 8000 байт.

Я как-то уже встречался с подобной проблемой, поэтому представлял, что делать: открываем таблицу, смотрим типы колонок, разбираемся, что можно заменить на TEXT или BLOB. Каково же было мое удивление, когда в нужной таблице оказалось 2 числовых поля и 40 полей с типом TEXT. Ну никак этот набор данных не может превышать 8Кб! Интерес добавляло еще то обстоятельство, что на тестовых небольших данных всё работало, данные записывались и гармония царила.

Оказывается, у InnoDB два формата хранения данных — Antelope и Barracuda. И у Antelope есть интересная особенность: текстовые поля хранятся не совсем так, как мы привыкли думать. У них первые 768 байт попадают в саму строку и только затем, если данных оказалось больше, ставится ссылка на остаток. Понятное дело, что 768*40 значительно превышает отведенные 8 килобайт. Так же понятно, почему всё хорошо работало на небольших объемах данных: все помещалось в строку и без всяких ссылок.

Мы решили эту проблему, выбросив все текстовые поля и заменив их одним, в котором все данных хранились в сериализованном виде. В интернете в качестве решения предлагался еще варианты разбить таблицу на несколько или сменить тип хранилица InnoDB.

Вот так живешь себе и не знаешь с какой стороны упадет сюрприз.