日常运维

需要密切关注以下指标,从而提前发现潜在故障。

服务器基础指标

包括cpu使用率,内存使用率,磁盘空间使用率,磁盘IO,网络IO,操作系统日志,操作系统安全日志等基础指标

SHOW 查看当前配置

show命令用来查看当前配置参数,这些参数也对应 unvdbsvr.conf 配置文件中的选项。当unvdbsvr.conf 未配置时可以通过show 查看默认配置。

show all; #显示当前所有参数值 show allow_system_table_mods; #显示指定参数值

注意: 并不是所有配置参数都能被修改。

SET 修改参数

set 命令修改运行时配置参数

SET [ SESSION | LOCAL ] 配置项 { TO | = } { value | ‘value‘ | DEFAULT } SESSION 声明这个命令只对当前会话起作用 LOCAL 声明该命令只在当前事务中有效

示例

unvdb=# show timezone;
   TimeZone    
---------------
 Asia/Shanghai
(1 row)
unvdb=# set timezone to 'PST8PDT';
SET
unvdb=# show timezone;
 TimeZone 
----------
 PST8PDT
(1 row)
unvdb=# reset timezone;
RESET
unvdb=# show timezone;
   TimeZone    
---------------
 Asia/Shanghai
(1 row)

RESET 重置参数

RESET 把一个运行时参数重置为缺省值,这个数值的实际源头可能是编译时的缺省:配置文件、命令行开关、针对每个数据库或每用户的缺省设置.

示例:

unvdb=# show timezone;
   TimeZone    
---------------
 Asia/Shanghai
(1 row)
unvdb=# set timezone to 'PST8PDT';
SET
unvdb=# show timezone;
 TimeZone 
----------
 PST8PDT
(1 row)
unvdb=# reset timezone;
RESET
unvdb=# show timezone;
   TimeZone    
---------------
 Asia/Shanghai
(1 row)

wal日志大小

/data/udb/ud_wal 此目录记录的所有写操作的数据,不适当的配置会导致此目录快速增长。
比如: 当使用了物理槽的从实例故障时,wal日志不能被清理,导致此目录快速增长,最终打满磁盘。
此时应该尽快恢复从实例或手动删除物理槽 SELECT * FROM pg_drop_replication_slot('物理槽名称');
如果使用管理平台,可以将不能快速恢复的故障节点删除
同时建议配置 wal_keep_size 来缓解此故障(注意:此值对物理槽无效)

运行日志

/data/udb/log/ 此目录记录了udb 所有日志,管理员应持续关注此日志,发现报错急时修复处理。

/var/log/message 服务级别日志会记录到系统日志文件,应该持续关注。

定期全量备份

参考 备份与恢复 章节 同时注意备份目录的大小,备份数据复制到异地

归档目录大小

全量备份+归档 可以恢复到指定的时间点,归档路径由 archive_command 参数指定。注意:如果 archive_command 有配置gz压缩,则需要占用更多的CPU资源。

归档开关由 archive_mode 参数指定,当从库设置为always 时也会产生wal日志并进行归档,需要注意wal日志大小。

应该定期打包压缩归档目录以释放更多空间,同时将归档文件和全量备份文件一同复制到异地。

会话管理

select * from pg_stat_activity where state = 'active'; --查看当前活动的会话
select * from pg_stat_activity; --查看所有会话

SELECT pg_cancel_backend(上一步获取的pid); --结束会话。这个函数只能 kill Select 查询,而updae,delete DML不生效。
SELECT pg_terminate_backend(上一步获取的pid); --杀死进程并强制退出进程会话

比如: 当进程使用数据库时,删除数据库会报以下错误,需要先杀死进程

unvdb=# drop database mydb;

ERROR:  database "mydb" is being accessed by other users
DETAIL:  There is 1 other session using the database.

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname='mydb' AND pid<>pg_backend_pid();

查看正在执行的sql

SELECT
    procpid,
    START,
    now() - START AS lap,
    current_query
FROM
    (
        SELECT
            backendid,
            pg_stat_get_backend_pid (S.backendid) AS procpid,
            pg_stat_get_backend_activity_start (S.backendid) AS START,
            pg_stat_get_backend_activity (S.backendid) AS current_query
        FROM
            (
                SELECT
                    pg_stat_get_backend_idset () AS backendid
            ) AS S
    ) AS S
