JavaRush /Блоги Java /Random-TG /Мушкилоти иҷрои SQL, ки аз "кори нолозим, вале зарурӣ" ба...

Мушкилоти иҷрои SQL, ки аз "кори нолозим, вале зарурӣ" бармеоянд

Дар гурӯҳ нашр шудааст
Сатҳи донише, ки барои фаҳмидани мақола зарур аст: фаҳмиши умумии пойгоҳи додаҳо ва SQL, баъзе таҷрибаи амалӣ бо DBMS.
Масъалаҳои иҷрои SQL аз ҷониби
Эҳтимол чизи муҳимтарине, ки шумо метавонед навиштани дархостҳои муассири SQL-ро ёд гиред, ин индексатсия аст. Бо вуҷуди ин, дар ҷои дуюм, хеле наздик аст, ин донишест, ки бисёре аз муштариёни SQL аз пойгоҳи додаҳо талаб мекунанд, ки "кори нолозим, вале заруриро" иҷро кунад . Пас аз ман такрор кунед:
Кори нолозим, вале талабшаванда
«Кори нолозим, вале ҳатмӣ» чист? Тавре ки капитан Обвиус ба мо мегӯяд, вай:

Нолозим

Бигзор дархости муштарии мо ба маълумоти зерин ниёз дорад:
Масъалаҳои иҷрои SQL аз ҷониби
Ҳеҷ чизи ғайриоддӣ нест. Мо бо пойгоҳи додаҳои филмҳо кор карда истодаем (ба мисли махзани Sakila ) ва мехоҳем унвон ва рейтинги ҳамаи филмҳоро ба корбарон нишон диҳем. Дархости зерин метавонад натиҷаи ба мо лозимро диҳад:
SELECT title, rating
FROM film
Аммо, барномаи мо (ё ORM мо) ба ҷои ин дархостро иҷро мекунад:
SELECT *
FROM film
Дар натиҷа мо чӣ ба даст меорем? Фикр кунед. Мо маълумоти зиёди бефоида мегирем:
Масъалаҳои иҷрои SQL аз ҷониби
Дар тарафи рост шумо ҳатто метавонед дидани баъзе JSON-и мураккабро бор кунед:
  • аз диск
  • ба кэш
  • бо сим
  • дар хотираи муштарӣ
  • ва ниҳоят партофташуда [чун нолозим]
Бале, мо қисми зиёди ин маълумотро мепартоем. Ҳама амалҳое, ки барои истихроҷи ин маълумот андешида шудаанд, комилан бефоида шуданд. Оё ин дуруст аст? Оё дуруст аст.

Ҳатмӣ

