🗄️ 数据库架构
深入学习数据库架构设计,包括关系型数据库设计、分库分表、读写分离、主从复制和数据库中间件
学习目标
- 掌握关系型数据库的设计原则和最佳实践
- 理解数据库分库分表的策略和实现方法
- 学习读写分离架构的设计和应用场景
- 了解主从复制的原理和配置方法
- 掌握数据库中间件的选择和使用
数据库架构概述
数据库架构是系统架构中的重要组成部分,它决定了数据的存储、访问和管理方式。随着业务规模的增长和数据量的激增,传统的单机数据库已经无法满足高并发、大数据量的需求,因此需要采用更加复杂和高效的数据库架构设计。
数据库架构设计需要考虑数据一致性、可用性、分区容错性(CAP定理)、性能、扩展性等多个维度,在不同场景下做出合适的权衡。
数据存储
设计合理的数据模型和存储结构,确保数据的完整性和一致性。
性能优化
通过索引优化、查询优化、缓存策略等手段提升数据库性能。
水平扩展
采用分库分表、读写分离等技术实现数据库的水平扩展。
关系型数据库设计
数据库设计原则
关系型数据库设计遵循一系列规范化原则,确保数据的一致性、完整性和可维护性。
- 第一范式(1NF):确保每个字段都是原子性的,不可再分
- 第二范式(2NF):消除部分函数依赖,确保非主键字段完全依赖于主键
- 第三范式(3NF):消除传递函数依赖,减少数据冗余
- BCNF范式:更严格的第三范式,消除所有非平凡的函数依赖
表结构设计
-- 用户表设计示例
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
status TINYINT DEFAULT 1 COMMENT '1:正常 0:禁用',
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_created_at (created_at)
);
索引设计策略
- 主键索引:每个表必须有主键,通常使用自增ID
- 唯一索引:确保字段值的唯一性,如用户名、邮箱
- 普通索引:提升查询性能,根据查询频率和选择性创建
- 复合索引:多字段组合索引,注意字段顺序
- 覆盖索引:索引包含查询所需的所有字段,避免回表
数据库分库分表
当单个数据库无法承载业务增长带来的数据量和并发压力时,需要采用分库分表策略来实现水平扩展。
垂直拆分
垂直拆分是按照业务功能将不同的表拆分到不同的数据库中,每个数据库负责特定的业务模块。
用户数据库
存储用户信息、认证数据等用户相关的表。
订单数据库
存储订单信息、支付记录等交易相关的表。
商品数据库
存储商品信息、库存数据等商品相关的表。
水平拆分
水平拆分是将同一个表的数据按照某种规则分散到多个数据库或表中,每个分片存储部分数据。
分片策略
- 范围分片:按照数据范围分片,如按时间、ID范围
- 哈希分片:使用哈希函数计算分片位置,分布均匀
- 目录分片:维护分片映射表,灵活但增加复杂性
- 一致性哈希:支持动态扩容,减少数据迁移
// 哈希分片示例
public class HashShardingStrategy {
private static final int SHARD_COUNT = 8;
public int getShardIndex(Long userId) {
return Math.abs(userId.hashCode()) % SHARD_COUNT;
}
public String getTableName(Long userId) {
int shardIndex = getShardIndex(userId);
return "user_" + shardIndex;
}
}
读写分离
读写分离是一种常见的数据库架构模式,通过将读操作和写操作分离到不同的数据库实例上,提升系统的并发处理能力和查询性能。
架构设计
- 主库(Master):处理所有写操作(INSERT、UPDATE、DELETE)
- 从库(Slave):处理所有读操作(SELECT)
- 数据同步:主库变更通过binlog同步到从库
- 负载均衡:多个从库之间进行读负载均衡
实现方案
@Component
public class DataSourceRouter {
@Autowired
private DataSource masterDataSource;
@Autowired
private List<DataSource> slaveDataSources;
public DataSource getDataSource(boolean isWrite) {
if (isWrite) {
return masterDataSource;
} else {
// 从库负载均衡
int index = ThreadLocalRandom.current().nextInt(slaveDataSources.size());
return slaveDataSources.get(index);
}
}
}
注意事项
- 数据延迟:主从同步存在延迟,需要考虑读取到旧数据的情况
- 事务处理:事务内的读操作应该路由到主库,保证一致性
- 故障切换:主库故障时需要快速切换到从库
- 监控告警:监控主从延迟、同步状态等关键指标
主从复制
主从复制是MySQL等关系型数据库提供的数据同步机制,通过将主库的变更操作同步到从库,实现数据的高可用和读写分离。
复制原理
Binlog记录
主库将所有变更操作记录到二进制日志(binlog)中。
日志传输
从库的IO线程从主库读取binlog并写入relay log。
重放执行
从库的SQL线程读取relay log并重放执行。
配置示例
# 主库配置 (my.cnf)
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
# 从库配置 (my.cnf)
[mysqld]
server-id = 2
relay-log = mysql-relay-bin
read_only = 1
复制模式
- 异步复制:主库不等待从库确认,性能最好但可能丢失数据
- 半同步复制:主库等待至少一个从库确认,平衡性能和可靠性
- 同步复制:主库等待所有从库确认,可靠性最高但性能较差
数据库中间件
数据库中间件是位于应用程序和数据库之间的软件层,提供数据路由、负载均衡、读写分离、分库分表等功能,简化复杂数据库架构的管理。
主流中间件
ShardingSphere
Apache开源的分布式数据库中间件,支持分库分表、读写分离、分布式事务。
MyCat
基于阿里开源Cobar的数据库分库分表中间件,支持MySQL协议。
Vitess
YouTube开源的MySQL集群系统,提供水平扩展和高可用能力。
ShardingSphere配置示例
# application.yml
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/demo_ds_0
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/demo_ds_1
username: root
password: root
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: t_order_inline
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database_inline
sharding-algorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 2}
中间件选择考虑因素
- 功能需求:是否支持所需的分库分表、读写分离等功能
- 性能表现:中间件本身的性能开销和延迟
- 运维复杂度:部署、配置、监控的复杂程度
- 社区活跃度:开源项目的维护状态和社区支持
- 兼容性:与现有技术栈的兼容程度
数据库架构最佳实践
设计原则
- 渐进式演进:从简单架构开始,随业务发展逐步演进
- 读写分离优先:在分库分表之前先考虑读写分离
- 避免跨库事务:尽量避免分布式事务,通过业务设计规避
- 监控和告警:建立完善的数据库监控和告警体系
- 备份和恢复:制定完善的数据备份和灾难恢复策略
性能优化
- 索引优化:合理创建和使用索引,避免过度索引
- 查询优化:优化SQL语句,避免全表扫描
- 连接池管理:合理配置数据库连接池参数
- 缓存策略:使用Redis等缓存减少数据库压力
- 批量操作:使用批量插入、更新减少网络开销
运维管理
- 容量规划:根据业务增长预测进行容量规划
- 版本管理:使用数据库迁移工具管理表结构变更
- 安全管理:设置合理的用户权限和访问控制
- 性能监控:监控慢查询、连接数、锁等待等指标
- 故障处理:建立故障处理流程和应急预案