【1.6 漫畫數據庫設計實戰 - 從零開始設計高性能數據庫】

1.6 漫畫數據庫設計實戰 - 從零開始設計高性能數據庫

🎯 學習目標

  • 掌握數據庫表結構設計原則
  • 理解字段類型選擇與優化
  • 學會雪花算法ID生成策略
  • 掌握索引設計與優化技巧
  • 了解分庫分表設計方案

📖 故事開始

小明: “老王,我總是不知道怎么設計數據庫表,字段類型該選什么,索引怎么建?”

架構師老王: “哈哈,數據庫設計就像蓋房子,地基不牢,地動山搖!今天我們從頭開始學習數據庫設計的藝術。”

小明: “那從哪里開始呢?”

架構師老王: “先從一個電商系統的用戶表開始…”


🏗? 第一章:表結構設計原則

1.1 三大范式與反范式

架構師老王: “數據庫設計有三大范式,但實際項目中我們經常需要反范式設計。”

-- 第一范式:原子性(每個字段不可再分)
-- ? 錯誤設計
CREATE TABLE user_bad (id BIGINT PRIMARY KEY,name VARCHAR(100),address TEXT  -- 包含省市區,違反第一范式
);-- ? 正確設計
CREATE TABLE user_good (id BIGINT PRIMARY KEY,name VARCHAR(100),province VARCHAR(50),city VARCHAR(50),district VARCHAR(50),detail_address VARCHAR(200)
);-- 第二范式:完全函數依賴
-- ? 錯誤設計
CREATE TABLE order_item_bad (order_id BIGINT,product_id BIGINT,product_name VARCHAR(100),  -- 依賴于product_id,不依賴于組合主鍵quantity INT,price DECIMAL(10,2),PRIMARY KEY (order_id, product_id)
);-- ? 正確設計
CREATE TABLE order_item_good (order_id BIGINT,product_id BIGINT,quantity INT,price DECIMAL(10,2),PRIMARY KEY (order_id, product_id)
);-- 第三范式:消除傳遞依賴
-- ? 錯誤設計
CREATE TABLE employee_bad (id BIGINT PRIMARY KEY,name VARCHAR(100),department_id BIGINT,department_name VARCHAR(100),  -- 傳遞依賴于department_idsalary DECIMAL(10,2)
);-- ? 正確設計
CREATE TABLE employee_good (id BIGINT PRIMARY KEY,name VARCHAR(100),department_id BIGINT,salary DECIMAL(10,2)
);CREATE TABLE department (id BIGINT PRIMARY KEY,name VARCHAR(100)
);

1.2 反范式設計場景

-- 電商訂單表 - 為了查詢性能,適當冗余
CREATE TABLE orders (id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,user_name VARCHAR(100),           -- 冗余用戶名,避免關聯查詢user_phone VARCHAR(20),           -- 冗余手機號total_amount DECIMAL(12,2),item_count INT,                   -- 冗余商品數量status TINYINT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_user_id (user_id),INDEX idx_status_created (status, created_at),INDEX idx_created_at (created_at)
);-- 商品表 - 冗余分類信息
CREATE TABLE products (id BIGINT PRIMARY KEY,name VARCHAR(200) NOT NULL,category_id BIGINT,category_name VARCHAR(100),       -- 冗余分類名brand_id BIGINT,brand_name VARCHAR(100),          -- 冗余品牌名price DECIMAL(10,2),stock INT DEFAULT 0,sales_count INT DEFAULT 0,        -- 冗余銷量統計created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_category (category_id),INDEX idx_brand (brand_id),INDEX idx_price (price),INDEX idx_sales (sales_count DESC)
);

🔢 第二章:字段類型選擇與優化

2.1 數值類型選擇

架構師老王: “選擇合適的數據類型,既能節省存儲空間,又能提高查詢性能。”