Ва ҳоло - бадтарин қисми. Гарчанде ки оптимизаторҳо ҳоло метавонанд бисёр корҳоро анҷом диҳанд, ин амалҳо барои пойгоҳи додаҳо ҳатмӣ мебошанд. Пойгоҳи додаҳо роҳи донистани он, ки барномаи муштарӣ ба 95% ин маълумот ниёз надорад. Ва ин танҳо соддатарин мисол аст. Тасаввур кунед, ки якчанд ҷадвалҳоро пайваст кунед ... Пас чӣ мегӯед, аммо базаҳои маълумот зуд мебошанд? Биёед ман шуморо дар бораи баъзе чизҳое, ки шумо дар бораи он фикр накардаед, равшан созам. Албатта, вақти иҷрои дархости инфиродӣ аслан ба чизе таъсир намекунад. Хуб, он якуним маротиба сусттар кор кард, аммо мо онро аз сар мегузаронем, дуруст? Барои роҳат? Баъзан ин дуруст аст. Аммо агар шумо ҳамеша иҷроишро барои роҳат қурбон кунед , ин чизҳои хурд ҷамъ мешаванд. Мо дигар на дар бораи иҷроиш (суръати иҷрои дархостҳои инфиродӣ), балки дар бораи гузаранда (вақти вокуниши система) сухан меронем ва он гоҳ мушкилоти ҷиддӣ оғоз мешаванд, ки ҳалли онҳо чандон осон нест. Он вақт шумо миқёспазириро аз даст медиҳед. Биёед ба нақшаҳои иҷро назар андозем, дар ин ҳолат, Oracle DBMS:
--------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes |
--------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1000 |   166K|
|   1 |  TABLE ACCESS FULL| FILM |  1000 |   166K|
--------------------------------------------------
дар муқоиса бо:
--------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes |
--------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1000 | 20000 |
|   1 |  TABLE ACCESS FULL| FILM |  1000 | 20000 |
--------------------------------------------------
Иҷрои дархости SELECT * ба ҷои унвони SELECT, рейтинг хотираи 8 маротиба бештар дар базаи маълумотро истифода мебарад. Ҳеҷ чизи ғайричашмдошт нест, дуруст? Мо медонистем, ки ин рӯй медиҳад. Аммо мо то ҳол ба ин барои бисёре аз дархостҳои худ розӣ мешавем, ки дар онҳо ба мо танҳо ба ин маълумот ниёз надоранд. Мо барои базаи маълумот корҳои нолозим, вале ҳатмӣ эҷод мекунем , ки он пайваста ҷамъ мешавад. Мо 8 маротиба бештар хотираро истифода мебарем (албатта мултипликатор тағир меёбад). Дар ҳамин ҳол, дар ҳама марҳилаҳои дигар (диски вуруди / баромад, интиқоли маълумот тавассути шабака, истеъмоли хотира аз ҷониби муштарӣ) мушкилот комилан якхелаанд, аммо ман онҳоро сарфи назар мекунам ва ба ҷои он ...

Истифодаи индексҳо

Аксари пойгоҳи додаҳо имрӯз аллакай консепсияи фарогирии индексҳоро қадр кардаанд . Индекси фарогирӣ худ як навъи махсуси индекс нест. Аммо он метавонад барои як пурсиши мушаххас "индекси махсус" бошад, ё "тасодуфан" ё азбаски он чунин бошад. Дархости зеринро баррасӣ кунед:
SELECT *
FROM actor
WHERE last_name LIKE 'A%'
Дар бобати татбики он ягон чизи гайричашмдошт нест. Ин як дархости оддӣ аст. Диапазонро аз рӯи индекс бинед, ба ҷадвал дастрасӣ пайдо кунед - ва шумо иҷро шудед:
-------------------------------------------------------------------
| Id  | Operation                   | Name                | Rows  |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                     |     8 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ACTOR               |     8 |
|*  2 |   INDEX RANGE SCAN          | IDX_ACTOR_LAST_NAME |     8 |
-------------------------------------------------------------------
Нақшаи хуб, ҳамин тавр не? Хуб, агар ба мо дар ҳақиқат ин лозим буд, пас не:
Масъалаҳои иҷрои SQL аз ҷониби
Аён аст, ки мо хотираро беҳуда сарф мекунем ва ғайра. Биёед ин дархостро ҳамчун алтернатива баррасӣ кунем:
SELECT first_name, last_name
FROM actor
WHERE last_name LIKE 'A%'
Нақшаи ӯ ин аст:
----------------------------------------------------
| Id  | Operation        | Name            | Rows  |
----------------------------------------------------
|   0 | SELECT STATEMENT |                 |     8 |
|*  1 |  INDEX RANGE SCAN| IDX_ACTOR_NAMES |     8 |
----------------------------------------------------
Мо тавонистем дастрасӣ ба ҷадвалро комилан аз байн барем, ба шарофати мавҷудияти индекс, ки тамоми ниёзҳои дархости моро қонеъ мекунад... индекси фарогир. Оё муҳим аст? Ва чӣ тавр! Ин равиш ба шумо имкон медиҳад, ки баъзе дархостҳоро бо тартиби андоза суръат бахшед (ё онҳоро бо тартиби бузургӣ, вақте ки индекс пас аз баъзе тағиротҳо дигар фаро намегирад) суст кунед. Индексҳои фарогир наметавонанд ҳамеша истифода шаванд. Шумо бояд барои индексатсияҳо пардохт кунед ва шумо набояд шумораи зиёди онҳоро илова кунед. Аммо дар ин ҳолат ҳама чиз равшан аст. Биёед иҷрои онро арзёбӣ кунем:
SET SERVEROUTPUT ON
DECLARE
  v_ts TIMESTAMP;
  v_repeat CONSTANT NUMBER := 100000;
