PostgreSQL LOGGED 和 UNLOGGED table 与 LOCK TABLE 用法的性能比较
PostgreSQL LOGGED and UNLOGGED table performance comparison with LOCK TABLE usage
我正在尝试在 PostgreSQL(10.3) 中测量 LOGGED
和 UNLOGGED
table 上的 UPDATE
速度。我想使用 LOCK TABLE
来防止其他应用程序相互干扰。
如果在不使用 LOCK TABLE
的情况下执行 UPDATE
,LOGGED
table 会得到 ~ 1K,UNLOGGED
[=40 会得到~ 4K =].
如果使用 LOCK TABLE
执行 UPDATE
,则两种 table 类型的结果相同。
为什么 LOCK TABLE
return 两种 table 类型的结果相同?
我的PL/pgSQL函数:
CREATE OR REPLACE FUNCTION public.myfunction(user_id integer, unitprice numeric(10,6),
islock boolean, useunlogged boolean) RETURNS integer AS
$BODY$
declare howMuch integer;
begin
if islock then
if useunlogged then
LOCK TABLE credittable_unlogged IN ACCESS EXCLUSIVE MODE;
else
LOCK TABLE credittable IN ACCESS EXCLUSIVE MODE;
end if;
end if;
if useunlogged then
select (credit_amount/unitprice)::integer into howMuch from credittable where userid=user_id and credit_amount>=unitprice;
if howMuch is null then
select 0 into howMuch;
else
update credittable set credit_amount=credit_amount-unitprice where userid=user_id;
end if;
else
select (credit_amount/unitprice)::integer into howMuch from credittable_unlogged where userid=user_id and credit_amount>=unitprice;
if howMuch is null then
select 0 into howMuch;
else
update credittable_unlogged set credit_amount=credit_amount-unitprice where userid=user_id;
end if;
end if;
RETURN howMuch;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.myfunction(integer, numeric, boolean, boolean)
OWNER TO postgres;
我的Java代码:
for(int i=1;i<=4;i++){
long startTime = System.nanoTime();
int counter = 0;
while ((System.nanoTime() - startTime) < 1000000000L) {
CallableStatement callst = null;
try {
String sql = "{? = call public.myfunction(?,?,?,?) }";
callst = con.prepareCall(sql);
callst.registerOutParameter(1, Types.INTEGER);
callst.setInt(2, 123456);
callst.setBoolean(3, (i > 2));
callst.setBoolean(4, (i%2 != 0));
callst.setBigDecimal(3, BigDecimal.valueOf(0.001));
callst.execute();
int howMuch = callst.getInt(1);
counter++;
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (callst != null) {
callst.close();
}
}
}
System.out.println("Counter :"+counter);
}
您在这里测量的很多内容是客户端-服务器延迟和 PL/pgSQL 执行。
不同之处在于需要将 WAL 同步到磁盘。
如果您使用未记录的 table,并且您不使用 LOCK
语句,则不会写入 WAL,并且在 COMMIT
时无需同步任何内容.
显式 table 锁会导致写入 WAL 记录,因此 COMMIT
仍然需要同步 WAL,并且您失去了未记录 table 的优势。
您可以使用pg_waldump
检查WAL文件,然后您会看到写入了哪些事务日志记录。
但我可以用我的 PostgreSQL v11 向您展示它,它是用 -DWAL_DEBUG
构建的。
这是我的测试table:
postgres=# \d t
Unlogged table "public.t"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
这里是 INSERT
没有 LOCK TABLE
:
postgres=# SET wal_debug=on;
SET
postgres=# BEGIN;
BEGIN
postgres=# INSERT INTO t VALUES (100);
INSERT 0 1
postgres=# COMMIT;
LOG: INSERT @ 0/166BFB8: - Transaction/COMMIT: 2018-05-18 20:34:20.060635+02
STATEMENT: COMMIT;
COMMIT
有提交,但没有 WAL 刷新。
postgres=# BEGIN;
BEGIN
postgres=# LOCK TABLE t;
LOG: INSERT @ 0/166C038: - Standby/LOCK: xid 569 db 13344 rel 16384
STATEMENT: LOCK TABLE t;
LOCK TABLE
postgres=# INSERT INTO t VALUES (101);
INSERT 0 1
postgres=# COMMIT;
LOG: INSERT @ 0/166C138: - Transaction/COMMIT: 2018-05-18 20:36:15.419081+02
STATEMENT: COMMIT;
LOG: xlog flush request 0/166C138; write 0/166BFF0; flush 0/166BFF0
STATEMENT: COMMIT;
COMMIT
现在我们有一个 WAL 刷新,这是昂贵的部分。
您看到写入了 Standby/LOCK
条记录。
解决这个问题的一种方法是将 wal_level
减少到 minimal
并将 max_wal_senders
减少到 0
,然后不必写入这些 WAL 记录。但是你不能有 WAL 归档和时间点恢复。
另一种解决方法是使用比 ACCESS EXCLUSIVE
更低的锁定级别。除非您绝对必须阻止读者,否则应该没问题。
我正在尝试在 PostgreSQL(10.3) 中测量 LOGGED
和 UNLOGGED
table 上的 UPDATE
速度。我想使用 LOCK TABLE
来防止其他应用程序相互干扰。
如果在不使用 LOCK TABLE
的情况下执行 UPDATE
,LOGGED
table 会得到 ~ 1K,UNLOGGED
[=40 会得到~ 4K =].
如果使用 LOCK TABLE
执行 UPDATE
,则两种 table 类型的结果相同。
为什么 LOCK TABLE
return 两种 table 类型的结果相同?
我的PL/pgSQL函数:
CREATE OR REPLACE FUNCTION public.myfunction(user_id integer, unitprice numeric(10,6),
islock boolean, useunlogged boolean) RETURNS integer AS
$BODY$
declare howMuch integer;
begin
if islock then
if useunlogged then
LOCK TABLE credittable_unlogged IN ACCESS EXCLUSIVE MODE;
else
LOCK TABLE credittable IN ACCESS EXCLUSIVE MODE;
end if;
end if;
if useunlogged then
select (credit_amount/unitprice)::integer into howMuch from credittable where userid=user_id and credit_amount>=unitprice;
if howMuch is null then
select 0 into howMuch;
else
update credittable set credit_amount=credit_amount-unitprice where userid=user_id;
end if;
else
select (credit_amount/unitprice)::integer into howMuch from credittable_unlogged where userid=user_id and credit_amount>=unitprice;
if howMuch is null then
select 0 into howMuch;
else
update credittable_unlogged set credit_amount=credit_amount-unitprice where userid=user_id;
end if;
end if;
RETURN howMuch;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.myfunction(integer, numeric, boolean, boolean)
OWNER TO postgres;
我的Java代码:
for(int i=1;i<=4;i++){
long startTime = System.nanoTime();
int counter = 0;
while ((System.nanoTime() - startTime) < 1000000000L) {
CallableStatement callst = null;
try {
String sql = "{? = call public.myfunction(?,?,?,?) }";
callst = con.prepareCall(sql);
callst.registerOutParameter(1, Types.INTEGER);
callst.setInt(2, 123456);
callst.setBoolean(3, (i > 2));
callst.setBoolean(4, (i%2 != 0));
callst.setBigDecimal(3, BigDecimal.valueOf(0.001));
callst.execute();
int howMuch = callst.getInt(1);
counter++;
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (callst != null) {
callst.close();
}
}
}
System.out.println("Counter :"+counter);
}
您在这里测量的很多内容是客户端-服务器延迟和 PL/pgSQL 执行。
不同之处在于需要将 WAL 同步到磁盘。
如果您使用未记录的 table,并且您不使用 LOCK
语句,则不会写入 WAL,并且在 COMMIT
时无需同步任何内容.
显式 table 锁会导致写入 WAL 记录,因此 COMMIT
仍然需要同步 WAL,并且您失去了未记录 table 的优势。
您可以使用pg_waldump
检查WAL文件,然后您会看到写入了哪些事务日志记录。
但我可以用我的 PostgreSQL v11 向您展示它,它是用 -DWAL_DEBUG
构建的。
这是我的测试table:
postgres=# \d t
Unlogged table "public.t"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
这里是 INSERT
没有 LOCK TABLE
:
postgres=# SET wal_debug=on;
SET
postgres=# BEGIN;
BEGIN
postgres=# INSERT INTO t VALUES (100);
INSERT 0 1
postgres=# COMMIT;
LOG: INSERT @ 0/166BFB8: - Transaction/COMMIT: 2018-05-18 20:34:20.060635+02
STATEMENT: COMMIT;
COMMIT
有提交,但没有 WAL 刷新。
postgres=# BEGIN;
BEGIN
postgres=# LOCK TABLE t;
LOG: INSERT @ 0/166C038: - Standby/LOCK: xid 569 db 13344 rel 16384
STATEMENT: LOCK TABLE t;
LOCK TABLE
postgres=# INSERT INTO t VALUES (101);
INSERT 0 1
postgres=# COMMIT;
LOG: INSERT @ 0/166C138: - Transaction/COMMIT: 2018-05-18 20:36:15.419081+02
STATEMENT: COMMIT;
LOG: xlog flush request 0/166C138; write 0/166BFF0; flush 0/166BFF0
STATEMENT: COMMIT;
COMMIT
现在我们有一个 WAL 刷新,这是昂贵的部分。
您看到写入了 Standby/LOCK
条记录。
解决这个问题的一种方法是将 wal_level
减少到 minimal
并将 max_wal_senders
减少到 0
,然后不必写入这些 WAL 记录。但是你不能有 WAL 归档和时间点恢复。
另一种解决方法是使用比 ACCESS EXCLUSIVE
更低的锁定级别。除非您绝对必须阻止读者,否则应该没问题。