数据库
数据库是后端开发的基础设施之一。对 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 + MySQL | Redis 缓存热点数据,MySQL 持久化 |
| 日志、文档数据 | MongoDB | 数据结构灵活,无需预定义字段 |
| 海量数据存储 | 分布式数据库 | 需要水平扩展能力 |
对于绝大多数 Java 中后台项目,关系型数据库仍然是业务主数据的核心载体。
关系型数据库的理论基础
关系模型
关系型数据库基于数学中的关系模型,由 E.F.Codd 于 1970 年提出。核心概念:
- 关系(Relation):即表,一个关系就是一个二维表
- 元组(Tuple):即行,表中的一条记录
- 属性(Attribute):即列,表中的一个字段
- 域(Domain):属性的取值范围,如性别只能取"男""女"
- 键(Key):能唯一标识一个元组的属性或属性组
关系模型的优点:
- 数据结构简单清晰(二维表)
- 理论基础扎实(集合论、关系代数)
- 支持复杂的数据操作(选择、投影、连接等)
ACID 特性
事务(Transaction)是数据库操作的基本单位,具有 ACID 四大特性:
| 特性 | 英文 | 含义 | 说明 |
|---|---|---|---|
| 原子性 | Atomicity | 事务是不可分割的工作单位 | 事务中的操作要么都做,要么都不做 |
| 一致性 | Consistency | 事务执行前后数据库状态一致 | 从一个一致性状态转换到另一个一致性状态 |
| 隔离性 | Isolation | 多个事务并发执行时互不干扰 | 各事务之间相互隔离,不会互相影响 |
| 持久性 | Durability | 事务提交后永久生效 | 即使系统崩溃,数据也不会丢失 |
示例:银行转账
-- 事务开始
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 基础上,非主属性不传递依赖主键 | 消除传递依赖 |
示例:
-- 不符合 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. 结构化管理
数据库通过表结构、约束、索引等机制,保证数据的完整性和一致性。
-- 表结构定义数据类型和约束
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. 查询能力
数据库支持复杂的查询条件、排序、分页、聚合等操作。
-- 复杂查询示例
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. 一致性保证
数据库通过约束和事务保证数据一致性。
-- 约束示例
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(多版本并发控制)保证并发访问的正确性。
事务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,功能完善,性能优秀
数据库中的核心概念
库、表、行、列
关系型数据库通常按以下层次组织数据:
数据库(Database)
└── 表(Table)
├── 行(Row) / 记录(Record)
│ ├── 列(Column) / 字段(Field)
│ ├── 列(Column)
│ └── 列(Column)
└── 行(Row)示例:用户表
-- 创建数据库
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 | 列的默认值 |
-- 约束示例
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 等数据结构存储索引
- 索引文件存储在磁盘,读取时加载到内存
- 通过索引可以快速定位数据,避免全表扫描
索引类型:
| 索引类型 | 说明 | 适用场景 |
|---|---|---|
| 主键索引 | 自动创建,聚簇索引 | 主键字段 |
| 唯一索引 | 列值唯一 | 唯一字段(手机号、邮箱等) |
| 普通索引 | 最基本的索引 | 经常作为查询条件的字段 |
| 组合索引 | 多列组合 | 多条件查询 |
| 全文索引 | 文本搜索 | 文章内容搜索 |
索引设计原则:
- 为经常出现在 WHERE、JOIN、ORDER BY 中的字段建索引
- 组合索引遵循最左前缀原则
- 避免在小表、更新频繁的表上建过多索引
- 索引不是越多越好,会影响写入性能
-- 创建索引
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 代码示例(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 事务示例
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 在很大程度上避免幻读
开发中的典型应用场景
场景一:用户注册
用户注册不是简单插入一条记录,还会涉及:
@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();
}
}涉及的知识点:
- 唯一约束保证用户名和手机号唯一
- 事务保证多张表同时成功或失败
- 密码加密存储
场景二:订单创建
订单业务通常会同时涉及订单表写入、库存扣减、支付状态更新。
@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 能不能查出来",而是性能问题。
问题示例:
-- 慢查询:全表扫描
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';-- 慢查询:未命中索引的排序
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 分析查询:
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(临时表)优化建议:
- 使用 EXPLAIN 分析查询计划
- 避免在 WHERE 条件中对索引列使用函数或计算
- 避免 SELECT *,只查询需要的字段
- 合理使用覆盖索引,避免回表
- 分页查询优化(避免 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. 表设计规范
-- 推荐:完整的表设计
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. 查询优化规范
-- × 不推荐: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. 事务使用规范
// × 不推荐:长事务
@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);
}
}后续阅读建议
按以下顺序学习,建立完整的数据库知识体系:
- 基础操作:继续阅读 MySQL 基础,掌握 MySQL 的安装配置和基本使用
- SQL 语法:阅读 DDL、DML,掌握建表和数据操作
- 进阶特性:学习 索引与事务排障、MVCC 与事务隔离级别
- Java 集成:结合 JDBC 总览 理解 Java 程序如何连接数据库
- 性能优化:学习数据库性能优化、分库分表等高级主题
学习建议:
- 理论与实践结合:看完概念后动手实践
- 关注原理:不仅知道怎么用,还要知道为什么
- 积累经验:在实际项目中遇到问题,深入分析原因
数据库是 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.7 | 8.0(主流)/ 8.4 LTS / 9.x(创新版) |
| Java 驱动 | mysql-connector-java 5.x/8.0 | mysql-connector-j 8.x/9.x |
本文基于 MySQL 5.7 编写,核心概念(索引、事务、锁、MVCC、InnoDB)在 8.0/8.4 中依然适用;8.0 的默认字符集、隐藏索引与 SQL 增强(CTE/窗口函数)是升级后的主要差异。