BEGIN
  v_ts := SYSTIMESTAMP;

  FOR i IN 1..v_repeat LOOP
    FOR rec IN (
      -- Наихудший вариант requestа: перерасход памяти ПЛЮС доступ к таблице
      SELECT *
      FROM actor
      WHERE last_name LIKE 'A%'
    ) LOOP
      NULL;
    END LOOP;
  END LOOP;

  dbms_output.put_line('Оператор 1 : ' || (SYSTIMESTAMP - v_ts));
  v_ts := SYSTIMESTAMP;

  FOR i IN 1..v_repeat LOOP
    FOR rec IN (
      -- Улучшенный request, но все равно с доступом к таблице
      SELECT /*+INDEX(actor(last_name))*/
        first_name, last_name
      FROM actor
      WHERE last_name LIKE 'A%'
    ) LOOP
      NULL;
    END LOOP;
  END LOOP;

  dbms_output.put_line('Оператор 2 : ' || (SYSTIMESTAMP - v_ts));
  v_ts := SYSTIMESTAMP;

  FOR i IN 1..v_repeat LOOP
    FOR rec IN (
      -- Оптимальный request: покрывающий индекс
      SELECT /*+INDEX(actor(last_name, first_name))*/
        first_name, last_name
      FROM actor
      WHERE last_name LIKE 'A%'
    ) LOOP
      NULL;
    END LOOP;
  END LOOP;

  dbms_output.put_line('Оператор 3 : ' || (SYSTIMESTAMP - v_ts));
END;
/

Дар натиҷа мо ба даст меорем:


Оператор 1: +000000000 00:00:02.479000000

Оператор 2: +000000000 00:00:02.261000000

Оператор 3: +000000000 00:00:01.857000000

Аҳамият диҳед, ки ҷадвали актёрҳо танҳо 4 сутун дорад, аз ин рӯ фарқияти иҷрои байни изҳороти 1 ва 2 он қадар калон нест, аммо он ҳанӯз ҳам муҳим аст. Ман инчунин қайд мекунам, ки ман маслиҳатҳои оптимизатори Oracle-ро истифода бурдам, то оптимизатор ин ё он индекси мушаххасро барои дархост интихоб кунад. Оператор 3 ғолиби бешубҳа дар мусобиқаи мост. Фаъолияти он хеле беҳтар аст ва мо дар бораи як дархости бениҳоят содда сухан меронем. Боз, вақте ки мо SELECT * -ро менависем, мо барои пойгоҳи додаҳо кори нолозим, вале ҳатмӣ эҷод мекунем , ки онро оптимизатсия карда наметавонад. Вай индекси фарогириро интихоб намекунад, зеро он нисбат ба шохиси LAST_NAME интихобкардаи ӯ каме баландтар аст ва дар байни чизҳои дигар, ӯ бояд ба ҷадвал дастрасӣ пайдо кунад, то сутуни бефоидаи LAST_UPDATE-ро дарёфт кунад. Аммо чӣ қадаре ки мо SELECT *-ро амиқтар таҳлил кунем, ҳамон қадар чизҳо бадтар мешаванд. Биёед дар бораи...

Табдилдиҳии SQL

