关于高性能的那点事

📅 2026/8/4 15:06:55
关于高性能的那点事
关于高性能的那点事在高并发、大数据时代“高性能”早已不是可选项而是生存底线。很多团队在业务初期只关注功能实现等用户量上来后却发现系统像老牛拉破车一样寸步难行。这里不聊玄学只讲实战。我会用具体代码和场景拆解高性能的三个核心层面算法与数据结构、并发与异步、缓存与存储。每一层都有大量可复用的工程技巧。—### 一、算法与数据结构性能的“地基”如果业务逻辑里用错了数据结构再牛的服务器也扛不住。比如一个只需要尾部插入和尾部读取的队列如果你用了数组头部插入unshift时间复杂度就是 O(n)而用链表尾部操作是 O(1)。这差距在百万级数据下就是秒级与毫秒级的鸿沟。实战案例假设我们要实现一个“最近 1 小时内用户的访问记录”要求快速插入和按时间倒序查询。如果用 Python 列表头部插入数据量大了就会卡死。python# 错误的做法用列表头部插入O(n)import timefrom collections import dequedef bad_insert(records, item): # records.insert(0, item) # 这行代码是 O(n) 的千万别用 passdef good_insert(records, item): # 使用 deque 的 appendleft 是 O(1) records.appendleft(item)# 测试插入10万条数据bad_list []good_deque deque()start time.time()for i in range(100000): bad_list.insert(0, i) # 这行代码是 O(n) 的千万别用print(f列表头部插入耗时: {time.time() - start:.2f}秒)start time.time()for i in range(100000): good_deque.appendleft(i) # deque 是 O(1)print(fdeque头部插入耗时: {time.time() - start:.2f}秒)运行结果典型值- 列表头部插入约 12.3 秒- deque 头部插入约 0.02 秒相差 600 倍。这就是算法选择的力量。永远不要忽视数据结构的时间复杂度。在写代码前先画一个操作频率表哪个操作最频繁是读还是写然后选择对应的数据结构哈希表、跳表、B树、堆等。—### 二、并发与异步榨干 CPU 的每一滴性能单线程处理 I/O 是最大的性能杀手。比如网络请求、数据库查询、文件读写都会让 CPU 空转等待。高性能系统必须使用异步非阻塞模型或者多线程/多进程配合。实战案例我们用 Python 的asyncio模拟一个高并发下载任务。对比同步和异步的耗时差异。pythonimport asyncioimport time# 模拟一个 I/O 操作比如网络请求async def fetch_url(url): await asyncio.sleep(0.1) # 模拟网络延迟 100ms return f数据来自 {url}# 同步版本低性能def sync_fetch_all(urls): results [] for url in urls: results.append(f数据来自 {url}) # 假设每个耗时0.1秒这里用sleep模拟 time.sleep(0.1) # 同步阻塞 return results# 异步版本高性能async def async_fetch_all(urls): tasks [fetch_url(url) for url in urls] return await asyncio.gather(*tasks)# 测试 10 个 URLurls [fhttps://api.example.com/{i} for i in range(10)]# 同步耗时start time.time()sync_fetch_all(urls)print(f同步耗时: {time.time() - start:.2f}秒)# 异步耗时start time.time()asyncio.run(async_fetch_all(urls))print(f异步耗时: {time.time() - start:.2f}秒)结果- 同步约 1.0 秒每个 0.1 秒串行- 异步约 0.1 秒全部并发异步让 10 个请求同时等待总耗时只等于一个请求的耗时。这就是高性能的“并发红利”。在实际业务中用asyncio、gevent、线程池、协程等机制可以轻松将吞吐量提升数倍甚至数十倍。—### 三、缓存与存储把热点数据“焊死”在内存里数据库是磁盘操作内存是纳秒级速度磁盘是毫秒级速度差 10^6 倍。所以对于读多写少的场景必须引入缓存层如 Redis。但缓存不是简单get/set还要考虑缓存穿透、缓存击穿、缓存雪崩。实战案例用 Python Redis 实现一个带防穿透的缓存逻辑。我们模拟一个获取用户信息的接口。pythonimport redisimport json# 连接 Redisr redis.Redis(hostlocalhost, port6379, db0)def get_user_info(user_id): # 1. 先从缓存查 cache_key fuser:{user_id} cached r.get(cache_key) if cached: return json.loads(cached) # 命中缓存 # 2. 缓存未命中查数据库这里用模拟数据 # 注意为了防止穿透如果数据库也没有也要缓存一个空值 if user_id 0: # 模拟数据库没有这个用户 r.set(cache_key, json.dumps(None), ex60) # 缓存空值60秒 return None user_data {id: user_id, name: f用户{user_id}, level: 5} # 3. 写入缓存设置过期时间防止雪崩加随机过期时间 import random expire_time 300 random.randint(0, 60) # 300~360秒 r.set(cache_key, json.dumps(user_data), exexpire_time) return user_data# 测试print(get_user_info(1001)) # 第一次查库print(get_user_info(1001)) # 第二次直接命中缓存print(get_user_info(0)) # 穿透情况缓存了空值这段代码解决了三个问题-穿透即使数据库没有也缓存空值。-雪崩过期时间加随机抖动避免同时失效。-击穿可以用互斥锁代码略解决热点 key 重建问题。缓存是高性能的“放大器”。但要注意缓存一致性是另一门艺术比如用 Canal 订阅 binlog 更新缓存或者用双删策略。—### 四、数据库优化索引与 SQL 的“镀金”最后要说的是数据库。很多性能问题最后都卡在慢 SQL 上。除了加索引更要学会覆盖索引、索引下推、避免回表。实战案例假设有一张订单表我们经常按用户 ID 和状态查询。sql-- 错误示范select * 且无复合索引SELECT * FROM orders WHERE user_id 123 AND status PAID;-- 正确做法创建复合索引并只查需要的列ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);SELECT order_id, amount, create_time FROM orders WHERE user_id 123 AND status PAID;如果表有 1000 万条数据没有索引是全表扫描秒级有索引是索引查找毫秒级。索引不是越多越好每个索引都会拖慢写入。所以要分析慢查询日志针对高频查询建联合索引。—### 总结高性能不是靠单点技术而是一个系统工程-算法与数据结构是地基选错就是灾难。-并发与异步是杠杆用好了可以十倍提升吞吐。-缓存与存储是加速器但要注意穿透、击穿、雪崩。-数据库优化是兜底一个慢 SQL 足以拖垮整个服务。最后的心法性能优化永远先测量再优化。用profiling工具如cProfile、JProfiler、arthas找到瓶颈而不是靠猜。记住过早优化是万恶之源但毫无优化是万劫不复。希望这篇文章能给你一些实战启发在性能优化这条路上少走弯路。