-- 數值類型選擇指南
CREATE TABLE type_examples (-- 主鍵:使用BIGINT,支持雪花算法id BIGINT UNSIGNED PRIMARY KEY,-- 狀態字段:使用TINYINTstatus TINYINT UNSIGNED DEFAULT 0 COMMENT '0:待支付 1:已支付 2:已發貨 3:已完成',-- 年齡:使用TINYINT UNSIGNED (0-255)age TINYINT UNSIGNED,-- 計數器:根據預期大小選擇view_count INT UNSIGNED DEFAULT 0,        -- 瀏覽量like_count MEDIUMINT UNSIGNED DEFAULT 0,  -- 點贊數-- 金額:使用DECIMAL,避免浮點精度問題price DECIMAL(10,2) NOT NULL COMMENT '價格,精確到分',balance DECIMAL(15,2) DEFAULT 0.00 COMMENT '余額',-- 百分比:可以存儲為整數(乘以100)discount_rate SMALLINT UNSIGNED COMMENT '折扣率,如85表示8.5折',-- 時間戳:根據需求選擇created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,expired_at DATETIME COMMENT '過期時間'
);

2.2 字符串類型優化

-- 字符串類型選擇
CREATE TABLE string_examples (id BIGINT PRIMARY KEY,-- 固定長度:使用CHARcountry_code CHAR(2) COMMENT '國家代碼 CN/US',gender CHAR(1) COMMENT '性別 M/F',-- 變長字符串:使用VARCHARusername VARCHAR(50) NOT NULL COMMENT '用戶名',email VARCHAR(100) COMMENT '郵箱',phone VARCHAR(20) COMMENT '手機號',-- 長文本:使用TEXTdescription TEXT COMMENT '商品描述',content LONGTEXT COMMENT '文章內容',-- JSON數據:MySQL 5.7+支持JSON類型extra_info JSON COMMENT '擴展信息',-- 枚舉類型:適合固定選項priority ENUM('low', 'medium', 'high') DEFAULT 'medium',INDEX idx_username (username),INDEX idx_email (email),INDEX idx_phone (phone)
);-- 字符串長度優化示例
CREATE TABLE user_profiles (user_id BIGINT PRIMARY KEY,nickname VARCHAR(50),      -- 昵稱最多50字符avatar_url VARCHAR(500),   -- 頭像URLbio VARCHAR(500),          -- 個人簡介location VARCHAR(100),     -- 地理位置website VARCHAR(200),      -- 個人網站-- 使用前綴索引優化長字符串INDEX idx_avatar_prefix (avatar_url(100)),INDEX idx_bio_prefix (bio(50))
);

?? 第三章:雪花算法ID生成策略

3.1 雪花算法原理

架構師老王: “雪花算法生成的ID是64位長整型,包含時間戳、機器ID和序列號。”

雪花算法ID結構(64位):
┌─────────────────────────────────────────────────┬──────────┬──────────┬──────────────┐
│                時間戳(41位)                      │機器ID(10位)│ 序列號(12位) │  符號位(1位)  │
└─────────────────────────────────────────────────┴──────────┴──────────┴──────────────┘
/*** 雪花算法ID生成器*/
@Component
public class SnowflakeIdGenerator {// 起始時間戳 (2020-01-01)private final long START_TIMESTAMP = 1577836800000L;// 各部分位數private final long SEQUENCE_BITS = 12;private final long MACHINE_BITS = 10;private final long TIMESTAMP_BITS = 41;// 最大值private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);private final long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS);// 位移private final long MACHINE_SHIFT = SEQUENCE_BITS;private final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;private long machineId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdGenerator() {// 從配置或環境變量獲取機器IDthis.machineId = getMachineId();}public synchronized long nextId() {long currentTimestamp = System.currentTimeMillis();// 時鐘回撥檢查if (currentTimestamp < lastTimestamp) {throw new RuntimeException("時鐘回撥,拒絕生成ID");}if (currentTimestamp == lastTimestamp) {// 同一毫秒內,序列號遞增sequence = (sequence + 1) & MAX_SEQUENCE;if (sequence == 0) {// 序列號溢出,等待下一毫秒currentTimestamp = waitNextMillis(currentTimestamp);}} else {// 不同毫秒,序列號重置sequence = 0L;}lastTimestamp = currentTimestamp;// 組裝IDreturn ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)| (machineId << MACHINE_SHIFT)| sequence;}private long waitNextMillis(long currentTimestamp) {while (currentTimestamp <= lastTimestamp) {currentTimestamp = System.currentTimeMillis();}return currentTimestamp;}private long getMachineId() {// 可以從配置文件、環境變量或數據庫獲取String machineIdStr = System.getProperty("machine.id", "1");long id = Long.parseLong(machineIdStr);if (id > MAX_MACHINE_ID || id < 0) {throw new IllegalArgumentException("機器ID超出范圍");}return id;}
}

