PostgreSQL如何保障数据的一致性

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: PostgreSQL如何保障数据的一致性 玩过MySQL的人应该都知道,由于MySQL是逻辑复制,从根子上是难以保证数据一致性的。玩MySQL玩得好的专家们知道有哪些坑,应该怎么回避。

PostgreSQL如何保障数据的一致性

玩过MySQL的人应该都知道,由于MySQL是逻辑复制,从根子上是难以保证数据一致性的。玩MySQL玩得好的专家们知道有哪些坑,应该怎么回避。为了保障MySQL数据的一致性,甚至会动用paxos,raft之类的终极武器建立严密的防护网。如果不会折腾,真不建议用MySQL存放一致性要求高的数据。

PostgreSQL由于是物理复制,天生就很容易保障数据一致性,而且回放日志的效率很高。 我们实测的结果,MySQL5.6的写qps超过4000备机就跟不上主机了;PG 8核虚机的写qps压到2.3w备机依然毫无压力,之所以只压到2.3w是因为主节点的CPU已经跑满压不上去了。

那么相比于MySQL,PG有哪些措施用于保障数据的一致性呢?

1. 严格单写

PG的备库处于恢复状态,不断的回放主库的WAL,不具备写能力。

而MySQL的单写是通过在备机上设置read_only或super_read_only实现的,DBA在维护数据库的时候可能需要解除只读状态,在解除期间发生点什么,或自动化脚本出个BUG都可能引起主备数据不一致。甚至备库在和主库建立复制关系之前数据就不是一致的,MySQL的逻辑复制并不阻止两个不一致的库建立复制关系。

2. 串行化的WAL回放

PG的备库以和主库完全相同顺序串行化的回放WAL日志。

MySQL中由于存在组提交,以及为了解决单线程复制回放慢而采取的并行复制,不得不在复制延迟和数据一致性之前做取舍。 并且这里牵扯到的逻辑很复杂,已经检出了很多的BUG;因为逻辑太复杂了,未来出现新BUG的概率应该相对也不会低。

3. 同步复制

PG通过synchronous_commit参数设置复制的持久性级别。

下面这些级别越往下越严格,从remote_write开始就可以保证单机故障不丢数据了。

  • off
  • local
  • remote_write
  • on
  • remote_apply

每个级别的含义参考手册:19.5. 预写式日志

MySQL通过半同步复制在很大程度上降低了failover丢失数据的概率。MySQL的主库在等待备库的应答超时时半同步复制会自动降级成异步,此时发生failover会丢失数据。 

4. 全局唯一的WAL标识

WAL文件头中保存了数据库实例的唯一标识(Database system identifier),可以确保不同数据库实例产生的WAL可以区别开,同一集群的主备库拥有相同唯一标识。

PG提升备机的时候会同时提升备机的时间线,时间线是WAL文件名的一部分,通过时间线就可以把新主和旧主产生的WAL区别开。 (如果同时提升2个以上的备机,就无法这样区分WAL了,当然这种情况正常不应该发生。)

WAL记录在整个WAL逻辑数据流中的偏移(lsn)作为WAL的标识。

以上3者的联合可唯一标识WAL记录

MySQL5.6开始支持GTID了,这对保障数据一致性是个极大的进步。对于逻辑复制来说,GITD确实做得很棒,但是和PG物理复制的时间线+lsn相比起来就显得太复杂了。时间线+lsn只是2个数字而已;GTID却是一个复杂的集合,而且需要定期清理。

MySQL的GTID是长这样的:

e6954592-8dba-11e6-af0e-fa163e1cf111:1-5:11-18,
e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27 

5. 数据文件的checksum

在初始化数据库时,使用-k选项可以打开数据文件的checksum功能。(建议打开,造成的性能损失很小) 如果底层存储出现问题,可通过checksum及时发现。

initdb -k $datadir 

MySQL也只支持数据文件的checksum,没什么区别。

6. WAL记录的checksum

每条WAL记录里都保存了checksum信息,如果WAL的传输存储过程中出现错误可及时发现。

MySQL的binlog记录里也包含checksum,没什么区别。

7. WAL文件的验证

WAL可能来自归档的拷贝或人为拷贝,PG在读取WAL文件时会进行验证,可防止DBA弄错文件。

  1. 检查WAL文件头中记录的数据库实例的唯一标识是否和本数据库一致
  2. 检查WAL页面头中记录的页地址是否正确
  3. 其它检查

上面第2项检查的作用主要是应付WAL再利用。

PG在清理不需要的WAL文件时,有2种方式,1是删除,2是改名为未来的WAL文件名防止频繁创建文件。

看下面的例子,000000030000000000000015及以后的WAL文件的修改日期比前面的WAL还要老,这些WAL文件就是被重命名了的。

[postgres@node1 ~]$ ll data1/pg_wal/
total 213000
-rw-------. 1 postgres postgres       41 Aug 27 00:53 00000002.history
-rw-------. 1 postgres postgres 16777216 Sep  1 23:56 000000030000000000000012
-rw-------. 1 postgres postgres 16777216 Sep  2 11:05 000000030000000000000013
-rw-------. 1 postgres postgres 16777216 Sep  2 11:05 000000030000000000000014
-rw-------. 1 postgres postgres 16777216 Aug 27 00:57 000000030000000000000015
-rw-------. 1 postgres postgres 16777216 Aug 27 00:58 000000030000000000000016
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 000000030000000000000017
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 000000030000000000000018
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 000000030000000000000019
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 00000003000000000000001A
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 00000003000000000000001B
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 00000003000000000000001C
-rw-------. 1 postgres postgres 16777216 Aug 27 00:59 00000003000000000000001D
-rw-------. 1 postgres postgres 16777216 Sep  1 23:56 00000003000000000000001E
-rw-------. 1 postgres postgres       84 Aug 27 01:02 00000003.history
drwx------. 2 postgres postgres       34 Sep  1 23:56 archive_status 

