PostgreSQL LOGGED 和 UNLOGGED table 与 LOCK TABLE 用法的性能比较

PostgreSQL LOGGED and UNLOGGED table performance comparison with LOCK TABLE usage

我正在尝试在 PostgreSQL(10.3) 中测量 LOGGEDUNLOGGED table 上的 UPDATE 速度。我想使用 LOCK TABLE 来防止其他应用程序相互干扰。

如果在不使用 LOCK TABLE 的情况下执行 UPDATELOGGED 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 更低的锁定级别。除非您绝对必须阻止读者,否则应该没问题。