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