惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

Blog — PlanetScale

Keeping a Postgres queue healthy — PlanetScale Patterns for Postgres Traffic Control — PlanetScale Graceful degradation in Postgres — PlanetScale High memory usage in Postgres is good, actually — PlanetScale Stripe Projects partnership: Provision PlanetScale Postgres and MySQL databases from the Stripe CLI — PlanetScale Enhanced tagging in Postgres Query Insights — PlanetScale Behind the scenes: How Database Traffic Control works — PlanetScale Introducing Database Traffic Control — PlanetScale Scaling Postgres connections with PgBouncer — PlanetScale Drizzle joins PlanetScale — PlanetScale Video Conferencing with Postgres — PlanetScale Faster PlanetScale Postgres connections with Cloudflare Hyperdrive — PlanetScale Introducing the PlanetScale MCP server — PlanetScale Database Transactions — PlanetScale Automating our changelog with Cursor commands — PlanetScale Postgres 18 is now available — PlanetScale Using MotherDuck with PlanetScale — PlanetScale $50 PlanetScale Metal is GA for Postgres — PlanetScale AI-Powered Postgres index suggestions — PlanetScale $5 PlanetScale is live — PlanetScale Announcing Vitess 23 — PlanetScale $50 PlanetScale Metal — PlanetScale Report on our investigation of the 2025-10-20 incident in AWS us-east-1 — PlanetScale $5 PlanetScale — PlanetScale Benchmarking Postgres 17 vs 18 — PlanetScale Larger than RAM Vector Indexes for Relational Databases — PlanetScale Partnering with Cloudflare to bring you the fastest globally distributed applications — PlanetScale Processes and Threads — PlanetScale PlanetScale for Postgres is now GA — PlanetScale Postgres High Availability with CDC — PlanetScale Announcing Neki — PlanetScale Caching — PlanetScale The principles of extreme fault tolerance — PlanetScale Announcing PlanetScale for Postgres — PlanetScale Benchmarking Postgres — PlanetScale Announcing Vitess 22 — PlanetScale The Real Failure Rate of EBS — PlanetScale IO devices and latency — PlanetScale Announcing PlanetScale Metal — PlanetScale PlanetScale Metal: There’s no replacement for displacement — PlanetScale Upgrading Query Insights to Metal — PlanetScale Automating cherry-picks between OSS and private forks — PlanetScale Database Sharding — PlanetScale Anatomy of a Throttler, part 3 — PlanetScale Introducing sharding on PlanetScale with workflows — PlanetScale Announcing Vitess 21 — PlanetScale Announcing the PlanetScale vectors public beta — PlanetScale Anatomy of a Throttler, part 2 — PlanetScale Instant deploy requests — PlanetScale Anatomy of a Throttler, part 1 — PlanetScale Increase IOPS and throughput with sharding — PlanetScale Tracking index usage with Insights — PlanetScale Faster backups with sharding — PlanetScale Building data pipelines with Vitess — PlanetScale The State of Online Schema Migrations in MySQL — PlanetScale Optimizing aggregation in the Vitess query planner — PlanetScale Dealing with large tables — PlanetScale Announcing Vitess 20 — PlanetScale Self-managed Vitess vs Managed Vitess with PlanetScale — PlanetScale The MySQL adaptive hash index — PlanetScale Introducing global replica credentials — PlanetScale Profiling memory usage in MySQL — PlanetScale Summer 2023: Fuzzing Vitess at PlanetScale — PlanetScale How PlanetScale makes schema changes — PlanetScale Identifying and profiling problematic MySQL queries — PlanetScale The Problem with Using a UUID Primary Key in MySQL — PlanetScale Announcing Vitess 19 — PlanetScale PlanetScale forever — PlanetScale Introducing schema recommendations — PlanetScale Amazon Aurora Pricing: The many surprising costs of running an Aurora database — PlanetScale Three common MySQL database design mistakes — PlanetScale OAuth applications are now available to everyone — PlanetScale Deprecating the Scaler plan — PlanetScale PlanetScale branching vs. Amazon Aurora blue/green deployments — PlanetScale Databases at scale — PlanetScale Considerations for building a database disaster recovery plan — PlanetScale Working with Geospatial Features in MySQL — PlanetScale PlanetScale vs Amazon Aurora replication — PlanetScale Introducing the Vantage and PlanetScale integration — PlanetScale MySQL isolation levels and how they work — PlanetScale Introducing the schemadiff command line tool — PlanetScale $ pscale ping — PlanetScale Announcing foreign key constraints support — PlanetScale The challenges of supporting foreign key constraints — PlanetScale What is HTAP? — PlanetScale Introducing Insights Anomalies — PlanetScale Webhook security: a hands-on guide — PlanetScale Announcing the Fivetran integration — PlanetScale Introducing webhooks — PlanetScale What is MySQL replication and when should you use it? — PlanetScale Sync user data between Clerk and a PlanetScale MySQL database — PlanetScale Introducing database reports — PlanetScale Distributed caching systems and MySQL — PlanetScale What is MySQL partitioning? — PlanetScale MySQL High Availability: Connection handling and concurrency — PlanetScale Personalizing your onboarding with Markdoc — PlanetScale PlanetScale vs. Amazon Aurora — PlanetScale PlanetScale vs. Amazon RDS — PlanetScale PlanetScale is bringing vector search and storage to MySQL — PlanetScale PlanetScale Managed is now PCI compliant — PlanetScale
Achieving data consistency with the consistent lookup Vindex — PlanetScale
2024-04-29 · via Blog — PlanetScale

Harshit Gangal, Deepthi Sigireddi |

What are Vindexes

Vitess uses Vindexes (short for Vitess Index) to associate rows in a table with a designated address known as Keyspace ID. This allows Vitess to direct a row to its intended destination, typically a shard within the cluster.

Vindexes play a dual role: enabling data sharding through Primary Vindexes and facilitating global indexing via Secondary Vindexes. Through this mechanism, Vindexes serve as an indispensable tool for routing queries in a sharded database, ensuring optimal performance and scalability.

Lookup Vindex

Lookup Vindex as a Secondary Vindex is used to direct Select/Update/Delete queries to the appropriate shard without incurring the performance penalty associated with scatter-gather operations—wherein the query is sent to all shards for processing.

When data is inserted into a table, a separate global index table maintains the mapping of a secondary index column to the corresponding Keyspace ID. This mapping information is later used to efficiently route queries to the destination shard.

Secondary Vindexes can be unique or non-unique, and we’ll illustrate both types. Let us look at an example to see how this works.

Data table definition

USER Table

+-------+--------------+------+-----+
| Field | Type         | Null | Key |
+-------+--------------+------+-----+
| id    | bigint       | NO   | PRI |
| name  | varchar(255) | YES  |     |
| phone | bigint       | YES  | UNI |
| email | varchar(255) | YES  |     |
+-------+--------------+------+-----+

Non unique Vindex table definition

NAME_USER_VDX Table

+-------------+--------------+------+-----+
| Field       | Type         | Null | Key |
+-------------+--------------+------+-----+
| name        | varchar(255) | NO   | PRI |
| id          | bigint       | NO   | PRI |
| keyspace_id | binary(8)    | YES  |     |
+-------------+--------------+------+-----+

Unique Vindex table definition

PHONE_USER_VDX Table

+-------------+-----------+------+-----+
| Field       | Type      | Null | Key |
+-------------+-----------+------+-----+
| phone       | bigint    | NO   | PRI |
| keyspace_id | binary(8) | YES  |     |
+-------------+-----------+------+-----+

When executing a query like select id, phone, email from user where name = 'Alex', the query planner uses the lookup Vindex table, name_user_vdx, to map the value Alex to its corresponding Keyspace ID. This lets the planner direct the query to a single destination shard rather than to all shards, thus avoiding a costly scatter-gather operation.

Of particular interest is the Consistent Lookup Vindex, a type of Secondary Vindex, which further enhances the efficiency and reliability of this routing mechanism.

Consistent lookup Vindex

The user data table and lookup Vindex tables are both sharded in most cases to enable optimal performance and storage. The sharding column for the user table and the Vindex tables are likely to be different. In the scenario above, let's consider the sharding columns to be:

TablePrimary Vindex Column
Userid
Name_User_Vdxname
Phone_User_Vdxphone

Changing data in the user table through DML statements (Insert/Update/Delete) leads to changes to rows in the Vindex tables as well. To maintain consistency between the user data table and the Vindex tables, all these operations will need to occur in a transaction that spans multiple shards. This means we need to implement a costly protocol like 2PC (Two Phase Commit) to guarantee Atomicity and Isolation (A and I from ACID). Not using a proper multi-shard transaction for these operations can lead to partial commit and inconsistent data.

Consistent Lookup Vindex uses an alternate approach that makes use of careful locking and transaction sequences to guarantee consistency without using 2PC for all DML operations. This allows Vitess to provide a consistent view of the user data table even when record in the Vindex tables may be inconsistent.

When data is being modified, Vitess uses 3 connections to perform DML operations. Let’s call them Pre, Main and Post. Any transaction open on these connections will follow a well-defined sequence of operations. Committing a transaction will result in the following:

  • Commit on Pre
  • Commit on Main
  • Commit on Post

A failure in any of these steps rolls back the remaining open transactions in the same order.

Let’s look at an example to see how this works.

Sample rows

USER:
+-----+------+------------+-----------------+
| id  | name | phone      | email           |
+-----+------+------------+-----------------+
| 100 | Alex | 8877991122 | alex@mail.com   |
| 200 | Emma | 8811229988 | emma@mail.com   |
+-----+------+------------+-----------------+

NAME_USER_VDX:
+------+-----+--------------------------+
| name | id  | keyspace_id              |
+------+-----+--------------------------+
| Alex | 100 | 0x313030                 |
| Emma | 200 | 0x323030                 |
+------+-----+--------------------------+