Оптимизаторҳо хеле хуб кор мекунанд, зеро онҳо дархостҳои SQL-ро табдил медиҳанд ( Ман дар бораи чӣ гуна кор кардани ин дар сӯҳбати охирини худ дар Voxxed Days дар Сюрих сӯҳбат кардам ). Масалан, табдor бениҳоят пурқуввати "истиснои JOIN" вуҷуд дорад. Намоиши ёрирасони зеринро баррасӣ кунед, ки мо бояд онро эҷод кунем, то ҳар дафъа дастӣ ба ҳамаи ин ҷадвалҳо ҳамроҳ нашавем:
CREATE VIEW v_customer AS
SELECT
  c.first_name, c.last_name,
  a.address, ci.city, co.country
FROM customer c
JOIN address a USING (address_id)
JOIN city ci USING (city_id)
JOIN country co USING (country_id)
Ин намуди зоҳирӣ танҳо ҳамаи пайвастҳои "...-ба-як"-ро байни ҷадвали муштарии МАРЗО ва ҷадвалҳои гуногун барои қисмҳои суроғаи онҳо анҷом медиҳад. Ташаккур, нормализатсия. Тасаввур кунед, ки пас аз каме кор кардан бо ин манзара мо ба он одат кардаем ва мизҳои зери онро фаромӯш кардем. Ва ҳоло мо дархости зеринро иҷро мекунем:
SELECT *
FROM v_customer
Дар натиҷа, мо нақшаи хеле таъсирбахш мегирем:
----------------------------------------------------------------
| Id  | Operation            | Name     | Rows  | Bytes | Cost |
----------------------------------------------------------------
|   0 | SELECT STATEMENT     |          |   599 | 47920 |   14 |
|*  1 |  HASH JOIN           |          |   599 | 47920 |   14 |
|   2 |   TABLE ACCESS FULL  | COUNTRY  |   109 |  1526 |    2 |
|*  3 |   HASH JOIN          |          |   599 | 39534 |   11 |
|   4 |    TABLE ACCESS FULL | CITY     |   600 | 10800 |    3 |
|*  5 |    HASH JOIN         |          |   599 | 28752 |    8 |
|   6 |     TABLE ACCESS FULL| CUSTOMER |   599 | 11381 |    4 |
|   7 |     TABLE ACCESS FULL| ADDRESS  |   603 | 17487 |    3 |
----------------------------------------------------------------
Хуб, албатта. Пойгоҳи додаҳо ҳамаи ин пайвастшавӣ ва сканҳои пурраи ҷадвалро иҷро мекунад, зеро он чизест, ки мо ба он гуфта будем - ҳамаи ин маълумотро гиред. Акнун, бори дигар, тасаввур кунед, ки ба мо танҳо ин лозим буд:
Масъалаҳои иҷрои SQL аз ҷониби
Чӣ, ҷиддӣ, дуруст? Акнун шумо фаҳмидед, ки ман дар бораи чӣ гап мезанам. Аммо тасаввур кунед, ки мо аз хатогиҳои гузашта чизе омӯхтем ва ин пурсиши беҳтаринро иҷро кунед:
SELECT first_name, last_name
FROM v_customer
Акнун биёед тафтиш кунем, ки чӣ рӯй дод!
------------------------------------------------------------------
| Id  | Operation          | Name        | Rows  | Bytes | Cost  |
------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |   599 | 16173 |     4 |
|   1 |  NESTED LOOPS      |             |   599 | 16173 |     4 |
|   2 |   TABLE ACCESS FULL| CUSTOMER    |   599 | 11381 |     4 |
|*  3 |   INDEX UNIQUE SCAN| SYS_C007120 |     1 |     8 |     0 |
------------------------------------------------------------------
Дигаргунихои катъй ба суи бехтар аз чихати ичрои. Пайвастшавӣ нест карда шуд, зеро оптимизатор ҳоло метавонад бубинад, ки онҳо бефоидаанд ва агар он бубинад (ва шумо ин корро бо интихоби * ҳатмӣ накардаед), пас он наметавонад ҳама корҳоро иҷро кунад. Чаро дар ин ҳолат ин тавр аст? Калиди хориҷии CUSTOMER.ADDRESS_ID ба калиди ибтидоии ADDRESS.ADDRESS_ID маҳз як арзиши охиринро кафолат медиҳад, ки ин маънои онро дорад, ки амалиёти JOIN пайвастани "...-ба як" хоҳад буд, ки шумораи сатрҳоро зиёд ё кам намекунад . Ва азбаски мо ягон сатрро тамоман интихоб намекунем ё дархост намекунем, пас бор кардани онҳо умуман маъно надорад. Хориҷ кардани JOIN эҳтимолан ба натиҷаи пурсиш умуман таъсир нарасонад. Пойгоҳи додаҳо ҳама вақт ин корро мекунанд. Шумо метавонед дархости зеринро қариб дар ҳама гуна пойгоҳи додаҳо иҷро кунед:
-- Oracle
SELECT CASE WHEN EXISTS (
  SELECT 1 / 0 FROM dual
) THEN 1 ELSE 0 END
FROM dual

-- Более адекватные диалекты SQL, например, PostgreSQL
SELECT EXISTS (SELECT 1 / 0)
Дар ин ҳолат, шумо метавонед интизор шавед, ки истиснои арифметикӣ ҳангоми иҷрои дархости зерин партофта шавад:
SELECT 1 / 0 FROM dual

Рӯй дод:


ORA-01476: тақсимкунанда ба сифр баробар аст

Аммо ин тавр намешавад. Оптимизатор (ё ҳатто таҳлилгар) метавонад кафолат диҳад, ки ягон унсури рӯйхати интихобшуда дар предикати EXISTS (SELECT ..) натиҷаи дархостро тағир надиҳад, аз ин рӯ зарурати иҷрои он вуҷуд надорад. Монанди ин!

Дар ҳамин ҳол...

Яке аз мушкилоти дилгиркунанда бо ORMҳо дар он аст, ки онҳо навиштани дархостҳои SELECT * хеле осонанд. Дар асл, масалан, дар HQL / JPQL онҳо одатан бо нобаёнӣ истифода мешаванд. Мо метавонем банди SELECT-ро тамоман аз даст диҳем, зеро мо тамоми an objectро гирифтан хоҳем кард, дуруст? Барои намуна:
FROM v_customer
Масалан, Влад Михалчеа, коршинос ва ҳимоятгари таҳия бо Hibernate , тавсия медиҳад, ки пурсишҳои [тахассусӣ] қариб ҳамеша ҳангоми мутмаин бошед, ки шумо пас аз ҳисоб ягон тағиротро захира кардан намехоҳед. ОРМ-хо халли масъалаи устувории графикхои an objectиро хеле осон мекунанд. Эзоҳ: устуворӣ. Вазифаҳои воқеан тағир додани графикҳои an objectӣ ва захира кардани тағирот ба таври ногусастанӣ алоқаманданд. Аммо агар шумо ин корро нахоҳед кард, пас чаро барои истихроҷи моҳият ташвиш медиҳед? Чаро дархости [тозашуда] нанависед? Биёед возеҳ бошем: аз нуқтаи назари иҷроиш, навиштани дархосте, ки ба ҳолати мушаххаси истифодаи шумо мутобиқ карда шудааст, бешубҳа беҳтар аз ҳама имконоти дигар аст. Шояд шумо парво надоред, зеро маҷмӯи маълумоти шумо хурд аст ва муҳим нест. бузург. Аммо вақте ки шумо ниҳоят ба миқёспазирӣ ниёз доред, тарҳрезии замимаҳои худро барои истифодаи дархостҳо ба ҷои гузариши ҳатмӣ аз диаграммаи an object хеле душвор хоҳад кард. Ва шумо бе он коре хоҳед дошт.

Ҳисоб кардани сатрҳо барои фаҳмидани он ки чизе вуҷуд дорад

