分享
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
### 梦彻底醒了
这话是上周一个学生跟我说的,他双非一本大三下,刚结束人生第一次面试,他跟我说自己那点底细在面试官面前根本藏不住 —— **简历上编的项目**,被追问两句就露了馅;**头天晚上临时抱佛脚背的八股文**,面试的时候一句也想不起来。
他说大学三年是真玩爽了,课能逃就逃,时间大多耗在宿舍里打游戏。直到面试官盯着他问 "这项目你到底做没做过",他才反应过来,**那些偷过的懒,迟早要变成拦路的坎**。
现在他暑期实习是指望不上了,目标很明确:**冲秋招**。
其实像他这样的学生我见过不少,总觉得时间还多,混到毕业再说。可职场不看你过去有多 "爽",只看你现在能拿出什么本事。**编的项目经不住推敲,临时背的知识点撑不起面试**,这些都是实实在在的教训。
### 醒了就不算晚
秋招还有时间,把那些打游戏的功夫用在补八股、做真项目上,比什么都强。别等机会真来了,又只能眼睁睁看着它溜走。
我把他的故事分享出来,也是想给还在迷茫的同学提个醒:**与其在虚拟世界里消磨时光,不如尽早认清现实,脚踏实地提升自己,毕竟为未来努力,什么时候开始都值得**。
>如果你和他有同样的烦恼,欢迎**关注并且私信**我,我替你出谋划策。
### 一起加油
话都说到这了,文章的剩余部分我就分享一些**数据库和缓存的常见面试题**供大家学习(复习):
### **MySQL常见面试题**
#### 1. 索引的作用是什么?有哪些类型?
**答案**:
索引是一种数据结构,用于快速定位表中的数据,减少I/O操作。其核心作用包括:
- **加速查询**:通过B+树或哈希结构快速定位数据。
- **减少锁竞争**:缩小查询范围,降低锁的粒度。
- **支持覆盖索引**:避免回表查询,直接从索引中获取数据。
常见索引类型包括:
- **B+树索引**:适用于范围查询和排序(如`WHERE id > 100`)。
- **哈希索引**:适用于等值查询(如`WHERE name='Alice'`),但不支持范围查询。
- **全文索引**:用于文本搜索(如`MATCH AGAINST`)。
- **组合索引**:多个字段联合索引,遵循**最左前缀原则**。
**注意**:索引并非越多越好,需避免过度索引导致写入性能下降。
#### 2. 事务的ACID特性是什么?如何实现?
**答案**:
ACID是事务的四大特性:
- **原子性(Atomicity)**:事务中的操作要么全部成功,要么全部回滚。
- **一致性(Consistency)**:事务执行前后,数据状态保持合法。
- **隔离性(Isolation)**:多个事务并发执行时,相互不干扰。
- **持久性(Durability)**:事务提交后,数据永久保存。
**实现机制**:
- **原子性**:通过Undo Log实现回滚。
- **持久性**:通过Redo Log实现故障恢复。
- **隔离性**:通过锁机制和MVCC(多版本并发控制)实现。
- **一致性**:由原子性、隔离性和持久性共同保证。
**MySQL的事务隔离级别**(从低到高):
- **读未提交(Read Uncommitted)**:可能出现脏读。
- **读已提交(Read Committed)**:避免脏读,但可能出现不可重复读。
- **可重复读(Repeatable Read)**:默认级别,避免脏读和不可重复读,但可能出现幻读。
- **串行化(Serializable)**:最高级别,通过锁表实现,性能较低。
#### 3. InnoDB和MyISAM的区别?
**答案**:
| **特性** | **InnoDB** | **MyISAM** |
|------------------|-------------------------------------|-----------------------------------|
| **事务支持** | 支持(ACID特性) | 不支持 |
| **锁机制** | 行锁(细粒度) | 表锁(粗粒度) |
| **索引结构** | B+树,聚簇索引 | B+树,非聚簇索引 |
| **外键支持** | 支持 | 不支持 |
| **存储文件** | .frm(表结构)、.ibd(数据+索引) | .frm(表结构)、.MYD(数据)、.MYI(索引) |
| **适用场景** | 高并发写入、事务型业务 | 读多写少、非事务场景 |
**总结**:InnoDB是MySQL的默认引擎,适合电商、金融等需要事务和高并发的场景;MyISAM已逐渐被淘汰,仅用于历史项目。
#### 4. 数据库锁的类型有哪些?乐观锁和悲观锁的区别?
**答案**:
数据库锁按粒度分为:
- **表锁**:锁整个表,并发性能差(如MyISAM)。
- **行锁**:锁单行数据,并发性能好(如InnoDB)。
- **页锁**:介于表锁和行锁之间。
按操作类型分为:
- **共享锁(S锁)**:允许读取,不允许修改。
- **排他锁(X锁)**:禁止其他事务加锁。
**乐观锁 vs 悲观锁**:
- **悲观锁**:假设数据冲突频繁,通过`SELECT ... FOR UPDATE`手动加锁,阻塞其他事务。
- **乐观锁**:假设数据冲突较少,通过版本号(`version`字段)或CAS(Compare-And-Swap)实现无锁操作。
**场景**:悲观锁适用于冲突频繁的场景(如秒杀),乐观锁适用于冲突较少的场景(如商品库存更新)。
#### 5. 如何优化慢查询?
**答案**:
优化步骤如下:
1. **定位慢查询**:开启慢查询日志(`slow_query_log`),使用`pt-query-digest`分析。
2. **分析执行计划**:通过`EXPLAIN`查看索引使用情况,检查是否存在全表扫描。
3. **优化索引**:
- 为频繁查询的字段添加索引。
- 避免索引失效(如避免在索引列使用函数、`OR`条件)。
- 使用覆盖索引(索引包含所有查询字段)。
4. **优化SQL语句**:
- 避免`SELECT *`,只查询必要字段。
- 减少子查询,使用`JOIN`替代。
- 分页优化(如`WHERE id > last_id LIMIT 10`)。
5. **硬件与配置**:
- 增加内存,提升缓存命中率。
- 调整InnoDB参数(如`innodb_buffer_pool_size`)。
6. **分库分表**:数据量过大时,按业务拆分数据库或表。
#### 6. 主从复制的原理是什么?如何解决主从延迟?
**答案**:
**主从复制原理**:
1. **主库(Master)**:将更新操作记录到二进制日志(Binlog)。
2. **从库(Slave)**:通过I/O线程读取主库的Binlog,写入中继日志(Relay Log)。
3. **从库**:通过SQL线程回放中继日志,实现数据同步。
**主从延迟原因**:
- 主库写入压力大,从库处理速度慢。
- 网络延迟或硬件性能差异。
**解决方法**:
1. **优化主库性能**:减少锁竞争,优化SQL语句。
2. **提升从库硬件**:增加CPU、内存或SSD。
3. **使用并行复制**:MySQL 5.7+支持多线程复制,并行回放事务。
4. **监控延迟**:通过`SHOW SLAVE STATUS`查看`Seconds_Behind_Master`,设置报警阈值。
#### 7. 分库分表的策略有哪些?如何选择?
**答案**:
分库分表策略分为:
1. **垂直拆分**:
- **垂直分库**:按业务模块拆分数据库(如用户库、订单库)。
- **垂直分表**:将大表拆分为主表和扩展表(如用户基本信息表和用户详情表)。
2. **水平拆分**:
- **哈希分片**:通过`hash(id) % N`将数据分散到N个库/表。
- **范围分片**:按时间或数值范围分片(如按年份拆分订单表)。
- **一致性哈希**:减少节点变动时的数据迁移量。
**选择策略**:
- **垂直拆分**:适用于业务模块清晰、数据耦合度低的场景。
- **水平拆分**:适用于数据量极大、单库性能不足的场景。
- **哈希分片**:数据分布均匀,但扩容复杂。
- **范围分片**:适合时间序列数据(如日志),但可能导致热点问题。
**注意事项**:
- 拆分后需解决分布式事务、跨库Join和分布式ID生成问题。
- 常用中间件:Sharding-JDBC、Mycat。
#### 8. 数据库范式是什么?为什么需要范式化?
**答案**:
数据库范式是设计数据库表结构的规则,目的是减少数据冗余,提高数据一致性。常见范式包括:
- **第一范式(1NF)**:字段原子性,不可再分。
- **第二范式(2NF)**:消除非主属性对主键的部分依赖。
- **第三范式(3NF)**:消除非主属性对主键的传递依赖。
**范式化的优点**:
- 减少数据冗余,节省存储空间。
- 降低数据不一致性风险。
- 便于维护和更新。
**缺点**:
- 增加表连接操作,可能影响查询性能。
**反范式化**:
在性能敏感的场景(如报表系统),可适当引入冗余字段,减少Join操作。
#### 9. 如何处理高并发下的写入问题?
**答案**:
1. **优化索引**:避免全表锁,减少锁竞争。
2. **批量写入**:使用`INSERT INTO ... VALUES (...),(...)`替代逐条插入。
3. **异步写入**:通过消息队列(如Kafka)缓冲写入请求,削峰填谷。
4. **分库分表**:分散写入压力,避免单库瓶颈。
5. **读写分离**:主库负责写入,从库负责读取,分担压力。
6. **事务优化**:减少事务范围,避免长事务。
7. **使用缓存**:对高频写入的数据(如点赞数),先写入缓存,再异步同步到数据库。
8. **批量提交**:InnoDB中设置`innodb_flush_log_at_trx_commit=2`,降低日志刷盘频率(牺牲部分持久化)。
#### 10. 解释一下MVCC(多版本并发控制)?
**答案**:
MVCC是InnoDB实现**可重复读**隔离级别的核心机制,通过版本号和Undo Log实现多版本数据共存。其原理如下:
1. **版本号**:每行数据有两个隐藏字段`trx_id`(事务ID)和`roll_ptr`(指向Undo Log的指针)。
2. **读视图(Read View)**:事务执行时生成一个快照,记录当前活跃事务ID的集合。
3. **数据可见性**:
- 当前事务修改的数据对自身可见。
- 其他事务修改的数据,根据版本号和读视图判断是否可见。
4. **Undo Log**:保存旧版本数据,用于回滚和MVCC读。
**优点**:
- 读写互不阻塞,提升并发性能。
- 避免幻读(通过间隙锁和MVCC结合)。
**局限性**:
- 增加内存和磁盘空间占用(需维护多个版本)。
- 长时间未提交的事务可能导致Undo Log膨胀。
### **Redis常见面试题**
#### 1. Redis支持哪些数据结构?各自的应用场景?
**答案**:
Redis支持以下数据结构:
1. **String**:
- **场景**:缓存简单对象(如用户ID对应的Session)、计数器(`INCR`)。
2. **List**:
- **场景**:消息队列(`LPUSH` + `BRPOP`)、最新消息列表。
3. **Hash**:
- **场景**:存储对象(如用户信息:`hset user:1 name Alice`)。
4. **Set**:
- **场景**:去重(如用户已读消息ID)、交集/并集运算(`SINTER`/`SUNION`)。
5. **ZSet(Sorted Set)**:
- **场景**:排行榜(如按分数排序的用户排名)、带权重的任务队列。
6. **HyperLogLog**:
- **场景**:基数统计(如日活用户数),内存占用低。
7. **Geospatial**:
- **场景**:地理位置查询(如附近的POI)。
**示例**:
- 微博热搜榜:使用ZSet,分数为搜索量。
- 电商购物车:使用Hash,键为用户ID,字段为商品ID,值为数量。
#### 2. Redis的持久化机制有哪些?RDB和AOF的区别?
**答案**:
Redis支持两种持久化机制:
1. **RDB(Redis Database Snapshot)**:
- **原理**:定期将内存数据快照保存到磁盘(`dump.rdb`)。
- **触发方式**:手动执行`SAVE`/`BGSAVE`,或配置自动触发(如`save 900 1`:900秒内至少1次写操作)。
- **优缺点**:
- 优点:恢复速度快,文件体积小。
- 缺点:可能丢失最后一次快照后的所有数据。
2. **AOF(Append Only File)**:
- **原理**:将写命令追加到日志文件(`appendonly.aof`)。
- **同步策略**:
- `always`:每条命令同步到磁盘(安全,性能低)。
- `everysec`:每秒同步一次(默认,平衡安全与性能)。
- `no`:由操作系统决定(性能高,风险大)。
- **优缺点**:
- 优点:数据丢失少,支持增量恢复。
- 缺点:文件体积大,恢复速度慢。
**选择建议**:
- 同时开启RDB和AOF,RDB用于快速恢复,AOF用于减少数据丢失。
- 对数据安全性要求极高的场景,使用`everysec`策略。
#### 3. 缓存穿透、雪崩、击穿是什么?如何解决?
**答案**:
1. **缓存穿透**:
- **定义**:查询不存在的数据,导致请求直达数据库。
- **解决方案**:
- **布隆过滤器**:提前过滤不存在的Key。
- **空值缓存**:缓存`NULL`值,设置短过期时间。
2. **缓存雪崩**:
- **定义**:大量缓存同时失效,请求瞬间压垮数据库。
- **解决方案**:
- **随机过期时间**:为缓存设置随机过期时间(如`TTL = 300 + random(60)`)。
- **热点数据永不过期**:通过后台线程定期更新缓存。
- **限流降级**:使用Hystrix等工具限制流量,返回默认值。
3. **缓存击穿**:
- **定义**:热点Key失效瞬间,大量请求直达数据库。
- **解决方案**:
- **互斥锁**:使用`SET ... NX`加锁,确保只有一个线程重建缓存。
- **逻辑过期**:在缓存中存储过期时间,业务线程主动更新缓存。
#### 4. Redis事务的特性?与MySQL事务的区别?
**答案**:
**Redis事务特性**:
- **原子性**:事务中的命令要么全部执行,要么全部失败。
- **一致性**:事务执行前后,数据状态保持一致。
- **隔离性**:事务执行期间,不会被其他事务打断(单线程模型保证)。
- **不支持持久性**:事务执行结果仅保存在内存,需依赖持久化机制。
**与MySQL事务的区别**:
| **特性** | **Redis事务** | **MySQL事务** |
|------------------|-------------------------------------|-----------------------------------|
| **原子性** | 命令级原子性(但不支持回滚) | 事务级原子性(支持回滚) |
| **隔离性** | 单线程执行,无并发问题 | 多线程并发,需锁机制和MVCC |
| **持久性** | 依赖RDB/AOF,不保证持久化 | 强持久化(Redo Log) |
| **使用场景** | 简单批量操作(如`INCR` + `SET`) | 复杂事务(如银行转账) |
**注意**:Redis事务中的命令在入队时检查语法错误,执行时不回滚。
#### 5. Redis集群的实现方式?主从复制和哨兵模式的区别?
**答案**:
Redis集群实现方式包括:
1. **主从复制**:
- **原理**:一个主节点(Master)负责写,多个从节点(Slave)负责读。
- **作用**:提升读性能和数据冗余。
2. **哨兵模式(Sentinel)**:
- **原理**:哨兵节点监控主从节点状态,自动进行故障转移(选举新主节点)。
- **优点**:实现高可用性,无需人工干预。
3. **Redis Cluster**:
- **原理**:数据分片存储(Sharding),每个节点存储部分数据,支持自动故障转移。
- **优点**:水平扩展,支持海量数据。
**主从复制 vs 哨兵模式**:
- **主从复制**:仅实现数据复制,需手动处理故障。
- **哨兵模式**:在主从复制基础上,增加自动故障转移功能,提升可用性。
**选择建议**:
- 小规模场景:主从复制 + 手动切换。
- 中规模场景:哨兵模式。
- 大规模场景:Redis Cluster。
#### 6. 如何优化Redis的性能?
**答案**:
优化Redis性能的策略包括:
1. **内存优化**:
- 合理设置`maxmemory`和淘汰策略(如`allkeys-lru`)。
- 避免大Key(`bigkey`),减少内存碎片。
- 使用`MEMORY USAGE`监控内存占用。
2. **网络优化**:
- 使用短连接(默认)或长连接(`keepalive`)。
- 调整TCP参数(如`tcp-backlog`)。
3. **命令优化**:
- 使用Pipeline减少网络往返次数。
- 避免在循环中执行Redis命令。
- 合理使用`MULTI`/`EXEC`事务。
4. **持久化优化**:
- 优先使用RDB,减少AOF日志量。
- 定期清理AOF日志(`BGREWRITEAOF`)。
5. **硬件与配置**:
- 使用SSD提升持久化速度。
- 绑定CPU核心,避免线程上下文切换。
6. **监控与调优**:
- 使用`redis-cli info`监控指标(如`used_memory`、`instantaneous_ops_per_sec`)。
- 分析慢查询日志(`slowlog get`),优化慢命令。
#### 7. 如何处理Redis的内存不足问题?
**答案**:
处理Redis内存不足的步骤如下:
1. **分析内存使用情况**:
- 使用`redis-cli --rdb memory`分析RDB文件的内存占用。
- 找出大Key(如`redis-cli --bigkeys`)。
2. **优化内存占用**:
- 压缩数据(如使用`snappy`压缩JSON)。
- 调整数据结构(如用`ZSet`替代`List`)。
- 淘汰过期Key(`active-expire-effort`参数)。
3. **调整淘汰策略**:
- 根据业务需求选择`maxmemory-policy`(如`volatile-ttl`、`allkeys-lru`)。
- 避免频繁淘汰热点数据。
4. **扩容**:
- 增加节点,采用Redis Cluster分片。
- 升级硬件,增加内存。
5. **持久化优化**:
- 降低AOF日志同步频率(`appendfsync everysec`)。
- 减少RDB快照频率,或改用AOF。
#### 8. 解释一下Redis的管道(Pipeline)和事务的区别?
**答案**:
| **特性** | **Pipeline** | **事务** |
|------------------|-------------------------------------|-----------------------------------|
| **命令执行** | 批量发送命令,服务端顺序执行 | 命令入队,通过`EXEC`原子执行 |
| **原子性** | 不保证原子性(可能部分成功) | 保证原子性(全部成功或失败) |
| **网络开销** | 减少网络往返次数,提升吞吐量 | 单次网络请求,开销低 |
| **错误处理** | 错误命令会被忽略 | 执行时发现错误,事务终止 |
| **适用场景** | 批量读操作(如`MGET`) | 复杂写操作(如`INCR` + `SET`) |
**示例**:
- 批量查询用户信息:使用Pipeline发送多个`GET`命令。
- 原子性扣减库存:使用事务`MULTI` + `DECR` + `SET`。
#### 9. Redis的过期策略有哪些?内存淘汰策略有哪些?
**答案**:
**过期策略**:
- **定期删除**:每秒随机检查少量Key,删除过期Key。
- **惰性删除**:访问Key时检查是否过期,过期则删除。
**内存淘汰策略**(通过`maxmemory-policy`配置):
1. **volatile-ttl**:优先淘汰剩余TTL短的Key。
2. **allkeys-lru**:淘汰最近最少使用的Key(默认策略)。
3. **allkeys-random**:随机淘汰Key。
4. **volatile-lru**:在设置了过期时间的Key中淘汰最近最少使用的。
5. **volatile-random**:在设置了过期时间的Key中随机淘汰。
6. **noeviction**:内存不足时拒绝写入,只读。
**选择建议**:
- 对时效性要求高的场景:`volatile-ttl`。
- 通用场景:`allkeys-lru`。
- 避免内存溢出:不建议使用`noeviction`。
#### 10. Redis和Memcached的区别?
**答案**:
| **特性** | **Redis** | **Memcached** |
|------------------|-------------------------------------|-----------------------------------|
| **数据结构** | 支持String、List、Hash等复杂结构 | 仅支持Key-Value |
| **持久化** | 支持RDB和AOF | 不支持 |
| **内存管理** | 自动淘汰(LRU等策略) | 自动淘汰(LRU) |
| **分布式** | 原生支持Cluster | 依赖客户端分片 |
| **事务** | 支持简单事务(`MULTI`/`EXEC`) | 不支持 |
| **适用场景** | 缓存、消息队列、分布式锁等 | 简单Key-Value缓存 |
**总结**:Redis功能更全面,适合复杂业务场景;Memcached轻量级,适合高并发简单缓存。
## 欢迎关注 ❤
我们搞了一个**免费的面试真题共享群**,互通有无,一起刷题进步。
**没准能让你能刷到自己意向公司的最新面试题呢。**
感兴趣的朋友们可以加我微信:**wangzhongyang1993**,备注:面试群。
有疑问加站长微信联系(非本文作者))
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信434 次点击 ∙ 1 赞
添加一条新回复
(您需要 后才能回复 没有账号 ?)
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传