WHERE
    current_query <> '<IDLE>'
ORDER BY
    lap DESC;

慢SQL

logging_collector = on
log_min_duration_statement = '3s' #记录超过3秒的sql语句,此值为0时可记录所有sql语句

慢日志记录在 /data/udb/log/ 目录内,应该及时优化改写这些慢日志。

也可以使用以下sql查询超过3秒的最长的100条记录。
注意:需要开启pg_stat_statements插件

SELECT
	to_char( now( ), 'yyyy-mm-dd hh24:mi:ss' ) "time",
	A.datname "datname(数据库名)",
	A.pid "pid(进程id)",
	b.rolname "username(用户名)",
	A.application_name "app_name(应用名称)",
	A.client_addr "client_ip(客户端ip)",
	A.query_start "query_start(当前查询开始时间)",
	to_char( A.state_change, 'yyyy-mm-dd hh24:mi:ss' ) "state_change(状态变化时间)",
	A.wait_event_type "wait_event_type(等待类型)",
	A.wait_event "wait_event(等待事件)",
	A.STATE "state(状态)",
	A.query "sql(执行的sql)",
	A.backend_type "backend_type(后端类型)" 
FROM
	pg_stat_activity
	A LEFT JOIN pg_roles b ON ( A.usesysid = b.OID ) 
WHERE
	A.STATE = 'active' 
	and 
  extract(epoch from((current_timestamp - a.query_start)))::int >3
	AND A.datname IS NOT NULL 
ORDER BY
	CURRENT_TIMESTAMP - state_change DESC 
	LIMIT 100;

锁管理

查询当前的锁。

