数据库-Redis-MQ速记
来源:本人笔记「面试/面试经」「MySQL」「Redis」「1经验(慢SQL优化 / 乐观锁和悲观锁 / 高并发方案总结)」「1工作/合集(数据库日常运维 / 分布式Redis锁)」「SpringCloud学习(RabbitMQ / RabbitMQ部署指南)」。面试前一晚只看这一页。
一、数据库
1. 慢 SQL 优化(最高频,背五步)
- 检查是否走了索引,没走就改 SQL 用上索引
- 走了索引,再看是不是最优索引
- 查的字段是否都必须,有没有查出多余数据
- 数据量是否过多,是否该分库分表
- 机器性能配置是否太低,能否加资源
口诀补充:走不走索引,效率差几百倍。
2. SQL 写法优化清单
- 批量操作
- 连接的表不要过多
- 避免
select * - 小表驱动大表
- 使用
limit - 用
union all 代替 union - in 小,exists 大;in 先内后外,exists 先外后内
3. 引擎与索引(送分题)
- 引擎:InnoDB、MyISAM、MEMORY
- 索引:B+ Tree 索引、哈希索引、全文索引、普通索引
4. 锁(一句话说清)
- 乐观锁:先改,提交时再看有没有人动过,有就重来
- 悲观锁:先占住,别人等我改完才能动
5. 连接方式
| 连接 | 结果 |
|---|---|
| 内连接 inner join | 两表都存在的行拼成一行;from T1,T2 where ... 等价但效率更低,建议用 inner join |
| 左外 left join | 左表全保留,右表不匹配补 NULL |
| 右外 right join | 右表全保留,左表不匹配补 NULL |
| 全联 full join | 两边全保留,去重 |
6. 聚合函数
count / max / min / sum / avg,均以列为单位。
坑点:聚合函数默认忽略 NULL 记录;要让 NULL 参与计算需 ifnull(height,0)。
count(col) 只算非 NULL 行数,count(*) 才算总行数。
7. 高并发三条(可结合项目讲)
- 频繁使用、并发率高的表用
DB.Update 更新对应字段,用 RF.Save会存在字段被覆盖更新的情况 - 批处理与延迟提交,批量插入/更新,减少数据库往返次数
- 能用异步就用异步、消息队列处理,降低主线程阻塞
8. 运维速查 SQL(被问"你会哪些运维手段"时用)
查表已有索引(判断单列/组合)
- Oracle:
user_indexes + user_ind_columns,LISTAGG拼列名 - SQL Server:
sys.indexes + sys.index_columns + sys.columns,FOR XML PATH拼列名
抓慢 SQL(Oracle) :dba_hist_snapshot 关联 dba_hist_sqlstat,按 elapsed_time_delta 倒序取 TOP 10,再 select * from v$sql where sql_id='xxx' 看原语句。
其他常用:表空间使用情况、数据库连接数、最占空间的表、删除库锁/表锁、查看存储过程对应 SQL。
9. SQL 关键字速查
| 关键字 | 用途 |
|---|---|
GROUP BY |
分组聚合 |
HAVING |
过滤分组结果(聚合值不能用 WHERE 过滤) |
ORDER BY |
排序,默认 ASC |
UNION |
合并去重,UNION ALL 保留重复 |
INTERSECT |
交集 |
EXCEPT |
差集 |
COLLATE |
排序规则,_CI_ 不区分大小写 / _CS_ 区分大小写 |
10. 事务写法(C#,结合项目讲)
// 普通事务
using (var trans = DB.TransactionScope(CoreEntityDataProvider.ConnectionStringName))
{
trans.Complete();
}
// 自治事务,不受嵌套事务影响
using (var trans = DB.AutonomousTransactionScope(CoreEntityDataProvider.ConnectionStringName))
{
trans.Complete();
}
IN 参数上限坑(SQL Server) :IN 参数上限 1000,分批按 999 处理,多层循环分批查询后合并结果。
二、Redis
1. 缓存的意义与读写逻辑(必背)
缓存的意义在于可以保护后台的接口不被大量的请求所攻击。
- 客户端第一次请求:先从数据库查到数据,写入 Redis 缓存,后面都直接从 Redis 读
- 后台做了增、删、改:把 Redis 对应键值清除;前端再次请求,还是从数据库获取并重新写入缓存
一句话:读时回填,写时删除。
2. 常用命令
# 连接
redis-cli -h host -p port -a password
redis-cli -p 6379
# 备份与恢复
save # 在备份目录生成 dump.rdb
config get dir # 查看备份路径
# 恢复:拷贝 dump.rdb 覆盖到目标机备份目录 -> 重启 redis -> 客户端刷新
| 类型 | 增/改 | 查 | 删 |
|---|---|---|---|
| string | set key value、mset k1 v1 k2 v2 |
get key、mget k1 k2 |
del key |
| hash | hset key 键 值、hmset key 键1 值1 |
hget key 键、hgetall key |
hdel key(整个)/ hdel key 键(单列) |
| list | lpush(左增)/ rpush(右增,可多值) |
lrange key 0 -1 |
lpop / rpop |
| set | sadd key value |
smembers key |
— |
3. 分布式 Redis 锁(项目实战,重点讲)
var key = $"JrSubmit:{submitData.ShippingOrderNo}";
using (RT.Service.Resolve<RedisLockAbstractHelper>()
.LockWait(TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(2),
new List<string> { key }, $"当前自主维护单正在{isSubmitString},请稍后再试!"))
{
// 业务逻辑
}
要点:key 按业务单据号粒度加锁(如 业务名:单据号),多个 key 可批量加锁;LockWait 两个 TimeSpan 分别是等待时长和持有时间;用 using 保证释放。
三、MQ(消息队列)
1. 为什么用 MQ:同步 vs 异步
同步通讯像打电话,实时响应但不能同时跟多个人通话;异步通讯像发邮件,可群发但响应有延迟。
同步调用的问题:耦合度高、性能和吞吐能力下降、有额外的资源消耗、有级联失败问题。
异步通讯(事件驱动,中间人 Broker)的好处:
- 吞吐量提升:无需等待订阅者处理完成
- 故障隔离:服务间没有直接调用,不存在级联失败
- 调用间无阻塞,不造成无效资源占用
- 耦合度极低,服务可灵活插拔
- 流量削峰:流量波动多大都由 Broker 接收,订阅者按自己速度处理
缺点:架构变复杂、业务没有明显流程线不好管理;依赖 Broker 的可靠、安全、性能。
2. 技术选型对比
| RabbitMQ | ActiveMQ | RocketMQ | Kafka | |
|---|---|---|---|---|
| 公司/社区 | Rabbit | Apache | 阿里 | Apache |
| 开发语言 | Erlang | Java | Java | Scala&Java |
| 协议支持 | AMQP、XMPP、SMTP、STOMP | OpenWire、STOMP、REST、XMPP、AMQP | 自定义协议 | 自定义协议 |
| 可用性 | 高 | 一般 | 高 | 高 |
| 单机吞吐量 | 一般 | 差 | 高 | 非常高 |
| 消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 |
| 消息可靠性 | 高 | 一般 | 高 | 一般 |
选型口诀:追求可用性选 Kafka / RocketMQ / RabbitMQ;追求可靠性选 RabbitMQ / RocketMQ;追求吞吐选 RocketMQ / Kafka;追求低延迟选 RabbitMQ / Kafka。
3. RabbitMQ 角色与消息模型
角色:publisher(生产者)、consumer(消费者)、exchange(交换机,负责路由)、queue(队列,存储消息)、virtualHost(虚拟主机,隔离不同租户)。
五个消息模型:
- 简单队列:生产者 → 队列 → 消费者
- WorkQueue:多个消费者绑定一个队列共同消费,一条消息只被一个消费者处理;默认平均分配,用
prefetch: 1 实现能者多劳 - Fanout:广播,消息交给所有绑定到交换机的队列
- Direct:定向,按
RoutingKey精确匹配队列 - Topic:通配符,
# 匹配一个或多个词,* 匹配恰好一个词(如 item.# 匹配 item.spu.insert;item.* 只匹配 item.spu)
关键结论:Exchange 只负责转发消息,不具备存储消息的能力;没有队列绑定或没有符合路由规则的队列,消息会丢失。
4. SpringAMQP 要点
三个功能:自动声明队列/交换机/绑定关系;基于注解的监听器模式异步接收;封装 RabbitTemplate 发送消息。
- 发送:
rabbitTemplate.convertAndSend(exchangeName, routingKey, message) - 接收:
@RabbitListener(queues = "simple.queue"),或 @RabbitListener(bindings = @QueueBinding(...))注解声明 - 声明 Bean:
Queue、FanoutExchange、Binding - 消息转换器:默认 JDK 序列化(体积大、有安全漏洞、可读性差),改为
Jackson2JsonMessageConverter
5. 部署与集群
# 单机(Docker)
docker run -e RABBITMQ_DEFAULT_USER=itcast -e RABBITMQ_DEFAULT_PASS=123321 \
--name mq --hostname mq1 -p 15672:15672 -p 5672:5672 -d rabbitmq:3-management
端口:5672 通信,15672 管理界面(初始 guest/guest)。
集群两种模式:
- 普通模式:不做数据同步,每个 MQ 有自己的队列和数据(交换机等元数据会同步)。消息在 mq1 但连到 mq2,mq2 会去 mq1 拉取;mq1 宕机消息就丢失
- 镜像模式:队列在各镜像节点之间同步,连任意节点都能取到消息,一个节点宕机不丢数据,代价是同步带宽消耗
集群前置:三台机器 /etc/hosts 互相写 mq1/mq2/mq3 并 ping 通。
四、临场串讲模板(1 分钟自我介绍里的技术线)
我做过 MES 制造系统开发,日常接触 Oracle / SQL Server / MySQL 三种库:慢 SQL 排查上我习惯先看执行计划是否走索引,再看索引是否最优、字段是否冗余,最后才考虑分库分表;数据库侧会写抓慢 SQL、查索引、查表空间和连接数的运维脚本。缓存用 Redis,读过回填、写时删除,分布式锁按单据号粒度加锁,防止重复提交和并发覆盖。服务间通信上,能异步的走 MQ,用 RabbitMQ 的 Topic 交换机做业务解耦和流量削峰,消费者侧用 prefetch 做能者多劳。
