10 - 电商系统设计
| > 面试频率: 需求类型 | 指标 |
|---|---|
| 用户规模 | 5000 万注册用户,500 万日活 |
| 商品规模 | 1000 万 SKU |
| 订单量 | 100 万单/天,大促 1000 万单/天 |
| QPS | 正常 10 万,大促 50 万 |
| 响应时间 | P99 < 500ms |
| 可用性 | 99.99% |
| 一致性 | 强一致性(库存、订单) |
1.3 面试官可能的追问
Q1: 电商系统和其他系统有什么不同?
A1:
- 复杂度高:涉及多个子系统(商品、订单、库存、支付)
- 一致性要求:库存不能超卖,订单不能丢失
- 高并发:大促场景 QPS 极高
- 业务规则复杂:促销、优惠券、分销等
Q2: 如何防止超卖?
A2:
- 数据库行锁:
SELECT ... FOR UPDATE - 乐观锁:version 版本号
- Redis 原子操作:DECR
- 预扣库存:下单时预扣,支付后确认
2. 容量估算
2.1 场景假设
- 日活用户(DAU):500 万
- 日订单量:100 万
- 平均客单价:200 元
- 日 GMV:2 亿元
- 大促(双十一):10 倍流量
2.2 QPS 估算
正常流量
商品浏览:500 万 × 20 次/天 = 1 亿次
浏览 QPS = 1 亿 / 86400 ≈ 1,157 QPS
下单:100 万 / 86400 ≈ 11.6 QPS
峰值 = 11.6 × 5 ≈ 58 QPS
大促流量
商品浏览 QPS = 1,157 × 10 = 11,570 QPS
下单 QPS = 58 × 100 = 5,800 QPS(前 1 小时)
2.3 存储估算
商品表
SKU 数量 = 1000 万
单条记录 = 2 KB
总存储 = 1000 万 × 2 KB = 20 GB
订单表
日订单 = 100 万
单条记录 = 1 KB
日存储 = 100 万 × 1 KB = 1 GB
年存储 = 1 GB × 365 = 365 GB
保留 3 年 = 365 GB × 3 ≈ 1.1 TB
订单商品表(订单详情)
平均每单 3 个商品
日订单商品 = 100 万 × 3 = 300 万
单条记录 = 500 字节
日存储 = 300 万 × 500 字节 = 1.5 GB
年存储 = 1.5 GB × 365 ≈ 548 GB
3 年 ≈ 1.6 TB
总存储需求:约 3 TB(3 年数据)
2.4 数据库估算
分库分表
订单表按用户 ID 分片:
- 分 256 个表
- 每表 = 100 万 × 365 × 3 / 256 ≈ 427 万条
分 32 个数据库:
- 每库 8 个表
3. API 设计
3.1 核心 API
3.1.1 商品列表
GET /api/v1/products
Request:
{
"category_id": 101,
"keyword": "手机",
"page": 1,
"page_size": 20,
"sort": "sales", // sales | price_asc | price_desc
"filters": {
"price_min": 1000,
"price_max": 5000,
"brand_ids": [1, 2, 3]
}
}
Response:
{
"code": 0,
"message": "success",
"data": {
"total": 1523,
"items": [
{
"product_id": 10001,
"spu_id": 1001,
"sku_id": 100101,
"title": "iPhone 15 Pro 256GB 黑色",
"price": 7999.00,
"original_price": 8999.00,
"stock": 1000,
"sales": 5234,
"image_url": "https://cdn.example.com/product.jpg",
"tags": ["热销", "新品"]
}
]
}
}
3.1.2 添加购物车
POST /api/v1/cart/items
Request:
{
"sku_id": 100101,
"quantity": 1
}
Response:
{
"code": 0,
"message": "success",
"data": {
"cart_item_id": 123456,
"total_count": 3,
"total_amount": 15998.00
}
}
3.1.3 创建订单
POST /api/v1/orders
Request:
{
"address_id": 5001,
"cart_item_ids": [123456, 123457],
"coupon_id": 2001,
"remark": "尽快发货",
"payment_method": "wechat"
}
Response:
{
"code": 0,
"message": "success",
"data": {
"order_id": "ORD20231113001",
"order_sn": "202311130001234567",
"total_amount": 7999.00,
"discount_amount": 100.00,
"final_amount": 7899.00,
"status": "unpaid",
"payment_url": "weixin://wxpay/...",
"expires_at": "2023-11-13T15:00:00Z"
}
}
3.1.4 支付订单
POST /api/v1/orders/{order_id}/pay
Request:
{
"payment_method": "wechat",
"payment_password": "******"
}
Response:
{
"code": 0,
"message": "success",
"data": {
"payment_order_id": "PAY20231113001",
"status": "paying",
"qr_code": "weixin://wxpay/..."
}
}
3.1.5 查询订单
GET /api/v1/orders/{order_id}
Response:
{
"code": 0,
"message": "success",
"data": {
"order_id": "ORD20231113001",
"order_sn": "202311130001234567",
"status": "paid",
"user_id": 12345,
"total_amount": 7999.00,
"final_amount": 7899.00,
"created_at": "2023-11-13T14:30:00Z",
"paid_at": "2023-11-13T14:35:00Z",
"items": [
{
"sku_id": 100101,
"title": "iPhone 15 Pro",
"price": 7999.00,
"quantity": 1,
"image_url": "https://cdn.example.com/product.jpg"
}
],
"address": {
"receiver": "张三",
"phone": "13800138000",
"address": "北京市朝阳区..."
},
"logistics": {
"company": "顺丰速运",
"tracking_number": "SF1234567890",
"status": "运输中"
}
}
}
4. 数据模型设计
4.1 商品表(SPU/SKU)
-- SPU(Standard Product Unit):标准化产品单元
CREATE TABLE products_spu (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
spu_id BIGINT NOT NULL UNIQUE,
title VARCHAR(256) NOT NULL,
category_id INT NOT NULL,
brand_id INT,
description TEXT,
detail_html TEXT,
status VARCHAR(20) DEFAULT 'active' COMMENT 'active|inactive',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_category (category_id),
INDEX idx_brand (brand_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品SPU表';
-- SKU(Stock Keeping Unit):库存单位
CREATE TABLE products_sku (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
sku_id BIGINT NOT NULL UNIQUE,
spu_id BIGINT NOT NULL,
title VARCHAR(256) NOT NULL,
-- 规格(JSON)
specs JSON COMMENT '{"颜色": "黑色", "内存": "256GB"}',
-- 价格
price DECIMAL(10, 2) NOT NULL,
original_price DECIMAL(10, 2),
-- 库存
stock INT DEFAULT 0,
-- 销量
sales INT DEFAULT 0,
-- 图片
image_url VARCHAR(512),
status VARCHAR(20) DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_spu (spu_id),
INDEX idx_price (price),
INDEX idx_sales (sales)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品SKU表';
4.2 购物车表
CREATE TABLE shopping_cart (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
sku_id BIGINT NOT NULL,
quantity INT NOT NULL DEFAULT 1,
-- 是否选中
selected BOOLEAN DEFAULT TRUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_sku (user_id, sku_id),
INDEX idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购物车表';
4.3 订单表
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id VARCHAR(64) NOT NULL UNIQUE,
order_sn VARCHAR(64) NOT NULL UNIQUE COMMENT '订单号(对外显示)',
user_id BIGINT NOT NULL,
-- 订单状态
status VARCHAR(20) NOT NULL COMMENT 'unpaid|paid|shipped|received|completed|cancelled',
-- 金额
total_amount DECIMAL(10, 2) NOT NULL COMMENT '商品总金额',
discount_amount DECIMAL(10, 2) DEFAULT 0 COMMENT '优惠金额',
shipping_amount DECIMAL(10, 2) DEFAULT 0 COMMENT '运费',
final_amount DECIMAL(10, 2) NOT NULL COMMENT '实付金额',
-- 收货地址
address_id BIGINT,
receiver_name VARCHAR(50),
receiver_phone VARCHAR(20),
receiver_address VARCHAR(512),
-- 优惠券
coupon_id BIGINT,
-- 备注
remark TEXT,
-- 支付信息
payment_method VARCHAR(20) COMMENT 'wechat|alipay|balance',
payment_order_id VARCHAR(64) COMMENT '支付订单号',
paid_at DATETIME,
-- 物流信息
logistics_company VARCHAR(50),
logistics_number VARCHAR(100),
shipped_at DATETIME,
-- 完成时间
received_at DATETIME,
completed_at DATETIME,
-- 取消
cancelled_at DATETIME,
cancel_reason TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_status (user_id, status),
INDEX idx_status_created (status, created_at),
INDEX idx_order_sn (order_sn)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
4.4 订单商品表
CREATE TABLE order_items (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id VARCHAR(64) NOT NULL,
sku_id BIGINT NOT NULL,
spu_id BIGINT NOT NULL,
title VARCHAR(256) NOT NULL,
image_url VARCHAR(512),
specs JSON,
price DECIMAL(10, 2) NOT NULL COMMENT '下单时价格',
quantity INT NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_order (order_id),
INDEX idx_sku (sku_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单商品表';
4.5 库存表
CREATE TABLE inventory (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
sku_id BIGINT NOT NULL UNIQUE,
-- 可用库存
available_stock INT NOT NULL DEFAULT 0,
-- 预占库存(已下单未支付)
locked_stock INT NOT NULL DEFAULT 0,
-- 总库存
total_stock INT NOT NULL DEFAULT 0,
-- 版本号(乐观锁)
version INT NOT NULL DEFAULT 0,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_sku (sku_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表';
4.6 库存流水表
CREATE TABLE inventory_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
log_id VARCHAR(64) NOT NULL UNIQUE,
sku_id BIGINT NOT NULL,
-- 操作类型
operation_type VARCHAR(20) NOT NULL COMMENT 'lock|unlock|deduct|return',
-- 数量
quantity INT NOT NULL,
-- 前后库存
before_stock INT NOT NULL,
after_stock INT NOT NULL,
-- 业务信息
business_type VARCHAR(50) COMMENT 'order|refund',
business_id VARCHAR(64),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_sku (sku_id),
INDEX idx_business (business_type, business_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表';
5. 架构设计
5.1 整体架构
┌─────────────────────────────────────────────────────────────┐
│ 用户端 │
│ (Web / App / 小程序) │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ API Gateway │
│ (限流、鉴权、路由、熔断) │
└─────────────┬───────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ 业务服务层 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 商品服务 │ │ 购物车 │ │ 订单服务 │ │ 库存服务 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 促销服务 │ │ 支付服务 │ │ 物流服务 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ 数据层 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ MySQL │ │ Redis │ │ Kafka/MQ │ │
│ │ (分库分表) │ │ (缓存) │ │ (消息队列) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
5.2 V1: 单体架构(MVP)
package main
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
)
// ==================== 数据结构 ====================
// Product 商品
type Product struct {
SkuID int64 `json:"sku_id"`
Title string `json:"title"`
Price float64 `json:"price"`
Stock int `json:"stock"`
Sales int `json:"sales"`
ImageURL string `json:"image_url"`
}
// CartItem 购物车项
type CartItem struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
SkuID int64 `json:"sku_id"`
Quantity int `json:"quantity"`
Selected bool `json:"selected"`
}
// Order 订单
type Order struct {
OrderID string `json:"order_id"`
OrderSn string `json:"order_sn"`
UserID int64 `json:"user_id"`
Status string `json:"status"`
TotalAmount float64 `json:"total_amount"`
DiscountAmount float64 `json:"discount_amount"`
FinalAmount float64 `json:"final_amount"`
CreatedAt time.Time `json:"created_at"`
}
// OrderItem 订单商品
type OrderItem struct {
OrderID string `json:"order_id"`
SkuID int64 `json:"sku_id"`
Title string `json:"title"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
TotalAmount float64 `json:"total_amount"`
}
// ==================== 商品服务 ====================
type ProductService struct {
db *sql.DB
}
// GetProducts 获取商品列表
func (s *ProductService) GetProducts(categoryID int, page, pageSize int) ([]*Product, error) {
offset := (page - 1) * pageSize
rows, err := s.db.Query(`
SELECT sku_id, title, price, stock, sales, image_url
FROM products_sku
WHERE status = 'active'
ORDER BY sales DESC
LIMIT ? OFFSET ?
`, pageSize, offset)
if err != nil {
return nil, err
}
defer rows.Close()
products := make([]*Product, 0)
for rows.Next() {
var p Product
rows.Scan(&p.SkuID, &p.Title, &p.Price, &p.Stock, &p.Sales, &p.ImageURL)
products = append(products, &p)
}
return products, nil
}
// GetProduct 获取商品详情
func (s *ProductService) GetProduct(skuID int64) (*Product, error) {
var p Product
err := s.db.QueryRow(`
SELECT sku_id, title, price, stock, sales, image_url
FROM products_sku
WHERE sku_id = ? AND status = 'active'
`, skuID).Scan(&p.SkuID, &p.Title, &p.Price, &p.Stock, &p.Sales, &p.ImageURL)
return &p, err
}
// ==================== 购物车服务 ====================
type CartService struct {
db *sql.DB
}
// AddToCart 添加到购物车
func (s *CartService) AddToCart(userID, skuID int64, quantity int) error {
// 检查商品是否存在
var stock int
err := s.db.QueryRow(`SELECT stock FROM products_sku WHERE sku_id = ?`, skuID).Scan(&stock)
if err != nil {
return fmt.Errorf("商品不存在")
}
if stock < quantity {
return fmt.Errorf("库存不足")
}
// 插入或更新购物车
_, err = s.db.Exec(`
INSERT INTO shopping_cart (user_id, sku_id, quantity)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE quantity = quantity + ?
`, userID, skuID, quantity, quantity)
return err
}
// GetCartItems 获取购物车列表
func (s *CartService) GetCartItems(userID int64) ([]*CartItem, error) {
rows, err := s.db.Query(`
SELECT id, user_id, sku_id, quantity, selected
FROM shopping_cart
WHERE user_id = ?
`, userID)
if err != nil {
return nil, err
}
defer rows.Close()
items := make([]*CartItem, 0)
for rows.Next() {
var item CartItem
rows.Scan(&item.ID, &item.UserID, &item.SkuID, &item.Quantity, &item.Selected)
items = append(items, &item)
}
return items, nil
}
// ==================== 订单服务 ====================
type OrderService struct {
db *sql.DB
inventoryService *InventoryService
}
// CreateOrder 创建订单
func (s *OrderService) CreateOrder(userID int64, cartItemIDs []int64, addressID int64) (*Order, error) {
// 1. 开启事务
tx, err := s.db.Begin()
if err != nil {
return nil, err
}
defer tx.Rollback()
// 2. 查询购物车商品
items, err := s.getCartItemsByIDs(tx, cartItemIDs)
if err != nil {
return nil, err
}
// 3. 检查并锁定库存
for _, item := range items {
if err := s.inventoryService.LockStock(tx, item.SkuID, item.Quantity); err != nil {
return nil, fmt.Errorf("库存不足: %w", err)
}
}
// 4. 计算金额
totalAmount := 0.0
for _, item := range items {
var price float64
tx.QueryRow(`SELECT price FROM products_sku WHERE sku_id = ?`, item.SkuID).Scan(&price)
totalAmount += price * float64(item.Quantity)
}
// 5. 创建订单
orderID := generateOrderID()
orderSn := generateOrderSn()
_, err = tx.Exec(`
INSERT INTO orders (
order_id, order_sn, user_id, status,
total_amount, final_amount, address_id
) VALUES (?, ?, ?, 'unpaid', ?, ?, ?)
`, orderID, orderSn, userID, totalAmount, totalAmount, addressID)
if err != nil {
return nil, err
}
// 6. 创建订单商品
for _, item := range items {
var title string
var price float64
tx.QueryRow(`SELECT title, price FROM products_sku WHERE sku_id = ?`, item.SkuID).
Scan(&title, &price)
_, err = tx.Exec(`
INSERT INTO order_items (
order_id, sku_id, title, price, quantity, total_amount
) VALUES (?, ?, ?, ?, ?, ?)
`, orderID, item.SkuID, title, price, item.Quantity, price*float64(item.Quantity))
if err != nil {
return nil, err
}
}
// 7. 删除购物车
_, err = tx.Exec(`DELETE FROM shopping_cart WHERE id IN (?)`, cartItemIDs)
if err != nil {
return nil, err
}
// 8. 提交事务
if err = tx.Commit(); err != nil {
return nil, err
}
// 9. 设置订单超时(15分钟未支付自动取消)
go s.scheduleOrderTimeout(orderID, 15*time.Minute)
order := &Order{
OrderID: orderID,
OrderSn: orderSn,
UserID: userID,
Status: "unpaid",
TotalAmount: totalAmount,
FinalAmount: totalAmount,
CreatedAt: time.Now(),
}
return order, nil
}
// PayOrder 支付订单
func (s *OrderService) PayOrder(orderID string, paymentMethod string) error {
// 1. 开启事务
tx, err := s.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// 2. 查询订单
var status string
err = tx.QueryRow(`SELECT status FROM orders WHERE order_id = ? FOR UPDATE`, orderID).Scan(&status)
if err != nil {
return fmt.Errorf("订单不存在")
}
if status != "unpaid" {
return fmt.Errorf("订单状态异常: %s", status)
}
// 3. 扣减库存(从预占转为实际扣减)
items, err := s.getOrderItems(tx, orderID)
if err != nil {
return err
}
for _, item := range items {
if err := s.inventoryService.DeductStock(tx, item.SkuID, item.Quantity); err != nil {
return err
}
}
// 4. 更新订单状态
_, err = tx.Exec(`
UPDATE orders
SET status = 'paid', paid_at = NOW()
WHERE order_id = ?
`, orderID)
if err != nil {
return err
}
// 5. 提交事务
return tx.Commit()
}
// CancelOrder 取消订单
func (s *OrderService) CancelOrder(orderID string, reason string) error {
tx, err := s.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// 1. 查询订单
var status string
tx.QueryRow(`SELECT status FROM orders WHERE order_id = ?`, orderID).Scan(&status)
if status == "paid" || status == "shipped" {
return fmt.Errorf("订单不能取消")
}
// 2. 释放库存
items, _ := s.getOrderItems(tx, orderID)
for _, item := range items {
s.inventoryService.UnlockStock(tx, item.SkuID, item.Quantity)
}
// 3. 更新订单状态
_, err = tx.Exec(`
UPDATE orders
SET status = 'cancelled', cancelled_at = NOW(), cancel_reason = ?
WHERE order_id = ?
`, reason, orderID)
if err != nil {
return err
}
return tx.Commit()
}
// scheduleOrderTimeout 订单超时处理
func (s *OrderService) scheduleOrderTimeout(orderID string, timeout time.Duration) {
time.Sleep(timeout)
// 查询订单状态
var status string
s.db.QueryRow(`SELECT status FROM orders WHERE order_id = ?`, orderID).Scan(&status)
if status == "unpaid" {
s.CancelOrder(orderID, "超时未支付")
}
}
func (s *OrderService) getCartItemsByIDs(tx *sql.Tx, ids []int64) ([]*CartItem, error) {
// 简化实现
return nil, nil
}
func (s *OrderService) getOrderItems(tx *sql.Tx, orderID string) ([]*OrderItem, error) {
rows, err := tx.Query(`
SELECT order_id, sku_id, title, price, quantity, total_amount
FROM order_items
WHERE order_id = ?
`, orderID)
if err != nil {
return nil, err
}
defer rows.Close()
items := make([]*OrderItem, 0)
for rows.Next() {
var item OrderItem
rows.Scan(&item.OrderID, &item.SkuID, &item.Title, &item.Price, &item.Quantity, &item.TotalAmount)
items = append(items, &item)
}
return items, nil
}
// ==================== 库存服务 ====================
type InventoryService struct {
db *sql.DB
}
// LockStock 锁定库存(预占)
func (s *InventoryService) LockStock(tx *sql.Tx, skuID int64, quantity int) error {
// 使用乐观锁
result, err := tx.Exec(`
UPDATE inventory
SET available_stock = available_stock - ?,
locked_stock = locked_stock + ?,
version = version + 1
WHERE sku_id = ?
AND available_stock >= ?
AND version = (SELECT version FROM (SELECT version FROM inventory WHERE sku_id = ?) AS tmp)
`, quantity, quantity, skuID, quantity, skuID)
if err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
return fmt.Errorf("库存不足或版本冲突")
}
// 记录流水
s.logInventory(tx, skuID, "lock", quantity)
return nil
}
// DeductStock 扣减库存(支付后)
func (s *InventoryService) DeductStock(tx *sql.Tx, skuID int64, quantity int) error {
_, err := tx.Exec(`
UPDATE inventory
SET locked_stock = locked_stock - ?,
total_stock = total_stock - ?
WHERE sku_id = ?
`, quantity, quantity, skuID)
if err != nil {
return err
}
// 记录流水
s.logInventory(tx, skuID, "deduct", quantity)
return nil
}
// UnlockStock 解锁库存(取消订单)
func (s *InventoryService) UnlockStock(tx *sql.Tx, skuID int64, quantity int) error {
_, err := tx.Exec(`
UPDATE inventory
SET available_stock = available_stock + ?,
locked_stock = locked_stock - ?
WHERE sku_id = ?
`, quantity, quantity, skuID)
if err != nil {
return err
}
// 记录流水
s.logInventory(tx, skuID, "unlock", quantity)
return nil
}
func (s *InventoryService) logInventory(tx *sql.Tx, skuID int64, opType string, quantity int) {
logID := uuid.New().String()
tx.Exec(`
INSERT INTO inventory_logs (log_id, sku_id, operation_type, quantity, created_at)
VALUES (?, ?, ?, ?, NOW())
`, logID, skuID, opType, quantity)
}
// ==================== 辅助函数 ====================
func generateOrderID() string {
return "ORD" + time.Now().Format("20060102") + uuid.New().String()[:8]
}
func generateOrderSn() string {
return time.Now().Format("20060102150405") + fmt.Sprintf("%06d", time.Now().Nanosecond()%1000000)
}
// ==================== HTTP Handlers ====================
type EcommerceHandler struct {
productService *ProductService
cartService *CartService
orderService *OrderService
inventoryService *InventoryService
}
func (h *EcommerceHandler) GetProductsHandler(w http.ResponseWriter, r *http.Request) {
products, err := h.productService.GetProducts(0, 1, 20)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
"message": "success",
"data": products,
})
}
func (h *EcommerceHandler) AddToCartHandler(w http.ResponseWriter, r *http.Request) {
var req struct {
SkuID int64 `json:"sku_id"`
Quantity int `json:"quantity"`
}
json.NewDecoder(r.Body).Decode(&req)
userID := int64(12345) // 从 token 获取
err := h.cartService.AddToCart(userID, req.SkuID, req.Quantity)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
"message": "success",
})
}
func (h *EcommerceHandler) CreateOrderHandler(w http.ResponseWriter, r *http.Request) {
var req struct {
CartItemIDs []int64 `json:"cart_item_ids"`
AddressID int64 `json:"address_id"`
}
json.NewDecoder(r.Body).Decode(&req)
userID := int64(12345) // 从 token 获取
order, err := h.orderService.CreateOrder(userID, req.CartItemIDs, req.AddressID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
"message": "success",
"data": order,
})
}
func main() {
db, _ := sql.Open("mysql", "root:password@tcp(localhost:3306)/ecommerce")
defer db.Close()
productService := &ProductService{db: db}
cartService := &CartService{db: db}
inventoryService := &InventoryService{db: db}
orderService := &OrderService{
db: db,
inventoryService: inventoryService,
}
handler := &EcommerceHandler{
productService: productService,
cartService: cartService,
orderService: orderService,
inventoryService: inventoryService,
}
r := mux.NewRouter()
r.HandleFunc("/api/v1/products", handler.GetProductsHandler).Methods("GET")
r.HandleFunc("/api/v1/cart/items", handler.AddToCartHandler).Methods("POST")
r.HandleFunc("/api/v1/orders", handler.CreateOrderHandler).Methods("POST")
fmt.Println("电商系统启动在 :8080")
http.ListenAndServe(":8080", r)
}
V1 特点:
- 简单易实现
- 数据库事务保证一致性
- 乐观锁防止超卖
- 单体应用,无法水平扩展
- 库存高并发性能差
5.3 V2: 微服务 + 分布式事务
优化点:
- 服务拆分(商品、订单、库存独立)
- TCC 分布式事务
- Redis 库存缓存
- 消息队列异步处理
// ==================== Redis 库存缓存 ====================
package cache
import (
"context"
"fmt"
"strconv"
"github.com/go-redis/redis/v8"
)
// InventoryCacheService 库存缓存服务
type InventoryCacheService struct {
redis *redis.Client
}
// DecrStock 扣减库存(原子操作)
func (s *InventoryCacheService) DecrStock(skuID int64, quantity int) error {
ctx := context.Background()
key := fmt.Sprintf("inventory:sku:%d:stock", skuID)
// Lua 脚本保证原子性
script := redis.NewScript(`
local stock = redis.call('GET', KEYS[1])
if not stock then
return -1
end
stock = tonumber(stock)
local quantity = tonumber(ARGV[1])
if stock < quantity then
return -2
end
redis.call('DECRBY', KEYS[1], quantity)
return stock - quantity
`)
result, err := script.Run(ctx, s.redis, []string{key}, quantity).Result()
if err != nil {
return err
}
remaining := result.(int64)
if remaining == -1 {
return fmt.Errorf("库存不存在")
} else if remaining == -2 {
return fmt.Errorf("库存不足")
}
return nil
}
// SyncToRedis 同步库存到 Redis
func (s *InventoryCacheService) SyncToRedis(skuID int64, stock int) error {
ctx := context.Background()
key := fmt.Sprintf("inventory:sku:%d:stock", skuID)
return s.redis.Set(ctx, key, stock, 0).Err()
}
// ==================== 分布式锁 ====================
type DistributedLock struct {
redis *redis.Client
}
// Lock 获取锁
func (l *DistributedLock) Lock(key string, expiration time.Duration) bool {
ctx := context.Background()
result, err := l.redis.SetNX(ctx, key, "1", expiration).Result()
return err == nil && result
}
// Unlock 释放锁
func (l *DistributedLock) Unlock(key string) {
ctx := context.Background()
l.redis.Del(ctx, key)
}
// ==================== 订单创建流程(TCC)====================
type OrderTCCService struct {
orderDB *sql.DB
inventoryService *InventoryCacheService
paymentService *PaymentService
}
// CreateOrderWithTCC 使用 TCC 创建订单
func (s *OrderTCCService) CreateOrderWithTCC(
userID int64,
items []*OrderItem,
addressID int64,
) (*Order, error) {
txID := generateTxID()
// Try 阶段
if err := s.tryCreateOrder(txID, userID, items, addressID); err != nil {
s.cancelCreateOrder(txID)
return nil, err
}
// Confirm 阶段
if err := s.confirmCreateOrder(txID); err != nil {
s.cancelCreateOrder(txID)
return nil, err
}
return nil, nil
}
// tryCreateOrder Try 阶段:预占资源
func (s *OrderTCCService) tryCreateOrder(
txID string,
userID int64,
items []*OrderItem,
addressID int64,
) error {
// 1. 检查库存并预占
for _, item := range items {
if err := s.inventoryService.DecrStock(item.SkuID, item.Quantity); err != nil {
return err
}
}
// 2. 创建预订单
orderID := generateOrderID()
_, err := s.orderDB.Exec(`
INSERT INTO orders_temp (order_id, tx_id, user_id, status)
VALUES (?, ?, ?, 'trying')
`, orderID, txID, userID)
return err
}
// confirmCreateOrder Confirm 阶段:确认提交
func (s *OrderTCCService) confirmCreateOrder(txID string) error {
// 1. 将临时订单转为正式订单
_, err := s.orderDB.Exec(`
INSERT INTO orders (order_id, user_id, status)
SELECT order_id, user_id, 'unpaid'
FROM orders_temp
WHERE tx_id = ? AND status = 'trying'
`, txID)
if err != nil {
return err
}
// 2. 删除临时订单
s.orderDB.Exec(`DELETE FROM orders_temp WHERE tx_id = ?`, txID)
return nil
}
// cancelCreateOrder Cancel 阶段:回滚
func (s *OrderTCCService) cancelCreateOrder(txID string) error {
// 1. 查询临时订单
rows, err := s.orderDB.Query(`
SELECT sku_id, quantity FROM order_items_temp WHERE tx_id = ?
`, txID)
if err != nil {
return err
}
defer rows.Close()
// 2. 释放库存
for rows.Next() {
var skuID int64
var quantity int
rows.Scan(&skuID, &quantity)
// 库存回滚
s.inventoryService.redis.IncrBy(
context.Background(),
fmt.Sprintf("inventory:sku:%d:stock", skuID),
int64(quantity),
)
}
// 3. 删除临时订单
s.orderDB.Exec(`DELETE FROM orders_temp WHERE tx_id = ?`, txID)
s.orderDB.Exec(`DELETE FROM order_items_temp WHERE tx_id = ?`, txID)
return nil
}
func generateTxID() string {
return "TCC_" + uuid.New().String()
}
type PaymentService struct{}
V2 特点:
- 微服务架构,服务独立部署
- Redis 缓存库存,性能提升 10 倍
- TCC 分布式事务保证一致性
- 支持水平扩展
- ⚠️ TCC 实现复杂
- ⚠️ 需要补偿机制
5.4 V3: 最终一致性 + 消息队列
优化点:
- 本地消息表 + 定时任务
- Kafka 异步处理
- 数据库分库分表
- Elasticsearch 商品搜索
// ==================== 本地消息表方案 ====================
package eventual
import (
"encoding/json"
)
// OrderEventPublisher 订单事件发布器
type OrderEventPublisher struct {
db *sql.DB
kafka *KafkaProducer
}
// PublishOrderCreatedEvent 发布订单创建事件
func (p *OrderEventPublisher) PublishOrderCreatedEvent(order *Order) error {
// 1. 在事务中插入订单和本地消息
tx, err := p.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// 插入订单
_, err = tx.Exec(`INSERT INTO orders ...`, order)
if err != nil {
return err
}
// 插入本地消息
messageID := uuid.New().String()
messageBody, _ := json.Marshal(map[string]interface{}{
"event_type": "order_created",
"order_id": order.OrderID,
"user_id": order.UserID,
"amount": order.FinalAmount,
})
_, err = tx.Exec(`
INSERT INTO local_messages (message_id, topic, body, status)
VALUES (?, 'order-events', ?, 'pending')
`, messageID, string(messageBody))
if err != nil {
return err
}
// 2. 提交事务
if err = tx.Commit(); err != nil {
return err
}
// 3. 异步发送消息到 Kafka(定时任务处理)
return nil
}
// MessageRelayJob 消息中继任务
func (p *OrderEventPublisher) MessageRelayJob() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
p.sendPendingMessages()
}
}
func (p *OrderEventPublisher) sendPendingMessages() {
rows, _ := p.db.Query(`
SELECT message_id, topic, body
FROM local_messages
WHERE status = 'pending'
LIMIT 100
FOR UPDATE SKIP LOCKED
`)
defer rows.Close()
for rows.Next() {
var messageID, topic, body string
rows.Scan(&messageID, &topic, &body)
// 发送到 Kafka
if err := p.kafka.Send(topic, body); err != nil {
// 发送失败,更新重试次数
continue
}
// 标记为已发送
p.db.Exec(`
UPDATE local_messages SET status = 'sent' WHERE message_id = ?
`, messageID)
}
}
type KafkaProducer struct{}
func (k *KafkaProducer) Send(topic, body string) error {
return nil
}
// ==================== 商品搜索(Elasticsearch)====================
package search
import (
"context"
"encoding/json"
"github.com/elastic/go-elasticsearch/v8"
)
type ProductSearchService struct {
es *elasticsearch.Client
}
// SearchProducts 搜索商品
func (s *ProductSearchService) SearchProducts(keyword string, page, pageSize int) ([]*Product, error) {
ctx := context.Background()
// 构建搜索查询
query := map[string]interface{}{
"query": map[string]interface{}{
"multi_match": map[string]interface{}{
"query": keyword,
"fields": []string{"title^3", "description"},
},
},
"from": (page - 1) * pageSize,
"size": pageSize,
"sort": []map[string]interface{}{
{"sales": "desc"},
{"_score": "desc"},
},
}
queryJSON, _ := json.Marshal(query)
// 执行搜索
res, err := s.es.Search(
s.es.Search.WithContext(ctx),
s.es.Search.WithIndex("products"),
s.es.Search.WithBody(strings.NewReader(string(queryJSON))),
)
if err != nil {
return nil, err
}
defer res.Body.Close()
// 解析结果
var result map[string]interface{}
json.NewDecoder(res.Body).Decode(&result)
products := make([]*Product, 0)
// 解析 hits ...
return products, nil
}
// SyncProductToES 同步商品到 ES
func (s *ProductSearchService) SyncProductToES(product *Product) error {
ctx := context.Background()
body, _ := json.Marshal(product)
_, err := s.es.Index(
"products",
strings.NewReader(string(body)),
s.es.Index.WithContext(ctx),
s.es.Index.WithDocumentID(fmt.Sprintf("%d", product.SkuID)),
)
return err
}
V3 特点:
- 最终一致性,性能更高
- 消息队列解耦服务
- Elasticsearch 搜索性能好
- 分库分表支持海量数据
- ⚠️ 存在短暂不一致
- 适合大规模场景
6. 核心问题与解决方案
6.1 防止超卖
方案 1: 数据库行锁
func DecrStockWithLock(db *sql.DB, skuID int64, quantity int) error {
tx, _ := db.Begin()
defer tx.Rollback()
// 行锁
var stock int
tx.QueryRow(`SELECT stock FROM inventory WHERE sku_id = ? FOR UPDATE`, skuID).Scan(&stock)
if stock < quantity {
return fmt.Errorf("库存不足")
}
tx.Exec(`UPDATE inventory SET stock = stock - ? WHERE sku_id = ?`, quantity, skuID)
return tx.Commit()
}
方案 2: Redis 原子操作
-- Lua 脚本
local stock = redis.call('GET', KEYS[1])
if tonumber(stock) < tonumber(ARGV[1]) then
return -1
end
redis.call('DECRBY', KEYS[1], ARGV[1])
return 0
方案 3: 乐观锁
func DecrStockOptimistic(db *sql.DB, skuID int64, quantity int) error {
for i := 0; i < 3; i++ {
var stock, version int
db.QueryRow(`SELECT stock, version FROM inventory WHERE sku_id = ?`, skuID).
Scan(&stock, &version)
if stock < quantity {
return fmt.Errorf("库存不足")
}
result, _ := db.Exec(`
UPDATE inventory
SET stock = stock - ?, version = version + 1
WHERE sku_id = ? AND version = ?
`, quantity, skuID, version)
rowsAffected, _ := result.RowsAffected()
if rowsAffected > 0 {
return nil
}
// 版本冲突,重试
time.Sleep(time.Millisecond * 10)
}
return fmt.Errorf("扣减库存失败")
}
6.2 订单超时处理
方案 1: 定时任务扫描
func ScanTimeoutOrders() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
// 查询超时未支付订单
rows, _ := db.Query(`
SELECT order_id
FROM orders
WHERE status = 'unpaid'
AND created_at < DATE_SUB(NOW(), INTERVAL 15 MINUTE)
LIMIT 1000
`)
for rows.Next() {
var orderID string
rows.Scan(&orderID)
// 取消订单
CancelOrder(orderID, "超时未支付")
}
}
}
方案 2: 延迟队列
// RabbitMQ 延迟队列
func PublishOrderTimeout(orderID string, delay time.Duration) {
message := map[string]interface{}{
"order_id": orderID,
"action": "check_timeout",
}
body, _ := json.Marshal(message)
// 发送到延迟队列
channel.Publish(
"",
"order.timeout.queue",
false,
false,
amqp.Publishing{
ContentType: "application/json",
Body: body,
Expiration: fmt.Sprintf("%d", delay.Milliseconds()),
},
)
}
6.3 分库分表
// 订单分片规则:按 user_id 取模
func GetOrderTableName(userID int64) string {
tableIndex := userID % 256
return fmt.Sprintf("orders_%03d", tableIndex)
}
func GetOrderDBIndex(userID int64) int {
return int(userID / 256 % 32)
}
// 路由查询
func QueryOrder(userID int64, orderID string) (*Order, error) {
dbIndex := GetOrderDBIndex(userID)
tableName := GetOrderTableName(userID)
db := dbCluster[dbIndex]
var order Order
db.QueryRow(fmt.Sprintf(`
SELECT * FROM %s WHERE order_id = ?
`, tableName), orderID).Scan(&order)
return &order, nil
}
7. 监控与告警
var (
orderTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "order_total",
Help: "Total orders",
},
[]string{"status"},
)
orderLatency = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "order_latency_seconds",
Help: "Order creation latency",
Buckets: []float64{0.1, 0.5, 1, 2, 5},
},
[]string{"step"},
)
inventoryStock = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "inventory_stock",
Help: "Current inventory stock",
},
[]string{"sku_id"},
)
oversellCount = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "inventory_oversell_total",
Help: "Total oversell count",
},
)
)
8. 面试问答(10个高频题)
如何防止库存超卖?
答:
方案对比:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 数据库行锁 | 简单可靠 | 性能差,并发低 | 小规模 |
| Redis原子操作 | 性能高 | 需要同步到DB | 高并发 |
| 乐观锁 | 无锁,并发高 | 冲突重试 | 中等并发 |
最佳实践:
Redis 预扣库存(快速响应)
↓
订单支付成功
↓
DB 扣减库存(持久化)
↓
定时对账(确保一致)
订单状态机如何设计?
答:
unpaid(待支付)→ paid(已支付)→ shipped(已发货)→ received(已收货)→ completed(已完成)
↓ ↓
cancelled(已取消) refunding(退款中)→ refunded(已退款)
状态流转规则:
- unpaid → paid: 支付成功
- paid → cancelled: 支付后可取消(退款)
- paid → shipped: 商家发货
- shipped → received: 用户确认收货
- received → completed: 超过售后期
代码实现:
func (o *Order) CanTransitionTo(newStatus string) bool {
transitions := map[string][]string{
"unpaid": {"paid", "cancelled"},
"paid": {"shipped", "refunding"},
"shipped": {"received"},
"received": {"completed", "refunding"},
}
allowed := transitions[o.Status]
for _, s := range allowed {
if s == newStatus {
return true
}
}
return false
}
如何处理订单超时?
答:
方案1:定时任务扫描
// 每分钟扫描一次
SELECT order_id FROM orders
WHERE status = 'unpaid'
AND created_at < DATE_SUB(NOW(), INTERVAL 15 MINUTE)
优点:简单 缺点:延迟高,DB 压力大
方案2:延迟队列
- RabbitMQ 死信队列
- Redis ZSET(按时间戳排序)
- 时间轮算法
优点:实时性好 缺点:实现复杂
方案3:本地定时器
time.AfterFunc(15*time.Minute, func() {
CancelOrder(orderID)
})
优点:实时 缺点:服务重启丢失
购物车如何设计?
答:
存储方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| MySQL | 持久化,支持复杂查询 | 性能一般 |
| Redis | 性能高,天然过期 | 持久化弱 |
| Cookie/LocalStorage | 无需登录 | 容量小,不同步 |
推荐方案:
- 未登录:LocalStorage
- 已登录:Redis + MySQL
Redis 结构:
HSET cart:user:12345 sku:100101 quantity:1
HSET cart:user:12345 sku:100102 quantity:2
EXPIRE cart:user:12345 2592000 # 30天
优惠券如何设计?
答:
类型:
- 满减券:满 100 减 10
- 折扣券:9 折
- 免邮券:免运费
- 品类券:指定品类可用
核心表:
CREATE TABLE coupons (
coupon_id BIGINT PRIMARY KEY,
type VARCHAR(20), -- full_discount | percentage | free_shipping
rule JSON, -- {"min_amount": 100, "discount": 10}
total_count INT, -- 总发行量
used_count INT, -- 已使用
valid_from DATETIME,
valid_until DATETIME
);
CREATE TABLE user_coupons (
id BIGINT PRIMARY KEY,
user_id BIGINT,
coupon_id BIGINT,
status VARCHAR(20), -- unused | used | expired
used_order_id VARCHAR(64),
used_at DATETIME
);
使用流程:
- 创建订单时选择优惠券
- 检查优惠券是否可用
- 计算优惠金额
- 锁定优惠券(状态=using)
- 支付成功→used,失败→unused
如何处理并发下单?
答:
问题:
- 多个用户同时购买最后一件商品
- 同一用户重复提交订单
解决方案:
1. 分布式锁:
lockKey := fmt.Sprintf("order:create:user:%d", userID)
if !redis.SetNX(lockKey, "1", 10*time.Second) {
return errors.New("请勿重复提交")
}
defer redis.Del(lockKey)
2. 幂等性:
// 基于请求ID去重
requestID := r.Header.Get("X-Request-ID")
if cache.Exists(requestID) {
return cache.Get(requestID) // 返回已有结果
}
3. 库存预扣:
// Redis 原子扣减
remaining := redis.DecrBy("stock:sku:100101", quantity)
if remaining < 0 {
redis.IncrBy("stock:sku:100101", quantity) // 回滚
return errors.New("库存不足")
}
分库分表如何查询?
答:
路由规则:
// 按 user_id 分片
dbIndex = user_id / 256 % 32 // 32个数据库
tableIndex = user_id % 256 // 256个表
查询场景:
1. 单个用户的订单(有 user_id):
db := dbCluster[GetDBIndex(userID)]
table := GetTableName(userID)
db.Query(fmt.Sprintf("SELECT * FROM %s WHERE user_id = ?", table), userID)
2. 全局查询(无 user_id,如按订单号查询):
- 方案1:在订单号中编码 user_id
订单号 = user_id + 时间戳 + 随机数
- 方案2:建立全局路由表(order_id → user_id)
- 方案3:Elasticsearch 索引
促销活动如何设计?
答:
活动类型:
- 限时折扣:指定时间段打折
- 满减:满 200 减 30
- N 件 M 折:第二件半价
- 秒杀:限量抢购
- 拼团:3人成团
核心设计:
CREATE TABLE promotions (
promo_id BIGINT PRIMARY KEY,
type VARCHAR(20), -- discount | full_reduction | seckill
rule JSON,
priority INT, -- 优先级(多个活动)
start_time DATETIME,
end_time DATETIME,
target_type VARCHAR(20), -- product | category | user_level
target_ids JSON
);
价格计算:
func CalculatePrice(skuID int64, quantity int, userID int64) float64 {
// 1. 获取商品原价
originalPrice := GetPrice(skuID)
// 2. 查询适用的促销活动(按优先级)
promotions := GetActivePromotions(skuID, userID)
// 3. 计算最优价格
bestPrice := originalPrice
for _, promo := range promotions {
price := promo.Calculate(originalPrice, quantity)
if price < bestPrice {
bestPrice = price
}
}
return bestPrice
}
如何保证订单与库存一致性?
答:
问题:
- 创建订单成功,扣库存失败
- 扣库存成功,创建订单失败
解决方案:
方案1:数据库事务(单体)
tx.Begin()
tx.Exec("INSERT INTO orders ...")
tx.Exec("UPDATE inventory SET stock = stock - ?")
tx.Commit()
方案2:TCC 分布式事务(微服务)
- Try: 预扣库存、创建临时订单
- Confirm: 确认扣库存、订单转正
- Cancel: 释放库存、删除订单
方案3:本地消息表 + 最终一致性
1. 创建订单 + 插入本地消息(事务)
2. 定时任务发送消息到 Kafka
3. 库存服务消费消息,扣减库存
4. 定时对账,发现不一致进行补偿
推荐:
- 小规模:数据库事务
- 大规模:最终一致性 + 对账
电商系统的性能优化?
答:
1. 缓存优化:
商品详情 → Redis(热点数据)
购物车 → Redis Hash
用户会话 → Redis
2. 数据库优化:
分库分表:订单按 user_id 分片
读写分离:主库写,从库读
索引优化:覆盖索引、组合索引
3. 异步化:
订单创建成功 → Kafka →
- 发送短信
- 更新用户画像
- 同步到数据仓库
4. 限流降级:
限流:单用户 QPS < 10
降级:关闭推荐、评论等非核心功能
熔断:第三方服务故障时快速失败
5. 搜索优化:
MySQL → Elasticsearch
全文搜索、聚合分析
6. 静态资源:
商品图片 → CDN
页面静态化
9. 总结
核心要点
防超卖
- Redis 原子操作
- 数据库行锁
- 乐观锁
- 预扣库存
订单流程
- 状态机管理
- 超时处理
- 幂等性保证
- 分布式事务
高性能
- Redis 缓存
- 分库分表
- 异步消息
- 搜索引擎
一致性
- 数据库事务(单体)
- TCC(微服务)
- 最终一致性 + 对账
架构演进
V1: 单体 + 数据库事务
↓
V2: 微服务 + TCC + Redis
↓
V3: 最终一致性 + 消息队列 + 分库分表
本章完,祝面试顺利!