Saturday, August 12, 2023

PostgreSQL JDBC prepareThreshold Behaviour with Database setting plan_cache_mode

Source code: https://raw.githubusercontent.com/luodonghua/PostgreSQL/main/Development/Java/JdbcPreparedPlanCacheModeDemo.java


Summary

Observations:

  1. During testing, the first time execution with id=1000 is much more expensive comparing to subsequent execution (483 ms vs 0.7 ms), although same plan used.
  2. In the situation with "prepared statements", the value for prepareThreshold=n and plan_cache_mode=auto, after n+5-1 executions, generic plan could be used if " its cost is not so much higher than the average custom-plan cost". (Ref: https://www.postgresql.org/docs/current/sql-prepare.html)
  3. plan_cache_mode only affects prepared statements, has no effect on literal queries.
  4. To ensure optimizer always pick the best plan, plan_cache_mode=force_custom_plan produces consistent plans.

Prepare Statements:

PreparedStatement ps = conn.prepareStatement("SELECT * FROM t WHERE id = ?");

ps.setInt(1, 1);
ps.executeQuery().close();
ps.setInt(1, 2);
ps.executeQuery().close();
ps.setInt(1, 3);
ps.executeQuery().close();
ps.setInt(1, 4);
ps.executeQuery().close();
ps.setInt(1, 5);
ps.executeQuery().close();
ps.setInt(1, 6);
ps.executeQuery().close();
ps.setInt(1, 1000);
ps.executeQuery().close();
ps.setInt(1, 7);
ps.executeQuery().close();
ps.setInt(1, 1000);
ps.executeQuery().close();     

Result

Config123456100071000
1.PrepareThreshold5CacheModeAutoBISISISISISISSEQISSEQ
2.PrepareThreshold5CacheModeForceCustomISISISISISISSEQISSEQ
3.PrepareThreshold5CacheModeForceGenericISISISISISISISISIS
4.PrepareThreshold10CacheModeAutoISISISISISISSEQISSEQ
5.PrepareThreshold10CacheModeForceCustomISISISISISISSEQISSEQ
6.PrepareThreshold10CacheModeForceGenericISISISISISISISISIS
7.PrepareThreshold1CacheModeAutoISISISISISISISISIS
8.PrepareThreshold1CacheModeForceCustomISISISISISISSEQISSEQ
9.PrepareThreshold1CacheModeForceGenericISISISISISISISISIS
  • BIS: Bitmap Index Scan
  • IS: Index Scan
  • SEQ: Seq Scan

Prepare Statements Starts with 1000

PreparedStatement ps = conn.prepareStatement("SELECT * FROM t WHERE id = ?");

ps.setInt(1, 1000);
ps.executeQuery().close();
ps.setInt(1, 2);
ps.executeQuery().close();
ps.setInt(1, 3);
ps.executeQuery().close();
ps.setInt(1, 4);
ps.executeQuery().close();
ps.setInt(1, 5);
ps.executeQuery().close();
ps.setInt(1, 6);
ps.executeQuery().close();
ps.setInt(1, 1000);
ps.executeQuery().close();
ps.setInt(1, 7);
ps.executeQuery().close();
ps.setInt(1, 1000);
ps.executeQuery().close();     

Result

Config100023456100071000
10.PrepareThreshold5CacheModeAutoSEQISISISISISSEQISSEQ
11.PrepareThreshold5CacheModeForceCustomSEQISISISISISSEQISSEQ
12.PrepareThreshold5CacheModeForceGenericISISISISISISISISIS
13.PrepareThreshold10CacheModeAutoSEQISISISISISSEQISSEQ
14.PrepareThreshold10CacheModeForceCustomSEQISISISISISSEQISSEQ
15.PrepareThreshold10CacheModeForceGenericISISISISISISISISIS
16.PrepareThreshold1CacheModeAutoSEQISISISISISISISIS
17.PrepareThreshold1CacheModeForceCustomSEQISISISISISSEQISSEQ
18.PrepareThreshold1CacheModeForceGenericISISISISISISISISIS
  • BIS: Bitmap Index Scan
  • IS: Index Scan
  • SEQ: Seq Scan

Statements:

Statement stmt = conn.createStatement();
stmt.execute("SELECT * FROM t WHERE id = 1");  
stmt.execute("SELECT * FROM t WHERE id = 2");  
stmt.execute("SELECT * FROM t WHERE id = 3");
stmt.execute("SELECT * FROM t WHERE id = 4");
stmt.execute("SELECT * FROM t WHERE id = 5");
stmt.execute("SELECT * FROM t WHERE id = 6");
stmt.execute("SELECT * FROM t WHERE id = 1000");
stmt.execute("SELECT * FROM t WHERE id = 7");
stmt.execute("SELECT * FROM t WHERE id = 1000");
stmt.close();
Config123456100071000
19.PrepareThreshold5CacheModeAutoISISISISISISSEQISSEQ
20.PrepareThreshold5CacheModeForceCustomISISISISISISSEQISSEQ
21.PrepareThreshold5CacheModeForceGenericISISISISISISSEQISSEQ
22.PrepareThreshold10CacheModeAutoISISISISISISSEQISSEQ
23.PrepareThreshold10CacheModeForceCustomISISISISISISSEQISSEQ
24.PrepareThreshold10CacheModeForceGenericISISISISISISSEQISSEQ
25.PrepareThreshold1CacheModeAutoISISISISISISSEQISSEQ
26.PrepareThreshold1CacheModeForceCustomISISISISISISSEQISSEQ
27.PrepareThreshold1CacheModeForceGenericISISISISISISSEQISSEQ
  • BIS: Bitmap Index Scan
  • IS: Index Scan
  • SEQ: Seq Scan

Statements:

Statement stmt = conn.createStatement();
stmt.execute("SELECT * FROM t WHERE id = 1000");  
stmt.execute("SELECT * FROM t WHERE id = 2");  
stmt.execute("SELECT * FROM t WHERE id = 3");
stmt.execute("SELECT * FROM t WHERE id = 4");
stmt.execute("SELECT * FROM t WHERE id = 5");
stmt.execute("SELECT * FROM t WHERE id = 6");
stmt.execute("SELECT * FROM t WHERE id = 1000");
stmt.execute("SELECT * FROM t WHERE id = 7");
stmt.execute("SELECT * FROM t WHERE id = 1000");
stmt.close();
Config100023456100071000
28.PrepareThreshold5CacheModeAutoSEQISISISISISSEQISSEQ
29.PrepareThreshold5CacheModeForceCustomSEQISISISISISSEQISSEQ
30.PrepareThreshold5CacheModeForceGenericSEQISISISISISSEQISSEQ
31.PrepareThreshold10CacheModeAutoSEQISISISISISSEQISSEQ
32.PrepareThreshold10CacheModeForceCustomSEQISISISISISSEQISSEQ
33.PrepareThreshold10CacheModeForceGenericSEQISISISISISSEQISSEQ
34.PrepareThreshold1CacheModeAutoSEQISISISISISSEQISSEQ
35.PrepareThreshold1CacheModeForceCustomSEQISISISISISSEQISSEQ
36.PrepareThreshold1CacheModeForceGenericSEQISISISISISSEQISSEQ
  • BIS: Bitmap Index Scan
  • IS: Index Scan
  • SEQ: Seq Scan