从PG14开始,PostgreSQL已经支持LZ4 压缩功能。而PG14之前的版本,其TOAST仅支持一个压缩算法PGLZ(PG内置算法)。众所周知,LZ4比PGLZ的压缩速度快很多。在不要求很高压缩比的场景下,LZ4更适合用于速度敏感型的数据压缩功能。
很多实际场景中,我们为了便于管理,经常将一些大文本和二进制文件存储到数据库里。这时,在不太影响读取速度的时候,存储空间当然是越小越好。很可惜,官方PostgreSQL13和之前的版本不支持LZ4压缩。
那么有没有可能,通过扩展PG的方式来使用lz4压缩功能呢?
答案是可以的。
最近,我就通过开发扩展PG函数的方式实现了lz4压缩和解压缩功能。可以方便的对二进制数据和文本数据进行压缩和解压缩。
我开发了4个自定义函数来进行数据压缩和解压:
# \df
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------------+------------------+---------------------+--------
public | lz4 | bytea | bytea bytea | normal
public | lz4_utf8 | bytea | msg text | normal
public | un_lz4 | bytea | bytea bytea | normal
public | un_lz4_utf8 | text | bytea bytea | normal
(4 rows)
简单测试一下:
使用lz4压缩
# select lz4('aaaaaaaaaaaaaaaaaaaaaaaaaaaaa,你好,我好,大家好!'::bytea);
lz4
--------------------------------------------------------------------------------
\x3c0000001f61010009d22ce4bda0e5a5bdefbc8ce688910900c0e5a4a7e5aeb6e5a5bdefbc81
(1 row)
使用un_lz4解压缩,然后convert_from转换成文本
# select convert_from(un_lz4(lz4('aaaaaaaaaaaaaaaaaaaaaaaaaaaaa,你好,我好,大家好!'::bytea)), 'utf8');
convert_from
----------------------------------------------------
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa,你好,我好,大家好!
(1 row)
lz4_utf8函数和un_lz4_utf8函数用于在utf8编码环境里,对文本进行lz4压缩和解压缩还原成文本。
以中文绕口令为例。2倍绕口令字节长度:
# select length('八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑;八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑.'::bytea);
length
--------
176
(1 row)
lz4压缩后的字节长度:
# select length(lz4_utf8('八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑;八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑.'));
length
--------
90
(1 row)
压缩并解压缩:
# select un_lz4_utf8(lz4_utf8('八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑;八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑.'));
un_lz4_utf8
--------------------------------------------------------------------------------------------------------------------------
八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑;八百标兵奔北坡 炮兵并排北边跑 炮兵怕把标兵碰 标兵怕碰炮兵跑.
(1 row)
在数据表格中使用lz4压缩函数。
CREATE TABLE tb_article (
id serial4 NOT NULL,
title text NOT NULL,
content bytea NOT NULL,
create_time timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT tb_disco_exec_pk PRIMARY KEY (id)
);
插入数据,同时用函数进行内容压缩
# insert into tb_article(title, content) values('哈利波特', lz4_utf8('哈利波特哈利波特哈利波特哈利波特哈利波特哈利波特'));
INSERT 0 1
用 * 查询:
# select * from tb_article;
id | title | content | create_time
----+-----------+----------------------------------------------------------+-------------------------------
1 | 哈利波特 | \x48000000cfe59388e588a9e6b3a2e789b90c002360e6b3a2e789b9 | 2022-03-21 15:25:45.851421+00
2 | 哈利波特2 | \x48000000cfe59388e588a9e6b3a2e789b90c002360e6b3a2e789b9 | 2022-03-21 15:25:53.897295+00
(2 rows)
查询并用函数对指定字段进行解压:
# select id, title, un_lz4_utf8(content) as content, create_time from tb_article;
id | title | content | create_time
----+-----------+--------------------------------------------------+-------------------------------
1 | 哈利波特 | 哈利波特哈利波特哈利波特哈利波特哈利波特哈利波特 | 2022-03-21 15:25:45.851421+00
2 | 哈利波特2 | 哈利波特哈利波特哈利波特哈利波特哈利波特哈利波特 | 2022-03-21 15:25:53.897295+00
(2 rows)
如何安装扩展函数?
目前扩展函数仅支持Linux操作系统。压缩包里有四个目录:
每个目录对应PG的一个大版本。编译时是使用的这些具体版本。但应该可以适用于对应的大版本。
每个目录下有三个文件:
安装时,将pg_lz4.so拷贝到xxx/postgresql/lib/目录下,pg_lz4.control和pg_lz4--0.1.0.sql文件拷贝到xxx/postgresql/share/extension/目录下。
然后,登录到数据库中创建扩展:
CREATE EXTENSION pg_lz4;
也可以指定创建扩展到具体某个schema下
CREATE EXTENSION IF NOT EXISTS pg_lz4 WITH SCHEMA xxxxxx
然后就可以使用了。
下载链接在这里。
大家使用过程中有任何问题可以找我处理。
当然,如果有对PG扩展开发有需求的,也可以找我聊聊。
我后续会针对PG扩展出更多的文章和函数库。