PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Преглед на кеширането за PostgreSQL

Повечето OLTP работни натоварвания включват произволно използване на I/O диск. Знаейки, че дисковете (включително SSD) са с по-бавна производителност от използването на RAM, системите за бази данни използват кеширане, за да увеличат производителността. Кеширането е свързано с съхраняване на данни в паметта (RAM) за по-бърз достъп в по-късен момент.

PostgreSQL също така използва кеширане на своите данни в пространство, наречено shared_buffers. В този блог ще разгледаме тази функционалност, за да ви помогнем да увеличите производителността.

Основи на кеширането на PostgreSQL

Преди да навлезем по-задълбочено в концепцията за кеширане, нека да разгледаме основите.

В PostgreSQL данните са организирани под формата на страници с размер 8KB и всяка такава страница може да съдържа множество кортежи (в зависимост от размера на кортежа). Опростеното представяне може да бъде като по-долу:

PostgreSQL кешира следното за ускоряване на достъпа до данни:

  • Данни в таблици
  • Индекси
  • Планове за изпълнение на заявка

Докато фокусът на кеширането на плана за изпълнение на заявката е върху запазването на цикли на процесора; Кеширането за данни от таблици и данни за индекси е насочено към спестяване на скъпоструващи операции за вход/изход на диска.

PostgreSQL позволява на потребителите да определят колко памет биха искали да резервират за запазване на такъв кеш за данни. Съответната настройка е shared_buffers в конфигурационния файл postgresql.conf. Крайната стойност на shared_buffers определя колко страници могат да бъдат кеширани във всеки един момент от време.

При изпълнение на заявка PostgreSQL търси страницата на диска, която съдържа съответния кортеж, и я избутва в кеша shared_buffers за страничен достъп. Следващия път, когато трябва да се осъществи достъп до същия кортеж (или който и да е кортеж на същата страница), PostgreSQL може да запази дисково IO, като го прочете в паметта.

На горната фигура страница-1 и страница-2 на определен таблицата са кеширани. В случай, че потребителска заявка трябва да получи достъп до кортежи между Tuple-1 до Tuple-200, PostgreSQL може да го извлече от самата RAM.

Ако обаче заявката трябва да получи достъп до кортежи от 250 до 350, тя ще трябва да извърши дисков I/O за страница 3 и страница 4.  Всеки по-нататъшен достъп за кортеж 201 до 400 ще бъде извлечен от кеша и дисков вход/изход няма да е необходим – по този начин заявката става по-бърза.

На високо ниво PostgreSQL следва LRU (най-малко използван) алгоритъм за идентифициране на страниците, които трябва да бъдат извадени от кеша. С други думи, страница, която е достъпна само веднъж, има по-високи шансове за изгонване (в сравнение със страница, която е достъпна многократно), в случай че нова страница трябва да бъде извлечена от PostgreSQL в кеша.

Кеширането на PostgreSQL в действие

Нека изпълним пример и да видим влиянието на кеша върху производителността.

Стартирайте PostgreSQL, като задържате shared_buffer на 128 MB по подразбиране

$ initdb -D ${HOME}/data

$ echo “shared_buffers=128MB” >> ${HOME}/data/postgresql.conf

$ pg_ctl -D ${HOME}/data start

Свържете се със сървъра и създайте фиктивна таблица tblDummy и индекс на c_id

CREATE Table tblDummy

(

id serial primary key,

p_id int,

c_id int,

entry_time timestamp,

entry_value int,

description varchar(50)  

);

CREATE INDEX ON tblDummy(c_id );

Попълнете фиктивни данни с 200 000 кортежи, така че да има 10 000 уникални p_id и за всеки p_id да има 200 c_id 

DO $$

DECLARE

random_value integer:= 1;

BEGIN

FOR p_id_ctr IN 1..10000 BY 1 LOOP               

FOR c_id_ctr IN 1..200 BY 1 LOOP                                 

random_value = (( random() * 75 ) + 25);

INSERT INTO tblDummy (p_id,c_id,entry_time, entry_value, description )

VALUES (p_id_ctr,c_id_ctr,'now', random_value, CONCAT('Description for :',p_id_ctr, c_id_ctr));

END LOOP ;

END LOOP ;                      

END $$;

Рестартирайте сървъра, за да изчистите кеша. Сега изпълнете заявка и проверете за времето, необходимо за изпълнение на същото

SELECT pg_stat_reset();

