postgresql 学习笔记 下载安装 参数配置 主从流复制读写分离 备份与恢复 增删改基本操作 python操作 用户与权限

官网:https://www.postgresql.org

中文社区: http://www.postgres.cn/

官方文档阅读: https://www.postgresql.org/docs/

一、简介:

  • 历史、历史版本

​ PostgreSQL 是对象关系型数据库管理系统,任何人都可以以任何目的免费使用、修改和分发 PostgreSQL。
1989 年6月发布了版本 1,1990年6月发布了版本2, 版本 3 在1991年出现,增加了多存储管理器的支持, 并且改进了查询执行器、重写了规则系统。
​ 在 1993 年间,随着用户的增加, 用于源代码维护的时间日益增加并占用了太多本应该用于数据库研究的时间,为了减少支持的负担,伯克利的 POSTGRES 项目在版本 4.2 时正式终止。
​ 在 1994 年,Andrew Yu 和 Jolly Chen 向 POSTGRES 中增加了 SQL 语言的解释器。并随后用新名字Postgres95 将源代码发布到互联网上供大家使用, 成为最初 POSTGRES 伯克利代码的开源继承者。
Postgres95 的源代码代码量减少了25%,提高了性能和可维护性,比 POSTGRES 的版本 4.2 快 30-50%,并在前后端上进行了迭代更新。
​ 到了 1996 年,更名为 PostgreSQL,版本号也从 6.0 开始。

  • 优势与特色

    PostgreSQL 与 MySQL 相比,优势何在? - luikore的回答 - 知乎
  1. PostgreSQL和oracle是进程模式,MySQL是线程模式 128。
  2. PostgreSQL中有优秀的连接池软件,如 pgbouncer 和 pgpool。pgadmin
  3. Standby 在应用日志同步时,还可以提供只读服务,这对做读写分离很有用。
  4. PostgreSQL 中可以有部分索引,也就是只能表中的部分数据做索引,create index 可以带 where 条件。同时 PostgreSQL 中的索引可以反向扫描,所以在PostgreSQL中可以不必建专门的降序索引了 。

二、安装:

​ 开源社区开发,迭代迅速,虽然 9 版本稳,但经过几个版本迭代,无论是性能还是功能上,更高的版本都有极大提升。而 11 版本是 2018 年出品,至今已有不错完善。最新版为 12 版本还在不断完善测试。
​ 中文版官方文档第9版已经全部翻译,10、11也在不断翻译完善中,可见pg的开源社区强大。

sudo vim /etc/apt/sources.list.d/pgdg.list

deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

sudo apt-get update

sudo apt install postgresql-11

service postgresql status  

安装完成后,默认会:

(1)创建名为"postgres"的Linux用户

(2)创建名为"postgres"、不带密码的默认数据库账号作为数据库管理员

(3)创建名为"postgres"的表

# 查看日志
cat /var/log/postgresql/postgresql-11-main.log

三、卸载:

sudo apt-get purge 'postgresql-*'
sudo apt-get autoremove 'postgresql-*'

四、Initdb、目录介绍

​ 一般使用环境变量PGDATA指向数据目录的根目录。这个目录是在安装时指定的,所以
在安装时需要指定一个合适的目录作为数据目录的根目录,而且,每一个数据库实例都需要
有这么一个目录。目录的初始化是使用initdb来完成的。

完成后,这数据根目录下就会生成三个配置文件。

  • postgresql.conf:数据库实例的主配置文件,基本上所有的配置参数都在此文件中。

  • pg_hba.conf:认证配置文件,配置了允许哪些IP的主机访问数据库,认证的方法是
    什么等信息。

  • pg_ident.conf: "ident”认证方式的用户映射文件。

    此目录下还会生成如下一些子目录。

  • base:默认表空间的目录。

  • global: 一些共享系统表的目录。

  • pg_clog: commit log 的目录。

  • pglog:系统日志目录,在查询一些系统错误时就可查看此目录下的日志文件。

  • pg_stat_tmp:统计信息的存储目录。

  • pg_tblsp:存储了指向各个用户自建表空间实际目录的链接文件。

  • pg_twophase:使用两阶段提交功能时分布式事务的存储目录。

  • pg_xlog: WAL日志的目录。

五、进入PG,基本命令

  1. 进入
# 默认只有 postgres 能够使用 psql 命令
sudo -u postgres psql 
  1. 基本命令
\password:设置密码
\q:退出
\h:查看SQL命令的解释,比如\h select。
\?:查看psql命令列表。
\l:列出所有数据库。
\c [database_name]:连接其他数据库。
\d:列出当前数据库的所有表格。
\d [table_name]:列出某一张表格的结构。
\du:列出所有用户。
\e:打开文本编辑器。
\conninfo:列出当前数据库和连接的信息。
  1. PG开启、重启、停止
 sudo /etc/init.d/postgresql start   # 开启
 sudo /etc/init.d/postgresql stop    # 关闭
 sudo /etc/init.d/postgresql restart # 重启

六、远程连接PG

psql -h指定服务器 -U指定用户 -d指定数据库 -p指定端口

七、修改Linux用户的密码

sudo -u postgres passwd

八、配置数据库以允许远程连接访问

  1. 修改监听地址
sudo vim /etc/postgresql/11/main/postgresql.conf 
# 将 #listen_addresses = 'localhost' 的注释去掉并改为 listen_addresses = '*' 
  1. 修改可访问用户的IP段
sudo vim /etc/postgresql/11/main/pg_hba.conf 
# 在文件末尾添加: host all all 0.0.0.0 0.0.0.0 md5 ,表示允许任何IP连接
  1. 重启数据库
sudo /etc/init.d/postgresql restart

九、创建用户

# 进入客户端
sudo -u postgres psql

# 创建用户并设置密码
create user videt with password '123456';

# 将 videt 改为超级管理员
alter user videt with superuser; 

十、创建、删除数据库

# 创建所有者为 videt 的数据库 testdb
create database testdb owner videt;
# 将 testdb 数据库的所有权限赋予 videt,否则 videt 只能登录psql,没有任何数据库操作权限:
grant all privileges on database testdb to videt;
# 删除数据库
drop database testdb

十一、基本数据库操作命令

  1. 数据类型介绍

    • 数值类型
    • 货币类型(money 类型存储带有固定小数精度的货币金额。)
    • 字符类型
    • 日期、时间类型
    • 布尔类型
    • 枚举类型
    • 几何类型(点、矩形、圆…)
    • 网络地址类型
    • 位串类型(bit)
    • 文本搜索类型(支持全文搜索,不太了解)
    • UUID类型
    • XML类型
    • JSON类型
    • 数组类型
    • 复合类型(不太了解)
    • 范围类型(daterange、numrange…)
    • 对象标识符类型(不太了解)
    • 伪类型(不太了解)
    名字 别名 描述
    bigint int8 有符号的8字节整数
    bigserial serial8 自动增长的8字节整数
    bit [ (*n*) ] 定长位串
    bit varying [ (*n*) ] varbit 变长位串
    boolean bool 逻辑布尔值(真/假)
    box 平面上的普通方框
    bytea 二进制数据(“字节数组”)
    character [ (*n*) ] char [ (*n*) ] 定长字符串
    character varying [ (*n*) ] varchar [ (*n*) ] 变长字符串
    cidr IPv4或IPv6网络地址
    circle 平面上的圆
    date 日历日期(年、月、日)
    double precision float8 双精度浮点数(8字节)
    inet IPv4或IPv6主机地址
    integer int, int4 有符号4字节整数
    interval [ *fields* ] [ (*p*) ] 时间段
    json 文本 JSON 数据
    jsonb 二进制 JSON 数据,已分解
    line 平面上的无限长的线
    lseg 平面上的线段
    macaddr MAC(Media Access Control)地址
    macaddr8 MAC (Media Access Control) 地址 (EUI-64 格式)
    money 货币数量
    numeric [ (*p*, *s*) ] decimal [ (*p*, *s*) ] 可选择精度的精确数字
    path 平面上的几何路径
    pg_lsn PostgreSQL日志序列号
    point 平面上的几何点
    polygon 平面上的封闭几何路径
    real float4 单精度浮点数(4字节)
    smallint int2 有符号2字节整数
    smallserial serial2 自动增长的2字节整数
    serial serial4 自动增长的4字节整数
    text 变长字符串
    time [ (*p*) ] [ without time zone ] 一天中的时间(无时区)
    time [ (*p*) ] with time zone timetz 一天中的时间,包括时区
    timestamp [ (*p*) ] [ without time zone ] 日期和时间(无时区)
    timestamp [ (*p*) ] with time zone timestamptz 日期和时间,包括时区
    tsquery 文本搜索查询
    tsvector 文本搜索文档
    txid_snapshot 用户级别事务ID快照
    uuid 通用唯一标识码
    xml XML数据
  2. 基本操作命令

    # 创建新表 
    CREATE TABLE videt(name VARCHAR(20), signup_date DATE);
    # 插入数据 
    INSERT INTO videt(name, signup_date) VALUES('张三', '2013-12-22');
    # 查询数据 
    SELECT * FROM videt;
    # 更新数据 
    UPDATE videt set name = '李四' WHERE name = '张三';
    # 删除数据 
    DELETE FROM videt WHERE name = '李四' ;
    # 添加字段 
    ALTER TABLE videt ADD email VARCHAR(40);
    # 更新表结构 
    ALTER TABLE videt ALTER COLUMN signup_date SET NOT NULL;
    # 字段重命名
    ALTER TABLE videt RENAME COLUMN signup_date TO signup;
    # 删除字段 
    ALTER TABLE videt DROP COLUMN email;
    # 数据表重命名
    ALTER TABLE videt RENAME TO backup_tbl;
    # 删除数据表 
    DROP TABLE IF EXISTS backup_tbl;
    # 聚合函数、视图、外键、事务、索引等操作 与 mysql 一致或相似
    

十二、配置参数

​ pg 配置优化

​ pg 配置参数详解

​ Linux系统、版本、CPU、内存查看、硬盘空间

  1. 实操配置

    # 监听
    listen_addresses= '*'                          
    
    # 日志
    logging_collector= on
    log_directory= 'pg_log'                 
    # 只保留 7 天日志,循环覆盖,还有其他模式
    log_filename= 'postgresql-%a.log'
    log_truncate_on_rotation= on     		                                
    log_rotation_age= 1d             		                                
    log_rotation_size= 0 
    
    # 中国时区
    log_timezone= 'PRC'   
    timezone= 'PRC' 
    
    # 最大连接数  # 物理内存(GB)*1000*(1/4)/5  # 注意从库需大于此数 
    max_connections= 750   
    # 指定分布式事务中两步提交准备事务的最大数量,参数值不应该小于max_connections参数值,这样每一个session都可以至少有一个可用的准备事务。
    # max_prepared_transactions=max_connections  
    max_prepared_transactions= 750     
    
    # 给超级用户提供的连接数
    superuser_reserved_connections= 10
    
    # TCP 空闲多长时间发 kpalive,多久没回复后发送下一个kpalive,直到多少次连接中断
    tcp_keepalives_idle= 180
    tcp_keepalives_interval= 10
    tcp_keepalives_count= 3
    
    # 用户密码认证及加密方法
    password_encryption= md5  
    
    # 大内存页
    huge_pages= try  
    
    # 主从
    # 启动搭建Hot Standby
    wal_level= hot_standby
    # 从库打开
    # hot_standby= on
    # 主库最多可以有多少个并发的standby数据库,需要设置为一个大于0的数
    max_wal_senders= 10 
    # 防止主库生成 WAL 日志太快,日志还没有来得及传送到 standby 就被覆盖,一个WAL日志文件16M,wal_keep_segments设置为64,将为standby库保留64个WAL日志文件,占用16*64=1GB磁盘空间
    wal_keep_segments=64
    # 主库成为新主库的从库?
    wal_log_hints=on
    
    # 提升性能 30%
    fsync=off
    # 声明提交一个事务是否需要等待其把 WAL 日志写入磁盘后再返回
    synchronous_commit= off
    # 日志缓存区的大小,-1 时自动计算
    wal_buffers=-1
    
    # 共享缓冲区
    shared_buffers= 4GB  # IF hugepage:主机内存*(1/4) ELSE:min(32GB,主机内存*(1/4))  
    # 工作内存
    work_mem= 4MB      # max(min(物理内存/4096, 64MB), 4MB)             
    # 维护工作内存
    maintenance_work_mem= 1GB             # min( 8G, (主机内存*1/8)/max_parallel_maintenance_workers )              
    autovacuum_work_mem= 1GB              # min( 8G, (主机内存*1/8)/autovacuum_max_workers )
     
    max_wal_size= 48GB                    # shared_buffers*2  
    min_wal_size= 12GB                    # shared_buffers/2  
    
    # 优化器假设一个查询可以用的最大内存
    effective_cache_size= 8GB            # 主机内存/2 
    
    # 以下多为新增参数
    # 设置维护命令(例如 CREATE INDEX) 允许的最大并行进程数
    # max_parallel_maintenance_workers= 2   # min( max(2, CPU核数/2) , 16 )   	
    # ax_parallel_workers_per_gather= 2    # min( max(2, CPU核数-4) , 24 )   
    
    # max_parallel_workers_per_gather+max_parallel_maintenance_workers 值应小于或等于 max_parallel_workers。
    # max_parallel_workers= 2              # max(2, CPU核数-4)  
    
    # max_sync_workers_per_subscription = 8  # min ( 32 , max(2, CPU核数-4) )    
       
    # autovacuum_max_workers= 8             # max(min( 8 , CPU核数/2 ) , 5)   
    # checkpoint 参数与commit
    # commit_delay= 0                       # range 0-100000, in microseconds
    # commit_siblings= 5 
    
  2. 配置文件说明

    auto.conf 配置文件优先级高于 conf 文件。值得注意的是 auto.conf这个文件必须在 psql 中使用 alter system 来修改,而conf可以直接在文本编辑器中修改。

  3. 查看配置参数是否需要重启数据库

    select namer context from pg_settings where name like '配置参数';
    

    需要重启:postmaster、

    不需要重启:sighup、backend

    特殊参数:internal 只读、superuser超级用户修改且只会影响自身、user只会影响自身连接

  4. 主要的连接配置项

    1. listen_addresses - 默认 localhost,常配 *
    2. port - 默认 5432
    3. max_connections - 最大并发连接数,只能服务器启动时设置。hot standby 此值需大于 master。
    4. superuser_reserved_connections - 超级用户连接数。普通用户连接数=max_conn - super。
    5. tcp_keepalives_idle - 在一个TCP连接中空闲多长时间后会发送一个 keepalive 报文。默认值为0,系统会将该参数设置为 2 小时。
    6. tcp_keepalives_interval - 在一个空闲TCP连接中,定义在发送第一个TCP keepalive包后如果在该参数给定的时间间隔内没有收到对端的回包,则开始发送第二个TCP keepalive包,若在给定的时间段内还没有回包则发送第三个keepaliv包,直到达到 tcp_keepalives_count 次后仍没有收到回包,则认为连接已中断,关闭连接。
    7. tcp_keepalives_count

    tcp 推荐配置:

    1. tcp_keepalives_idle = 180
    2. tcp_keepalives_interval = 10
    3. tcp_keepalives_count = 3
  5. 主要的内存配置项

    1. shared_buffers - 共享内存缓冲区数量/大小。 postgresql 对数据操作时都要先将数据从磁盘读取到内存中,然后进行更新,最后再将数据写回磁盘。shared_buffers的功能就是用于存放从磁盘读取的数据。 此缓冲区为缓存数据块所用,且是放在共享内存中的。该值必须大于16,并且至少是 max_connections 的两倍。通常合理的 shared_buffers 值设为物理内存的 25%~40% 。再大可能与文件系统缓存出现双缓存问题。

    2. **temp_buffers - 临时缓冲区, 属于本地内存。用于数据库会话访问临时表数据,**系统默认值为8M。可以在单独的 session 中对该参数进行设置,尤其是需要访问比较大的临时表时,将会有显著的性能提升。

    3. **work_mem - 工作内存或者操作内存。**其负责内部的sort和hash操作,合适的work_mem大小能够保证这些操作在内存中进行。定义太小的话,sort或者hash操作将需要与硬盘进行swap,这样会极大的降低系统的性能;太大的话致使在能够在内存中完成的操作数量减少,其他的部分需要与磁盘进行swap操作,增加IO降低性能。系统提供的默认值是1M,在实际的生产环境中,要对系统监控数据进行分析,作出最好的选择。
      大致有两种方式:估计方法与计算方法。第一种是可以根据业务量的大小和类型,一般语句运行时间,来粗略的估计一下。第二种方式是通过对数据库的监控,数据采集,然后计算其大小。总之合适的大小对系统的性能至关重要。 在实际的维护中可以通过explain analyze分析语句的work_mem大小是否合适。在语句中设置work_mem参数的大小可以充分利用内存,提高语句的执行效率。
      对于work_mem内存分配时还要考虑数据库的并发情况,max_connections决定了系统的最大的并发连接数。不论如何调整work_mem都要考虑max_connections*work_mem+shared_buffers+temp_buffers+maintenance_work_mem+操作系统所需内存不能够超过整个的RAM大小,这是非常重要的。
      work_mem参数对系统的性能是如此的重要,让其实时的适应数据库的运行状况显的不太可能,但是可以通过对数据库运行周期的监控,总结相应的数据,然后定制一个专用的脚本,专门用来修改work_mem的大小,使其阶段性的更加适应系统的状况

    4. **maintence_work_mem - 维护工作内存 。**主要用于维护操作使用的最大内存数,如 vacuum、create index、alert table add foreign key。 在对整个数据库进行VACUUM或者较大的index进行重建时,适当的调整该参数非常必要。 通常情况实例不会有太多这样的操作,可以设置的比 work_mem 大,以提高维护操作的速度。

      在启用了autoacuum功能的情况下,该参数不能配置的过大。

    5. max_stack_depth - 声明服务器执行堆栈的最大安全深度, 通常保持默认值。

  6. 配置日志策略

    这些参数修改都是需要重启生效的

    1. 打开日志收集

      logging_collector = on
      log_directory = 'pg_log' 
      
    2. 日志策略

      1. 每天生成一个新的文件

        log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
        log_truncate_on_rotation = off
        log_rotation_age = 1d
        log_rotation_size = 0
        
      2. 每当日志写满一定的大小,(如10M),则切换一个日志

        log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
        log_truncate_on_rotation = off
        log_rotation_age = 0
        log_rotation_size = 10M
        
      3. 只保留七天的日志,进行循环覆盖

        log_filename = 'postgresql-%a.log'
        log_truncate_on_rotation = on
        log_rotation_age = 1d
        log_rotation_size = 0
        
  7. 预写式日志的配置项

    1. **wal level - 决定有多少信息可写入WAL 日志中。**改变这个参数需要重启数据库服务。默认值 “minimal”,即只写入在数据库崩溃或突然关机后进行恢复时所需要的信息。设置为“archive”,会添加WAL归档需要的记录;设置为“ hot_standby”,则会添加一些备库只读查询需要的信息。如果想使用 WAL 日志做归档,用归档作增量恢复,则需要把此参数设置为“archive”;如果要搭建hot_standby备库,则需要把此参数设置为“hot_standby”。

    2. **fsync - 表示是否使用fsync()系统调用(或等价调用),把文件系统中的脏页刷新到物理磁盘,确保数据库在操作系统或者硬件崩滞的情况下可恢写到谁与改变这个参数需要重新装载配置文件。**此参数默认值为“on”,大多数情况下,都应该把这个参数设置为 “on”。但当此数据库不是很重要,或者此数据库中的数据很容易从其他系统中重建出来时,为了提高性能,可以把此参数设置为“off”。

    3. synchronous_commit - 声明提交一个事务是否需要等待其把 WAL 日志写入磁盘后再返回,默认值是“on”。为了事务的安全,通常都应当设置为 “on”。一般用户可以直接改变此参数值,且在提交一些不重要的事务时,可以先把此参数设置为 “off”,然后再提交,这样可以提高性能。

    4. **wal_sync_method - 用来指定向磁盘强制更新 WAL 日志数据的方法。**此选项一般保持默认值就可以了。如果 fsync 的设置为“off”,那么这个设置就没有意义。

    5. **wal_buffers - 指定放在共享内存里用于存储 WAL 日志的缓冲区数目。**默认值为 8,即 64KB 改变这个参数需要重启数据库服务。这个设置值只需要能够保存一次事务生成的 WAL 数据即可,这些数据在每次事务提交时都会写入磁盘。通常此参数设置为 8〜128 (即64KB〜1MB)就可以了。

    6. ········

十三、创建用户、角色

  1. 创建用户

    # CREATE USER指令创建的用户默认是有登录权限的,CREATE ROLE没有。
    CREATE ROLE rolename;
    CREATE USER username;
    # CREATE ROLE username WITH LOGIN == CREATE USER username;
    
  2. 权限介绍

    key
    superuser 超级用户
    createuser 是否有执行 创建数据库的权限
    inherit 是否有创建其他用户的权限
    login 是否可以连接到数据库
    in role role_name 加入到角色

    以上 key 都有反向 key,如 nologin 等

  3. 权限管理
    pg中任何数据库对象都是属于某个用户的。自己创建的数据库对象,自己拥有该对象的所有权限,其他人没有删除、修改该对象的权限。

    修改用户权限的两种方式

    1. alter role

      ALTER ROLE username WITH NOLOGIN;
      
    2. grant
      grant 拥有两个功能:

      1. 让某个用户加入某个角色,从而拥有该角色的权限。

      2. 把某些数据库逻辑结构对象的操作权限赋予某个用户或某个角色

        grant some_privileges on database_object_type object_name to role_name;
        

        创建只读用户:

        先把创建表权限收回
        			revoke create on schema public from public;
        			create user readonly with password ‘123456’;
        			grant select on all tables in schema public to readonly;
        			# 上述命令将现有表权限赋予 readonly,新增表权限也自动赋予的话执行如下:
        			alter default privileges in schema public grant select on tables to readonly;
        			# 上述只给schema下名为public的表赋予了只读权限,访问其他schema下的表则重复执行上述步骤
        

