part7.all

一、postgresql架构与原理

1.体系架构概览

PostgreSQL和MySQL相似,也采用典型的C/S模型。
PostgreSQL体系结构分两部分

  • 实例 instance(内存)
  • 磁盘存储

实例 instance 包括

  • 进程(客户端的响应进程,服务端的处理进程)
  • 内存存储结构


    image.png

PostgreSQL是进程架构模型,MySQL是线程架构模型

image.png

2.进程

Postmaster 主进程

  • 它是整个数据库实例的主控制进程,负责启动和关闭该数据库实例。
  • 实际上,使用pg ctl来启动数据库时,pg_ctl也是通过运行postgres来启动数据库的,只是它做了一些包装,更容易启动数据库。
  • 它是第一个PostgreSQL进程,此主进程还会fork出其他子进程,并管理它们。
  • 当用户和PostgreSQL建立连接时,首先是和Postmaster进程建立连接。首先,客户端会发出身份验证的信息给Postmaster进程,Postmaster进程根据消息中的信息进行身份验证判断,如果验证通过,它会fork出一个会话子进程为这个连接服务。
  • 当某个服务进程出现错误的时候,Postmaster主进程会自动完成系统的恢复。恢复过程中会停掉所有的服务进程,然后进行数据库数据的一致性恢复,等恢复完成后,数据库又可以接受新的连接。
    1. 验证功能是通过配置文件pg_hba.conf和用户验证模块来提供。
    2. postmaster 程序是指向postgres的软链接
[root@ubuntu2004 ~]#ll /apps/pgsql/bin/postmaster 
lrwxrwxrwx 1 root root 8 Dec 28 01:19 /apps/pgsql/bin/postmaster -> 
postgres*

BgWriter 后台写进程

  • 为了提高插入、删除和更新数据的性能,当往数据库中插入或者更新数据时,并不会马上把数据持久化到数据文件中,而是先写入Buffer中
  • 该辅助进程可以周期性的把内存中的脏数据刷新到磁盘中

