代码之家  ›  专栏  ›  技术社区  ›  onder

PostgreSQL记录和卸载表性能与锁表使用情况的比较

  •  2
  • onder  · 技术社区  · 8 年前

    我在试着测量 UPDATE 打开速度 LOGGED 和 UNLOGGED PostgreSQL中的表(10.3)。我想用 LOCK TABLE 以防止其他应用程序相互干扰。

    如果 更新 执行时不使用 锁定表 ,我得到~1K 记录的 桌子,~4K 已卸载 表。

    如果 更新 使用 锁定表 ,两种表类型的结果相同。

    为什么? 锁定表 在两种表类型中返回相同的结果?

    我的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);
    }   
    
    1 回复  |  直到 8 年前
        1
  •  3
  •   Laurenz Albe    8 年前

    您在这里测量的很多内容是客户机-服务器延迟和pl/pgsql执行。

    这种差异是由于需要将wal同步到磁盘造成的。

    如果你用的是一张没有装好的桌子,而不是 LOCK 声明,不写wal,也不需要同步 COMMIT 时间。

    显式表锁导致写入一个wal记录,因此 提交 仍然必须同步沃尔,你失去了你的优势,你从一个不下人的桌子。

    你可以用 pg_waldump 要检查wal文件,您将看到写入了哪些事务日志记录。

    但是我可以用我的PostgreSQL v11向你展示它,它是用 -DWAL_DEBUG .

    这是我的测试台:

    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
    

    有一个承诺,但没有沃尔玛同花顺。

    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
    

    现在我们有一个沃尔冲水器,这是最昂贵的部分。

    你看到了吗? Standby/LOCK 记录是写的。

    解决这个问题的方法之一是减少 wal_level 到 minimal 和 max_wal_senders 到 0 那么这些沃尔记录就不必写了。但是你不能让Wal存档和时间点恢复。

    另一个解决方法是使用比 ACCESS EXCLUSIVE . 如果你不一定要阻止读者,那就没问题了。