Например: Программист
0,2
рейтинг
16 августа 2011 в 00:45

Разработка → 8123 байта хватит каждому

Сегодня во время перевода одного сайта с таблиц MyISAM на InnoDB, у последних выяснилась одна интересна особенность. Запрос на изменение движка для двух таблиц возвращал странную ошибку «Got error 139 from storage engine». После поиска информации на эту тему, было выяснено, что данная ошибка возникает тогда, когда какая-либо строка таблицы не вмещается в половину страницы памяти, с которыми работает MySQL. Страницы эти равны 16 Кб, а половина, стало быть, 8 Кб.

Само по себе ограничение довольно странное, но на первый взгляд кажется трудно достижимым, ведь как известно, MySQL хранят текстовые данные в хранилище, отдельном от табличных строк. Оказалось, что это верно только на половину. На самом деле InnoDB хранит в отдельном хранилище только «излишки», к коим он не относит первые 768 байтов каждого текстового поля. Т.е. любой текст будет отъедать от длины строки столько байт, сколько он содержит, но не больше 768. Несложно подсчитать, что максимальное число текстовых полей длиной от 768 байт, которое можно безопасно хранить в одной таблице — 10. И действительно, если запустить пример, все пройдет гладко. Но стоит увеличить количество полей хотя бы на одно, и мы получим ту же ошибку, что и в начале.

Больше всего поражает не абсурдность ограничения, и даже не «прожорливость» строковых типов данных, а умалчивание этой проблемы. InnoDB позволяет совершенно спокойно создавать таблицы с сотнями текстовых полей. При этом о том, что вы не сможете ими воспользоваться, вы узнаете только на продакшене при заполнении таблицы реальными данными. Невнятное сообщение об ошибке тоже оставляет мало положительных эмоций.

Бороться с недугом можно двумя способами:

Перекомпиляция с увеличением UNIV_PAGE_SIZE (продолжение).