WalWriter 预写式日志进程(类似于mysql的事务日志
有了日志优点安全、速度快,缺点占用磁盘空间大、IO多一次—日志追加写入比直接随机写入数据库快

  • WAL是write ahead log的缩写,WAL log旧版中称为xlog,相当于MySQL中Redo log
  • 预写式日志是在修改数据之前,必须把这些修改操作记录到磁盘中,这样后面更新实际数据时,就不需要实时的把数据持久化到文件中了。即使机器突然宕机或者数据库异常退出,导致一部分内存中的脏数据没有及时的刷新到文件中,在数据库重启后,通过读取WAL日志,并把最后一部分WAL日志重新执行一遍,就能恢复到宕机时的状态了
  • WAL日志保存在pg_wal目录(早期版本为pg_xlog) 下。每个xlog文件默认是16MB,为了满足恢复要求,在pg_wal目录下会产生多个WAL日志,这样就可保证在宕机后,未持久化的数据都可以通过WAL日志来恢复,那些不需要的WAL日志将会被自动覆盖

Checkpointer 检查点进程(数据写入到磁盘)

  • 检查点(Checkpoints)是事务序列中的点,保证在该点之前的所有日志信息都更新到数据文件中。
  • 在检查点时,所有脏数据页都冲刷到磁盘并且向日志文件中写入一条特殊的检查点记录。在发生崩溃的时候,恢复器就知道应该从日志中的哪个点(称做redo 记录)开始做 REDO操作,因为在该记录前的对数据文件的任何修改都已经在磁盘上了。在完成检查点处理之后,任何在redo记录之前写的日志段都不再需要,因此可以循环使用或者删除。在进行WAL 归档的时候,这些日志在循环利用或者删除之前应该必须先归档保存检查点(CKPT)在特定时间自动执行一个检查点,通过向数据库写入进(BgWriter) 传递消息来启动检查点请求。

AutoVacuum 自动清理进程

  • 执行delete操作时,旧的数据并不会立即被删除,在更新数据时,也不会在旧的数据上做更新,而是新生成一行数据。旧的数据只是被标识为删除状态,在没有并发的其他事务读到这些旧数据时,它们才会被清除掉autovacuum lanucher负责回收垃圾数据的master进程,如果开启了autovacuum的话,那postmaster会fork这个进程
  • autovacuum worker 负责回收垃圾数据的worker进程,是lanucher进程fork出来的

PgStat 统计数据收集进程

  • 此进程主要做数据的统计收集工作
  • 收集的信息主要用于查询优化时的代价估算。统计的数据包括对一个表或索引进行的插入、删除、更新操作,磁盘块读写的次数以及行的读次数等。
  • 系统表pg_statistic中存储了PgStat收集的各类统计信息

PgArch 归档进程

  • 默认没有此进程,开启归档功能后才会启动archiver进程
  • WAL日志文件会被循环使用,也就是说WAL日志会被覆盖,利用PgArch进程会在覆盖前把WAL日志备份出来,类似于binlog,可用于备份功能
  • PostgreSQL 从8.X版本开始提供了PITR ( Point-In-Time-Recovery)技术,即就是在对数据厍进行过一次全量备份后,该技术将备份时间点后面的WAL日志通过归档进行备份,将来可以 使用数据库的全量备份再加上后面产生的WAL 日志,即可把数据库向前恢复到全量备份后的任意一个时间点的状态

SysLogger 系统日志进程

  • 默认没有此进程,配置文件 postgresql.conf
    1. 设置参数logging_collect设置为“on”时,主进程才会启动SysLogger辅助进程
    2. 它从Postmaster主进程、所有的服务进程以及其他辅助进程收集所有的stderr输出,并将这些输出写入到日志文件中
  • startup 启动进程
    用于数据库恢复的进程
  • Session 会话进程
    1. 每一个用户发起连接后,一旦验证成功,postmaster进程就会fork—个新的子进程负责连接此用户。
    2. 通常表现为进程形式: postgres postgres [local] idle

2.内存结构

PostgreSQL的内存空间包括共享内存和本地内存两部分

  • 共享内存

    1. PostgreSQL启动后,会生成一块共享内存,共享内存主要用做数据块的缓冲区,以便提高读写性能。WAL日志缓冲区和CLOG(Commit log)缓冲区也存在于共享内存中。除此以外,一些全局信息也保存在共享内存中,如进程信息、锁的信息、全局统计信息等。
    2. PostgreSQL 9.3之前的版本与Oracle数据库一样,都是使用“SystemV”类型的共享内存,但到
    3. PostgreSQL9.3之后,PostgreSQL使用mmap()方式共享内存,好处能使用较大的共享内 存。
      可以通过配置postgresql.conf文件中shared_buffers 指定,默认128M,建议是内存的50%


      image.png
  • 本地内存
    后台服务进程除访问共享内存外,还会申请分配一些本地内存,以便暂存一些不需要全局存储的数
    据。都可以通过在配置postgresql.conf文件中指定这些内存缓冲区主要有以下几类:

    1. temp_buffers :用于访问临时表的本地缓冲区,默认为8M
    2. work_mem:内部排序操作和Hash表在使用临时磁盘文件之前使用的内存缓冲区,默认为4M
    3. maintenance_work_mem:在维护性操作(比如 VACUUM、CREATE INDEX和ALTERTABLE
      ADDFOREIGN KEY 等)中使用的内存缓冲区,默认为64M

范例:查看内存空间

postgres=# show shared_buffers;
 shared_buffers
----------------
 128MB
(1 row)
postgres=# show maintenance_work_mem;
 maintenance_work_mem
----------------------
 64MB
(1 row)
postgres=# show work_mem;
 work_mem
----------
 4MB
(1 row)

3.数据更新过程

image.png
  1. 先将数据库文件中的更改的数据加载至内存
  2. 在内存更新数据
  3. 将日志写入内存WAL的缓存区
  4. 将日志提交,将日志写入操作系统 cache
  5. 同步日志到磁盘
  6. 后台写数据库的更新后的数据到操作系统 cache
  7. 写完数据后,更新检查点checkpoint
  8. 同步数据到磁盘

二、基于流复制完成postgresql的高可用

一、postgresql架构与原理

1.体系架构概览

PostgreSQL和MySQL相似,也采用典型的C/S模型。
PostgreSQL体系结构分两部分

  • 实例 instance(内存)
  • 磁盘存储

实例 instance 包括

  • 进程(客户端的响应进程,服务端的处理进程)
  • 内存存储结构


    image.png

PostgreSQL是进程架构模型,MySQL是线程架构模型

image.png

2.进程

Postmaster 主进程

  • 它是整个数据库实例的主控制进程,负责启动和关闭该数据库实例。
  • 实际上,使用pg ctl来启动数据库时,pg_ctl也是通过运行postgres来启动数据库的,只是它做了一些包装,更容易启动数据库。
  • 它是第一个PostgreSQL进程,此主进程还会fork出其他子进程,并管理它们。
  • 当用户和PostgreSQL建立连接时,首先是和Postmaster进程建立连接。首先,客户端会发出身份验证的信息给Postmaster进程,Postmaster进程根据消息中的信息进行身份验证判断,如果验证通过,它会fork出一个会话子进程为这个连接服务。
  • 当某个服务进程出现错误的时候,Postmaster主进程会自动完成系统的恢复。恢复过程中会停掉所有的服务进程,然后进行数据库数据的一致性恢复,等恢复完成后,数据库又可以接受新的连接。
    1. 验证功能是通过配置文件pg_hba.conf和用户验证模块来提供。
    2. postmaster 程序是指向postgres的软链接
[root@ubuntu2004 ~]#ll /apps/pgsql/bin/postmaster 
lrwxrwxrwx 1 root root 8 Dec 28 01:19 /apps/pgsql/bin/postmaster -> 
postgres*

BgWriter 后台写进程

  • 为了提高插入、删除和更新数据的性能,当往数据库中插入或者更新数据时,并不会马上把数据持久化到数据文件中,而是先写入Buffer中
  • 该辅助进程可以周期性的把内存中的脏数据刷新到磁盘中

WalWriter 预写式日志进程(类似于mysql的事务日志
有了日志优点安全、速度快,缺点占用磁盘空间大、IO多一次—日志追加写入比直接随机写入数据库快

  • WAL是write ahead log的缩写,WAL log旧版中称为xlog,相当于MySQL中Redo log
  • 预写式日志是在修改数据之前,必须把这些修改操作记录到磁盘中,这样后面更新实际数据时,就不需要实时的把数据持久化到文件中了。即使机器突然宕机或者数据库异常退出,导致一部分内存中的脏数据没有及时的刷新到文件中,在数据库重启后,通过读取WAL日志,并把最后一部分WAL日志重新执行一遍,就能恢复到宕机时的状态了
  • WAL日志保存在pg_wal目录(早期版本为pg_xlog) 下。每个xlog文件默认是16MB,为了满足恢复要求,在pg_wal目录下会产生多个WAL日志,这样就可保证在宕机后,未持久化的数据都可以通过WAL日志来恢复,那些不需要的WAL日志将会被自动覆盖

Checkpointer 检查点进程(数据写入到磁盘)

  • 检查点(Checkpoints)是事务序列中的点,保证在该点之前的所有日志信息都更新到数据文件中。
  • 在检查点时,所有脏数据页都冲刷到磁盘并且向日志文件中写入一条特殊的检查点记录。在发生崩溃的时候,恢复器就知道应该从日志中的哪个点(称做redo 记录)开始做 REDO操作,因为在该记录前的对数据文件的任何修改都已经在磁盘上了。在完成检查点处理之后,任何在redo记录之前写的日志段都不再需要,因此可以循环使用或者删除。在进行WAL 归档的时候,这些日志在循环利用或者删除之前应该必须先归档保存检查点(CKPT)在特定时间自动执行一个检查点,通过向数据库写入进(BgWriter) 传递消息来启动检查点请求。

AutoVacuum 自动清理进程

  • 执行delete操作时,旧的数据并不会立即被删除,在更新数据时,也不会在旧的数据上做更新,而是新生成一行数据。旧的数据只是被标识为删除状态,在没有并发的其他事务读到这些旧数据时,它们才会被清除掉autovacuum lanucher负责回收垃圾数据的master进程,如果开启了autovacuum的话,那postmaster会fork这个进程
  • autovacuum worker 负责回收垃圾数据的worker进程,是lanucher进程fork出来的

PgStat 统计数据收集进程

  • 此进程主要做数据的统计收集工作
  • 收集的信息主要用于查询优化时的代价估算。统计的数据包括对一个表或索引进行的插入、删除、更新操作,磁盘块读写的次数以及行的读次数等。
  • 系统表pg_statistic中存储了PgStat收集的各类统计信息

PgArch 归档进程

  • 默认没有此进程,开启归档功能后才会启动archiver进程
  • WAL日志文件会被循环使用,也就是说WAL日志会被覆盖,利用PgArch进程会在覆盖前把WAL日志备份出来,类似于binlog,可用于备份功能
  • PostgreSQL 从8.X版本开始提供了PITR ( Point-In-Time-Recovery)技术,即就是在对数据厍进行过一次全量备份后,该技术将备份时间点后面的WAL日志通过归档进行备份,将来可以 使用数据库的全量备份再加上后面产生的WAL 日志,即可把数据库向前恢复到全量备份后的任意一个时间点的状态

SysLogger 系统日志进程

  • 默认没有此进程,配置文件 postgresql.conf
    1. 设置参数logging_collect设置为“on”时,主进程才会启动SysLogger辅助进程
    2. 它从Postmaster主进程、所有的服务进程以及其他辅助进程收集所有的stderr输出,并将这些输出写入到日志文件中
  • startup 启动进程
    用于数据库恢复的进程
  • Session 会话进程
    1. 每一个用户发起连接后,一旦验证成功,postmaster进程就会fork—个新的子进程负责连接此用户。
    2. 通常表现为进程形式: postgres postgres [local] idle

2.内存结构

PostgreSQL的内存空间包括共享内存和本地内存两部分

  • 共享内存

    1. PostgreSQL启动后,会生成一块共享内存,共享内存主要用做数据块的缓冲区,以便提高读写性能。WAL日志缓冲区和CLOG(Commit log)缓冲区也存在于共享内存中。除此以外,一些全局信息也保存在共享内存中,如进程信息、锁的信息、全局统计信息等。
    2. PostgreSQL 9.3之前的版本与Oracle数据库一样,都是使用“SystemV”类型的共享内存,但到
    3. PostgreSQL9.3之后,PostgreSQL使用mmap()方式共享内存,好处能使用较大的共享内 存。
      可以通过配置postgresql.conf文件中shared_buffers 指定,默认128M,建议是内存的50%


      image.png
  • 本地内存
    后台服务进程除访问共享内存外,还会申请分配一些本地内存,以便暂存一些不需要全局存储的数
    据。都可以通过在配置postgresql.conf文件中指定这些内存缓冲区主要有以下几类:

    1. temp_buffers :用于访问临时表的本地缓冲区,默认为8M
    2. work_mem:内部排序操作和Hash表在使用临时磁盘文件之前使用的内存缓冲区,默认为4M
    3. maintenance_work_mem:在维护性操作(比如 VACUUM、CREATE INDEX和ALTERTABLE
      ADDFOREIGN KEY 等)中使用的内存缓冲区,默认为64M

范例:查看内存空间

postgres=# show shared_buffers;
 shared_buffers
----------------
 128MB
(1 row)
postgres=# show maintenance_work_mem;
 maintenance_work_mem
----------------------
 64MB
(1 row)
postgres=# show work_mem;
 work_mem
----------
 4MB
(1 row)

3.数据更新过程

image.png
  1. 先将数据库文件中的更改的数据加载至内存
  2. 在内存更新数据
  3. 将日志写入内存WAL的缓存区
  4. 将日志提交,将日志写入操作系统 cache
  5. 同步日志到磁盘
  6. 后台写数据库的更新后的数据到操作系统 cache
  7. 写完数据后,更新检查点checkpoint
  8. 同步数据到磁盘

二、基于流复制完成postgresql的高可用

实验环境
192.168.217.135 Master
192.168.217.134 Standby

1.内核优化

[root@pgsql-master ~]#vim /etc/sysctl.conf 
kernel.shmmax = 68719476736  #(CentOS6以前版本默认值,CentOS7默认为18446744073692774399)
kernel.shmall = 4294967296  #(CentOS6以前版本默认值,CentOS7默认为18446744073692774399)
kernel.shmmni = 4096
kernel.sem = 50100 64128000 50100 1280
fs.file-max = 7672460
net.ipv4.ip_local_port_range = 9000 65000
net.core.rmem_default = 1048576
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576
[root@pgsql-master ~]#vi /etc/security/limits.conf
* - nofile 100000
* - nproc 100000
* - memlock 60000

2.打开远程连接

[root@rocky8 ~]#vim /pgsql/data/postgresql.conf
listen_addresses = '*'  #修改此行中的localhost为 *
#listen_addresses = '0.0.0.0' #或者修改为 0.0.0.0
[root@rocky8 ~]#vim /pgsql/data/pg_hba.conf
host   all             all             ::1/128                 trust
#上面行的后面加一行
host   all             all             0.0.0.0/0               md5

3.主节点配置

[postgres@pgsql-master root]$vi /pgsql/data/pg_hba.conf 
host    replication     repluser        0.0.0.0/0               md5

[postgres@pgsql-master root]$vi /pgsql/data/postgresql.conf
synchronous_standby_names = '*'  #开启此项,表示同步方式,需要同时打开synchronous_commit = on,此为默认值,默认是异步方式  
synchronous_commit = on #开启同步模式
archive_mode = on   #建议打开归档模式,防止长时间无法同步,WAL被覆盖造成数据丢失
archive_command = '[ ! -f /archive/%f ] && cp %p /archive/%f'
wa1_level = replica #设置wal的级别
max_wal_senders = 5 #这个设置可以最多有几个流复制连接,一般有几个从节点就设置几个
wal_keep_segments = 128 #设置流复制保留的最多的WAL文件数目
wal_sender_timeout = 60s #设置流复制主机发送数据的超时时间
max_connections = 200 # 一般查多于写的应用从库的最大连接数要比较大
hot_standby = on #对主库无影响,用于将来可能会成为从库,这台机器不仅仅是用于数据归档,也用于数据查询,在从库上配置此项后为只读
max_standby_streaming_delay = 30s #数据流备份的最大延迟时间
wal_receiver_status_interval = 10s #多久向主报告一次从的状态,当然从每次数据复制都会向主报告状态,只是设置最长的间隔时间
hot_standby_feedback = on #如果有错误的数据复制,是否向主进行反馈
wal_log_hints = on     #对非关键更新进行整页写入


[postgres@pgsql-master root]$vi /pgsql/data/postgresql.conf
max_connections = 200 #原配置为100,修改项

#添加项
synchronous_standby_names = '*'
archive_mode = on
archive_command= '[ ! -f /archive/%f ] && cp %p /archive/%f'
hot_standby_feedback = on
wal_log_hints = on

4.从节点配置

#清空数据和归档
[root@pgsql-slave ~]#mkdir /archive
[root@pgsql-slave ~]#mkdir -p /pgsql/backup
[root@pgsql-slave ~]#rm -rf /pgsql/data/* 

#备份主库数据到备库
[root@pgsql-slave ~]#pg_basebackup -D /pgsql/backup/ -Ft -Pv -Urepluser -h 192.168.217.135 -p 5432 -R
Password: 
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_21248"
26354/26354 kB (100%), 1/1 tablespace                                         
pg_basebackup: write-ahead log end point: 0/2000138
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

#还原备份的数据,实现初始的主从数据同步
[root@pgsql-slave ~]# tar xf /pgsql/backup/base.tar -C /pgsql/data
[root@pgsql-slave ~]#tar xf /pgsql/backup/pg_wal.tar -C /archive/
[root@pgsql-slave ~]#vi /pgsql/data/postgresql.conf
#添加下面两行
primary_conninfo = 'host=192.168.217.135 port=5432 user=repluser password=123456'
restore_command = 'cp /archive/%f %p' #此项可不配置
#修改配置(可选):
hot_standby = on   #开启此项,此是默认项
recovery_target_timeline = latest # 默认
max_connections = 120 # 大于等于主节点,正式环境应当重新考虑此值的大小
max_standby_streaming_delay = 30s
wal_receiver_status_interval = 10
shot_standby_feedback = on
max_wal_senders = 15
logging_co1lector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d.log'

5.监控同步状态

#查看主库状态
[root@pgsql-master ~]#pg_controldata
pg_control version number:            1300
Catalog version number:               202107181
Database system identifier:           7184267676821319539
Database cluster state:               in production #主库状态

#下面只在主节点查看同步模式,注意:如果无从节点连接,将无任何显示信息
[postgres@pgsql-master root]$psql 
psql (14.2)
Type "help" for help.

postgres=# select pid,state,client_addr,sync_priority,sync_state from  pg_stat_replication;
  pid  |   state   |   client_addr   | sync_priority | sync_state 
-------+-----------+-----------------+---------------+------------
 21746 | streaming | 192.168.217.134 |             1 | sync
#下面只在主节点查看同步模式,注意:如果无从节点连接,将无任何显示信息
postgres=# SELECT pg_current_wal_insert_lsn(),* from pg_stat_replication;
-[ RECORD 1 ]-------------+------------------------------
pg_current_wal_insert_lsn | 0/5000148
pid                       | 21746
usesysid                  | 16384
usename                   | repluser
application_name          | walreceiver
client_addr               | 192.168.217.134
client_hostname           | 
client_port               | 16182
backend_start             | 2023-01-03 18:38:57.266737+08
backend_xmin              | 
state                     | streaming
sent_lsn                  | 0/5000148
write_lsn                 | 0/5000148  #同步的lsn号,和上面一致,说明同步,有差表示有同步延迟
flush_lsn                 | 0/5000148
replay_lsn                | 0/5000148
write_lag                 | 
flush_lag                 | 
replay_lag                | 
sync_priority             | 1
sync_state                | sync  #async是异步,同步显示为sync
reply_time                | 2023-01-03 18:43:47.944498+08

三、实现postgresql的时间点还原

场景说明
每天3:00备份,第二天12:00误删除数据库,如何恢复?
故障恢复过程
备份数据和归档
数据还原

  • 还原完全备份
  • 归档日志恢复:
    1. 备份中的归档
    2. 恢复3:00到12:00之间的归档
    3. 恢复在线redo

1.备份

(1)在PGSQL服务器开启归档
[postgres@pgsql-master root]$vim /pgsql/data/postgresql.conf
archive_mode = on
archive_command= '[ ! -f /archive/%f ] && cp %p /archive/%f'
[postgres@pgsql-master root]$pg_ctl restart -D $PGDATA  #重启pgsql

(2)在PGSQL服务器上创建测试数据
postgres=# create database testdb;
CREATE DATABASE
postgres=# \c testdb;
You are now connected to database "testdb" as user "postgres".
testdb=# create table t1(id int);
CREATE TABLE
testdb=# insert into t1 values (1);
INSERT 0 1

(3)在备份服务器对PG数据库进行远程完全备份
[root@pgsql-slave ~]#vim /pgsql/data/postgresql.conf 
archive_mode = on
archive_command= '[ ! -f /archive/%f ] && cp %p /archive/%f'
[root@pgsql-slave ~]#chown postgres. /pgsql/backup/
[root@pgsql-slave ~]#chown postgres. /archive
[root@pgsql-slave ~]#pg_basebackup -D /pgsql/backup/ -Ft -Pv -Urepluser -h 192.168.217.135 -p 5432 -R

(4)在PG服务器上继续生成测试数据
testdb=# insert into t1 values (2);
INSERT 0 1
testdb=# insert into t1 values (3);

(5)在PG服务器上模拟数据库删除
postgres=# drop database testdb;
DROP DATABASE

(6)发现故障,停止用户访问
#查看当前日志文件
postgres=#  select pg_walfile_name(pg_current_wal_lsn());
     pg_walfile_name      
--------------------------
 00000001000000000000000E


#查看当前事务ID
postgres=# select txid_current();
 txid_current 
--------------
          762

2.还原

(1)在PG服务器上切换归档日志
postgres=# select pg_switch_wal();
 pg_switch_wal 
---------------
 0/E000EC8

(2)在备份服务器上还原,先停止服务,再清理数据
[postgres@pgsql-slave root]$pg_ctl stop
[root@pgsql-slave ~]#rm -rf /pgsql/data/*
[root@pgsql-slave ~]#rm -rf /archive/*
[root@pgsql-slave ~]#tar xf /pgsql/backup/base.tar -C /pgsql/data
[root@pgsql-slave ~]#rsync -a 192.168.217.135:/archive/ /archive/
[root@pgsql-slave ~]#ls /archive/
00000001000000000000000C  00000001000000000000000D  00000001000000000000000D.00000028.backup  00000001000000000000000E

(3)查看故障点事务ID
[root@pgsql-slave ~]#pg_waldump /archive/00000001000000000000000E | grep Database
rmgr: Database    len (rec/tot):     38/    38, tx:        761, lsn: 0/0E000DA8, prev 0/0E000D30, desc: DROP dir 1663/24586
#可以确认删库的事务id是761,所有恢复到760即可(恢复到760事务的时间点也可以)

(4)修改备份服务器配置文件postgresql.conf
[root@pgsql-slave ~]#vim /pgsql/data/postgresql.conf 
restore_command = 'cp /archive/%f %p'
recovery_target_xid = '760'
#也可以通过下面方式指定还原至的位置
recovery_target_time = '2023-01-06 11:39:07'
recovery_target_lsn = '0/04000290'

(5)还原后查看数据
postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 testdb    | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
(4 rows)

postgres=# \c testdb ;
You are now connected to database "testdb" as user "postgres".
testdb=# select * from t1 ;
 id 
----
  1
  2
  3
(3 rows)

#当前无法写入
testdb=# insert into t1 values (4);
ERROR:  cannot execute INSERT in a read-only transaction
#查看此时的状态
[root@pgsql-slave ~]#pg_controldata 
pg_control version number:            1300
Catalog version number:               202107181
Database system identifier:           7184267676821319539
Database cluster state:               in archive recovery #此时状态不是production

(6)执行此命令可让备份服务器恢复到写状态
postgres=# select pg_wal_replay_resume();
 pg_wal_replay_resume 
----------------------
#查看此时的状态
[root@pgsql-slave ~]#pg_controldata 
pg_control version number:            1300
Catalog version number:               202107181
Database system identifier:           7184267676821319539
Database cluster state:               in production

四、规划高可用的LAMP,要求wordpress网站放在NFS共享存储上,并且用户可以正常发布博客,上传图片。尝试更新wordpress版本,测试网站仍可用

实验环境
共设5台主机
两台是web服务器,分别是:
LAP1 192.168.217.135
LAP2 192.168.217.134
一台是数据库服务器
mysql 192.168.217.133
两台是NFS服务器,分别是
NFS1(主服务器) 192.168.217.132
NFS2(备份服务器) 192.168.217.131

1.服务器配置

1.1web服务器配置

(1)在LAP1和2机器上准备相关包和文件
[root@LAP1/2 ~]#yum -y install httpd php php-mysqlnd php-json
[root@LAP1/2 ~]#systemctl enable --now httpd
[root@LAP1/2 ~]#wget https://cn.wordpress.org/latest-zh_CN.tar.gz #下载wordpress
[root@LAP1/2 ~]#tar xvf latest-zh_CN.tar.gz 
[root@LAP1/2 ~]#mv wordpress/* /var/www/html/
[root@LAP1/2 ~]#chown apache.apache /var/www/html -R  #将移来的wordpress文件权限授予apache

(2)实现LAP1和2机器上的文件远程挂载
[root@LAP1/2 ~]#showmount -e 192.168.217.132
Export list for 192.168.217.132:
/data/www *
#查看是否可以看到192.168.217.132的共享
[root@LAP1/2 ~]#cd /var/www/html/wp-content
[root@LAP1/2 wp-content]#mkdir uploads #创建共享文件夹
[root@LAP1/2 wp-content]#chown apache.apache uploads  #更改所属用户和组
[root@LAP1/2~]#vim /etc/fstab
192.168.217.132:/data/www    /var/www/html/wp-content/uploads   nfs   _netdev  0 0
#挂载文件                                挂载目录                                           协议  没有网络不挂载
[root@LAP1/2 ~]#mount -a  #将/etc/fstab的所有内容重新加载

1.2mysql服务器配置

[root@mysql ~]#yum -y install mysql-server
[root@mysql ~]#systemctl enable --now mysqld
[root@mysql ~]#mysql
mysql> create user wordpress@'192.168.217.%' identified by '123456';
Query OK, 0 rows affected (0.02 sec)

mysql> create database wordpress;
Query OK, 1 row affected (0.00 sec)

mysql> grant all on wordpress.* to wordpress@'192.168.217.%';
Query OK, 0 rows affected (0.00 sec)

1.3NFS服务器配置

(1)NFS1基于存储wordpress图片设置
[root@nfs1 ~]#yum -y install nfs-utils 
[root@nfs1 ~]#mkdir -p /data/www  #此文件夹是存放共享图片的
[root@nfs1 ~]#useradd -u 666 www #创建id号为666的用户www,此账号用于后面的压榨账号
[root@nfs1 ~]#vim /etc/exports
/data/www  *(rw,all_squash,anonuid=666,anonuid=666)
#  共享地址    读写权限,压榨所有用户,指定压榨后映射的用户uid为666的用户
[root@nfs1 ~]#chown www.www /data/www  #将文件给与www用户权限
[root@nfs1 ~]#systemctl enable --now  nfs-server

(2)基于主NFS1服务器和备份NFS2服务器进行配置
#rsync 常用于做为 linux系统下的数据镜像备份工具,实现远程同步,支持本地复制,或者与其他SSH、rsync主机同步数据,支持增量备份,配合任务计划,rsync能实现定时或间隔同步,配合inotify或sersync,可以实现触发式的实时数据同步
[root@nfs2 ~]#yum -y install rsync       #用于在备份NFS服务器上开启远程监听端口873(centos8安装rsync-daemon)
[root@nfs2 ~]#systemctl enable --now  rsyncd.service
[root@nfs2 ~]#ss -ntl                   #查看873端口已开启
State       Recv-Q Send-Q    Local Address:Port         Peer Address:Port                
LISTEN      0      5                     *:873          *:*
 [root@nfs2 ~]#vim /etc/rsyncd.conf
uid = root       #提定以哪个用户来访问共享目录,将之指定为生成的文件所有者,默认为nobody
gid = root       #默认为nobody,Ubuntu中为nogroup
max connections = 0
ignore errors
exclude = lost+found/
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsyncd.lock
reverse lookup = no

[backup]                  #每个模块名对应一个不同的path目录,如果同名后面模块生效
path = /data/backup/      #备份服务器共享的文件夹
comment = backup dir
read only = no           #默认是yes,即只读
auth users = rsyncuser   #默认anonymous可以访问rsync服务器,指定访问账号rsyncuser
secrets file = /etc/rsync.pas  #账号密码存放路径
:wq
[root@nfs2 ~]#mkdir -p /data/backup    #创建共享文件夹 
[root@nfs2 ~]#echo "rsyncuser:123456" > /etc/rsync.pas #备份服务器端生成验证文件
[root@nfs2 ~]#chmod 600 /etc/rsync.pas      #指定只允许root用户读写

(3)nfs1主服务器基于rsync daemon实现 sersync
#在数据服务器上下载sersync,并拷贝至相应的目录,设置PATH变量 
[root@nfs1 ~]#wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/sersync/sersync2.5.4_64bit_binary_stable_final.tar.gz
[root@nfs1 ~]#tar xvf sersync2.5.4_64bit_binary_stable_final.tar.gz 
GNU-Linux-x86/
GNU-Linux-x86/sersync2
GNU-Linux-x86/confxml.xml
[root@nfs1 ~]#cp -a GNU-Linux-x86/ /usr/local/sersync[root@nfs1 ~]#echo 'PATH=/usr/local/sersync:$PATH' > /etc/profile.d/sersync.sh
[root@nfs1 ~]#source /etc/profile.d/sersync.sh 
#备份sersync配置文件 
[root@nfs1 ~]#cp /usr/local/sersync/confxml.xml{,.bak}
#sersync目录只有两个文件:一个是二进制程序文件,一个是xml格式的配置文件 
[root@nfs1 ~]#vim /usr/local/sersync/confxml.xml
#修改sersync配置文件 
<?xml version="1.0" encoding="ISO-8859-1"?> 
<head version="2.5"> 
<host hostip="localhost" port="8008"></host> 
<debug start="false"/> # 是否开启调试模式 
<fileSystem xfs="false"/> 
<filter start="false"> #不开启文件过滤功能,当为true时,以下类型的文件将不同 
步 
<exclude expression="(.*)\.svn"></exclude> 
<exclude expression="(.*)\.gz"></exclude> 
<exclude expression="^info/*"></exclude> 
<exclude expression="^static/*"></exclude> 
</filter> 
<inotify> # 监控事件,默认监控 
delete/close_write/moved_from/moved_to/create folder 
<delete start="true"/>
<createFolder start="true"/> 
<createFile start="false"/> 
<closeWrite start="true"/> 
<moveFrom start="true"/> 
<moveTo start="true"/> 
<attrib start="true"/> #修改此行为true,文件属性变化后也会同步 
<modify start="false"/> 
</inotify> 
<sersync> # rsync命令的配置段 
<localpath watch="/data/www"> #修改此行,需要同步的源目录或文件,建议同步目 
录 
<remote ip="备份服务器IP" name="backup"/> #修改此行,指定备份服务器地址和rsync 
daemon的模块名,如果下面开启了ssh start,此时name为远程shell方式运行时的目标目录 
<!--<remote ip="192.168.8.39" name="tongbu"/>--> 
<!--<remote ip="192.168.8.40" name="tongbu"/>--> 
</localpath> 
<rsync> 
<commonParams params="-artuz"/> # 指定rsync选项 
<auth start="true" users="rsyncuser" passwordfile="/etc/rsync.pas"/> #修 
改此行为true,指定备份服务器的rsync配置的用户和密码文件 
<userDefinedPort start="false" port="874"/><!-- port=874 -->#指定rsync的非 
标准端口号
<timeout start="false" time="100"/><!-- timeout=100 --> 
<ssh start="false"/> #默认使用rsync daemon运行rsync命令,true为使用远程shell模 
式 
</rsync> 
<failLog path="/tmp/rsync_fail_log.sh" timeToExecute="60"/><!--default every 
60mins execute once--> #错误重传及日志文件路径 
<crontab start="false" schedule="600"><!--600mins--> #不开启crontab功能 
<crontabfilter start="false"> #不开启crontab定时传输的筛选功能 
<exclude expression="*.php"></exclude> 
<exclude expression="info/*"></exclude> 
</crontabfilter> 
</crontab> 
<plugin start="false" name="command"/> 
</sersync> 
#####################################以下行不需要修改 
#################################### 
<plugin name="command"> 
<param prefix="/bin/sh" suffix="" ignoreError="true"/> <!--prefix 
/opt/tongbu/mmm.sh suffix--> 
<filter start="false"> 
<include expression="(.*)\.php"/> 
<include expression="(.*)\.sh"/> 
</filter> 
</plugin> 
<plugin name="socket"> 
<localpath watch="/opt/tongbu"> 
<deshost ip="192.168.138.20" port="8009"/> 
</localpath> 
</plugin> 
<plugin name="refreshCDN"> 
<localpath watch="/data0/htdocs/cms.xoyo.com/site/"> 
<cdninfo domainname="ccms.chinacache.com" port="80" username="xxxx" 
passwd="xxxx"/> 
<sendurl base="http://pic.xoyo.com/cms"/>
<regexurl regex="false" match="cms.xoyo.com/site([/a-zA-Z0- 
9]*).xoyo.com/images"/> 
</localpath> 
</plugin> 
</head> 
#创建连接rsynd服务器的用户密码文件,并必须修改权限
[root@nfs1 ~]#echo '123456' > /etc/rsync.pas
[root@nfs1 ~]#chmod 600 /etc/rsync.pas 
#查看帮助 
[root@nfs1 ~]#sersync2 -h 
set the system param
execute:echo 50000000 > /proc/sys/fs/inotify/max_user_watches
execute:echo 327679 > /proc/sys/fs/inotify/max_queued_events
parse the command param
_______________________________________________________
参数-d:启用守护进程模式
参数-r:在监控前,将监控目录与远程主机用rsync命令推送一遍
c参数-n: 指定开启守护线程的数量,默认为10个
参数-o:指定配置文件,默认使用confxml.xml文件
参数-m:单独启用其他模块,使用 -m refreshCDN 开启刷新CDN模块
参数-m:单独启用其他模块,使用 -m socket 开启socket模块
参数-m:单独启用其他模块,使用 -m http 开启http模块
不加-m参数,则默认执行同步程序
#以后台方式执行同步 
[root@nfs1 ~]#sersync2 -dro /usr/local/sersync/confxml.xml
set the system param
execute:echo 50000000 > /proc/sys/fs/inotify/max_user_watches
execute:echo 327679 > /proc/sys/fs/inotify/max_queued_events
parse the command param
option: -d  run as a daemon
option: -r  rsync all the local files to the remote servers before the sersync work
option: -o  config xml name:  /usr/local/sersync/confxml.xml
daemon thread num: 10
parse xml config file
host ip : localhost host port: 8008
daemon start,sersync run behind the console 
use rsync password-file :
user is rsyncuser
passwordfile is     /etc/rsync.pas
config xml parse success
please set /etc/rsyncd.conf max connections=0 Manually
sersync working thread 12  = 1(primary thread) + 1(fail retry thread) + 10(daemon sub threads) 
Max threads numbers is: 22 = 12(Thread pool nums) + 10(Sub threads)
please according your cpu ,use -n param to adjust the cpu rate
------------------------------------------
rsync the directory recursivly to the remote servers once
working please wait...
execute command: cd /data/www && rsync -artuz -R --delete ./ rsyncuser@192.168.217.131::backup --password-file=/etc/rsync.pas >/dev/null 2>&1 
run the sersync: 
watch path is: /data/www