WITH t_wait AS (
	SELECT A
		.MODE,
		A.locktype,
		A.DATABASE,
		A.relation,
		A.page,
		A.tuple,
		A.classid,
		A.GRANTED,
		A.objid,
		A.objsubid,
		A.pid,
		A.virtualtransaction,
		A.virtualxid,
		A.transactionid,
		A.fastpath,
		b.STATE,
		b.query,
		b.xact_start,
		b.query_start,
		b.usename,
		b.datname,
		b.client_addr,
		b.client_port,
		b.application_name 
	FROM
		pg_locks A,
		pg_stat_activity b 
	WHERE
		A.pid = b.pid 
		AND NOT A.GRANTED 
	),
	t_run AS (
	SELECT A
		.MODE,
		A.locktype,
		A.DATABASE,
		A.relation,
		A.page,
		A.tuple,
		A.classid,
		A.GRANTED,
		A.objid,
		A.objsubid,
		A.pid,
		A.virtualtransaction,
		A.virtualxid,
		A.transactionid,
		A.fastpath,
		b.STATE,
		b.query,
		b.xact_start,
		b.query_start,
		b.usename,
		b.datname,
		b.client_addr,
		b.client_port,
		b.application_name 
	FROM
		pg_locks A,
		pg_stat_activity b 
	WHERE
		A.pid = b.pid 
		AND A.GRANTED 
	),
	t_overlap AS (
	SELECT
		r.* 
	FROM
		t_wait w
		JOIN t_run r ON (
			r.locktype IS NOT DISTINCT 
		FROM
			w.locktype 
			AND r.DATABASE IS NOT DISTINCT 
		FROM
			w.DATABASE 
			AND r.relation IS NOT DISTINCT 
		FROM
			w.relation 
			AND r.page IS NOT DISTINCT 
		FROM
			w.page 
			AND r.tuple IS NOT DISTINCT 
		FROM
			w.tuple 
			AND r.virtualxid IS NOT DISTINCT 
		FROM
			w.virtualxid 
			AND r.transactionid IS NOT DISTINCT 
		FROM
			w.transactionid 
			AND r.classid IS NOT DISTINCT 
		FROM
			w.classid 
			AND r.objid IS NOT DISTINCT 
		FROM
			w.objid 
			AND r.objsubid IS NOT DISTINCT 
		FROM
			w.objsubid 
			AND r.pid <> w.pid 
		) 
	),
	t_unionall AS ( SELECT r.* FROM t_overlap r UNION ALL SELECT w.* FROM t_wait w ) SELECT
	pid,
	locktype,
	datname,
	relation :: REGCLASS,
	page,
	tuple,
	virtualxid,
	transactionid :: TEXT,
	classid :: REGCLASS,
	objid,
	objsubid,
	string_agg (
		'Pid: ' ||
	CASE			

			WHEN pid IS NULL THEN
			'NULL' ELSE pid :: TEXT 
		END || chr( 10 ) || 'Lock_Granted: ' ||
CASE
		
	WHEN GRANTED IS NULL THEN
		'NULL' ELSE GRANTED :: TEXT 
	END || ' , Mode: ' ||
CASE
	
WHEN MODE IS NULL THEN
	'NULL' ELSE MODE :: TEXT 
	END || ' , FastPath: ' ||
CASE
	
	WHEN fastpath IS NULL THEN
	'NULL' ELSE fastpath :: TEXT 
	END || ' , VirtualTransaction: ' ||
CASE
	
	WHEN virtualtransaction IS NULL THEN
	'NULL' ELSE virtualtransaction :: TEXT 
	END || ' , Session_State: ' ||
CASE
	
WHEN STATE IS NULL THEN
	'NULL' ELSE STATE :: TEXT 
	END || chr( 10 ) || 'Username: ' ||
CASE
		
		WHEN usename IS NULL THEN
		'NULL' ELSE usename :: TEXT 
	END || ' , Database: ' ||
CASE
	
	WHEN datname IS NULL THEN
	'NULL' ELSE datname :: TEXT 
	END || ' , Client_Addr: ' ||
CASE
	
	WHEN client_addr IS NULL THEN
	'NULL' ELSE client_addr :: TEXT 
	END || ' , Client_Port: ' ||
CASE
	
	WHEN client_port IS NULL THEN
	'NULL' ELSE client_port :: TEXT 
	END || ' , Application_Name: ' ||
CASE
	
	WHEN application_name IS NULL THEN
	'NULL' ELSE application_name :: TEXT 
	END || chr( 10 ) || 'Xact_Start: ' ||
CASE
		
		WHEN xact_start IS NULL THEN
		'NULL' ELSE xact_start :: TEXT 
	END || ' , Query_Start: ' ||
CASE
	
	WHEN query_start IS NULL THEN
	'NULL' ELSE query_start :: TEXT 
	END || ' , Xact_Elapse: ' ||
CASE
	
	WHEN ( now( ) - xact_start ) IS NULL THEN
	'NULL' ELSE ( now( ) - xact_start ) :: TEXT 
	END || ' , Query_Elapse: ' ||
CASE
	
	WHEN ( now( ) - query_start ) IS NULL THEN
	'NULL' ELSE ( now( ) - query_start ) :: TEXT 
	END || chr( 10 ) || 'SQL (Current SQL in Transaction): ' || chr( 10 ) ||
CASE
		
		WHEN query IS NULL THEN
		'NULL' ELSE query :: TEXT 
	END,
	chr( 10 ) || '--------' || chr( 10 ) 
ORDER BY
	(
	CASE
		MODE 
			WHEN 'INVALID' THEN
			0 
			WHEN 'AccessShareLock' THEN
			1 
			WHEN 'RowShareLock' THEN
			2 
			WHEN 'RowExclusiveLock' THEN
			3 
			WHEN 'ShareUpdateExclusiveLock' THEN
			4 
			WHEN 'ShareLock' THEN
			5 
			WHEN 'ShareRowExclusiveLock' THEN
			6 
			WHEN 'ExclusiveLock' THEN
			7 
			WHEN 'AccessExclusiveLock' THEN
			8 ELSE 0 
		END 
		) DESC,
		( CASE WHEN GRANTED THEN 0 ELSE 1 END ) 
		) AS lock_conflict 
	FROM
		t_unionall 
	GROUP BY
		pid,
		locktype,
		datname,
		relation,
		page,
		tuple,
		virtualxid,
		transactionid :: TEXT,
		classid,
	objid,
	objsubid;

事务管理

查询当前空闲的事务。
长时间空闲的事务会导致锁冲突的概率增加,应该当及时提交或回滚事务。

SELECT EXTRACT
	( epoch FROM ( ( CURRENT_TIMESTAMP - A.query_start ) ) ) :: INT AS COST,
-- to_char(now(),'yyyy-mm-dd hh24:mi:ss') "time" ,
	A.datname,
	A.pid,
	b.rolname,
	A.application_name,
	A.client_addr,
	A.xact_start,
	A.query_start,
	A.state_change "state_change",
	A.STATE,
	A.query,
	A.backend_type 
FROM
	pg_stat_activity
	A INNER JOIN pg_roles b ON ( A.usesysid = b.OID ) 
WHERE
	A.STATE IN ( 'idle in transaction', 'idle in transaction (aborted)' ) 
ORDER BY COST DESC 
	LIMIT 100;

同步延迟监控

select pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_flush_lsn(),write_lsn)) delay_wal_size,* from pg_stat_replication ;
-- 检查同步延迟

查看表占用磁盘大小

SELECT
       relname AS "table_name",
       pg_size_pretty(pg_table_size(C.oid)) AS "table_size"
FROM
       pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema') AND nspname !~ '^pg_toast' AND relkind IN ('r')
ORDER BY pg_table_size(C.oid) DESC;

统计所有表行数

注意:此命令会使数据库暂时变为只读状态,请谨慎执行。

\c yourdb;

WITH tbl AS
  (SELECT table_schema,
          TABLE_NAME
   FROM information_schema.tables
   WHERE TABLE_NAME not like 'pg_%'
     AND table_schema in ('public'))
SELECT table_schema,
       TABLE_NAME,
       (xpath('/row/c/text()', query_to_xml(format('select count(*) as c from %I.%I', table_schema, TABLE_NAME), FALSE, TRUE, '')))[1]::text::int AS rows_n
FROM tbl
ORDER BY rows_n DESC;

查看当前表的死行数量

SELECT relname, n_dead_tup FROM pg_stat_user_tables;

VACUUM 清理

注意:谨慎手动执行 VACUUM 或 ANALYZE

VACUUM 用于恢复表中“死元组”占用的空间。当记录被删除或更新(删除后插入)时,会创建一个死元组。udb 不会从表中物理删除旧行,而是在其上放置一个“标记”,以便查询不会返回该行。当vacuum进程运行时,这些死元组占用的空间被标记为可被其他元组重用。

当删除大量数据,磁盘空间未释放时,可以考虑手动VACUUM释放空间。

VACUUM 可以最大限度地减少表膨胀并防止事务 ID 回卷。Autovacuum 不会恢复死元组占用的磁盘空间。但是,手动运行VACUUM FULL命令则会释放死元组占用的磁盘空间。VACUUM FULL 在操作期间被独占锁定,甚至阻止对表的读取。该进程还会制作表的完整副本,这在运行时需要额外的磁盘空间。所以建议不要运行 VACUUM FULL,除非膨胀率非常高,并且查询受到严重影响。

最好不要在整个数据库上过于频繁地运行手动清理;目标数据库可能已经通过 autovacuum 过程进行了最佳清理。因此,手动清理可能不会删除任何死元组,但会导致不必要的 I/O 负载或 CPU 峰值。如有必要,手动清理应仅在需要时逐表运行,例如活动行与死行的比率较低,或自动清理之间的间隙较大。此外,应在用户活动最少时进行手动vacuum。

当导入大量数据时也建议手动执行 vacuum analyze 使数据库能够使用比较好的查询计划。

autovacuum 自动清理规则

test1=# select name,setting,short_desc from pg_settings where name in ('autovacuum','autovacuum_vacuum_threshold','autovacuum_vacuum_scale_factor','autovacuum_max_workers','track_counts','autovacuum_analyze_threshold','autovacuum_analyze_scale_factor');
              name               | setting |                                        short_desc                                      
 autovacuum                      | on      | Starts the autovacuum subprocess.
 autovacuum_analyze_scale_factor | 0.1     | Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples.
 autovacuum_analyze_threshold    | 50      | Minimum number of tuple inserts, updates, or deletes prior to analyze.
 autovacuum_max_workers          | 3       | Sets the maximum number of simultaneously running autovacuum worker processes.
 autovacuum_vacuum_scale_factor  | 0.2     | Number of tuple updates or deletes prior to vacuum as a fraction of reltuples.
 autovacuum_vacuum_threshold     | 50      | Minimum number of tuple updates or deletes prior to vacuum.
 track_counts                    | on      | Collects statistics on database activity.
(7 rows)