PHONE_USER_VDX:
+------------+--------------------------+
| phone      | keyspace_id              |
+------------+--------------------------+
| 8811229988 | 0x323030                 |
| 8877991122 | 0x313030                 |
+------------+--------------------------+

Delete operation

Deletion of Lookup Vindex table data happens through the Post connection.

Example: delete from user where id = 100

  1. First select all the lookup columns from the User Table

    Main: select id, name, phone from user where id = 100 for update

  2. Delete the Lookup Vindex Rows

    Post-Transaction:

    1. delete from name_user_vdx where name = 'Alex' and id = 100
    2. delete from phone_user_vdx where phone = 8877991122
  3. Delete the User Table Row

    Main: delete from user where id = 100

On Commit, suppose the Main transaction succeeds but the Post transaction fails. Let’s see how we are still able to maintain consistency.

Updated rows

USER:
+-----+------+------------+-----------------+
| id  | name | phone      | email           |
+-----+------+------------+-----------------+
| 200 | Emma | 8811229988 | emma@mail.com   |
+-----+------+------------+-----------------+

NAME_USER_VDX:
+------+-----+--------------------------+
| name | id  | keyspace_id              |
+------+-----+--------------------------+
| Alex | 100 | 0x313030                 |
| Emma | 200 | 0x323030                 |
+------+-----+--------------------------+

PHONE_USER_VDX:
+------------+--------------------------+
| phone      | keyspace_id              |
+------------+--------------------------+
| 8811229988 | 0x323030                 |
| 8877991122 | 0x313030                 |
+------------+--------------------------+

If a select query is received select count(*) from user where name = 'Alex'

A lookup call will happen with name = 'Alex' to the name_user_vdx Vindex which will return the shard destination with keyspace_id of 0x313030. When the query is sent down to the specific shard a matching row does not exist in the User table any longer and hence will return no results.

+----------+
| count(*) |
+----------+
|        0 |
+----------+

The lookup Vindex table may be inconsistent with the User table but the results returned for the query remained consistent with the User table.

Insert operation

Insertion of Lookup Vindex table data happens through the Pre connection.

Example: insert into user(id, name, phone, email) values (300, 'Emma', 8877991122, 'xyz@mail.com')

  1. Insert into Lookup Vindex table Pre-Transaction:
    1. insert into name_user_vdx(name, id, keyspace_id) values ('Emma', 300, '0x333030') No error as name is a non-unique column.
    2. insert into phone_user_vdx(phone, keyspace_id) values (8877991122, '0x333030') This results in a duplicate key error as it is a unique column. Note that this row is left over from the error we got during the previous delete operation. We’ll get into the details of how this is handled in a minute.
  2. Insert the User table Row Main: insert into user(id, name, phone, email) values (300, 'Emma', 8877991122, 'xyz@mail.com')

Handling of Duplicate Key Error in Lookup Vindex:

  1. Lock the lookup row so that no other transaction can race with the current operation. Pre-Transaction: select phone, keyspace_id from phone_user_vdx where phone = 8877991122 for update
  2. Lock the main table row to ensure that the row we want to insert does not exist yet and no other transaction can race with the current operation.
    1. Main: select phone from user where phone = 8877991122 for update Because we previously deleted the corresponding row for this select, it will return no results. This tells us that the lookup Vindex table has an orphan row which can be updated with the new value from the insert statement.
    2. Pre-Transaction: update phone_user_vdx set keyspace_id = ‘0x333030’ where phone = 8877991122

Updated rows

USER:
+-----+------+------------+-----------------+
| id  | name | phone      | email           |
+-----+------+------------+-----------------+
| 200 | Emma | 8811229988 | emma@mail.com   |
| 300 | Emma | 8877991122 | xyz@mail.com    |
+-----+------+------------+-----------------+

NAME_USER_VDX:
+------+-----+--------------------------+
| name | id  | keyspace_id              |
+------+-----+--------------------------+
| Alex | 100 | 0x313030                 |
| Emma | 200 | 0x323030                 |
| Emma | 300 | 0x333030                 |
+------+-----+--------------------------+

PHONE_USER_VDX:
+------------+--------------------------+
| phone      | keyspace_id              |
+------------+--------------------------+
| 8811229988 | 0x323030                 |
| 8877991122 | 0x333030                 |
+------------+--------------------------+

Update operation

Update of Lookup Vindex table data happens through a Delete operation followed by an Insert operation. We already know that Delete operation is handled through Post connection and Insert operation through Pre connection.

In the special case of an update where the Vindex column value is unchanged, it will cause lock wait timeout on the Insert operation (on the Pre connection) as the row lock will be held by the Delete operation (on the Post connection). To mitigate this, updating Vindex column data with the same value as before is turned into a no-op for lookup Vindex tables.

However, it is still possible to run into this limitation if the same lookup Vindex value is deleted and inserted as two different statements inside the same transaction.

If you want to learn more about this feature in Vitess, check out the Vitess documentation. They have docs on Vindexes, Unique and Non-Unique lookup vindexes. You are also welcome to join the vitess community.