Tuning performance for write

High write loads can have different patterns. For example, this can be a result of writing events to PostgreSQL or it can be a result of a bulk load from a database dump or an ETL job. One can tune PostgreSQL for high write loads by doing the following: 

  • Hardware configuration:
    • One should use RAID 1+0 instead of RAID 5 or 6. RAID 10 has much better performance for heavy writes. Also it is better to store transaction logs (pg_xlog) on a separate hard disk.
    • One can use SSD hard disks with Write-back cache (WBC), which significantly increases write performance. Also make sure your SSDs can persist cached data on power failure.
  • PostgreSQL server setting:
    • fsync: By default, fsync is on. This parameter makes sure that the database can be recovered in the event of a hardware crash. fsync makes sure that the data is actually written on the hard disk. You can disable this option if you can trust your hardware. Hardware failure might lead to corrupt data if this option is disabled.
    • synchronous_commit and commit_delayWhen synchronous_commit is enabled by default, the transaction will wait until the WAL file is written to the hard disk to report success to the client. This will help to boost the performance of systems that have heavy concurrent small transactions. commit_delay is used to delay WAL flushing in microseconds. Combining both will reduce the effect off fsync. Unlike disabling fsync, a hardware crash will not cause data to be corrupted, but might cause data to be lost. 
    • max_wal_size or checkpoint_segments (in PostgreSQL 9.4 and older): the checkpoint_segments setting was deprecated in PostgreSQL 9.5 and replaced with min_wal_size and max_wal_size. The relation between max_wal_size and checkpoint_segment can be given as: max_wal_size = (3 * checkpoint_segments) * 16 MB. Increasing max_wal_size causes a performance gain since WAL are not written very frequently to hard disks. This affects checkpoint triggering and causes a slow recovery in crash scenarios. 
    • wal_buffers: This setting is disabled by default. It is used to store WAL data that is not written to a hard disk. Increasing this value helps a busy server with several concurrent clients. The maximum value of this setting is 16 MB. 
    • maintenece_work_mem: This setting does not directly affect the performance of data insert, but it increases the performance of creating and maintaining indexes. Increasing the value of maintenece_work_mem increases the performance of the INSERT operation indirectly, especially if the target table has indexes. 
    • Other settings: One can disable several other settings to increase performance, for example, logging and logging collection can increase performance if one is logging data heavily. Also, autovacuum can be disabled on bulk load scenarios in order to not interrupt the loading process.
  • DDL and DML statements:
    • In bulk load scenarios, there are a lot of tricks to speed up performance. For example, one can disable triggers, indexes, and foreign keys on the table that one needs to copy. Also, one can use an UNLOGGED table and then convert it to a logged table using ALTER TABLE <table name> SET LOGGED;.
    • For a heavy insert scenario, there are several common tricks. The first trick is to increase the batch size of each transaction. This is quite useful because it will decrease the delay caused by synchronous_commit. In addition to that, it will preserve transaction IDs, thus, less vacuum is required to prevent transaction IDs wraparound. The second trick is to use COPY command. The JDBC driver CopyManager can be used for this purpose. Finally, it is a good practice to use prepared statements. Prepared statements are faster to execute since the statements are precompiled on the PostgreSQL side.
  • External tools: For bulk uploads one can use pg_bulkload, which is quite fast.

To sum up, for a heavy write systems it is good to write to the hard disk in batches instead of single INSERT SQL statements. Also, it is better to use a ;COPY statement instead of an INSERT statement. Finally, one can also parallelize statements using several sessions instead of one session. Starting from PostgreSQL 9.3, one can use COPY with the FREEZE option, which is used often for initial data loading. The FREEZE option violates the MVCC concept in PostgreSQL, which means the data is immediately visible to other sessions after it is loaded. 

To test the effect of fsync, let's prepare a SQL script for pgbench as follows:

$ cat test.sql 
set aid random(1, 100000 * :scale)
set bid random(1, 1 * :scale)
set tid random(1, 10 * :scale)
set delta random(-5000, 5000)
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
END;

Now let's run pgbench before making any changes to test the system base performance. Note that, in this test, all settings have default values:

$pgbench -t 1000 -c 15 -f test.sql 
starting vacuum...end.
transaction type: test.sql
scaling factor: 1
query mode: simple
number of clients: 15
number of threads: 1
number of transactions per client: 1000
number of transactions actually processed: 15000/15000
latency average = 18.455 ms
tps = 812.801437 (including connections establishing)
tps = 813.032862 (excluding connections establishing)

To test the effect of disabling fsync, one can alter the system settings and restart the server as follows:

$ psql -U postgres << EOF
> ALTER SYSTEM RESET ALL;
> ALTER SYSTEM SET fsync to off;
> EOF
ALTER SYSTEM
ALTER SYSTEM

$/etc/init.d/postgresql restart
[ ok ] Restarting postgresql (via systemctl): postgresql.service.

$ pgbench -t 1000 -c 15 -f test.sql
starting vacuum...end.
transaction type: test.sql
scaling factor: 1
query mode: simple
number of clients: 15
number of threads: 1
number of transactions per client: 1000
number of transactions actually processed: 15000/15000
latency average = 11.976 ms
tps = 1252.552937 (including connections establishing)
tps = 1253.082492 (excluding connections establishing)

To test the effect of synchronous_commit and commit_delay, one can alter the settings as follows: 

$ psql -U postgres << EOF
> ALTER SYSTEM RESET ALL;
> ALTER SYSTEM SET synchronous_commit to off;
> ALTER SYSTEM SET commit_delay to 100000;
> EOF
ALTER SYSTEM
ALTER SYSTEM
ALTER SYSTEM
$ /etc/init.d/postgresql restart
[ ok ] Restarting postgresql (via systemctl): postgresql.service.

$ pgbench -t 1000 -c 15 -f test.sql
starting vacuum...end.
transaction type: test.sql
scaling factor: 1
query mode: simple
number of clients: 15
number of threads: 1
number of transactions per client: 1000
number of transactions actually processed: 15000/15000
latency average = 12.521 ms
tps = 1197.960750 (including connections establishing)
tps = 1198.416907 (excluding connections establishing)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset