{T}

数据库

数据库是后端开发的基础设施之一。对 Java 开发者来说,数据库不只是"数据放哪里"的问题,更关系到系统的查询效率、事务一致性、并发写入和故障排查能力。

概念与背景

什么是数据库

数据库(Database)是用于存储、管理和检索数据的系统。它通过结构化或半结构化的方式组织数据,使数据能够被稳定地保存、查询、更新和维护。

在真实项目里,数据库通常承担这些职责:

  • 持久化存储:将业务数据保存在磁盘,进程重启后数据不丢失
  • 结构化组织:通过表、字段、约束等方式组织数据,避免混乱
  • 高效查询:支持复杂查询条件、排序、分页、聚合等操作
  • 并发控制:多用户同时访问数据时保证一致性
  • 安全保障:通过权限控制、事务机制、备份恢复保护数据

什么是 DBMS

数据库管理系统(DBMS,Database Management System)是用于创建、管理和操作数据库的软件。我们平时说"使用 MySQL""连接 PostgreSQL",本质上是在使用某种数据库管理系统。

DBMS 一般至少提供这些能力:

能力分类具体功能说明
数据定义创建库、表、索引、约束DDL 操作,定义数据结构
数据操作增删改查DML 操作,业务数据的日常维护
事务管理提交、回滚、隔离保证数据一致性
权限控制用户、角色、访问限制DCL 操作,数据安全保护
备份恢复崩溃恢复、日志恢复、数据导出导入灾难恢复能力

关系型数据库与非关系型数据库

常见数据库可以粗略分成两类:

关系型数据库(RDBMS)

关系型数据库基于关系模型,使用表(Table)来组织数据,表之间通过外键建立关联。

核心特点:

  • 数据以二维表形式存储,结构固定
  • 支持 SQL(结构化查询语言)进行数据操作
  • 提供 ACID 事务保证
  • 支持复杂查询、连接、聚合等操作
  • 数据一致性要求高

典型产品:

  • MySQL:开源关系型数据库,社区活跃,Java 生态集成完善
  • PostgreSQL:功能强大的开源数据库,支持高级特性
  • Oracle:商业数据库,企业级功能完善
  • SQL Server:微软商业数据库,与 .NET 生态深度集成

适合场景: 订单、支付、库存、账户等核心业务数据,对一致性要求高的场景。

非关系型数据库(NoSQL)

非关系型数据库不使用表结构,数据模型灵活,强调高性能和扩展性。

核心特点:

  • 数据结构灵活,无需预定义表结构
  • 水平扩展能力强
  • 查询性能高,但不支持复杂 SQL 操作
  • 最终一致性,非强一致性

典型产品:

  • Redis:内存数据库,用作缓存、分布式锁、消息队列
  • MongoDB:文档数据库,存储 JSON 格式数据
  • Cassandra:分布式宽列存储,适合海量数据

适合场景: 缓存、文档数据、热点数据、海量分布式场景。

选择建议

场景推荐类型原因
核心业务数据(订单、账户)关系型数据库需要强一致性、事务支持
高并发读取Redis + MySQLRedis 缓存热点数据,MySQL 持久化
日志、文档数据MongoDB数据结构灵活,无需预定义字段
海量数据存储分布式数据库需要水平扩展能力

对于绝大多数 Java 中后台项目,关系型数据库仍然是业务主数据的核心载体。

关系型数据库的理论基础

关系模型

关系型数据库基于数学中的关系模型,由 E.F.Codd 于 1970 年提出。核心概念:

  • 关系(Relation):即表,一个关系就是一个二维表
  • 元组(Tuple):即行,表中的一条记录
  • 属性(Attribute):即列,表中的一个字段
  • 域(Domain):属性的取值范围,如性别只能取"男""女"
  • 键(Key):能唯一标识一个元组的属性或属性组

关系模型的优点:

  • 数据结构简单清晰(二维表)
  • 理论基础扎实(集合论、关系代数)
  • 支持复杂的数据操作(选择、投影、连接等)