3.2 數據庫表設計中的ID策略

-- 用戶表 - 使用雪花算法ID
CREATE TABLE users (id BIGINT UNSIGNED PRIMARY KEY COMMENT '雪花算法生成的用戶ID',username VARCHAR(50) UNIQUE NOT NULL,email VARCHAR(100) UNIQUE,phone VARCHAR(20) UNIQUE,password_hash VARCHAR(255) NOT NULL,salt VARCHAR(32) NOT NULL,status TINYINT DEFAULT 1 COMMENT '1:正常 0:禁用',created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_username (username),INDEX idx_email (email),INDEX idx_phone (phone),INDEX idx_status (status),INDEX idx_created_at (created_at)
);-- 訂單表 - 雪花算法ID + 業務訂單號
CREATE TABLE orders (id BIGINT UNSIGNED PRIMARY KEY COMMENT '雪花算法ID',order_no VARCHAR(32) UNIQUE NOT NULL COMMENT '業務訂單號',user_id BIGINT UNSIGNED NOT NULL,total_amount DECIMAL(12,2) NOT NULL,status TINYINT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_order_no (order_no),INDEX idx_user_id (user_id),INDEX idx_status_created (status, created_at)
);-- 訂單號生成規則
-- 格式:日期(8位) + 機器ID(2位) + 序列號(6位)
-- 示例:2024010101000001

3.3 分布式ID生成服務

/*** 分布式ID生成服務*/
@Service
public class DistributedIdService {@Autowiredprivate SnowflakeIdGenerator snowflakeGenerator;@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 生成用戶ID*/public Long generateUserId() {return snowflakeGenerator.nextId();}/*** 生成訂單號*/public String generateOrderNo() {String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));String machineId = String.format("%02d", getMachineId());// 使用Redis生成序列號,保證單機唯一String key = "order_seq:" + date + ":" + machineId;Long seq = redisTemplate.opsForValue().increment(key);// 設置過期時間為2天redisTemplate.expire(key, Duration.ofDays(2));return date + machineId + String.format("%06d", seq);}/*** 生成商品SKU編碼*/public String generateSkuCode(Long categoryId) {String categoryCode = String.format("%04d", categoryId);String timestamp = String.valueOf(System.currentTimeMillis() % 100000);String random = String.format("%03d", new Random().nextInt(1000));return "SKU" + categoryCode + timestamp + random;}private int getMachineId() {// 從配置獲取機器IDreturn Integer.parseInt(System.getProperty("machine.id", "1"));}
}

📊 第四章:索引設計與優化

4.1 索引類型與選擇

架構師老王: “索引就像書的目錄,選對了事半功倍,選錯了適得其反。”

-- 單列索引
CREATE TABLE products (id BIGINT PRIMARY KEY,name VARCHAR(200),category_id BIGINT,brand_id BIGINT,price DECIMAL(10,2),stock INT,status TINYINT,created_at TIMESTAMP,-- 普通索引INDEX idx_category (category_id),INDEX idx_brand (brand_id),INDEX idx_price (price),INDEX idx_status (status),-- 唯一索引UNIQUE INDEX uk_name_brand (name, brand_id),-- 前綴索引(適用于長字符串)INDEX idx_name_prefix (name(20))
);-- 復合索引設計
CREATE TABLE user_orders (id BIGINT PRIMARY KEY,user_id BIGINT,status TINYINT,order_type TINYINT,total_amount DECIMAL(12,2),created_at TIMESTAMP,-- 復合索引:最左前綴原則INDEX idx_user_status_created (user_id, status, created_at),INDEX idx_status_type_amount (status, order_type, total_amount),-- 覆蓋索引:包含查詢所需的所有字段INDEX idx_user_cover (user_id, status, total_amount, created_at)
);-- 函數索引(MySQL 8.0+)
CREATE TABLE users (id BIGINT PRIMARY KEY,email VARCHAR(100),phone VARCHAR(20),created_at TIMESTAMP,-- 函數索引:支持大小寫不敏感查詢INDEX idx_email_lower ((LOWER(email))),-- 表達式索引INDEX idx_created_year ((YEAR(created_at)))
);

