Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
postgresql_management_locking [2020/01/20 11:49] andonovjpostgresql_management_locking [2020/01/20 20:36] (current) – external edit 127.0.0.1
Line 2: Line 2:
 PostgreSQL is using an engine called Multiversion concurrency control or (MVCC for short). Such way of operating is used by other Databases such as: Oracle and MySQL (InnoDB). PostgreSQL is using an engine called Multiversion concurrency control or (MVCC for short). Such way of operating is used by other Databases such as: Oracle and MySQL (InnoDB).
 In a nutshell, this engine allows you to store different versions of the data at the same time in the shared buffers, depending on the consistency needs which your query must fulfill. In a nutshell, this engine allows you to store different versions of the data at the same time in the shared buffers, depending on the consistency needs which your query must fulfill.
 +======Types of Locks======
 In PostgreSQL, we have several types of locks: In PostgreSQL, we have several types of locks:
 +Firstly the locks are separated between:
 +
 +  * Table Locks
 +  * Row Locks
 +  * Advisory Locks
 +
 +Let's start with the Table Locks
 +
 +=====Table Locks=====
  
   * Access Share: Taken when person performs select.   * Access Share: Taken when person performs select.
Line 16: Line 25:
  
 {{ :postgres_table_locks.png?600 |}} {{ :postgres_table_locks.png?600 |}}
 +
 +As I already said, a part of the Table locks, there are Row locks:
 +
 +=====Row Locks=====
 +In addition to table-level locks, there are row-level locks, which are listed as below with the contexts in which they are used automatically by PostgreSQL. Note that a transaction can hold conflicting locks on the same row, even in different subtransactions; but other than that, two transactions can never hold conflicting locks on the same row. Row-level locks do not affect data querying; they block only writers and lockers to the same row.
 +
 +  * FOR UPDATE: FOR UPDATE causes the rows retrieved by the SELECT statement to be locked as though for update. This prevents them from being locked, modified or deleted by other transactions until the current transaction ends. That is, other transactions that attempt UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE or SELECT FOR KEY SHARE of these rows will be blocked until the current transaction ends; conversely, SELECT FOR UPDATE will wait for a concurrent transaction that has run any of those commands on the same row, and will then lock and return the updated row (or no row, if the row was deleted). Within a REPEATABLE READ or SERIALIZABLE transaction, however, an error will be thrown if a row to be locked has changed since the transaction started. The FOR UPDATE lock mode is also acquired by any DELETE on a row, and also by an UPDATE that modifies the values on certain columns. Currently, the set of columns considered for the UPDATE case are those that have a unique index on them that can be used in a foreign key (so partial indexes and expressional indexes are not considered), but this may change in the future.
 +
 +  * FOR NO KEY UPDATE: Behaves similarly to FOR UPDATE, except that the lock acquired is weaker: this lock will not block SELECT FOR KEY SHARE commands that attempt to acquire a lock on the same rows. This lock mode is also acquired by any UPDATE that does not acquire a FOR UPDATE lock.
 +
 +  * FOR SHARE: Behaves similarly to FOR NO KEY UPDATE, except that it acquires a shared lock rather than exclusive lock on each retrieved row. A shared lock blocks other transactions from performing UPDATE, DELETE, SELECT FOR UPDATE or SELECT FOR NO KEY UPDATE on these rows, but it does not prevent them from performing SELECT FOR SHARE or SELECT FOR KEY SHARE.
 +
 +  * FOR KEY SHARE: Behaves similarly to FOR SHARE, except that the lock is weaker: SELECT FOR UPDATE is blocked, but not SELECT FOR NO KEY UPDATE. A key-shared lock blocks other transactions from performing DELETE or any UPDATE that changes the key values, but not other UPDATE, and neither does it prevent SELECT FOR NO KEY UPDATE, SELECT FOR SHARE, or SELECT FOR KEY SHARE.
 +
 +PostgreSQL doesn't remember any information about modified rows in memory, so there is no limit on the number of rows locked at one time. However, locking a row might cause a disk write, e.g., SELECT FOR UPDATE modifies selected rows to mark them locked, and so will result in disk writes.
 +
 +You can check again the table below for compatability between the different types of Row Locks:
 +
 +{{ :postgres_row_locks.png?600 |}}
 +
 +=====Advisory Locks=====
 +PostgreSQL provides a means for creating locks that have application-defined meanings. These are called advisory locks, because the system does not enforce their use — it is up to the application to use them correctly. Advisory locks can be useful for locking strategies that are an awkward fit for the MVCC model. For example, a common use of advisory locks is to emulate pessimistic locking strategies typical of so-called "flat file" data management systems. While a flag stored in a table could be used for the same purpose, advisory locks are faster, avoid table bloat, and are automatically cleaned up by the server at the end of the session.
 +
 +======Monitoring======
 +To monitor the Locks, PostgreSQL provided us with couple views which we can use:
 +
 +====Row Locks (Blocking====
 +The following query may be helpful to see what processes are blocking SQL statements (these only find row-level locks, not object-level locks).
 +<sxh bash>
 +  SELECT blocked_locks.pid     AS blocked_pid,
 +         blocked_activity.usename  AS blocked_user,
 +         blocking_locks.pid     AS blocking_pid,
 +         blocking_activity.usename AS blocking_user,
 +         blocked_activity.query    AS blocked_statement,
 +         blocking_activity.query   AS current_statement_in_blocking_process
 +   FROM  pg_catalog.pg_locks         blocked_locks
 +    JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
 +    JOIN pg_catalog.pg_locks         blocking_locks 
 +        ON blocking_locks.locktype = blocked_locks.locktype
 +        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
 +        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
 +        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
 +        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
 +        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
 +        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
 +        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
 +        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
 +        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
 +        AND blocking_locks.pid != blocked_locks.pid
 +    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
 +</sxh>
 +
 +Setting application_name variable in the begging of each transaction allows you to which logical process blocks another one. It can be information which source code line starts transaction or any other information that helps you to match application_name to your code.
 +If you want to include the application name, use:
 +
 +
 +<sxh bash>
 +SET application_name='%your_logical_name%';
 +SELECT blocked_locks.pid     AS blocked_pid,
 +         blocked_activity.usename  AS blocked_user,
 +         blocking_locks.pid     AS blocking_pid,
 +         blocking_activity.usename AS blocking_user,
 +         blocked_activity.query    AS blocked_statement,
 +         blocking_activity.query   AS current_statement_in_blocking_process,
 +         blocked_activity.application_name AS blocked_application,
 +         blocking_activity.application_name AS blocking_application
 +   FROM  pg_catalog.pg_locks         blocked_locks
 +    JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
 +    JOIN pg_catalog.pg_locks         blocking_locks 
 +        ON blocking_locks.locktype = blocked_locks.locktype
 +        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
 +        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
 +        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
 +        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
 +        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
 +        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
 +        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
 +        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
 +        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
 +        AND blocking_locks.pid != blocked_locks.pid
 +    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
 +   WHERE NOT blocked_locks.GRANTED;
 +</sxh>
 +
 +Here's an alternate view of that same data that includes an idea how old the state is
 +<sxh bash>
 +SELECT a.datname,
 +         l.relation::regclass,
 +         l.transactionid,
 +         l.mode,
 +         l.GRANTED,
 +         a.usename,
 +         a.query,
 +         a.query_start,
 +         age(now(), a.query_start) AS "age",
 +         a.pid
 +FROM pg_stat_activity a
 +JOIN pg_locks l ON l.pid = a.pid
 +ORDER BY a.query_start;
 +</sxh>
 +
 +For PostgreSQL older than 9.0:
 +<sxh bash>
 +  SELECT a.datname,
 +         c.relname,
 +         l.transactionid,
 +         l.mode,
 +         l.GRANTED,
 +         a.usename,
 +         a.current_query, 
 +         a.query_start,
 +         age(now(), a.query_start) AS "age", 
 +         a.procpid 
 +    FROM  pg_stat_activity a
 +     JOIN pg_locks         l ON l.pid = a.procpid
 +     JOIN pg_class         c ON c.oid = l.relation
 +    ORDER BY a.query_start;
 +</sxh>
  • postgresql_management_locking.1579520960.txt.gz
  • Last modified: 2020/01/20 19:49
  • (external edit)