#如果同步失败,可以手动执行下面命令,观察过程 
[root@nfs1 ~]#cd /data/www && rsync -artuz -R --delete ./ rsyncuser@backup-server::backup --password-file=/etc/rsync.pas >/dev/null 2>&1

#为了实现开机启动此服务,可以将此命令写入/etc/rc.local,实现开机启动
[root@nfs1 ~]#vim /etc/rc.local       
/opt/GNU-Linux-x86/sersync2 -dro /opt/GNU-Linux-x86/confxml.xml
:wq
[root@nfs1 ~]#chmod +x /etc/rc.local

1.4登录wordpress,并验证数据的存储

①浏览器输入网址:192.168.217.135(可做dns)


image.png

②连接数据库,设置账户,登录


image.png

③编写文章,并查看文章
image.png
image.png

④web服务器查看存储位置


image.png

⑤查看NFS1主服务器是否存储


image.png

⑥查看NFS2备份服务器是否共享
image.png

五、redis数据类型

此篇罗列了 Redis 的所有数据类型并介绍了常用的5种数据类型(strings,Lists,Hashes,Sets,Sorted sets)简单的命令和使用场景

Redis的数据类型

  • Binary-safe strings , 简单的K-V 结构的存储
  • Lists , 按插入顺序排序的字符串元素集合。基本上就是链表
  • Sets ,唯一的,不排序的集合
  • Sorted sets ,类似于集合,但每个字符串元素都与一个称为score的浮数值相关联,元素总是按分数排序,因此与集合不同,可以检索一系列元素
  • Hashes , 由与值关联的字段组成的映射。字段和值都是字符串
  • Bit arrays (or simply bitmaps) , 可以使用特殊命令处理字符串值
  • HyperLogLogs , 这是一个概率数据结构,用于估计集合的基数
  • Streams ,仅附加的类似于地图的条目集合,提供抽象日志数据类型