Подключение InnoDB через плагин, поддерживающий формат файлов barracuda и изменение ROW_FORMAT у таблиц на DYNAMIC. Или просто использование ROW_FORMAT=DYNAMIC, если у вас уже MySQL 5.5.
Александр Карпинский @homm
карма
88,8
рейтинг 0,2
Например: Программист
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (27)

  • –30
    Не холивара ради, но имхо MySQL самая глючная СУБД из всех что я видел.
    Из бесплатных лучше Postgresql, на худой конец если нужно что-то легковесное то SQLite.
    • +8
      Холивар таки случился…

      А меня в мускуле насторожило, что два из четырёх типов хранилища не обещают транзакционной целостности. Причём схема может быть размещена на нескольких хранилищах.

      Рекомендуют класть туда журналы приложения, которым, якобы, целостность не нужна. Хех.

      Боюсь меня щас утопят в минусах за оффтоп…
  • +10
    Пипец, вот настрочили комменты.
    В каждой базе данных хватает своих особенностей, ограничений и глюков. Код пишет не подразделение НАСА, которое пишет софт для шатлов. А у MySQL историческое наследие, которое слава богу выпиливают и меняют. У же не говорю о том, что прямо в топике написано, что с 5.5 такой проблемы уже нет (либо использовать InnoDB Plugin, который кажись с 5.1 уже работает).

    А меня в мускуле насторожило, что два из четырёх типов хранилища не обещают транзакционной целостности.

    Ограниченность мышления данного индивидума… Ну вы поняли.
    А ещё, он знает всего 4 типа движков для MySQL. Чел спец! :trollface:
    Их вобще-то 8, просто часть не включена по умолчанию. А так же с десяток-два сторонних бесплатных и комерческих движков — советую заглянуть на mysqlperformanceblog.com для самообразования что-бы узнать побольше о самых известных (там в основном колоночные движки для аналитики рассматриваются — клиенты пользуются ихние).

    Что касается автора — он молодец. Теперь меньше людей будут недоумевать над причиной проблемы. К счастью 10+ колонок типа TEXT в таблице — редкость, так что это знание больше из разряда уличной магии :)
    • –9
      Да я всё понимаю, но количество всяких багофич которые нужно помнить и знать при работе с MySQL оно как-то очень велико по сравнению с другими СУБД.

      имхо.
      • +2
        Наверное, не меньше оракула.
        Но оракулу можно простить, так как вытягивает базы и государственного масштаба.
        • –1
          Можно поспорить. Есть стандарт SQL и оракл в своих базах максимально ему следует. Как например и Postgres.
          О MySQL так не скажешь.
      • +1
        Для работы в 90% Вам не нужно знать «все багофичи», мускул работает вполне стабильно и ожидаемо, их нужно знать когда создаешь таблицы с _10 и более текстовыми полями_.
      • 0
        Ну ещё есть такое обстоятельство что семейство Oracle/MSSQL/PostgreSQL расчитаны на корпоративное применение и использование продвинутого функционала.
        MySQL позиционируется как простой сервер баз данных для веб сайтов.

        Поэтому если из MySQL пытаться выжать то же что скажем с PostreSQL, то багофитч можно накопать тонну. Может просто взять инструмент, который подходит под задачу?
        Зато MySQL гибок, всегда есть решения не вырезанием гланд через ж… у, а можно поставить движок, который подходит для задачи и использовать по назначению.
        Но если замарачиваться, то оптимизировать и работать с огромными базами можно успешно. Есть просто одна проблема — MySQL ошибок не прощает.
  • +1
    Вроде на поля VARCHAR это ограничение тоже распространяется, т.е. они тоже «отъедают» байты из этих 8Кб и их надо учитывать. Поправьте меня, если ошибаюсь.
    • +3
      Это справедливо для всех типов колонок переменной длины: TEXT, VARCHAR, BLOB, VARBINARY.
  • +20
    Всё верно. Хочу добавить следующее.

    Текст ошибки может быть такой: “SQL Error (1118): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 8126. You have to change some columns to TEXT or BLOBs”.

    Чтобы решить проблему в MySQL 5.5, нужно:

    1) включить новый формат InnoDB файлов в my.cnf:
    innodb_file_format = Barracuda
    Если создаётся таблица, которая не нуждается в новых возможностях, то она будет создана в более простом формате Antelope.

    2) преобразовать таблицу в новый формат строки:
    ALTER TABLE tableName ENGINE = InnoDB ROW_FORMAT = Dynamic;
    Формат строки Dynamic означает, что для длинных TEXT/VARCHAR, не являющихся частью primary key, в B-дереве могут хранятся только 20 байт указателя на отдельную область с данными (overflow). База сама выбирает, какие колонки держать в B-дереве, а какие слишком длинные, и нужно держать отдельно в overflow: если строка таблицы не умещается в размер половины страницы (8126 байт), то самая длинная колонка целиком помещается во внешнее хранилище. Процедура повторяется, пока все оставшиеся колонки не уместятся в размер половины страницы.

    Итак, в этом формате каждая TEXT/VARCHAR колонка занимает минимум 20 байт в странице, и одна строка таблицы может содержать максимум 400 TEXT/VARCHAR полей. Колонка может занимать больше байт в странице, если там осталось место.

    Чтобы в будущем MySQL выдавал ошибку при создании таблицы, если она не помещается в формат строки, включаем innodb_strict_mode в my.cnf:
    innodb_strict_mode = ON
    Также можно обратить внимание на sql_mode для упрощения отладки.
    • +3
      При использовании
      innodb_file_format = Barracuda
      не забудьте добавить и
      innodb_file_per_table
  • +16
    Рубрика «нам пишут анонимы»:
    Доброе утро

    Прочитал ваш топик и сразу возник вопрос — и за чем он нужен? Каково реальное применение? Хранить в БД большие объёмы текстовых данных — это несусветная глупость, т.к. это сильно тормозит БД, съедает кэш и существуют другие оптимальные способы.

    Уберите топик в черновики, пожалуйста.

    И вам, Виталий, доброе утро.

    Реальное применение топика — избегать таких ошибок у себя в проектах. Как я уже сказал, ошибка эта не сильно освещена в документации, и понять в чем проблема из сообщения об ошибке не представляется возможным. Кроме того, информация о том, что эта особенность не проявляется в новой версии InnoDB при определенном формате таблиц, была найдена мной эмпирически. О том, что базы данных не предназначены для хранения текстовой информации я слышу впервые, научите, пожалуйста, как правильно.

    Топик не уберу, спасибо.
    • +2
      Хана, а о чем как не о таких вот вещах писать на хабр?
      • +16
        О том, что Гугл покупает Мобильное подразделение Моторолы ;-)
      • +4
        • 0
          Что-то неправильное расписание. Гугл стартап еще вчера прикупила.
    • +1
      Не чувствую себя вправе публиковать дальнейшую переписку с Виталием, но если кому-то интересно, как правильно хранить текстовую информацию, то Виталий считает, что в отдельных файлах. При этом, в локальных или в удаленных, он не уточняет.
      • 0
        хм… понятно… о_о
      • +1
        Тут, скорее, все зависит от задачи — для чего хранить. Если для поиска — БД, если для вывода — файлы.
        • +1
          И от объемов информации, разумеется
        • +2
          Виталий?
          • 0
            Нет, это не он :)
      • 0
        Виталий не уточняет как искать, например полнотекстовым поиском в этих эээ… файлах информацию?

        Спасибо за топик, не знал про данную проблему, у нас 5.1 повсеместно, пока не сталкивались, но пригодится очень даже может.
        • 0
          Искать я бы посоветовал сфинксом (или аналогом), а не полнотекстовым поиском от mysql. Зависит, конечно, от задачи, но в большинстве случаев сфинкс будет быстрее и функциональнее. и в этому индексу уже прикручивать файлы, если для остальных задач (кроме поиска) выходит, что лучше хранить инфу в них.
      • 0
        Может Виталий напишет статью, где буду аргументирован его метод хранения текстовых данных?
    • 0
      Виталий наверно не знает что есть Sphinx, который берёт MySQL за жабры, по конфигам пользователя вытрясает из него текстовые (и не только) данные, делает по ним поисковые индексы и запросы принимает на себя :)

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