由于有上面的第2项检查,如果读到了这些WAL文件,可以立即识别出来。

[postgres@node1 ~]$ pg_waldump data1/pg_wal/000000030000000000000015
pg_waldump: FATAL:  could not find a valid record after 0/15000000 

MySQL的binlog文件名一般是长下面这样的,从binlog文件名上看不出任何和GTID的映射关系。

mysql_bin.000001 

不同机器上产生的binlog文件可能同名,如果要管理多套MySQL,千万别拿错文件。因为MySQL是逻辑复制,这些binlog文件就像SQL语句一样,拿到哪里都可以执行。

 参考

src/backend/access/transam/xlogreader.c:

static bool
ValidXLogRecord(XLogReaderState *state, XLogRecord *record, XLogRecPtr recptr)
{
    pg_crc32c   crc;

    /* Calculate the CRC */
    INIT_CRC32C(crc);
    COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord);
    /* include the record header last */
    COMP_CRC32C(crc, (char *) record, offsetof(XLogRecord, xl_crc));
    FIN_CRC32C(crc);

    if (!EQ_CRC32C(record->xl_crc, crc))
    {
        report_invalid_record(state,
               "incorrect resource manager data checksum in record at %X/%X",
                              (uint32) (recptr >> 32), (uint32) recptr);
        return false;
    }

    return true;
}
...
static bool
ValidXLogPageHeader(XLogReaderState *state, XLogRecPtr recptr,
                    XLogPageHeader hdr)
{
...
        if (state->system_identifier &&
            longhdr->xlp_sysid != state->system_identifier)
        {
            char        fhdrident_str[32];
            char        sysident_str[32];

            /*
             * Format sysids separately to keep platform-dependent format code
             * out of the translatable message string.
             */
            snprintf(fhdrident_str, sizeof(fhdrident_str), UINT64_FORMAT,
                     longhdr->xlp_sysid);
            snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
                     state->system_identifier);
            report_invalid_record(state,
                                  "WAL file is from different database system: WAL file database system identifier is %s, pg_control database system identifier is %s",
                                  fhdrident_str, sysident_str);
            return false;
        }
...
    if (hdr->xlp_pageaddr != recaddr)
    {
        char        fname[MAXFNAMELEN];

        XLogFileName(fname, state->readPageTLI, segno);

        report_invalid_record(state,
                    "unexpected pageaddr %X/%X in log segment %s, offset %u",
              (uint32) (hdr->xlp_pageaddr >> 32), (uint32) hdr->xlp_pageaddr,
                              fname,
                              offset);
        return false;
    }
...
}
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
29天前
|
存储 关系型数据库 分布式数据库
PolarDB常见问题之PolarDB冷存数据到OSS之后恢复失败如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
1月前
|
SQL 关系型数据库 分布式数据库
在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
【2月更文挑战第14天】在PolarDB中,行数评估是通过对表的统计数据、基数估计以及算子代价模型来进行估算的。
82 1
|
6月前
|
关系型数据库 MySQL Linux
TiDB实时同步数据到PostgreSQL(三) ---- 使用pgloader迁移数据
使用PostgreSQL数据迁移神器pgloader从TiDB迁移数据到PostgreSQL,同时说明如何在最新的Rocky Linux 9(CentOS 9 stream也适用)上通过源码编译安装pgloader。
226 0
|
2天前
|
SQL 关系型数据库 MySQL
关系型数据库插入数据的语句
使用SQL的`INSERT INTO`语句向关系型数据库的`students`表插入数据。例如,插入一个`id`为1,`name`为'张三',`age`为20的记录:`INSERT INTO students (id, name, age) VALUES (1, '张三', 20)。如果`id`自增,则可简化为`INSERT INTO students (name, age) VALUES ('张三', 20)`。
5 2
|
2天前
|
SQL 存储 Oracle
关系型数据库查询数据的语句
本文介绍了关系型数据库中的基本SQL查询语句,包括选择所有或特定列、带条件查询、排序、分组、过滤分组、表连接、限制记录数及子查询。SQL还支持窗口函数、存储过程等高级功能,是高效管理数据库的关键。建议深入学习SQL及相应数据库系统文档。
6 2
|
9天前
|
人工智能 Cloud Native 算法
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
AI与云数据库的深度结合是数据库发展的必然趋势,基于AI能力的加持,云数据库未来可以实现更快速的查询和决策,帮助企业更好地利用海量数据进行业务创新和决策优化。
数据之势丨AI时代,云原生数据库的最新发展趋势与进展
|
26天前
|
关系型数据库 MySQL OLAP
PolarDB +AnalyticDB Zero-ETL :免费同步数据到ADB,享受数据流通新体验
Zero-ETL是阿里云瑶池数据库提供的服务,旨在简化传统ETL流程的复杂性和成本,提高数据实时性。降低数据同步成本,允许用户快速在AnalyticDB中对PolarDB数据进行分析,降低了30%的数据接入成本,提升了60%的建仓效率。 Zero-ETL特性包括免费的PolarDB MySQL联邦分析和PolarDB-X元数据自动同步,提供一体化的事务处理和数据分析,并能整合多个数据源。用户只需简单配置即可实现数据同步和实时分析。
|
6月前
|
关系型数据库 数据管理 Go
《PostgreSQL数据分区:原理与实战》
《PostgreSQL数据分区:原理与实战》
85 0
|
2月前
|
关系型数据库 分布式数据库 PolarDB
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
85 3
|
2月前
|
关系型数据库 分布式数据库 PolarDB
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
76 1