4.2 索引優化策略

-- 查詢優化示例
-- ? 低效查詢
SELECT * FROM orders 
WHERE DATE(created_at) = '2024-01-01';-- ? 高效查詢(使用范圍查詢,能利用索引)
SELECT * FROM orders 
WHERE created_at >= '2024-01-01 00:00:00' AND created_at < '2024-01-02 00:00:00';-- ? 低效查詢(函數導致索引失效)
SELECT * FROM users WHERE UPPER(username) = 'ADMIN';-- ? 高效查詢(使用函數索引或存儲計算結果)
SELECT * FROM users WHERE username = 'admin';-- 分頁查詢優化
-- ? 深分頁性能差
SELECT * FROM products 
ORDER BY created_at DESC 
LIMIT 100000, 20;-- ? 使用游標分頁
SELECT * FROM products 
WHERE created_at < '2024-01-01 10:00:00'
ORDER BY created_at DESC 
LIMIT 20;-- ? 使用ID分頁
SELECT * FROM products 
WHERE id > 1000000
ORDER BY id 
LIMIT 20;

4.3 索引監控與維護

-- 查看索引使用情況
SELECT TABLE_SCHEMA,TABLE_NAME,INDEX_NAME,CARDINALITY,SUB_PART,NULLABLE
FROM information_schema.STATISTICS 
WHERE TABLE_SCHEMA = 'your_database'
ORDER BY TABLE_NAME, SEQ_IN_INDEX;-- 查看未使用的索引
SELECT s.TABLE_SCHEMA,s.TABLE_NAME,s.INDEX_NAME
FROM information_schema.STATISTICS s
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage tON s.TABLE_SCHEMA = t.OBJECT_SCHEMAAND s.TABLE_NAME = t.OBJECT_NAMEAND s.INDEX_NAME = t.INDEX_NAME
WHERE t.INDEX_NAME IS NULLAND s.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')AND s.INDEX_NAME != 'PRIMARY';-- 分析表和索引
ANALYZE TABLE products;-- 優化表(重建索引)
OPTIMIZE TABLE products;

🔄 第五章:分庫分表設計

5.1 垂直拆分

架構師老王: “當單表數據量過大時,我們需要考慮分庫分表。先看垂直拆分。”

-- 原始用戶表(字段過多)
CREATE TABLE users_original (id BIGINT PRIMARY KEY,username VARCHAR(50),email VARCHAR(100),phone VARCHAR(20),password_hash VARCHAR(255),salt VARCHAR(32),nickname VARCHAR(50),avatar_url VARCHAR(500),gender TINYINT,birthday DATE,province VARCHAR(50),city VARCHAR(50),district VARCHAR(50),address VARCHAR(200),bio TEXT,hobby TEXT,education VARCHAR(100),occupation VARCHAR(100),company VARCHAR(100),created_at TIMESTAMP,updated_at TIMESTAMP
);-- 垂直拆分后
-- 用戶基礎信息表
CREATE TABLE users (id BIGINT PRIMARY KEY,username VARCHAR(50) UNIQUE NOT NULL,email VARCHAR(100) UNIQUE,phone VARCHAR(20) UNIQUE,password_hash VARCHAR(255) NOT NULL,salt VARCHAR(32) NOT NULL,status TINYINT DEFAULT 1,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_username (username),INDEX idx_email (email),INDEX idx_phone (phone)
);-- 用戶詳細信息表
CREATE TABLE user_profiles (user_id BIGINT PRIMARY KEY,nickname VARCHAR(50),avatar_url VARCHAR(500),gender TINYINT,birthday DATE,bio TEXT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,FOREIGN KEY (user_id) REFERENCES users(id)
);-- 用戶地址信息表
CREATE TABLE user_addresses (id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,province VARCHAR(50),city VARCHAR(50),district VARCHAR(50),detail_address VARCHAR(200),is_default TINYINT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_user_id (user_id),FOREIGN KEY (user_id) REFERENCES users(id)
);

5.2 水平分表

-- 訂單表水平分表(按月分表)
-- 2024年1月訂單表
CREATE TABLE orders_202401 (id BIGINT PRIMARY KEY,order_no VARCHAR(32) UNIQUE NOT NULL,user_id BIGINT NOT NULL,total_amount DECIMAL(12,2),status TINYINT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_order_no (order_no),INDEX idx_user_id (user_id),INDEX idx_status (status),INDEX idx_created_at (created_at)
);-- 2024年2月訂單表
CREATE TABLE orders_202402 (-- 結構相同id BIGINT PRIMARY KEY,order_no VARCHAR(32) UNIQUE NOT NULL,user_id BIGINT NOT NULL,total_amount DECIMAL(12,2),status TINYINT DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_order_no (order_no),INDEX idx_user_id (user_id),INDEX idx_status (status),INDEX idx_created_at (created_at)
);-- 分表路由邏輯
/*** 分表路由服務*/
@Service
public class ShardingService {/*** 根據時間路由到對應的訂單表*/public String getOrderTableName(LocalDateTime createTime) {String suffix = createTime.format(DateTimeFormatter.ofPattern("yyyyMM"));return "orders_" + suffix;}/*** 根據用戶ID路由到對應的用戶表*/public String getUserTableName(Long userId) {// 按用戶ID取模分表int tableIndex = (int) (userId % 16);return "users_" + String.format("%02d", tableIndex);}/*** 獲取查詢時間范圍內的所有表名*/public List<String> getOrderTableNames(LocalDateTime startTime, LocalDateTime endTime) {List<String> tableNames = new ArrayList<>();LocalDateTime current = startTime.withDayOfMonth(1);while (!current.isAfter(endTime)) {String suffix = current.format(DateTimeFormatter.ofPattern("yyyyMM"));tableNames.add("orders_" + suffix);current = current.plusMonths(1);}return tableNames;}
}

5.3 分庫策略

# ShardingSphere配置示例
spring:shardingsphere:datasource:names: ds0,ds1,ds2,ds3ds0:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/ecommerce_0username: rootpassword: passwordds1:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/ecommerce_1username: rootpassword: passwordds2:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/ecommerce_2username: rootpassword: passwordds3:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/ecommerce_3username: rootpassword: passwordrules:sharding:tables:users:actual-data-nodes: ds$->{0..3}.users_$->{00..15}database-strategy:standard:sharding-column: idsharding-algorithm-name: user-database-inlinetable-strategy:standard:sharding-column: idsharding-algorithm-name: user-table-inlineorders:actual-data-nodes: ds$->{0..3}.orders_$->{202401..202412}database-strategy:standard:sharding-column: user_idsharding-algorithm-name: order-database-inlinetable-strategy:standard:sharding-column: created_atsharding-algorithm-name: order-table-inlinesharding-algorithms:user-database-inline:type: INLINEprops:algorithm-expression: ds$->{id % 4}user-table-inline:type: INLINEprops:algorithm-expression: users_$->{String.format('%02d', id % 16)}order-database-inline:type: INLINEprops:algorithm-expression: ds$->{user_id % 4}order-table-inline:type: INLINEprops:algorithm-expression: orders_$->{created_at.format('yyyyMM')}

🎯 第六章:性能優化實戰

6.1 查詢優化

-- 商品搜索優化
CREATE TABLE products (id BIGINT PRIMARY KEY,name VARCHAR(200) NOT NULL,category_id BIGINT,brand_id BIGINT,price DECIMAL(10,2),stock INT DEFAULT 0,sales_count INT DEFAULT 0,rating DECIMAL(3,2) DEFAULT 0,status TINYINT DEFAULT 1,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,-- 復合索引優化多條件查詢INDEX idx_category_price_sales (category_id, price, sales_count DESC),INDEX idx_brand_price (brand_id, price),INDEX idx_status_created (status, created_at DESC),-- 全文索引支持商品名稱搜索FULLTEXT INDEX ft_name (name)
);-- 優化后的查詢
-- 分類 + 價格區間 + 排序
SELECT id, name, price, sales_count 
FROM products 
WHERE category_id = 1001 AND price BETWEEN 100 AND 500 AND status = 1
ORDER BY sales_count DESC 
LIMIT 20;-- 使用全文索引搜索
SELECT id, name, price 
FROM products 
WHERE MATCH(name) AGAINST('手機 華為' IN NATURAL LANGUAGE MODE)AND status = 1
ORDER BY rating DESC 
LIMIT 20;

6.2 統計查詢優化

-- 訂單統計表(預計算)
CREATE TABLE order_statistics (id BIGINT PRIMARY KEY,stat_date DATE NOT NULL,stat_type TINYINT NOT NULL COMMENT '1:日統計 2:月統計',total_orders INT DEFAULT 0,total_amount DECIMAL(15,2) DEFAULT 0,avg_amount DECIMAL(10,2) DEFAULT 0,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,UNIQUE INDEX uk_date_type (stat_date, stat_type),INDEX idx_stat_date (stat_date)
);-- 用戶行為統計表
CREATE TABLE user_behavior_stats (user_id BIGINT PRIMARY KEY,total_orders INT DEFAULT 0,total_amount DECIMAL(15,2) DEFAULT 0,last_order_time TIMESTAMP NULL,avg_order_amount DECIMAL(10,2) DEFAULT 0,favorite_category_id BIGINT,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_total_amount (total_amount DESC),INDEX idx_last_order (last_order_time DESC)
);-- 定時任務更新統計數據
/*** 統計數據更新服務*/
@Service
public class StatisticsService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate StatisticsMapper statisticsMapper;/*** 每日統計任務*/@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1點執行public void updateDailyStatistics() {LocalDate yesterday = LocalDate.now().minusDays(1);// 計算昨日訂單統計OrderStatistics stats = orderMapper.getDailyStatistics(yesterday);// 更新或插入統計數據statisticsMapper.upsertDailyStats(stats);log.info("更新日統計數據完成: {}", yesterday);}/*** 用戶行為統計更新*/@Asyncpublic void updateUserBehaviorStats(Long userId) {UserBehaviorStats stats = orderMapper.getUserBehaviorStats(userId);statisticsMapper.updateUserBehaviorStats(stats);}
}

📋 面試常考知識點

Q1: 如何選擇合適的數據類型?

A:

