
By Yichang, Zanye, Xiahua
PolarDB-X is a distributed database built on a unified centralized-and-distributed architecture. To handle hybrid workloads flexibly, each Data Node (DN) — the data storage layer — employs multiple data structures, with a row store providing online transaction processing (OLTP) capability. As a database 100% compatible with the MySQL ecosystem, the DN builds on InnoDB's storage structures with deep optimizations that significantly improve data access efficiency.

In OLTP workloads, the unique key (UK) index is one of the most common constraint mechanisms. Business tables rely on UK indexes to enforce uniqueness of critical fields such as order IDs, phone numbers, IDs, and transaction serial numbers — a cornerstone of data integrity. However, InnoDB's current MVCC implementation for UK indexes introduces two categories of problems under high concurrency:
Both problems trace back to the same root cause: design limitations in InnoDB's multi-version management of UK indexes. To address this, the PolarDB-X DN storage engine introduces Panda Index — a new-generation UK index structure that grants UK indexes native multi-version capability, fundamentally eliminating the issues of inflated lock scope and table-lookup-dependent visibility checks.
To understand the problems with UK indexes, we first need to understand how MVCC stores historical versions. Mainstream TP databases use two broad paradigms for storing historical versions of primary table data:
| PostgreSQL: append-only storage | InnoDB/Oracle: rollback segment storage | |
|---|---|---|
| Approach | Multi-version physical append: historical versions stay in place, new versions are inserted at new locations | In-place update: the latest version directly overwrites the historical version |
| Historical version location | Data tablespace | Separate undo tablespace |
| Historical version record format | Complete record: each version is a self-describing full tuple | Delta patch: only the diff of the modified record is stored |
| Historical version space management | Space tends to bloat: relies on the vacuum mechanism to scan the data tablespace for cleanup | Space is more compact: undo segments can be reused cyclically, and purge can perform precise cleanup far more efficiently |
| Historical version reads | Fast reads: directly reads the complete tuple with no extra I/O | Slow reads: requires extra undo page I/O and applying deltas to reconstruct the full tuple |
| Impact of updates on indexes | Significant write amplification: the new version's physical location changes, so all indexes must be updated (when HOT updates are not triggered) | Stable write behavior: primary table data is updated in place, and logical index pointers remain unchanged |
InnoDB's clustered index uses the rollback segment approach: each record contains a TRX_ID (transaction ID) and a ROLL_PTR (pointer to the undo chain). On modification, InnoDB first writes an undo log entry, then updates the record in place. Historical versions are traced through the undo chain, and the B+ tree always retains only the latest version — a clean and efficient structure.

InnoDB clustered index version chain diagram
However, InnoDB's secondary indexes — including UK indexes — do not use the same version management strategy as the clustered index. Secondary index records carry no TRX_ID or ROLL_PTR, no version information, and no undo chain. When a UK record needs to be updated, InnoDB uses a delete-mark-then-insert approach: it first marks the old record as deleted, then inserts a new record.
This means that on the UK index's B+ tree, multiple physical records with the same unique key value can coexist simultaneously — a delete-marked old version and a new version. Until the purge thread cleans them up, these historical versions remain on the index structure.

InnoDB secondary index multi-version storage diagram
This "multi-version physical append" approach creates problems on three levels:
In InnoDB's locking protocol, to enforce the uniqueness constraint (insert conflict detection) on a multi-version-appended UK index, the system must place gap locks on multiple physical version records sharing the same key value.
Consider this scenario: transaction A deletes the record with uk_col = 10, leaving a delete-marked record on the B+ tree. Transaction B then attempts to insert uk_col = 10 and must check for conflicts. Because the index may contain multiple physical records with the same UK value (the current version and deleted-but-not-yet-purged versions), insert conflict detection must scan all these records and place gap locks on the corresponding intervals.
The result: transactions that are logically non-conflicting end up waiting on — or even deadlocking with — each other due to overlapping lock ranges. This not only reduces system concurrency throughput but also introduces significant unpredictability into application-side SQL logic. The problem is especially acute in high-concurrency UK update scenarios such as trading systems, balance deductions, and flash sales.
Because InnoDB secondary indexes carry no version information, an index-only scan cannot independently determine visibility. Even when all queried columns are present in the UK index (theoretically eligible for a covering index), MVCC queries must still go back to the clustered index records and their undo chains to check version information before determining whether a record is visible to the current transaction.
Additionally, implicit lock detection and purge paths also require table lookups to obtain transaction information. These extra table lookup operations place unnecessary pressure on the buffer pool and I/O resources.
Delete-marked UK records must wait for the purge thread to clean them up asynchronously. In write-intensive workloads, purge may fall behind the rate of deletions, causing delete-marked records to accumulate on the UK index. These residual records not only waste storage space but also expand the scan range during subsequent insert conflict detection, further exacerbating lock contention and latency.
The core idea behind Panda Index is to push the clustered index's native multi-version capability down into the UK index.
Specifically, Panda Index augments the traditional UK index record with the clustered index's transaction information columns and version chain, giving the UK index native multi-version capability.
This design introduces two fundamental changes:

