首页 文章 精选 留言 我的

精选列表

搜索[科学大模型],共10000篇文章
优秀的个人博客,低调大师

算力网络不止是建设:剖析东数西算下三大落地卡点与实施路径

算力网络如何落地,关键不在“再建多少算力”,而在“把已经建好的算力用起来”。西部部分枢纽节点上架率不足30%,全国智算中心平均算力使用率约三成,两组数据放在一起,东数西算当前的矛盾已经从“建不建”转向“用不用”。本文按“空间布局→能源协同→调度运营”的主线,拆开算电资源错配、算力运营碎片化、价格机制传导不畅三道关卡,最后给出一套可判断、可执行的落地路径。

优秀的个人博客,低调大师

从六大成因到接口级定位的排查路径

云上排查丢包,和机房里的手感完全不同。机房里有明确的物理接口、明确的对端设备、明确的光模块;云上链路被托管服务切成若干段,你拿不到每台设备的接口计数器,能被你"看见"的东西少了很多。 一个常见的困惑场景:业务反馈实时音视频卡顿,你在云监控上把带宽、连接数、实例负载都看了一遍,全都正常,但用户依然抱怨。这时候需要换一套思路——云上的丢包往往不发生在你关心的那条链路上,而是发生在某个被忽略的边界上。 **核心要点** - **云上丢包的常见来源不在"设备",而在边界**:安全组与网络 ACL 拦截、实例带宽限速、NAT 与负载均衡的连接数上限。 - **流日志是云上最接近"接口计数"的东西**:它能告诉你哪些流量被允许、哪些被拒绝,但拿不到端到端时延与丢包。 - **端到端探测在云上必须自己部署**:托管服务不提供面向你业务的端到端丢包。 - **跨可用区与跨地域链路是最容易被忽略的一段**。 - **云上的限速型丢包是"配置事实"而不是"故障"**,它不会出现在任何告警里。 ## 一、云上丢包多出来的四个来源 **第一类是策略拦截型。**安全组与网络 ACL 拒绝的流量,在主机侧表现为"没有响应"或"连接被重置",很容易被当成丢包。好消息是这类"丢弃"是可观测的——开启 VPC 流日志后,记录里带 `action` 字段,被拒绝的流量会被明确标注为拒绝,而不是让你去猜。 ```javascript # 打开 VPC 流日志,收集到日志服务后按 action 分类 $ cloud-cli vpc create-flow-log --vpc-id vpc-xxxx \ --flow-log-name net-loss-obs --traffic-type ALL --storage-id logset-xxxx # 查询被拒绝的流量(这类"丢包"其实是策略命中,不是链路问题) SELECT srcaddr, dstaddr, dstport, action, count(*) AS cnt FROM flow_log WHERE ts >= now() - interval '1 hour' AND action = 'REJECT' GROUP BY srcaddr, dstaddr, dstport, action ORDER BY cnt DESC LIMIT 50; # 排查顺序建议: # 1) 先排除 REJECT —— 这是配置事实,改策略即可,不需要查链路 # 2) 剩下的 ACCEPT 流量里如果业务仍有丢包,才去怀疑链路与限速 ``` **第二类是限速型。**云上实例的公网带宽、内网带宽、以及部分网关类产品都有明确的规格上限。当瞬时流量超过规格时,超出部分会被丢弃。这类丢包的麻烦之处在于:它是**契约行为,不是故障**——不会触发任何告警,也查不到"错误计数",只会表现为业务侧丢包。判断方法是把流量曲线与规格上限对齐,看丢包是否恰好发生在触顶时刻。 **第三类是连接数型。**NAT 网关、负载均衡、代理这类组件通常有连接数或新建连接速率的规格限制。超出限制时表现为新建连接失败或超时,在应用侧看起来很像丢包。这类问题的指纹是"丢包与并发量相关,与带宽无关"。 **第四类是跨段链路型。**跨可用区、跨地域、以及通过专线/VPN 连回机房的链路,是云上最容易被忽略的一段。它不在任何一台实例的指标里,也不在你直接管理的设备上。这段链路的丢包只能靠端到端探测发现。 ## 二、云上可用的三类观测手段 |**手段**|**能回答什么**|**局限**| | --- | --- | --- | |**VPC 流日志**|谁和谁通信、被允许还是被拒绝、流量大小|无端到端时延与丢包,只有流量事实| |**云监控指标**|实例与托管组件的带宽、连接数、包量等聚合指标|口径与设备级计数不等价,且粒度较粗| |**自建端到端探测**|业务视角的端到端丢包、时延、抖动|需要自己部署与维护,覆盖点取决于部署位置| 三者的关系是互补:流日志解决"有没有被策略拦掉",云监控解决"有没有触到规格上限",自建探测解决"端到端到底丢没丢、丢在哪一段"。 探测部署上有个实践建议:**不要只在一个可用区部署探测点**。至少要在每个承载业务的可用区各放一个探测点,并且互相探测。这样才能把"跨可用区那一段"的丢包从整体里区分出来——否则你看到的只是"整体偶尔丢包",而无法判断是不是某一对可用区之间的问题。 ## 三、云上排查的四步 1. **先排除策略拦截。**查流日志里的 REJECT 记录。这一步能排除掉相当一部分"伪丢包",而且是纯配置问题,不需要查链路。 1. **再排除规格触顶。**把丢包发生的时间点与带宽曲线、连接数曲线对齐,看是否恰好触到规格上限。触顶型丢包不会出现在任何错误计数里。 1. **用端到端探测确认。**自建探测点确认端到端确实丢包,并用分位数量化(均值会掩盖尖峰,而实时业务恰恰只对尖峰敏感)。 1. **缩小到具体链路段。**用逐跳探测缩小范围,但对中间跳的结果要做三项验证:末端是否同向劣化、双向是否对称、换探测协议是否仍丢。三条不成立就是设备对探测包限速。 ## 四、混合云:两侧口径要对齐 混合云场景下最容易出现的偏差是两个口径不一致:机房侧采的是设备级接口错误与丢弃计数(增量),云侧采的是云监控的聚合指标(速率)。两者量纲、统计点、采样间隔都不一样,直接放到同一张图上比较,会得出错误结论。 可行的做法是**把云侧对齐到"可比的增量口径"**:从流日志聚合出单位时间的包数与字节数,再与自建探测的丢包比率结合,形成一套与机房侧可比的度量。至于专线两端,必须显式建立一条连接关系,否则跨侧的丢包定位会卡在边界上。 ## 五、落地顺序 先把流日志打开并按 action 归类(这一步成本最低、收益最直接);再把实例与网关的规格上限整理成一份清单,作为"限速型丢包"的判据;然后在每个可用区部署探测点并把数据接进时序库;最后才是告警收敛与自动化。 ## 一句话总结 云上排查丢包,先排除策略拦截与规格触顶这两类"配置事实",再用自建探测确认端到端并量化到分位数,最后用带验证规则的逐跳探测缩小到具体链路段——照着机房那套思路查云上丢包,大概率会查错方向。