Redis 常用的数据类型

1. strings字符串

strings 可以用来存储 k-v 结构的数据,做计数器等;

image

常用命令:

set <key> <value>
get <key>
incr <key>
mget <key> <key...>

2. lists列表

Lists,可以用来实现粉丝列表,评论列表等;

image

常用命令:

lpush <key> <value>
lpop <key>
rpush <key> <value>
rpop <key>
lrange <key> <start> <end>

# lpush 是把元素插入到链表的头部,lpop 是从头部弹出一个元素并删除
# rpush 是把元素插入到链表的尾部,rpop 是从尾部弹出一个元素并删除

3. sets集合

Sets ,可以利用其无序,唯一(自动去重)的特性,例如,共同好友(用到了 SINTER 命令)等;

image

执行sadd <key> <value> , 成功返回1,数据已存在返回0,数据类型不对返回异常

常用命令:

sadd <key> <member> <member...>
scard <key>
sdiff <key> <key...>
spop <key> [count]
smembers <key>

4. sorted sets有序集合

Sorted sets,用来做排名等;

image
image

zadd 命令如果已存在会覆盖

常用命令:

zadd <key> <score> <member>
zrange <key> <start> <end>
zrem <key> <member>

5. hashes哈希

Hashes , 用来存储个人信息等;

