PostgreSQL内核技术--基础概览

一: 基本概念:

1: 数据库集簇(database cluster) :

        一组数据库(databse)的集合。

2: 数据库对象:

         存储或引用数据的数据结构。常见: table, datatabse, index, view, sequence, fuction, tablespace

3: 数据库对象 <==> oid :

        oid是数据库对象的唯一标识

4: 查询数据库的oid:

        select oid from pg_database;

5: 查询表的oid: 

         select  oid  from  pg_class;

6: 数据库集簇:

        物理上就是一个目录(基础目录base)。 initdb时,会在指定的目录下(一般是环境变量PGDATA),创建基础目录base.

7:  base目录中的每一个子目录, 物理上都对应一个数据库. 那么该数据库的表、索引等对象都在这个子目录下存储为一个文件。

8: 一个数据库<==> base子目录下的一个目录对应。该目录名称==数据库oid.

select oid  from pg_database  <==>  $PGDATA/base/*

9: 表和索引作为数据库对象也是通过oid管理的。

  select relfilename, oid, relfilenode from pg_class;

这里relfilenode字段表示的是数据库文件名。  relfilename字段表示的是表名。

当其小于1GB,表在数据库目录下会存储为单个文件。当超过1GB,会扩展出relfilenode.1(表oid.1)这个文件。

(表名relfilenode 和 表oid一致)

10: 数据目录

        base/数据库oid/表oid

11: cd $PGDATA

         ls -la base/16384/18751*

       >>    base/16384/18751

       >>   base/16384/18751_fsm

       >>   base/16384/18751_vm

fsm: 空闲空间映射, vm可见性空间映射。

12:  创建表空间:

         create tablespace tablespace_name  realative location '表空间的目录';

        eg: create tablespace examples relative location 'tablespace3';

        在指定的目录:PGDATA的realative location下创建一个相对路径tablespace3, $PGDTA/realative location/tablespace3;

该语句会在pg的表空间目录下$PGDTA/pg_tblspc/ 下创建一个链接指向指定的目录。链接的名称和表空间的oid一致。

eg:  ls -la $PGDTA/pg_tblspc

       >>  16386 ->  $PGDTA/realative location/tablespace3

       ls -la  $PGDTA/realative location/tablespace3

       >>  PG_主版本号_目录版本号     (创建的表空间下会有一个特定子目录)

13:  数据文件(base目录下的文件):

        包括表,索引, 空闲空间映射、可见性空间映射。在磁盘或内存中以固定长度页,大小为8KB, 来存储。页的个数用区块号表示, 从0开始。

14: 页:

(1)堆元祖:数据记录本身, 位于页面底部(一步步堆叠)

(2)行指针:每个行指针4KB, 指向堆元组。行指针形成一个简单的数组,看做是元祖的索引。

(3)首部数据:PageHeaderData.  大小为24KB.

       包括:pd_lsn: 本页面最近变更对应的xlog记录的标识

        pd_checksum: 本页面的校验和

        pd_lower,pd_upper: 指向行指针的末尾和最新堆元组的起始位置

15:页的空闲空间:

         行指针的末尾和最新元祖的起始位置之间的空余空间。

16:TID(tuple id):

        数据库内部元组的唯一标识符。

        TID由一对值组成,分别为页的区块号,和行指针的偏移号。TID=(block=1, offset=2)表述表中第1页第2个元组。

17: 读写堆元组:

        写入: 页面tuple从下往上堆叠, 行指针增加 ,修改pd_lower和pd_upper

        读取: 顺序扫描和B树索引扫描;

        顺序扫描:扫描行指针,依序读取页面tuple

        B树索引扫描: 

18: 索引扫描:

        (避免不必要的页面扫描)

        索引文件中包含着索引元组.
        索引元组 index tuple: 由一个键值对组成。键:被索引的列值;值:目标堆元祖的TID.

Key: 'NAME'      Value: TID。之后根据TID来读取对应的元组。

19:PG的进程架构:

        PG是多进程架构,协同工作。

        (1):pg服务器进程(PM): 所有子进程的父进程

        (2):后端进程(backend process): pg进程,负责处理客户端发过来的查询和语句。通过单条tcp连接与客户端通信。一条连接只允许操作一个服务器上指定数据库。多个客户端同时连接的话,指定参数max_connections。

        (3):后台进程(backgroud process): 负责执行数据库的各项任务,例如checkpoint(检查点处理)、autovacume(清理)、wal writer(将缓冲区wal数据持久化)、backgroud writer(将共享缓冲池的脏页逐渐刷入持久化存储中)

20: pg的内存架构(pg使用内存上下文来管理内存,避免内存泄漏等问题):

        本地内存区域: 由后端进程分配,供自己使用

        共享内存区域: PG主进程分配。例如 shared_buffer_pool: 将表和索引中的页面从持久化存储中加载到此中,进行读操作。

21: PG的并发控制

        并发控制用来维护数据库一致性和隔离性的技术。

        数据库事务正确执行的4个基本属性:ACID

【数据库】数据库四大特性ACID_IT猫咪酱的博客-CSDN博客

        三种并发控制技术:

        (1) 多版本并发控制MVCC

        (2) 严格两阶段锁定S2PL

        (3) 乐观并发控制OCC

二: 进阶概念:

1: MVCC:

        多版本并发控制技术。有点:读写互不阻塞,事务会选择其中的一个版本进行事务的读取,确保各个事务的隔离性。MVCC: 每个写操作都会创建一个新版本的数据项,并保留其旧版本。

2: 快照隔离(Snapshot Isolation: SI):

        MVCC的一种变体。

3: SQL标准里定义的三种异常:

(1): 脏读: 读取到了未提交的数据

(2):  不可重复读: 同一事务中,多次读到的数据不一致

(3):幻读: 读某一范围数据,前后读取的数据不一致

4: PG的事务隔离等级:

(1): 读已提交 

(2):可重复读

(3):可串行化

5: 事务标识:

        当事务开始时(begin), 事务管理器就会为事务分配一个唯一事务标识(txid), 之后真正启动事务。

        #BEGIN;

        #select txid txid_current();   // 查询当前事务的事务id

6:   0:无效事务id

     1: initdb时候初始化事务id

     2: 冻结的事务id

7:  <txid:过去, 事务可见

>=txid: 当前 未来,事务不可见

8: 事务逻辑上无限,实际空间有限, 环形空间结构

9: 元组(tiple):  

        普通数据元组(Heap Tuple);

        toast元组。

10: 普通元组数据结构:

11: 元组的增、删、改

12:空闲空间映射

        插入堆表或者索引的元组时, PG使用表或者索引相应的FSM来选择可供插入的页面。

FSM都是以fsm后缀fsm存储。

13:CLOG:

        提交日志,保存事务的状态。分配在共享内存中。

14:事务状态:

4种;

in_progress(事务运行中);commited(事务提交);aborted(事务异常终止);sub_commited(子事务相关).

15: 事务快照 snapshot

记录哪些事务是活跃的(acative: 正在进行中的,或者还未开始的事务。 不可见)哪些事务是不活跃的(inactive:已经提交的或者终止的事务。 可见)

#select txid_current_snapshot; // 查询当前事务的快照

16: 可见性规则

《postgresql指南--内幕探索》第五章并发控制(二)_postgresql 序列化异常_hmxz1024的博客-CSDN博客

需要用到:

  (1): tuple中的t_xmin, t_xmax;

  (2):clog:记录当前事务的状态

  (3): 当前的snapshot 

t_xmin的状态为ABORTED ⇒ Tuple is Invisible

t_xmin的状态为IN_PROGRESS ⇒ Tuple is Invisible (most) :

反之eg: If Status(t_xmin) = IN_PROGRESS && t_xmin = current_txid && t_xmax = INVAILD ⇒ Tuple is Visible  如果这个tuple是当前事务提交的,并tuple的t_xmax值是0,说明这个tuple是由当前事务插入并且并没有被修改过,所以,它是可见的。

t_xmin的状态为COMMITTED  ⇒ Tuple is visible (most)

反之eg:

如果tuple被当前事务UPDATE或者DELETE了,自然这个tuple对于我们来说是旧版本了,不可见;

对于一个tuple,插入它的事务已经提交(COMMITED),并且该事务在当前的snapshot下是active的,说明该事务对当前事务中的命令来说是 in progress 或者 not yet started,故该事务插入的tuple对在当前为不可见;

17:详细的pg并发制技术,请见

(2条消息) 《postgresql指南--内幕探索》第五章并发控制(一)_hmxz2nn的博客-CSDN博客

《postgresql指南--内幕探索》第五章并发控制(二)_postgresql 序列化异常_hmxz1024的博客-CSDN博客

18:vacuum     PG的维护清理过程

  (0) :死元组:对于任何事务都不可见的元组,即delete/update操作过的元组(t_xmax被有效设置)。

(1): 对于DELETE和UPDATE比较多的的表,死元组可能占据很多磁盘空间.同时,死元组也将从索引中引用,进一步增加了浪费的磁盘空间量.这就是我们在PostgreSQL中称之为“膨胀”的东西,同时因为查询也会变慢。

(2): vacuum两个任务: 删除死元组;冻结事务标识

  (3):vacuum清理过程的两种模式:

          并发清理:删除表文件每个页面的死元组。其他事务在运行时可以读取该表。

          完整清理:不会删除所有死元组,并且对所有活的元组记性碎片整理。其他事务执行时,无法读取该表。

(4):vacuum 清理过程涉及全表扫描,代价高昂。中引入了可见性空间映射( Visibility Map,VM)来提高移除死元组的效率。

19: vacuum并发清理流程

详细流程见:《postgresql指南--内幕探索》第六章 清理过程_hmxz1024的博客-CSDN博客

20:  自动清理守护进程  auto_vacuum
自动清理守护进程已经将清理过程自动化,因此 PostgreSQL 运维起来非常简单。自动清理守护程序周期性地唤起几个 autovacuum_worker 进程,默认情况下每分钟唤醒一次(由参数 autovacuum_naptime 定义),每次唤起三个工作进程(由 autovacuum_max_works 定义)。

自动清理守护进程唤起的 autovacuum 工作进程会依次对各个表执行并发清理,从而将对数据库活动的影响降至最低。

21:  完整清理(FULL VACUUM)

虽然并发清理对于运维至关重要,但光有它还不够。比如,即使删除了很多死元组,也无法压缩表大小的情况。死元组虽然都被移除了,但表的尺寸没有减小。这种情况既浪费了磁盘空间,又会对数据库性能产生负面影响。使用完整清理可以将压缩原表的大小。

(1).创建新表文件:
当对表执行VACUUM FULL命令时,PostgreSQL首先获取该表的AccessExclusiveLock锁,然后创建一个大小为8 KB的新表文件。AccessExclusiveLock锁不允许访问。

(2).将活动元组复制到新表中:
PostgreSQL仅将旧表文件中的活动元组复制到新表中。

(3).删除旧文件,重建索引,并更新统计信息,FSM和VM:
复制所有活动元组后,PostgreSQL删除旧文件,重建所有关联的表索引,更新该表的FSM和VM,并更新关联的统计信息和系统目录。

使用VACUUM FULL命令时应考虑两点:

  • 在处理Full VACUUM时,没有人可以访问(读/写)该表。
  • 最多临时使用表的磁盘空间的两倍;因此,在处理一个巨大的表时,有必要检查剩余的磁盘容量。

22: pg的缓冲区管理器:

(1)缓冲区管理器管理共享内存和持久存储之间的数据。对系统性能有重要影响。

(2)Pg的缓冲区管理器包括:缓冲表;缓冲区描述符;缓冲池;

23:   buffer_tag(缓冲区标签): 数据页面的唯一标识。有三个值组成:

        [1]:关系文件节点:  表空间oid;数据库oid;表oid

        [2]:关系分支编号: 0:main文件 1:fsm    2:vm

        [3]:页面号:3 : 3号页面

24: 缓冲区的结构

缓冲表:散列表(哈希表):散列表的每个数据项包括:buffer_tag和buffer_id。

缓冲区描述符:保存数据页面(槽)的元数据。和缓冲池的槽一一对应。是一个数组。

缓冲池:槽的大小为8KB(等于页面大小), 是一个数组。每一个槽存着一个数据文件(表或者索引)页。数组槽的索引称为buffer_id。

25: 后段进程如何读取数据页:

(1)向缓冲区管理器发送请求,请求中带有目标页面的buffer_tag

  (2) 根据buffer_tag返回buffer_id, 即目标页面在缓冲池中的槽位。

      如果该页面不在缓冲池中,那么管理器就会将页面从持久存储位置加载一个到缓冲池槽中,然后返回该槽的buffer_id

  (3) 后端进程访问buffer_id对应的槽,读取页面

26:脏页:

        后端进程修改缓冲池中的页面,例如向该页面插入元组,但是没有刷新到持久存储。这种页面被称为脏页。

27:脏页刷盘:

        缓冲区管理器不会做这个事情,需要额外进程如:检查点进程checkpoint; 后台写入进程bg writer

28: 页面置换算法:

         页面置换是?当所有缓冲区槽位都被占,但是还不包括申请访问的页面,此时需要将缓冲区的一个页面逐出去,放置所请求的页面。

        常见算法:LRU, 时钟扫描算法。

29:脏页刷盘:
        (1)检查点进程---->脏页刷盘。

                将检查点记录写入WAL段文件,在检查点开始时进行脏页刷盘。

       (2)后台写入进程---->脏页刷盘。

                目的是通过少量多次的脏页刷盘,减少检查点带来的密集写入的影响。会一点点将脏页落盘,尽可能减少对数据库活动的影响。参数bg_writer_delay被唤醒去刷页。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值