#sql #база_данных #postgresql
Каким образом резервируется память для типа varchar? Например, есть таблица, в которой столбец обьявлен как varchar(128). Что происходит, если я вставляю, строку, которая, скажем, содержит лишь 20 символов? Будет ли память для остальных 108 символов зарезервирована СУБД? Также интересно поведение при вставке null. И, главный вопрос, насколько важно иметь тип минимального размера в целях рационального использования памяти (то есть не допускать явного излишества при создании схемы БД)?
Ответы
Ответ 1
На самом деле вы затронули не очень простой и не самый короткий вопрос. Мой ответ касается строго PostgreSQL, с ссылками на его исходный код и детали реализации (потому что могу). Цифра в скобках varchar - это только ограничение. Не влияет совершенно ни на что, кроме получения ошибки за попытку записать что-то превышающее этот лимит. И сразу важнейшая оговорка: эта цифра - длина в символах, не в байтах. Вероятно читателю уже понятно, что формат хранения не может зависеть от этой цифры. Не дело это, когда для разных символов одной длины нам надо хранить различающийся в 4 раза объём данных (бывают более длинные кодировки? может быть, но я не знаю. Юникод до 4 байт на символ пока ещё влезает). Для хранения текстов базово действует правило: The storage requirement for a short string (up to 126 bytes) is 1 byte plus the actual string, which includes the space padding in the case of character. Longer strings have 4 bytes of overhead instead of 1. Long strings are compressed by the system automatically, so the physical requirement on disk might be less. Very long values are also stored in background tables so that they do not interfere with rapid access to shorter column values. In any case, the longest possible character string that can be stored is about 1 GB. То есть: в общих чертах используется стиль хранения данных с записью длины строки в байтах в начале записи строки короче 126 байт кодируются в виде структуры varattrib_1b: 1 байт для хранения длины строки (в байтах), но из которых 1 бит зарезервирован как маркер того, что это 1-байтовый формат хранения. если в 126 байт строка не влезла - то используем длинную форму varattrib_4b: здесь уже используется 4 байта на заголовок, что позволяет хранить существенно более длинные строки. Опять же некоторое биты зарезервированы, но до 1гб данных сохранить возможно. Однако особые приключения начинаются позже. PostgreSQL манипулирует данными только фиксированными блоками по 8кб (обычно, настройка времени компиляции СУБД). Как 1гб данных записать в таком случае? На помощь приходит огромный фокус ушами (а у слона уши-то большие, так что всё с нами ясно, с таким-то логотипом): длинные строки нарезаются на части и отправляются в отдельную TOAST таблицу. И здесь используется другая форма заголовка, varattrib_1b_e. Где вместо данных хранится идентификатор по которому нужные данные можно прочитать из TOAST. Плюс к тому, длинные строки могут сжиматься самой базой. Длинной считаются 1/4 блока (2кб то есть). Могут сжиматься после перемещения в TOAST, а могут - до. То есть прямо на месте сжали и сохранили, если сжатые данные влезли в 2кб. (через alter table в некоторых пределах можно этой логикой управлять) И так мы приходим ко второй форме уже знакомого varattrib_4b - Compressed-in-line формат. Теперь после уже известных 4 байт заголовка с длиной данных на диске ещё хранится в других 4 байтах длина несжатого текста. И весь фокус в том, что всё это многообразие никак не зависит от объявления таблицы. Формат используется тот, который подходит для нужных данных. В общем я старательно запутал с internals кухней. Так что отдельно и по конкретным вопросам: Что происходит, если я вставляю, строку, которая, скажем, содержит лишь 20 символов? 20 символов меньше порога в 126 символов. Поэтому база сможет использовать короткую форму с заголовком в 1 байт. Следовательно строка займёт... От 21 до 81 байта. Смотря какую кодировку ваша база использует и какие это символы. Например, create temp table foo (a varchar); insert into foo values ('привет'); select a, pg_column_size(a) from foo; Строка "привет" в UTF8 занимает 12 байт, поэтому pg_column_size насчитает 13 байт итог. Будет ли память для остальных 108 символов зарезервирована СУБД? Нет, не будет. Если говорить именно о varchar, а не char. В этом и заключается их различие. Притом именно для PostgreSQL никаких бенефитов от такого поведения char нет. Это просто требование стандарта. я могу создавать столбцы varchar(1024), вставлять туда по одному символу для каждой строки и по памяти никакой раницы с varchar(1) не будет? Да, верно. Число - это ограничение на данные. Формат хранения от него не зависит. И, главный вопрос, насколько важно иметь тип минимального размера в целях рационального использования памяти (то есть не допускать явного излишества при создании схемы БД)? В моей практике бывала ситуация, когда по ошибке в поле вместо 10 символов прилетает 10 мегабайт непойми чего от приложения. По ошибке в момент записи место ошибки в приложении искать банально удобнее. Про NULL отдельно Все NULL в PostgreSQL независимо от типа поля хранятся идентично - в битовой маске после заголовка tuple (одной строки таблицы). Использование памяти: 1 байт на каждые 8 полей которые могут быть NULL. (с округлением вверх, конечно) При вставке NULL проставляется 1 бит соответствующий этому полю в битовой маске и затем данные этого поля не пишутся вовсе никак. Поле NULL, и одного бита в битовой маске более чем достаточно, нет нужды хранить что-то ещё.
Комментариев нет:
Отправить комментарий