【趣学SQL】第七章:SQL 实战案例 7.2 社交媒体应用中的 SQL 优化——从“卡成PPT”到“纵享丝滑”的逆袭之路

在这里插入图片描述

第七章:SQL 实战案例

7.2 社交媒体应用中的 SQL 优化——从“卡成PPT”到“纵享丝滑”的逆袭之路

欢迎来到「社交媒体数据库急诊室」!今天我们将化身“SQL外科医生”,用一款日活千万的虚拟社交App“喵友圈”的崩溃案例,教你如何让“卡顿到用户卸载”的数据库重获新生。📱💥


7.2.1 社交媒体的数据模型——用户、帖子、点赞的“爱恨纠葛”

核心表设计

-- 用户表(每天被舔狗疯狂刷新)  
CREATE TABLE users (  
  user_id BIGINT AUTO_INCREMENT PRIMARY KEY,  
  username VARCHAR(50) UNIQUE,        -- 唯一索引  
  password_hash CHAR(60),             -- bcrypt加密  
  follower_count INT DEFAULT 0,       -- 实时更新重灾区  
  INDEX idx_username (username)  
) ENGINE=InnoDB;  

-- 帖子表(包含海量emoji和九宫格图)  
CREATE TABLE posts (  
  post_id BIGINT AUTO_INCREMENT PRIMARY KEY,  
  user_id BIGINT,  
  content TEXT,                       -- 全文搜索字段  
  created_at DATETIME DEFAULT NOW(),  
  like_count INT DEFAULT 0,           -- 高频更新字段  
  INDEX idx_user_post (user_id, created_at)  
);  

-- 点赞表(每秒写入峰值破万)  
CREATE TABLE likes (  
  like_id BIGINT AUTO_INCREMENT PRIMARY KEY,  
  user_id BIGINT,  
  post_id BIGINT,  
  created_at DATETIME DEFAULT NOW(),  
  UNIQUE KEY uk_user_post (user_id, post_id) -- 防止重复点赞  
);  

典型问题

  • 用户主页查询LIMIT 100000,10导致深度分页卡顿
  • SELECT COUNT(*) FROM likes WHERE post_id=666 耗时3秒
  • 热点帖子点赞引发死锁(并发事务打架)

7.2.2 常见的查询瓶颈——社交媒体的“十大酷刑”

酷刑1:舔狗刷女神主页卡顿
-- 查询用户最新帖子(分页噩梦)  
SELECT * FROM posts  
WHERE user_id = 1314  
ORDER BY created_at DESC  
LIMIT 100000, 10;  -- 用户翻到第10000页时数据库哭了  
酷刑2:热搜榜生成慢如蜗牛
-- 统计过去1小时点赞Top100帖子  
SELECT post_id, COUNT(*) AS like_num  
FROM likes  
WHERE created_at > NOW() - INTERVAL 1 HOUR  
GROUP BY post_id  
ORDER BY like_num DESC  
LIMIT 100;  -- 全表扫描+临时文件排序  
酷刑3:@消息推送延迟
-- 查找所有@用户的消息  
SELECT * FROM posts  
WHERE content LIKE '%@喵小美%';  -- 左模糊导致索引失效  

7.2.3 索引优化案例——给数据库装上“涡轮增压”

案例1:拯救舔狗的分页查询

优化前执行计划

+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+  
| id | select_type | table | type | possible_keys | key  | rows    | Extra|  
+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+  
| 1  | SIMPLE      | posts | ALL  | idx_user_post | NULL | 1589200 | Using where; Using filesort |  
+----+-------------+-------+------+---------------+------+---------+------+--------+----------------+  

手术方案

-- 添加游标分页专用索引  
ALTER TABLE posts  
ADD INDEX idx_user_time_desc (user_id, created_at DESC);  

-- 改写分页查询(记住上次的位置)  
SELECT * FROM posts  
WHERE user_id = 1314  
  AND created_at < '2024-03-01 12:00:00'  -- 上一页最后一条的时间  
ORDER BY created_at DESC  
LIMIT 10;  

术后效果

  • 查询时间从 4.2秒 → 0.03秒
  • 索引大小仅增加 5%(B+树高度从5降为3)

7.2.4 查询优化案例——让SQL学会“凌波微步”

案例2:热搜榜生成提速100倍

优化前(全表扫描+文件排序)

