面试题答案
一键面试存储设计优化策略
- 合理使用范式与反范式
- 范式方面:严格遵循第三范式(3NF)来设计核心业务表,减少数据冗余,确保数据的一致性。例如,在订单系统中,将客户信息、订单信息、订单详情等分别存储在不同的表中,通过主键和外键关联。这样在数据更新时,不会出现同一数据在多处存储导致的不一致问题。
- 反范式方面:对于查询频繁且对实时性要求高的场景,适当引入反范式设计。比如在一些统计报表相关的表中,为了避免多表复杂连接查询,可冗余一些常用字段。但要注意冗余字段的更新一致性,可通过触发器等机制在主表数据更新时同步更新冗余字段。
- 分区表设计
- 范围分区:根据时间、ID 等具有明显范围特征的字段进行分区。如订单表按订单创建时间进行月份分区,将历史订单数据存储在不同分区,新订单写入当前月份分区。这样查询近期订单时,只需扫描当前分区,大大减少了数据扫描范围。
- 哈希分区:对于数据分布较为均匀,且希望数据在各个分区均匀分布的情况,可采用哈希分区。例如用户表,根据用户 ID 进行哈希分区,可有效分散数据,提升并发读写性能。
- 索引优化
- 创建复合索引:对于经常一起出现在 WHERE 子句中的字段,创建复合索引。比如在用户登录场景中,经常根据用户名和密码进行查询,可创建(username, password)的复合索引,注意索引字段顺序要遵循最左前缀原则,以提高索引利用率。
- 覆盖索引:尽量使用覆盖索引,即索引包含查询所需的所有字段。这样在查询时,MySQL 无需回表操作,直接从索引中获取数据,大大提升查询性能。例如查询用户表中用户的姓名和邮箱,若创建(name, email)的索引,就可以实现覆盖索引。
查询优化策略
- SQL语句优化
- 避免全表扫描:通过合理使用索引,确保查询语句能够利用索引进行检索。例如,在 WHERE 子句中避免对索引字段使用函数操作,因为这样会导致索引失效,如
SELECT * FROM users WHERE UPPER(username) = 'ADMIN';
应改为SELECT * FROM users WHERE username = 'admin';
- 优化子查询:尽量将子查询改写为连接查询,因为连接查询在很多情况下执行效率更高。例如子查询
SELECT * FROM orders WHERE customer_id IN (SELECT customer_id FROM customers WHERE region = 'Asia');
可改写为连接查询SELECT orders.* FROM orders JOIN customers ON orders.customer_id = customers.customer_id WHERE customers.region = 'Asia';
- 避免全表扫描:通过合理使用索引,确保查询语句能够利用索引进行检索。例如,在 WHERE 子句中避免对索引字段使用函数操作,因为这样会导致索引失效,如
- 查询缓存
- 开启查询缓存:对于一些不经常变化的数据,开启 MySQL 的查询缓存功能。例如一些配置表的数据,查询结果不随时间频繁变化,查询缓存可以直接返回之前的查询结果,减少数据库的查询压力。但要注意,当表数据发生变化时,查询缓存中的相关数据会失效,所以适合数据相对静态的场景。
- 缓存粒度控制:根据业务需求合理控制查询缓存的粒度。可以按表、按查询语句等不同粒度进行缓存,避免缓存粒度太粗导致缓存失效频繁,或者粒度太细导致缓存管理成本过高。
架构层面优化策略
- 主从复制架构
- 读写分离:设置主库用于写操作,从库用于读操作。主库负责处理数据的插入、更新和删除操作,从库通过主从复制机制同步主库的数据,并承担大量的读请求。这样可以有效减轻主库的压力,提升系统的并发处理能力。例如在新闻网站系统中,文章发布时写入主库,而用户浏览文章时从从库读取数据。
- 多从库设置:根据读请求的压力,合理增加从库数量。可以根据地域、业务模块等进行从库的划分,比如对于不同地区的用户,分别由不同地域的从库提供数据读取服务,减少数据传输延迟。
- 分布式架构
- 分库分表:当数据量和业务规模进一步扩大时,采用分库分表策略。可以按业务模块进行垂直分库,如将用户相关的数据放在一个库,订单相关的数据放在另一个库。水平分表则可根据数据量和查询特征,如按用户 ID 范围、时间范围等进行分表,将数据分散存储在多个数据库和表中,提升系统的扩展性和性能。
- 使用分布式缓存:引入 Redis 等分布式缓存,将热点数据存储在缓存中。例如热门商品的信息、频繁查询的用户信息等,先从缓存中获取数据,若缓存中不存在再查询数据库,有效减轻数据库压力,提升系统响应速度。