十四、备份与恢复

  1. pg_dump
  • 备份数据库

    pg_dump mydb > db.sql
    
  • 备份数据表

    pg_dump -t mytab mydb > db.sql
    
  1. pg_dumpall

    • 备份所有数据库

      pg_dumpall > db.out
      
    • 恢复所有数据库

      psql -f db.out postgres
      
  2. pg_restore
    • 恢复数据库

      pg_restore -d newdb db.sql
      
  3. 文件系统级备份

    使用压缩工具直接将数据库的数据目录进行备份

    tar -cf backup.tar /usr/local/pgsql/data
    
  4. 连续归档和时间点恢复

    1. pg_basebackup(全备)

      pgsql 提供的一个方便基础备份的工具,经常用来搭建流复制环境,属于物理备份,逻辑备份是 pg_dump 可远程执行。

      pg_basebackup -h host -D 数据目录 -U postgres -F p -l label -P -v -X fetch  -R
      # 示例。 需要注意的是,指定目录必须为空
      pg_basebackup -h 10.5.5.235 -D /var/lib/postgresql/11/main -U postgres -F p -l label -P -v -X fetch  -R
      
      • -P 或 -progress :允许在备份过程中实时地打印备份的进度。
      • -v 打印出备份进度的详细信息
      • -l label 或 -label=label :指定备份的一个标识
      • -X fetch 备份时会把备份中产生的xlog文件也自动备份出来
      • -R 备份生成一个 recovery.conf,启动备份库时只需简单修改此文件
      • 理论上一个数据库可以被几个 pg_basebackup 同时连接上去,但为了不影响主库的性能,
        建议最好还是一个数据库上同时只有一个 pg_basebackup 在做备份。
      • PostgreSQL9.2 之后支持级连复制,所以在9.2及之后的版本中,pg_basebackup 也可以从另一个Standby库上做基础备份,但从Standby备份有以下一些注意事项:
        • 备份中没有备份历史文件。
        • 不确保所有需要的 WAL 文件都备份了,如果想确保,需要加命令行参数“-X”。
        • 如果在备份过程中Standby被提升为主库,则备份会失败。
        • 要求主库中打开了 “ fiill_page_writes”参数,WAL 文件不能被类似 pg_compresslog 的工具去掉fiill-page writes信息
    2. PostgreSQL利用全备与WAL日志恢复数据库

