← Files PlanetScaleARCHIVED FILE
skills/database-vitess/references/vschema.md
5.24 KB · Oct 4, 2026 · 12:25 UTC
---
title: VSchema Design and Configuration
description: VSchema config guide
tags: vitess, vschema, vindexes, sharding, sequences, lookup-vindexes
---
# VSchema Design and Configuration
## Contents
- [VSchema structure](#vschema-structure)
- [Vindexes](#vindexes)
- [Lookup vindexes](#lookup-vindexes)
- [Sequences](#sequences)
- [Discovering existing VSchema](#discovering-existing-vschema)
- [Sharding guidelines](#sharding-guidelines)
- [Advanced properties](#advanced-properties)
- [Troubleshooting scatter queries](#troubleshooting-scatter-queries)
The VSchema (Vitess Schema) tells VTGate how to route queries. It defines how tables map to keyspaces/shards, which columns determine shard placement (vindexes), and how tables relate across shards.
Reference: https://vitess.io/docs/23.0/user-guides/vschema-guide/
## VSchema structure
```json
{ "sharded": true, "vindexes": { ... }, "tables": { ... } }
```
For unsharded keyspaces: `{ "tables": { "product": {}, "my_seq": { "type": "sequence" } } }`
## Vindexes
A **vindex** maps a column value to a keyspace ID (determines shard placement). Every sharded table needs a **Primary Vindex** which must be unique and is immutable after insert.
| Vindex Type | Use For |
| --- | --- |
| `xxhash` | Any column type (most common) |
| `unicode_loose_xxhash` | Text columns needing case-insensitive hashing |
| `binary_md5` | Any column type (MD5-based alternative) |
**Choosing a primary vindex column**: pick the column most used in high-QPS WHERE clauses, that enables join co-location (tables joined frequently should shard on the same column), keeps transactions single-shard, and has high cardinality for even distribution.
### Example
```json
{
"sharded": true,
"vindexes": { "xxhash": { "type": "xxhash" } },
"tables": {
"customer": { "column_vindexes": [{ "column": "customer_id", "name": "xxhash" }] },
"orders": { "column_vindexes": [{ "column": "customer_id", "name": "xxhash" }] }
}
}
```
Both tables shard on `customer_id` (**shared vindex**), so rows with the same `customer_id` land on the same shard, enabling single-shard joins and transactions.
## Lookup vindexes
Provide secondary routing to avoid scatter queries on non-primary-vindex columns. Backed by a separate lookup table mapping column values to keyspace IDs. **Lookup vindexes are expensive** — consider schema redesign or alternative access patterns before using.
```json
"customer_email_lookup": {
"type": "consistent_lookup",
"params": { "table": "product.customer_email_lookup", "from": "email", "to": "keyspace_id" },
"owner": "customer"
}
```
Use `consistent_lookup` (or `consistent_lookup_unique` if strictly needed, though database-level uniqueness enforcement is a scalability anti-pattern). The `owner` table maintains the lookup. Backfill existing data with `vtctldclient LookupVindex create ...` (see `vtctldclient LookupVindex --help` for required args).
## Sequences
Replace MySQL `AUTO_INCREMENT` for sharded tables (per-shard auto-increment produces duplicates). A sequence is a single-row table in an **unsharded** keyspace.
```sql
CREATE TABLE customer_seq (id BIGINT, next_id BIGINT, cache BIGINT, PRIMARY KEY (id)) COMMENT 'vitess_sequence';
INSERT INTO customer_seq (id, next_id, cache) VALUES (0, 1, 1000);
```
Register in unsharded VSchema: `{ "customer_seq": { "type": "sequence" } }`
Link to sharded table:
```json
"customer": {
"column_vindexes": [{ "column": "customer_id", "name": "xxhash" }],
"auto_increment": { "column": "customer_id", "sequence": "product.customer_seq" }
}
```
Sequence gaps from caching/restarts are expected and harmless.
## Discovering existing VSchema
Retrieve the current VSchema for a keyspace via CLI or SQL:
```bash
# Full VSchema JSON for a keyspace
vtctldclient GetVSchema <keyspace>
# List all vindexes defined in a keyspace
vtctldclient GetVSchema <keyspace> | jq '.vindexes'
```
```sql
-- From a VTGate MySQL session
SHOW VSCHEMA TABLES; -- list tables known to the VSchema
SHOW VSCHEMA VINDEXES; -- list vindexes and their types
SHOW CREATE TABLE <table>; -- includes vindex column info in comments
```
Use `SHOW VSCHEMA TABLES` to quickly confirm whether a table is recognized by VTGate routing. Use `GetVSchema` for the full JSON when you need to inspect vindex params, sequences, or advanced properties.
## Sharding guidelines
Optimal shard size depends on hardware (CPUs, RAM, disk I/O) and workload characteristics — there is no universal number. Highest-QPS query's WHERE clause dictates primary vindex. Co-locate joined tables; keep transactions local. For multi-tenant apps, use multi-column vindexes. `MoveTables` can change sharding keys later.
## Advanced properties
`auto_increment` (link to sequence), `type: "reference"` (copied to all shards), `pinned` (pin to shard), `column_list_authoritative` (planner only trusts columns explicitly listed in VSchema).
## Troubleshooting scatter queries
Check: is WHERE filtering on primary vindex? Is a lookup vindex configured for that column? Use `VEXPLAIN PLAN` to see routing. For deeper performance debugging, use `VEXPLAIN ALL` to include MySQL query plans and `VEXPLAIN TRACE` to see metrics on how many rows are passed between parts of the query. Primary vindex column updates are blocked; use `MoveTables` to re-shard.
SHA-256: d8eb7d5a2261bbfd1925ac6693598aeb24ab9ab7724e0cede5e9cc1df9d57bf7