Uber Engineering · Kho dữ liệu chuyến điUber Engineering · Trip Datastore
Schemaless
Cách Uber lưu trữ lượng chuyến đi khổng lồ trên MySQL thông thường — chia thành hàng nghìn phần, sao chép qua nhiều trung tâm dữ liệu, và không bao giờ ghi đè.How Uber stores a planet's worth of trips on plain MySQL — split thousands of ways, copied across data centers, and never overwritten.
Một cuốn sổ là không đủOne notebook breaks
Hãy hình dung mọi chuyến đi Uber ghi trong một cuốn sổ. Khi lớn lên, ba vấn đề xảy ra:Picture every Uber trip written in a single notebook. Three things go wrong as it grows:
Hết chỗRuns out of room
Một máy chạm giới hạn dung lượng và tốc độ.One machine hits its limit on space and speed.
Điểm chết đơn lẻSingle point of failure
Lỡ đổ cà phê là mất sạch chuyến đi.Spill coffee once and every trip is gone.
Ghi đè lẫn nhauWriters collide
Hai lần sửa cùng một dòng cùng lúc gây loạn.Two edits to the same line at once cause chaos.
Cell: dữ liệu không bao giờ ghi đèThe cell: data you never overwrite
Đơn vị nhỏ nhất là cell — ghi chú không sửa được, tìm bằng ba khoá. Để “cập nhật”, bạn không xoá; bạn xếp chồng phiên bản mới với ref_key lớn hơn. Mới nhất thắng, cũ vẫn còn.The smallest unit is a cell — an immutable note found by three keys. To “update”, you don't erase; you stack a new version with a higher ref_key. Newest wins, old ones stay.
Thử xem. Mỗi thao tác thêm một phiên bản mới — không gì phía trên biến mất.Try it. Each action appends a new version — nothing above ever disappears.
Cùng row_key + column_name, ref_key cao hơn = phiên bản mới. Vì lệnh ghi chỉ thêm vào, gửi lại một lệnh là vô hại — nhờ vậy Schemaless retry an toàn (idempotent).Same row_key + column_name, higher ref_key = a new version. Because writes only ever add, re-sending one is harmless — that's why Schemaless can safely retry (idempotent).
Worker định tuyến, storage lưu giữWorkers route, storage keeps
Hai loại máy chủ, tách riêng để mỗi loại tự mở rộng. Worker định tuyến request; storage giữ dữ liệu gồm một master và hai minion ở các trung tâm dữ liệu khác. Tắt master để xem cơ chế dự phòng.Two server types, separated so each scales alone. Workers route requests; storage holds data as one master plus two minions in other data centers. Flip the master off to see the failover.
4096 ngăn kéo, mỗi khoá một chỗ4096 drawers, one home per key
Dữ liệu chia thành 4096 shard. Một row_key luôn băm về cùng một shard, nên dữ liệu của một chuyến đi nằm cùng chỗ. Nhập một khoá và định tuyến:Data is split into 4096 shards. A row_key always hashes to the same shard, so one trip's data stays together. Type a key and route it:
Buffered writes: không mất gìBuffered writes: lose nothing
MySQL sao chép sang bản dự phòng sau khi báo “đã lưu”. Nếu master sập sau đó, lệnh ghi mất. Cách khắc phục: ghi vào hai cluster; chỉ báo thành công khi cả hai cùng thành công.MySQL copies to backups after saying “saved”. If the master then dies, the write is lost. The fix: write to two clusters; report success only if both work.
Bên trong một shard: bảng entityInside one shard: the entity table
Mỗi cell là một hàng MySQL. Trường body linh hoạt là phần “schemaless” — client tự quyết hình dạng dữ liệu, MySQL không đổi.Every cell is one MySQL row. The flexible body blob is the “schemaless” part — the client controls the shape, MySQL never changes.
| CộtColumn | KiểuType | Mục đíchPurpose |
|---|---|---|
| added_id | int auto-inc | Khoá chính. Khiến MySQL ghi hàng tuần tự trên đĩa (nhanh); cũng là con trỏ theo thứ tự.Primary key. Makes MySQL write rows linearly on disk (fast); also an ordered pointer. |
| row_key | uuid | Đối tượng nào — vd: một chuyến đi.Which thing — e.g. a trip. |
| column_name | string | Trường nào.Which field. |
| ref_key | int | Phiên bản nào (cao hơn = mới hơn).Which version (higher = newer). |
| body | blob | Nội dung JSON, được nén (MessagePack + ZLib).The JSON content, compressed (MessagePack + ZLib). |
| created_at | datetime | Thời điểm cell được chèn.When the cell was inserted. |
Khi nào nên dùngWhen to reach for it
Rất phù hợpGreat fits
- Bản ghi chuyến đi / đơn hàngTrip / order records — lượng ghi lớn, cần lịch sử, không được mất dữ liệu.high write volume, needs history, must never lose data.
- Nhật ký sự kiện & auditEvent logs & audit trails — append-only tạo ra log có thứ tự tự nhiên.append-only gives a natural ordered log.
- Hồ sơ / cài đặt người dùngUser profiles / settings — JSON linh hoạt thích nghi khi trường thay đổi.flexible JSON adapts as fields change.
- Bản ghi kiểu tài chínhFinancial-style records — versioning + buffered writes = độ bền cao.versioning + buffered writes = strong durability.
Đánh đổi phải chấp nhậnTrade-offs to accept
- Dung lượng tăngStorage grows — phiên bản cũ được giữ, không xoá.old versions are kept, not deleted.
- Minion có thể cũMinions can be stale — nhân bản bất đồng bộ trễ dưới một giây.async replication lags ~subsecond.
- Dữ liệu đệm chưa đọc đượcBuffered data isn't readable yet — đã lưu khi sập, nhưng chờ cluster chính hồi phục.saved on crash, but not until the primary recovers.
- Không hợp join phức tạpNot for complex joins — truy vấn quan hệ ad-hoc hợp RDBMS truyền thống hơn.ad-hoc relational queries fit a classic RDBMS better.