作者:约宾·奥古斯丁(Jobin Augustine)
即时编译(JIT:Just-in-time) SQL语句是PostgreSQL 11中突出的特性之一。在社区中有很高的热度,因为许多人声称性能提高了30%。并不是所有的查询和工作负载都能从JIT编译中获益。因此,您可能需要针对这个新特性测试您的工作负载。
但是,了解它的作用以及我们可以期望在何处获得性能提升是很重要的。安装带有新的JIT编译功能的PostgreSQL 11需要一些额外的步骤和包。花时间和精力弄清楚如何做到这一点不应该成为回避尝试这些前沿特性和针对JIT特性测试工作负载的理由。这篇博文是为那些想尝试的人准备的。
JIT是什么,它在PostgreSQL中做什么
任何DBMS软件中正常的SQL执行类似于解释语言对源代码的处理。SQL语句不会生成机器码。但是我们都知道,JIT编译和执行它生成的机器码可以带来多么显著的性能提升。我们看到了Google V8引擎对JavaScript语言的神奇作用。用SQL语句做类似的事情已经有一段时间了。但这是一项具有挑战性的任务。
这是一个挑战,因为我们在PostgreSQL服务器中没有准备好的源代码(SQL语句)。需要进行JIT的源代码来自客户端连接,并且可能存在具有不同数量参数的表达式/函数,并且可能处理具有不同数量和类型列的表。
一般来说,计算机程序在运行时不会在这个级别被修改,因此分支预测是可能的。来自客户端连接并不时访问数据库的SQL语句具有不可预测性和动态性,因此没有预先进行预测或编译的余地。这意味着JIT编译器应该在数据库每次获得SQL语句时启动。由于这个原因,PostgreSQL需要像LLVM这样的编译器基础设施的帮助。尽管还有其他一些选择,但该特性的主要开发人员(Andres Freund)有充分的理由说明LLVM是正确的选择。
在PostgreSQL 11中,JIT目前的功能是:
1.加速表达式求值:WHERE子句、目标列表、聚合和投影中的表达式
2.元组变形:将磁盘映像转换为相应的内存表示。
3.内联:小型自定义函数、操作符和用户定义数据类型的主体被内联到使用它们的表达式中
4.您可以使用LLVM提供的编译器优化来准备优化的机器码。
在这个博客中,我们将看到如何安装后GUGLSQL。就像常规的后处理SQL安装一样,我们有两种选择:
1.从PGDG存储库中的包中获取PostgreSQL
2.从源代码构建PostgreSQL
安装方式
在这篇博客中,我们将看到如何在安装PostgreSQL中使用JIT。就像常规的PostgreSQL安装一样,我们有两个选择:
1.从PGDG存储库中的包中获取PostgreSQL
2.从源代码构建PostgreSQL
方式1:从PGDG存储库安装
从源代码编译需要我们安装所有的编译器和工具。出于各种原因,我们可能希望避免这种情况。从PGDG存储库安装包非常简单。在生产系统或容器上,您可能只希望安装所需的最低限度的软件包。不真正使用的附加包总是一个安全问题。像Ubuntu这样的发行版在其默认的库中提供了最新版本的库和工具集。然而,像CentOS / RHEL这样的发行版是相当保守的——它们优先考虑的是稳定性和经过验证的服务器,而不是前沿的功能。所以在这部分的帖子主要与CentOS7/RHEL 7有关。
以下是在CentOS7上安装具有JIT特性的PostgreSQL的步骤:
步骤1:安装PGDG repo和安装PostgreSQL服务器包。
这通常是最小的安装,如果我们不需要JIT特性。
sudo yum install https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-centos11-11-2.noarch.rpm
sudo yum install postgresql11-server
在这个阶段,如果我们不需要JIT,我们可以初始化数据目录并启动服务:
sudo /usr/pgsql-11/bin/postgresql*-setup initdb
sudo systemctl start postgresql-11
步骤2:安装EPEL存储库
sudo yum install epel-release
步骤3:安装带有Llvmjit的PostgreSQL软件包
sudo yum install postgresql11-llvmjit
既然我们已经添加了EPEL存储库,现在这种依赖性可以通过Yum来解决,它可以从EPEL拉出并安装必要的包。安装消息中包含必要的包。
...
Installing:
postgresql11-llvmjit x86_64 11.1-1PGDG.rhel7 pgdg11 9.0 M
Installing for dependencies:
llvm5.0 x86_64 5.0.1-7.el7 epel 2.6 M
llvm5.0-libs x86_64 5.0.1-7.el7 epel 13 M
...
正如我们看到的,有两个包:LLvm5.00和Llvm5.5-L进行安装。
Ubuntu 用户说明:
正如我们已经提到的,最新版本的Ubuntu存储库包含最新版本的LLVM库。例如,Ubuntu 16.04 LTS repo默认包含libllvm6.0。而且,PostgreSQL的服务器包没有被分割成一个单独的包来存放jit相关的文件。所以默认安装PostgreSQL 11也可以获得JIT特性。
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
sudo apt install postgresql-11
方式2:从源头建造
分发PostgreSQL的主要方式是源代码。构建一个最小的PostgreSQL实例只需要一个C编译器。但是构建JIT选项需要更多的东西。您可能遇到的挑战之一是,由于系统中存在较旧版本的LLVM和Clang,在构建过程中会出现不同的错误。
步骤1:下载PostgreSQL源码
tarballs在存储库中可用。我们可以获得解压后使用
curl -LO https://ftp.postgresql.org/pub/source/v11.0/postgresql-11.0.tar.bz2
tar -xvf postgresql-11.0.tar.bz2
步骤2:获得SCL存储库并安装工具集
在SCL中可以获得最新版本的LLVM、CLang和GCC。我们可以在短时间内获得所有东西:
sudo yum install centos-release-scl
sudo yum install llvm-toolset-7 llvm-toolset-7-llvm-devel.x86_64
现在,您可以设置或编辑您的路径,使所有新的工具都在路径中。我更愿意把这个写进环境变量文件里:
PATH=/opt/rh/devtoolset-7/root/usr/bin/:/opt/rh/llvm-toolset-7/root/usr/bin/:$PATH
另外,我们也可以用SCL打开一个bash:
scl enable devtoolset-7 llvm-toolset-7 bash
我们应该尝试从设置了所有这些路径的bash上编译源代码。
步骤3:安装更多库文件或工具
根据您想要的配置选项,这个列表可能会更改。为示范目的,将其作为一个样本:
sudo yum install readline-devel zlib-devel libxml2-devel openssl-devel
步骤4:使用—with-llvm选项进行配置
现在我们应该能够配置和制作我们的首选选项。如果指定了—with-llvm选项,JIT特性将可用。在这个演示中,我使用的安装目录是我的主目录(/home/postgres/pg11):
./configure --prefix=/home/postgres/pg11 --with-openssl --with-libxml --with-zlib --with-llvm
make
make install
启用JIT
你可能会注意到在PostgreSQL的lib文件夹下有一个名为bit code的新目录,其中包含许多扩展名为.bc的文件,这些是为LLVM预先生成的字节码,用于促进内联等功能。
默认情况下,PostgreSQL 11的JIT特性是关闭的。如果你想测试它,你可能必须启用jit参数:
postgres=# ALTER SYSTEM SET jit=on;
ALTER SYSTEM
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
postgres=# show jit;
jit
-----
on
(1 row)
默认情况下,由于成本原因,大多数简单查询不会使用JIT。当JIT开始使用时,成本很高。如果我们想要测试JIT配置是否正确,我们可以通过调整参数值来告诉PostgreSQL这个成本非常低。然而,应该记住,我们正在接受负性能收益。让我举一个简单的例子:
postgres=# SET jit_above_cost=5;
SET
postgres=# create table t1 (id int);
CREATE TABLE
postgres=# insert into t1 (SELECT (random()*100)::int FROM generate_series(1,800000) as g);
INSERT 0 800000
postgres=# analyze t1;
ANALYZE
postgres=# explain select sum(id) from t1;
QUERY PLAN
-------------------------------------------------------------------------------------
Finalize Aggregate (cost=8706.88..8706.89 rows=1 width=8)
-> Gather (cost=8706.67..8706.88 rows=2 width=8)
Workers Planned: 2
-> Partial Aggregate (cost=7706.67..7706.68 rows=1 width=8)
-> Parallel Seq Scan on t1 (cost=0.00..6873.33 rows=333333 width=4)
JIT:
Functions: 6
Options: Inlining false, Optimization false, Expressions true, Deforming true
(8 rows)
正如我们在上面的示例中看到的,在解释计划中出现了一个单独的JIT部分。
我们期望JIT编译在复杂的分析查询中发挥作用,因为JIT编译中的开销只有在代码在持续时间内运行时才会得到补偿。下面是一个用于演示的简单聚合查询。(我知道这不是一个复杂的查询,也不是演示JIT特性的完美示例):
postgres=# EXPLAIN ANALYZE SELECT COMPANY_ID,
SUM(SHARES) TOT_SHARES,
SUM(SHARES* RATE) TOT_INVEST,
MIN(SHARES* RATE) MIN_TRADE,
MAX(SHARES* RATE) MAX_TRADE,
SUM(SHARES* RATE * 0.002) BROKERAGE
FROM TRADING
GROUP BY COMPANY_ID;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=757298.72..758741.91 rows=5005 width=138) (actual time=16992.290..17011.395 rows=5000 loops=1)
Group Key: company_id
-> Gather Merge (cost=757298.72..758466.64 rows=10010 width=138) (actual time=16992.270..16996.919 rows=15000 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=756298.70..756311.21 rows=5005 width=138) (actual time=16983.900..16984.356 rows=5000 loops=3)
Sort Key: company_id
Sort Method: quicksort Memory: 1521kB
Worker 0: Sort Method: quicksort Memory: 1521kB
Worker 1: Sort Method: quicksort Memory: 1521kB
-> Partial HashAggregate (cost=755916.09..755991.16 rows=5005 width=138) (actual time=16975.997..16981.354 rows=5000 loops=3)
Group Key: company_id
-> Parallel Seq Scan on trading (cost=0.00..287163.65 rows=12500065 width=12) (actual time=0.032..1075.833 rows=10000000 loops=3)
Planning Time: 0.073 ms
Execution Time: 17013.116 ms
(15 rows)
我们可以在会话级别上打开JIT参数并重试相同的查询:
postgres=# SET JIT=ON;
SET
postgres=# EXPLAIN ANALYZE SELECT COMPANY_ID,
SUM(SHARES) TOT_SHARES,
SUM(SHARES* RATE) TOT_INVEST,
MIN(SHARES* RATE) MIN_TRADE,
MAX(SHARES* RATE) MAX_TRADE,
SUM(SHARES* RATE * 0.002) BROKERAGE
FROM TRADING
GROUP BY COMPANY_ID;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=757298.72..758741.91 rows=5005 width=138) (actual time=15672.809..15690.901 rows=5000 loops=1)
Group Key: company_id
-> Gather Merge (cost=757298.72..758466.64 rows=10010 width=138) (actual time=15672.781..15678.736 rows=15000 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=756298.70..756311.21 rows=5005 width=138) (actual time=15661.144..15661.638 rows=5000 loops=3)
Sort Key: company_id
Sort Method: quicksort Memory: 1521kB
Worker 0: Sort Method: quicksort Memory: 1521kB
Worker 1: Sort Method: quicksort Memory: 1521kB
-> Partial HashAggregate (cost=755916.09..755991.16 rows=5005 width=138) (actual time=15653.390..15658.581 rows=5000 loops=3)
Group Key: company_id
-> Parallel Seq Scan on trading (cost=0.00..287163.65 rows=12500065 width=12) (actual time=0.039..1084.820 rows=10000000 loops=3)
Planning Time: 0.072 ms
JIT:
Functions: 28
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 5.844 ms, Inlining 137.226 ms, Optimization 201.152 ms, Emission 125.022 ms, Total 469.244 ms
Execution Time: 15696.092 ms
(19 rows)
在这里,我们看到性能提高了7.7%。我执行了好几次,发现这个简单的查询(执行需要15秒)的性能增益始终是7-8%。对于有更多计算/表达式的查询,收益更高。
总结
如上所示,用PostgreSQL安装和配置JIT非常简单。我们想要强调的一点是,可以在数据库启动并运行时在线安装JIT包和启用JIT特性。这是因为所有与JIT相关的参数本质上都是动态的。参数更改可以由超级用户加载SIGHUP信号或SELECT pg_reload_conf()。如果它对我们的工作量没有帮助,我们可以随时关闭它。没有什么可以阻止您在非生产环境中尝试它。对于执行时间更短的小型简单查询,我们可能不会看到任何好处,因为执行JIT编译的开销可能超过执行SQL语句的开销。但是,对于运行时间较长的复杂查询,我们应该期望在OLAP工作负载中获得良好的增益。
本文分享自微信公众号 - 开源软件联盟PostgreSQL分会(kaiyuanlianmeng)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。