Kërkesat paralele në PostgreSQL

Kërkesat paralele në PostgreSQL
Në procesorët modernë ka shumë bërthama. Për vite me radhë, aplikacionet dërgojnë kërkesa në bazat e të dhënave në mënyrë paralele. Nëse ky është një kërkesë raporti për shumë rreshta në tabelë, ajo ekzekutohet më shpejt kur angazhon disa procesorë, dhe në PostgreSQL kjo është e mundur që nga versioni 9.6.

Kërkoi 3 vite për të realizuar funksionin e kërkesave paralele — duhej të ri-shkruhej kodi në faza të ndryshme të ekzekutimit të kërkesave. Në PostgreSQL 9.6 u krijua infrastruktura për përmirësimin e mëtejshëm të kodit. Në versionet pasuese, dhe lloje të tjera kërkesash ekzekutohen në mënyrë paralele.

Kufizimet

  • Mos aktivizoni ekzekutimin paralel nëse të gjitha bërthamën janë të zëna, përndryshe kërkesat e tjera do të ngadalësohen.
  • E rëndësishme, përpunimi paralel me vlera të larta të WORK_MEM angazhon shumë memorie — çdo lidhje hëzho ose renditje konsumon memorie në volum të work_mem.
  • Kërkesat OLTP me vonesë të ulët nuk mund të përshpejtohen me ekzekutimin paralel. Nëse një kërkesë kthen një rresht, përpunimi paralel vetëm e ngadalëson atë.
  • Zhvilluesve u pëlqen të përdorin benchmark TPC-H. Ndoshta keni kërkesa të ngjashme për ekzekutimin e përsosur paralel.
  • Vetëm kërkesat SELECT pa bllokim predikativ ekzekutohen në mënyrë paralele.
  • Nganjëherë, indeksimi i saktë është më mirë se skanimi sekondar në mënyrë paralele.
  • Pejsazhet e kërkesave dhe kursori nuk mbështeten.
  • Funksionet e dritareve dhe funksionet agregate të grupeve të renditura nuk janë paralele.
  • Nuk fitoni asgjë në ngarkesën e I/O.
  • Nuk ka algorithma të paralelizuara të renditjes. Por kërkesat me renditje mund të ekzekutohen paralelisht në disa aspekte.
  • Zëvendëso CTE (ME …) me një SELECT të ngulitur për të përfshirë përpunimin paralel.
  • Përmbajtjet e të dhënave të jashtme ende nuk mbështesin përpunimin paralel (edhe pse mund të mbështeteshin!)
  • FULL OUTER JOIN nuk mbështetet.
  • max_rows çaktivizon përpunimin paralel.
  • Nëse kërkesa ka një funksion, i cili nuk është e shënuar si PARALLEL SAFE, ajo do të jetë një-kohore.
  • Niveli i izolimit të transaksionit SERIALIZABLE çaktivizon përpunimin paralel.

Mjedisi i testimit

Zhvilluesit e PostgreSQL u përpoqën të pakësonin kohën e përgjigjes për kërkesat e benchmark-ut TPC-H. Shkarko benchmark-un dhe e adaptoje atë për PostgreSQL. Ky është një përdorim jozyrtar i benchmark-ut TPC-H — jo për të krahasuar bazat e të dhënave ose pajisjet.

  1. Shkarko TPC-H_Tools_v2.17.3.zip (apo versionin më të ri) nga faqja zyrtare TPC.
  2. Rivendos makefile.suite në Makefile dhe ndrysho si është përshkruar këtu: https://github.com/tvondra/pg_tpch . Kompliko kodin me komandën make.
  3. Gjenere të dhënat: ./dbgen -s 10 krijon një bazë të dhënash prej 23 GB. Kjo do të mjaftojë për të parë diferencën në performancën e kërkesave paralele dhe jo-paralele.
  4. Konverto skedarët tblcsv me for dhe sed.
  5. Klononi depot pg_tpch dhe kopjo skedarët csvpg_tpch/dss/data.
  6. Krijo kërkesat me komandën qgen.
  7. Ngarko të dhënat në bazë me komandën ./tpch.sh.

