PolarDB提速200倍,10行PL/Python实现DuckDB代理
参考文档点击文末 阅读原文 打开; 推荐 《最好的PostgreSQL学习镜像 》 ;
性能爽翻了, 在PolarDB plpython中使用DuckDB
为什么要在PolarDB PostgreSQL中使用duckdb? 还不是因为PG 在复杂分析查询方面优化器做得太慢了, 弱爆了. 已经写过很多对比文章, 不再细说.
《PG被DuckDB碾压,该反省哪些方面? DuckDB v0.10.3 在Macmini 2023款上的tpch性能表现如何? PostgreSQL使用duckdb_fdw 的tpch加速性能表现如何?》为什么现在要来说这个? PostgreSQL不是早就支持plpython存储过程了么, 还不是因为要打比赛, 给直接来个上次问AI得到的核爆级弯道超车思路.
《qwen2.5-coder真牛~~用它打PolarDB国赛劲劲的》 AI给了思路和DEMO代码, 我就试试到底行不行, 有多少提升? 结果嘛不用说, 不同的SQL提升各不相同, 拿Q17来说, 可能提升上百倍性能. 分享DEMO之前, 先发个本周五北京的数据库线下活动, 欢迎 扫码 报名 参加.
DEMO
我假设你已经按下面的指南部署好了环境, 在容器内操作.
首先需要把一些依赖包放到你的代码包里, 后面你需要把安装过程写入build.sh脚本内
https://pypi.org/project/setuptools/
cd /tmp
curl https://files.pythonhosted.org/packages/11/0a/7f13ef5cd932a107cd4c0f3ebc9d831d9b78e1a0e8c98a098ca17b1d7d97/setuptools-41.6.0.zip -o ./setuptools-41.6.0.zip
sudo apt-get install -y unzip
unzip setuptools-41.6.0.zip
tar -zcvf setuptools-41.6.0.tar.gz setuptools-41.6.0
cd setuptools-41.6.0
sudo python3 setup.py install
https://pypi.org/project/pip/
cd /tmp
curl https://files.pythonhosted.org/packages/ce/ea/9b445176a65ae4ba22dce1d93e4b5fe182f953df71a145f557cffaffc1bf/pip-19.3.1.tar.gz -o ./pip-19.3.1.tar.gz
tar -zxvf pip-19.3.1.tar.gz
cd pip-19.3.1
sudo python3 setup.py install
https://pypi.org/project/duckdb/
duckdb包请根据你的开发环境选择, 我用的是m2 芯片mac, 下载arm版本. 测试机可能是x86, 得选另一个.
# curl https://files.pythonhosted.org/packages/e9/35/3fa9b1bbbf4e91e680abfb4c2a2560802698b43c6720096aae0c2d6ef7eb/duckdb-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -o ./duckdb-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
cd /tmp
curl https://files.pythonhosted.org/packages/37/7c/cf0a0dd56e84570b94927a522698c49fd173b38074ab41a9eb044127b3b3/duckdb-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -o ./duckdb-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
sudo pip3.8 install ./duckdb-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
现在你可以创建一个plpython3u函数, 在里面可以import duckdb了.
postgres=# create extension plpython3u ;
-- 创建或替换函数
CREATE OR REPLACE FUNCTION duckdb_proxy(query_text TEXT) RETURNS SETOF RECORD AS $$
import duckdb
# 连接到 DuckDB 数据库
con = duckdb.connect(database=':memory:')
# 执行查询
result = con.execute(query_text)
# 获取结果并返回
for row in result.fetchall():
yield tuple(row)
# 关闭连接
con.close()
$$ LANGUAGE plpython3u;
下面在python shell中把data目录中的csv文件都转换成parquet文件, 可以大幅提升性能. 类型请参考dss.sql文件进行定义.
$ python3.8
import duckdb
duckdb.sql("""copy (select * from
read_csv('/data/customer.tbl',
delim = '|',
header = false,
columns = {
' ': 'INTEGER',
'c_name': 'VARCHAR(25)',
'c_address': 'VARCHAR(40)',
'c_nationkey': 'INTEGER',
'c_phone': 'CHAR(15)',
'c_acctbal': 'DECIMAL(15,2)',
'c_mktsegment': 'CHAR(10)',
'c_comment': 'VARCHAR(117)'
}
)
) to '/data/customer.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/nation.tbl',
delim = '|',
header = false,
columns = {
'n_nationkey': 'INTEGER',
'n_name': 'CHAR(25)',
'n_regionkey': 'INTEGER',
'n_comment': 'VARCHAR(152)'
}
)
) to '/data/nation.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/lineitem.tbl',
delim = '|',
header = false,
columns = {
'l_orderkey': 'INTEGER',
'l_partkey': 'INTEGER',
'l_suppkey': 'INTEGER',
'l_linenumber': 'INTEGER',
'l_quantity': 'DECIMAL(15,2)',
'l_extendedprice': 'DECIMAL(15,2)',
'l_discount': 'DECIMAL(15,2)',
'l_tax': 'DECIMAL(15,2)',
'l_returnflag': 'CHAR(1)',
'l_linestatus': 'CHAR(1)',
'l_shipdate': 'DATE',
'l_commitdate': 'DATE',
'l_receiptdate': 'DATE',
'l_shipinstruct': 'CHAR(25)',
'l_shipmode': 'CHAR(10)',
'l_comment': 'VARCHAR(44)'
}
)
) to '/data/lineitem.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/orders.tbl',
delim = '|',
header = false,
columns = {
'o_orderkey': 'INTEGER',
'o_custkey': 'INTEGER',
'o_orderstatus': 'CHAR(1)',
'o_totalprice': 'DECIMAL(15,2)',
'o_orderdate': 'DATE',
'o_orderpriority': 'CHAR(15)',
'o_clerk': 'CHAR(15)',
'o_shippriority': 'INTEGER',
'o_comment': 'VARCHAR(79)'
}
)
) to '/data/orders.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/part.tbl',
delim = '|',
header = false,
columns = {
'p_partkey': 'INTEGER',
'p_name': 'VARCHAR(55)',
'p_mfgr': 'CHAR(25)',
'p_brand': 'CHAR(10)',
'p_type': 'VARCHAR(25)',
'p_size': 'INTEGER',
'p_container': 'CHAR(10)',
'p_retailprice': 'DECIMAL(15,2)',
'p_comment': 'VARCHAR(23)'
}
)
) to '/data/part.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/partsupp.tbl',
delim = '|',
header = false,
columns = {
'ps_partkey': 'INTEGER',
'ps_suppkey': 'INTEGER',
'ps_availqty': 'INTEGER',
'ps_supplycost': 'DECIMAL(15,2)',
'ps_comment': 'VARCHAR(199)'
}
)
) to '/data/partsupp.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/region.tbl',
delim = '|',
header = false,
columns = {
'r_regionkey': 'INTEGER',
'r_name': 'CHAR(25)',
'r_comment': 'VARCHAR(152)'
}
)
) to '/data/region.parquet'""")
duckdb.sql("""copy (select * from
read_csv('/data/supplier.tbl',
delim = '|',
header = false,
columns = {
's_suppkey': 'INTEGER',
's_name': 'CHAR(55)',
's_address': 'VARCHAR(40)',
's_nationkey': 'INTEGER',
's_phone': 'CHAR(15)',
's_acctbal': 'DECIMAL(15,2)',
's_comment': 'VARCHAR(101)'
}
)
) to '/data/supplier.parquet'""")
抽查一下转换后的parquet文件数据是否正常
>>> duckdb.sql("select * from read_parquet('/data/customer.parquet') limit 10")
┌───────────┬────────────────────┬──────────────────────┬─────────────┬─────────────────┬───────────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────┐
│ c_custkey │ c_name │ c_address │ c_nationkey │ c_phone │ c_acctbal │ c_mktsegment │ c_comment │
│ int32 │ varchar │ varchar │ int32 │ varchar │ decimal(15,2) │ varchar │ varchar │
├───────────┼────────────────────┼──────────────────────┼─────────────┼─────────────────┼───────────────┼──────────────┼─────────────────────────────────────────────────────────────────────────────────┤
│ 1 │ Customer#000000001 │ IVhzIApeRb ot,c,E │ 15 │ 25-989-741-2988 │ 711.56 │ BUILDING │ to the even, regular platelets. regular, ironic epitaphs nag e │
│ 2 │ Customer#000000002 │ XSTf4,NCwDVaWNe6tE… │ 13 │ 23-768-687-3665 │ 121.65 │ AUTOMOBILE │ l accounts. blithely ironic theodolites integrate boldly: caref │
│ 3 │ Customer#000000003 │ MG9kdTD2WBHm │ 1 │ 11-719-748-3364 │ 7498.12 │ AUTOMOBILE │ deposits eat slyly ironic, even instructions. express foxes detect slyly. bl… │
│ 4 │ Customer#000000004 │ XxVSJsLAGtn │ 4 │ 14-128-190-5944 │ 2866.83 │ MACHINERY │ requests. final, regular ideas sleep final accou │
│ 5 │ Customer#000000005 │ KvpyuHCplrB84WgAiG… │ 3 │ 13-750-942-6364 │ 794.47 │ HOUSEHOLD │ n accounts will have to unwind. foxes cajole accor │
│ 6 │ Customer#000000006 │ sKZz0CsnMD7mp4Xd0Y… │ 20 │ 30-114-968-4951 │ 7638.57 │ AUTOMOBILE │ tions. even deposits boost according to the slyly bold packages. final accoun… │
│ 7 │ Customer#000000007 │ TcGe5gaZNgVePxU5kR… │ 18 │ 28-190-982-9759 │ 9561.95 │ AUTOMOBILE │ ainst the ironic, express theodolites. express, even pinto beans among the exp │
│ 8 │ Customer#000000008 │ I0B10bB0AymmC, 0Pr… │ 17 │ 27-147-574-9335 │ 6819.74 │ BUILDING │ among the slyly regular theodolites kindle blithely courts. carefully even th… │
│ 9 │ Customer#000000009 │ xKiAFTjUsCuxfeleNq… │ 8 │ 18-338-906-3675 │ 8324.07 │ FURNITURE │ r theodolites according to the requests wake thinly excuses: pending requests… │
│ 10 │ Customer#000000010 │ 6LrEaV6KR6PLVcgl2A… │ 5 │ 15-741-346-9870 │ 2753.54 │ HOUSEHOLD │ es regular deposits haggle. fur │
├───────────┴────────────────────┴──────────────────────┴─────────────┴─────────────────┴───────────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────┤
│ 10 rows 8 columns │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
>>> duckdb.sql("select count(*) from read_parquet('/data/customer.parquet')")
┌──────────────┐
│ count_star() │
│ int64 │
├──────────────┤
│ 15000 │
└──────────────┘
为了在PostgreSQL中调用方便, 可以把这8个parquet文件创建为对应的视图, 并把视图保留到duckdb数据文件
/data/duckdb_proxy.db
中
con = duckdb.connect(database='/data/duckdb_proxy.db')
con.execute("""
CREATE view customer as select * from '/data/customer.parquet';
CREATE view lineitem as select * from '/data/lineitem.parquet';
CREATE view nation as select * from '/data/nation.parquet';
CREATE view orders as select * from '/data/orders.parquet';
CREATE view part as select * from '/data/part.parquet';
CREATE view partsupp as select * from '/data/partsupp.parquet';
CREATE view region as select * from '/data/region.parquet';
CREATE view supplier as select * from '/data/supplier.parquet';
""")
con.checkpoint()
现在可以直接查询这些视图, 相当于查的后面的parquet文件.
con.sql('select count(*) from customer')
con.sql('select count(*) from lineitem')
con.sql('select count(*) from nation')
con.sql('select count(*) from orders')
con.sql('select count(*) from part')
con.sql('select count(*) from partsupp')
con.sql('select count(*) from region')
con.sql('select count(*) from supplier')
找个tpch query来试一下, 性能杠杠的
con.sql("""
select
l_returnflag,
l_linestatus,
sum(l_quantity) as sum_qty,
sum(l_extendedprice) as sum_base_price,
sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
avg(l_quantity) as avg_qty,
avg(l_extendedprice) as avg_price,
avg(l_discount) as avg_disc,
count(*) as count_order
from
lineitem
where
l_shipdate <= date '1998-12-01' - interval '100' day
group by
l_returnflag,
l_linestatus
order by
l_returnflag,
l_linestatus;""")
┌──────────────┬──────────────┬───────────────┬────────────────┬─────────────────┬────────────────────┬────────────────────┬────────────────────┬─────────────────────┬─────────────┐
│ l_returnflag │ l_linestatus │ sum_qty │ sum_base_price │ sum_disc_price │ sum_charge │ avg_qty │ avg_price │ avg_disc │ count_order │
│ varchar │ varchar │ decimal(38,2) │ decimal(38,2) │ decimal(38,4) │ decimal(38,6) │ double │ double │ double │ int64 │
├──────────────┼──────────────┼───────────────┼────────────────┼─────────────────┼────────────────────┼────────────────────┼────────────────────┼─────────────────────┼─────────────┤
│ A │ F │ 3774200.00 │ 5320753880.69 │ 5054096266.6828 │ 5256751331.449234 │ 25.537587116854997 │ 36002.12382901414 │ 0.05014459706340077 │ 147790 │
│ N │ F │ 95257.00 │ 133737795.84 │ 127132372.6512 │ 132286291.229445 │ 25.30066401062417 │ 35521.32691633466 │ 0.04939442231075697 │ 3765 │
│ N │ O │ 7407220.00 │ 10438352681.32 │ 9916049778.5866 │ 10312596801.845673 │ 25.545573370211855 │ 35999.16085721873 │ 0.05009328840775139 │ 289961 │
│ R │ F │ 3785523.00 │ 5337950526.47 │ 5071818532.9420 │ 5274405503.049367 │ 25.5259438574251 │ 35994.029214030925 │ 0.04998927856184382 │ 148301 │
└──────────────┴──────────────┴───────────────┴────────────────┴─────────────────┴────────────────────┴────────────────────┴────────────────────┴─────────────────────┴─────────────┘
关闭连接
con.close()
更多的duckdb python api请查看手册
-
https://duckdb.org/docs/api/python/reference/
接下来可以用PostgreSQL函数, 把SQL请求转发给duckdb执行了, 还是用到这个函数, 返回setof record, 这样比较通用, 一个函数就可以转发所有tpch query.
-- 创建或替换函数
CREATE OR REPLACE FUNCTION duckdb_proxy(query_text TEXT) RETURNS SETOF RECORD AS $$
import duckdb
# 连接到 DuckDB 数据库
con = duckdb.connect(database='/data/duckdb_proxy.db')
# 获取结果并返回
for row in tuple(con.execute(query_text).fetchall()):
yield row
# 关闭连接
con.close()
$$ LANGUAGE plpython3u;
用下面的SQL试试这个函数好不好用?
select * from duckdb_proxy($$select count(*) from customer$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from lineitem$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from nation$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from orders$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from part$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from partsupp$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from region$$) as t(c int8);
select * from duckdb_proxy($$select count(*) from supplier$$) as t(c int8);
可以了
postgres=# select * from duckdb_proxy($$select count(*) from customer$$) as t(c int8);
c
-------
15000
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from lineitem$$) as t(c int8);
c
--------
600572
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from nation$$) as t(c int8);
c
----
25
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from orders$$) as t(c int8);
c
--------
150000
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from part$$) as t(c int8);
c
-------
20000
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from partsupp$$) as t(c int8);
c
-------
80000
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from region$$) as t(c int8);
c
---
5
(1 row)
postgres=# select * from duckdb_proxy($$select count(*) from supplier$$) as t(c int8);
c
------
1000
(1 row)
你需要测试tpch SQL的话, 可以使用qgen生成, 方法如下:
cd /tmp/PolarDB-for-PostgreSQL/tpch-dbgen
cp qgen dists.dss queries/
cd queries/
./qgen -h
TPC-H Parameter Substitution (v. 2.14.0 build 0)
Copyright Transaction Processing Performance Council 1994 - 2010
USAGE: ./qgen <options> [ queries ]
Options:
-a -- use ANSI semantics.
-b <str> -- load distributions from <str>
-c -- retain comments found in template.
-d -- use default substitution values.
-h -- print this usage summary.
-i <str> -- use the contents of file <str> to begin a query.
-l <str> -- log parameters to <str>.
-n <str> -- connect to database <str>.
-N -- use default rowcounts and ignore :n directive.
-o <str> -- set the output file base path to <str>.
-p <n> -- use the query permutation for stream <n>
-r <n> -- seed the random number generator with <n>
-s <n> -- base substitutions on an SF of <n>
-v -- verbose.
-t <str> -- use the contents of file <str> to complete a query
-x -- enable SET EXPLAIN in each query.
# 例如 生成q1,q17,q18,q20,q21 . -s 0.1 不需要也可以. 因为我前面使用了scale 0.1生成数据.
./qgen -s 0.1 -d 1
./qgen -s 0.1 -d 17
./qgen -s 0.1 -d 18
./qgen -s 0.1 -d 20
./qgen -s 0.1 -d 21
接下来你可能要问, 每次都要定义result table的结构, 字段名可以随便取, 但是我怎么知道返回的每个字段是什么类型呢? 两种方法:
1, 使用duckdb跑一遍, 看看duckdb返回了什么类型,
con.sql("""
select
l_returnflag,
l_linestatus,
sum(l_quantity) as sum_qty,
sum(l_extendedprice) as sum_base_price,
sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
avg(l_quantity) as avg_qty,
avg(l_extendedprice) as avg_price,
avg(l_discount) as avg_disc,
count(*) as count_order
from
lineitem
where
l_shipdate <= ( date '1998-12-01' - interval '100' day )
group by
l_returnflag,
l_linestatus
order by
l_returnflag,
l_linestatus
""")
例如第一条前面已经跑了, 从结果中照抄, 但是一定要注意, 不要随便用float8, 除非在PG中本来也是返回float8, 否则建议改成decimal, 保证精度
select * from duckdb_proxy($$
select
l_returnflag,
l_linestatus,
sum(l_quantity) as sum_qty,
sum(l_extendedprice) as sum_base_price,
sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
avg(l_quantity) as avg_qty,
avg(l_extendedprice) as avg_price,
avg(l_discount) as avg_disc,
count(*) as count_order
from
lineitem
where
l_shipdate <= ( date '1998-12-01' - interval '100' day )
group by
l_returnflag,
l_linestatus
order by
l_returnflag,
l_linestatus
$$) as t (
l_returnflag varchar,
l_linestatus varchar,
sum_qty decimal(38,2),
sum_base_price decimal(38,2),
sum_disc_price decimal(38,4),
sum_charge decimal(38,6),
avg_qty decimal, -- 不要使用float8, 结果可能不正确
avg_price decimal, -- 不要使用float8, 结果可能不正确
avg_disc decimal, -- 不要使用float8, 结果可能不正确
count_order int8
);
2, 方法2是使用pg_typeof函数, 可以知道返回的是什么类型, 但是要使用PG本地表跑一遍, 例如
postgres=# select
pg_typeof(sum(l_extendedprice) / 7.0) as avg_yearly
from
lineitem,
part
where
p_partkey = l_partkey
and p_brand = 'Brand#13'
and p_container = 'JUMBO PKG'
and l_quantity < (
select
0.2 * avg(l_quantity)
from
lineitem
where
l_partkey = p_partkey
);
avg_yearly
------------
numeric
(1 row)
tpch 的q17在PG里面跑是比较慢的对吧?
select
sum(l_extendedprice) / 7.0 as avg_yearly
from
lineitem,
part
where
p_partkey = l_partkey
and p_brand = 'Brand#13'
and p_container = 'JUMBO PKG'
and l_quantity < (
select
0.2 * avg(l_quantity)
from
lineitem
where
l_partkey = p_partkey
);
avg_yearly
--------------------
28135.621428571429 -- 小数点12位
(1 row)
Time: 11898.286 ms (00:11.898)
在duckdb中小意思
con.sql("""
select
sum(l_extendedprice) / 7.0 as avg_yearly
from
lineitem,
part
where
p_partkey = l_partkey
and p_brand = 'Brand#13'
and p_container = 'JUMBO PKG'
and l_quantity < (
select
0.2 * avg(l_quantity)
from
lineitem
where
l_partkey = p_partkey
);
""")
┌───────────────────┐
│ avg_yearly │
│ double │
├───────────────────┤
│ 28135.62142857143 │ -- 小数点11位
└───────────────────┘
注意上面这个精度问题, 实际是是PG返回的numeric, 而duckdb返回的是float8.
PG有个相关参数extra_float_digits, 可以控制浮点精度, 但是duckdb貌似没有, 可能需要改duckdb代码? -- 这里就不细说了.
q17 改写成代理SQL如下, 性能直接提升200多倍.
select * from duckdb_proxy($$
select
sum(l_extendedprice) / 7.0 as avg_yearly
from
lineitem,
part
where
p_partkey = l_partkey
and p_brand = 'Brand#13'
and p_container = 'JUMBO PKG'
and l_quantity < (
select
0.2 * avg(l_quantity)
from
lineitem
where
l_partkey = p_partkey
)
$$) as t (
avg_yearly decimal -- 不要使用float8, 结果可能不正确
);
avg_yearly
-------------------
28135.62142857143 -- 因为duckdb使用了float8, 所以结果并没有fix
(1 row)
Time: 56.766 ms
小结
上面这个例子使用plpython3u 写了一个通用函数, 使用这个函数可以将PostgreSQL SQL请求转发给duckdb执行, 加速效果非常明显.
例子中还将csv文件转换为parquet文件, duckdb查询的实际上是parquet文件. 实际上还可以使用duckdb postgres_scanner/csv_reader, 不需要将数据导出到parquet, 对复杂查询性能也有提升, 本文不再赘述. 有兴趣的同学可以试试.
为了方案的完整性和通用性, 建议还要考虑
-
如何“自动的/方便用户有选择的”将数据同步转换为parquet? -
结合query rewrite, 让优化器来决定什么时候使用duckdb? 什么时候使用PostgreSQL/PolarDB heap table? 例如基于代价优化选择?
duckdb本身的优化本文没有提出, 有兴趣的同学也可以关注 https://github.com/digoal/blog 中的相关文章。
最后就是对duckdb为什么更快的原理要能说得明白,能嫁接到polardb吗?怎么和其他队在成绩上打开差异?
参考
https://duckdb.org/docs/api/python/reference/
《PG被DuckDB碾压,该反省哪些方面? DuckDB v0.10.3 在Macmini 2023款上的tpch性能表现如何? PostgreSQL使用duckdb_fdw 的tpch加速性能表现如何?》 《qwen2.5-coder真牛~~用它打PolarDB国赛劲劲的》 《PolarDB数据库创新设计国赛 - 初赛提交作品指南》这篇文章也给了一些思路.
今日荐书彩蛋: 国产数据库周边生态
当然一款数据库要流行起来, 除了自己要强大, 还离不开生态. 用好周边生态工具, 管理水平战胜90%老司机!!! 下面简单介绍一下国产数据库周边生态.
1、管控软件
鸣嵩 (前阿里云数据库总经理 / 研究员)等大佬们创业创办的云猿生, 核心产品是 KubeBlocks . 他们的理念是让管理数据库和搭积木一样简单, 如果你要管理很多套并且种类(OLTP\OLAP\NoSQL\KV\TS\MQ等)很多的数据库产品, 推荐首选.-
https://github.com/apecloud/kubeblocks
-
https://www.csudata.com/
-
https://pigsty.cc/zh/
2、审计监控诊断优化
翟总 (曾经是我背后的男人)到海信聚好看后研发的 DBdoctor , 采用ebpf技术, 在对数据库几乎没有影响的情况下实时监控数据库和服务器的各项指标, 发现和诊断问题根因非常方便.-
https://www.dbdoctor.cn/
-
https://bytebase.cc/docs/introduction/what-is-bytebase/
-
https://www.modb.pro/db/567140
3、国产数据库IDE
IDE是开发者的必备工具,例如社区有pgAdmin, 国产IDE则可以看看老程序猿 达刚 老师的 DeskUI :-
https://www.deskui.com
4、数据同步&迁移&备份恢复
NineData , 老领导出去创业做的产品, 产品涵盖了数据同步、迁移、备份、比对、devops、chatDBA等.-
https://www.ninedata.cloud/home
-
https://www.dsgdata.com/
公开课
如果你对PolarDB学习感兴趣可以阅读这个公开课系列:
- 国庆拿下这个国产信创数据库
-
国产数据库PolarDB公开课-1 架构解读
-
国产数据库PolarDB公开课-2 快速体验
-
国产数据库PolarDB公开课-3 安装部署
- 国产数据库PolarDB公开课-4 日常运维
-
国产数据库PolarDB公开课-5 特性解读与体验
-
国产数据库PolarDB公开课-6 使用PostgreSQL开源工具/插件
-
国产数据库PolarDB公开课-7 应用场景与实践
-
国产数据库PolarDB公开课-7.1 TPCH测试
-
国产数据库PolarDB公开课-8 生态
-
国产数据库PolarDB公开课-9 参与开源社区
除了PolarDB还非常值得关注的几款PG栈国产数据库:
- HaloDB (基于PG兼容PostgreSQL、Oracle、MySQL. http://www.halodbtech.com/ )、
- IvorySQL (基于开源PG兼容PG、Oracle. https://www.ivorysql.org/zh-cn/ )、
- ProtonBase (云原生分布式数仓. https://protonbase.com/ )、
- 成都 文武 数据库(https://ww-it.cn)
参考文档点击 阅读原文 获得
感谢关注我的github (https://github.com/digoal/blog) 及视频号: