可靠性
可靠性是任何严肃数据库的重要属性。
可靠操作是指:任何一个被提交的事务记录都应被存储在一个持久化区域(磁盘等)。不会因为电力、操作系统失败等因素导致数据丢失。只要硬盘驱动器幸存,就可以把事务记录迁移到另外一台计算机上,保持事务原状。
可靠性灾难
因为磁盘驱动器写入速度远慢于CPU和内存,中间存在多层高速缓存,数据在持久化到磁盘之前要经历高速缓存。断电时缓存内的数据有丢失风险,这就是可靠性灾难。
- 操作作系统的高速缓存
- 磁盘驱动器的控制器高速缓存
- 直写(Write Through)
数据直接写入磁盘,这种方式安全但是会牺牲写性能,因为只有等数据完全落入硬盘后,才算是一次io完成,这个过程会成cpu的iowait。 - 回写 (Write Back)
数据先写入缓存,因为缓存速度远远大于磁盘,所以可以提升性能。同时如果断电就存在缓存数据丢失的风险。
- 直写(Write Through)
写操作丢失风险。
盘片会被分割为扇区,通常每个扇区512字节。每次物理读写都对整个扇区进行操作。当一个写操作到达磁盘的时候,它可能是512字节(PostgreSQL通常一次写8192字节或者16个扇区)的某个倍数,而写入处理可能因为电力失效在任何时候失败。
为了避免写入扇区失败,pgsql会周期性把数据页面(包含多个扇区)写入WAL存储,后续可以恢复部分数据。
WAL 预写日志
预写式日志(WAL)是保证数据完整性的一种通用的标准方法。
WAL的中心概念是数据文件(存储着表和索引)的修改在写入磁盘之前,必须先记入日志记录。
如果我们遵循这种过程,我们不需要在每个事务提交时刷写数据页
面到磁盘,因为我们知道在发生崩溃时可以使用日志来恢复数据库:任何还没有被应用到数据页面的改变可以根据其日志记录重做(这是前滚恢复,也被称为REDO)。
WAL可以做什么
- WAL可以显著降低磁盘的写次数。事务中的每一条数据文件不会被直接刷入磁盘,而是在日志文件刷入磁盘时,整个事物一起提交。日志文件是顺序写入的,代价更小。
- WAL也使得在线备份和时间点恢复能被支持。
异步提交
异步提交是一个允许事务能更快完成的选项,代价是在数据库崩溃时最近的事务会丢失。在很多应用中这是一个可接受的交换。
事务提交通常是同步的:服务器等到事务的WAL记录被刷写到持久存储之后才向客户端返回成功指示。
但是,对于短事务来说这种延迟是其总执行时间的主要部
分。选择异步提交模式意味着服务器将在事务被逻辑上提交后立刻返回成功,而此时由它生成的WAL记录还没有被真正地写到磁盘上,有数据丢失的风险
如果数据库在异步提交和事务WAL记录写入之间的风险窗口期间崩溃,在该事务期间所作的
修改将丢失。
WAL配置考虑要素
检查点checkpoint
检查点是在事务序列中的点,这种点保证被更新的堆和索引数据文件的所有信息在该检查点之前已被写入。数据库崩溃后恢复时,将选取最新的一个检查点,从这个检查点开始恢复数据。
简单说,每次数据写入磁盘之前,都需要先写入WAL文件。检查点就是周期检查WAL日志的写入情况,并打一个标记。标记之前的部分代表已经写入磁盘,而未标记的部分表示还没有写入磁盘。未写入磁盘的数据就是恢复的对象(REDO记录)。
注意:checkpoint时刻,会把所有脏数据页被刷写到磁盘。这个操作会带来不小的I/O负载。所以需要保证下一次checkpoint开始前完成刷盘的操作,避免性能下降。
检查点触发条件
- 距离上一次价差点时间达到checkpoint_timeout秒时。
- wal日志文件大小超过max_wal_size时。
默认是5分钟,和1GB。
- 检查点间隔越小,触发越频繁,崩溃后数据恢复越快。(原因如上)。但是检查点是十分昂贵的,刷脏数据本身代价不小,而且有可能导致wal日志写入IO增加。
相关参数配置
checkpoint_warning =30s 检查点频率检查
如果检查点触发的频率小于30s,则log日志中会提醒你增加max_wal_size。
- 参考设置:如果硬盘的性能很好可以设置小一些,如果硬盘性能差就设置大一些。这个参数不影响性能。只是用来检查实际checkpoint发生的频率。
checkpoint_timeout = 30 mins 检查点触发时间间隔
自动检查点出发的时间,增加这个参数的值会增加崩溃恢复所需的时间。磁盘性能好的话可以适当减小。这个值减小会增加检查点频率,降低性能。
max_wal_size = 8GB
- 参考值:主机内存1/2
在自动WAL检查点使得WAL增长到最大尺寸,到了这个尺寸,检查点就开始工作。
min_wal_size = 1GB
WAL磁盘使用率低于这个设置,旧的WAL文件总数被回收,确保预留足够的WAL空间处理WAL使用中的峰值。
- 参考值:主机内存1/8