Skanimi paralel sekondar

Ai mund të jetë më i shpejtë jo nga leximi paralel, por sepse të dhënat janë shpërndara në shumë bërthama të procesorit. Në sistemet operuese moderne, skedarët e të dhënave PostgreSQL janë mirë të ruajtur në cache. Me leximin parashikues, mund të merrni nga ruajtja një bllok më shumë se sa kërkon demon PG. Prandaj, performanca e kërkesës nuk kufizohet nga I/O e diskut. Ai konsumon cikle të procesorit për të:

  • lexuar rreshta një nga një nga faqet e tabelës;
  • krahason vlerat e rreshtave dhe kushtet WHERE.

Ekzekuto një kërkesë të thjeshtë select:

tpch=# explain analyze select l_quantity as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Skanimi Sekondar mbi lineitem (cost=0.00..1964772.00 rows=58856235 width=5) (actual time=0.014..16951.669 rows=58839715 loops=1)
Filtri: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rreshtat e hequra nga Filtri: 1146337
Koha e Planifikimit: 0.203 ms
Koha e Ekzekutimit: 19035.100 ms

Skanimi sekondar jep shumë rreshta pa agregim, kështu që kërkesa ekzekutohet me një bërthamë procesori.

Nëse shtoni SUM(), është e qartë se dy procese ndihmës do të ndihmojnë për të përshpejtuar kërkesën:

explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
Përfundimi i Agregimit (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)
-> Grumbull (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)
Punëtorë të Planifikuar: 2
Punëtorë të Lançuar: 2
-> Agregimi i Pjesës (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)
-> Skanimi Sekondar Paralel mbi lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)
Filtri: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rreshtat e hequra nga Filtri: 382112
Koha e Planifikimit: 0.241 ms
Koha e Ekzekutimit: 8555.131 ms

Agregimi Paralel

Noda 'Skanimi Sekondar Paralel' prodhon rreshta për agregimin e pjesëve. Noda 'Agregimi i Pjesës' i zvogëlon këto rreshta me SUM(). Në fund, numri i SUM nga çdo proces ndihmës grumbullohet nga noda 'Grumbull'.

Rezultati përfundimtar llogaritet nga nodi 'Finalize Aggregate'. Nëse keni funksione të tua agregimi, sigurohuni që t'i shënoni si 'parallel safe'.

Numri i proceseve punuese

Numri i proceseve punuese mund të rritet pa e rinisur serverin:

explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
Përfundimi i Agregimit (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)
-> Grumbull (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)
Punëtorë të Planifikuar: 2
Punëtorë të Lançuar: 2
-> Agregimi i Pjesës (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)
-> Skanimi Sekondar Paralel mbi lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)
Filtri: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rreshtat e hequra nga Filtri: 382112
Koha e Planifikimit: 0.241 ms
Koha e Ekzekutimit: 8555.131 ms

Çfarë po ndodh këtu? Numri i proceseve punuese është dyfishuar, ndërsa kërkesa u bë vetëm 1,6599 herë më e shpejtë. Llogaritjet janë interesante. Kishim 2 procese punuese dhe 1 lider. Pas ndryshimit, u bë 4+1.

Shpejtësia maksimale që arrijmë nga përpunimi paralel: 5/3 = 1,66(6) herë.

Si funksionon?

Proceset

Ekzekutimi i kërkesës fillon gjithmonë me procesin drejtues. Lideri bën të gjitha proceset jo-paralel dhe një pjesë të përpunimit paralel. Proceset e tjera që ekzekutojnë të njëjtat kërkesa quhen procese punuese. Përpunimi paralel përdor infrastrukturën e proceseve punuese dinamike në sfond (nga versioni 9.4). Pasi pjesët e tjera të PostgreSQL përdorin procese dhe jo fijet, një kërkesë me 3 procese punuese mund të jetë 4 herë më e shpejtë se përpunimi tradicional.

Ndërveprimi

Proceset punuese komunikojnë me liderin përmes një rrethi mesazhe (bazuar në memorie të përbashkët). Çdo proces ka 2 rreth mesazhesh: për gabime dhe për tuple.

Sa procese punuese nevojiten?

Kufizimi minimal caktohet nga parametri max_parallel_workers_per_gather. Pastaj ekzekutori i kërkesave merr proceset punuese nga puli, i kufizuar nga parametri max_parallel_workers size. Kufizimi i fundit është max_worker_processes, pra numri total i proceseve të sfondit.

Nëse nuk arrihet ndarja e një procesi punues, përpunimi do të jetë me një proces.

Planifikuesi i kërkesave mund të zvogëlojë proceset punuese në varësi të madhësisë së tabelës ose indekseve. Për këtë ka parametra min_parallel_table_scan_size dhe min_parallel_index_scan_size.

set min_parallel_table_scan_size='8MB'
8MB tabelë => 1 punonjës
24MB tabelë => 2 punonjës
72MB tabelë => 3 punonjës
x => log(x / min_parallel_table_scan_size) / log(3) + 1 punonjës

Çdo herë që tabela është 3 herë më e madhe se min_parallel_(index|table)_scan_size, Postgres shton një proces punues. Numri i proceseve punuese nuk bazohet në kosto. Varësia e rrethit e vështirëson realizimet e komplikuara. Në vend të kësaj, planifikuesi përdor rregulla të thjeshta.

Në praktikë, këto rregulla nuk janë gjithmonë të përshtatshme për prodhim, kështu që është e mundur të ndryshohet numri i proceseve punuese për një tabelë specifike: ALTER TABLE … SET (parallel_workers = N).

Pse nuk përdoret përpunimi paralel?

Përveç listës së gjatë të kufizimeve ka edhe kontrollime kostoje:

parallel_setup_cost — për të shmangur përpunimin paralel të kërkesave të shkurtra. Ky parametr koston kohën për të përgatitur memorien, për të nisur procesin dhe për shkëmbimin fillestar të të dhënave.

parallel_tuple_cost: komunikimi midis liderit dhe punonjësve mund të zgjasë në proporcioni me numrin e tupleve nga proceset punuese. Ky parametr llogarit kostot për shkëmbimin e të dhënave.

Bashkimet e cikleve të thella — Nested Loop Join

PostgreSQL 9.6+ может выполнять вложенные циклы параллельно — это простая операция.

explain (costs off) select c_custkey, count(o_orderkey)
                from    customer left outer join orders on
                                c_custkey = o_custkey and o_comment not like '%special%deposits%'
                group by c_custkey;
                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Finalize GroupAggregate
   Group Key: customer.c_custkey
   ->  Gather Merge
         Workers Planned: 4
         ->  Partial GroupAggregate
               Group Key: customer.c_custkey
               ->  Nested Loop Left Join
                     ->  Parallel Index Only Scan using customer_pkey on customer
                     ->  Index Scan using idx_orders_custkey on orders
                           Index Cond: (customer.c_custkey = o_custkey)
                           Filter: ((o_comment)::text !~~ '%special%deposits%'::text)

Grupi formohet në fazën e fundit, kështu që Nested Loop Left Join është një operacion paralel. Parallel Index Only Scan është paraqitur vetëm në versionin 10. Ai funksionon në mënyrë të ngjashme me skanimin paralel sekondar. Kushti c_custkey = o_custkey lexon një rend nga çdo rresht të klientit. Pra, ai nuk është paralel.

Bashkim me Hash — Hash Join

Çdo proces punues krijon tabelën e tij të hash deri në PostgreSQL 11. Dhe nëse këto procese janë më shumë se katër, performanca nuk përmirësohet. Në versionin e ri, tabela e hash-it është e përbashkët. Çdo proces punues mund të përdorë WORK_MEM për të krijuar një tabelë hash.

