暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL特性矩阵解析系列24之Support for anonymous shared memory

557

引言

    9.3版本以前,调整共享内存大小常常需要调整内核参数以适应更改。9.3之后的版本已经不需要了。

匿名共享内存

匿名共享内存的实现是已Ashmem驱动程序为基础所构建起来的一套方案,基于linux的临时文件系统tmpfs.

    装了一个9.0.9版本 不支持 Support for anonymous shared memory
    postgres=# select version();
    version

    -------------------------------------------------------------------------------------------------------
    ------------
    PostgreSQL 9.0.9 on x86_64-unknown-linux-gnu, compiled by GCC gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-
    44), 64-bit
    (1 row)

    通过smem查询
    [root@pg01 ~]# smem -U postgres -k |head -1;smem -U postgres -k |grep -i postgres |sort -k 1 -n -r
    PID User Command Swap USS PSS RSS
    87635 postgres postgres: postgres postgres 0 3.6M 4.6M 6.5M
    87634 postgres psql -h 127.0.0.1 -p 5432 p 0 876.0K 908.0K 1.9M
    87590 postgres postgres: stats collector p 0 148.0K 233.0K 968.0K
    87589 postgres postgres: autovacuum launch 0 756.0K 1.0M 2.1M
    87588 postgres postgres: wal writer proces 0 128.0K 268.0K 1.1M
    87587 postgres postgres: writer process 0 204.0K 1.7M 4.1M
    87585 postgres postgres: logger process 0 136.0K 200.0K 828.0K
    87584 postgres opt/pgsql9.09/bin/postgres 0 138.3M 140.3M 143.8M
    87479 postgres -bash 0 1.7M 1.9M 3.4M

    切换到12.8版本PostgreSQL
    postgres=# select version();
    version

    -------------------------------------------------------------------------------------------------------
    --
    PostgreSQL 12.8 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bi
    t
    (1 row)

    postgres=#
    通过smem查询
    [root@pg01 ~]# smem -U postgres -k |head -1;smem -U postgres -k |grep -i postgres |sort -k 1 -n -r
    PID User Command Swap USS PSS RSS
    87852 postgres postgres: postgres postgres 0 2.6M 3.2M 5.4M
    87851 postgres psql 0 1.3M 1.4M 3.1M
    87850 postgres postgres: logical replicati 0 476.0K 710.0K 2.3M
    87849 postgres postgres: stats collector 0 216.0K 336.0K 1.6M
    87848 postgres postgres: autovacuum launch 0 564.0K 856.0K 2.6M
    87847 postgres postgres: walwriter 0 220.0K 60.4M 121.7M
    87846 postgres postgres: background writer 0 224.0K 617.0K 2.2M
    87845 postgres postgres: checkpointer 0 168.0K 292.0K 1.6M
    87843 postgres postgres: logger 0 172.0K 291.0K 1.5M
    87842 postgres opt/pgsql12.8/bin/postgres 0 32.7M 93.7M 157.2M
    87479 postgres -bash 0 1.7M 1.9M 3.4M

    源码解析

      src/backend/storage/ipc/shmem.c
      shmem index table 类型是 hash table
      /*-------------------------------------------------------------------------
      *
      * shmem.c
      * create shared memory and initialize shared memory data structures.
      *
      * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
      * Portions Copyright (c) 1994, Regents of the University of California
      *
      *
      * IDENTIFICATION
      * src/backend/storage/ipc/shmem.c
      *
      *-------------------------------------------------------------------------
      */
      /*
      * POSTGRES processes share one or more regions of shared memory.
      * The shared memory is created by a postmaster and is inherited
      * by each backend via fork() (or, in some ports, via other OS-specific
      * methods). The routines in this file are used for allocating and
      * binding to shared memory data structures.
      *
      * NOTES:
      * (a) There are three kinds of shared memory data structures
      * available to POSTGRES: fixed-size structures, queues and hash
      * tables. Fixed-size structures contain things like global variables
      * for a module and should never be allocated after the shared memory
      * initialization phase. Hash tables have a fixed maximum size, but
      * their actual size can vary dynamically. When entries are added
      * to the table, more space is allocated. Queues link data structures
      * that have been allocated either within fixed-size structures or as hash
      * buckets. Each shared data structure has a string name to identify
      * it (assigned in the module that declares it).
      *
      * (b) During initialization, each module looks for its
      * shared data structures in a hash table called the "Shmem Index".
      * If the data structure is not present, the caller can allocate
      * a new one and initialize it. If the data structure is present,
      * the caller "attaches" to the structure by initializing a pointer
      * in the local address space.
      * The shmem index has two purposes: first, it gives us
      * a simple model of how the world looks when a backend process
      * initializes. If something is present in the shmem index,
      * it is initialized. If it is not, it is uninitialized. Second,
      * the shmem index allows us to allocate shared memory on demand
      * instead of trying to preallocate structures and hard-wire the
      * sizes and locations in header files. If you are using a lot
      * of shared memory in a lot of different places (and changing
      * things during development), this is important.
      *
      * (c) In standard Unix-ish environments, individual backends do not
      * need to re-establish their local pointers into shared memory, because
      * they inherit correct values of those variables via fork() from the
      * postmaster. However, this does not work in the EXEC_BACKEND case.
      * In ports using EXEC_BACKEND, new backends have to set up their local
      * pointers using the method described in (b) above.
      *
      * (d) memory allocation model: shared memory can never be
      * freed, once allocated. Each hash table has its own free list,
      * so hash buckets can be reused when an item is deleted. However,
      * if one hash table grows very large and then shrinks, its space
      * cannot be redistributed to other tables. We could build a simple
      * hash bucket garbage collector if need be. Right now, it seems
      * unnecessary.
      */

      #include "postgres.h"

      #include "access/transam.h"
      #include "miscadmin.h"
      #include "storage/lwlock.h"
      #include "storage/pg_shmem.h"
      #include "storage/shmem.h"
      #include "storage/spin.h"


      /* shared memory global variables */

      static PGShmemHeader *ShmemSegHdr; /* shared mem segment header */

      static void *ShmemBase; /* start address of shared memory */

      static void *ShmemEnd; /* end+1 address of shared memory */

      slock_t *ShmemLock; /* spinlock for shared memory and LWLock
      * allocation */

      static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */


      /*
      * InitShmemAccess() --- set up basic pointers to shared memory.
      *
      * Note: the argument should be declared "PGShmemHeader *seghdr",
      * but we use void to avoid having to include ipc.h in shmem.h.
      */
      void
      InitShmemAccess(void *seghdr)
      {
      PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;

      ShmemSegHdr = shmhdr;
      ShmemBase = (void *) shmhdr;
      ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
      }

      /*
      * InitShmemAllocation() --- set up shared-memory space allocation.
      *
      * This should be called only in the postmaster or a standalone backend.
      */
      void
      InitShmemAllocation(void)
      {
      PGShmemHeader *shmhdr = ShmemSegHdr;
      char *aligned;

      Assert(shmhdr != NULL);

      /*
      * Initialize the spinlock used by ShmemAlloc. We must use
      * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
      */
      ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));

      SpinLockInit(ShmemLock);

      /*
      * Allocations after this point should go through ShmemAlloc, which
      * expects to allocate everything on cache line boundaries. Make sure the
      * first allocation begins on a cache line boundary.
      */
      aligned = (char *)
      (CACHELINEALIGN((((char *) shmhdr) + shmhdr->freeoffset)));
      shmhdr->freeoffset = aligned - (char *) shmhdr;

      /* ShmemIndex can't be set up yet (need LWLocks first) */
      shmhdr->index = NULL;
      ShmemIndex = (HTAB *) NULL;

      /*
      * Initialize ShmemVariableCache for transaction manager. (This doesn't
      * really belong here, but not worth moving.)
      */
      ShmemVariableCache = (VariableCache)
      ShmemAlloc(sizeof(*ShmemVariableCache));
      memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
      }

      /*
      * ShmemAlloc -- allocate max-aligned chunk from shared memory
      *
      * Throws error if request cannot be satisfied.
      *
      * Assumes ShmemLock and ShmemSegHdr are initialized.
      */
      void *
      ShmemAlloc(Size size)
      {
      void *newSpace;

      newSpace = ShmemAllocNoError(size);
      if (!newSpace)
      ereport(ERROR,
      (errcode(ERRCODE_OUT_OF_MEMORY),
      errmsg("out of shared memory (%zu bytes requested)",
      size)));
      return newSpace;
      }

      /*
      * ShmemAllocNoError -- allocate max-aligned chunk from shared memory
      *
      * As ShmemAlloc, but returns NULL if out of space, rather than erroring.
      */
      void *
      ShmemAllocNoError(Size size)
      {
      Size newStart;
      Size newFree;
      void *newSpace;

      /*
      * Ensure all space is adequately aligned. We used to only MAXALIGN this
      * space but experience has proved that on modern systems that is not good
      * enough. Many parts of the system are very sensitive to critical data
      * structures getting split across cache line boundaries. To avoid that,
      * attempt to align the beginning of the allocation to a cache line
      * boundary. The calling code will still need to be careful about how it
      * uses the allocated space - e.g. by padding each element in an array of
      * structures out to a power-of-two size - but without this, even that
      * won't be sufficient.
      */
      size = CACHELINEALIGN(size);

      Assert(ShmemSegHdr != NULL);

      SpinLockAcquire(ShmemLock);

      newStart = ShmemSegHdr->freeoffset;

      newFree = newStart + size;
      if (newFree <= ShmemSegHdr->totalsize)
      {
      newSpace = (void *) ((char *) ShmemBase + newStart);
      ShmemSegHdr->freeoffset = newFree;
      }
      else
      newSpace = NULL;

      SpinLockRelease(ShmemLock);

      /* note this assert is okay with newSpace == NULL */
      Assert(newSpace == (void *) CACHELINEALIGN(newSpace));

      return newSpace;
      }


      参考

      https://www.postgresql.org/about/featurematrix/detail/240/


      文章转载自CP的PostgreSQL厨房,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论