Яке аз бадтарин партовҳои захираҳо иҷро кардани COUNT(*) дархостҳост, то бубинад, ки оё чизе дар пойгоҳи додаҳо мавҷуд аст. Масалан, мо бояд фаҳмем, ки оё корбари додашуда умуман фармоиш дорад. Ва мо дархостро иҷро мекунем:
SELECT count(*)
FROM orders
WHERE user_id = :user_id
ибтидоӣ. Агар COUNT = 0 бошад, пас фармоишҳо вуҷуд надоранд. Дар акси ҳол, ҳа. Намоиш он қадар бад нахоҳад буд, зеро мо эҳтимолан дар сутуни ORDERS.USER_ID индекс дошта бошем. Аммо шумо фикр мекунед, ки иҷрои дархости дар боло овардашуда бо варианти зерин муқоиса карда мешавад:
-- Oracle
SELECT CASE WHEN EXISTS (
  SELECT *
  FROM orders
  WHERE user_id = :user_id
) THEN 1 ELSE 0 END
FROM dual

-- Более адекватные диалекты SQL, например, PostgreSQL
SELECT EXISTS (
  SELECT *
  FROM orders
  WHERE user_id = :user_id
)
Барои фаҳмидани он, ки предикати мавҷудияти ҳақиқӣ баробари пайдо кардани сатрҳои аввал аз ҷустуҷӯи сатрҳои иловагӣ қатъ мешавад, як олими мушак лозим нест . Пас, агар натиҷа "бе фармоиш" бошад, суръат муқоисашаванда хоҳад буд. Аммо, агар натиҷа «ҳа, фармоишҳо ҳастанд», пас дар ҳолате, ки миқдори дақиқро ҳисоб кардан лозим нест, ҷавоб хеле зудтар қабул карда мешавад. Охир, мо ба ракамдои дакик манфиатдор нестем. Аммо, мо ба базаи маълумот гуфтем, ки онро ҳисоб кунад ( кори нолозим ) ва базаи маълумот намедонад, ки мо ҳамаи натиҷаҳои аз 1 калонтарро нодида мегирем ( кори лозим ). Албатта, агар мо list.size()-ро дар коллексияи аз ҷониби JPA дастгирӣшуда даъват кунем, барои ба даст овардани ҳамон натиҷаҳо хеле бадтар мебуд. Ман қаблан дар ин бора дар блоги худ навишта будам ва санҷиши муқоисавии ҳарду вариантро дар...

Хулоса

Дар ин мақола маълум аст. Махзани маълумотро маҷбур накунед, ки кори нолозим, вале заруриро анҷом диҳад . Ин нолозим аст , зеро бо назардошти талабот, шумо медонед, ки ягон кори мушаххас лозим нест. Бо вуҷуди ин, шумо ба базаи маълумот мегӯед, ки ин корро кунад. Ин талаб карда мешавад, зеро барои махзани маълумот ҳеҷ роҳе вуҷуд надорад, ки ин кор нолозим аст . Ин маълумот танҳо барои муштарӣ дастрас аст ва ба server дастрас нест. Пас, базаи маълумот бояд онро иҷро кунад. Мақола ба SELECT * тамаркуз кардааст, зеро он an objectи қулай барои дидан аст. Аммо ин на танҳо ба пойгоҳи додаҳо дахл дорад. Ин ба ҳамаи алгоритмҳои тақсимшуда дахл дорад, ки дар онҳо муштарӣ ба server мегӯяд, ки кори нодаркор, вале заруриро иҷро кунад . Дар барномаи миёнаи AngularJS-и шумо чанд вазифаи N+1 мавҷуд аст, ки дар он UI тавассути натиҷаи хидмати А, ба хидмати B чанд маротиба занг мезанад, ба ҷои бастабандии ҳама зангҳо ба B ба як занг? Ин як падидаи хеле маъмул аст. Ҳалли масъала ҳамеша як аст. Чӣ қадаре ки шумо ба an objectе, ки фармонҳои шуморо иҷро мекунад, маълумоти бештар диҳед, он (аз ҷиҳати назариявӣ) ин фармонҳоро ҳамон қадар тезтар иҷро мекунад. Саволҳои оптималиро нависед. Ҳамеша. Тамоми системаи шумо барои ин ба шумо ташаккур хоҳад кард. Мақолаи аслӣ
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION