{"id":42323,"date":"2026-01-08T10:31:26","date_gmt":"2026-01-08T09:31:26","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=42323"},"modified":"2026-01-08T10:31:28","modified_gmt":"2026-01-08T09:31:28","slug":"postgresql-an-introduction-to-row-locking","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/","title":{"rendered":"PostgreSQL: An introduction to row locking"},"content":{"rendered":"\n<p>Sometimes it is necessary to troubleshoot locks in PostgreSQL to figure out what is going on in the system. While locks are important and necessary in a database system, too many locks can also be an issue, performance wise and resource consumption wise. There are several levels of locks, e.g. table level and row level locks. Depending on what kind of locks are causing an issue you need to take other actions to resolve it.<\/p>\n\n\n\n<p>We&#8217;ll start with some basics and a very simple playground: One simple table containing one row:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1,3]; title: ; notranslate\" title=\"\">\npostgres=# create table t (a int);\nCREATE TABLE\npostgres=# insert into t values (1);\nINSERT 0 1\n<\/pre><\/div>\n\n\n<p>One source of information about locking is <a href=\"https:\/\/www.postgresql.org\/docs\/current\/view-pg-locks.html\">pg_locks<\/a>. Do we already see something in this catalog view? Indeed we do:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1]; title: ; notranslate\" title=\"\">\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks;\n  locktype  | database | relation | pid  |      mode       | granted | waitstart \n------------+----------+----------+------+-----------------+---------+-----------\n relation   | postgres | pg_locks | 9119 | AccessShareLock | t       | \n virtualxid |          |          | 9119 | ExclusiveLock   | t       | \n(2 rows)\n<\/pre><\/div>\n\n\n<p>What we see here is our own session (ignore the second row with &#8220;virtualxid&#8221; for now):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1]; title: ; notranslate\" title=\"\">\npostgres=# select pg_backend_pid();\n pg_backend_pid \n----------------\n           9119\n(1 row)\n<\/pre><\/div>\n\n\n<p>The question is why? We only did a simple select, but even a select which does not modify the database needs locking because while we&#8217;re reading nobody else is allowed to modify the underlying objects. This is what &#8220;AccessShareLock&#8221; is about: Others can still read, but not change.<\/p>\n\n\n\n<p>If you do the same in a second session you might expect to see four rows in total, but:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2]; title: ; notranslate\" title=\"\">\n-- session 2\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks;\n  locktype  | database | relation | pid  |      mode       | granted | waitstart \n------------+----------+----------+------+-----------------+---------+-----------\n relation   | postgres | pg_locks | 9673 | AccessShareLock | t       | \n virtualxid |          |          | 9673 | ExclusiveLock   | t       | \n(2 rows)\n<\/pre><\/div>\n\n\n<p>Seems a bit surprising but pg_locks shows only locks from active processes and once the statement completes the process is not active anymore and the lock is gone.<\/p>\n\n\n\n<p>So what we can do to exclude our own locks is this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1]; title: ; notranslate\" title=\"\">\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n locktype | database | relation | pid | mode | granted | waitstart \n----------+----------+----------+-----+------+---------+-----------\n(0 rows)\n<\/pre><\/div>\n\n\n<p>The story is a bit different if we do the same thing in a transaction in the first session and then query the view in the second session:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,4,10]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# begin;\nBEGIN\npostgres=*# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n locktype | database | relation | pid | mode | granted | waitstart \n----------+----------+----------+-----+------+---------+-----------\n(0 rows)\n\n-- session 2\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n  locktype  | database | relation | pid  |      mode       | granted | waitstart \n------------+----------+----------+------+-----------------+---------+-----------\n relation   | postgres | pg_locks | 9119 | AccessShareLock | t       | \n virtualxid |          |          | 9119 | ExclusiveLock   | t       | \n(2 rows)\n<\/pre><\/div>\n\n\n<p>Because the first session is still active and the transaction is open, this session still holds the lock. Ending the transaction releases the lock:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,6]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=*# end;\nCOMMIT\n\n-- session 2\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n locktype | database | relation | pid | mode | granted | waitstart \n----------+----------+----------+-----+------+---------+-----------\n(0 rows)\n<\/pre><\/div>\n\n\n<p>Lets start doing something with our table in session two:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,4]; title: ; notranslate\" title=\"\">\n-- session 2\npostgres=# begin;\nBEGIN\npostgres=*# select * from t;\n a \n---\n 1\n(1 row)\n<\/pre><\/div>\n\n\n<p>How does it look like in session one?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1]; title: ; notranslate\" title=\"\">\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n  locktype  | database | relation | pid  |      mode       | granted | waitstart \n------------+----------+----------+------+-----------------+---------+-----------\n relation   | postgres | t        | 9673 | AccessShareLock | t       | \n virtualxid |          |          | 9673 | ExclusiveLock   | t       | \n(2 rows)\n<\/pre><\/div>\n\n\n<p>This is consistent to the results above. As the second session is reading from the table and the transaction is still open, the lock is still there, no surprise. Lets update the row in the still open transaction and then have a look again in session one:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,6]; title: ; notranslate\" title=\"\">\n-- session 2\npostgres=*# update t set a = 2 where a = 1;\nUPDATE 1\n\n-- session 1\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart from pg_locks where pid != pg_backend_pid();\n   locktype    | database | relation | pid  |       mode       | granted | waitstart \n---------------+----------+----------+------+------------------+---------+-----------\n relation      | postgres | t        | 9673 | AccessShareLock  | t       | \n relation      | postgres | t        | 9673 | RowExclusiveLock | t       | \n virtualxid    |          |          | 9673 | ExclusiveLock    | t       | \n transactionid |          |          | 9673 | ExclusiveLock    | t       | \n(4 rows)\n<\/pre><\/div>\n\n\n<p>Now we got a &#8220;RowExclusiveLock&#8221; in addition and that means nobody else can do anything with the new row\/tuple we&#8217;ve just created. When someone else (third session) is trying to perform the same update this will block:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,4]; title: ; notranslate\" title=\"\">\n-- session 3\npostgres=# begin;\nBEGIN\npostgres=*# update t set a = 2 where a = 1;\n<\/pre><\/div>\n\n\n<p>&#8230; and the results in the first session look like this (I have added an order by and the &#8220;page&#8221; and &#8220;tuple&#8221; columns):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart,page,tuple from pg_locks where pid != pg_backend_pid() order by pid;\n   locktype    | database | relation | pid  |       mode       | granted |           waitstart           | page | tuple \n---------------+----------+----------+------+------------------+---------+-------------------------------+------+-------\n relation      | postgres | t        | 9673 | RowExclusiveLock | t       |                               |      |      \n virtualxid    |          |          | 9673 | ExclusiveLock    | t       |                               |      |      \n transactionid |          |          | 9673 | ExclusiveLock    | t       |                               |      |      \n relation      | postgres | t        | 9673 | AccessShareLock  | t       |                               |      |      \n tuple         | postgres | t        | 9691 | ExclusiveLock    | t       |                               |    0 |     1\n transactionid |          |          | 9691 | ExclusiveLock    | t       |                               |      |      \n relation      | postgres | t        | 9691 | RowExclusiveLock | t       |                               |      |      \n virtualxid    |          |          | 9691 | ExclusiveLock    | t       |                               |      |      \n transactionid |          |          | 9691 | ShareLock        | f       | 2026-01-08 09:14:00.330564+01 |      |      \n(9 rows)\n<\/pre><\/div>\n\n\n<p>What we see now is, that the third session (pid=9691) is trying to get the lock but can&#8217;t and must wait (waitstart) for the second session to release the lock. We can also see that the row\/tuple in question is in block\/page 0 and the tuple\/row is the first one in that block. Once session two either commits or rollbacks the update in session three will proceed but there is nothing to update as there is no row which matches the where clause anymore.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,7,13]; title: ; notranslate\" title=\"\">\n-- session 2\npostgres=*# commit;\nCOMMIT\n\n-- session 3\nUPDATE 0\npostgres=*# select * from t;\n a \n---\n 2\n(1 row)\n\npostgres=*# end;\nCOMMIT\n<\/pre><\/div>\n\n\n<p>In session one we can verify that all locks are gone:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [1]; title: ; notranslate\" title=\"\">\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart,page,tuple from pg_locks where pid != pg_backend_pid() order by pid;\n locktype | database | relation | pid | mode | granted | waitstart | page | tuple \n----------+----------+----------+-----+------+---------+-----------+------+-------\n(0 rows)\n<\/pre><\/div>\n\n\n<p>Another example with 5 rows in the table and two sessions update only the last row:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,4,8,10,14,16,19]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# truncate t;\nTRUNCATE TABLE\npostgres=# insert into t values (1),(2),(3),(4),(5);\nINSERT 0 5\n\n-- session 2\npostgres=# begin;\nBEGIN\npostgres=*# update t set a = -1 where a = 5;\nUPDATE 1\n\n-- session 3\npostgres=# begin;\nBEGIN\npostgres=*# update t set a = -1 where a = 5;\n\n-- session 1\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart,page,tuple from pg_locks where pid != pg_backend_pid() order by pid;\n   locktype    | database | relation | pid  |       mode       | granted |           waitstart           | page | tuple \n---------------+----------+----------+------+------------------+---------+-------------------------------+------+-------\n relation      | postgres | t        | 9673 | RowExclusiveLock | t       |                               |      |      \n virtualxid    |          |          | 9673 | ExclusiveLock    | t       |                               |      |      \n transactionid |          |          | 9673 | ExclusiveLock    | t       |                               |      |      \n virtualxid    |          |          | 9691 | ExclusiveLock    | t       |                               |      |      \n transactionid |          |          | 9691 | ShareLock        | f       | 2026-01-08 09:58:38.867534+01 |      |      \n tuple         | postgres | t        | 9691 | ExclusiveLock    | t       |                               |    0 |     5\n transactionid |          |          | 9691 | ExclusiveLock    | t       |                               |      |      \n relation      | postgres | t        | 9691 | RowExclusiveLock | t       |                               |      |      \n(8 rows)\n<\/pre><\/div>\n\n\n<p>Now it is row number 5 in block 0. <\/p>\n\n\n\n<p>The rows with &#8220;transactionid&#8221; and &#8220;virtualxid&#8221; are about the transactions, details <a href=\"https:\/\/www.postgresql.org\/docs\/current\/transaction-id.html\">here<\/a>. Those values are also recorded in pg_locks:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# select locktype,database::regdatabase,relation::regclass,pid,mode,granted,waitstart,page,tuple,transactionid,virtualxid from pg_locks where pid != pg_backend_pid() order by pid;\n   locktype    | database | relation | pid  |       mode       | granted |           waitstart           | page | tuple | transactionid | virtualxid \n---------------+----------+----------+------+------------------+---------+-------------------------------+------+-------+---------------+------------\n relation      | postgres | t        | 9673 | RowExclusiveLock | t       |                               |      |       |               | \n virtualxid    |          |          | 9673 | ExclusiveLock    | t       |                               |      |       |               | 24\/22\n transactionid |          |          | 9673 | ExclusiveLock    | t       |                               |      |       |           836 | \n virtualxid    |          |          | 9691 | ExclusiveLock    | t       |                               |      |       |               | 25\/18\n transactionid |          |          | 9691 | ShareLock        | f       | 2026-01-08 09:58:38.867534+01 |      |       |           836 | \n tuple         | postgres | t        | 9691 | ExclusiveLock    | t       |                               |    0 |     5 |               | \n transactionid |          |          | 9691 | ExclusiveLock    | t       |                               |      |       |           837 | \n relation      | postgres | t        | 9691 | RowExclusiveLock | t       |                               |      |       |               | \n\n<\/pre><\/div>\n\n\n<p>When there are only a few locks in the database this is an easy way to find out who is blocking whom but if you have hundreds of sessions and locks there is a more convenient way to check for blockers and waiters, and this is the function <a href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-info.html\">pg_blocking_pids<\/a>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# select pg_blocking_pids(9691);\n pg_blocking_pids \n------------------\n {9673}\n(1 row)\n<\/pre><\/div>\n\n\n<p>Another option to check more details in regards to row locking is the extension <a href=\"https:\/\/www.postgresql.org\/docs\/current\/pgrowlocks.html\">pgrowlocks<\/a>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; highlight: [2,4]; title: ; notranslate\" title=\"\">\n-- session 1\npostgres=# create extension pgrowlocks;\nCREATE EXTENSION\npostgres=# select * from t as a, pgrowlocks(&#039;t&#039;) as b where b.locked_row = a.ctid;\n a | locked_row | locker | multi | xids  |       modes       |  pids  \n---+------------+--------+-------+-------+-------------------+--------\n 5 | (0,5)      |    836 | f     | {836} | {&quot;No Key Update&quot;} | {9673}\n(1 row)\n<\/pre><\/div>\n\n\n<p>In the next post we&#8217;ll look more closely on transaction IDs and virtual transaction IDs when it comes to locking.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes it is necessary to troubleshoot locks in PostgreSQL to figure out what is going on in the system. While locks are important and necessary in a database system, too many locks can also be an issue, performance wise and resource consumption wise. There are several levels of locks, e.g. table level and row level [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[229,198],"tags":[77],"type_dbi":[],"class_list":["post-42323","post","type-post","status-publish","format-standard","hentry","category-database-administration-monitoring","category-database-management","tag-postgresql"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>PostgreSQL: An introduction to row locking - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PostgreSQL: An introduction to row locking\" \/>\n<meta property=\"og:description\" content=\"Sometimes it is necessary to troubleshoot locks in PostgreSQL to figure out what is going on in the system. While locks are important and necessary in a database system, too many locks can also be an issue, performance wise and resource consumption wise. There are several levels of locks, e.g. table level and row level [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-01-08T09:31:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-08T09:31:28+00:00\" \/>\n<meta name=\"author\" content=\"Daniel Westermann\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@westermanndanie\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Daniel Westermann\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/\"},\"author\":{\"name\":\"Daniel Westermann\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d08e9bd996a89bd75c0286cbabf3c66\"},\"headline\":\"PostgreSQL: An introduction to row locking\",\"datePublished\":\"2026-01-08T09:31:26+00:00\",\"dateModified\":\"2026-01-08T09:31:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/\"},\"wordCount\":650,\"commentCount\":0,\"keywords\":[\"PostgreSQL\"],\"articleSection\":[\"Database Administration &amp; Monitoring\",\"Database management\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/\",\"name\":\"PostgreSQL: An introduction to row locking - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-01-08T09:31:26+00:00\",\"dateModified\":\"2026-01-08T09:31:28+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d08e9bd996a89bd75c0286cbabf3c66\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/postgresql-an-introduction-to-row-locking\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PostgreSQL: An introduction to row locking\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d08e9bd996a89bd75c0286cbabf3c66\",\"name\":\"Daniel Westermann\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g\",\"caption\":\"Daniel Westermann\"},\"description\":\"Daniel Westermann is Principal Consultant and Technology Leader Open Infrastructure at dbi services. He has more than 15 years of experience in management, engineering and optimization of databases and infrastructures, especially on Oracle and PostgreSQL. Since the beginning of his career, he has specialized in Oracle Technologies and is Oracle Certified Professional 12c and Oracle Certified Expert RAC\\\/GridInfra. Over time, Daniel has become increasingly interested in open source technologies, becoming \u201cTechnology Leader Open Infrastructure\u201d and PostgreSQL expert. \u00a0Based on community or EnterpriseDB tools, he develops and installs complex high available solutions with PostgreSQL. He is also a certified PostgreSQL Plus 9.0 Professional and a Postgres Advanced Server 9.4 Professional. He is a regular speaker at PostgreSQL conferences in Switzerland and Europe. Today Daniel is also supporting our customers on AWS services such as AWS RDS, database migrations into the cloud, EC2 and automated infrastructure management with AWS SSM (System Manager). He is a certified AWS Solutions Architect Professional. Prior to dbi services, Daniel was Management System Engineer at LC SYSTEMS-Engineering AG in Basel. Before that, he worked as Oracle Developper &amp;\u00a0Project Manager at Delta Energy Solutions AG in Basel (today Powel AG). Daniel holds a diploma in Business Informatics (DHBW, Germany). His branch-related experience mainly covers the pharma industry, the financial sector, energy, lottery and telecommunications.\",\"sameAs\":[\"https:\\\/\\\/x.com\\\/westermanndanie\"],\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/daniel-westermann\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"PostgreSQL: An introduction to row locking - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/","og_locale":"en_US","og_type":"article","og_title":"PostgreSQL: An introduction to row locking","og_description":"Sometimes it is necessary to troubleshoot locks in PostgreSQL to figure out what is going on in the system. While locks are important and necessary in a database system, too many locks can also be an issue, performance wise and resource consumption wise. There are several levels of locks, e.g. table level and row level [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/","og_site_name":"dbi Blog","article_published_time":"2026-01-08T09:31:26+00:00","article_modified_time":"2026-01-08T09:31:28+00:00","author":"Daniel Westermann","twitter_card":"summary_large_image","twitter_creator":"@westermanndanie","twitter_misc":{"Written by":"Daniel Westermann","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/"},"author":{"name":"Daniel Westermann","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66"},"headline":"PostgreSQL: An introduction to row locking","datePublished":"2026-01-08T09:31:26+00:00","dateModified":"2026-01-08T09:31:28+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/"},"wordCount":650,"commentCount":0,"keywords":["PostgreSQL"],"articleSection":["Database Administration &amp; Monitoring","Database management"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/","url":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/","name":"PostgreSQL: An introduction to row locking - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2026-01-08T09:31:26+00:00","dateModified":"2026-01-08T09:31:28+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/postgresql-an-introduction-to-row-locking\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"PostgreSQL: An introduction to row locking"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d08e9bd996a89bd75c0286cbabf3c66","name":"Daniel Westermann","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/31350ceeecb1dd8986339a29bf040d4cd3cd087d410deccd8f55234466d6c317?s=96&d=mm&r=g","caption":"Daniel Westermann"},"description":"Daniel Westermann is Principal Consultant and Technology Leader Open Infrastructure at dbi services. He has more than 15 years of experience in management, engineering and optimization of databases and infrastructures, especially on Oracle and PostgreSQL. Since the beginning of his career, he has specialized in Oracle Technologies and is Oracle Certified Professional 12c and Oracle Certified Expert RAC\/GridInfra. Over time, Daniel has become increasingly interested in open source technologies, becoming \u201cTechnology Leader Open Infrastructure\u201d and PostgreSQL expert. \u00a0Based on community or EnterpriseDB tools, he develops and installs complex high available solutions with PostgreSQL. He is also a certified PostgreSQL Plus 9.0 Professional and a Postgres Advanced Server 9.4 Professional. He is a regular speaker at PostgreSQL conferences in Switzerland and Europe. Today Daniel is also supporting our customers on AWS services such as AWS RDS, database migrations into the cloud, EC2 and automated infrastructure management with AWS SSM (System Manager). He is a certified AWS Solutions Architect Professional. Prior to dbi services, Daniel was Management System Engineer at LC SYSTEMS-Engineering AG in Basel. Before that, he worked as Oracle Developper &amp;\u00a0Project Manager at Delta Energy Solutions AG in Basel (today Powel AG). Daniel holds a diploma in Business Informatics (DHBW, Germany). His branch-related experience mainly covers the pharma industry, the financial sector, energy, lottery and telecommunications.","sameAs":["https:\/\/x.com\/westermanndanie"],"url":"https:\/\/www.dbi-services.com\/blog\/author\/daniel-westermann\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/42323","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=42323"}],"version-history":[{"count":19,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/42323\/revisions"}],"predecessor-version":[{"id":42343,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/42323\/revisions\/42343"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=42323"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=42323"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=42323"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=42323"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}