【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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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增强…