一、数据库中的数据加密
数据库加密并不是一个开关,而是多层防护。不同方式解决的是不同类型的风险。
1. 连接加密:TLS/SSL
连接加密主要用于防止数据在网络传输过程中被抓包或窃听。
客户端和数据库之间开启 TLS/SSL 后,网络上传输的是密文。即使有人抓包,看到的也只是无法直接理解的数据。
它主要防的是:
- 网络抓包
- 链路窃听
- 中间人攻击
但前提是证书校验正确。
2. 表空间加密
表空间加密指的是数据库文件落盘时进行加密。
例如 MySQL 的 .ibd 文件中的数据页可以被加密。即使攻击者把整个数据库文件复制走,也不能直接读取其中的数据。
它的优点是应用代码基本不需要修改。
但它也有局限:
如果有人拥有数据库账号,并且有 SELECT 权限,那么他仍然可以通过 SQL 查询到明文数据,也可能通过逻辑导出拿走数据。
所以,表空间加密主要解决的是"数据库物理文件泄露",而不是"高权限账号滥用"。
3. 字段加密
对于手机号、身份证号、银行卡号等敏感数据,可以单独进行字段加密。
MySQL 可以使用 AES_ENCRYPT() 和 AES_DECRYPT()。
示例:
SET block_encryption_mode = 'aes-256-cbc';
SET @key_str = SHA2('my_secret_key', 512);
SET @iv = RANDOM_BYTES(16);
INSERT INTO user_secure(username, phone_cipher, phone_iv)
VALUES (
'zhangsan',
AES_ENCRYPT('13812345678', @key_str, @iv),
@iv
);
SELECT username,
CAST(AES_DECRYPT(phone_cipher, @key_str, phone_iv) AS CHAR) AS phone
FROM user_secure;
实际生产环境中,更推荐由应用层或专业密钥管理服务管理密钥,而不是把密钥和密文都放在同一个数据库中。
另外,同一个明文如果每次使用随机 IV,加密结果不同是正常的,而且更安全。
4. 密文加哈希检索字段
字段加密后,通常无法直接进行等值查询或模糊搜索。
例如手机号加密后,如果还要根据手机号查用户,可以额外保存一个哈希检索字段。
常见做法是:
- 保存加密后的手机号,用于展示或解密;
- 保存手机号的 HMAC 哈希,用于等值查询;
- 查询时,对输入手机号做同样的 HMAC 计算,再查询哈希字段。
不要直接使用普通 SHA-256 保存手机号,因为手机号取值空间较小,容易被枚举反查。
5. 密码不能加密,只能哈希
密码不应该使用 AES 等可逆加密算法保存。
因为一旦密钥泄露,所有密码都可能被解密。
密码校验只需要判断"输入是否正确",不需要恢复原始密码。因此密码应该使用专门的慢速、带盐哈希算法,例如:
- Argon2id
- bcrypt
- scrypt
总结一句话:
传输加密防抓包,表空间加密防文件泄露,字段加密防明文存储,密码必须使用专用哈希。
二、什么是心跳机制?
心跳机制就是客户端和服务器之间定期发送一个很小的数据包,用来证明"我还活着,连接还正常"。
可以理解为:
客户端每隔几秒告诉服务器一次:"我还在线。"
服务器收到后,就更新这个连接的最后活跃时间。如果服务器在一段时间内没有收到任何消息,就认为客户端可能掉线了。
心跳机制的主要作用:
- 检测连接是否仍然可用;
- 防止 NAT、代理、网关等中间设备回收空闲连接;
- 及时发现掉线用户,更新在线状态;
- 清理无效连接,释放服务端资源。
WebSocket 协议本身定义了 Ping/Pong 控制帧,优先使用框架提供的 Ping/Pong 能力。
浏览器原生 WebSocket API 不能直接发送协议级 Ping 时,也可以定义应用层的 heartbeat 消息。
一个常见策略:
- 客户端每 20~30 秒发送一次心跳;
- 服务端记录
lastActiveAt; - 如果连续 2~3 个周期没有收到消息,则关闭连接;
- 超时时间要结合网关、Nginx、负载均衡的空闲超时一起配置。
三、心跳机制问题排查
排查心跳问题时,先分成两类:
- 心跳不正常:心跳包没有正常收发;
- 心跳正常:连接还活着,但业务消息仍然有问题。
心跳不正常时怎么排查?
1. 客户端有没有发送?
检查浏览器网络面板、客户端日志和定时器。
注意:浏览器进入后台、手机省电模式、系统休眠时,定时器可能会被暂停或降频。
2. 服务端有没有收到?
服务端日志中应该记录:
- 用户 ID
- 连接 ID
- 服务节点 ID
- 心跳接收时间
- 消息类型
不要只打印"收到心跳"这种无法关联用户的信息。
3. 服务端有没有正确处理或返回?
检查:
- 消息类型是否一致;
- JSON 序列化和反序列化是否正确;
- 心跳处理是否抛异常;
- Ping/Pong 或 ACK 的返回逻辑是否正常。
4. 超时时间是否合理?
客户端的发送周期、服务端的超时时间、网关的超时时间必须匹配。
例如客户端每 30 秒发送一次心跳,而服务端 20 秒就判定超时,那么一定会频繁误判掉线。
5. Nginx、网关、负载均衡是否提前断开?
很多线上问题并不是代码问题,而是代理层超时配置问题。
重点检查:
- Nginx 的
proxy_read_timeout - 网关空闲超时
- 负载均衡连接超时
- CDN 是否支持 WebSocket
- 防火墙或 NAT 是否回收空闲连接
6. 服务端扫描逻辑有没有 Bug?
重点检查:
- 时间单位是秒还是毫秒;
- 超时判断是否写反;
- 遍历连接时是否有并发问题;
- 用户重连后,旧连接关闭时是否误删了新连接;
- 关闭连接后,在线状态是否正确更新。
心跳正常,但业务仍然有问题怎么办?
如果心跳一直正常,说明连接大概率还活着,但不代表业务消息一定发送成功或展示正确。
此时应该继续排查:
- 房间状态是否正确;
- 用户是否还在正确的房间或订阅中;
- Redis 中的在线状态是否被旧连接覆盖;
- 消息队列是否积压、消费失败或重复消费;
- 多个 WebSocket 节点之间是否完成消息广播;
- 客户端是否收到了消息,但状态管理或渲染逻辑没有更新。
线上排查最好让每条消息带上可关联字段:
requestId
messageId
userId
connectionId
nodeId
timestamp
这样才能把一条消息从"客户端发送"到"网关转发",再到"服务端接收、消息广播、客户端展示"的完整链路串起来。
十、总结
心跳机制解决的是连接是否存活,但不能保证业务消息一定正确送达。
数据库加密也不是一个单点能力,而是传输加密、存储加密、字段加密、权限控制和密钥管理共同组成的安全体系。
面试时不要只回答"是什么",还要能说明:
- 它解决什么问题;
- 它的优点和代价是什么;
- 它的适用边界在哪里;
- 出问题时如何排查。
真正的工程能力,往往就体现在这些追问里。