使用pl/lua寫存儲過程提升10倍性能

1. 爲什麼要使用pl/lua

pl/lua是一個插件,可以使用lua語言寫PostgreSQL的存儲過程和函數,使用lua語言有如下幾個好處,

可以大大提升存儲過程的性能,特別是循環的性能。

在PostgreSQL中數組或一些json對象都是不可變的,如往數組或json對象中添加元素時,需要把拷貝源對象而生成一個新對象,導致很大的開銷。而使用lua語言的list和table對象就沒有這個問題了。

lua語言的語法更靈活。

也就是當我們需要在存儲過程或函數中做一些密集的運算,使用plpgsql會比較慢,而使用pl/lua會提升10倍以上的性能。這個提升還是很觀的,所以建議這些存儲過程使用pllua來編寫。

2. 我們先看看性能

2.1 查看循環的效率

我們分別使用pllua和pgplsql建兩個循環的函數:

create or replace function f_pl01(cnt int) returns int language plpgsql as $$
declare
i int;
begin
i:=0;
LOOP
i = i + 1;
EXIT WHEN i >= cnt;
END LOOP;
return i;
end;
$$;
create function f_lua01(cnt int) returns int language pllua as $$
local i=0
while( i < cnt ) do
   i = i+1
end
return i
$$;

運行一下看執行時間:

postgres=# \timing
Timing is on.
postgres=# select f_pl01(10000000);
  f_pl01
----------
 10000000
(1 row)
Time: 6482.846 ms (00:06.483)
postgres=# select f_lua01(10000000);
 f_lua01
----------
 10000000
(1 row)
Time: 556.831 ms

可以看出使用pgplsql循環1千萬次,需要6秒多,而使用pllua只需要557毫秒,快了近12倍。

如果我們建一個plpython的函數,如下所示:

create or replace function f_py01(cnt int) returns int language plpython3u as $$
    i = 0
while i < cnt:
        i = i + 1
return i
$$;

看一下執行時間:

postgres=# select f_py01(10000000);
  f_py01
----------
 10000000
(1 row)
Time: 1008.750 ms (00:01.009)

可以看出使用python是1秒中,也比plpgsql快很多。

2.2 數組中添加元素中的效率

我們再建兩個存儲過程,

create or replace function f_pl02(cnt int) returns int language plpgsql as $$
declare
i int;
myarr text[];
s1 text;
begin
i:=0;
s1 :=  lpad('', 2048, 'helloosdba');
LOOP
myarr := array_append(myarr, s1);
i = i + 1;
EXIT WHEN i >= cnt;
END LOOP;
return array_length(myarr, 1);
end;
$$;
create or replace function f_lua02(cnt int) returns int language pllua as $$
local i=0
local myarr = {}
local s1 = string.rep('helloosdba', 2048)
while( i < cnt ) do
   i = i+1
   myarr[i] = s1
end
return #myarr
$$;
postgres=# select f_pl02(100000);
 f_pl02
--------
 100000
(1 row)
Time: 756.772 ms
postgres=# select f_lua02(100000);
 f_lua02
---------
  100000
(1 row)
Time: 10.731 ms

可以看到差了70多倍。這是因爲使用plpgsql是,每次改變數組都需要把源數組複製一次,當數組越來越大時,每複製一次會花很長的時間。

如果是plpython:

create or replace function f_py02(cnt int) returns int language plpython3u as $$
    i = 0
    myarr = []
    s1 = 'helloosdba'*2048
while i < cnt:
        i = i+1
        myarr.append(s1)
return len(myarr)
$$;

執行時間:

postgres=# select f_py02(100000);
 f_py02
--------
 100000
(1 row)
Time: 23.459 ms

plpython也比較快,不過比pllua慢一倍。

3. 安裝方法

安裝pllua之前需要先安裝lua5.3.5,到網站上http://www.lua.org/download.html 下載lua5.3.5,然後編譯安裝:

cd /usr/src
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar xvf lua-5.3.5.tar.gz

默認的編譯出來的lua再編譯選項中沒有加“-fPIC”,會導致我們後面編譯pllua時報錯:

/usr/bin/ld: /usr/local/lib/liblua.a(loadlib.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status

到目錄cd lua-5.3.5/src下,修改Makefile,在CFLAGS中增加-fPIC:

CFLAGS= -fPIC -O2 -Wall -Wextra -DLUA_COMPAT_5_2 $(SYSCFLAGS) $(MYCFLAGS)

然後再編譯和安裝lua:

make install

編譯安裝pllua:

cd /usr/src
git clone https://github.com/pllua/pllua-ng.git
cd pllua-ng
make PG_CONFIG=/usr/pgsql-11/bin/pg_config
make PG_CONFIG=/usr/pgsql-11/bin/pg_config install

然後在數據庫中就可以加載pllua插件了:

create extension pllua;

4. pl/lua的一些資料

使用文檔: https://pllua.github.io/pllua-ng/

源代碼:https://github.com/pllua/pllua-ng

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章