定制插件

oraudb

本插件是基于orafce 的UDB定制版本,UDB在orafce的基础上对部分功能进行了增强。该插件的启用方法如下:

[root@pgtest1 orafce-VERSION_3_18_1]# ud_sql -c "CREATE EXTENSION oraudb;"

鉴于oraudb的一些对象建立在oracle schema下,可以根据需要在会话级、database级或实例级,设置search_path 包含oracle schema。 例如:

ALTER ROLE role_name SET search_path to "$user",public,oracle ;

或者

ALTER DATABASE database_name  SET search_path = "$user",public,oracle ;

这样就配置完成了,但是需要注意时区问题,orafce 默认使用的是UDB-TX 数据库系统的时区,由单独的参数 orafce.timezone 控制。

[root@pgtest1 ~]# vi unvdbsvr.conf
log_timezone = 'PRC'
timezone = 'PRC'
orafce.timezone = 'PRC'
[root@pgtest1 ~]# 
[root@pgtest1 ~]# ud_sql
unvdbsvr=# show orafce.timezone;
 orafce.timezone
-----------------
 PRC
(1 row)
unvdbsvr=# select oracle.sysdate(),now(),current_timestamp,clock_timestamp();
       sysdate       |              now              |       current_timestamp       |        clock_timestamp
---------------------+-------------------------------+-------------------------------+-------------------------------
 2022-04-01 10:32:42 | 2022-04-01 10:32:41.543877+08 | 2022-04-01 10:32:41.543877+08 | 2022-04-01 10:32:41.543979+08
(1 row)

与 Oracle 数据库兼容的特性

数据类型

数据类型 说明
VARCHAR2 可变长度字符数据类型
NVARCHAR2 可变长度国家字符数据类型
DATE 存储日期和时间的数据类型
unvdbsvr=# set search_path="$user", public, oracle;
SET
unvdbsvr=# CREATE TABLE tt (
unvdbsvr(#  name VARCHAR2(64) NOT NULL,
unvdbsvr(#  status NVARCHAR2(2000),
unvdbsvr(#  update_time DATE default (SYSDATE())
unvdbsvr(# );
CREATE TABLE
unvdbsvr=# \d tt
                        Table "public.tt"
   Column    |      Type       | Collation | Nullable |  Default
-------------+-----------------+-----------+----------+-----------
 name        | varchar2(64)    |           | not null |
 status      | nvarchar2(2000) |           |          |
 update_time | date            |           |          | sysdate()
# 测试在 date 数据类型的字段上创建分区表
create table test_range(id serial, create_time date) partition by range(create_time);
create table test_range_20220301 PARTITION of test_range FOR VALUES FROM ('2022-03-01 00:00:00') TO ('2022-03-02 00:00:00');
create table test_range_20220302 PARTITION of test_range FOR VALUES FROM ('2022-03-02 00:00:00') TO ('2022-03-03 00:00:00');
create table test_range_20220303 PARTITION of test_range FOR VALUES FROM ('2022-03-03 00:00:00') TO ('2022-03-04 00:00:00');
create table test_range_20220304 PARTITION of test_range FOR VALUES FROM ('2022-03-04 00:00:00') TO ('2022-03-05 00:00:00');
create table test_range_20220305 PARTITION of test_range FOR VALUES FROM ('2022-03-05 00:00:00') TO ('2022-03-06 00:00:00');
create table test_range_default partition of test_range default;
unvdbsvr=# \d+ test_range
                                            Partitioned table "public.test_range"
   Column    |  Type   | Collation | Nullable |                Default                 | Storage | Stats target | Description
-------------+---------+-----------+----------+----------------------------------------+---------+--------------+-------------
 id          | integer |           | not null | nextval('test_range_id_seq'::regclass) | plain   |              |
 create_time | date    |           |          |                                        | plain   |              |
Partition key: RANGE (create_time)
Partitions: test_range_20220301 FOR VALUES FROM ('2022-03-01') TO ('2022-03-02'),
            test_range_20220302 FOR VALUES FROM ('2022-03-02') TO ('2022-03-03'),
            test_range_20220303 FOR VALUES FROM ('2022-03-03') TO ('2022-03-04'),
            test_range_20220304 FOR VALUES FROM ('2022-03-04') TO ('2022-03-05'),
            test_range_20220305 FOR VALUES FROM ('2022-03-05') TO ('2022-03-06'),
            test_range_default DEFAULT
# 向分区表中插入数据
unvdbsvr=# insert into test_range (create_time) values (sysdate());
INSERT 0 1
# 查询分区表里的数据
unvdbsvr=# select * from test_range;
 id | create_time
----+-------------
  2 | 2022-07-31
(1 row)
unvdbsvr=# select to_char(create_time,'YYYY-MM-DD HH24:MI:SS') from test_range;
       to_char
---------------------
 2022-07-31 00:00:00
(1 row)

为什么这里的 date 数据类型似乎只存储 ‘年月日’,而 Oracle 的 date 数据类型会存储 ‘年月日 时分秒’。 这里就要说一下使用 orafce 的注意事项,详见后面章节<使用 orafce 的注意事项>。

支持 DUAL 表

unvdbsvr=# \d+ dual
                                  View "public.dual"
 Column |       Type        | Collation | Nullable | Default | Storage  | Description
--------+-------------------+-----------+----------+---------+----------+-------------
 dummy  | character varying |           |          |         | extended |
View definition:
 SELECT 'X'::character varying AS dummy;
unvdbsvr=# \dv public.*
               List of relations
 Schema |        Name        | Type |  Owner
--------+--------------------+------+----------
 public | dual               | view | unvdbsvr
 public | pg_stat_statements | view | unvdbsvr
(2 rows)
unvdbsvr=# select 1 from dual;
 ?column?
----------
        1
(1 row)
unvdbsvrs=# select * from dual;
 dummy
-------
 X
(1 row)
unvdbsvr=# SELECT CURRENT_DATE "date" FROM DUAL;
    date
------------
 2022-04-01

SQL 函数

数学函数

函数名称 说明 对比无插件UDB-TX22.4
BITAND Performs a bitwise AND operation 增强
COSH Calculates the hyperbolic cosine of a number 自带
SINH Calculates the hyperbolic sine of a number 自带
TANH Calculates the hyperbolic tangent of a number 自带

字符串函数

函数名称 说明 对比无插件UDB-TX22.4
INSTR Returns the position of a substring in a string 新增
LENGTH Returns the length of a string in number of characters 增强
LENGTHB Returns the length of a string in number of bytes 新增
LPAD Left-pads a string to a specified length with a sequence of characters 增强
LTRIM Removes the specified characters from the beginning of a string 增强
NLSSORT Returns a byte string used to sort strings in linguistic sort sequence based on locale 新增
REGEXP_COUNT searches a string for a regular expression, and returns a count of the matches 新增
REGEXP_INSTR returns the beginning or ending position within the string where the match for a pattern was located 新增
REGEXP_LIKE condition in the WHERE clause of a query, causing the query to return rows that match the given pattern 新增
REGEXP_SUBSTR returns the string that matches the pattern specified in the call to the function 新增
REGEXP_REPLACE replace substring(s) matching a POSIX regular expression 增强
RPAD Right-pads a string to a specified length with a sequence of characters 增强
RTRIM Removes the specified characters from the end of a string 增强
SUBSTR Extracts part of a string using characters to specify position and length 增强
SUBSTRB Extracts part of a string using bytes to specify position and length 新增

Date/time 函数

函数名称 说明 对比无插件UDB-TX22.4
ADD_MONTHS Adds months to a date 新增
DBTIMEZONE Returns the value of the database time zone 新增
LAST_DAY Returns the last day of the month in which the specified date falls 新增
MONTHS_BETWEEN Returns the number of months between two dates 新增
NEXT_DAY Returns the date of the first instance of a particular day of the week that follows the specified date 新增
ROUND Rounds a date 增强
SESSIONTIMEZONE Returns the time zone of the session 新增
SYSDATE Returns the system date 新增
TRUNC Truncates a date 增强

数据类型格式化函数

函数名称 说明 对比无插件UDB-TX22.4
TO_CHAR Converts a value to a string 增强
TO_DATE Converts a string to a date in accordance with the specified format 增强
TO_MULTI_BYTE Converts a single-byte string to a multibyte string 新增
TO_NUMBER Converts a value to a number in accordance with the specified format 增强
TO_SINGLE_BYTE Converts a multibyte string to a single-byte string 新增

条件表达式函数

函数名称 说明 对比无插件UDB-TX22.4
DECODE Compares values, and if they match, returns a corresponding value 增强
GREATEST Returns the greatest of the list of one or more expressions 增强
LEAST Returns the least of the list of one or more expressions 增强
LNNVL Evaluates if a value is false or unknown 新增
NANVL Returns a substitute value when a value is not a number (NaN) 新增
NVL Returns a substitute value when a value is NULL 新增
NVL2 Returns a substitute value based on whether a value is NULL or not NULL 新增

聚合函数

函数名称 说明 对比无插件UDB-TX22.4
LISTAGG Returns a concatenated, delimited list of string values 新增
MEDIAN Calculates the median of a set of values 新增
  • 返回内部信息的函数

函数名称 说明 对比无插件UDB-TX22.4
DUMP Returns internal information of a value 新增

SQL 运算符

名称 说明 对比无插件UDB-TX22.4
Datetime operator Datetime operator for the DATE type -

Packages 包

名称 说明 对比无插件UDB-TX22.4
DBMS_ALERT Sends alerts to multiple sessions -
DBMS_ASSERT Validates the properties of an input value -
DBMS_OUTPUT Sends messages to clients -
DBMS_PIPE Creates a pipe for inter-session communication -
DBMS_RANDOM Generates random numbers -
DBMS_UTILITY Provides various utilities -
UTL_FILE Enables text file operations -

unvdbGIS

unvdbgis 是一组基于PostGIS定制的适用于UDB-TX 数据库的GIS扩展。每个扩展提供的技术细节,可联机查询。 类似命令如下:

unvdb=# \dx+ unvdbgis

unvdbgis

unvdbgis的基本核心功能,仅支持地理图形(矢量要素),在其他Extension前启用。这个是基础插件,必须要安装的。

unvdbgis_raster

对栅格数据的支持。安装过unvdbgis后可直接启用该扩展,默认不启用。

unvdbgis_topology

拓扑功能的支持。安装过unvdbgis后可直接启用该扩展,默认不启用。

unvdbgis_tiger_geocoder

TIGER指的是(拓扑集成地理编码和参考),这个是美国人口普查局的GIS数据,提供了美国全国的行政区划、交通道路、水系等空间数据。这个Extension提供了TIGER数据的地理编码支持,需要注意的是这个Extension启用前,需要先启用fuzzystrmatch(字符串模糊查询)Extension(至少需要启用这个),以及可选的address_standardizer(TIGER数据地址规则化)、address_standardizer_data_us(地址规则化示例数据集)Extension。

(先安装了fuzzystrmatch后,才可以启用unvdbgis_tiger_geocoder)

unvdbgis_sfcgal

这个Extension主要是集成了CGAL(Computational Geometry Algorithms Library,计算几何算法库),来进行三维空间数据的空间运算,例如ST_3DDifference、ST_3DUnion 等,可见是通常空间运算在三维空间上的拓展。可产生各种3d模型,进而利用产生的数据进行3d渲染。

ud_dirtyread-查看脏数据

简介

ud_dirtyread 是脏读插件,用于读取那些已经删除但未被清理的数据。当误操作delete数据时,可以使用它从脏数据中恢复出来。 不支持 DDL 操作和 truncate 操作的恢复。

前提要求

  • 未手动或自动执行 vacuum 清理操作

查询上一次 vacuum 执行时间,如已经执行了清理,则数据无法恢复。

select relname,last_vacuum, last_autovacuum, last_analyze, vacuum_count, autovacuum_count,  last_autoanalyze from pg_stat_user_tables;

注意事项

  • 不建议在生产环境直接执行本节的所有操作。

  • 建议将当前所有文件复制到一个独立的机器进行恢复操作,避免对当前环境造成更大的数据损坏。

  • 在将结果恢复到生产环境时,建议先备份生产环境当前的所有数据。

安装/启用

udb-tx-v22.4.22及以后的版本,已启用 ud_dirtyread, 直接使用即可。 udb-tx-v22.4.22及以前的版本,按以下步骤安装启用。

安装

【附件】ud_dirtyread.zip 如下载失败请联系技术支持获取。

启用

  • ud_dirtyread.so 放到安装目录的lib/ 下

  • ud_dirtyread.control 和 ud_dirtyread–*.sql 放到安装目录的share/extension/ 下

  • chown -R udb.udb 安装目录/lib/ud_dirtyread.so

  • chown -R udb.udb 安装目录/share/extension/ud_dirtyread*

验证安装

waltest=#  CREATE EXTENSION ud_dirtyread;
CREATE EXTENSION

使用示例

创建测试数据

waltest=# select * from tb1;
 id | name 
----+------
  1 | a
  2 | b
(2 rows)
waltest=# \dt tb1;
       List of relations
 Schema | Name | Type  | Owner 
--------+------+-------+-------
 public | tb1  | table | unvdb
(1 row)
waltest=# \d tb1;
                        Table "public.tb1"
 Column |          Type          | Collation | Nullable | Default 
--------+------------------------+-----------+----------+---------
 id     | integer                |           | not null | 
 name   | character varying(255) |           |          | 
Indexes:
    "tb1_pkey" PRIMARY KEY, btree (id)

查看当前数据元组信息

waltest=# select xmin,xmax,ctid,* from tb1;     
 xmin | xmax | ctid  | id | name 
------+------+-------+----+------
 1650 |    0 | (0,1) |  1 | a
 1651 |    0 | (0,2) |  2 | b
(2 rows)

查看是否为脏数据,dead列为f,表示非死数据

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,name varchar(255)); 
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | name 
----------+-------+------+------+------+------+------+----+------
    66027 | (0,1) | 1650 |    0 |    0 |    0 | f    |  1 | a
    66027 | (0,2) | 1651 |    0 |    0 |    0 | f    |  2 | b
(2 rows)

删除数据

waltest=# delete from tb1 where id=1;
DELETE 1
waltest=# select * from tb1;
 id | name 
----+------
  2 | b
(1 row)

修改数据

waltest=# update tb1 set name='bb' where id=2;
UPDATE 1

查看已删除未清理的脏数据

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,name varchar(255));
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | name 
----------+-------+------+------+------+------+------+----+------
    66027 | (0,1) | 1650 | 1652 |    0 |    0 | t    |  1 | a
    66027 | (0,2) | 1651 | 1653 |    0 |    0 | t    |  2 | b
    66027 | (0,3) | 1653 |    0 |    0 |    0 | f    |  2 | bb
(3 rows)

会发现id=1的那条数据状态 dead=t 更新的id=2那条数据的事务已经由1651变为1653,原来的状态变为了删除,值已经变更为bb

删除列

waltest=# alter table tb1 drop column name;
ALTER TABLE
waltest=# select * from tb1;
 id 
----
  2
(1 row)

查看已删除列的脏数据,此时要用 dropped_n 来代替

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,dropped_2 varchar(255)); 
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | dropped_2 
----------+-------+------+------+------+------+------+----+-----------
    66027 | (0,1) | 1650 | 1652 |    0 |    0 | t    |  1 | a
    66027 | (0,2) | 1651 | 1653 |    0 |    0 | t    |  2 | b
    66027 | (0,3) | 1653 |    0 |    0 |    0 | f    |  2 | bb
(3 rows)

ud_walminer-从wal日志中提取sql实现数据修复

简介

ud_walminer 可以解析 wal 日志,尽可能多的获取有用信息,进一步生成undo语句,实现恢复删除或丢失的数据。 功能如下:

  • 从wal日志中提取DML和少部分DDL,并生成对应的回滚语句;

  • 磁盘部分损坏或执行了TRUNCATE等清除操作时,可以使用ud_walminer尝试从wal中提取数据,尽最大可能找回数据。不保证一定能恢复成功。

前提要求

  • 表结构需要尽可能完整,例如字段名称,字段类型等

  • wal日志文件越多越好

注意事项

  • 不建议在生产环境直接执行本节的所有操作。

  • 建议将当前所有文件复制到一个独立的机器进行恢复操作,避免对当前环境造成更大的数据损坏。

  • 在将结果恢复到生产环境时,建议先备份生产环境当前的所有数据。

  • 在一次解析开始时会首先创建或truncate walminer_contents表,当得到想要的数据时建议先导出备份它们再执行第二次解析。

安装/启用

udb-tx-v22.4.22及以后的版本,已启用 ud_walminer, 直接使用即可。 udb-tx-v22.4.22及以前的版本,按以下步骤安装启用。

安装

【附件】ud_walminer.zip 如下载失败请联系技术支持获取。

启用

  • walminer.so 放到安装目录的lib/ 下

  • walminer.control和walminer–3.0.sql 放到安装目录的share/extension/ 下

  • chown -R udb.udb 安装目录/lib/walminer.so

  • chown -R udb.udb 安装目录/share/extension/walminer.*

验证安装

[udb@0a2593d70d5b udb]$ ud_sql 
ud_sql (2.4)
Type "help" for help.
unvdb=# create extension walminer;
-- 创建扩展插件
CREATE EXTENSION
unvdb=# 

使用说明

基础操作

create extension walminer; -- 创建扩展
select walminer_wal_add('/data/udb/ud_wal'); --添加要解析的wal日志目录或单个文件名
select walminer_wal_remove('/opt/test/wal'); --移除wal日志目录或单个文件名
select walminer_wal_list(); --列出wal文件

解析操作

#普通解析
select walminer_all(); --解析已添加的全部wal日志
select walminer_by_time(starttime, endtime); --在add的wal日志中查找对应时间范围的wal记录
select walminer_by_lsn(startlsn, endlsn); --在add的wal日志中查找对应lsn范围的wal记录
select walminer_by_xid(xid); --在add的wal日志中查找对应xid的wal记录
#精确解析
select walminer_by_time(starttime, endtime,'true');  --精确查找对应时间范围的wal记录
select walminer_by_lsn(startlsn, endlsn,'true');  --精确查找对应lsn范围的wal记录
select walminer_by_xid(xid,'true'); --精确查找对应xid的wal记录
#普通解析会严格匹配给定的范围,但可能没找到之前的checkpoint点,所以会出现记录不全的情况;
#精确解析会自动查找给定范围之前的checkpoint点,更完美的解析出结果。当普通解析结果不全时可以尝试精确解析。
#当本机没有数据结构时,可以加载其它机器生成的的数据结构
select walminer_build_dictionary('/tmp/store_dictionary'); --根据当前数据库结构生成数据字典
select walminer_load_dictionary('/tmp/store_dictionary'); --加载数据字典
#reloid 参数可以查找指定表,要用表的oid
select walminer_by_time(starttime, endtime,'false',reloid); --非精确模式查找对应时间范围的reloid表oid的wal记录
select walminer_by_lsn(startlsn, endlsn,'true',reloid); --在add的wal日志中查找对应lsn范围的wal记录
select walminer_by_xid(xid,'true',reloid);--在add的wal日志中查找对应xid的wal记录
#当表结构不完整时创建临时表并使用临时表映射解析。
create table tmp_avatar(i int); --创建临时表
select walminer_table_avatar(tmp_avatar, missed_relfilenode); --映射到临时表。此时的解析结果将按临时表格式输出。
select wal2sql(); --执行解析,wal文件越多,需要的时间也越多。
select * from walminer_contents; --查看解析结果
-- 表walminer_contents 
(
 sqlno int,         --本条sql在其事务内的序号
 xid bigint,        --事务ID
 topxid bigint,        --如果为子事务,这是是其父事务;否则为0
 sqlkind int,        --sql类型1->insert;2->update;3->delete(待优化项目)
 minerd bool,        --解析结果是否完整(缺失checkpoint情况下可能无法解析出正确结果)
 timestamp timestampTz, --这个SQL所在事务提交的时间
 op_text text,        --sql
 undo_text text,    --undo sql
 complete bool,        --如果为false,说明有可能这个sql所在的事务是不完整解析的
 schema text,        --目标表所在的模式
 relation text,        --目标表表名
 start_lsn pg_lsn,    --这个记录的开始LSN
 commit_lsn pg_lsn    --这个事务的提交LSN
)
select walminer_stop(); --结束分析操作,并释放内存。

实践演示

delete 操作恢复

模拟删除操作

waltest=# select * from tb1;         
 id | name 
----+------
  1 | a
  2 | b
(2 rows)
waltest=# delete from tb1;
DELETE 2
waltest=# select * from tb1;
 id | name 
----+------
(0 rows)

配置ud_walminer

waltest=# create extension walminer;--创建扩展
CREATE EXTENSION
waltest=# select walminer_wal_add('/data/udb/ud_wal');--添加wal目录
  walminer_wal_add   
---------------------
 24 file add success
(1 row)
waltest=# select walminer_wal_list();--列出wal文件
              walminer_wal_list              
---------------------------------------------
 (/data/udb/ud_wal/00000001000000090000001C)
 (/data/udb/ud_wal/00000001000000090000001D)
 (/data/udb/ud_wal/00000001000000090000001E)
 (/data/udb/ud_wal/00000001000000090000001F)
 (/data/udb/ud_wal/000000010000000900000020)
 (/data/udb/ud_wal/000000010000000900000021)
 (/data/udb/ud_wal/000000010000000900000022)
 (/data/udb/ud_wal/000000010000000900000023)
 (/data/udb/ud_wal/000000010000000900000024)
 (/data/udb/ud_wal/000000010000000900000025)
 (/data/udb/ud_wal/000000010000000900000026)
 (/data/udb/ud_wal/000000010000000900000027)
 (/data/udb/ud_wal/000000010000000900000028)
 (/data/udb/ud_wal/000000010000000900000029)
 (/data/udb/ud_wal/00000001000000090000002A)
 (/data/udb/ud_wal/00000001000000090000002B)
 (/data/udb/ud_wal/00000001000000090000002C)
 (/data/udb/ud_wal/00000001000000090000002D)
 (/data/udb/ud_wal/00000001000000090000002E)
 (/data/udb/ud_wal/00000001000000090000002F)
 (/data/udb/ud_wal/000000010000000900000030)
 (/data/udb/ud_wal/000000010000000900000031)
 (/data/udb/ud_wal/000000010000000900000032)
 (/data/udb/ud_wal/000000010000000900000033)
(24 rows)
waltest=# select walminer_all(); --提取wal日志
NOTICE:  Switch wal to 00000001000000090000001C on time 2023-03-27 13:58:30.446893+08
NOTICE:  Switch wal to 00000001000000090000001D on time 2023-03-27 13:58:30.447915+08
NOTICE:  Switch wal to 00000001000000090000001E on time 2023-03-27 13:58:30.44841+08
    walminer_all     
---------------------
 ud_minerwal success
(1 row)
waltest=# select * from walminer_contents; --查看提取结果
 sqlno | xid  | topxid | sqlkind | minerd |           timestamp           |              op_text              |                    undo_text                    | complete | schema | relat
ion | start_lsn  | commit_lsn 
-------+------+--------+---------+--------+-------------------------------+-----------------------------------+-------------------------------------------------+----------+--------+------
----+------------+------------
     1 | 1528 |      0 |       3 | t      | 2023-03-27 13:56:45.925757+08 | DELETE FROM public.tb1 WHERE id=1 | INSERT INTO public.tb1(id ,name) VALUES(1 ,'a') | t        | public | tb1  
    | 9/1C0058F8 | 9/1C005A10
     2 | 1528 |      0 |       3 | t      | 2023-03-27 13:56:45.925757+08 | DELETE FROM public.tb1 WHERE id=2 | INSERT INTO public.tb1(id ,name) VALUES(2 ,'b') | t        | public | tb1  
    | 9/1C0059A0 | 9/1C005A10
(2 rows)
--执行undo_text修复数据
waltest=# INSERT INTO public.tb1(id ,name) VALUES(1 ,'a');
INSERT 0 1
waltest=# INSERT INTO public.tb1(id ,name) VALUES(2 ,'b');
INSERT 0 1
waltest=# select * from tb1;--验证结果                                                                                                    
 id | name 
----+------
  1 | a
  2 | b
(2 rows)
select walminer_stop();--结束分析并释放内存。

DDL操作恢复

注意:ddl删除的列,只能恢复列信息,不能恢复列数据。

模拟删除列操作

unvdb=# \c waltest;
You are now connected to database "waltest" as user "unvdb".
waltest=# select * from tb1;
 id | name | age 
----+------+-----
  1 | a    |   1
  2 | b    |   2
(2 rows)
waltest=# ALTER TABLE "public"."tb1" DROP COLUMN "age";
ALTER TABLE
waltest=# select * from tb1;                           
 id | name 
----+------
  1 | a
  2 | b
(2 rows)

恢复配置

waltest=# CREATE EXTENSION  walminer;
CREATE EXTENSION
waltest=# select walminer_wal_add('/data/udb/ud_wal');
  walminer_wal_add   
---------------------
 11 file add success
(1 row)
waltest=# select walminer_wal_list();
              walminer_wal_list              
---------------------------------------------
 (/data/udb/ud_wal/000000010000000900000029)
 (/data/udb/ud_wal/00000001000000090000002A)
 (/data/udb/ud_wal/00000001000000090000002B)
 (/data/udb/ud_wal/00000001000000090000002C)
 (/data/udb/ud_wal/00000001000000090000002D)
 (/data/udb/ud_wal/00000001000000090000002E)
 (/data/udb/ud_wal/00000001000000090000002F)
 (/data/udb/ud_wal/000000010000000900000030)
 (/data/udb/ud_wal/000000010000000900000031)
 (/data/udb/ud_wal/000000010000000900000032)
 (/data/udb/ud_wal/000000010000000900000033)
(11 rows)
waltest=# select wal2sql_with_ddl(); --开启ddl解析
 wal2sql_with_ddl 
------------------
 t
(1 row)
waltest=# select walminer_by_time('2023-03-27 15:00', '2023-03-27 15:15','true'); --必须使用精确解析
NOTICE:  Switch wal to 000000010000000900000029 on time 2023-03-27 15:12:59.812091+08
NOTICE:  Con not find relfilenode 65692 in dictionary, ignored related records
NOTICE:  Con not find relfilenode 65693 in dictionary, ignored related records
  walminer_by_time   
---------------------
 ud_minerwal success
(1 row)
waltest=# select * from walminer_contents;--检查解析结果
 sqlno | xid  | topxid | sqlkind | minerd |           timestamp           |                     op_text                     |             undo_text             | complete |   schema   |  
 relation   | start_lsn  | commit_lsn 
-------+------+--------+---------+--------+-------------------------------+-------------------------------------------------+-----------------------------------+----------+------------+--
------------+------------+------------
     1 | 1578 |      0 |       4 | t      | 2023-03-27 15:01:03.11249+08  | TRUNCATE TABLE public.tb1                       |                                   | t        | pg_catalog | p
g_class     | 9/291A7EB8 | 9/291AA7E0
     1 | 1583 |      0 |       1 | t      | 2023-03-27 15:09:24.706606+08 | INSERT INTO public.tb1(id ,name) VALUES(1 ,'a') | DELETE FROM public.tb1 WHERE id=1 | t        | public     | t
b1          | 9/291DA7F8 | 9/291DA910
     1 | 1584 |      0 |       1 | t      | 2023-03-27 15:09:27.201411+08 | INSERT INTO public.tb1(id ,name) VALUES(2 ,'b') | DELETE FROM public.tb1 WHERE id=2 | t        | public     | t
b1          | 9/291DA948 | 9/291DA9F8
     1 | 1585 |      0 |       2 | t      | 2023-03-27 15:09:31.79967+08  | UPDATE public.tb1 SET  WHERE id=2               | UPDATE public.tb1 SET  WHERE id=2 | t        | public     | t
b1          | 9/291DA9F8 | 9/291DAA78
     1 | 1586 |      0 |       4 | t      | 2023-03-27 15:10:15.078618+08 | ALTER TABLE public.tb1 DROP COLUMN age          |                                   | t        | pg_catalog | p
g_attribute | 9/291DCB98 | 9/291E0BF0
(5 rows)

修复数据

op_text列显示执行过 ALTER TABLE public.tb1 DROP COLUMN age,使用alter add 把它添加进来即可,但此列的数据无法恢复。

异常处理

cstore-列式存储

简介

cstore插件是列式存储扩展,列式存储仅从磁盘读取相关的数据,可以更快速的分析统计数据。 数据大小比普通表减少4-10倍的磁盘空间,能够更快速的将数据加载到内存处理。

优点:

  • 压缩:磁盘占用空间减少4-10倍

  • 列投影:只读取和查询相关的数据,提高IO性能

  • 跳过索引:存储行组的最小最大统计信息,并使用它们跳过不相关的行

使用限制:

  • 不支持 DELETE 删除数据

  • 不支持 UPDATE 更新表

  • 不支持 INSERT INTO 单行数据,需多行批量插入。因为插入操作时会flush,如果插入一行也会占用一个条带的磁盘空间,会导致性能下降的严重的磁盘浪费。

使用

CREATE EXTENSION cstore_fdw; -- 创建扩展
CREATE SERVER cstore_server FOREIGN DATA WRAPPER cstore_fdw; -- 创建server指定外部数据
CREATE FOREIGN TABLE t1 -- 创建 FOREIGN (
    customer_id TEXT,
    review_date DATE,
    review_rating INTEGER,
    review_votes INTEGER,
    review_helpful_votes INTEGER,
    product_id CHAR(10),
    product_title TEXT,
    product_sales_rank BIGINT,
    product_group TEXT,
    product_category TEXT,
    product_subcategory TEXT,
    similar_product_ids CHAR(10)[]
) SERVER cstore_server -- 指定server
OPTIONS(filename '/data/udb/cstore/cstb1.cstore',compression 'pglz'); -- 其它选项
\COPY t1 FROM '/data/udb/file/data.csv' WITH CSV; --导入数据

OPTIONS 参数

  • filename(可选):表数据存储位置目录。 默认值为 $UDBDATA/cstore_fdw

  • compression压缩(可选):用于压缩价值流的压缩。 默认值为 none,不压缩;可选值 pglz

  • stripe_row_count(可选):每个条带的行数。 默认值为 150000。减少它会减少用于加载数据和查询的内存量,但也会降低性能。

  • block_row_count(可选):每个列块的行数。 默认值为 10000。cstore 以块粒度压缩、创建,跳过索引并从磁盘读取。增加此值有助于压缩并减少从磁盘读取的结果。但是,较高的值也会降低跳过不相关行块的可能性。

空间对比

相同数据量下,cstore 列式存储占用 磁盘空间 比普通表 减少4倍左右。

普通表大小

du -sh  /data/udb/udb22.4.21-1/data/udb/base/66354/
521M    /data/udb/udb22.4.21-1/data/udb/base/66354/
test=# SELECT pg_size_pretty(pg_table_size('t1'));
 pg_size_pretty 
----------------
 412 MB
(1 row)

列式存储表大小

du -sh  /data/udb/udb22.4.21-1/data/udb/cstore
100M    /data/udb/udb22.4.21-1/data/udb/cstore
cstore=# SELECT pg_size_pretty(cstore_table_size('t1'));
 pg_size_pretty 
----------------
 100 MB
(1 row)

查询对比

相同数据量下,cstore 列式存储查询 比普通表 性能提升 15倍 左右。

test=# select count(*) from t1 where review_date ='2023-04-06';
 count 
-------
  4024
(1 row)
Time: 59.165 ms
test=# \c cstore
You are now connected to database "cstore" as user "unvdb".
cstore=# select count(*) from t1 where review_date ='2023-04-06';
 count 
-------
  4024
(1 row)
Time: 4.758 ms