在 kubernetes 上运行 Postgres 以及 Postgres 特性

本贴最后更新于 1433 天前,其中的信息可能已经事过境迁

在 kubernetes 上运行 Postgres 以及 Postgres 特性

Postgres 是一款开源的关系性数据库,并且自称是世界上最先进的开源关系性数据库。经过 30 多年的积极开发,已在可靠性,功能健壮性和性能方面赢得了极高的声誉。

安装

官方文档对于源代码安装和二进制安装的教程已经十分详细这里不详细介绍了,这里我会介绍如何在 kubernetes 上安装 postgresql 数据库

第一步:下载 postgres 的镜像,需要 docker 环境,这里以 postgres:11.3 为例

docker pull postgres:11.3

image.png

第二步:创建 namesapce postgres 我们会将 postgres 放在 postgres 的 namespace 下

kubectl create ns postgres

image.png

第三步:准备好 postgres 的资源定义文件,有三个: **cm.yaml(挂载配置文件),postgres.yaml(资源定义), svc.yaml(服务发现)

cm.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-cm                                 # configmaps的名称
  namespace: postgres                               # configmaps的域
  labels:
    app: postgres-cm                                # 标签
data:
  master.conf: |
    listen_addresses = '*'                          # 监听所有IP
    archive_mode = on                               # 允许归档
    archive_command = 'cp %p /var/lib/postgresql/data/pg_archive/%f'  # 用该命令来归档logfile segment
    wal_level = hot_standby                         #开启热备
    max_wal_senders = 32                            # 这个设置了可以最多有几个流复制连接,差不多有几个从,就设置几个
    wal_keep_segments = 64                          # 设置流复制保留的最多的xlog数目,一份是 16M,注意机器磁盘 16M*64 = 1G
    wal_sender_timeout = 60s                        # 设置流复制主机发送数据的超时时间
    max_connections = 100                           # 这个设置要注意下,从库的max_connections必须要大于主库的
  pg_hba.conf: |
    local   all             all                                     trust
    host    all             all             127.0.0.1/32            trust
    host    all             all             ::1/128                 trust
    local   replication     all                                     trust
    host    replication     all             127.0.0.1/32            trust
    host    replication     all             ::1/128                 trust
    host  all       all       all           trust
    host    all             all           0.0.0.0/0             trust
    host    replication     postgres        0.0.0.0/0             trust
  slave.conf: |
    wal_level = hot_standby                         # 热备
    max_connections = 1000                          # 一般查多于写的应用从库的最大连接数要比较大
    hot_standby = on                                # 说明这台机器不仅仅是用于数据归档,也用于数据查询
    max_standby_streaming_delay = 30s               # 数据流备份的最大延迟时间
    wal_receiver_status_interval = 10s              # 多久向主报告一次从的状态,当然从每次数据复制都会向主报告状态,这里只是设置最长的间隔时间
    hot_standby_feedback = on                       # 如果有错误的数据复制,是否向主进行反馈
    log_destination = 'csvlog'                      # 日志文件的位置
    logging_collector = on                          # 启动日志信息写到了当前terminal的stdout,系统操作日志信息写到了pg_log/enterprisedb-*.log
    log_directory = 'log'                           # 日志文件目录
  recovery.conf: |
    standby_mode = on                               # 启动从节点
    primary_conninfo = 'host=postgres-0.postgres.postgres port=5432 user=postgres password=r00tme'  # 主节点信息
    recovery_target_timeline = 'latest'             # 更新备份[root@paas-54 postgres]

svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgres         # 服务名
  namespace: postgres    # 服务所在域
  labels:                # 标签
    app: postgres        # 键值对为{"app":"postgres"}的标签
spec:
  ports:                 # 端口
  - name: postgres       # 端口名
    port: 5432           # 内部服务访问Service的端口
  clusterIP: None        # Headless Service,设置后服务没有内网IP,访问服务会直接寻路到Pod
  selector:              # 服务选择器
    app: postgres        # 服务选择标签键值对为{"app":"postgres"}的Pod
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-read    # 服务名
  namespace: postgres    # 服务所在域
  labels:                # 标签
    app: postgres        # 键值对为{"app":"postgres"}的标签
spec:
  externalIPs:               # 暴露Service到外部IP
  - 192.168.1.225        #填宿主机ip即可
  ports:                 # 端口
  - name: postgres       # 端口名
    port: 5432           # 内部服务访问Service的端口
    targetPort: 5432     # Pod内的端口
  selector:              # 服务选择器
    app: postgres        # 服务选择标签键值对为{"app":"postgres-slave"}的Pod

postgres.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres                                      # 主库
  namespace: postgres                                 # 使用postgres域
spec:
  replicas: 1      # 创建副本数
  selector:
    matchLabels:
      app: postgres                                   # 被{"app":"postgres"}的标签匹配
  serviceName: postgres                               # Statefulset使用的Headless Serivce为postgres
  template:                                           # 创建Pod模板
    metadata:
      name: postgres                                  # 创建Pod名
      labels:
        app: postgres                                 # Pod对应的标签
        node: master                                  # 只能在主节点上部署
    spec:
      tolerations:                                    # 1分钟如果节点不可达视为异常
      - key: "node.kubernetes.io/unreachable"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 60
      - key: "node.kubernetes.io/not-ready"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 60
      affinity:                                       # 亲和性
        nodeAffinity:                              # Pod亲和性
          requiredDuringSchedulingIgnoredDuringExecution:   # 硬要求
            nodeSelectorTerms:
            - matchExpressions:
              - key: node                             # 根据label是node的键来配对
                operator: In                          # 适用表达式
                values:                               # 值如下
                - master                              # 调度到有master标签的节点
        podAntiAffinity:                              # 如果检测到节点有{app:postgres}则不部署,避免postgres在同一节点重复部署
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - postgres
            topologyKey: "kubernetes.io/hostname"
      terminationGracePeriodSeconds: 0                # 异常立即删除
      initContainers:                                 # 初始化容器
      - command:                                      # 命令
        - bash
        - "-c"
        - |
          set -ex                                     # 写入环境变量
          [[ `hostname` =~ -([0-9]+)$ ]]              # 获取容器的主机名,用于数据库同步判断
          ordinal=${BASH_REMATCH[1]}                  # 获取主机名后获取主机编号
          if [[ $ordinal -eq 0 ]]; then               # 如果是postgres-0,也就是主节点
            echo test
          else                                        # 如果是非postgres-0,则为从节点
            rm /var/lib/postgresql/data1 -fr
            mkdir -p /var/lib/postgresql/data1
            pg_basebackup -h  postgres-0.postgres.postgres -U postgres -D /var/lib/postgresql/data1 -X stream -P    # 与postgres-0进行同步
            \cp /mnt/config-map/slave.conf /var/lib/postgresql/data1/postgresql.conf -f          # 写入配置文件
            \cp /mnt/config-map/recovery.conf /var/lib/postgresql/data1/recovery.conf -f         # 写入配置文件
            rm /var/lib/postgresql/data/* -fr
            mv /var/lib/postgresql/data1/* /var/lib/postgresql/data/
          fi
        env:
        - name: POSTGRES_USER                         # 数据库用户
          value: postgres
        - name: POSTGRES_DB                           # 数据库DB
          value: test
        name: init-postgres                           # 容器名
        image: postgres:11.3    # 镜像名
        imagePullPolicy: IfNotPresent                 # 若镜像存在,则不拉取
        volumeMounts:                                 # 容器内挂载目录
        - name: postgres-pv                           # 挂载文件名
          mountPath: /var/lib/postgresql/data/        # 挂载路径
        - name: config-map                            # configmap的文件存储路径
          mountPath: /mnt/config-map                  # 存储路径
        - name: tz
          mountPath: /etc/localtime
      containers:                                     # Pod中容器
      - name: postgres                                # 容器名
        image: postgres:11.3    # 镜像名
        ports:                                        # 端口
        - name: postgres                              # 端口名
          containerPort: 5432                         # 端口号
        volumeMounts:                                 # 容器内挂载目录
        - name: postgres-pv                           # 挂载文件名
          mountPath: /var/lib/postgresql/data/        # 挂载路径
        - name: config-map                            # configmap的文件存储路径
          mountPath: /mnt/config-map                  # 存储路径
        - name: tz
          mountPath: /etc/localtime
        env:                                          # 环境变量
        - name: TZ                                    # 时区,键
          value: Asia/Shanghai                        # 值
      volumes:                                        # Pod外挂载位置
      - name: postgres-pv                             # 挂载文件名
        hostPath:                                     # 挂载在主机目录
          path: /srv/system/postgres/data           # 主机目录路径
      - name: tz
        hostPath:
          path: /etc/localtime
      - name: config-map                              # 挂载文件名
        configMap:                                    # 挂载在configmaps
          name: postgres-cm                           # configmaps的名字

创建数据挂载目录

mkdir data

启动

kubectl apply -f .

写入配置文件

这里由于一些原因,导致需要手动将文件写入,这一步是可以省略的,可以在 yaml 定义里面去优化,具体优化以后在进行。

写入 postgresql.conf

kubectl exec -it postgres-0 -n postgres -- sh -c 'cp /mnt/config-map/master.conf /var/lib/postgresql/data/postgresql.conf -f'

写入 pg_hab.conf

kubectl exec -it postgres-0 -n postgres -- sh -c 'cp /mnt/config-map/pg_hba.conf /var/lib/postgresql/data/pg_hba.conf -f'

重启

kubectl delete pods postgres-0 -n postgres

image.png

架构

postgres 是典型的 C/S 架构,并且是与 mysql 的多线程架构不同,postgres 是多进程架构 。

下面此为进程架构图

image.png

内存架构

postgres 中的内存可以分为两大类:

  1. 本地内存区域

    • 由每个后端进程分配以供自己使用
  2. 共享内存区域

    • Shared Buffer

      • postgres 为了减少磁盘 IO 次数,将表和索引中的页面从持久性存储加载到此处,并直接对其进行操作。
    • WAL Buffer

      • 为确保服务器故障不会丢失任何数据,PostgreSQL 支持 WAL 机制。WAL 数据(也称为 XLOG 记录)是 PostgreSQL 中的事务日志;WAL 缓冲区是在写入持久性存储之前 WAL 数据的缓冲区。

进程类型


名字
含义 作用
Postmaster (Daemon) Process postgres server 主后台驻留进程是 PostgreSQL 启动时第一个启动的进程。启动时,他会执行恢复、初始化共享内存爱你的运行后台进程操作。正常服役期间,当有客户端发起链接请求时,它还负责创建后端进程。
Background Process 后台进程 与 Postmaster 和 Backend 相比,不可能简单地解释每个功能,因为这些功能取决于个别的特定功能和 PostgreSQL 内部。
Backend Process 后端进程 由 postgres 服务器进程启动,并处理由一个连接的客户端发出的所有查询。它通过单个 TCP 连接与客户端通信,并在客户端断开连接时终止。
Client Process 8 bytes 客户端进程需要和后端进程配合使用,处理每一个客户链接。通常情况下,Postmaster 进程会派生一个子进程用来处理用户链接。

Background 进程详细列表

进程 作用
logger 将错误信息写到 log 日志中
checkpointer 当检查点出现时,将脏内存块写到数据文件
writer 周期性的将脏内存块写入文件
wal writer 将 WAL 缓存写入 WAL 文件
Autovacuum launcher 当自动 vacuum 被启用时,用来派生 autovacuum 工作进程。autovacuum 进程的作用是在需要时自动对膨胀表执行 vacuum 操作。
archiver 在归档模式下时,复制 WAL 文件到特定的路径下。
stats collector 用来收集数据库统计信息,例如会话执行信息统计(使用 pg_stat_activity 视图)和表使用信息统计(pg_stat_all_tables 视图)

特性

在使用方面,postgres 性能十分强劲,使用起来也十分方便,而且 postgres 深耕多年,其目标早已不仅仅是关系性数据库了。

不只是关系性数据库

postgresql 自称世界上最先进的开源关系数据库,但其实 postgres 丰富的数据类型以及支持 json,这让它完全可以像 mongodb 一样来存储文档,并且 postgres 的性能更好

image.png

postgres 和 mongodb 在文档数据方面的性能比较 postgres vs mongodb

丰富的数据类型

postgres 内置了丰富的数据类型和支持自定类型的机制就决定了他的野心不至于关系性数据库,postgres 在文档数据库方面的能力也十分强大,当然如果没有足够丰富的数据类型,postgres 也不会来分 mongodb 和 es 的蛋糕

网络地址类型

PostgreSQL 提供用来存储 IPv4、IPv6 和 MAC 地址等三种网络地址的数据类型, 我们使用这些数据类型来保存网络地址比用 varchar 或者 text 好,因为 postgres 内置了这些类型时提供输入校验和一些网络算法函数,这样我们就可以在数据库层面上进行一些简单的网络计算,可以减少我们自身应用的内存消耗。下面是数据类型的简介。

名字 存储尺寸 描述
cidr 7 或 19 字节 IPv4 和 IPv6 网络
inet 7 或 19 字节 IPv4 和 IPv6 主机以及网络
macaddr 6 字节 MAC 地址
macaddr8 8 bytes MAC 地址(EUI-64 格式)
json

JSON 类型也是 postgres 的卖点之一,json 类型也是 postgres 可以做文档数据库的基础。json 也可以被存储为 text 或者 varchar 类型,但是 JSON 数据类型提供输入正则和许多 JSON 相关的函数和操作符,我们可以十分方便的检索整个 json 或者其中的 key 或者 value,这样我们也可以在 sql 层面上对 json 对象进行计算,这与 mongodb 的理念不谋而合,但 postgres 的 json 操作符使用简单,性能更是完爆 mongodb。

jsonjsonb。它们 几乎接受完全相同的值集合作为输入。主要的实际区别之一是效率。json 数据类型存储输入文本的精准拷贝,处理函数必须在每 次执行时必须重新解析该数据。而 jsonb 数据被存储在一种分解好的 二进制格式中,它在输入时要稍慢一些,因为需要做附加的转换。但是 jsonb 在处理时要快很多,因为不需要解析。jsonb 也支持索引,这也是一个令人瞩目的优势。

数组类型

PostgreSQL 允许一个表中的列定义为变长多维数组。可以创建任何内建或用户定义的基类、枚举类型、组合类型或者域的数组。

这给我们设计数据结构提供了非常高的灵活度。

CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][],
    body            json
);

我可以使用 postgres 建立上面的这种表,这挣脱了关系性型数据库的束缚,拥抱 json

几何类型

几何数据类型表示二维的空间物体。

名字 存储尺寸 表示 描述
point 16 字节 平面上的点 (x,y)
line 32 字节 无限长的线 {A,B,C}
lseg 32 字节 有限线段 ((x1,y1),(x2,y2))
box 32 字节 矩形框 ((x1,y1),(x2,y2))
path 16+16n 字节 封闭路径(类似于多边形) ((x1,y1),...)
path 16+16n 字节 开放路径 [(x1,y1),...]
polygon 40+16n 字节 多边形(类似于封闭路径) ((x1,y1),...)
circle 24 字节 <(x,y),r>(中心点和半径)

Postgres 有一系列丰富的函数和操作符可用来进行各种几何操作, 如缩放、平移、旋转和计算相交等

索引

postgres 提供了多种适用于不同场景的索引供我们选择使用

  • B-tree 索引: CREATE INDEX 命令创建适合于大部分情况的 B-tree 索引。

  • Hash 索引: 只能处理简单等值比较。不论何时当一个索引列涉及到一个使用了 = 操作符的比较时,查询规划器将考虑使用一个 Hash 索引。下面的命令将创建一个 Hash 索引:

    CREATE INDEX name ON policy USING HASH (column);
    
  • GiST 索引: GiST 索引并不是一种单独的索引,而是可以用于实现很多不同索引策略的基础设施 postgres 的标准库提供了用于多种二维几何数据类型的 GiST 操作符。

    -- 它将找到离给定目标点最近的10个位置。
    SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
    
  • GIN 索引: GIN 索引是“倒排索引”,它适合于包含多个组成值的数据值,例如数组。倒排索引中为每一个组成值都包含一个单独的项,它可以高效地处理测试指定组成值是否存在的查询。

    CREATE INDEX idxgin ON policy USING gin (source,destination); -- 在 source 和 destination字段上建立联合索引 遵循最左法则
    
  • BRIN 索引: (块范围索引的缩写)存储有关存放在一个表的连续物理块范围上的值摘要信息。与 GiST、SP-GiST 和 GIN 相似,BRIN 可以支持很多种不同的索引策略,并且可以与一个 BRIN 索引配合使用的特定操作符取决于索引策略。

运维

这里会记录一些我常遇到的 postgres 问题和命令

postgres 数据备份恢复

#导出sql文件
pg_dump -U postgres nap > nap.sql

#导入
drop database nap; #删除原来的库

CREATE DATABASE nap;

psql -U postgres -d nap -f nap.sql
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    342 引用 • 708 回帖
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    110 引用 • 54 回帖
2 操作
Gakkiyomi2019 在 2020-12-21 14:09:40 更新了该帖
Gakkiyomi2019 在 2020-12-11 16:36:23 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...