1. 数据库索引优化实战:从B+树到执行计划
索引优化是数据库性能调优的核心环节,也是面试中最常被深挖的技术点。以MySQL的InnoDB引擎为例,其索引实现基于B+树数据结构,这种设计使得等值查询和范围查询都能保持O(log n)的时间复杂度。但在实际业务中,仅了解数据结构远远不够。
1.1 复合索引的最左匹配原则
假设我们有一个用户表包含字段:user_id(主键)、username、age、city,现在需要频繁执行以下查询:
SELECT * FROM users WHERE city='北京' AND age>25 ORDER BY username;此时创建复合索引的正确姿势应该是:
ALTER TABLE users ADD INDEX idx_city_age_username (city, age, username);这个索引之所以高效,是因为:
- 完全覆盖WHERE条件中的字段(city和age)
- 满足ORDER BY的username字段
- 遵循最左匹配原则,查询条件从索引最左侧开始匹配
常见误区:将选择性高的字段放在索引最前面。实际上应该按照查询条件的出现顺序和过滤性综合考量。
1.2 索引失效的典型场景
即使创建了合适的索引,这些情况仍会导致索引失效:
- 在索引列上使用函数:
WHERE YEAR(create_time)=2023 - 隐式类型转换:
WHERE user_id='10001'(user_id是整型) - 使用
!=或<>操作符 - 使用
OR连接非索引列条件 - 使用
LIKE以通配符开头:WHERE username LIKE '%张'
实测案例:某用户表有200万数据,对手机号字段建立索引后,执行WHERE mobile LIKE '138%'耗时3ms,而WHERE mobile LIKE '%8888'耗时达到1200ms。
1.3 EXPLAIN执行计划深度解读
理解EXPLAIN输出是诊断查询性能的关键。重点关注以下列:
| 列名 | 关键值 | 含义 |
|---|---|---|
| type | const | 通过主键或唯一索引查询 |
| ref | 普通索引查询 | |
| range | 索引范围扫描 | |
| index | 全索引扫描 | |
| ALL | 全表扫描 | |
| key_len | 数值 | 索引使用的字节数,可判断是否用到全部索引列 |
| Extra | Using filesort | 需要额外排序操作 |
| Using temporary | 需要创建临时表 |
一个真实的优化案例:某订单查询接口响应缓慢,EXPLAIN显示type=ALL且Extra有Using filesort。通过添加(user_id, status)复合索引,并将ORDER BY改为ORDER BY user_id, create_time,查询时间从2.3秒降至23ms。
2. Redis持久化机制:RDB与AOF的工程实践
Redis作为内存数据库,持久化机制直接影响数据安全性和服务性能。面试中常被问及RDB和AOF的差异及适用场景。
2.1 RDB快照的底层实现
RDB通过fork子进程进行数据快照,关键配置参数:
save 900 1 # 900秒内至少1次修改触发保存 save 300 10 # 300秒内至少10次修改触发保存 dbfilename dump.rdb stop-writes-on-bgsave-error yes技术细节:
- fork操作使用写时复制(Copy-On-Write)技术
- 子进程遍历所有数据库生成紧凑的二进制文件
- 默认采用LZF压缩算法
生产环境经验:
- 当数据集较大(>10GB)时,fork可能阻塞主线程
- 虚拟机环境fork性能较差,建议物理机部署
- 备份时建议关闭AOF重写:
config set auto-aof-rewrite-percentage 0
2.2 AOF日志的运作机制
AOF以追加方式记录写命令,提供三种同步策略:
appendonly yes appendfsync always # 每个命令都同步,最安全但性能差 appendfsync everysec # 每秒同步,推荐配置 appendfsync no # 由操作系统决定AOF重写过程:
- 创建新AOF文件
- 遍历数据库生成当前数据状态的命令
- 将新文件原子替换旧文件
性能优化技巧:
- 设置
no-appendfsync-on-rewrite yes避免重写时同步阻塞 - AOF文件过大时,手动执行
BGREWRITEAOF而非等待自动触发 - 定期检查AOF文件完整性:
redis-check-aof --fix
2.3 混合持久化实践
Redis 4.0引入的混合持久化结合了两者优点:
aof-use-rdb-preamble yes这种模式下:
- AOF文件前半段是RDB格式的全量数据
- 后半段是增量AOF日志
- 重启时先加载RDB部分再重放AOF
某电商平台实测数据:
- 纯RDB:启动加载8GB数据耗时45秒
- 纯AOF:加载7GB AOF耗时6分钟
- 混合模式:加载3GB(RDB)+200MB(AOF)耗时28秒
3. PostgreSQL向量存储:AI时代的数据处理
随着AI应用普及,PostgreSQL凭借其强大的扩展能力成为向量数据处理的热门选择。
3.1 pgvector扩展安装与使用
安装步骤:
# 编译安装 git clone --branch v0.5.1 https://github.com/pgvector/pgvector.git cd pgvector make && make install # 数据库内创建扩展 CREATE EXTENSION vector;创建带向量列的表:
CREATE TABLE items ( id bigserial PRIMARY KEY, name text, description text, embedding vector(384) # 384维向量 );3.2 向量相似度搜索
pgvector支持多种距离计算方式:
-- 欧式距离 SELECT * FROM items ORDER BY embedding <-> '[0.1, 0.2, ...]' LIMIT 10; -- 余弦相似度 SELECT * FROM items ORDER BY embedding <=> '[0.1, 0.2, ...]' LIMIT 10; -- 内积 SELECT * FROM items ORDER BY embedding <#> '[0.1, 0.2, ...]' LIMIT 10;性能优化方案:
- 创建IVFFlat索引:
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);- 调整
lists参数平衡精度和速度 - 对十亿级数据考虑使用HNSW索引
3.3 实际应用案例
某推荐系统实现方案:
- 使用Sentence-BERT将商品描述转换为384维向量
- 每周全量更新向量数据
- 用户浏览时实时计算相似商品:
SELECT id, name FROM items WHERE category = 'electronics' ORDER BY embedding <=> (SELECT embedding FROM users WHERE id=123) LIMIT 50;性能数据:
- 100万条向量数据
- 无索引:查询耗时1200ms
- IVFFlat索引:查询耗时23ms(召回率92%)
- HNSW索引:查询耗时45ms(召回率98%)
4. 跨数据库协同方案设计
现代系统往往需要多种数据库协同工作,如何设计高效的数据流转方案成为面试高频考点。
4.1 MySQL到Redis的缓存策略
经典缓存模式对比:
| 模式 | 写流程 | 读流程 | 适用场景 |
|---|---|---|---|
| Cache-Aside | 1. 写数据库 2. 删缓存 | 1. 查缓存 2. 未命中则查库 3. 回填缓存 | 通用场景 |
| Write-Through | 1. 写缓存 2. 缓存同步写库 | 直接读缓存 | 写少读多 |
| Write-Behind | 1. 写缓存 2. 异步批量写库 | 直接读缓存 | 高写入负载 |
防缓存击穿方案:
def get_user(user_id): # 尝试获取锁 lock_key = f"lock:{user_id}" if not redis.setnx(lock_key, 1, ex=10): time.sleep(0.1) return get_user(user_id) try: # 查缓存 user = redis.get(f"user:{user_id}") if user: return json.loads(user) # 查数据库 user = db.query("SELECT * FROM users WHERE id=?", user_id) if user: redis.setex(f"user:{user_id}", 3600, json.dumps(user)) return user finally: redis.delete(lock_key)4.2 PostgreSQL与Redis的实时同步
使用Debezium实现CDC同步:
# debezium配置示例 connector.class: io.debezium.connector.postgresql.PostgresConnector database.hostname: postgres database.port: 5432 database.user: replicator database.password: password database.dbname: mydb database.server.name: pg_server table.include.list: public.users plugin.name: pgoutput处理逻辑:
- Debezium监控WAL日志
- 将变更事件发送到Kafka
- 消费者服务更新Redis
4.3 异构数据库事务一致性
Saga模式实现示例:
// 订单创建Saga public class CreateOrderSaga { @SagaStart public void handle(CreateOrderCommand command) { // 1. 创建订单(MySQL) Order order = orderService.create(command); // 2. 扣减库存(Redis) inventoryService.reserve(order.getItems()); // 3. 扣减积分(PostgreSQL) creditService.deduct(order.getUserId(), order.getPoints()); } @SagaCompensation public void compensate(CreateOrderCommand command) { // 逆向操作 creditService.refund(command.getUserId(), command.getPoints()); inventoryService.release(command.getItems()); orderService.cancel(command.getOrderId()); } }某电商平台实测数据:
- 分布式事务成功率从98.7%提升到99.99%
- 平均延迟从320ms降至150ms
- 通过定时任务补偿解决0.01%的异常情况