当客户说"3秒处理1亿测点,还不能加机器"

40 阅读8分钟

大家好,我是老丁,一个写了十年代码拥有“丁点技术“的“老”程序员。

上周六晚上9点,我正躺在沙发上刷手机,微信突然弹出来一条消息。

是我一个技术群内做物联网架构师的朋友。

老张:"老丁,在吗?有个技术问题想请教你。"

我心想,周末这么晚还聊技术,肯定是遇到麻烦了。

我:"说吧,什么问题?"

老张:"我们有个项目,1亿个设备测点,每15秒上报一次数据,客户要求3秒内完成告警判断。"

我坐了起来,放下手机,又拿起来看了一遍。

我:"分布式集群?多少台服务器?"

老张:"这就是问题所在……客户说预算有限,不同意加机器,就用现在的单台服务器。"

我盯着屏幕看了半分钟,打出两个字:

我:"??"

那个让人窒息的计算

我起身走到书房,打开电脑,给老张发了个Excel表格。

【性能需求分析】
数据量:100,000,000 测点
时间限制:3 秒
所需吞吐:33,333,333 次判断/秒

换算:
• 每毫秒:33,333 次判断
• 每微秒:33 次判断
• 单核 CPU @ 3GHz:每次判断只有 90 个时钟周期

老张:"我也算过,但客户说别的厂商承诺能做到……"

看到这句话,我苦笑了。这个行业待久了,这种剧情见得太多了。

我:"老张,咱俩认识多少年了?"

老张:"5年了,怎么了?"

我:"那我就直说了。这事儿,基本不可能。"

凌晨1点的深夜长谈

老张没有马上回复。过了五分钟,他的电话打过来了。

"老丁,我知道很难,但项目已经签了,客户那边态度很强硬……"电话里他的声音有些疲惫,"你能帮我分析分析,到底有没有一丝可能?"

我点了根烟,打开 IDE,一边写 demo 一边和他聊。

我先给他看了个最简单的测试:

import numpy as np
import time

# 1亿个测点,最简单的阈值判断
values = np.random.rand(100_000_000)
thresholds = np.random.rand(100_000_000)

start = time.time()
result = values > thresholds  # 就一个比较操作
elapsed = time.time() - start

print(f"耗时: {elapsed:.2f}秒")

"你猜猜这个要多久?"我问他。

"emmm……1秒?"

"我刚在我的工作站上跑了,顺序访问的话,0.8秒。"

老张明显松了口气:"那不是可以吗?"

"等等,我话还没说完。" 我吸了口烟,"这是理想情况:没有任何业务逻辑,不查数据库,不调接口,甚至连判断规则都没有,就纯粹比大小。"

我继续说:"而且这是顺序访问内存,如果是随机访问呢?6.2秒。"

电话那头沉默了。

那些藏在需求里的魔鬼

凌晨12点半,我给老张又发了一段代码。

"这是你们真实的告警判断逻辑吧?"

def real_alarm_check(point):
    # 1. 查询测点配置
    config = db.query(
        f"SELECT * FROM point_config WHERE id={point.id}"
    )
    
    # 2. 获取1小时历史数据算平均值
    history = db.query(f"""
        SELECT AVG(value) FROM history 
        WHERE point_id={point.id} 
        AND timestamp > NOW() - INTERVAL 1 HOUR
    """)
    
    # 3. 查关联测点
    related = db.query(f"""
        SELECT value FROM points 
        WHERE device_id={point.device_id}
    """)
    
    # 4. 复杂规则判断
    if point.value > config.threshold:
        if point.value > history.avg * 1.2:
            if check_related_points(related):
                return create_alarm()
    
    return None

老张:"对……就是这样的,怎么了?"

我:"你算算,一次数据库查询多久?"

老张:"局域网的话,5-10毫秒吧。"

我:"好,按5毫秒算。三次查询就是15毫秒。1亿个测点 × 15毫秒 = ?"

老张那边传来键盘敲击声,然后是一声长叹。

"1,500,000 秒……17天……"

"对,17天。" 我把烟按在烟灰缸里,"而且这还没算查询数据库的锁竞争、连接池耗尽这些问题。"

凌晨2点,我们开始务实地聊

电话里安静了一会儿,老张的声音传来:"老丁,那……真的一点办法都没有吗?"

我听出了他的无奈。项目已经签了,客户的钱已经收了,总不能退款说做不了。

"办法不是没有,但得看客户能接受什么程度的妥协。" 我重新倒了杯茶,"我给你梳理几个方案。"

方案一:增量计算

我:"先问你一个问题,这1亿个测点,每次真的都会变化吗?"

老张:"这个……应该不会吧,大部分都是温度、湿度这类缓慢变化的。"

我:"那就对了。工业监控场景,通常只有2%-5%的测点会在一个周期内变化。"

我快速写了个方案发给他:

class IncrementalChecker:
    def __init__(self):
        self.last_values = {}  # 上次的值
        
    def check(self, updates):
        # updates 只包含变化的测点
        # 假设只有5% = 500万个
        alarms = []
        
        for point_id, new_value in updates.items():
            old_value = self.last_values.get(point_id)
            
            # 只判断变化的
            if self._rule_triggered(old_value, new_value):
                alarms.append(point_id)
            
            self.last_values[point_id] = new_value
        
        return alarms

我:"5%变化率的话,500万次判断,按你们现在的性能,应该能在2-3秒内完成。"

老张:"但如果客户说必须全量判断呢?"

我:"那就和他讲道理。阿里云、AWS这些大厂,处理亿级测点都是增量计算 + 边缘预处理,没人傻到真的全量判断。"

方案二:规则简化 + 预计算

我继续说:"第二个问题,你们的规则必须这么复杂吗?"

老张:"产品经理说要和历史数据对比,还要关联其他测点……"

"那就是性能杀手。" 我打断他,"能不能改成预计算?"

我又发了段代码:

# 原来:实时查询历史平均值(慢)
history_avg = db.query("SELECT AVG...")

# 改成:每小时预计算一次阈值(快)
class PrecomputedChecker:
    def __init__(self):
        # 定时任务每小时更新一次
        self.dynamic_thresholds = load_precomputed_thresholds()
    
    def check(self, point_id, value):
        # 直接查表,不查数据库
        threshold = self.dynamic_thresholds[point_id]
        return value > threshold

老张:"这个可以,但客户会说实时性不够……"

我:"那就问他:是要100%实时但做不到,还是要99%实时但能做到?"

凌晨3点,真相时刻

聊到这里,我觉得是时候说点实话了。

我:"老张,我再问你一个问题。"

老张:"你说。"

我:"那个所谓'别的厂商承诺能做到',你信吗?"

电话那头沉默了十几秒。

老张:"说实话……我也觉得悬。但客户就是拿这个来压我们。"

我深吸了口气: "那我告诉你这个行业的真相。"

"那些厂商,要么是根本不懂技术的销售在吹牛,到时候做不出来就拖;要么是偷换概念,说的'1亿测点'其实是分布在100台服务器上的。"

"你觉得,一个真正懂技术的人,会承诺单机3秒处理1亿次数据库查询吗?"

老张:"不会……"

我:"那就对了。所以你现在的问题,不是技术问题,是商务问题。"

结局

凌晨3点半,我给老张发了最后一段话:

第一步:需求澄清(必须做)

约个会,把下面这些问题问清楚:

  • 1亿测点是否每次都要判断?→ 争取增量计算
  • 3秒是指收到数据后3秒,还是15秒周期内留3秒判断?→ 时间可能没那么紧
  • 告警能否分级?紧急告警1秒,普通告警5秒?→ 降低部分需求
  • 规则能否简化?→ 去掉实时查询

第二步:POC验证(保护自己)

别直接承诺能做到,先做POC:

  • 用100万真实数据测试
  • 给出实测性能报告
  • 明确说明扩展到1亿的风险

第三步:方案对比(让客户选)

方案性能成本风险
增量计算2秒需客户确认变化率
边缘计算1秒需升级网关
分布式集群0.5秒需加服务器
单机全量10-30秒做不到

让客户自己选,并签字确认。

第四步:CYA(Cover Your Ass)

技术方案文档里必须写清楚:

## 风险声明
本方案基于以下假设:
✓ 测点变化率 < 10%
✓ 规则不包含实时数据库查询
✓ 单条规则执行时间 < 1ms

如任一假设不成立,无法保证3秒性能要求。

客户确认签字:_____________

写在最后:一个老程序员的碎碎念

做了这么多年的开发,见过太多这样的案例了:

  • 客户说要"秒级响应",实际上5秒也能接受
  • 客户说要"处理1亿数据",实际上常用的只有100万
  • 客户说要"99.999%可用性",实际上一年宕机几次都没人投诉

技术人最大的问题,不是技术不行,而是不敢问清楚需求。

怕问多了显得自己不专业,怕质疑需求得罪客户,怕承认做不到丢了项目。

但真相是:

  • 真正的专业,是敢于说"不"
  • 真正的负责,是把风险讲清楚
  • 真正的高手,是用数据说话

下次再遇到"不可能的需求",别慌,先问清楚:

  • 这个需求的真实场景是什么?
  • 有哪些约束条件是可以商量的?
  • 客户真正在意的是什么?

很多时候,你会发现需求根本没有你想的那么恐怖。


朋友们,你们遇到过哪些"一听就不靠谱"的需求?后来怎么解决的?欢迎留言分享!

对了,如果你身边有朋友也在为类似问题头疼,帮忙转发给他,说不定能救他一命 😂