  • 數值類型:根據取值范圍選擇最小的類型
  • 字符串:固定長度用CHAR,變長用VARCHAR
  • 時間:TIMESTAMP vs DATETIME的區別
  • 金額:使用DECIMAL避免精度問題

Q2: 雪花算法的優缺點?

A:

  • 優點:全局唯一、趨勢遞增、高性能
  • 缺點:依賴系統時鐘、機器ID管理復雜
  • 替代方案:UUID、數據庫自增ID、Redis生成

Q3: 如何設計高效的索引?

A:

  • 遵循最左前綴原則
  • 避免在索引列上使用函數
  • 考慮覆蓋索引減少回表
  • 定期監控和清理無用索引

Q4: 什么時候需要分庫分表?

A:

  • 單表數據量超過1000萬
  • 單庫連接數不夠用
  • 讀寫QPS達到瓶頸
  • 需要考慮數據一致性和跨庫查詢問題

🎯 最佳實踐總結

架構師老王: “數據庫設計的核心原則:”

  1. 合理范式化: 在性能和規范之間找平衡
  2. 選擇合適類型: 夠用就好,不要過度設計
  3. 索引設計: 查詢驅動,定期優化
  4. 分庫分表: 提前規劃,平滑擴展
  5. 監控運維: 持續優化,預防問題

小明: “原來數據庫設計有這么多門道!”

架構師老王: “是的,好的數據庫設計是系統性能的基石。記住:設計時多思考,運行時少煩惱!”

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912657.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912657.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912657.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OSPF虛擬鏈路術語一覽:快速掌握網絡路由

大家好&#xff0c;這里是G-LAB IT實驗室。今天帶大家了解一下OSPF的相關知識&#xff01; 01 OSPF虛擬鏈路術語大全 網絡架構中&#xff0c;OSPF&#xff08;開放式最短路徑優先&#xff09;是一種重要的路由協議。通過其鏈路狀態路由機制&#xff0c;OSPF能夠有效維護和更新…

oracle常用的函數(一) 之 to_char、to_date

文章目錄 前言to_char基本語法格式模型格式模型介紹無FM示例使用FM輸出貨幣負數輸出尖括號 將日期格式化將數字格式化為帶有貨幣符號和千位分隔符的格式總結 to_date語法語法示例 戳這里&#xff0c;第二彈 → oracle常用的函數&#xff08;二&#xff09; 之 nvl、decode、l…

數據庫服務器宕機的處理方法與實戰策略

在當今數字化時代,數據庫作為企業數據存儲與管理的核心,承載著業務運行的關鍵信息。一旦數據庫服務器宕機,將導致業務中斷、數據丟失等嚴重后果,甚至可能給企業帶來巨大的經濟損失和聲譽損害。因此,掌握一套系統、科學的數據庫服務器宕機處理方法尤為重要。本文將從應急響…

如何hack邊緣的kubelet修改Cgroup數值

之前做了一個VPA項目的需求&#xff0c;就是需要不重啟的方式修改容器的Cgroup的值已達到垂直擴縮容的目的&#xff0c;項目中核心的思路如下 上游下發要VPA的結果的值寫入到容器的Annotation里面Kubelet 感知到這個 annoation 的變化我們本地運行一個 Agent&#xff0c;里面運…

熟悉 PyCharm

界面 我們常用的就這個幾個地方&#xff1a; 常用配置 調整字體大小 Ctrl 滾輪調整字體大小 插件推薦 Indent Rainbow 該插件的作用在于能夠對于不同層級縮進的空格標注不同的顏色&#xff1a; 快捷鍵 快捷鍵的 pdf 下載鏈接&#xff1a; Windows 版&#xff1a;https:…

pytorch--模型訓練的一般流程

文章目錄 前言0、數據集準備1、數據集2、dataset3、model4、訓練模型 前言 在pytorch中模型訓練一般分為以下幾個步驟&#xff1a; 0、數據集準備 1、數據集讀取&#xff08;dataset模塊&#xff09; 2、數據集轉換為tensor&#xff08;dataloader模塊&#xff09; 3、定義模型…

智能合同管理實戰:基于區塊鏈的電子簽約技術實現

在數字經濟時代,傳統紙質合同簽署方式已難以滿足企業高效、安全、合規的業務需求。智能合同管理(Smart Contract Management)結合區塊鏈技術,正在重塑電子簽約流程,實現合同全生命周期的自動化、可追溯和防篡改。本文將深入探討基于區塊鏈的電子簽約技術實現,涵蓋核心架構…

設計模式精講 Day 22:模板方法模式(Template Method Pattern)

【設計模式精講 Day 22】模板方法模式&#xff08;Template Method Pattern&#xff09; 文章標簽 設計模式, 模板方法模式, Java開發, 面向對象設計, 軟件架構, 設計模式實戰, Java應用開發 文章簡述 模板方法模式是一種行為型設計模式&#xff0c;它通過定義一個算法的骨架…

如何在pytorch中使用tqdm:優雅實現訓練進度監控

文章目錄 為什么需要進度條&#xff1f;tqdm 簡介基礎用法示例深度學習中的實戰應用1. 數據加載進度監控2. 訓練循環增強版3. 驗證階段集成 高級技巧與最佳實踐1. 自定義進度條樣式2. 嵌套進度條&#xff08;多任務&#xff09;3. 分布式訓練支持4. 與日志系統集成 性能優化建議…

Linux中的xxd命令詳解

xxd 是一個 十六進制轉儲&#xff08;hex dump&#xff09;工具&#xff0c;通常用于將二進制文件轉換為十六進制格式&#xff0c;或者反向轉換&#xff08;十六進制→二進制&#xff09;。它是 vim 的一部分&#xff0c;但在大多數 Linux 系統&#xff08;如 Ubuntu&#xff0…

磐維數據庫panweidb3.1.0單節點多實例安裝

0 說明 業務科室提單需要在某臺主機上部署多個單機磐維數據庫&#xff0c;用于業務測試。以下內容展示如何在單節點安裝多個磐維數據庫實例。 1 部署環境準備 1.1 IP 地址及端口 instipport實例1192.168.131.1717700實例2192.168.131.1727700 在131.17上分別安裝兩個實例&…

轉錄組分析流程(三):功能富集分析

我們的教程主要是以一個具體的例子作為線索,通過對公共數據庫數據bulk-RNA-seq的挖掘,利用生物信息學分析來探索目標基因集作為某種疾病數據預后基因的潛能及其潛在分子機制,同時在單細胞水平分析(對scRNA-seq進行挖掘)預后基因的表達,了解細胞之間的通訊網絡,以期為該疾病…

全面掌握 tkinter:Python GUI 編程的入門與實戰指南

在自動化、工具開發、數據可視化等領域&#xff0c;圖形用戶界面&#xff08;GUI&#xff09;往往是提升用戶體驗的重要方式。作為 Python 官方內置的 GUI 庫&#xff0c;tkinter 以其輕量、跨平臺、易于學習的特性成為初學者和輕量級應用開發者首選。 本文將以深入淺出的方式…

TDH社區開發版安裝教程

&#xff08;注&#xff1a;本文章來源于星環官網安裝手冊&#xff09; 后面放置了視頻和安裝手冊連接 1、硬件及環境要求 Docker17及以上版本&#xff0c;支持Centos&#xff0c;Ubuntu等系統&#xff08;注&#xff1a;這里我使用CentOS-7版本&#xff0c;最佳版本推薦為7.…

Linux基本命令篇 —— grep命令

grep是Linux/Unix系統中一個非常強大的文本搜索工具&#xff0c;它的名字來源于"Global Regular Expression Print"&#xff08;全局正則表達式打印&#xff09;。grep命令用于在文件中搜索包含特定模式的行&#xff0c;并將匹配的行打印出來。 目錄 一、基本語法 二…

蒼穹外賣問題系列之 蒼穹外賣訂單詳情前端界面和網課給的不一樣

問題 如圖&#xff0c;我的前端界面和網課里面給的不一樣&#xff0c;沒有“申請退款”和一些其他的該有的東西。 原因分析 “合計”這一欄顯示undefined說明我們的總金額沒有輸入進去。可以看看訂單提交那塊的代碼&#xff0c;是否可以正確輸出。還有就是訂單詳細界面展示這…

CppCon 2018 學習:EMULATING THE NINTENDO 3DS

我們來逐個分析一下這個 組件交互模型 和 仿真 & 序列化 的關系&#xff0c;特別是主線程&#xff08;Main Thread&#xff09;與其他系統組件之間的交互。 1. Main Thread — simple (basically memcpy) --> GPU Main Thread&#xff08;主線程&#xff09;負責游戲的…

[Python 基礎課程]數字

數字 數字數據類型用于存儲數值&#xff0c;比如整數、小數等。數據類型是不允許改變的&#xff0c;這就意味著如果改變數字數據類型的值&#xff0c;將重新分配內存空間。 創建數字類型的變量&#xff1a; var1 1 var2 10創建完變量后&#xff0c;如果想廢棄掉這個變量&a…

Linux CentOS環境下Java連接MySQL數據庫指南

文章目錄 前言一、環境準備1.1 系統更新1.2 Java環境安裝1.3 MySQL數據庫安裝1.4 下載JDBC驅動 二、編寫Java程序2.1 代碼如下2.2 編譯和運行2.3 驗證創建結果 三、代碼上傳至Gitee3.1 安裝配置Git3.2 克隆倉庫到本地3.3 添加Java項目文件3.4 提交代碼到本地倉庫3.5 推送到Gite…

LLM面試12

訊飛算法工程師面試題 SVM核函數能否映射到無窮維 可以的&#xff0c;多項式核函數將低維數據映射到高維&#xff08;維度是有限的&#xff09;&#xff0c;而高斯核函數可以映射到無窮維。由 描述下xgb原理&#xff0c;損失函數 首先需要說一說GBDT,它是一種基于boosting增強…