Skip to content

Commit f8a46dc

Browse files
HuSen8891my-ship-it
authored andcommitted
Optimize lock for select-for-update and similar queries
Add GUC 'enable_lock_optimization' to enable lock optimization for select-for-update and similar queries. If the lock for query can be optimized, we use tuple lock instead of relation lock to improve concurrency performance.
1 parent a1573a2 commit f8a46dc

8 files changed

Lines changed: 103 additions & 6 deletions

File tree

src/backend/parser/analyze.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@
7070
#include "parser/parse_func.h"
7171
#include "utils/lsyscache.h"
7272

73+
/*
74+
* GUC parameter
75+
*
76+
* Enable locking optimization for extended query protocol to avoid
77+
* ExclusiveLock in case of select-for-update and similar queries.
78+
*/
79+
bool enableLockOptimization = false;
80+
7381
/* Working state for transformSetOperationTree_internal */
7482
typedef struct
7583
{
@@ -132,7 +140,6 @@ static bool test_raw_expression_coverage(Node *node, void *context);
132140
static int get_distkey_by_name(char *key, IntoClause *into, Query *qry, bool *found);
133141
static void setQryDistributionPolicy(ParseState *pstate, IntoClause *into, Query *qry);
134142

135-
static bool checkCanOptSelectLockingClause(SelectStmt *stmt);
136143
static bool queryNodeSearch(Node *node, void *context);
137144
static void sanity_check_on_conflict_update_set_distkey(GpPolicy *policy, List *onconflict_set);
138145
static void sanity_check_on_conflict_update(Oid relid, List *on_conflict_set, Node *on_conflict_where);
@@ -4080,7 +4087,7 @@ setQryDistributionPolicy(ParseState *pstate, IntoClause *into, Query *qry)
40804087
* can behave like Postgres. We have to know it before
40814088
* we acquire any locks on the tables.
40824089
*/
4083-
static bool
4090+
bool
40844091
checkCanOptSelectLockingClause(SelectStmt *stmt)
40854092
{
40864093
QueryNodeSearchContext ctx = {false};

src/backend/tcop/postgres.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,6 +2171,22 @@ exec_parse_message(const char *query_string, /* string to execute */
21712171
*/
21722172
if (IS_SINGLENODE())
21732173
((SelectStmt *)raw_parse_tree->stmt)->disableLockingOptimization = false;
2174+
else if (enableLockOptimization)
2175+
{
2176+
SelectStmt *stmt = (SelectStmt *)raw_parse_tree->stmt;
2177+
2178+
/*
2179+
* If GUC 'enableLockOptimization' is on, we try to optimize the lock for
2180+
* select-for-update and similar queries.
2181+
*
2182+
* If the lock cannot be optimized, use the default way.
2183+
*/
2184+
stmt->disableLockingOptimization = false;
2185+
if (!checkCanOptSelectLockingClause(stmt))
2186+
{
2187+
((SelectStmt *)raw_parse_tree->stmt)->disableLockingOptimization = true;
2188+
}
2189+
}
21742190
else
21752191
((SelectStmt *)raw_parse_tree->stmt)->disableLockingOptimization = true;
21762192
}
@@ -2472,6 +2488,26 @@ exec_bind_message(StringInfo input_message)
24722488

24732489
portal->is_extended_query = true;
24742490

2491+
/*
2492+
* If GUC 'enableLockOptimization' is on, we try to optimize the lock for
2493+
* select-for-update and similar queries.
2494+
*
2495+
* If the lock for query can be optimized, we use tuple lock instead of
2496+
* relation lock to improve Concurrency Performance. LockRows is executed
2497+
* on segment, but reader gangs cannot execute LockRows, so we need to
2498+
* dispatch query plan to writer gangs, that's why we reset is_extended_query
2499+
* to false here.
2500+
*/
2501+
if (enableLockOptimization && IsA(psrc->raw_parse_tree->stmt, SelectStmt))
2502+
{
2503+
SelectStmt *stmt = (SelectStmt *)psrc->raw_parse_tree->stmt;
2504+
2505+
if (checkCanOptSelectLockingClause(stmt))
2506+
{
2507+
portal->is_extended_query = false;
2508+
}
2509+
}
2510+
24752511
/*
24762512
* Prepare to copy stuff into the portal's memory context. We do all this
24772513
* copying first, because it could possibly fail (out-of-memory) and we
@@ -2973,6 +3009,45 @@ exec_execute_message(const char *portal_name, int64 max_rows)
29733009
if (max_rows <= 0)
29743010
max_rows = FETCH_ALL;
29753011

3012+
/*
3013+
* If the lock for select-for-update and similar queries is optimized, we should
3014+
* fetch all rows here.
3015+
*
3016+
* Since we optimize the lock for query, the query plan is dispatched to writer
3017+
* gangs to execute. If we do not fetch all rows here, the writer gangs are occupied
3018+
* and can not execute other query plans.
3019+
*/
3020+
if (enableLockOptimization)
3021+
{
3022+
CachedPlanSource *psrc;
3023+
3024+
if (portal->prepStmtName)
3025+
{
3026+
PreparedStatement *pstmt;
3027+
3028+
pstmt = FetchPreparedStatement(portal->prepStmtName, true);
3029+
psrc = pstmt->plansource;
3030+
}
3031+
else
3032+
{
3033+
/* special-case the unnamed statement */
3034+
psrc = unnamed_stmt_psrc;
3035+
if (!psrc)
3036+
ereport(ERROR,
3037+
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
3038+
errmsg("unnamed prepared statement does not exist")));
3039+
}
3040+
3041+
if (IsA(psrc->raw_parse_tree->stmt, SelectStmt) &&
3042+
checkCanOptSelectLockingClause((SelectStmt *)psrc->raw_parse_tree->stmt) &&
3043+
max_rows != FETCH_ALL)
3044+
{
3045+
ereport(ERROR,
3046+
(errcode(ERRCODE_INTERNAL_ERROR),
3047+
errmsg("Should fetch all rows for query if lock optimization is enabled")));
3048+
}
3049+
}
3050+
29763051
completed = PortalRun(portal,
29773052
max_rows,
29783053
true, /* always top level */

src/backend/utils/misc/guc.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "optimizer/optimizer.h"
6868
#include "optimizer/paths.h"
6969
#include "optimizer/planmain.h"
70+
#include "parser/analyze.h"
7071
#include "parser/parse_expr.h"
7172
#include "parser/parse_type.h"
7273
#include "parser/parser.h"
@@ -2209,6 +2210,13 @@ static struct config_bool ConfigureNamesBool[] =
22092210
NULL, NULL, NULL
22102211
},
22112212

2213+
{
2214+
{"enable_lock_optimization", PGC_SIGHUP, CUSTOM_OPTIONS},
2215+
&enableLockOptimization,
2216+
false,
2217+
NULL, NULL, NULL
2218+
},
2219+
22122220
/* End-of-list marker */
22132221
{
22142222
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL

src/include/parser/analyze.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "parser/parse_node.h"
1818
#include "utils/queryjumble.h"
1919

20+
/* GUC parameter */
21+
extern PGDLLIMPORT bool enableLockOptimization;
22+
2023
/* Hook for plugins to get control at end of parse analysis */
2124
typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
2225
Query *query,
@@ -44,7 +47,7 @@ extern void CheckSelectLocking(Query *qry, LockClauseStrength strength);
4447
extern void applyLockingClause(Query *qry, Index rtindex,
4548
LockClauseStrength strength,
4649
LockWaitPolicy waitPolicy, bool pushedDown);
47-
50+
extern bool checkCanOptSelectLockingClause(SelectStmt *stmt);
4851
extern List *BuildOnConflictExcludedTargetlist(Relation targetrel,
4952
Index exclRelIndex);
5053

src/include/utils/unsync_guc_name.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"enable_incremental_sort",
118118
"enable_indexonlyscan",
119119
"enable_indexscan",
120+
"enable_lock_optimization",
120121
"enable_material",
121122
"enable_memoize",
122123
"enable_mergejoin",

src/test/regress/expected/rangefuncs_cdb.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ name not in ('enable_parallel',
1919
enable_incremental_sort | off
2020
enable_indexonlyscan | on
2121
enable_indexscan | on
22+
enable_lock_optimization | off
2223
enable_material | on
2324
enable_memoize | on
2425
enable_mergejoin | off
@@ -32,7 +33,7 @@ name not in ('enable_parallel',
3233
enable_seqscan | on
3334
enable_sort | on
3435
enable_tidscan | on
35-
(22 rows)
36+
(23 rows)
3637

3738
-- start_ignore
3839
create schema rangefuncs_cdb;

src/test/regress/expected/sysviews.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ name not in ('enable_parallel',
117117
enable_incremental_sort | off
118118
enable_indexonlyscan | on
119119
enable_indexscan | on
120+
enable_lock_optimization | off
120121
enable_material | on
121122
enable_memoize | on
122123
enable_mergejoin | off
@@ -130,7 +131,7 @@ name not in ('enable_parallel',
130131
enable_seqscan | on
131132
enable_sort | on
132133
enable_tidscan | on
133-
(22 rows)
134+
(23 rows)
134135

135136
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
136137
-- more-or-less working. We can't test their contents in any great detail

src/test/singlenode_regress/expected/sysviews.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ name not in ('enable_parallel',
116116
enable_incremental_sort | off
117117
enable_indexonlyscan | on
118118
enable_indexscan | on
119+
enable_lock_optimization | off
119120
enable_material | on
120121
enable_memoize | on
121122
enable_mergejoin | off
@@ -129,7 +130,7 @@ name not in ('enable_parallel',
129130
enable_seqscan | on
130131
enable_sort | on
131132
enable_tidscan | on
132-
(22 rows)
133+
(23 rows)
133134

134135
-- Test that the pg_timezone_names and pg_timezone_abbrevs views are
135136
-- more-or-less working. We can't test their contents in any great detail

0 commit comments

Comments
 (0)