跪拜 Guibai
← All articles
Backend

Database Standards That Keep Large Chinese Tech Teams From Breaking Production

By 苏三说技术 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Schema mistakes compound silently—a table that works fine at 100k rows becomes a liability at 5 million, and renaming a column in production means touching every service that references it. These rules encode the hard-won lessons of teams that run databases through Singles' Day traffic spikes, and they translate directly to any team whose data will outlive its initial feature.

Summary

Database tables are the least changeable layer in a stack—once a field name or type ships, every downstream consumer is locked in. This set of conventions, drawn from Alibaba and ByteDance practice, treats every DDL as a permanent decision. Naming rules mandate all-lowercase singular table names, `is_xxx` boolean columns backed by unsigned tinyint, and decimal for any monetary value. Every table gets `id`, `create_time`, and `update_time`; without the timestamp, post-mortems are blind. Index design caps at five per table, enforces unique indexes on business-unique columns, and bans function calls on indexed columns—one varchar-to-int implicit conversion on a 20-million-row table turned an 8-second query into a full scan. SQL rules forbid SELECT *, limit JOINs to three tables, and push computation to the application layer. ORM mapping requires stripping the `is` prefix from Java POJO boolean fields to avoid serialization failures. The piece also contrasts Alibaba’s conservative, mandatory-heavy style with ByteDance’s more flexible, recommendation-driven approach, noting that both converge on the same goal: making schema decisions that survive years of traffic growth without a rewrite.

Takeaways
Table and field names must be all lowercase with underscores; MySQL’s case sensitivity differs between Windows and Linux, so mixed case creates silent divergence.
Boolean columns use the `is_xxx` naming pattern with `unsigned tinyint`, where 1 means yes and 0 means no.
Any field storing money, exchange rates, or other precise decimals must use `decimal`; `float` and `double` introduce rounding errors that break reconciliation.
Every table requires three columns: `id` (bigint unsigned auto-increment), `create_time`, and `update_time`—without `update_time`, data changes are untraceable.
Single tables should stay under five indexes; each additional index slows writes because all indexes update on every row change.
Composite indexes follow the leftmost prefix rule: the highest-selectivity column goes first, and queries that skip the leading column won’t hit the index.
Applying a function or implicit type conversion to an indexed column invalidates the index; a varchar status column queried with an integer literal triggers a full table scan.
SELECT * is banned—explicit column lists enable covering indexes and reduce network and memory waste.
JOINs across more than three tables are prohibited; split the query and assemble results in application code instead.
Java POJO boolean fields must drop the `is` prefix (e.g., `deleted`, not `isDeleted`) and map via resultMap to avoid serialization and RPC failures.
Logical deletion with an `is_deleted` flag preserves audit trails but breaks natural unique keys, requiring separate handling.
Sharding is recommended when a single table exceeds 5 million rows or 2GB, but not if the table won’t reach those thresholds within three years.
Conclusions

The gap between Alibaba’s mandatory-heavy rules and ByteDance’s recommendation-heavy style reflects a real trade-off: stability-obsessed organizations encode rules as hard gates, while fast-iteration cultures treat them as defaults that teams can override with justification. Neither is wrong, but picking the wrong style for your team’s risk tolerance creates friction.

Implicit type conversion is the most insidious index killer because it produces no error—the query just silently degrades to a full scan. The 20-million-row table that took 8 seconds because an int was passed to a varchar column is a failure mode that code review alone rarely catches.

The `is_xxx` database column vs. no-`is`-prefix Java POJO rule exposes a leaky abstraction between persistence and application layers. It’s a small mapping detail that breaks serialization in frameworks like Dubbo and Jackson, and most teams only discover it in production.

Mandating `update_time` on every table is less about auditing and more about operational survivability: when a data corruption incident happens at 3 a.m., the first question is always ‘when did this change,’ and without that column the answer requires restoring backups and diffing.

Concepts & terms
Covering index
An index that contains all columns requested by a query, allowing MySQL to satisfy the query entirely from the index without reading the actual table rows. SELECT * defeats this optimization because it requests columns not in the index.
Leftmost prefix principle
A rule for composite indexes in MySQL: the index can only be used for lookups if the query’s WHERE clause references columns starting from the leftmost column of the index definition. A composite index on (A, B, C) can serve queries on A, or A+B, or A+B+C, but not B or C alone.
Implicit type conversion
When MySQL automatically converts a value’s data type to match a column’s type during comparison—for example, converting the integer 1 to the string '1' when comparing against a varchar column. This conversion prevents index usage and forces a full table scan.
Logical deletion
A soft-delete pattern where rows are marked as deleted via a flag column (e.g., is_deleted = 1) rather than physically removed. Preserves data for audit and recovery but requires every query to filter on the flag and complicates unique constraints.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