一、为什么需要高并发处理?
想象一下,当你在双十一抢购手机时,同时有数百万人点击按钮,这个瞬间的请求量有多大?MySQL如果不做特殊处理,就像一条狭窄的单行道被成千上万辆汽车同时涌上来,很快就会堵塞。我经历过一个电商系统,刚开始设计时只考虑了1000用户同时在线的场景,结果在第一次大促时数据库CPU使用率直接飙到99%,整个系统几乎瘫痪。这就是为什么我们需要提前规划高并发处理策略。
二、常见的瓶颈在哪里?
首先得明白,高并发下MySQL最容易遇到什么问题:
- 连接数过多:默认情况下MySQL最大连接数是100,但在高并发下远远不够
- 锁竞争严重:InnoDB的行锁、表锁都可能成为瓶颈
- 查询性能下降:复杂查询占用过多资源
- 内存不足:缓冲区不够用
我记得有一次排查问题,发现某个页面在高峰期出现大量请求响应超时,根本原因是MySQL连接池满了,新进来的请求只能排队等待。解决方法就是优化连接配置。
三、分库分表策略
3.1 水平拆分
当数据量超过千万级时,可以考虑将一个大表拆分成多个小表。比如用户表可以根据user_id取模拆分:
-- 示例:将user_0, user_1, user_2...
CREATE TABLE user_0 (id INT, name VARCHAR(50), email VARCHAR(100)) ENGINE=InnoDB;
CREATE TABLE user_1 (id INT, name VARCHAR(50), email VARCHAR(100)) ENGINE=InnoDB;
-- 这样每个表的数据量就减小了,查询效率自然提高
3.2 垂直拆分
对于大字段如text类型的描述信息,可以单独拆分出来:
-- 主表只存常用信息
CREATE TABLE user_main (
id INT PRIMARY KEY,
username VARCHAR(50),
email VARCHAR(100)
) ENGINE=InnoDB;
-- 详情表存大数据
CREATE TABLE user_detail (
user_id INT PRIMARY KEY,
description TEXT,
avatar_url VARCHAR(255)
) ENGINE=InnoDB;
这种做法让主表更轻快,访问热点数据时不需要加载大字段,大大提升了速度。
四、缓存层的重要性
Redis作为缓存神器,在高并发场景中是必选项。我的经验是先查缓存,再查数据库,并且注意防雪崩:
def get_user_info(user_id):
# 先尝试从Redis获取
cache_key = f"user:{user_id}"
cached_data = redis.get(cache_key)
if cached_data:
return cached_data
# 缓存不存在则查询数据库
result = db.execute("SELECT * FROM user WHERE id = %s", (user_id,))
# 设置缓存,避免重复查询
redis.setex(cache_key, 3600, result)
return result
还要注意设置合适的过期时间,既保证数据新鲜度又减少数据库压力。
五、读写分离与复制架构
单台数据库扛不住太多读操作怎么办?可以采用主从读写分离架构:
# 应用配置示例
database:
master: # 负责写入
host: 192.168.1.10
port: 3306
slaves: # 负责读取
- host: 192.168.1.11
port: 3306
- host: 192.168.1.12
port: 3306
写入操作走主库,读操作从从库读取,这样负载均衡效果明显。但要注意主从延迟问题,有些业务可能需要强一致性,这时就要谨慎选择。
六、索引优化是个技术活
不要盲目加索引!正确做法是先分析慢查询日志,找出真正需要优化的SQL。比如这个查询:
-- 低效写法
SELECT * FROM orders WHERE YEAR(order_date) = 2023 AND MONTH(order_date) = 10;
-- 改进写法,允许索引生效
SELECT * FROM orders WHERE order_date >= '2023-10-01' AND order_date < '2023-11-01';
还有联合索引的最左前缀原则,这点经常被忽视。比如idx_name_age_gender这样的索引,只能用于查询name或name+age,不能直接用age或gender查。
七、连接池管理很关键
HikariCP是我推荐的首选连接池配置:
# HikariCP配置示例
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
合理设置这些参数可以显著减少创建连接的开销,避免频繁新建连接导致CPU消耗过高。
八、限流与降级保护
面对突发流量,要学会自我保护。可以实施以下策略:
// 伪代码示例实现限流
public Response handleRequest() {
if (!rateLimiter.tryAcquire()) {
return responseFromCacheOrFallback(); // 返回缓存或降级数据
}
return callDatabaseAndProcess();
}
比如某个非核心功能在高峰期暂时不可用,优先保证支付、下单等核心功能正常运行,这是业务层面的保护。
九、监控报警不可或缺
没有监控就像开车不看仪表盘。我建议重点关注以下指标:
- QPS(每秒查询数)
- CPU使用率
- 连接数占用比例
- 慢查询数量
- 事务冲突次数
利用Prometheus+Grafana这套组合拳可以轻松搭建可视化监控平台。记得设定合理的告警阈值,避免误报。
十、实际案例分享
记得有一个订单系统,高峰期日均订单量达百万级别。我们通过以下步骤实现了稳定运行:
- 使用ShardingSphere做分库分表,按user_id hash取模分16个库
- Redis缓存热门商品信息,TTL设置为5分钟
- RabbitMQ削峰填谷,异步处理订单库存扣减
- 读写分离后读吞吐量提升3倍
- 添加熔断机制防止故障扩散
最终支撑住了每秒2000+的并发压力,故障率降低到了万分之一以下。
结语
高并发处理没有银弹,需要根据具体场景灵活组合各种策略。我的建议是从简单开始,逐步优化,永远不要一次性把所有高级特性都用上。记住一点:最好的架构是能满足当前需求,并留有扩展余地的架构。
