ݺߣ

ݺߣShare a Scribd company logo
www.postgrespro.ru
ЛОКАЛЬНАЯ ПАМЯТЬ
ПРОЦЕССОВ
Hacking PostgreSQL
28.04.2016
2
Содержание
1.Создание процесса
2.SysCache
3.PlanCache
4.MemoryContexts
5.work_mem
6.maintenance_work_mem
3
Память в PostgreSQL
4
5
Локальная память
src/include/utils/
src/include/storage/proc.h
src/backend/utils/cache
attoptcache.c catcache.c evtcache.c inval.c lsyscache.c
plancache.c relcache.c relfilenodemap.c relmapper.c
spccache.c syscache.c ts_cache.c typcache.c
src/backend/utils/mmgr
src/backend/storage/lmgr/proc.c
src/backend/utils/init/postinit.c
6
EXEC_BACKEND
src/backend/main/main.c
main()
startup_hacks(); //Platform-specific startup hacks
#ifdef EXEC_BACKEND
if (argc > 1 && strncmp(argv[1], "--fork", 6) == 0)
SubPostmasterMain(argc, argv);
#endif
if (argc > 1 && strcmp(argv[1], "--boot") == 0)
AuxiliaryProcessMain(argc, argv);
else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)
GucInfoMain();
else if (argc > 1 && strcmp(argv[1], "--single") == 0)
PostgresMain(argc, argv,
NULL, /* no dbname */
Strdup(get_user_name_or_exit(progname)));
else
PostmasterMain(argc, argv);
7
Создание процесса
PostgresMain()
InitProcess() //инициализировать структуру PGPROC для процесса
InitPostgres(dbname, InvalidOid, username, InvalidOid, NULL);
InitProcessPhase2(); //добавить запись PGPROC в ProcArray
ProcSignalInit(MyBackendId); //добавить запись в ProcSignal
RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
IdleInTransactionSessionTimeoutHandler);
RelationCacheInitialize(); //хэш-таблица RelationIdCache
InitCatalogCache(); //массив CatCache *SysCache[SysCacheSize]
InitPlanCache(); //регистрация callback-функций
RelationCacheInitializePhase2(); //кэшировать записи основных глобальных
//таблиц каталога
...
8
Чтение данных каталога в кэш
RelationCacheInitializePhase2()
RelationMapInitializePhase2() //загрузить данные из pg_filenode.map
if (!load_relcache_init_file(true)) //пытаемся прочитать pg_internal.init
{
formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
true, Natts_pg_database, Desc_pg_database);
formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
true, Natts_pg_authid, Desc_pg_authid);
formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
false, Natts_pg_auth_members, Desc_pg_auth_members);
formrdesc("pg_shseclabel", SharedSecLabelRelation_Rowtype_Id, true,
false, Natts_pg_shseclabel, Desc_pg_shseclabel);
}
9
Чтение данных каталога в кэш
load_relcache_init_file() //пытаемся прочитать pg_internal.init
for (relno = 0; relno < num_rels; relno++)
{
//вставить запись в хэш-таблицу RelationIdCache
RelationCacheInsert(rels[relno], false);
}
formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
true, Natts_pg_database, Desc_pg_database);
//Создать новый дескриптор relation
//инициализировать без обращения к каталогу
//вставить запись в хэш-таблицу RelationIdCache
RelationCacheInsert(relation, false);
10
11
Создание процесса
(продолжение)
PostgresMain()
InitProcess() //инициализировать структуру PGPROC для процесса
InitPostgres(dbname, InvalidOid, username, InvalidOid, NULL);
...
RelationCacheInitializePhase2(); //кэшировать основные глобальные
//таблицы каталога
PerformAuthentication(); //выполнить аутентификацию клиента
MyProc->databaseId = MyDatabaseId; //Подключиться к базе
SetDatabasePath(fullpath);
RelationCacheInitializePhase3(); //кэшировать таблицы и индексы каталога
12
Чтение данных каталога в кэш
RelationCacheInitializePhase3()
if(!load_relcache_init_file(false)) //пытаемся прочитать pg_internal.init
{
formrdesc("pg_class", RelationRelation_Rowtype_Id, false,
true, Natts_pg_class, Desc_pg_class);
formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,
false, Natts_pg_attribute, Desc_pg_attribute);
formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,
true, Natts_pg_proc, Desc_pg_proc);
formrdesc("pg_type", TypeRelation_Rowtype_Id, false,
true, Natts_pg_type, Desc_pg_type);
}
load_critical_index() //загружаем критические индексы
//обновляем и дополняем все записи RelationIdCache
InitCatalogCachePhase2(); //завершаем инициализацию массива SysCache
CatalogCacheInitializeCache()
13
SysCache
src/backend/utils/cache/lsyscache.c
char *get_attname(Oid relid, AttrNumber attnum);
Oid get_atttype(Oid relid, AttrNumber attnum);
Oid get_commutator(Oid opno);
Oid get_negator(Oid opno);
char *get_func_name(Oid funcid);
Oid get_func_rettype(Oid funcid);
bool func_strict(Oid funcid);
char func_volatile(Oid funcid);
14
Инвалидация кэша
src/include/storage/sinval.h
src/backend/utils/cache/inval.c
CacheInvalidateCatalog(Oid catalogId) //после CLUSTER TABLE pg_*
CacheInvalidateRelcache(Relation relation) //например, DROP INDEX
src/backend/utils/cache/catcache.c
CatalogCacheIdInvalidate(int cacheId, uint32 hashValue) //удалить записи из кэша
src/backend/commands/cluster.c
swap_relation_files() //Пример инвалидации кэша
15
16
Prepared statements
PREPARE usrrptplan (int) AS
SELECT * FROM users u, logs l WHERE u.usrid=$1 AND u.usrid=l.usrid
AND l.date = $2;
EXECUTE usrrptplan(1, current_date);
select * from pg_prepared_statements;
name | usrrptplan
statement | PREPARE usrrptplan (int) AS +
| SELECT * FROM users u, logs l WHERE u.usrid=$1 AND u.usrid=l.usrid+
| AND l.date = $2;
prepare_time | 2016-04-28 13:42:57.3563+03
parameter_types | {integer,"time without time zone"}
from_sql | t
17
Plan cache
src/include/utils/plancache.h
src/backend/utils/cache/plancache.c
CreateCachedPlan()
CompleteCachedPlan()
SaveCachedPlan() //перенести план в долгоживущий контекст памяти
DropCachedPlan() //удалить сохранённый план
choose_custom_plan() //выбор между обобщенным и конкретным планом
BuildCachedPlan() //построить обобщённый или конкретный план
//из сохраненной информации
18
19
Memory Context API
src/include/nodes/memnodes.h
typedef struct MemoryContextData
{
NodeTag type; // всегда T_AllocSetContex
bool isReset;
bool allowInCritSection;
MemoryContextMethods *methods;
MemoryContext parent;
MemoryContext firstchild;
MemoryContext prevchild;
MemoryContext nextchild;
char *name; // Имя контекста. Для отладки
MemoryContextCallback *reset_cbs;
} MemoryContextData;
20
Дерево контекстов
Context
firstchild
parent
nextchild. . .
. . .
. . .
21
Memory Context Methods
src/include/nodes/memnodes.h
typedef struct MemoryContextMethods
{
void *(*alloc) (MemoryContext context, Size size);
/* call this free_p in case someone #define's free() */
void (*free_p) (MemoryContext context, void *pointer);
void *(*realloc) (MemoryContext context, void *pointer, Size size);
void (*init) (MemoryContext context);
void (*reset) (MemoryContext context);
void (*delete_context) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer);
bool (*is_empty) (MemoryContext context);
void (*stats) (MemoryContext context, int level, bool print,
MemoryContextCounters *totals);
#ifdef MEMORY_CONTEXT_CHECKING
void (*check) (MemoryContext context);
#endif
} MemoryContextMethods;
22
AllocSetMethods
src/backend/utils/mmgr/aset.c
static MemoryContextMethods AllocSetMethods = {
AllocSetAlloc,
AllocSetFree,
AllocSetRealloc,
AllocSetInit,
AllocSetReset,
AllocSetDelete,
AllocSetGetChunkSpace,
AllocSetIsEmpty,
AllocSetStats
#ifdef MEMORY_CONTEXT_CHECKING
,AllocSetCheck
#endif
};
23
Контексты
src/include/utils/memutils.h
src/backend/utils/mmgr/README
TopMemoryContext; //Корень дерева контекстов
ErrorContext;
PostmasterContext;
CacheMemoryContext;
MessageContext;
TopTransactionContext;
CurTransactionContext;
CurrentMemoryContext;
24
Переключение контекстов
src/backend/utils/mmgr/mcxt.c
MemoryContext tempContext = AllocSetContextCreate(….);
MemoryContext saveContext = MemoryContextSwitchTo(tempContext);
/* делаем что-то в новом контексте */
char * x = palloc(128);
char * y = palloc(256);
. . . . . . . . . . . . . . . . . .
MemoryContextSwitchTo(saveContext);
MemoryContextDelete(tempContext);
25
AllocSetContext
src/backend/utils/mmgr/aset.c
typedef struct AllocSetContext
{
MemoryContextData header; /* Standard memory-context fields */
AllocBlock blocks; /* head of list of blocks in this set */
AllocChunk freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */
Size initBlockSize;
Size maxBlockSize;
Size nextBlockSize; /* next block size to allocate */
Size allocChunkLimit;
AllocBlock keeper;
} AllocSetContext;
26
AllocBlock
src/backend/utils/mmgr/aset.c
typedef struct AllocBlockData
{
AllocSet aset; /* aset that owns this block */
AllocBlock next; /* next block in aset's blocks list */
char *freeptr; /* start of free space in this block */
char *endptr; /* end of space in this block */
} AllocBlockData;
MemoryContext AllocSetContextCreate(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
27
blocks
header
header
blocks
data free
header data
header data
header data
28
AllocChunk
src/backend/utils/mmgr/aset.c
typedef struct AllocChunkData
{
/* aset is the owning aset if allocated, or the freelist link if free */
void *aset;
/* size is always the size of the usable space in the chunk */
Size size;
#ifdef MEMORY_CONTEXT_CHECKING
/* when debugging memory usage, also store actual requested size */
Size requested_size;
#endif
} AllocChunkData;
29
chunks
header data free
1 2 3 4 5
30
palloc
header data free
1 2 3 4 5
6
31
palloc
header data free
1 2 3 4 5 6
32
pfree
1 2 3 4 5 6
freelist[]
33
Источники потерь
●
Выделение множества мелких кусочков памяти
– сопоставимых с заголовком чанка (16 байт)
●
Выделение кусочков размера 2N
+ o
●
Создание множества контекстов под мелкие запросы
●
Увеличение размера выделенной памяти (repalloc)
34
Настройки памяти
●
work_mem
●
maintenance_work_mem
●
autovacuum_work_mem
●
replacement_sort_tuples
35
Источники
●
Introduction to MemoryContexts by Tomas Vondra
●
Examples of palloc overhead by Tomas Vondra
●
Sorting Through The Ages
www.postgrespro.ru
СПАСИБО ЗА ВНИМАНИЕ!
ВОПРОСЫ?
Hacking PostgreSQL
28.04.2016
hacking@postgrespro.ru

More Related Content

Hacking PostgreSQL. Локальная память процессов. Контексты памяти.