Що отримується з диска під час запиту?


14

Досить просте запитання, напевно, десь відповіли, але я не можу сформувати правильне пошукове запитання для Google ...

Чи впливає кількість стовпців у певній таблиці на ефективність запиту під час запитів у підмножині цієї таблиці?

Наприклад, якщо таблиця Foo має 20 стовпців, але мій запит вибирає лише 5 з цих стовпців, чи 20-ти стовпців (проти, скажімо, 10) впливає на ефективність запиту? Припустимо для простоти, що що-небудь в пункті WHERE міститься в цих 5 колонках.

Мене хвилює використання буфера кешів Postgres на додаток до дискового кешу операційної системи. Я дуже втрачаю розуміння фізичного дизайну Postgres. Таблиці зберігаються на декількох сторінках (за замовчуванням до 8 кб на сторінці), але я не зовсім розумію, як кортежі розташовані звідти. Чи PG досить розумний, щоб отримати лише з диска дані, що містять ці 5 стовпців?


Ви говорите про отримання 50 байтів, але не про інші 150. Ваш диск, ймовірно, читає з більшим кроком, ніж це!
Андомар

Звідки ви берете ці цифри?
Jmoney38

Відповіді:


15

Фізичне зберігання рядків описано в документах у макеті сторінки бази даних . Вміст стовпця для цього ж рядка зберігається на одній сторінці диска, за винятком винятку вмісту TOAST 'ed (занадто великий, щоб вмістити сторінку). Зміст вилучається послідовно в кожному рядку, як пояснено

Щоб прочитати дані, потрібно по черзі вивчити кожен атрибут. Спочатку перевірте, чи поле NULL відповідно до нульової растрової карти. Якщо це так, переходьте до наступного. Потім переконайтеся, що ви маєте правильне вирівнювання. Якщо поле є фіксованою шириною, то всі байти просто розміщуються.

У найпростішому випадку (без стовпців TOAST'ed), postgres отримає весь рядок, навіть якщо потрібно кілька стовпців. Отже, у цьому випадку відповідь "так", якщо більше стовпців може мати явний негативний вплив на кеш-пам'ять буфера, особливо якщо вміст стовпця є великим, поки він знаходиться під порогом TOAST.

Тепер випадок TOAST: коли окреме поле перевищує ~ 2 кБ, двигун зберігає вміст поля в окрему фізичну таблицю. Він також починає грати, коли весь рядок не вміщується на сторінку (8 кБ за замовчуванням): деякі поля переміщуються у сховище TOAST. Док каже:

Якщо це поле змінної довжини (attlen = -1), то це трохи складніше. Усі типи даних змінної довжини мають загальну структуру заголовка структури varlena, яка включає загальну довжину збереженого значення та деякі біти прапора. Залежно від прапорів, дані можуть бути вбудованими або в таблиці TOAST; він також може бути стиснутим

Вміст TOAST''s не виймається, коли вони явно не потрібні, тому їх вплив на загальну кількість сторінок для отримання невеликий (кілька байт на стовпець). Це пояснює результати у відповіді @ dezso.

Що стосується записів, кожен рядок із усіма його стовпцями повністю переписується на кожну ОНОВЛЕННЯ, незалежно від того, які стовпці змінено. Тож мати більше колонок, очевидно, дорожче для запису.


Це відповідь у відповідь. Саме те, що я шукаю. Дякую.
Jmoney38

1
Хороший ресурс я знайшов відносно структури рядків (pageinspect, і деякого використання зразка) тут .
Jmoney38

10

Відповідь Даніеля зосереджується на вартості читання окремих рядків. У цьому контексті: розміщення NOT NULLстовпців фіксованого розміру спочатку у вашій таблиці мало допомагає. Поставлення спочатку відповідних стовпців (тих, про які ви запитуєте) мало допомагає. Мінімізація прокладки (завдяки вирівнюванню даних), граючи вирівнювання тетрісу зі своїми стовпцями, може трохи допомогти. Але найбільш важливий ефект поки не згадується, особливо для великих столів.

Додаткові стовпці очевидно роблять рядком більше місця на диску, так що на одній сторінці даних розміщується менше рядків (8 кБ за замовчуванням). Окремі рядки розкладені на більше сторінок. Двигун бази даних, як правило, повинен отримувати цілі сторінки, а не окремі рядки . Мало значення, чи є окремі рядки дещо меншими чи більшими - до тих пір, поки має бути прочитана однакова кількість сторінок.

Якщо запит отримує (відносно) невелику частину великої таблиці, де рядки розподіляються більш-менш випадковим чином по всій таблиці, підтримується індексом, це призведе до приблизно однакової кількості прочитаних сторінок, при цьому мало уваги до розміру рядка Нерелевантні стовпці не дуже сповільнить вас у такому (рідкісному) випадку.

Як правило, ви будете отримувати патчі або кластери рядків, які були введені в послідовності або близькості, та обмінюватися сторінками даних. Ці рядки розкинуті через безлад, для задоволення вашого запиту потрібно прочитати більше сторінок диска. Необхідно прочитати більше сторінок, як правило, є найважливішою причиною повільнішого запиту. І це найважливіший фактор, чому нерелевантні стовпці роблять ваші запити повільнішими.

З великими базами даних, як правило, недостатньо оперативної пам'яті, щоб зберігати все це в кеш-пам'яті. Більші рядки займають більше кешу, більше суперечок, менше звернень кешу, більше дискових вводу-виводу. А читання дисків, як правило, набагато дорожче. Менше це стосується SSD, але істотна різниця залишається. Це додає до вищезгаданого пункту про читанні сторінки.

Це може мати значення або не мати значення, якщо неактуальні стовпці будуть TOAST-ed. Відповідні стовпці також можуть бути TOAST-ed, що призведе до того ж ефекту.


1

Невеликий тест:

CREATE TABLE test2 (
    id serial PRIMARY KEY,
    num integer,
    short_text varchar(32),
    longer_text varchar(1000),
    long_long_text text
);

INSERT INTO test2 (num, short_text, longer_text, long_long_text)
SELECT i, lpad('', 32, 'abcdefeghji'), lpad('', 1000, 'abcdefeghji'), lpad('', (random() * 10000)::integer, 'abcdefeghji')
FROM generate_series(1, 10000) a(i);

ANALYZE test2;

SELECT * FROM test2;
[...]
Time: 1091.331 ms

SELECT num FROM test2;
[...]
Time: 21.310 ms

Обмеження запиту до перших 250 рядків ( WHERE num <= 250) призводить до 34,539 мс та 8,343 мс відповідно. Вибір усіх, крім long_long_textцього обмеженого набору, призводить до 18,432 мс. Це свідчить про те, що на ваших термінах PG досить розумний.


Ну, я, безумовно, ціную вклад. Однак я не можу з впевненістю сказати, що цей тестовий сценарій підтверджує те, що я спочатку запропонував. Є кілька питань. Для одного, коли ви вперше запустите "SELECT * FROM test2", він повинен був заповнити загальний кеш-пам'ять. Цей запит зайняв би набагато більше часу, щоб отримати з диска. Таким чином, 2-й запит теоретично був би набагато швидшим, оскільки він був би отриманий з кешу SB. Але я погоджуюсь, що він «підказує», що PG лише отримує потрібні йому рядки, виходячи з ваших пізніших тестів / порівнянь.
Jmoney38

Ви праві, цей тест (бути простим) має свої вади. Якщо у мене буде достатньо часу, я спробую також висвітлити їх.
дезсо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.