ACID 特性

事务(Transaction)是数据库操作的基本单位,具有 ACID 四大特性:

特性英文含义说明
原子性Atomicity事务是不可分割的工作单位事务中的操作要么都做,要么都不做
一致性Consistency事务执行前后数据库状态一致从一个一致性状态转换到另一个一致性状态
隔离性Isolation多个事务并发执行时互不干扰各事务之间相互隔离,不会互相影响
持久性Durability事务提交后永久生效即使系统崩溃,数据也不会丢失

示例:银行转账

sql
-- 事务开始
START TRANSACTION;

-- 从账户 A 扣款 100 元
UPDATE account SET balance = balance - 100 WHERE id = 'A';

-- 向账户 B 存款 100 元
UPDATE account SET balance = balance + 100 WHERE id = 'B';

-- 提交事务
COMMIT;

-- 如果中间任何一步失败,执行 ROLLBACK 回滚
-- ROLLBACK;

为什么需要 ACID:

  • 原子性保证转账要么成功要么失败,不会出现钱扣了但没到账的情况
  • 一致性保证 A+B 的总金额不变
  • 隔离性保证并发转账不会互相干扰
  • 持久性保证转账成功后即使数据库崩溃也能恢复

范式理论

范式(Normalization)是关系数据库设计的基本原则,用于消除数据冗余和更新异常。

常见范式:

范式要求解决的问题
第一范式(1NF)每一列都是不可分割的原子值消除列的复合属性
第二范式(2NF)在 1NF 基础上,非主属性完全依赖主键消除部分依赖
第三范式(3NF)在 2NF 基础上,非主属性不传递依赖主键消除传递依赖

示例:

sql
-- 不符合 1NF:地址字段包含省市区
CREATE TABLE bad_user (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    address VARCHAR(200)  -- "北京市朝阳区望京街道"
);

-- 符合 1NF:地址拆分为多个字段
CREATE TABLE good_user (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    province VARCHAR(50),
    city VARCHAR(50),
    district VARCHAR(50),
    detail VARCHAR(200)
);

-- 不符合 2NF:订单明细表中商品名称部分依赖主键
CREATE TABLE bad_order_item (
    order_id INT,
    product_id INT,
    product_name VARCHAR(100),  -- 依赖 product_id,不依赖 order_id
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

-- 符合 2NF:商品名称放到商品表
CREATE TABLE product (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE good_order_item (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

实际开发建议:

  • 范式化设计可以减少数据冗余,但可能导致查询性能下降
  • 实际项目中常采用反范式化设计,适度冗余以提升查询效率
  • 例如订单表中冗余商品名称,避免每次查询都要 JOIN 商品表

为什么业务系统普遍离不开数据库

数据库相比文件和内存的优势

常见存储方式对比如下:

存储方式优点缺点典型用途
内存访问速度极快进程退出后数据丢失,容量有限缓存、临时状态、计算中间结果
文件持久化简单直观查询、并发控制、结构化能力弱配置、日志、导出文件
数据库持久化、查询、事务、约束、权限控制能力完整学习和运维成本较高业务核心数据管理

数据库之所以重要,不只是因为"能存数据",更因为它提供了业务系统最需要的几种能力:

1. 结构化管理

数据库通过表结构、约束、索引等机制,保证数据的完整性和一致性。

sql
-- 表结构定义数据类型和约束
CREATE TABLE user_account (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(64) NOT NULL COMMENT '用户名',
    phone VARCHAR(20) UNIQUE COMMENT '手机号',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1正常,0禁用',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    
    -- 唯一约束
    UNIQUE KEY uk_username (username),
    -- 索引
    KEY idx_phone (phone),
    KEY idx_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户账户表';

2. 查询能力

数据库支持复杂的查询条件、排序、分页、聚合等操作。

sql
-- 复杂查询示例
SELECT 
    u.username,
    COUNT(o.id) AS order_count,
    SUM(o.amount) AS total_amount
FROM user_account u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 1
  AND o.created_at >= '2026-01-01'
GROUP BY u.id
HAVING total_amount > 10000
ORDER BY total_amount DESC
LIMIT 10;

3. 一致性保证

数据库通过约束和事务保证数据一致性。

sql
-- 约束示例
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    status TINYINT NOT NULL,
    
    -- 外键约束:保证 user_id 存在
    CONSTRAINT fk_user FOREIGN KEY (user_id) 
        REFERENCES user_account(id),
    -- 检查约束:保证金额为正
    CONSTRAINT chk_amount CHECK (amount > 0)
);

4. 并发访问控制

数据库通过锁机制和 MVCC(多版本并发控制)保证并发访问的正确性。

code
事务A: SELECT * FROM orders WHERE id=1 FOR UPDATE;  -- 加行锁
事务B: UPDATE orders SET status=2 WHERE id=1;        -- 等待锁释放

5. 可靠恢复能力

数据库通过日志(WAL)、备份恢复等机制保证数据的可靠性。

  • WAL(Write-Ahead Logging):先写日志,再写数据,崩溃后可通过日志恢复
  • 备份恢复:支持全量备份、增量备份
  • 主从复制:数据实时同步到从库,提供高可用

Java 后端为什么常用 MySQL

在 Java 后端开发里,MySQL 非常常见,主要原因包括:

优势说明
社区成熟资料丰富,问题容易找到解决方案
生态完善JDBC 驱动、连接池、ORM 框架支持完善
功能适用InnoDB 引擎支持事务、行锁、外键,适合业务系统
部署简单安装配置简单,运维成本低
成本低廉社区版免费,商业版价格相对合理

MySQL 的存储引擎:

引擎特点适用场景
InnoDB支持事务、行锁、外键,MySQL 5.5 后默认引擎业务系统主表
MyISAM不支持事务,表锁,查询性能高只读或读多写少的场景
Memory数据存储在内存,速度快,重启后丢失临时表、缓存

注意事项:

  • MySQL 现在依然有可用的社区版,并不是"从某个版本开始就收费不能用"
  • 企业场景下是否采购商业支持,是另外一个问题
  • 建议使用 MySQL 5.7 或 8.0,功能完善,性能优秀

数据库中的核心概念

库、表、行、列

关系型数据库通常按以下层次组织数据:

code
数据库(Database)
  └── 表(Table)
        ├── 行(Row) / 记录(Record)
        │     ├── 列(Column) / 字段(Field)
        │     ├── 列(Column)
        │     └── 列(Column)
        └── 行(Row)

示例:用户表

sql
-- 创建数据库
CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE shop;

-- 创建表
CREATE TABLE user_account (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(64) NOT NULL COMMENT '用户名',
    phone VARCHAR(20) UNIQUE COMMENT '手机号',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1正常,0禁用',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户账户表';

-- 插入数据(行)
INSERT INTO user_account (username, phone, status) 
VALUES ('zhangsan', '13800138000', 1);

-- 查询数据
SELECT * FROM user_account WHERE username = 'zhangsan';

约束(Constraint)

约束用于保证数据的完整性和一致性。

约束类型关键字作用
主键约束PRIMARY KEY唯一标识一行,不能为 NULL
唯一约束UNIQUE列值唯一,可以为 NULL
非空约束NOT NULL列值不能为 NULL
外键约束FOREIGN KEY保证引用完整性
检查约束CHECK保证列值满足指定条件
默认值DEFAULT列的默认值
sql
-- 约束示例
CREATE TABLE product (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,              -- 非空约束
    sku VARCHAR(50) UNIQUE,                  -- 唯一约束
    price DECIMAL(10,2) NOT NULL,
    status TINYINT DEFAULT 1,                -- 默认值
    CHECK (price > 0),                       -- 检查约束
    
    INDEX idx_name (name),                   -- 索引
    INDEX idx_status (status)
);

CREATE TABLE order_item (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    quantity INT NOT NULL,
    
    -- 外键约束
    CONSTRAINT fk_order FOREIGN KEY (order_id) 
        REFERENCES orders(id) ON DELETE CASCADE,
    CONSTRAINT fk_product FOREIGN KEY (product_id) 
        REFERENCES product(id)
);

索引(Index)

索引是数据库中用于加速查询的数据结构,类似于书的目录。

索引原理:

  • 数据库使用 B+Tree 等数据结构存储索引
  • 索引文件存储在磁盘,读取时加载到内存
  • 通过索引可以快速定位数据,避免全表扫描

索引类型:

索引类型说明适用场景
主键索引自动创建,聚簇索引主键字段
唯一索引列值唯一唯一字段(手机号、邮箱等)
普通索引最基本的索引经常作为查询条件的字段
组合索引多列组合多条件查询
全文索引文本搜索文章内容搜索

索引设计原则:

  1. 为经常出现在 WHERE、JOIN、ORDER BY 中的字段建索引
  2. 组合索引遵循最左前缀原则
  3. 避免在小表、更新频繁的表上建过多索引
  4. 索引不是越多越好,会影响写入性能
sql
-- 创建索引
CREATE INDEX idx_user_phone ON user_account(phone);
CREATE INDEX idx_user_status_created ON user_account(status, created_at);

-- 查看索引
SHOW INDEX FROM user_account;

-- 使用 EXPLAIN 分析查询是否命中索引
EXPLAIN SELECT * FROM user_account WHERE phone = '13800138000';

注意事项:

  • 索引不是万能的,数据量小时全表扫描可能更快
  • 索引会占用磁盘空间
  • 插入、更新、删除数据时需要维护索引,影响性能

事务(Transaction)

事务是数据库操作的执行单元,具有 ACID 特性。

事务控制语句:

语句作用
START TRANSACTION / BEGIN开启事务
COMMIT提交事务
ROLLBACK回滚事务
SAVEPOINT设置保存点
ROLLBACK TO SAVEPOINT回滚到保存点

示例:银行转账

java
// Java 代码示例(Spring 事务管理)
@Service
public class AccountService {
    
    @Autowired
    private AccountMapper accountMapper;
    
    @Transactional  // Spring 声明式事务
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // 1. 检查余额
        Account from = accountMapper.selectById(fromAccount);
        if (from.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }
        
        // 2. 扣款
        accountMapper.decreaseBalance(fromAccount, amount);
        
        // 3. 存款
        accountMapper.increaseBalance(toAccount, amount);
        
        // 方法正常结束自动提交,抛出异常自动回滚
    }
}
sql
-- SQL 事务示例
START TRANSACTION;

-- 查询余额并加锁(悲观锁)
SELECT balance FROM account WHERE id = 'A' FOR UPDATE;

-- 扣款
UPDATE account SET balance = balance - 100 WHERE id = 'A';

-- 存款
UPDATE account SET balance = balance + 100 WHERE id = 'B';

-- 提交事务
COMMIT;

-- 如果出现异常,回滚
-- ROLLBACK;

事务隔离级别:

隔离级别脏读不可重复读幻读性能
READ UNCOMMITTED最好
READ COMMITTED×较好
REPEATABLE READ××一般
SERIALIZABLE×××最差
  • MySQL InnoDB 默认使用 REPEATABLE READ 隔离级别
  • 通过 MVCC 和 Next-Key Lock 在很大程度上避免幻读

开发中的典型应用场景

场景一:用户注册

用户注册不是简单插入一条记录,还会涉及:

java
@Service
public class UserService {
    
    @Transactional
    public Long register(UserRegisterDTO dto) {
        // 1. 检查用户名是否唯一
        if (userMapper.selectByUsername(dto.getUsername()) != null) {
            throw new BusinessException("用户名已存在");
        }
        
        // 2. 检查手机号是否唯一
        if (userMapper.selectByPhone(dto.getPhone()) != null) {
            throw new BusinessException("手机号已注册");
        }
        
        // 3. 密码加密
        String encryptedPassword = passwordEncoder.encode(dto.getPassword());
        
        // 4. 插入用户记录
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setPhone(dto.getPhone());
        user.setPassword(encryptedPassword);
        user.setStatus(1);
        userMapper.insert(user);
        
        // 5. 初始化用户资料
        UserProfile profile = new UserProfile();
        profile.setUserId(user.getId());
        profile.setNickname(dto.getUsername());
        userProfileMapper.insert(profile);
        
        // 6. 记录注册日志
        UserLog log = new UserLog();
        log.setUserId(user.getId());
        log.setAction("register");
        log.setIp(dto.getIp());
        userLogMapper.insert(log);
        
        return user.getId();
    }
}

涉及的知识点:

  • 唯一约束保证用户名和手机号唯一
  • 事务保证多张表同时成功或失败
  • 密码加密存储

场景二:订单创建

订单业务通常会同时涉及订单表写入、库存扣减、支付状态更新。

java
@Service
public class OrderService {
    
    @Transactional(rollbackFor = Exception.class)
    public Long createOrder(OrderCreateDTO dto) {
        // 1. 检查商品库存(使用行锁)
        Product product = productMapper.selectByIdForUpdate(dto.getProductId());
        if (product.getStock() < dto.getQuantity()) {
            throw new BusinessException("库存不足");
        }
        
        // 2. 扣减库存
        productMapper.decreaseStock(dto.getProductId(), dto.getQuantity());
        
        // 3. 创建订单
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(dto.getUserId());
        order.setProductId(dto.getProductId());
        order.setQuantity(dto.getQuantity());
        order.setAmount(product.getPrice().multiply(new BigDecimal(dto.getQuantity())));
        order.setStatus(OrderStatus.CREATED.getCode());
        orderMapper.insert(order);
        
        // 4. 扣减用户余额
        accountMapper.decreaseBalance(dto.getUserId(), order.getAmount());
        
        // 5. 更新订单状态为已支付
        orderMapper.updateStatus(order.getId(), OrderStatus.PAID.getCode());
        
        // 6. 发送订单创建消息(异步)
        messageProducer.sendOrderCreatedMessage(order.getId());
        
        return order.getId();
    }
}

涉及的知识点:

  • 使用 SELECT ... FOR UPDATE 加行锁,防止超卖
  • 事务保证订单、库存、账户同时成功或失败
  • 分布式场景下可能需要分布式事务(Seata、TCC 等)

场景三:报表查询优化

一旦业务数据量上来,很多问题就不再是"SQL 能不能查出来",而是性能问题。

问题示例:

sql
-- 慢查询:全表扫描
SELECT * FROM orders WHERE DATE(created_at) = '2026-03-29';
-- 问题:索引列使用了函数,索引失效

-- 优化后:范围查询,命中索引
SELECT * FROM orders 
WHERE created_at >= '2026-03-29 00:00:00' 
  AND created_at < '2026-03-30 00:00:00';
sql
-- 慢查询:未命中索引的排序
SELECT * FROM orders 
WHERE user_id = 12345 
ORDER BY created_at DESC 
LIMIT 10;
-- 问题:如果有 idx_user_id,可以命中索引,但需要 filesort

-- 优化:创建组合索引
CREATE INDEX idx_user_created ON orders(user_id, created_at DESC);

使用 EXPLAIN 分析查询:

sql
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;

-- 关键字段:
-- type:访问类型,const > eq_ref > ref > range > index > ALL
-- key:实际使用的索引
-- rows:预估扫描行数
-- Extra:Using index(覆盖索引)、Using filesort(文件排序)、Using temporary(临时表)

优化建议:

  1. 使用 EXPLAIN 分析查询计划
  2. 避免在 WHERE 条件中对索引列使用函数或计算
  3. 避免 SELECT *,只查询需要的字段
  4. 合理使用覆盖索引,避免回表
  5. 分页查询优化(避免 LIMIT 100000, 10)

学习数据库时要重点关注什么

1. 表结构设计是否清晰

  • 范式化设计减少冗余,反范式化提升性能
  • 合理使用约束保证数据完整性
  • 字段类型选择合适(TINYINT vs INT, VARCHAR vs TEXT)

2. SQL 是否语义明确且能命中索引

  • 避免 SELECT *,明确指定字段
  • WHERE 条件避免对索引列使用函数
  • 使用 EXPLAIN 验证查询计划

3. 事务边界是否合理

  • 事务尽量简短,避免长事务
  • 事务中不要有远程调用、文件 IO 等耗时操作
  • 合理设置隔离级别

4. 并发访问下是否会出现锁冲突

  • 理解行锁、表锁、间隙锁
  • 避免长事务持有锁时间过长
  • 乐观锁 vs 悲观锁的选择

5. 排障时是否会看执行计划和慢查询日志

  • 使用 EXPLAIN 分析慢查询
  • 开启慢查询日志定位问题 SQL
  • 使用 SHOW PROFILE 分析查询耗时

常见误区

误区一:认为数据库只是"保存数据的地方"

错误认知:数据库就是个仓库,存数据、取数据就行。

正确理解:数据库是业务系统的核心组件,涉及事务、锁、索引、并发控制等复杂机制。理解这些机制才能写出高性能、高可用的系统。

误区二:只会写 CRUD,不关心执行计划

错误认知:只要 SQL 能查出结果就行,管它怎么执行的。

正确理解:

  • 查询慢往往是索引设计不当或 SQL 写法问题
  • 使用 EXPLAIN 分析查询计划是必备技能
  • 理解索引原理、B+Tree 结构、回表、覆盖索引

误区三:以为 ORM 能替代数据库基础

错误认知:用了 MyBatis/JPA,不需要懂 SQL 和数据库原理。

正确理解:

  • ORM 框架只是简化了 SQL 编写,不能替代数据库知识
  • N+1 查询问题、批量插入优化、索引失效等需要懂原理才能解决
  • 复杂查询、性能优化还是需要手写 SQL

误区四:表设计时不考虑约束和索引

错误认知:先实现功能,性能出问题再优化。

正确理解:

  • 表设计阶段就应该考虑索引和约束
  • 后期优化成本高,可能需要停机维护
  • 合理的设计可以避免很多问题

误区五:把缓存和数据库混为一谈

错误认知:Redis 也是数据库,为什么还要用 MySQL?

正确理解:

  • Redis 是内存数据库,适合缓存、临时数据
  • MySQL 是关系型数据库,适合持久化存储核心业务数据
  • 两者职责不同,通常搭配使用(缓存 + 数据库)

误区六:忽视事务的重要性

错误认知:事务没什么用,代码逻辑写好就行。

正确理解:

  • 没有事务保证,并发场景下会出现数据不一致
  • 理解 ACID、隔离级别、锁机制
  • 分布式场景还需要理解分布式事务

最佳实践

1. 表设计规范

sql
-- 推荐:完整的表设计
CREATE TABLE user_account (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(64) NOT NULL COMMENT '用户名',
    phone VARCHAR(20) COMMENT '手机号',
    email VARCHAR(100) COMMENT '邮箱',
    password VARCHAR(100) NOT NULL COMMENT '密码(加密)',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1正常,0禁用',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted_at DATETIME COMMENT '删除时间(软删除)',
    
    UNIQUE KEY uk_username (username),
    UNIQUE KEY uk_phone (phone),
    UNIQUE KEY uk_email (email),
    KEY idx_status (status),
    KEY idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户账户表';

2. 查询优化规范

sql
-- × 不推荐:SELECT *
SELECT * FROM user_account WHERE status = 1;

-- √ 推荐:明确字段
SELECT id, username, phone, status FROM user_account WHERE status = 1;

-- × 不推荐:索引列使用函数
SELECT * FROM user_account WHERE DATE(created_at) = '2026-03-29';

-- √ 推荐:范围查询
SELECT * FROM user_account 
WHERE created_at >= '2026-03-29 00:00:00' 
  AND created_at < '2026-03-30 00:00:00';

-- × 不推荐:大偏移量分页
SELECT * FROM user_account ORDER BY id LIMIT 100000, 10;

-- √ 推荐:延迟关联
SELECT u.* FROM user_account u
INNER JOIN (
    SELECT id FROM user_account ORDER BY id LIMIT 100000, 10
) t ON u.id = t.id;

3. 事务使用规范

java
// × 不推荐:长事务
@Transactional
public void processOrder(Long orderId) {
    // 1. 查询订单
    Order order = orderMapper.selectById(orderId);
    
    // 2. 调用第三方支付接口(耗时操作)
    paymentService.callThirdPartyPayment(order);
    
    // 3. 更新订单状态
    orderMapper.updateStatus(orderId, OrderStatus.PAID);
    
    // 4. 发送短信通知(耗时操作)
    smsService.sendNotification(order);
    
    // 问题:事务包含了远程调用和消息发送,持有锁时间过长
}

// √ 推荐:缩短事务范围
public void processOrder(Long orderId) {
    // 1. 查询订单(不需要事务)
    Order order = orderMapper.selectById(orderId);
    
    // 2. 调用第三方支付接口(不需要事务)
    paymentService.callThirdPartyPayment(order);
    
    // 3. 更新订单状态(需要事务)
    orderService.updateOrderStatus(orderId, OrderStatus.PAID);
    
    // 4. 发送短信通知(异步,不需要事务)
    smsService.sendNotificationAsync(order);
}

@Service
public class OrderService {
    @Transactional
    public void updateOrderStatus(Long orderId, OrderStatus status) {
        orderMapper.updateStatus(orderId, status);
    }
}

后续阅读建议

按以下顺序学习,建立完整的数据库知识体系:

  1. 基础操作:继续阅读 MySQL 基础,掌握 MySQL 的安装配置和基本使用
  2. SQL 语法:阅读 DDLDML,掌握建表和数据操作
  3. 进阶特性:学习 索引与事务排障MVCC 与事务隔离级别
  4. Java 集成:结合 JDBC 总览 理解 Java 程序如何连接数据库
  5. 性能优化:学习数据库性能优化、分库分表等高级主题

学习建议:

  • 理论与实践结合:看完概念后动手实践
  • 关注原理:不仅知道怎么用,还要知道为什么
  • 积累经验:在实际项目中遇到问题,深入分析原因

数据库是 Java 后端开发的核心技能,扎实的数据库基础能帮助你设计出高性能、高可用的系统。不要停留在"会写 SQL"的层面,深入理解数据库原理,才能真正成为优秀的后端工程师。

版本差异(MySQL 5.7 → 8.0/8.4)

特性旧版(本文编写时,MySQL 5.7)当前(MySQL 8.0/8.4 LTS)
默认字符集utf8(需显式配置 utf8mb4)utf8mb4(MySQL 8.0 起默认)
索引普通 B+Tree降序索引、隐藏索引、函数索引(8.0+)
SQL 能力常规查询递归 CTE、窗口函数(8.0+)
版本策略5.78.0(主流)/ 8.4 LTS / 9.x(创新版)
Java 驱动mysql-connector-java 5.x/8.0mysql-connector-j 8.x/9.x

本文基于 MySQL 5.7 编写,核心概念(索引、事务、锁、MVCC、InnoDB)在 8.0/8.4 中依然适用;8.0 的默认字符集、隐藏索引与 SQL 增强(CTE/窗口函数)是升级后的主要差异。