十五、主从配置(流复制)

  • PITR

    ​ PostgreSQL 在数据目录的 pg.xlog 子目录中始终维护着一个WAL日志文件。这个日志文件用于记录数据库数据文件的每次改变。当初设计这个日志文件的主要目的是为了在数据库异常崩溃后,能够通过重放最后一次checkpoint 点之后的日志文件,把数据库推到一致状态。

    ​ 事实上,这种日志文件机制,也提供了一种数据库热备份方案:在把数据库使用文件系统的方式备份出来的同时把相应的WAL日志也备份出来。虽然直接拷贝数据库数据文件会导致拷贝出来的文件不一致(比如拷贝的多个数据文件不是同一个时间点文件;拷贝一个 8KB 的数据块时,也存在不一致的情况:假设刚拷贝完前 4KB 的块,数据库又写了后 4KB 的块内容,那么所拷贝的块就不是一个完整的数据块),但因为有了 WAL日志,即使备份出来的数据块不一致,也可以重放备份开始后的 WAL 日志,把备份的内容推到一致状态。由此可见,有了 WAL 日志之后,备份数据库时不再需要完美的一致性备份了,备份中任何数据的非一致性都会被重放 WAL 日志文件进行纠正,所以在备份数据库时可以通过简单的 cp 命令或 tar 等拷贝、备份文件来实现数据库的在线备份。不停地重放 WAL 日志就可以把数据推到备份结束后的任意一个时间点,这就是基于时间点的备份,英文为“Point-in-Time Recovery”,缩写为 PITR。

    ​ 使用简单的 cp 命令或其他命令把数据库给在线拷贝出来的备份,被称为基础备份。后续 WAL 日志的备份与此基础备份构成一个完整备份。把基础备份恢复到另一台机器,然后不停地从原始数据库机器上接收 WAL 日志,在新机器上持续重放 WAL 日志,这样就可以在任何时间内在另一台机器上打开这个新产生的数据库,它拥有当前数据库的最新数据状态。这个新机器上的数据库被称为 Standby 数据库。

    ​ 当前的主数据库出现问题或主数据库的机器出现故障无法正常提供服务时,可以把 Standby 数据库打开提供服务,从而实现高可用。

    ​ 把 WAL 日志传送到另一台机器上的方法有两种,一种是通过 WAL 归档日志实现,一种是被称为流复制的方法。

  • 流复制

    ​ 使用流复制时,只要Primary数据库一产生日志,就会马上传递到Standby数据库。流复制传递日志的方式有两种,一种是异步方式,一种是同步方式。

    ​ 若使用同步方式,在Primary数据库提交事务时,一定会等到WAL 日志传递到Standby后才会返回,这样可以做到Standby数据库完全与Primary数据库同步,没有一点落后,当主备库切换时使用同步方式可以做到零数据丢失。

    ​ 异步方式,则是事务提交后不必等日志传递到Standby就即可返回,所以Standby数据库通常也只比Primary数据库落后很少(如几秒)的时间。

    ​ 流复制功能不仅能传递WAL日志,也能传递其他数据,如数据文件。

  • 异步流复制

    1. 编辑 postgres.conf

      要使用流复制,主库一定要配置的参数

      # max_wal_senders 最多有几个流复制连接
      max_wal_senders= 5
      # 流复制超时时间
      wal_sender_timeout=60s
      # 最大连接,此值从库必须大于主库
      max_connections=101
      # 热备模式
      wal_level= hot_standby
      # WAL日志归档 归档模式与归档命令
      # archive_mode= on
      # archive_command='scp %p [email protected],100:/backup/pgarch/%f'
      # 使用上面拷贝WAL文件的方式来同步主、备数据库之间数据时,备库会落后主库一个WAL日志文件,具体落后多长时间取决于主库上生成一个完整的WAL文件所需要的时间。
      
    2. 编辑 pg_hba.conf

      # 增加连接许可,trust不需要密码
      # 允许所有用户从 10.5.5.235/32 的网络上发起到本数据库的流复制连接,使用md5的密码认证.
      host  replication  all 10.5.5.235/32	md5
      host  all          all 0.0.0.0/0        trust
      
    3. 清空从库数据目录,执行 pg_basebackup 备份至备份机器数据目录下

    4. 修改 全备后从库生成的 recovery.conf

      # 该节点是从库
      standby_mode=on
      # 从库机器信息和连接用户
      primary_coninfo='host=host port=5432 user=用户名 password=密码'
      # 说明恢复到最新状态
      recovery_target_timelint='latest'
      
    5. 编辑修改 postgresql.conf

      # 热备模式
      wal_level=hot_standby
      # 最大连接,从库要大于主库
      max_connections=101
      # 说明本机不仅用于数据归档,还可以用于数据查询?
      hot_standby=on
      # 流备份的最大延迟时间
      max_standby_streaming_delay=30s
      # 向主机汇报本机状态的间隔时间
      wal_receiver_status_interval=10s
      # 出现错误复制向主机汇报
      hot_standby_feedback=on
      
    6. sudo service postgresql restart

    7. 查看相关进程验证是否配置成功

      ps aux | grep postgres
      
    8. 在主库查看主从情况

      select client_addr, sync_state from pg_stat_replication;
      
  • 同步流复制

    PostgreSQL的流复制是异步的,异步的缺点是Standby上的数据落后于主库上的数据,
    如果使用Hot Standby做读写分离,就会存在数据一致性的问题,这对于一些一致性较高的应
    用来说是不可接受的。所以PostgreSQL从9.1版本之后提供了同步流复制的架构。同步复制
    要求在数据写入Standby数据库后,事务的commit才返回,所以Standby库出现问题时,会
    导致主库被hang住。解决这个问题的方法是启动两个Standby数据库,这两个Standby数据
    库只要有一个是正常的,就不会让主库hang住。所以在实际应用中,同步流复制,总是有1个主库和2个以上的Standby库。

    略。

  • 检查流复制情况

    select * from pg_stat_replication;
    
    列名称 类 型 解 释
    pid integer 数据库上WAL sender进程的进程ID
    usesysid oid 登录主库的流复制用户的OID
    usename name 登录主库的流复制用户的名称
    applicationname text 流复制连接中连接参数application name指定的字符申
    clientaddr inet Standby的IP地址
    clienthostname text Standby的主机名。注意,只有打开了 log_hostname和使用了 IP 连接时,这列才会显示主机名,否则显示为空
    client port integer 流复制连接中Standby端的socket端口
    backendstart timestamp with time zone WAL sender进程启动的时间。实际也是Standby连接过来的时间, 因为只有Standby连接过来时,才会启动一个WAL sender进程,连 接中断后,WAL Sender进程也会中止
    state text WAL sender进程的状态
    sentlocation text 在流复制连接上发送WAL时的发送位置
    writelocation text Standby端写WAL日志的位置
    flushjocation text Standby端写WAL日志刷新到磁盘的位置
    replay location text Standby端重放WAL日志的位置
    sync priority integer 同步复制时不同Standby的优先级,对于异步复制,此字段总是0
    sync__state text 同步的状态,可以为“sync”、“ potential”、° async"
  • 流复制的注意事项

    • wal_keep_segments

      使用流复制建好备库后,如果备库由于各种原因接收日志较慢,而主库很快,这容易导
      致主库上的WAL日志还没有传递到备库就被回卷覆盖掉了,如果被覆盖掉的WAL日志文件
      又没有归档备份,那么备库就再也无法与主库同步了,这会导致备库需要重新搭建。

    • vacuum_defer_cleanup_age

