Галоўная > Наколькі павольны SELECT *? (глыбокае апусканне)

Наколькі павольны SELECT *? (глыбокае апусканне)

SQL
БД

Прапаную разгледзець новую важную тэму, якая датычыцца БД і запытаў для выбаркі даных. Няхай выбачаюць богі БД і адэпты бэкэнда, што, згодна назве сайта, артыкул трапляе ў катэгорыю "іншая трасца". Але богі высока, а адэпты далёка, таму, я са сваёй старонкі зраблю спробу перакладу наступнага артыкула. Калі тэма цікавая, не шкадуйце лайкаў, пішыце каментары з заўвагамі і пажаданнямі, каб мы бачылі, артыкулы на якую тэму найбольш запатрабаваныя.

Пераклад артыкулу Hussein Nasser How Slow is SELECT *? (A deep dive)

path of the SELECT

У механізме захоўвання радкоў базы даных радкі захоўваюцца ў адзінках, якія называюцца старонкамі (pages). Кожная старонка мае фіксаваны загаловак і змяшчае мноства радкоў, кожны радок мае загаловак запісу, за якім ідуць адпаведныя слупкі. Напрыклад, разгледзім наступны прыклад з PostgreSQL:

Postgres Page

Калі база даных запытвае старонку і змяшчае яе ў агульным пуле буфераў (shared buffer pool), мы атрымліваем доступ да ўсіх радкоў і слупкоў гэтай старонкі. Такім чынам узнікае пытанне: калі мы маем усе слупкі, даступныя ў памяці, чаму SELECT * будзе павольным і дарагім? Гэта на самой справе так павольна, як кажуць людзі? І, калі гэта так, то чаму? У гэтым артыкуле мы разглядзім гэтыя і іншыя пытанні.

Сканіраванне index Only Scan гаворыць "да пабачэння"

Выкарыстоўванне SELECT * азначае, што аптымізатар базы даных не можа выбраць Index Only Scan. Напрыклад, выкажам здагадку, што вам патрэбныя ідэнтыфікатары студзентаў, якія набралі больш за 90 балаў, і існуе індэкс у слупку адзінак, які змяшчае ID студэнта як неключавы. Гэты індэкс ідэальна падыходзіць для гэтага запыту.

Аднак, паколькі вы запытваеце ўсе палі, база даных павінна атрымаць доступ да старонкі даных кучы (heap data page) каб атрымаць астатнія палі, павялічваючы такім чынам выпадковыя чытанні, што ў выніку прывядзе да значна большай колькасці I/O-аперацый. І наадварот, база даных магла б толькі адсканіраваць індэкс адзнак і вярнуць ідентыфікатары, калі б вы не выкарыстоўвалі SELECT *.

Кошт дэсерыялізацыі

Дэсерыялізацыя, альбо дэкадаванне, - гэта працэс пераўтварэння неапрацаваных байтаў у тыпы даных. Гэта ўключае ў сябе ўзяцце паслядоўнасці байтаў (звычайна, з файла, з сеткавага ўзаемадзеяння ці іншай крыніцы) і пераўтварэнне яе ў больш структураваны фармат даных, такі як аб'екты ці пераменныя ў мове праграмавання.

Калі вы выконваеце запыт SELECT *, базе даных патрэбна дэсерыялізаваць усе слупкі, якія нават могуць і не спатрэбіцца ў вашым канкрэтным выпадку выкарыстання. Гэта можа павялічыць вылічальныя выдаткі і запаволіць выконванне запытаў. Калі ж вы выбіраеце толькі неабходныя слупкі, то такім чынам можаце паменьшыць выдаткі на дэсерыялізацыю і палепшыць эфектыўнасць запытаў.

Не ўсе слупкі інлайнавыя

Адной з істотных праблем з запытамі SELECT * з'яўляецца тое, што не ўсе слупкі захоўваюцца ўнутры старонкі. Вялікія слупкі, такія як тэкст (text) і blob, могуць захоўвацца ў знешніх табліцах і выцягваюцца з іх толькі калі узнікае запыт (Postgres TOAST табліцы, напрыклад). Гэтыя слупкі часта сціскаюцца, таму, калі вы выконваеце запыт SELECT * са шматлікімі тэкставымі палямі, геаметрычнымі данымі ці blob, вы ствараеце дадатковую нагрузку на базу даных, каб атрымаць значэнні са знешніх табліц, распакаваць іх і вярнуць вынікі запыту.

Кошт сеткі

Перад тым, як вынік запыта вернецца кліенту, ён павінен быць серыялізаваны згодна пратакола камунікацыі, падтрымліваемым БД. Чым больш даных трэба серыялізаваць, тым больш працы патрабуецца ад працэсара. Пасля таго, як байты будуць серыялізаваныя, яны перадаюцца праз TCP/IP. Чым больш фрагментаў (TCP-пакетаў - удакладненне перакладчыка) трэба адправіць, тым вышэй кошт перадачы, што ў канчатковым выніку ўплывае на затрымку сеткі.

Пры вяртанні ўсіх слупкоў можа спатрэбіцца дэсерыялізацыя вялікіх слупкоў, такіх як радкі (верагодна меўся на ўвазе text - прым. перакладчыка) або blob, якія кліенты насамрэч могуць ніколі не выкарыстоўваць.

Дэсерыялізацыя на кліенце

Як толькі кліент атрымлівае неапрацаваныя байты, кліенская апплікацыя павінна дэсерыялізаваць даныя на той мове праграмавання, якую выкарыстоўвае кліент, зноў павялічваючы агульны час апрацоўкі. Чым больш даных перадаецца, тым павольней будзе працэс.

Заключэнне:

Падводзячы вынікі, запыт SELECT * задзейнічвае шмат складаных працэсаў, таму лепей запытваць толькі тыя палі, якія будуць патрэбныя, каб пазбегнуць залішніх выдаткаў. Майце на ўвазе, што калі ваша табліца мае няшмат слупкоў з простымі тыпамі даных, выдаткі на запыт могуць быць нязначнымі. Але ж звычайна добрая практыка - выбарачна ставіцца да слупкоў, якія запытваеце.

Дзякуй!

Дадаткова:

  1. Is SELECT * Expensive? (eng video)
loveJS, 2023-05-22
Каментары

    (Каб даслаць каментар залагуйцеся ў свой уліковы запіс)

    ;