image

常用命令:

hset <key> <field> <value>
hget <key> <field>
hgetall <key>

六、redis RDB和AOF比较

Redis 中数据的持久化

前言

我们知道 Redis 是内存数据库,所有操作都在内存上完成。内存的话,服务器断电,内存上面的数据就会丢失了。这个问题显然是需要解决的。

Redis 中引入了持久化来避免数据的丢失,主要有两种持久化的方式 RDB 持久化和 AOF 持久化。

AOF 持久化

什么是 AOF 持久化

AOF(Append Only File):通过保存数据库执行的命令来记录数据库的状态。

image

AOF日志对数据库命令的保存顺序是,Redis 先执行命令,把数据写入内存,然后才记录日志。

为什么要后记录日志呢

1、后写,能够避免记录到错误的命令。因为是先执行命令,后写入日志,只有命令执行成功了,命令才能被写入到日志中。

2、避免阻塞当前的写操作,是在命令执行后才记录日志,所以不会阻塞当前的写操作。

AOF 的潜在风险

  • 1、如果命令执行成功,写入日志的时候宕机了,命令没有写入到日志中,这时候就有丢失数据的风险了,因为这时候没有写入日志,服务断电之后,这部分数据就丢失了。

这种场景在别的地方也很常见,比如基于 MQ 实现分布式事务,也会出现业务处理成功 + 事务消息发送失败这种场景,RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

  • 2、AOF 日志写入也是在主线程进行的,如果磁盘的压力很大,写入速度变慢了,会影响后续的操作。