Comparison of Panda Index vs. standard UK index
Panda Index adds four system columns to the traditional UK record:
| System column | Description |
|---|---|
| TRX_ID | Transaction ID of the most recent modification, used for transaction state checks |
| ROLL_PTR | Pointer to Panda Index's own independent undo record, used to trace historical versions |
| SCN | Lizard transaction system commit sequence number, used for visibility checks |
| UBA | Undo block address (transaction slot address), used for visibility checks |
The semantics of TRX_ID, SCN, and UBA are consistent with those in clustered index records, allowing Panda Index to reuse the Lizard transaction system's existing visibility determination framework (Vision). The ROLL_PTR, however, points to Panda Index's own independent undo records — a chain completely separate from the clustered index's undo chain — through which all historical versions of the current record can be retrieved.

Panda Index record format
With ROLL_PTR on Panda Index records, updates no longer need the delete-mark-then-insert approach to retain historical versions on the data page. The update mechanism now aligns with that of the clustered index:
This "multi-version physical append" approach creates problems on three levels:
• The B+ tree index structure retains only the current version. There are no residual delete-marked records to impose extra costs on record scanning and locking.
• All historical versions are stored in a separate undo tablespace, managed by the purge system. Any historical version can be easily retrieved through ROLL_PTR without table lookups.
In-place update capability yields an important derivative benefit: only one physical record can exist for any given unique key on the index.
This dramatically simplifies insert conflict detection logic:
• Traditional UK: Must scan all index records that might share the same unique key (including delete-marked ones) and place gap locks on the entire interval to prevent concurrent transactions from inserting duplicate keys.
• Panda Index: If no record with the same key value exists in the index, the insert succeeds immediately. If one exists, only a record lock is placed on that single record, and the system further checks whether the record is delete-marked to determine if there is a true conflict.
The entire process requires no gap locks, shrinks the lock scope, and significantly reduces deadlock probability in high-concurrency UK operations.
Panda Index has a completely independent undo log system that does not share an undo chain with the clustered index. This means Panda Index has self-driven, independent processing logic on the following critical paths:
• Forward DML: Each modification to a Panda Index record independently writes an undo log entry, separate from the clustered index's undo writes.
• Rollback: On transaction rollback, Panda Index's undo records are applied independently. The rollback logic does not depend on the clustered index.
• Purge: The background purge thread has an independent cleanup path for Panda Index's delete-marked records, verifying the record version through ROLL_PTR before performing physical deletion.
The independent undo system eliminates the coupling between the UK index and the clustered index in version management, making Panda Index's version lifecycle management fully self-contained.
Panda Index records carry TRX_ID, SCN, and UBA transaction information directly, enabling visibility checks to be performed locally on the index without table lookups.
Similar to the Lizard transaction system's clustered index path, Panda Index's consistent read path works as follows:
The entire process completes entirely within the Panda Index B+ tree, requiring no table lookup operations. This enables covering index queries on Panda Index to achieve true index-only scans, and paths such as implicit lock detection no longer need to access the clustered index.
A simple concurrent scenario demonstrates the effect of Panda Index's gap lock elimination.
CREATE TABLE t1(
id int,
c1 int,
PRIMARY KEY(id),
UNIQUE KEY uk1(c1)
);
INSERT INTO t1 VALUES (1, 1);
INSERT INTO t1 VALUES (100, 100);
Session 1 performs a delete-then-insert operation (transaction uncommitted):
BEGIN;
DELETE FROM t1 WHERE id = 1;
INSERT INTO t1 VALUES (2, 1);
Session 2 inserts a non-conflicting record that falls within the gap:
INSERT INTO t1 VALUES (3, 2);
Standard UK behavior: Session 2's insert times out waiting for Session 1's gap lock. Inspecting lock information reveals multiple gap locks on the UK:
SELECT lock_data, lock_mode FROM performance_schema.data_locks WHERE index_name = 'uk1';
+-----------+---------------+
| lock_data | lock_mode |
+-----------+---------------+
| 1, 1 | X,REC_NOT_GAP |
| 1, 1 | S,GAP |
| 100, 100 | S,GAP |
| 1, 2 | S,GAP |
+-----------+---------------+
Panda Index behavior: Session 2's insert succeeds immediately, because Panda Index does not produce unnecessary gap locks:
SELECT lock_data, lock_mode FROM performance_schema.data_locks WHERE index_name = 'uk1';
+-----------+---------------+
| lock_data | lock_mode |
+-----------+---------------+
| 1, 2 | X,REC_NOT_GAP |
+-----------+---------------+
ApsaraDB - March 26, 2025
ApsaraDB - October 16, 2024
ApsaraDB - April 10, 2024
ApsaraDB - February 10, 2026
ApsaraDB - July 1, 2024
ApsaraDB - November 7, 2024
PolarDB for MySQL
Alibaba Cloud PolarDB for MySQL is a cloud-native relational database service 100% compatible with MySQL.
Learn More
PolarDB for Xscale
Alibaba Cloud PolarDB for Xscale (PolarDB-X) is a cloud-native high-performance distributed database service independently developed by Alibaba Cloud.
Learn More
LedgerDB
A ledger database that provides powerful data audit capabilities.
Learn More
ApsaraDB for OceanBase
A financial-grade distributed relational database that features high stability, high scalability, and high performance.
Learn MoreMore Posts by ApsaraDB