EXPLAIN ANAYZE SELECT count(*) from tbldummy where c_id=1;



                           QUERY PLAN

--------------------------------------------------------------

 Aggregate  (cost=17407.33..17407.34 rows=1 width=8) (actual time=160.269..160.269 rows=1 loops=1)

   ->  Bitmap Heap Scan on tbldummy  (cost=189.52..17382.46 rows=9948 width=0) (actual time=10.627..156.275 rows=10000 loops=1)

         Recheck Cond: (c_id = 1)

         Heap Blocks: exact=10000

         ->  Bitmap Index Scan on tbldummy_c_id_idx  (cost=0.00..187.04 rows=9948 width=0) (actual time=5.091..5.091 rows=10000 loops=1)

               Index Cond: (c_id = 1)

 Planning Time: 1.325 ms

 Execution Time: 160.505 ms

След това проверете блоковете, прочетени от диска

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

heap_blks_read | heap_blks_hit

---------------+---------------

10000          |             0

В горния пример имаше 1000 блока, прочетени от диска, за да се намерят кортежи за броене, където c_id =1. Отне 160 ms, тъй като имаше дисков I/O, за да извлече тези записи от диска.

Изпълнението е по-бързо, ако същата заявка се изпълни повторно, тъй като всички блокове все още са в кеша на PostgreSQL сървъра на този етап

SELECT pg_stat_reset();

EXPLAIN ANAYZE SELECT count(*) from tbldummy where c_id=1;

                                                               

                                 QUERY PLAN

-------------------------------------------------------------------------------------

 Aggregate  (cost=17407.33..17407.34 rows=1 width=8) (actual time=33.760..33.761 rows=1 loops=1)

   ->  Bitmap Heap Scan on tbldummy  (cost=189.52..17382.46 rows=9948 width=0) (actual time=9.584..30.576 rows=10000 loops=1)

         Recheck Cond: (c_id = 1)

         Heap Blocks: exact=10000

         ->  Bitmap Index Scan on tbldummy_c_id_idx  (cost=0.00..187.04 rows=9948 width=0) (actual time=4.314..4.314 rows=10000 loops=1)

               Index Cond: (c_id = 1)

 Planning Time: 0.106 ms

 Execution Time: 33.990 ms

и блокира четенето от диска спрямо кеша

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

heap_blks_read | heap_blks_hit

---------------+---------------

    0          |         10000

Отгоре е видно, че тъй като всички блокове бяха прочетени от кеша и не се изискваше I/O диск. Следователно това също даде по-бързи резултати.

Задаване на размера на кеша на PostgreSQL

Размерът на кеша трябва да бъде настроен в производствена среда в съответствие с количеството налична RAM памет, както и заявките, необходими за изпълнение.

Като пример – shared_buffer от 128MB може да не е достатъчен за кеширане на всички данни, ако заявката трябваше да извлече повече кортежи: 

SELECT pg_stat_reset();

SELECT count(*) from tbldummy where c_id < 150;

SELECT heap_blks_read, heap_blks_hit from pg_statio_user_tables where relname='tbldummy';

 heap_blks_read | heap_blks_hit

----------------+---------------

      20331     |      288

Променете shared_buffer на 1024MB, за да увеличите heap_blks_hit.

Всъщност, като се имат предвид заявките (базирани на c_id), в случай, че данните се реорганизират, по-добро съотношение на попадане в кеша може да се постигне и с по-малък shared_buffer.

В Data_Organization-1, PostgreSQL ще се нуждае от 1000 четения на блока (и консумация на кеш ) за намиране на c_id=1. От друга страна, за Data_Organisation-2, за същата заявка, PostgreSQL ще се нуждае само от 104 блока.

По-малко блокове, необходими за една и съща заявка, в крайна сметка консумират по-малко кеш и също така поддържат времето за изпълнение на заявката оптимизирано.

Заключение

Докато shared_buffer се поддържа на ниво процес на PostgreSQL, кешът на ниво ядрото също се взема предвид за идентифициране на оптимизирани планове за изпълнение на заявка. Ще разгледам тази тема в по-късна серия от блогове.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Прозоречна функция на PostgreSQL:дял чрез сравнение

  2. Как да предоставим всички привилегии за изгледи на произволен потребител

  3. Разрешението е отказано при опит за импортиране на CSV файл от PGAdmin

  4. Случайно премахнати привилегиите на суперпотребител по подразбиране на postgres - мога ли да ги върна?

  5. Разделете дадения низ и подгответе изявление за case