优秀的个人博客,低调大师

Python的Django ORM把我坑惨了,原来select_related和prefetch_related的区别这么大

上个月帮同事看一个 Django 接口的性能问题。页面加载要七八秒,他定位到是某个列表页查询太慢。代码大概长这样: ``` orders = Order.objects.filter(status='paid') for order in orders: print(order.user.username) # 访问关联的 User for item in order.items.all(): # 访问关联的 OrderItem print(item.product.name) # 再访问 OrderItem 的 Product ``` 他说:“Django 的 ORM 不是自动处理关联查询吗?我直接用点号访问,多方便。” 是方便。但每次点号访问,只要关联对象还没被加载,Django 就会**单独发一条 SQL** 去数据库取。 一个订单列表页,假设有 200 个订单,每个订单 5 个商品,涉及 100 个用户。那么: - 取订单:1 条 SQL - 每个订单取用户:200 条 SQL - 每个订单取商品项:200 条 SQL - 每个商品项取商品:1000 条 SQL 加起来 1401 条 SQL。每条 SQL 哪怕只花 5 毫秒,也要 7 秒多。这就是典型的 **N+1 查询问题**。不是数据库慢,是查询次数太多。 解决办法就是 `select_related` 和 `prefetch_related`。但这两个名字长得像、功能描述也像——“都是用来减少查询次数的”。很多人随便挑一个用,结果要么没效果,要么报错,要么内存暴涨。 ## **先搞清楚外键在数据库里长什么样** 要理解两者的区别,得先回到数据库层面看关联关系是怎么存的。 以订单和用户为例: ``` class User(models.Model): username = models.CharField(max_length=100) class Order(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) status = models.CharField(max_length=20) created_at = models.DateTimeField(auto_now_add=True) ``` `Order` 表里有一个 `user_id` 字段,存的是用户表的主键。这是一个**多对一**关系:多个订单可以属于同一个用户,每个订单只有一个用户。 当 Django 执行 `order.user` 的时候,它看到 `Order` 实例的 `user_id` 是 5,但 `user` 对象还没加载,于是发一条 SQL: ``` SELECT * FROM user WHERE id = 5; ``` 如果循环里对每个订单都这么做,就是 N 条额外 SQL。 `select_related` 解决的就是这个问题。它告诉 Django:“在取订单的时候,顺便把关联的用户也取出来。” Django 会用 SQL 的 `JOIN` 把两张表连起来,一次查询拿到所有数据: ``` SELECT order.*, user.* FROM order INNER JOIN user ON order.user_id = user.id WHERE order.status = 'paid'; ``` 拿到结果后,Django 在内存里把每一行拆成 `Order` 和 `User` 两个对象,并建立好引用。之后 `order.user` 直接返回已经加载好的对象,不再发 SQL。 一次 JOIN 查询代替了 N+1 条查询。 ## **多对多和反向关系,JOIN 搞不定** 现在看订单和商品项的关系: ``` class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE) product = models.ForeignKey(Product, on_delete=models.CASCADE) quantity = models.IntegerField() ``` `OrderItem` 有外键指向 `Order`。反过来,一个订单可以有多个订单项。从 `Order` 到 `OrderItem` 是一个**一对多**的反向关系。 Django 默认给反向关系起名叫 `orderitem_set`,但如果 `ForeignKey` 里设置了 `related_name='items'`,就可以用 `order.items.all()` 访问。 问题来了:`select_related` 能不能用在 `order.items.all()` 上? 不能。`select_related` 只能用于**单值关系**,也就是 `ForeignKey` 和 `OneToOneField`。因为 JOIN 之后,一行订单对应一行用户,一对一,不会产生重复行。 但 `order.items.all()` 是一对多,一个订单对应多个订单项。如果硬用 JOIN: ``` SELECT order.*, orderitem.* FROM order INNER JOIN orderitem ON orderitem.order_id = order.id; ``` 一个订单有 5 个订单项,结果集里就会出现 5 行。Django 拿到这 5 行后,需要把它们合并成一个 `Order` 对象,再把 5 个 `OrderItem` 挂上去。这可行,但如果有多个一对多关系同时 JOIN,行数会乘法式膨胀,性能反而更差。 所以 Django 对多对多和反向一对多提供了另一个工具:`prefetch_related`。 ## **prefetch_related 走的是另一条路** `prefetch_related` 不用 JOIN。它的做法是**分两步查询,然后在 Python 层面做匹配**。 还是用订单和订单项举例: ``` orders = Order.objects.prefetch_related('items').filter(status='paid') ``` Django 会执行两条 SQL: 第一条,取所有订单: ``` SELECT * FROM order WHERE status = 'paid'; ``` 假设拿到 200 个订单,ID 是 1 到 200。 第二条,取这些订单的所有订单项: ``` SELECT * FROM orderitem WHERE order_id IN (1, 2, 3, ..., 200); ``` 一条 SQL 把所有相关订单项都取回来。 然后在 Python 里,Django 遍历这 200 个订单项,根据每项的 `order_id`,把它挂到对应的 `Order` 对象的 `_prefetched_objects_cache` 里。之后再访问 `order.items.all()`,直接返回缓存好的列表,不再发 SQL。 总共 2 条 SQL,而不是 201 条。 `prefetch_related` 还能继续往下预取。比如订单项里还有 `product` 外键: ``` orders = Order.objects.prefetch_related('items__product') ``` 它会发三条 SQL:取订单、取订单项、取商品。然后在 Python 里把 `OrderItem` 和 `Product` 关联起来。比用 `select_related` 做多层 JOIN 更可控,也更省内存。 ## **核心区别:一条 SQL 还是两条 SQL** 现在可以做个清晰的对比了: |维度|select_related|prefetch_related| | --- | --- | --- | |查询方式|SQL JOIN|分步查询 + Python 匹配| |SQL 条数|1 条|2 条(或更多)| |适用关系|ForeignKey、OneToOneField|ManyToManyField、反向 ForeignKey| |能否用于多对多|不能|能| |能否用于反向一对多|不能|能| |内存占用|较低(单行不膨胀)|较高(要存所有关联对象)| |是否支持自定义查询|有限|支持 Prefetch 对象| |数据库压力|JOIN 可能较重|多条简单查询,通常更轻| 一句话总结:**select_related 用 JOIN 一次拿完,适合单值关系;prefetch_related 分两次拿,适合多值关系。** 判断标准很简单:**关联对象只有一个,用 select_related;关联对象有多个,用 prefetch_related。** `order.user` 是单个用户,用 `select_related('user')`。 `order.items.all()` 是多个订单项,用 `prefetch_related('items')`。 `user.order_set.all()` 是反向多个订单,用 `prefetch_related('order_set')`。 ## **用错了会怎样** 用 `prefetch_related` 去预取一个 ForeignKey 行不行?行,但没必要。它会多发一条 SQL,虽然也能工作,但比 `select_related` 多一次数据库往返。 用 `select_related` 去预取多对多呢?直接报错: ``` FieldError: Cannot resolve keyword 'items' into field. Choices are: ... ``` Django 在解析 `select_related` 的参数时,只会查 ForeignKey 和 OneToOneField。遇到多对多或反向关系,它找不到对应的字段,直接抛异常。 还有一种常见错误:在 `prefetch_related` 里套 `select_related` 的语法: ``` Order.objects.prefetch_related('user__profile') # 可以,会分步预取 Order.objects.select_related('items__product') # 报错,items 不是单值关系 ``` 如果一条链路上既有单值又有多值,可以混用: ``` Order.objects.select_related('user').prefetch_related('items__product') ``` Django 会先 JOIN 用户,再分步预取订单项和商品。这是处理复杂关联的标准写法。 ## **Prefetch 对象:更精细的控制** `prefetch_related` 默认会把关联对象的全部字段和全部记录都取回来。如果关联数据量很大,或者你只需要其中一部分,可以用 `Prefetch` 对象做精细控制。 ``` from django.db.models import Prefetch orders = Order.objects.prefetch_related( Prefetch( 'items', queryset=OrderItem.objects.filter(quantity__gt=1).select_related('product'), to_attr='big_items' ) ) ``` 这里做了三件事: 1. 只预取 `quantity > 1` 的订单项,不是全部。 1. 预取时顺便用 `select_related` 把 `product` 也加载了,避免后续再查。 1. 结果存到 `order.big_items` 这个自定义属性里,而不是默认的 `order.items`。 之后用 `order.big_items` 访问,得到的是一个列表,已经过滤好、关联好,且不再触发 SQL。 `Prefetch` 的价值在于:它让你在“分步查询”这个框架里,仍然能对第二步查询做定制。这是 `select_related` 做不到的——JOIN 的 SQL 由 Django 生成,你很难在中间插条件。 ## **性能对比:不只是 SQL 条数** 很多人以为 `select_related` 一定比 `prefetch_related` 快,因为 SQL 条数少。这不一定。 `select_related` 用 JOIN,如果关联表很大,或者关联链很长,JOIN 出来的结果集可能非常宽。比如 `select_related('user', 'product', 'address', 'payment')`,每行订单都会带上四张表的全部字段。字段越多,网络传输和内存解析的开销越大。 `prefetch_related` 分步查,每条 SQL 只取一张表的字段,结果集更干净。在关联数据量大、字段多的情况下,反而可能更快。 另一个因素是索引。JOIN 的 ON 条件如果没有索引,数据库要做全表扫描。而 `prefetch_related` 的 `IN` 查询通常能用到主键索引,效率很高。 所以选择依据不是“哪个更快”,而是“哪个正确”。单值关系用 `select_related`,多值关系用 `prefetch_related`。这是由关系类型决定的,不是性能调优的结果。 ## **一个真实场景** 我之前维护一个博客系统,首页要展示最新的 20 篇文章,每篇文章显示作者名、分类名、标签列表、评论数。 最初的代码: ``` posts = Post.objects.all()[:20] for post in posts: print(post.author.username) print(post.category.name) for tag in post.tags.all(): print(tag.name) print(post.comments.count()) ``` `author` 和 `category` 是外键,可以用 `select_related`。`tags` 是多对多,`comments` 是反向一对多,需要用 `prefetch_related`。 优化后: ``` posts = Post.objects.select_related('author', 'category').prefetch_related('tags', 'comments')[:20] ``` SQL 从 1 + 20 + 20 + 20 + 20 = 81 条降到 1 + 20 + 20 + 20 = 61 条?不是。 `select_related` 把 author 和 category 合并进第一条 SQL,所以取文章是 1 条。 `prefetch_related('tags')` 是 1 条:`SELECT * FROM tag WHERE post_id IN (...)`。 `prefetch_related('comments')` 是 1 条:`SELECT * FROM comment WHERE post_id IN (...)`。 总共 3 条 SQL。页面加载从 4 秒降到 200 毫秒。 注意 `comments.count()` 的问题。`prefetch_related('comments')` 会把所有评论对象加载到内存,如果只是想显示数量,用 `annotate` 更合适: ``` from django.db.models import Count posts = Post.objects.select_related('author', 'category').prefetch_related('tags').annotate(comment_count=Count('comments'))[:20] ``` 这样评论数由数据库直接算好,不用把评论对象全部取回来。`prefetch_related` 适合“需要访问关联对象本身”的场景,`annotate` 适合“只需要聚合值”的场景。 ## **怎么发现 N+1** Django Debug Toolbar 是最直观的工具。装好之后,页面侧边栏会显示每个请求执行了多少条 SQL。如果看到几十上百条相似结构的查询,基本就是 N+1。 没有 toolbar 的话,可以用 `connection.queries`: ``` from django.db import connection # 执行查询 list(orders) print(len(connection.queries)) for q in connection.queries: print(q['sql']) ``` `connection.queries` 只在 `DEBUG=True` 时记录,生产环境不适用。生产环境可以用 `django-silk` 或者 APM 工具做 SQL 监控。 还有一个办法:在测试里断言查询次数。 ``` from django.test import TestCase from django.assertNumQueries import assertNumQueries class OrderTest(TestCase): def test_order_list_queries(self): with self.assertNumQueries(3): orders = Order.objects.select_related('user').prefetch_related('items') for order in orders: order.user.username list(order.items.all()) ``` 如果实际查询次数和预期不符,测试直接失败。这个方法能把 N+1 挡在上线之前。 ## **什么时候不用预取** 预取不是越多越好。有几个场景要谨慎: 第一,**关联数据量极大**。比如一篇热门文章有十万条评论,`prefetch_related('comments')` 会一次性把十万条评论全部加载到内存,直接 OOM。这时候应该分页,或者用 `annotate` 只取数量。 第二,**预取了但没用到**。`select_related` 和 `prefetch_related` 都有开销。如果某个关联对象在后续逻辑里根本没被访问,预取就是浪费。只预取确定会用到的关系。 第三,**查询集只取一条记录**。`Post.objects.get(id=1)` 本身只发一条 SQL,即使有 N+1,N 也是 1。预取的意义不大。预取主要在列表页、循环访问的场景下发挥作用。 ## **最后** `select_related` 和 `prefetch_related` 的区别,本质上是**单值关系**和**多值关系**的区别,是**一条 JOIN** 和**分步查询**的区别。 名字像,但适用场景不重叠:ForeignKey 和 OneToOneField 用 `select_related`,ManyToManyField 和反向 ForeignKey 用 `prefetch_related`。混用、用错,轻则没效果,重则报错或内存暴涨。 处理列表页的时候,习惯性地问自己:这个循环里访问了哪些关联对象?它们是单个还是多个?单个的进 `select_related`,多个的进 `prefetch_related`。再配上 `assertNumQueries` 写个测试,N+1 就再也坑不到你了。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册