EXPLAIN  
SELECT post_id, COUNT(*) FROM likes  
WHERE created_at > NOW() - INTERVAL 1 HOUR  
GROUP BY post_id  
ORDER BY COUNT(*) DESC  
LIMIT 100;  
-- 扫描行数:1,200,000行  
-- 临时文件大小:800MB  

手术方案(空间换时间)

-- 创建统计专用汇总表  
CREATE TABLE post_hourly_stats (  
  post_id BIGINT PRIMARY KEY,  
  like_count INT DEFAULT 0,  
  stat_hour DATETIME  
);  

-- 每隔1小时定时更新(替代实时统计)  
REPLACE INTO post_hourly_stats  
SELECT post_id, COUNT(*), DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')  
FROM likes  
WHERE created_at >= NOW() - INTERVAL 1 HOUR  
GROUP BY post_id;  

-- 查询优化后  
SELECT post_id, like_count  
FROM post_hourly_stats  
WHERE stat_hour = '2024-03-01 15:00:00'  
ORDER BY like_count DESC  
LIMIT 100;  

术后效果

  • 查询时间 3.1秒 → 0.05秒
  • 临时表空间占用 800MB → 2MB

7.2.5 系统调优案例——给服务器注入“肾上腺素”

案例3:点赞死锁急救

症状

-- 并发点赞时的死锁日志  
LATEST DETECTED DEADLOCK  
...  
*** WAITING FOR THIS LOCK TO BE GRANTED:  
RECORD LOCKS space id 666 page no 7 n bits 72 index uk_user_post of table `social`.`likes`  

手术方案(并发控制)

-- 使用INSERT IGNORE避免锁竞争  
INSERT IGNORE INTO likes (user_id, post_id)  
VALUES (123, 456);  

-- 配合Redis缓存计数器(最终一致性)  
INCRBY post:456:likes 1  -- Redis操作  

参数调优

# my.cnf  
innodb_buffer_pool_size = 32G          # 内存的70%  
innodb_deadlock_detect = OFF           # 高并发时关闭死锁检测(慎用!)  
innodb_flush_log_at_trx_commit = 2     # 适当降低持久性换性能  

术后效果

  • 点赞TPS从 1200 → 9500
  • 死锁发生率 99% → 0.1%

7.2.6 安全管理案例——防住“黑粉”的暴击

案例4:防止SQL注入

漏洞代码(前端拼接SQL)

# 危险!用户输入直接拼接  
query = f"SELECT * FROM posts WHERE content LIKE '%{user_input}%'"  

防御方案(参数化查询)

# 使用预编译语句  
cursor.execute("""  
  SELECT * FROM posts  
  WHERE content LIKE %s  
""", (f"%{user_input}%",))  
案例5:敏感数据加密
-- 存储加密的手机号  
ALTER TABLE users  
ADD COLUMN phone_cipher VARBINARY(256);  

-- AES加密写入  
UPDATE users  
SET phone_cipher = AES_ENCRYPT('13812345678', 'sup3r_secret_k3y');  

-- 解密查询  
SELECT AES_DECRYPT(phone_cipher, 'sup3r_secret_k3y')  
FROM users WHERE user_id = 123;  

术后护理指南

监控工具套餐

# 慢查询分析神器  
pt-query-digest /var/log/mysql/slow.log  

# 死锁实时监控  
show engine innodb status\G  

# 压力测试工具  
sysbench oltp_read_write --threads=100 run  

备份方案

# Percona XtraBackup全量备份  
xtrabackup --backup --target-dir=/backups/full  

# Binlog增量备份  
mysqlbinlog --start-datetime="2024-03-01 00:00:00" /var/log/mysql/binlog.0* > inc.sql  

本章冷知识

  • 微博曾因VARCHAR(255)存储IP地址导致索引失效(改用INT UNSIGNED存储IPv4)
  • Instagram的分布式ID生成算法(Snowflake变种)避免主键冲突
  • 某社交App因忘记给created_at字段加索引,导致情人节宕机8小时

现在你已经成为“社交媒体数据库神医”!下一章我们将进入《编写可维护的 SQL 代码——从"代码屎山"到"代码花园"的改造指南》的玄学领域,记得给你的服务器准备急救包——互联网的疯狂永不停歇! 🚀🔥

posted @   爱上编程技术  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示