The one thing to understand first
Every SELECT takes a lock on the tables it reads — a gentle AccessShareLock that doesn’t block anyone. But if every one of thousands of concurrent queries had to register that lock in a single shared lock table, the table’s internal locks would themselves become the bottleneck. PostgreSQL’s answer is the fast-path: weak, non-conflicting relation locks are recorded privately in each backend’s own slot, never touching the shared table. Understanding it explains a whole class of partition-heavy performance cliffs.
The heavyweight lock manager
Table-level locks live in the heavyweight lock manager in src/backend/storage/lmgr/lock.c. The shared lock table holds two structures: a LOCK per locked object and a PROCLOCK per (lock, holder) pair. To reduce contention the table is split into 16 partitions by a hash of the lock tag, each guarded by its own lightweight lock (LWLock). There are eight lock modes, from AccessShareLock (taken by SELECT) up to AccessExclusiveLock (taken by most DDL), and a conflict matrix decides which modes can coexist.
LOCALLOCK: don’t even ask twice
Before any shared work, each backend checks its private LOCALLOCK hash — a per-backend <a class="sev1-termlink" href="https://thesev1database.com/glossary/tuple/" title="Tuple">record of locks it already holds and how many times. If your transaction asks for a lock it already has (very common), the request is satisfied from LOCALLOCK with no access to shared memory at all. Only a genuinely new lock proceeds further.