autovacuum 默认是on,开启自动清理 autovacuum_max_workers 默认是3,最大多少个进程执行清理 autovacuum_vacuum_threshold 默认是50 autovacuum_vacuum_scale_factor 默认是0.2 track_counts 默认是on,关闭时统计信息不会收集死行数 当 死行数 大于 数据表总行数 * autovacuum_vacuum_scale_factor + autovacuum_vacuum_threshold ,触发自动清理。 例如: 某表有10000行数据,当死行的数据超过(10000*0.2+50)= 2050 行时,会触发自动清理。

查询上一次清理或统计时间

test1=# select relname,last_vacuum, last_autovacuum, last_analyze, vacuum_count, autovacuum_count,
  last_autoanalyze from pg_stat_user_tables;
 relname |          last_vacuum          | last_autovacuum | last_analyze | vacuum_count | autovacuum_count | last_autoanalyze 
---------+-------------------------------+-----------------+--------------+--------------+------------------+------------------
 tb2     | 2022-09-12 11:38:37.076698+08 |                 |              |            1 |                0 | 
(1 row)

autovacuum微调

比如某表有100万行数据,按以上配置,当死行超过200050 时才触发自动清理,显然这个值过大。 也会导致自动清理周期可能会较长,清理速度也更慢。更坏的情况当有事务正在锁定此表时,自动清理不会运行。 此时可以微调vacuum配置,核心目的是:让死行数相对较低。从而自动清理运行并不会花费很长时间。

例如: 把 autovacuum_vacuum_scale_factor 设置为0,autovacuum_vacuum_threshold 设置为5000。这样当表死行达到5000行时,就会运行自动清理。

ALTER TABLE <table_name> SET (autovacuum_vacuum_threshold = 50000)
ALTER TABLE <table_name> SET (autovacuum_vacuum_scale_factor = 0)

VACUUM手动清理

VACUUM; #回收页空间,使这部分空间再次被使用 VACUUM 表名; #回收指定表的页空间 VACUUM full; #清理并释放磁盘空间 VACUUM full 表名; #清理并释放指定表的磁盘空间

ANALYZE 统计信息

注意:谨慎手动执行 VACUUM 或 ANALYZE

ANALYZE 收集数据库中表内容的统计信息,然后把结果保存在系统表pg_statistic里。 随后,查询规划器就可以使用这些统计帮助判断查询的最佳规划。 随着在数据库中插入、删除和更新行,列统计信息也会发生变化。ANALYZE 会在 autovacuum 后自动运行。

当导入大量数据时,新增加的行会导致统计信息不准确,查询性能不理想。此时可以手动运行 ANALYZE 重建统计信息,而不用等待自动清理触发ANALYZE。

ANALYZE 要求目标表上的 SHARE UPDATE EXCLUSIVE 锁。这个锁会与这些锁冲突:SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE。

每天在业务低峰期执行一次 ANALYZE 是个不错的选择。

当导入大量数据时也建议手动执行 vacuum analyze 使数据库能够使用比较好的查询计划。

ANALYZE 自动统计规则

test1=# select name,setting,short_desc from pg_settings where name in ('autovacuum_analyze_threshold','autovacuum_analyze_scale_factor');
              name               | setting |                                        short_desc                                      
 autovacuum_analyze_scale_factor | 0.1     | Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples.
 autovacuum_analyze_threshold    | 50      | Minimum number of tuple inserts, updates, or deletes prior to analyze.
(2 rows)

autovacuum_analyze_threshold 默认是50 autovacuum_analyze_scale_factor 默认是0.1 当 **插入更新或删除的行数 大于 数据表总行数 * autovacuum_analyze_scale_factor + autovacuum_analyze_threshold **,触发自动统计。例如:某表有10000行数据,当修改的数据超过(10000*0.2+50)= 2050 行时,会触发自动统计。

查询上一次清理或统计时间

test1=# select relname,last_vacuum, last_autovacuum, last_analyze, vacuum_count, autovacuum_count,
  last_autoanalyze from pg_stat_user_tables;
 relname |          last_vacuum          | last_autovacuum |         last_analyze          | vacuum_count | autovacuum_count | last_autoanalyze 
 tb2     | 2022-09-12 11:40:32.771084+08 |                 | 2022-09-12 11:56:32.975965+08 |            2 |                0 | 
(1 row)

analyze微调

比如某表有100万行数据,按以上配置,当修改行数超过200050 时才触发自动清理,显然这个值过大。 也会导致自动统计周期可能会较长,速度也更慢。 此时可以微调analyze配置,核心目的是:让已修改行数相对较低。从而自动统计运行并不会花费很长时间。

例如: 把 autovacuum_analyze_scale_factor 设置为0,autovacuum_analyze_threshold 设置为5000。这样当修改委达到5000行时,就会运行自动统计。

ALTER TABLE <table_name> SET (autovacuum_analyze_threshold = 50000)
ALTER TABLE <table_name> SET (autovacuum_analyze_scale_factor = 0)

analyze手动统计

analyze tb2; #统计指定表 analyze; #统计当前库

SAVEPOINT 定义保存点

SAVEPOINT 在当前事务中定义一个新的保存点

示例: 要建立一个保存点并且后来撤销在它建立后执行的所有命令的效果:

BEGIN;
  INSERT INTO tb2 VALUES (1);
  SAVEPOINT myname;
  INSERT INTO tb2 VALUES (2);
  ROLLBACK TO SAVEPOINT myname;
  INSERT INTO tb2 VALUES (3);
COMMIT;

上面的事务将插入值1和3,但不会插入2。

要建立并且稍后销毁一个保存点:

BEGIN;
  INSERT INTO tb2 VALUES (3);
  SAVEPOINT myname;
  INSERT INTO tb2 VALUES (4);
  RELEASE SAVEPOINT myname;
COMMIT;

上面的事务将插入3和4。

注意: 当建立另一个同名保存点时,SQL 要求之前的那个保存点自动被销毁。在数据库中,旧的保存点会被保留,不过在进行回滚或释放时只能使用最近的那一个

CHECKPOINT 强制设置日志检查点

test1=# checkpoint;
CHECKPOINT

预写式日志记录(WAL)每隔一段时间将一个检查点放在事务日志中。根据服务器配置参数 max_wal_size 和 checkpoint_timeout,每个数据库段实例设置自动检查点间隔。 CHECKPOINT 命令在发出命令时强制立即检查点,而不等待计划的检查点。 只有超级用户可以调用 CHECKPOINT。该命令不适用于正常操作。一般不需要手动执行它。

数据损坏修复

简介

本节主要讲述数据或磁盘设备损坏时,如何尽最大能力进行修复,并不保证数据一定能修复。 请务必做好日常备份,并首选从备份中恢复。

ud_resetwal

此命令适用于 delete 数据后,没有手动或自动 vacuum 的场景,核心原理是将幽灵数据可见。 此命令可能导致数据混乱,不建议在生产环境中使用。 更多数据修复方法可参考 “插件管理” 内相关功能。

操作步骤

将当前数据复制到一个单独的机器,避免对当前数据产生二次损坏

注意文件权限问题,建议使用rsync -avzP 源目录 目标主机 命令同步

从wal 日志中分析要还原的 事务 号

ud_waldump /tmp/000000070000000000000018
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/18000028, prev 0/170019E0, desc: RUNNING_XACTS nextXid 53112 latestCompletedXid 53111 oldestRunningXid 53112
rmgr: XLOG        len (rec/tot):     49/  6057, tx:          0, lsn: 0/18000060, prev 0/18000028, desc: FPI_FOR_HINT , blkref #0: rel 1664/0/1262 blk 0 FPW
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/18001810, prev 0/18000060, desc: RUNNING_XACTS nextXid 53112 latestCompletedXid 53111 oldestRunningXid 53112
rmgr: XLOG        len (rec/tot):     49/   545, tx:          0, lsn: 0/18001848, prev 0/18001810, desc: FPI_FOR_HINT , blkref #0: rel 1664/0/2671 blk 1 FPW
rmgr: Heap2       len (rec/tot):     60/    60, tx:      53112, lsn: 0/18001A70, prev 0/18001848, desc: NEW_CID rel 1664/0/1262; tid 0/25; cmin: 0, cmax: 4294967295, combo: 4294967295
rmgr: Heap        len (rec/tot):    291/   291, tx:      53112, lsn: 0/18001AB0, prev 0/18001A70, desc: INSERT off 25 flags 0x00, blkref #0: rel 1664/0/1262 blk 0
rmgr: Btree       len (rec/tot):     64/    64, tx:      53112, lsn: 0/18001BD8, prev 0/18001AB0, desc: INSERT_LEAF off 2, blkref #0: rel 1664/0/2671 blk 1
rmgr: Btree       len (rec/tot):     53/   553, tx:      53112, lsn: 0/18001C18, prev 0/18001BD8, desc: INSERT_LEAF off 23, blkref #0: rel 1664/0/2672 blk 1 FPW
rmgr: Standby     len (rec/tot):     54/    54, tx:          0, lsn: 0/18001E48, prev 0/18001C18, desc: RUNNING_XACTS nextXid 53113 latestCompletedXid 53111 oldestRunningXid 53112; 1 xacts: 53112
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 0/18001E80, prev 0/18001E48, desc: CHECKPOINT_ONLINE redo 0/18001E48; tli 7; prev tli 7; fpw true; xid 0:53113; oid 32810; multi 1; offset 0; oldest xid 727 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 53112; online
rmgr: Database    len (rec/tot):     42/    42, tx:      53112, lsn: 0/18001EF8, prev 0/18001E80, desc: CREATE copy dir 1663/1 to 1663/24619
#在这里创建了数据库
rmgr: Standby     len (rec/tot):     54/    54, tx:          0, lsn: 0/18001F28, prev 0/18001EF8, desc: RUNNING_XACTS nextXid 53113 latestCompletedXid 53111 oldestRunningXid 53112; 1 xacts: 53112
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 0/18001F60, prev 0/18001F28, desc: CHECKPOINT_ONLINE redo 0/18001F28; tli 7; prev tli 7; fpw true; xid 0:53113; oid 32810; multi 1; offset 0; oldest xid 727 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 53112; online
rmgr: Transaction len (rec/tot):     46/    46, tx:      53112, lsn: 0/18001FD8, prev 0/18001F60, desc: INVALIDATION ; inval msgs: catcache 21
rmgr: Transaction len (rec/tot):     66/    66, tx:      53112, lsn: 0/18002020, prev 0/18001FD8, desc: COMMIT 2022-08-23 14:45:41.510217 CST; inval msgs: catcache 21; sync
#commit提交
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/18002068, prev 0/18002020, desc: RUNNING_XACTS nextXid 53113 latestCompletedXid 53112 oldestRunningXid 53113
rmgr: XLOG        len (rec/tot):     49/  6325, tx:          0, lsn: 0/180020A0, prev 0/18002068, desc: FPI_FOR_HINT , blkref #0: rel 1664/0/1262 blk 0 FPW
rmgr: Heap2       len (rec/tot):     60/    60, tx:      53113, lsn: 0/18003958, prev 0/180020A0, desc: NEW_CID rel 1664/0/1262; tid 0/25; cmin: 4294967295, cmax: 0, combo: 4294967295
rmgr: Heap        len (rec/tot):     54/    54, tx:      53113, lsn: 0/18003998, prev 0/18003958, desc: DELETE off 25 flags 0x00 KEYS_UPDATED , blkref #0: rel 1664/0/1262 blk 0
rmgr: Standby     len (rec/tot):     54/    54, tx:          0, lsn: 0/180039D0, prev 0/18003998, desc: RUNNING_XACTS nextXid 53114 latestCompletedXid 53112 oldestRunningXid 53113; 1 xacts: 53113
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 0/18003A08, prev 0/180039D0, desc: CHECKPOINT_ONLINE redo 0/180039D0; tli 7; prev tli 7; fpw true; xid 0:53114; oid 32810; multi 1; offset 0; oldest xid 727 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 53113; online
rmgr: Database    len (rec/tot):     38/    38, tx:      53113, lsn: 0/18003A80, prev 0/18003A08, desc: DROP dir 1663/24619
#这里是删除操作
rmgr: Transaction len (rec/tot):     46/    46, tx:      53113, lsn: 0/18003AA8, prev 0/18003A80, desc: INVALIDATION ; inval msgs: catcache 21
rmgr: Transaction len (rec/tot):     66/    66, tx:      53113, lsn: 0/18003AD8, prev 0/18003AA8, desc: COMMIT 2022-08-23 14:45:48.894563 CST; inval msgs: catcache 21; sync
#删除提交

重置wal

ud_resetwal -x 53113 -D /data/udb/

此时数据会还原到53113事务之前的状态。

修复数据

从上面的数据中提取想要的数据,并写入生产环境.