select
        l_shipmode,
        sum(case
                when o_orderpriority = '1-URGENT'
                        or o_orderpriority = '2-HIGH'
                        then 1
                else 0
        end) as high_line_count,
        sum(case
                when o_orderpriority  '1-URGENT'
                        and o_orderpriority  '2-HIGH'
                        then 1
                else 0
        end) as low_line_count
from
        orders,
        lineitem
where
        o_orderkey = l_orderkey
        and l_shipmode in ('MAIL', 'AIR')
        and l_commitdate < l_receiptdate
        and l_shipdate = date '1996-01-01'
        and l_receiptdate   Finalize GroupAggregate  (cost=1964755.66..1966196.11 rows=7 width=27) (actual time=7579.590..7579.591 rows=1 loops=1)
         Group Key: lineitem.l_shipmode
         ->  Gather Merge  (cost=1964755.66..1966195.83 rows=28 width=27) (actual time=7559.593..7922.319 rows=6 loops=1)
               Workers Planned: 4
               Workers Launched: 4
               ->  Partial GroupAggregate  (cost=1963755.61..1965192.44 rows=7 width=27) (actual time=7548.103..7564.592 rows=2 loops=5)
                     Group Key: lineitem.l_shipmode
                     ->  Sort  (cost=1963755.61..1963935.20 rows=71838 width=27) (actual time=7530.280..7539.688 rows=62519 loops=5)
                           Sort Key: lineitem.l_shipmode
                           Sort Method: external merge  Disk: 2304kB
                           Worker 0:  Sort Method: external merge  Disk: 2064kB
                           Worker 1:  Sort Method: external merge  Disk: 2384kB
                           Worker 2:  Sort Method: external merge  Disk: 2264kB
                           Worker 3:  Sort Method: external merge  Disk: 2336kB
                           ->  Parallel Hash Join  (cost=382571.01..1957960.99 rows=71838 width=27) (actual time=7036.917..7499.692 rows=62519 loops=5)
                                 Hash Cond: (lineitem.l_orderkey = orders.o_orderkey)
                                 ->  Parallel Seq Scan on lineitem  (cost=0.00..1552386.40 rows=71838 width=19) (actual time=0.583..4901.063 rows=62519 loops=5)
                                       Filter: ((l_shipmode = ANY ('{MAIL,AIR}'::bpchar[])) AND (l_commitdate < l_receiptdate) AND (l_shipdate = '1996-01-01'::date) AND (l_receiptdate   Parallel Hash  (cost=313722.45..313722.45 rows=3750045 width=20) (actual time=2011.518..2011.518 rows=3000000 loops=5)
                                       Buckets: 65536  Batches: 256  Memory Usage: 3840kB
                                       ->  Parallel Seq Scan on orders  (cost=0.00..313722.45 rows=3750045 width=20) (actual time=0.029..995.948 rows=3000000 loops=5)
 Planning Time: 0.977 ms
 Execution Time: 7923.770 ms

Kërkesa 12 nga TPC-H ilustron qartë bashkimin e realizuar me hash në mënyrë të paralel. Çdo proces punëtor merr pjesë në krijimin e një tabele hash të përbashkët.

Bashkimi me shkrirje — Merge Join

Bashkimi me shkrirje ka karakteristika jo-paralele. Mos u shqetësoni nëse ky është hapi i fundit i kërkesës, — ai mund të ekzekutohet gjithësesi në mënyrë paralele.

-- Kërkesa 2 nga TPC-H
shpjego (kostot jashtë) përzgjedh s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment
nga    part, supplier, partsupp, nation, region
ku
        p_partkey = ps_partkey
        dhe s_suppkey = ps_suppkey
        dhe p_size = 36
        dhe p_type si '%BRASS'
        dhe s_nationkey = n_nationkey
        dhe n_regionkey = r_regionkey
        dhe r_name = 'AMERIKA'
        dhe ps_supplycost = (
                përzgjedh
                        min(ps_supplycost)
                nga    partsupp, supplier, nation, region
                ku
                        p_partkey = ps_partkey
                        dhe s_suppkey = ps_suppkey
                        dhe s_nationkey = n_nationkey
                        dhe n_regionkey = r_regionkey
                        dhe r_name = 'AMERIKA'
        )
rendit sipas s_acctbal zbritës, n_name, s_name, p_partkey
LIMIT 100;
                                                PLANNA E KËRKESËS
----------------------------------------------------------------------------------------------------------
 Limit
   -&gt;  Rendit
         Çelësi i Rendit: supplier.s_acctbal ZBRITËS, nation.n_name, supplier.s_name, part.p_partkey
         -&gt;  Bashkimi i Bashkëve
               Kushti i Bashkimit: (part.p_partkey = partsupp.ps_partkey)
               Filtri i Bashkimit: (partsupp.ps_supplycost = (SubPlani 1))
               -&gt;  Mbledhja e Bashkimeve
                     Punëtorë të Planifikuar: 4
                     -&gt;  Skemi Paralele e Indeksit duke përdorur <strong>part_pkey</strong> në pjesë
                           Filtri: (((p_type)::text ~~ '%BRASS'::text) DHE (p_size = 36))
               -&gt;  Materializo
                     -&gt;  Rendit
                           Çelësi i Rendit: partsupp.ps_partkey
                           -&gt;  Cikli i Foleve
                                 -&gt;  Cikli i Foleve
                                       Filtri i Bashkimit: (nation.n_regionkey = region.r_regionkey)
                                       -&gt;  Skemi Sekuencial në region
                                             Filtri: (r_name = 'AMERIKA'::bpchar)
                                       -&gt;  Bashkimi i Hash
                                             Kushti i Hash: (supplier.s_nationkey = nation.n_nationkey)
                                             -&gt;  Skemi Sekuencial në supplier
                                             -&gt;  Hash
                                                   -&gt;  Skemi Sekuencial në nation
                                 -&gt;  Skemi Indeksi duke përdorur idx_partsupp_suppkey në partsupp
                                       Kushti i Indeksit: (ps_suppkey = supplier.s_suppkey)
               SubPlani 1
                 -&gt;  Agregati
                       -&gt;  Cikli i Foleve
                             Filtri i Bashkimit: (nation_1.n_regionkey = region_1.r_regionkey)
                             -&gt;  Skemi Sekuencial në region region_1
                                   Filtri: (r_name = 'AMERIKA'::bpchar)
                             -&gt;  Cikli i Foleve
                                   -&gt;  Cikli i Foleve
                                         -&gt;  Skemi Indeksi duke përdorur idx_partsupp_partkey në partsupp partsupp_1
                                               Kushti i Indeksit: (part.p_partkey = ps_partkey)
                                         -&gt;  Skemi Indeksi duke përdorur supplier_pkey në supplier supplier_1
                                               Kushti i Indeksit: (s_suppkey = partsupp_1.ps_suppkey)
                                   -&gt;  Skemi Indeksi duke përdorur nation_pkey në nation nation_1
                                         Kushti i Indeksit: (n_nationkey = supplier_1.s_nationkey)

Noda «Merge Join» ndodhet mbi «Gather Merge». Pra, bashkimi nuk përdor përpunim paralel. Por nodi «Parallel Index Scan» ende ndihmon me segmentin. part_pkey.

Bashkimi sipas seksioneve

Në PostgreSQL 11 bashkimi sipas seksioneve çaktivizuar në mënyrë të parazgjedhur: ka një planifikim shumë të shtrenjtë. Tabelat me seksionim të ngjashëm mund të bashkohen seksion pas seksioni. Kështu, Postgres do të përdorë tabela hesh të vogla. Çdo bashkim seksionesh mund të jetë paralel.

tpch=# set enable_partitionwise_join=t;
tpch=# explain (costs off) select * from prt1 t1, prt2 t2
where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;
                    PLANI I KËRQESJES
---------------------------------------------------
 Shtesë
   ->  Bashkimi Hash
         Kusht Hash: (t2.b = t1.a)
         ->  Skanim Sekuencial në prt2_p1 t2
               Filtri: ((b >= 0) DHE (b <= 10000))
         ->  Hesh
               ->  Skanim Sekuencial në prt1_p1 t1
                     Filtri: (b = 0)
   ->  Bashkimi Hash
         Kusht Hash: (t2_1.b = t1_1.a)
         ->  Skanim Sekuencial në prt2_p2 t2_1
               Filtri: ((b >= 0) DHE (b <= 10000))
         ->  Hesh
               ->  Skanim Sekuencial në prt1_p2 t1_1
                     Filtri: (b = 0)
tpch=# set parallel_setup_cost = 1;
tpch=# set parallel_tuple_cost = 0.01;
tpch=# explain (costs off) select * from prt1 t1, prt2 t2
where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;
                        PLANI I KËRQESJES
-----------------------------------------------------------
 Mblidhen
   Punonjësit e Planifikuar: 4
   ->  Shtesë Paralel
         ->  Bashkimi Hash Paralel
               Kusht Hash: (t2_1.b = t1_1.a)
               ->  Skanim Sekuencial Paralel në prt2_p2 t2_1
                     Filtri: ((b >= 0) DHE (b <= 10000))
               ->  Hesh Paralel
                     ->  Skanim Sekuencial Paralel në prt1_p2 t1_1
                           Filtri: (b = 0)
         ->  Bashkimi Hash Paralel
               Kusht Hash: (t2.b = t1.a)
               ->  Skanim Sekuencial Paralel në prt2_p1 t2
                     Filtri: ((b >= 0) DHE (b <= 10000))
               ->  Hesh Paralel
                     ->  Skanim Sekuencial Paralel në prt1_p1 t1
                           Filtri: (b = 0)

E rëndësishme, bashkimi sipas seksioneve është paralel vetëm nëse këto seksione janë mjaft të mëdha.

Shtesë Paralel — Parallel Append

Shtesë Paralel mund të përdoret në vend të blloqeve të ndryshme në procese të ndryshme. Zakonisht ndodh kjo me kërkesat UNION ALL. Disavantazhi — më pak paralelizëm, pasi çdo proces përpunon vetëm 1 kërkesë.

Këtu janë aktivizuar 2 procese, ndonëse janë planifikuar 4.

tpch=# explain (costs off) select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day union all select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '2000-12-01' - interval '105' day;
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Gather
   Workers Planned: 2
   ->  Parallel Append
         ->  Aggregate
               ->  Seq Scan on lineitem
                     Filter: (l_shipdate <= '2000-08-18 00:00:00'::timestamp without time zone)
         ->  Aggregate
               ->  Seq Scan on lineitem lineitem_1
                     Filter: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)

Variablat më të rëndësishme

  • WORK_MEM kufizon sasinë e memories për çdo proces, jo vetëm për kërkesat: work_mem proceset konneksionet = shumë memoria.
  • max_parallel_workers_per_gather — sa shumë procese punuese do të përdorë programi për procesimin paralel nga plani.
  • max_worker_processes — përshtat numrin total të proceseve punuese me numrin e bërthamave të CPU në server.
  • max_parallel_workers — e njëjta, por për proceset punuese paralel.

Përfundimet

Që nga versioni 9.6, procesimi paralel mund të përmirësojë ndjeshëm performancën e kërkesave të komplikuara që skanojnë shumë rreshta ose indekse. Në PostgreSQL 10 procesimi paralel është aktivizuar si parazgjedhje. Mos harroni ta çaktivizoni në serverët me ngarkesë të lartë OLTP. Skanat sekondar ose skanat e indekseve konsumojnë shumë burime. Nëse nuk po kryeni një raport mbi të gjithë grumbullin e të dhënave, kërkesat mund të bëhen më efikase thjesht duke shtuar indekset që mungojnë ose duke përdorur ndarjen e duhur.

Linke

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster