跪拜 Guibai
← All articles
Interview · Java · Database

Sharding a 100-Million-Row Order Table Without Breaking Merchant Queries

By 吃饱了得干活 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Most sharding tutorials stop at splitting by user ID, but a production order system breaks the moment a seller, CSR, or internal tool queries without that key. This design shows the full dual-path architecture—MySQL for buyers, Elasticsearch for merchants—that keeps both sides fast without scanning every shard.

Summary

When an e-commerce order table hits hundreds of millions of rows, a simple user query can take 12 seconds. Sharding by `user_id` with a hash-modulo strategy solves the buyer side, but the real trouble starts when a merchant needs to list their store's orders without a user ID to route by. The answer is physical isolation: keep the primary MySQL cluster for buyer lookups and build a separate query path for merchants.

The merchant path uses Canal to stream binlog changes into an Elasticsearch index keyed by `seller_id`, turning multi-second cross-shard scans into millisecond searches. For teams that cannot add ES, a lightweight heterogeneous index table sharded by `seller_id` works, backed by RocketMQ transactional messages for consistency. A modified Snowflake algorithm embeds 12 bits of the user ID into every order number, so a lookup by order ID can reverse-engineer the exact shard without broadcasting to every database.

Migration from a single database uses a dual-write, gradual-cutover pattern with DataX for historical backfill. The design caps shard counts at powers of two to make future doubling expansions cheap, and it bans deep pagination and cross-database JOINs outright to keep latency predictable.

Takeaways
Shard by `user_id` using hash-modulo; size databases and tables as powers of two so future doubling expansions require minimal data movement.
Embed 12 bits of the user ID into a modified Snowflake order ID so a lookup by order number can compute the shard directly, avoiding a broadcast query across all tables.
Never let a merchant query scan every shard; stream order data via Canal into an Elasticsearch index keyed by `seller_id` for sub-millisecond complex searches.
A lighter alternative is a separate MySQL index table sharded by `seller_id`, kept consistent with RocketMQ transactional messages and limited to hot data from the last six months.
Push report-style aggregation queries to an offline columnar store like ClickHouse; the online database should serve only simple, recent-page lookups.
Ban `LIMIT`-based deep pagination; enforce a 100-page cap, use cursor-based pagination, and default queries to the last three months.
Migrate from a single database with a dual-write phase, DataX for historical backfill, and gradual traffic cutover to avoid downtime.
Conclusions

Sharding is not a storage problem—it is a connection-count and DDL-pain problem. Modern SSDs let a single table run fine past 10 million rows, but an `ALTER TABLE` on a 50-million-row table locks out writes for an unacceptable window.

The merchant-query blind spot is the real interview filter. Anyone can recite modulo sharding; recognizing that a seller has no `user_id` and therefore needs a physically separate query path separates a book answer from production experience.

Embedding routing information into the ID itself is a quiet but powerful pattern. It turns a distributed-lookup problem into a local computation, eliminating the need for a separate mapping service or global scan.

Deep pagination is a universal trap across MySQL and Elasticsearch. The fix is not technical but product-level: cap the page depth, switch to cursors, and scope queries to recent data by default.

Concepts & terms
Gene Method (基因法)
A technique that embeds part of the user ID into a Snowflake-generated order ID. When querying by order number, the system extracts these bits to compute the exact database shard, avoiding a full scan of all shards.
Canal
An open-source Alibaba tool that pretends to be a MySQL replica, consumes the binary log, and streams row-level changes to other systems like Elasticsearch or Kafka for real-time data synchronization.
Hash-Modulo Sharding
A data distribution strategy where a hash of the shard key (e.g., user ID) is taken modulo the number of shards to determine where a row lives. It provides even distribution but makes resharding expensive unless shard counts are powers of two.
Dual-Write Migration
A zero-downtime migration pattern where new writes go to both the old single database and the new sharded cluster simultaneously, while a batch job backfills historical data, followed by a gradual cutover of read traffic.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