这两种情况可以通过调整 AOF 文件的写入磁盘的时机来避免

AOF 文件的写入和同步

AOF 文件持久化的功能分成三个步骤,文件追加(append),文件写入,文件同步(sync)。

AOF 文件在写入磁盘之前是先写入到 aof_buf 缓冲区中,然后通过调用 flushAppendOnlyFile 将缓冲区中的内容保存到 AOF 文件中。

写入的策略通过 appendfsync 来进行配置

  • Always:同步写回 每次操作命令执行完后,同步将 AOF 日志数据写回硬盘;
  • Everysec:每秒写回 每次操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
  • No:操作系统控制的写回 Redis 不在控制命令的写会时机,交由系统控制。每次操作命令执行完成之后,命令会被放入到 AOF 文件的内核缓冲区,之后什么时候写入到磁盘,交由系统控制。

AOF 文件重写机制

因为每次执行的命令都会被写入到 AOF 文件中,随着系统的运行,越来越多的文件会被写入到 AOF 文件中,这样 AOF 文件势必会变得很大,这种情况该如何去处理呢?

为了解决这种情况,Redis 中引入了重写的机制

什么是重写呢?

因为 AOF 文件中记录的是每个命令的操作记录,举个,比如当一个键值对被多条写命令反复修改时,AOF文件会记录相应的多条命令,那么重写机制,就是根据这个键值对当前的最新状态,为它生成对应的写入命令,保存成一行操作命令。这样就精简了 AOF 文件的大小。