十六、Python操作

psycopg2是非常小,快速,稳定的。 您不需要单独安装此模块,因为默认情况下它会随着Python 2.5.x版本一起发布。

  • 安装

    pip3 install python-psycopg2
    
  • 连接到数据库

    import psycopg2
    
    #  如果数据库不存在,那么它将自动创建,最后将返回一个数据库对象
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    
    print("Opened database successfully")
    
  • 创建表

    import psycopg2
    
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    print("Opened database successfully")
    
    cur = conn.cursor()
    cur.execute('''CREATE TABLE COMPANY
           (ID INT PRIMARY KEY     NOT NULL,
           NAME           TEXT    NOT NULL,
           AGE            INT     NOT NULL,
           ADDRESS        CHAR(50),
           SALARY         REAL);''')
    print("Table created successfully")
    
    conn.commit()
    conn.close()
    
  • 插入操作

    import psycopg2
    
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    print("Opened database successfully")
    
    cur = conn.cursor()
    
    cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
          VALUES (1, 'Paul', 32, 'California', 20000.00 )");
    
    cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
          VALUES (2, 'Allen', 25, 'Texas', 15000.00 )");
    
    cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
          VALUES (3, 'Teddy', 23, 'Norway', 20000.00 )");
    
    cur.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
          VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 )");
    
    conn.commit()
    print("Records created successfully");
    conn.close()
    
  • SELECT操作

    import psycopg2
    
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    print("Opened database successfully")
    
    cur = conn.cursor()
    
    cur.execute("SELECT id, name, address, salary  from COMPANY")
    rows = cur.fetchall()
    for row in rows:
       print("ID = ", row[0])
       print("NAME = ", row[1])
       print("ADDRESS = ", row[2])
       print("SALARY = ", row[3], "\n")
    
    print("Operation done successfully");
    conn.close()
    
  • 更新操作

    import psycopg2
    
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    print("Opened database successfully")
    
    cur = conn.cursor()
    
    cur.execute("UPDATE COMPANY set SALARY = 25000.00 where ID=1")
    conn.commit
    print("Total number of rows updated :", cur.rowcount)
    
    cur.execute("SELECT id, name, address, salary  from COMPANY")
    rows = cur.fetchall()
    for row in rows:
       print("ID = ", row[0])
       print("NAME = ", row[1])
       print("ADDRESS = ", row[2])
       print("SALARY = ", row[3], "\n")
    
    print("Operation done successfully");
    conn.close()
    
  • 删除操作

    import psycopg2
    
    conn = psycopg2.connect(database="testdb", user="postgres", password="pass123", host="127.0.0.1", port="5432")
    print("Opened database successfully")
    
    cur = conn.cursor()
    
    cur.execute("DELETE from COMPANY where ID=2;")
    conn.commit
    print("Total number of rows deleted :", cur.rowcount)
    
    cur.execute("SELECT id, name, address, salary  from COMPANY")
    rows = cur.fetchall()
    for row in rows:
       print("ID = ", row[0])
       print("NAME = ", row[1])
       print("ADDRESS = ", row[2])
       print("SALARY = ", row[3], "\n")
    
    print("Operation done successfully");
    conn.close()
    

十七、其它

  • pg_ctl 命令使用

  • 预写式日志(WAL)写优化

    ​ 数据文件修改时,先将操作记录到日志中,数据文件修改后的脏页不会马上刷新至磁盘。在产生 chekpoint 时,会将脏页刷新至磁盘并向日志文件写入检查点记录,确保之前的所有信息都已经写到数据文件。

    • 检查点发生的频率控制:
      • checkpoint_timeout
      • checkpoint_segment
    • PG 检查 timeout 接近 warning 就报警:
      • checkpoint_warning
    • 太多脏页刷新至磁盘导致过多 I/O,导致性能出现大的抖动:
      • checkpoint_completion_target
    • WAL 缓存:
      • wal_buffers 默认 4 MB
  • 热备份其它方案

    文件系统或块设备级别的快照功能,LVM 的快照功能,这要求数据库建立在 LVM 上。

    Logical Volume Manager(逻辑卷管理)

你可能感兴趣的:(PG)