192.168.56.118:6379> set name "xiaoming"
OK
192.168.56.118:6379> get name
"xiaoming"
192.168.56.118:6379> set name "xiaozhang"
OK
192.168.56.118:6379> set name "xiaoli"
OK

# 重写后就是
192.168.56.118:6379> set name "xiaoli"

简单来讲就是多变一,就是把 AOF 中日志根据当前键值的状态,合并成一条操作命令。

重写之后的文件会保存到新的 AOF 文件中,这是旧的 AOF 文件和新的 AOF 文件中键值对应的状态是一样的。然后新的 AOF 文件会替换掉旧的 AOF 文件,这样 重写操作一直在进行,AOF 文件就不至于变得过大。

重写是后台进行的, AOF 重写会放到子进程中进行的,使用子进程的优点:

1、子进程处理 AOF 期间,不会影响 Redis 主线程对数据的处理;

2、子进程拥有所在线程的数据副本,子进程能够避免锁的使用,保证数据的安全。

这里来看下,AOF 的处理流程

AOF 重写也有一个缓冲区,当服务节接收到新的命令的时候,如果在正在进行 AOF 重写,命令同样也会被发送到 AOF 缓冲区

image

的进程执行 AOF 重写的过程,服务端进程主要处理以下内容

1、接收并处理客户端发送的命令;

2、将执行后的命令写入到 AOF 缓冲区;

3、将执行后的命令也写入到 AOF 重写缓冲区;

AOF 缓冲区和 AOF 重写缓冲区中的内容会被定期的同步到 AOF 文件和 AOF 重写文件中

当子进程完成重写的时候,会给父进程发送一个信号,这时候父进程主要主要进行下面的两步操作:

1、将 AOF 重写缓冲区中的内容全部写入到 AOF 重写文件中,这时候重写 AOF 文件保存的数据状态是和服务端数据库的状态一致的;

2、将 AOF 重写文件替换旧的 AOF 文件;

通过 AOF 的重写操作,新的 AOF 文件不断的替换旧的 AOF 文件,这样就能控制 AOF 文件的大小

AOF 的数据还原

AOF 文件包了重建数据库索引锁需要的全部命令,所以只需要读入并重新执行一遍 AOF 文件中保存的命令,即可还原服务关闭之前数据库的状态。

RDB 持久化

什么是 RDB 持久化

RDB(Redis database):实现方式是将存在 Redis 内存中的数据写入到 RDB 文件中保存到磁盘上从而实现持久化的。

和 AOF 不同的是 RDB 保存的是数据而不是操作,在进行数据恢复的时候,直接把 RDB 的文件读入到内存,即可完成数据恢复。

image

RDB 如何做内存快照

Redis 中对于如何备份数据到 RDB 文件中,提供了两种方式

  • 1、save: 在主线程中执行,不过这种会阻塞 Redis 服务进程;
  • 2、bgsave: 主线程会 fork 出一个子进程来负责处理 RDB 文件的创建,不会阻塞主线程的命令操作,这也是 Redis 中 RDB 文件生成的默认配置;

对于 save 和 bgsave 这两种快照方式,服务端是禁止这两种方式同时执行的,防止产生竞争条件。

Redis 中可以使用 save 选项,来配置服务端执行 BGSAVE 命令的间隔时间

#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1
save 300 10
save 60 10000

save 900 1 就是服务端在900秒,读数据进行了至少1次修改,就会触发一次 BGSAVE 命令

save 300 10 就是服务端在300秒,读数据进行了至少10次修改,就会触发一次 BGSAVE 命令

快照时发生数据修改

举个栗子:我们在t时刻开始对内存数据内进行快照,假定目前有 2GB 的数据需要同步,磁盘写入的速度是 0.1GB/s 那么,快照的时间就是 20s,那就是在 t+20s 完成快照。

如果在 t+6s 的时候修改一个还没有写入磁盘的内存数据 test 为 test-hello。那么就会破坏快照的完整性了,因为 t 时刻备份的数据已经被修改了。当然是希望在备份期间数据不能被修改。

如果不能被修改,就意味这在快照期间不能对数据进行修改操作,就如上面的栗子,快照需要进行20s,期间不允许处理数据更新操作,这显然也是不合理的。

这里需要聊一下 bgsave 是可以避免阻塞,不过需要注意的是避免阻塞和正常读写操作是有区别的。避免阻塞主线程确实没有阻塞可以处理读操作,但是为了保护快照的完整性,是不能修改快照期间的数据的。

这里就需要引入一种新的处理方案,写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

bgsave 子进程是由主线程 fork 生成的,所以是可以共享主线程的内存的,bgsave子进程运行后会读取主线程中的内存数据,并且写入到 RDB 文件中。

写复制技术就是,如果主线程在内存快照期间修改了一块内存,那么这块内存会被复制一份,生成该数据的副本,然后 bgsave 子进程在把这段内存写入到 RDB 文件中。这样就可以在快照期间进行数据的修改了。

image

多久做一次快照

对于快照,如果做的太频繁,可能会出现前一次快照还没有处理完成,后面的快照数据马上就进来了,同时过于频繁的快照也会增加磁盘的压力。

如果间隔时间过久,服务器在两次快照期间宕机,丢失的数据大小会随着快照间隔时间的增长而增加。

是否可以选择增量式快照呢?选择增量式快照,我们就需要记住每个键值对的状态,如果键值对很多,同样也会引入很多内存空间,这对于内存资源宝贵的Redis来说,有些得不偿失。

相较于 AOF 来对比,RDB 是会在数据恢复时,速度更快。但是 RDB 的内存快照同步频率不太好控制,过多过少都有问题。

Redis 4.0中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作。

通过混合使用AOF日志和内存快照的方法,RDB 快照的频率不需要过于频繁,在两次 RDB 快照期间,使用 AOF 日志来记录,这样也不用考虑 AOF 的文件过大问题,在下一次 RDB 快照开始的时候就可以删除 AOF 文件了。

image

过期的键如何持久化

在生成 RDB 文件的过程中,如果一个键已经过期,那么其不会被保存到 RDB 文件中。在载入 RDB 的时候,要分两种情况:

  • 1、如果 Redis 以主服务器的模式运行,那么会对 RDB 中的键进行时间检查,过期的键不会被恢复到 Redis 中。
  • 2、如果 Redis 以从服务器的模式运行,那么 RDB 中所有的键都会被载入,忽略时间检查。在从服务器与主服务器进行数据同步的时候,从服务器的数据会先被清空,所以载入过期键不会有问题。

对于 AOF 来说,如果一个键过期了,那么不会立刻对 AOF 文件造成影响。因为 Redis 使用的是惰性删除和定期删除,只有这个键被删除了,才会往 AOF 文件中追加一条 DEL 命令。在重写 AOF 的过程中,程序会检查数据库中的键,已经过期的键不会被保存到 AOF 文件中。

在运行过程中,对于主从复制的 Redis,主服务器和从服务器对于过期键的处理也不相同:

  • 1、对于主服务器,一个过期的键被删除了后,会向从服务器发送 DEL 命令,通知从服务器删除对应的键;
  • 2、从服务器接收到读取一个键的命令时,即使这个键已经过期,也不会删除,而是照常处理这个命令;
  • 3、从服务器接收到主服务器的 DEL 命令后,才会删除对应的过期键。

这样保证了数据的一致性,一个键值对存在于主服务器,也必然存在于从服务器。

总结

AOF

优点:AOF 中有三种策略可以进行选择,AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。

缺点:AOF 文件体积一般情况下比 RDB 文件体积大,并且数据还原速度也慢于 RDB。

RDB

优点:可以快速恢复数据,相比于 AOF 的顺序,逐一执行操作命令,效率更高;

缺点:因为是内存快照,频率过快,过慢,都会有响应的问题。过快,浪费磁盘资源,会给磁盘造成压力,过慢会存在较多数据丢失的问题。

Redis 4.0中提出了一个混合使用 AOF 日志和内存快照的方法,如果想要保证数据不丢失,这是一个比较好的选择;

如果允许分钟级别的数据丢失,可以只使用RDB;

如果只用AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。

七、redis配置文件详解

vim /etc/redis/redis.conf

单位
文件的开头位置是各种单位的换算公式
而且可以看出,对unit对大小写是不敏感的

image

include 包含
可以将其他的配置文件导入,一并使用(和import差不多)

image

网络

bind 127.0.0.1  #绑定的ip,代表着哪些ip可以访问
# 关闭protected-mode模式,此时外部网络可以直接访问;
# 开启protected-mode保护模式,需配置bind ip或者设置访问密码
protected-mode yes  #保护模式。
port 6379 #端口号 

GENERAL 通用

# 设置为yes表示指定Redis以守护进程的方式启动(后台启动)。默认值为 no
daemonize yes  
# 配置PID文件路径,当redis作为守护进程运行的时候,它会把 pid 默认写到 /var/redis/run/redis_6379.pid 文件里面
pidfile /var/run/redis_6379.pid

#日志级别
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice  # 定义日志级别。默认值为notice
logfile "" # 配置log文件路径名称,为空则默认打印在命令行终端的窗口上

# 设置数据库的数目。默认的数据库是DB 0 ,可以在每个连接上使用select  <dbid> 命令选择一个不同的数据库,dbid是一个介于0到databases - 1 之间的数值。默认值是 16,也就是说默认Redis有16个数据库。
databases 16

always-show-logo no# 是否一直显示logo(开启redis时候的那个图案)

快照 SNAPSHOTTING
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb和.aof
redis是内存数据库,如果没有持久化,那么数据断电就不见了!

# 配置触发 Redis的持久化条件;
save 900 1  # 表示900 秒内如果至少有 1 个 key 的值变化,则进行持久化操作保存
save 300 10  # 表示300 秒内如果至少有 10 个 key 的值变化,则进行持久化操作保存
save 60 10000  # 表示60 秒内如果至少有 10000 个 key 的值变化,则进行持久化操作保存

stop-writes-on-bgsave-error yes  # 持久化出现错误后,是否依然进行继续进行工作

rdbcompression yes# 是否压缩.rdb文件。如果是的话,redis会采用LZF算法进行压缩。会消耗CPU来进行压缩

rdbchecksum yes  #保存.rdb文件的时候,进行文件错误校验,这样大约增加10%的性能消耗

dir ./# rdb 文件保存的目录

REPLICATION主从复制

replication 主机pi 主机端口  #配置主结点
masterauth password #主机的密码(没有密码不用写)

SECURITY安全

# requirepass foobared  # 设置redis的密码,默认是没有密码!
#设置密码(有密码后访问数据库需要先验证 : auth 123456)
requirepass  123456  #在配置文件中设置密码
config set requirepass "123456" #在命令行中设置密码

CLIENTS限制

maxclients 10000  # 设置能连接上redis的最大客户端的数量
maxmemory <bytes>  # redis 配置最大的内存容量,默认是字节
maxmemory-policy noeviction # 内存达到上限之后的处理策略。永不过期,返回错误
1、noeviction:默认策略,不淘汰,如果内存已满,添加数据是报错。
2、allkeys-lru:在所有键中,选取最近最少使用的数据抛弃。
3、volatile-lru:在设置了过期时间的所有键中,选取最近最少使用的数据抛弃。
4、allkeys-random: 在所有键中,随机抛弃。
5、volatile-random: 在设置了过期时间的所有键,随机抛弃。
6、volatile-ttl:在设置了过期时间的所有键,抛弃存活时间最短的数据。

APPEND ONLY 模式 aof配置

appendonly no #默认是不开启aof模式的,因为我们默认是使用rdb持久化

# 指定本地数据库文件名,默认值为 appendonly.aof
appendfilename "appendonly.aof" #持久化文件的名字

appendfsync           #进行文件同步配置的频率
# appendfsync always  # always表示每次修改都执行fsync,以保证数据同步到磁盘,慢,但是最安全。
appendfsync everysec  # everysec表示每秒执行一次fsync,可能会导致丢失这1s数据,每秒写一次。
# appendfsync no      # no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容