Scott Shi推分布式爬虫架构 靠无状态解耦与分片存储破除瓶颈 Scott Shi
爬虫系统最大瓶颈在于目标站点限流与容错开销,通过消息队列将抓取与解析异步解耦、并将状态完全下沉至分片存储实现计算幂等,是达成集群无限横向扩展的核心解法。
AI-assisted summary of [Chinese] Case Study: Design a distributed crawler — original source . Check the original for full context.
Source published: 2020-11-05
Key ideas 分布式爬虫的核心设计在于抓取(Crawler)与解析(Parser)的异步解耦,将抓取瓶颈与解析重试隔离,以避免因下游 Schema 变动引发高昂的重新抓取开销。 (25:00) 整个系统的瓶颈几乎永远来自被爬取网站的服务端限流(Server-side Throttling),系统性能必须通过多账号轮换、分布式代理 IP 池与爬取礼节进行对抗与调优。 (15:00) 系统容错依赖于状态下沉与纯无状态的计算 Worker,通过基于 Hash(URL, Iteration) 的幂等性保障和独立的 Sweeper 巡检进程实现丢消息自愈与节点崩溃后的无副作用恢复。 (60:00) 面对海量抓取需求(如一天内爬完 Amazon),工程落地应采取实验与调优策略:先设定小规模 Baseline 部署,根据瓶颈按比例弹性调整 Crawler 和 Parser 的资源配比以达到平衡态。 (75:00) Detailed notes ## Background
该内容由 Scott Shi 于 2020 年 11 月 5 日发布在 YouTube 频道。视频记录了一场针对系统设计面试(System Design Interview)的分布式爬虫案例实战分享,主讲人结合实际项目推导了面向多业务场景的通用爬虫架构,并提供了配套的设计参考文档。
## TL;DR
本期内容系统讲解了大规模分布式爬虫的架构推导与工程落地策略。主讲人强调将网络抓取(Crawler)与数据解析(Parser)深度异步解耦,以最小化外部瓶颈并控制重试开销;同时深入探讨了存储层在关系型 SQL 分片与 NoSQL 之间的本质取舍和容量边界。通过设计状态驱动的元数据表、消息队列竞争消费机制以及后台巡检进程(Sweeper),系统实现了彻底的幂等性与容错能力。最终结合个性化新闻聚合与 Dota 比赛推荐实战,归纳出计算与状态分离、异步解耦与死信补偿等四大核心系统设计思想。
## Atmosphere & style
整场分享呈现出浓厚的硅谷工业界实战氛围与务实犀利的批判色彩。主讲人风格直接且不留情面,既直言不讳地指出市面流行面试资料的粗制滥造,又以生动的“星巴克小票排队”等比喻拆解并发概念。期间主讲人与连线工程师(如 Redshift 研发)就底层事务锁与存储分片展开了硬核交锋,整体节奏紧凑通透,宛如一场资深架构师面对面的工程复盘闭门研讨。
## Bulletin
- [25:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1500s) 分布式爬虫的核心设计在于抓取(Crawler)与解析(Parser)的异步解耦,将抓取瓶颈与解析重试隔离,以避免因下游 Schema 变动引发高昂的重新抓取开销。
- [15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=900s) 整个系统的瓶颈几乎永远来自被爬取网站的服务端限流(Server-side Throttling),系统性能必须通过多账号轮换、分布式代理 IP 池与爬取礼节进行对抗与调优。
- [01:00:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3600s) 系统容错依赖于状态下沉与纯无状态的计算 Worker,通过基于 Hash(URL, Iteration) 的幂等性保障和独立的 Sweeper 巡检进程实现丢消息自愈与节点崩溃后的无副作用恢复。
- [01:15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4500s) 面对海量抓取需求(如一天内爬完 Amazon),工程落地应采取实验与调优策略:先设定小规模 Baseline 部署,根据瓶颈按比例弹性调整 Crawler 和 Parser 的资源配比以达到平衡态。
## Key takeaways
- [04:40](https://www.youtube.com/watch?v=awPOdsnPL-8&t=280s) 在分布式爬虫设计中,High Scalability(高扩展性)的优先级远高于 High Availability(高可用性),系统必须能够针对多变的任务规模弹性扩缩容 Worker 节点。
- [15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=900s) 爬虫的外部最大瓶颈是被爬取站点的限流(Throttling),工程上需通过账号并发、代理 IP 轮换以及降低单域名抓取频率来做个“良好公民”,避免被打封或打挂目标网站。
- [25:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1500s) 抓取(Crawler)与解析(Parser)必须通过消息队列严格异步解耦:爬虫拉取的 Raw Data 原样落盘,Parser 解析失败只需重跑解析任务,绝不重试昂贵的网络爬取。
- [19:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1140s) 递归抓取的深度控制需在元数据中维护 depth 字段,通过层级累加(parent.depth + 1)并在超过阈值时丢弃新 URL,防止全网或复杂电商爬虫陷入无休止发散。
- [25:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1500s) Raw Data 与 Clean Data 需分流存储:Raw Data 仅作为 Key-Value 或大对象暂存,Clean Data 则按业务结构化提取落表;针对网页归档等深拷贝场景需把静态资源外置至对象存储(如 S3)。
- [35:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2100s) 存储选型应依据实际用例做权衡:严格事务与调度元数据必须选用 SQL 保障持久性,Raw Data 若数据量大或单行体积超过限制(如 DynamoDB 400KB 上限)则应倾向于对象存储。
- [55:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3300s) 切忌让程序自动根据 JSON 动态更改底层数据库 Schema,遇到未知字段应采取阻塞报警或“非阻塞忽略并异步告警”策略,保障数据库核心契约的稳定。
- [01:00:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3600s) 任务记录表以 Hash(URL, Iteration) 为主键分片,并建立 QUEUED、CRAWLED、PROCESSED、STUCK 的确定性状态机,天然消除并发冲突并实现写操作的幂等性。
- [01:10:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4200s) 后台巡检组件(Sweeper)是容错自愈的枢纽:负责处理 Schema 未知的 STUCK 告警、扫描并重新派发超时卡死的 QUEUED 任务,同时向业务端聚合汇报进度。
- [01:05:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3900s) 上游任务分发应依赖消息队列让 Worker 主动 Claim 消费,而非直接全表扫描;结合底层分片的覆盖写或静默丢弃(No-Op),即便任务重复投递也无任何系统副作用。
- [01:16:30](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4590s) 贯穿分布式爬虫系统的四大架构核心为:幂等性(不怕失败)、消息队列异步解耦(关注点分离与独立伸缩)、计算与状态分离(Stateless Worker)、以及基于 Sweeper 的死信容错补偿机制。
## Detailed walkthrough
### [00:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=0s) 爬虫核心诉求与系统设计面试策略
设计一个分布式爬虫(例如爬取亚马逊这样的大型电商平台),核心通常围绕几个关键能力展开:首先是 URL Discovery(发现新链接),其次是周期性调度(如 Daily Crawl 每日抓取),再者是 Smart Schema Detection(模式检测)。爬虫的数据主动权完全掌握在第三方平台(如 Amazon、eBay)手中,目标网站一旦修改前端页面结构,爬虫端必须能及时感知并处理最新的 Schema 变动。最后,爬虫抓下来的原始数据(Raw Data)非常粗糙杂乱,必须经过清洗和加工(Massage)才能转化为规范化的 Formalized Data 供下游系统消费。
在系统设计面试(Mock Interview)的实战把控上,时间规划至关重要。通常一个 45 分钟左右的面试,建议在心中形成明确的时间配比:
- **Use Cases & Business Sense**:花 10 分钟左右厘清业务场景与需求。
- **Constraints(约束与估算)**:花几分钟敲定数据规模与瓶颈。
- **Architecture(系统架构)**:重中之重,必须分配至少 10 到 15 分钟深入推导。
- **API Design**:视情况花 5 到 10 分钟。
但在爬虫这类题目中,API 往往不是核心难点,甚至三两句话就可以带过,系统设计的成败几乎完全取决于架构(Architecture)。资深工程师(Experienced Engineer)与初级程序员(Junior Developer)最大的分水岭,就在于是否真正重视并具备扎实的 Business Sense 与约束推演能力。
如果在面试中某一个环节卡住了,切忌慌乱纠缠。成熟的沟通策略是主动进行 Timebox(时间盒控制):“比如我先给自己限定一分钟,如果这一步暂时推导不出来,我们先往后走去选型 Database,把核心框架搭建完成,最后有剩余时间再回过头来攻克这个卡点。”这种表现向面试官传递的是一种极度成熟的工程权衡信号(Signal)。
[02:40](https://www.youtube.com/watch?v=awPOdsnPL-8&t=160s) 批判粗劣学习资料与推崇经典系统设计读物 针对有同学提到在《Grokking the System Design Interview》中看到相似的问题一个用 MySQL 解决、另一个却用 Cassandra 解决而感到困惑,这种现象本质上属于工程解法的“非最优但可行(Working but not optimal)”。方案只要在满足业务约束的前提下跑得通,就没有绝对的冲突。但必须指出,《Grokking the System Design Interview》是非常糟糕且混乱的参考书,代码和逻辑都很糙,就像某些依靠 SEO 排在搜索前列的技术博客一样,搜 Optimistic Locking(乐观锁)很容易搜到它,但内容东拼西凑、质量堪忧。真正优质的技术内容永远是极其精炼、高度概括的(Concise)。
建立系统设计的独立思考与常识(Common Sense),最核心的途径是阅读经典著作与原始论文:
- **经典书籍**:强烈推荐剑桥大学 PhD Martin Kleppmann 撰写的《Designing Data-Intensive Applications》(DDIA,数据密集型应用系统设计)。这本书是系统设计领域公认的殿堂级佳作,完全可以作为体系化学习的目录索引。
- **行业工程博客**:如 Uber 早期的高质量 Engineering Blog、Dropbox、Netflix 以及 High Scalability 网站上关于大型分布式架构演进的技术实录。系统设计能力高度依赖阅读量、长期经验积累与独立批判思考。
[04:40](https://www.youtube.com/watch?v=awPOdsnPL-8&t=280s) 爬虫系统的核心特性与非功能需求 分布式爬虫的核心设计目标非常明确,但在可用性与扩展性的权衡上有其独特性:
- **High Scalability(高扩展性)远重于 High Availability(高可用性)**:爬虫面对的 URL 动辄百万甚至上亿级,必须具备弹性的扩展能力。需要爬取的数据规模暴增时,系统能够迅速扩容 Worker 节点;任务量缩减时又能快速缩容。由于前期往往无法精确估算目标站点的真实规模,必须在工程实践中“摸着石头过河(Practical practice)”——先上线 10 台机器打个样,观察抓取一轮所需的时间和资源开销,再动态调整集群规模。
- **Fault Tolerance(容错能力)与 Idempotent(幂等性)**:Worker 节点在抓取、启动或调度阶段随时可能发生 Crash。系统必须支持故障节点的无缝断点续爬。而伴随故障重试必然会产生重复操作(Repeated Action),因此下游所有写入与处理逻辑必须严格保证幂等性,消除一切副作用。
- **Flexible Schema(灵活的数据模式)**:爬虫爬取第三方网站时,对方随时可能突然增加一个未声明的新字段(例如亚马逊突然上线 `Compared price` 对比价)。系统必须能够自动探测到未知字段,并及时告警通知工程师人工干预或配置特殊处理流水线,业界常戏称为 "Smart Schema",但本质上只是一套严谨的字段探测与通知机制,并非神秘的机器学习模型。
在本次讨论中,两项内容被明确划出范围:一是网站的 Authentication(需要模拟登录态与账号授权的场景),二是目标站点的 Throttling 逆向对抗。因为爬虫系统最大的外部瓶颈永远来自于被爬取网站的服务端限流(Server-side Throttling)——正常用户一分钟点击不到 10 个页面,而爬虫若一小时扫数千页,必触发 IP 封锁或 Rate Limiting。
[08:50](https://www.youtube.com/watch?v=awPOdsnPL-8&t=530s) 讲师的研究生实战:个性化新闻聚合与 Dota 比赛推荐 为了建立直观的工程语境,这里回顾两个在研究生时期亲手实现的真实爬虫项目:
第一个项目是 2013 年主导的个性化新闻聚合器(Personalized News Aggregator),其产品逻辑与当时刚刚崭露头角的今日头条(字节跳动成立于 2012 年底)几乎如出一辙。系统从 CNN、华尔街日报(Wall Street Journal)、华盛顿邮报(Washington Post)等新闻源抓取 HTML 页面,利用 XPath 抽取封面高光图与正文;借助 Solr 进行文本处理、多源新闻去重并自动提炼 Summary 摘要;最后通过协同过滤(Collaborative Filtering)记录用户点击行为与偏好,提供个性化推荐并邮件触达。整个系统连续运行了约 50 天,爬取了 12,000 多条深度新闻,沉淀了 2GB 左右的干净数据。
第二个项目则是针对多人在线对战游戏 Dota(类似英雄联盟 LOL)的阵容胜率实时预测工具。当时 Steam 官方 API 每天产出约 150 万场公开比赛(Match Replay)。团队设计了爬虫集群,从 7,300 位高水平玩家切入,抓取了 50 万场 Replay 的原始数据:
- **数据形态**:Steam API 返回的是结构极其工整的 JSON 数据(包含天辉与夜魇双方挑选的英雄及胜负结果),几乎不需要耗费精力去清洗脏数据。
- **离线训练与前端交互**:离线端用机器学习训练英雄对阵组合的高胜率模型;客户端则用 C# 编写桌面程序。当玩家在游戏选人阶段按下专属截图键,客户端利用极速的感知哈希算法(pHash)在毫秒级内从英雄选择池截图中匹配出双方已选英雄,调用后端模型实时推荐出克制对手、胜率最高的可选英雄。
```mermaid
flowchart TD
accTitle: Dota Replay Crawler and Recommendation Pipeline
accDescr: Pipeline showing data ingestion from Steam API to ML training and real-time client inference using pHash.
subgraph Ingestion ["数据采集 (Data Ingestion)"]
A["Steam API"] -->|"抓取 50 万场比赛 (JSON)"| B["分布式爬虫集群"]
B --> C[("Replay 原始数据池")]
end
subgraph Offline ["离线计算 (Offline ML Model)"]
C --> D["特征提取 & 阵容胜率训练"]
D --> E["离线胜率推荐模型"]
end
subgraph Client ["客户端实时推理 (Real-time Client)"]
F["玩家游戏选人界面"] -->|"按下快捷键截屏"| G["C# 客户端"]
G -->|"pHash 图像比对"| H["极速提取已选英雄 ID"]
H -->|"查询最优英雄"| E
E -->|"返回推荐列表"| G
end
```
[15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=900s) 吞吐瓶颈拆解:数据估算与服务端限流对抗 在工程评估阶段,以游戏 Replay 爬取为例进行规模估算:每个比赛 Replay 的 JSON 数据约为 20KB,全网每天产生 170 万场,每日增量数据大约为:
$$1.7 \times 10^6 \times 20\text{ KB} \approx 34\text{ GB/天}$$
在实际工程中,系统的瓶颈(Bottleneck)几乎永远不在内部架构,而在目标服务的 Server-side Throttling。“Steam 官方服务器没有任何义务支撑你每秒上百万次的暴击抓取”,只要请求频率过高,IP 或账号就会被立即封禁。针对不同层级的限流,业界常用的破局手段包括:
- **Account 维度限流**:如果按用户账号限制,就注册并维护 20 多个有效账号,分配给多个线程隔离并发爬取。
- **IP 维度限流**:如果按 IP 限制,就需要调度多节点通过分布式代理 IP 池轮换发起请求。
如果第三方没有对你施加任何 Throttle,那该网站大概率本身架构脆弱,极其容易被爬虫直接打挂造成拒绝服务(DDoS)。商业级爬虫通常还会受到商业协议约束,必须遵守爬虫礼节做一个“良好公民(Good Citizen)”——单节点同时交叉抓取多个不同域名,避免逮住单一站点死薅导致被封杀。至于第三方的限制阈值,在没有商务沟通渠道的前提下完全是一个黑盒,只能依靠工程师手工由高到低(例如从 10 QPS 逐步试探降到 5 QPS)反复摸索、调优与配置。
[19:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1140s) 分布式爬虫通用架构与深度优先/广度优先调度 无论是爬取网页新闻还是结构化的 Replay,爬虫的核心处理流都遵循一套通用的骨架体系:
```mermaid
flowchart LR
accTitle: Distributed Crawler Architecture Flow
accDescr: Architecture diagram showing interaction between Scheduler, Message Queue, Crawler, DBs, Parser, and Sweeper.
subgraph Scheduling ["调度与抓取 (Fetch & Schedule)"]
S["Scheduler 调度器"] -->|"生成 Seed URLs"| M[("Metadata DB")]
M --> Q1["抓取任务队列 (Message Queue)"]
Q1 --> C["Crawler 集群 Worker"]
end
subgraph Processing ["数据解析与清洗 (Parse & Clean)"]
C -->|"写入原始数据"| R[("Raw Data DB")]
C -->|"发送解析通知"| Q2["Parser 任务队列"]
Q2 --> P["Parser 集群 Worker"]
P -->|"读取与验证 Format"| R
P -->|"写入合规数据"| CL[("Clean Data DB")]
P -->|"提取新 URL (2,3,4,5...)"| M
end
subgraph Maintenance ["状态维护 (Maintenance)"]
SW["Sweeper 巡检巡视服务"] -.->|"监控 Crash / 唤醒超时未完成任务"| C
SW -.->|"重推卡死任务"| P
end
```
- **Scheduler(调度器)**:负责控制爬取节律。针对周期性抓取(如每日定时刷新亚马逊全站竞品价格),由调度器从域名白名单(Whitelist of domains)中提取 Seed URLs 写入元数据存储(Metadata DB),并向消息队列推送异步任务。
- **Crawler 集群**:监听消息队列中的待抓取链接,拉取网页或接口内容后,原封不动地沉淀到原始数据表(Raw Data DB),随后给下游发消息通知解析。
- **Parser 集群**:消费通知后从 Raw Data DB 读取数据,验证 Schema 格式是否合法。若字段完全识别无误,则将处理好的结构化数据沉淀到 Clean Data DB;同时从 HTML 中提取出新的外链(如 URL 2、3、4、5),回填入 Metadata DB 排队作为下一轮抓取的候选源。
- **Sweeper(巡检进程)**:实时巡视并处理节点崩溃。一旦 Crawler 或 Parser 在执行阶段发生 Crash 导致任务挂起,Sweeper 会自动检测超时心跳并进行状态重置与无缝重启(Restart)。
针对现场讨论中关于“爬取网页时究竟使用广度优先搜索(BFS)还是深度优先搜索(DFS)以及如何控制层级深度”的提问:
- **深度控制(Depth Control)机制**:默认情况下全网爬虫更注重去重而不是限制深度,但如果面对规模庞大的电商平台(如亚马逊包含海量关联商品及变体),层级无限延伸会导致爬虫彻底失控。此时必须在 Metadata 记录中维护一个层级字段 `depth`。
- **层级迭代逻辑**:初始 Seed URL 的 `depth` 设为 1;父页面解析出子 URL 时,统一赋值为 `parent.depth + 1`;当解析出的链接其 `depth` 超过预设阈值(例如深度超过 10 层)时,系统便丢弃该 URL 不再入队,从而实现安全受控的抓取边界。
[25:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1500s) 爬虫与解析器分离:解耦关注点与数据模型设计 在分布式爬虫的架构中,将抓取(Crawler)与解析(Parser)拆分成两个完全异步的独立步骤是至关重要的设计考量。其核心原因在于控制重试的粒度。整个爬虫系统最大的瓶颈(Bottleneck)始终在于从爬虫到外部网站(External Website)的网络抓取过程。如果在解析(Parse)页面时遇到了未知的 Schema 导致解析失败,但由于 Crawl 和 Parse 耦合在同一环节而不得不重新触发抓取,就会造成极大的外部带宽与时间资源浪费。因此,系统通过解耦关注点(Separation of Concerns),将爬取到的原始数据先暂存到 Raw Data Store 中,即便后续 Parse 环节失败,也只需重启解析即可,完全不需要去外部网站重新爬取(Recrawl)。
```mermaid
flowchart LR
accTitle: Crawler and Parser Pipeline
accDescr: Pipeline showing data flowing from an external site to a raw database and then through a parser to a clean database.
subgraph S1["Raw Data Ingestion"]
A["External Website"] --> B["Crawler"]
B --> C[("Raw Data Store")]
end
subgraph S2["Data Processing"]
C --> D["Parser"]
D --> E[("Clean Data Store")]
end
```
Raw Data 与 Clean Data 的数据模型设计有着本质区别:
- **Raw Data**:通常是简单的 Key-Value 结构,例如主键 ID 搭配一个 JSON 或 HTML 原始内容。
- **Clean Data**:是经过清洗和结构化的数据。例如游戏复盘(Game Replay)场景,包含 URL、抓取迭代轮次(Iteration)、甲方选了哪些英雄、乙方选了哪些英雄等自定义字段;而在新闻抓取(News Crawl)场景,则包含抓取类型、URL、新闻摘要(Highlight,如“Uber 又撒钱了”)、正文主体(Main Text)以及首图存储路径。
针对数据暂存,还存在浅拷贝(Shallow Copy)与深拷贝(Deep Copy)的区分。如果抓取的是 JSON 结构,并不涉及深拷贝问题;但如果是构建网页归档服务(Web Archive),则必须做深拷贝,不仅要把 HTML 保存下来,还要把引用的 CSV 文件、图片(Image)、视频(Video)等所有静态资源完整复制下来。深拷贝场景下,新闻的正文文本与高分辨率图片往往需要分流存储在不同的数据库中。
---
[30:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=1800s) 吞吐瓶颈与系统各核心组件的异步设计 爬虫需要从外部网站下载数据并落盘存储,网络带宽和吞吐面临双重压力。针对空间与网络占用的优化,首要手段是对抓取后的数据进行压缩(Compress)而非加密,通过在计算端消耗解压缩(Decompress)的时间来换取空间与传输成本,这种时间换空间的权衡(Trade-off)在外部抓取为主要瓶颈的前提下具有极高业务价值。
系统内部的核心组件及其职责如下:
- **Scheduler**:周期性(Periodic)或按需(On-Demand)触发新的爬取任务,并负责注入初始种子链接(Seed URLs)。其输入既支持通过 API 添加定时任务,也支持直接启动爬取。
- **Crawler**:无状态(Stateless)的执行节点,接收上游下发的命令后,以异步方式处理耗时沉重的爬取工作(Heavy Lifting)。
- **Parser**:同样异步运行,负责读取、校验(Validate)Raw Data,并将其转换为特定 Schema 后存入 Clean Data Store。
- **Sweeper**:负责监控整体进度(Monitor Progress)。当 Crawler 或 Parser 挂掉时,重新触发对应的抓取或解析任务,同时生成通知与汇总报告(Summary Report)。
- **Notification**:通知当前进度、卡住(Staggered)的处理流程或无法识别的异常 Schema。
系统各组件之间大量采用虚线表示的异步(Asynchronous)调用,遵循 "Send and Forget" 原则。相比于同步(Synchronized)调用需要繁琐的容错与重启机制,异步设计不仅能做到关注点分离,还能极其灵活地根据任务体量弹性伸缩 Worker 数量(例如负载低时用 10 个 Worker,负载高时瞬间扩展到 100 个)。
---
[35:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2100s) SQL 与 NoSQL 的工程选型争议及本质取舍 关于存储层选择 SQL 还是 NoSQL,业内存在大量争论。现实中,技术选型往往带有浓重的团队政治与工程文化色彩——"软件工程是个寡头政治",可能仅仅因为组内两位 Lead Engineer 达成了一致,觉得 Cassandra 满足需求,该方案就会被采用;后续随着公司名气变大,这种架构被广泛宣传模仿,但它绝不代表在所有维度上都是最优解。正如 Google 当初推出 MapReduce 时,虽然学术界批评其为批处理历史的倒退、吞吐优化并不算极致,但因其概念极其简洁实用,最终成为了业界主流。
在数据库选型上,最核心的原则始终是依据用例(Use Case)做权衡(Trade-off)。参与讨论的景祥(Amazon Redshift 研发工程师)强调:对于不强调高一致性(High Consistency)的场景,比如社交网络上用户上传图片即使朋友短暂看不见也无大碍;但对于涉及资金结算的银行交易,漏掉一笔数据就是灾难。世界上不存在万能的数据库,一切都要在功能与性能之间取舍。
```mermaid
flowchart TD
accTitle: Database Selection Trade-off
accDescr: Decision flow for choosing between SQL, KV stores, and Object Storage.
Q1{"Is ACID / Transaction<br/>strictly required?"}
Q1 -- "Yes" --> SQL["Relational DB<br/>(MySQL / PostgreSQL)"]
Q1 -- "No" --> Q2{"Record Size &<br/>Schema Flexibility?"}
Q2 -- "Flexible Schema &<br/>Item <= 400KB" --> KV["Key-Value Store<br/>(DynamoDB / Cassandra)"]
Q2 -- "Large Binary / Web Archive<br/>(HTML, Images, Video)" --> OBJ["Object Storage<br/>(Amazon S3)"]
```
在高级特性取舍上,需要清晰区分以下维度:
- **事务与一致性**:从弱事务(Dirty Reads、Read-after-write)到最强的可串行化(Serializable),单对象(Single-Object)事务与多对象(Multi-Object)事务差异极大。
- **线性一致性(Linearizability)**:与事务(Transactional)是两个完全不同的概念。没有任何主流数据库天然无条件保证完全线性一致,类似 Paxos/Raft 的共识服务也通常针对 Linearizable Write 提供保证,而 Linearizable Read 往往需要额外配置。
- **复杂查询支持**:需要 Rich SQL Query(如复杂聚合 `GROUP BY`)时,关系型数据库较合适;但在分片(Sharded)环境下,跨分片的复杂 SQL 查询极难支持。
---
[40:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2400s) 存储选型细节:容量边界与 Schema 演进成本 在具体的 NoSQL 和对象存储选型中,必须严密关注实现细节与物理限制:
- **DynamoDB**:单条记录(Item,包含各 Attributes)的上限其实仅有 400 KB,而非某些人预想的 1 MB。单行数据较大或存储整块 JSON 时,直接放入 Amazon S3 等对象存储更为稳妥。
- **消息与宽表限制**:单行数据若高达数百兆字节(MB),绝对不能直接扔进 Kafka 发布,也不能存入 DynamoDB 或 Cassandra。
- **Schema 灵活性(Schema Flexibility)**:MySQL 在执行 `ALTER TABLE` 增删列时,传统机制需要重建整张表并全量拷贝旧数据。如果数据量达到万亿级别(Trillion),变更一次可能耗时数天甚至引发停机(Downtime),而 PostgreSQL 在加列方面相对较快。这正是当年 Uber 为何要基于 MySQL 自研无模式系统(Schemeless)——将数据直接打包为 JSON 字符串存储,再由上层业务逻辑(Application Logic)自行 Parse,彻底避开频繁修改 DDL 的痛点。
针对数据库吞吐量,单纯的 QPS(Query Per Second)和 TPS(Transaction Per Second)属于完全不同的量级。在关闭事务锁的情况下,SQLite 单机读取能达到 400 万级,普通 SQL 简单的纯读写也能支撑约 10 万 QPS;但一旦引入保持原子性(Atomicity)与隔离性的两阶段锁(Two-Phase Locking, 2PL)机制(Read block Write, Write block Read)或串行化事务(Serializable Transaction),锁开销会急剧拉低吞吐。
---
[45:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2700s) 分布式 Scale 探讨:分片 MySQL 到底算不算 SQL? 在讨论系统可扩展性(Scalability)时,参会者与讲师展开了关于关系型与非关系型边界的深度思辨:
部分工程师认为,一旦单机关系型数据库无法支撑,需要引入多台机器做 Sharding(水平分片)时,该系统从产品抽象和使用体验上就已经变成了 NoSQL,即使底层存储引擎(Storage Engine)仍在使用 MySQL(正如早期 DynamoDB 使用 Berkeley DB 作为本地存储引擎,后迭代为内部新引擎如 Tiger 那样)。NoSQL 换取超高性能的代价,是将事务完整性、跨节点联合查询等原本由 SQL 内核处理的重任推卸给了应用层开发者。
然而,Scott 与景祥等持强 SQL 立场的工程师则指出:
1. **优先选择 SQL**:关系型数据库历经 50 余年工业界演进,在持久性(Durability)、并发控制及声明式抽象上极其成熟。只要负载未超出极限,优先使用 SQL 可以极大降低开发成本。
2. **SQL 同样支持无限扩展(Infinite Scale)**:在架构设计得当的前提下,SQL 同样能够水平分片。例如 Uber 的 Schemeless 和 Facebook 的社交图谱数据库底层均基于大规模 Sharded MySQL 运行。
3. **概念辨析**:虽然跨分片事务(Cross-shard Transaction)极难实现,但在单分片内(Single-shard)依然可以完美支持强事务保证。SQL(Structured Query Language)从 Michael Stonebraker 创立之时起,核心目的就是将底层并发、持久化等复杂的系统级困难与上层业务应用开发解耦开来。
[50:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3000s) SQL 与 NoSQL 的本质分界及存储选型 在关系型数据库诞生之初,SQL(Structured Query Language)作为一种抽象层,实现了应用开发者(Application Developer)与底层基础架构/计算机科学家(Infra / Computer Scientist)之间的关注点分离。应用开发者只需要写简单的 `SELECT... FROM...` 查询,底层复杂的索引与存储管理则被高度封装。这种极其简洁优美的抽象,彻底革命并取代了早期的层次数据库(Hierarchical Database)和网状导航数据库(Network/Navigation Database),统治了软件行业近半个世纪。直到 2007 至 2010 年前后,NoSQL 概念爆发。彼时工程师们反感 SQL 的一大原因是对象与关系映射(ORM)的割裂感——“你想存一个数据,就像把一辆车拆散,把天窗存在一部分、车门车轴存在另一部分,取的时候再拼起来才能使用”,因此大家渴望一种不需要拆解拼装的存储方式。
然而,在系统设计面试中,“什么是 SQL,什么是 NoSQL”是一个极易产生分歧的话题。严格来说,SQL 是一种查询语言标准,而 NoSQL 则泛指非传统关系型数据库;但如果你用关系型数据库自建分片(Sharded Relational Database),它到底算 SQL 还是 NoSQL?这很难给出一个非黑即白的定义。面对面试官的提问,最稳妥的沟通策略是反问对方:“在你的心目中,你是如何定义 SQL 和 NoSQL 的?”这能避免因双方认知偏差而产生误解。从高维架构视角来看,传统 SQL 的设计起源于单机时代(Single Node),核心追求的是 ACID 事务与强一致性(Consistency & Transaction);而 NoSQL 天生聚焦于跨多节点(Cross Multiple Nodes)的水平扩展与容错。
回到分布式爬虫的存储选型上,不同的数据有着截然不同的生命周期与特性需求:
- **Raw Data(原始抓取数据)**:本质是极度简单的 Key-Value 读写(例如 URL 映射到抓取的 HTML 或 JSON 文本)。如果数据大小可预测且很小(如固定约 20 KB 的游戏回放 Replay JSON),存入 Amazon S3、DynamoDB 甚至 MongoDB 都可以(尽管主讲人调侃自己读研在 GrooveGamer 时把 Replay 存进 MongoDB“踩了挺多坑”);但如果抓取的数据体积大或大小剧烈波动,Amazon S3 等对象存储是更优解。
- **Metadata(元数据)**:涉及抓取进度、状态机变迁、调度控制等,必须要求强持久性(Durability)与事务支持(Transaction),因此这部分元数据必须依赖关系型数据库(SQL)来保证状态正确性。
---
[55:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3300s) 调度与抓取 API 设计及数据 Schema 演进讨论 系统需要定义两个关键的核心 API:`add_schedule_crawl` 和 `start_crawling`。现场有听众提出疑问:如果要爬取某个网站,这两个 API 看起来都可以用,它们在本质上有何区别?第二个 API 里为何也有个调度类型参数?
主讲人解释,这两个 API 的定位完全不同:
1. **`add_schedule_crawl`**:属于定义层,主要向调度表(Schedule Table)插入一条规则配置(例如通过一个 YAML 文件定义每天早上 8 点爬取游戏回放,配置种子 URL、递归抓取深度限制、黑白名单等)。
2. **`start_crawling`**:属于执行层,真正触发抓取动作(开始从种子 URL 不断发现新 URL 并递归向下抓取)。至于参数中传入的触发类型(Type 是 On-demand 还是 Scheduled),类似于 Airflow 的运行机制,用来标识当前正在跑的 Process/Flow 到底是由定时调度器自动驱动的,还是由人工在界面上点击按钮手动触发的。如果遇到黑名单 URL(Blacklisted URL),处理极其简单:在抓取链路中直接忽略丢弃,不往抓取记录表里写入即可。
在表结构设计上:
- **Raw Data Table**:结构非常扁平轻量,包含数据主键 `UID`、原始数据内容 `Data`(HTML 或 JSON)以及创建时间 `CreatedTime`。
- **Clean Data Table**:存储清洗和解析(Parse)后的结构化数据。该表的主键 `ID` 推荐使用 `URL + Iteration` 进行哈希计算(`Hash(URL + Iteration)`)。在哈希空间足够大的情况下碰撞概率基本为零。表中包含根据业务需求抽取的具体字段(例如游戏回放需要 Field 1 至 10,新闻聚合器需要完全不同的字段集合)。
针对能否用机器做“智能字段检测(Smart Fields Detector)”动态调整表结构的问题,主讲人强调工程落地必须保持务实与克制。如果新闻网站突然新增了一个名为 `breaking_news` 的字段,依赖算法自动推断其语义并映射到数据表基本是不可能的。遇到未知字段,通常只有两种处理方案:
- **阻塞式报警(Blocking & Notify)**:暂停解析流程,报警通知工程师手动处理。
- **非阻塞式忽略与通知(Non-blocking, Ignore & Notify)**:按已知规则继续解析入库,遇到不认识的字段直接抛弃(Ignore),同时在后台异步通知工程师“发现了未知的新字段,请确认是否需要调整解析 Schema”。
千万不要做“根据 JSON 动态更改底层数据库 Schema”的完全自动化机制。数据库 Schema 是系统最脆弱、最核心的契约之一,若将其强依赖于不受控的第三方外部网站,一旦对方接口变更或出现异常,整个数据库 Schema 就会被彻底搞乱,导致极为痛苦的人工清理成本。
---
[01:00:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3600s) 状态驱动的元数据模型与无状态 Worker 的容错机制 爬虫系统的调度表(Schedule Table)与任务记录表(Records Table)承担着维护整个系统状态的重任:
```mermaid
flowchart TD
accTitle: 爬虫抓取记录状态机流转
accDescr: 展示 URL 任务从入队、抓取中、解析中,到最终成功处理或进入阻塞状态的生命周期
Q["QUEUED<br/>任务排队中"] --> C["CRAWLED<br/>原始数据抓取完成"]
C --> P["PROCESSED<br/>清洗解析成功并入库"]
C --> S["STUCK<br/>Schema未知或解析异常"]
S --> N["NOTIFIED<br/>已报警通知开发者"]
```
- **Schedule Table**:记录任务定义的宏观状态。包括任务名称、执行频率(如每天早晨 8 点)、YAML 种子配置、开关状态(Active / Inactive / Paused)、当前迭代轮次(Iteration,如某年某月的第 9 次抓取周期)、以及当前迭代下已发现 URL 数(Found)、已抓取数(Crawled)、已处理数(Processed)和异常受阻数(Stuck)。
- **Records Table**:全系统的核心状态机枢纽。主键由 `Hash(URL, Iteration)` 生成,天然具备**幂等性(Idempotence)**。无论从种子注入还是下游 Discovery Service 发现新 URL,只要向该表插入一条记录,若已存在则直接触发 No-Op(无副作用忽略)。记录包含状态字段(`QUEUED` -> `CRAWLED` -> `PROCESSED` 或 `STUCK`)、时间戳以及是否已通知开发者的标记(`Notified`)。
在容错(Fault Tolerance)设计上,核心原则是:**Worker 必须是纯无状态的“重体力搬运工”(Stateless Heavy Lifter),采用“发送即忘”(Send-and-Forget)的思想,将所有状态全部下沉到数据层。**
- **调度阶段挂了**:Scheduler 重新读取 YAML,把种子 URL 作为 `QUEUED` 状态插入 Records 表中;若此前已经插过,则静默跳过,无副作用。全部成功后,往 Progress 表写入一条新轮次的初始进度行(Found 等于种子数,其他指标皆为 0)。
- **抓取与解析阶段挂了**:Crawler 和 Parser 都是无状态节点,处理失败、挂掉或者网络抖动无需自己痛苦地实现自愈。它们挂了自会有独立的保洁/巡检进程(Sweeper)去扫描并重新触发。从 Crawler 视角看,拿到 URL 后去查数据表,如果已抓取过就做 No-Op;没抓取过就爬取并写入 Raw Data Table,然后把 Records 状态推进到 `CRAWLED`。只有这几个动作全部完成,状态才会翻转。
---
[01:05:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3900s) 分布式调度挑战与高并发消息处理冲突化解 现场针对系统的并发与一致性展开了深度讨论。听众 Paul 提出关键疑问:**Scheduler 是一台机器还是多台机器?多台 Crawler 机器如何防止拉取到相同的 URL 产生冲突?**
主讲人回顾了在 Salesforce 工作时的经历:当时团队专门耗费一整年时间去开发一个真正的分布式调度器(Distributed Scheduler)。分布式调度极其复杂,涉及到分布式共识机制(Consensus),以及物理时钟可能向前/向后跳跃(Clock Jump Back in Time)导致的单调递增时间抽象(Monotonic Increasing Guarantee)失效问题。好在工业界已有成熟的基础设施,比如开源的 Apache Airflow,或者 Uber 内部由 Airflow 演化而来的 Piper,它们本质上就是一个多节点高可用的分布式调度引擎,能够严格保证在特定的时间点触发 Flow。在系统设计面试中,直接声明复用类似 Airflow 的抽象是明智之举。
针对听众担心的“多台机器同时爬取同一 URL 导致冲突”的问题,主讲人通过两个层面进行了澄清:
1. **上游消息队列的竞争消费机制**:任务分发并不是让 Crawler 直接用 SQL `SELECT * FROM table WHERE...` 扫库(那样确实极易发生多线程读到同一条数据的情况),而是使用 Message Queue。主讲人打了一个生动形象的比方:“这就像去星巴克点咖啡,收银台出了一张小票(Receipt)丢进 Queue 里,吧台后面站着三个做咖啡的咖啡师。一个咖啡师接下了你的小票,另一个咖啡师接下了 Paul 的小票,第三个咖啡师接下了另一位顾客的小票——每个 Worker 主动 Claim 一条消息,互不干扰。”
2. **底层存储的分片自愈与幂等性保障**:即便极端情况下调度器或队列重复投递了同一个 URL,也不会破坏数据。因为 Crawler 本身没有状态,全部状态都在数据层;系统通过 `URL + Iteration` 将同一任务一致性哈希分片(Shard)到同一台数据库节点上。如果两台 Crawler 前后抓取了同一个 URL,先到的把数据写入 Shard 1 并更新状态;后到的在写入时会检测到底层已经变更为处理完成,直接覆盖写(Overwrite)或者静默丢弃(Abort/No-Op)即可。这种纯粹的“幂等操作(Idempotent Action)”彻底消除了竞态条件下的副作用。
```mermaid
flowchart LR
accTitle: 爬虫并发消费与底层存储幂等处理流程
accDescr: 展示消息队列分发任务给无状态 Worker,最终通过一致性哈希在同一存储分片上化解并发冲突的机制
subgraph Sched["调度与队列层"]
MQ["Message Queue<br/>Worker 竞争消费 Receipt"]
end
subgraph Workers["无状态 Worker 矩阵"]
C1["Crawler A"]
C2["Crawler B"]
end
subgraph DB["数据存储层 (一致性哈希分片)"]
Shard["Shard: 同一 Hash(URL, Iter) 归属同机<br/>检测冲突 / 覆盖写 / 幂等 No-Op"]
end
MQ -->|"Claim Msg 1"| C1
MQ -->|"重复投递 Msg 1"| C2
C1 -->|"先写入"| Shard
C2 -->|"后写入 (覆盖或直接 Abort)"| Shard
```
---
[01:10:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4200s) 解析流水线幂等设计、巡检器(Sweeper)及无限扩展性 解析器(Parser)与 Crawler 遵循完全对称的幂等架构设计:
- 读取待处理的 Record,如果查表发现其状态已经是 `PROCESSED` 或 `STUCK`,说明已处于终态,直接跳过(No-Op)。
- 进行数据 Schema 校验,如果不合法(Invalid),将记录标记为 `STUCK`;如果校验合法(Valid),则将提取出的结构化数据写入 Clean Data Table,并将记录状态更新为 `PROCESSED`。
- 如果 Parser 在解析或写入 Clean Data 期间崩溃,该记录的状态尚未变为终态,整个流程在重启后可以无条件重新执行一遍。只要没被标记为 `PROCESSED`,重试覆盖写入依然是安全的。
为了实现系统级的闭环自治,必须引入后台**巡检服务(Sweeper)**。Sweeper 定期巡检整个数据库:
- **处理 `STUCK` 状态任务**:发现遇到无法识别的未知 Schema 而卡住的记录,由于表中设置了 `Notified` 状态位,Sweeper 会发送报警邮件/消息给开发者,并将状态翻转为已通知,防止重复打扰。
- **处理长耗时 `QUEUED` 任务(丢消息与 Crash 兜底)**:如果一条记录卡在 `QUEUED` 状态超时(例如超过 15 分钟),背后可能有两种故障原因:一是投递到消息队列的消息中途丢失;二是原本认领该任务的 Worker 突发崩溃。由于 Worker 完全无状态,Sweeper 根本不需要区分究竟是哪一步崩了,只需重新签发一条处理命令(Re-issue Command)丢回队列。下游 Worker 哪怕发生重复处理,也完全由幂等性吞吐消化。
- **进度聚合与汇总通报**:读取当前迭代周期的全局进度指标(Found、Crawled、Processed、Stuck 数量),形成清晰的 Summary Notification 推送给业务方。跨分片存储的数据也可以在此由后台定时汇总。
主讲人总结,这种将状态集中下沉到分片数据层、Worker 采用无状态与 Send-and-Forget 驱动、配合后台 Sweeper 扫雷补漏的设计架构,拥有**无限可扩展性(Infinite Scalable)**。无论是网络 I/O 密集的抓取(Crawler)还是 CPU 算力密集的解析(Parser),任何耗费重资源的计算环节,都可以通过简单地对数据库加分片(Shard)和给 Worker 加机器来进行无限横向扩展。
[01:15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4500s) 系统水平扩展能力与调优实战 在分布式爬虫架构中,无论针对什么样的数据类型,只要对状态进行分片(Sharding),系统就具备无限横向扩展(Infinite Scale)的潜力。比如调度表(Schedule)可以分片,抓取进度状态(Progress)同样可以分片,因此可扩展性完全不成问题。尽管像 Google 早期主要通过自己发明的 MapReduce 框架来处理海量网页抓取和离线数据计算,但针对当前这个系统设计,即使不走批处理计算框架,通过消息队列与水平分片的方式也能轻松支持海量爬取。
在实际面试场景中,如果面试官提出一个具体指标,例如“要求在一天之内把整个 Amazon 网站完整爬取一遍”,最好的应对策略是通过实验与性能分析迭代调优。在方案初期,可以先设定一个 baseline 实验环境,例如部署 100 个 Crawler 节点和 10 个 Parser 节点,随后观察抓取的吞吐量、资源耗尽情况以及性能瓶颈所在。通过监控指标分析链路究竟卡在何处:究竟是 Crawler 数量不足、Parser 解析能力跟不上,还是耗费了两天才跑完任务。若初次测试用了两天,下一次迭代就可以成倍增加计算资源(Double Resource),根据不同的负载表现持续微调,直到将 Crawler 和 Parser 的配比优化到一个平衡状态(Balanced State),通常只需几次测试就能精确满足面试官给出的 SLA。
```mermaid
flowchart TD
accTitle: Crawler Capacity Tuning Cycle
accDescr: 爬虫系统资源调优与容量规划流程图
subgraph S1["实验与基线建立"]
A["实验基线配置<br/>(100 Crawler + 10 Parser)"] --> B["全量爬取执行<br/>(如 Amazon 站点)"]
end
subgraph S2["瓶颈分析与定位"]
B --> C{"指标与耗时分析"}
C -- "耗时超期 (如花费 2 天)" --> D["定位瓶颈环节<br/>(Crawler 卡住 or Parser 不足)"]
end
subgraph S3["弹性扩容与平衡"]
D --> E["资源翻倍 / 针对性调整资源配比"]
E --> F["达到平衡状态 (Balanced State)<br/>1天内完成爬取目标"]
end
```
[01:16:30](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4590s) 分布式爬虫设计的四大核心思想 回顾整个分布式爬虫的架构,虽然技术栈与实现细节较多,但其底层贯穿着四个非常经典的核心架构思想:
1. **幂等性(Idempotent)**:系统在设计上必须具备故障自愈能力,做到“不怕失败,失败后重试即可”。无论 Crawler 在抓取网页时挂掉,还是后续流程出错,只需重复执行对应操作,系统状态都不会发生脏写或不一致。
2. **基于消息队列的异步解耦(Asynchronous Processing via Message Queue)**:引入消息队列主要达成两个目的。其一是**关注点分离(Separation of Concerns)**,将抓取(Crawl)与解析(Parse)解耦;如果 Parser 环节处理失败,系统无需重新抓取网页,直接从 Parser 失败的那一步重新入队继续解析即可。其二是**弹性扩缩容(Elastic Scalability)**,Crawler 和 Parser 可以根据各自的实际处理负载独立增减机器,Crawler 性能不足就扩 Crawler,Parser 解析变慢就单独扩 Parser。
3. **计算与状态分离(Stateless Compute & Stateful Storage)**:执行计算任务的 Crawler 与 Parser 节点均为无状态(Stateless),所有的爬取进度、任务状态与最终数据完全保存在数据库层(Stateful Database)。只要计算节点无状态,宕机转移与弹性调度便极易处理。
4. **容错机制与死信补偿(Fault Tolerance)**:系统需要配备后台巡检组件(Sweeper),不间断轮询并检查系统是否卡在某一步骤。如果发现某一步骤超时未完成,Sweeper 会针对卡住的任务重新派发指令(Re-issue Command),无论是重新发起抓取还是重新触发解析,都能迅速让卡顿任务恢复流转。
- **核心原则一**:Idempotent(幂等重试无副作用,不怕失败)
- **核心原则二**:Asynchronous Processing(通过 MQ 解耦抓取与解析,支持针对不同 Load 弹性伸缩)
- **核心原则三**:Stateless Machine(计算节点无状态,状态全交由 Database 保障)
- **核心原则四**:Fault Tolerance(Sweeper 后台巡检任务进度,自动 Re-issue 失败卡顿的 Command)
[01:17:48](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4668s) Q&A:数据清洗表设计与多业务用例落地 在问答环节,针对现场听众提出的疑问,讲师针对数据清洗表的结构定义与系统多业务兼容性进行了详细解答:
针对“清洗数据表(Clean Data Table)如何分辨不同的字段类型”的问题,讲师指出该表字段完全是由用户根据实际业务需求自定义的(User Defined)。例如抓取新闻网站,需要提取的字段包括新闻标题(Headline)、图片链接(Image)以及正文文本(Body Text),这些都是特定用例下的自定义 Schema。无论是业务属性还是底层的字段存储类型(Data Format),都是根据具体的数据格式与用例(Use Case)来定义的,并没有放之四海而皆准的死板规范。
```mermaid
flowchart LR
accTitle: Multi-Tenant Crawler Pipeline
accDescr: 通用分布式爬虫在不同业务场景下的流转与落表流程
subgraph Ingestion["通用抓取与解析"]
C["通用 Crawler<br/>(基于任务名称区分)"] --> P["Parser"]
end
subgraph NewsUseCase["新闻推荐场景 (如今日头条)"]
P --> N1["解析新闻网站 (CNN 等)"]
N1 --> N2["提取 Headline / Image"]
N2 --> N3[("新闻 Clean Data 表")]
end
subgraph GameUseCase["游戏推荐引擎场景"]
P --> G1["抓取游戏 Replay (JSON)"]
G1 --> G2["提取游戏对局特征"]
G2 --> G3[("游戏 Replay 表")]
end
```
针对“两个表如何知道各自存什么”的疑问,讲师说明,Crawler 在启动任务时会根据任务的名称(Name)明确其业务归属。以自己曾实际落地的两个项目为例:
- **今日头条类的新闻资讯场景**:需要爬取 CNN 等各大主流新闻网站,解析 HTML 提取标题、正文及配图,清洗后写入新闻资讯表,支撑后续的新闻资讯流构建。
- **机器学习驱动的游戏推荐引擎(Machine Learning Powered Game Recommendation Engine)**:需要爬取游戏对局回放数据(Replay JSON),获取对战记录并落库到专用的对局数据表中。
尽管两者的目标数据格式截然不同,但所使用的 Crawler 架构是非常通用(Generic)的。在抓取任务发起之初,系统就已通过任务元数据清楚得知其数据类型与目标落表,因此可以灵活支撑各种截然不同的业务场景。最后,讲师提醒大家可以查阅共享文件夹中的系统设计文档,若后续有新的疑问可以在文档的 Q&A 栏目中继续交流,并向大家致谢结束了本次分享。
## Notable quotes
> [00:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=0s) 比如我先给自己限定一分钟,如果这一步暂时推导不出来,我们先往后走去选型 Database,把核心框架搭建完成,最后有剩余时间再回过头来攻克这个卡点。 — Scott Shi
> [04:40](https://www.youtube.com/watch?v=awPOdsnPL-8&t=280s) 摸着石头过河(Practical practice)——先上线 10 台机器打个样,观察抓取一轮所需的时间和资源开销,再动态调整集群规模。 — Scott Shi
> [15:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=900s) Steam 官方服务器没有任何义务支撑你每秒上百万次的暴击抓取 — Scott Shi
> [35:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2100s) 软件工程是个寡头政治 — Scott Shi
> [35:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=2100s) 对于不强调高一致性(High Consistency)的场景,比如社交网络上用户上传图片即使朋友短暂看不见也无大碍;但对于涉及资金结算的银行交易,漏掉一笔数据就是灾难。世界上不存在万能的数据库,一切都要在功能与性能之间取舍。 — 景祥
> [50:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3000s) 你想存一个数据,就像把一辆车拆散,把天窗存在一部分、车门车轴存在另一部分,取的时候再拼起来才能使用 — Scott Shi
> [50:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3000s) 在你的心目中,你是如何定义 SQL 和 NoSQL 的? — Scott Shi
> [55:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3300s) 千万不要做‘根据 JSON 动态更改底层数据库 Schema’的完全自动化机制。数据库 Schema 是系统最脆弱、最核心的契约之一,若将其强依赖于不受控的第三方外部网站,一旦对方接口变更或出现异常,整个数据库 Schema 就会被彻底搞乱,导致极为痛苦的人工清理成本。 — Scott Shi
> [01:05:00](https://www.youtube.com/watch?v=awPOdsnPL-8&t=3900s) 这就像去星巴克点咖啡,收银台出了一张小票(Receipt)丢进 Queue 里,吧台后面站着三个做咖啡的咖啡师。一个咖啡师接下了你的小票,另一个咖啡师接下了 Paul 的小票,第三个咖啡师接下了另一位顾客的小票——每个 Worker 主动 Claim 一条消息,互不干扰。 — Scott Shi
> [01:16:30](https://www.youtube.com/watch?v=awPOdsnPL-8&t=4590s) 不怕失败,失败后重试即可 — Scott Shi
## Mentioned
Scott Shi, 景祥, Paul, Martin Kleppmann, Michael Stonebraker, Amazon, eBay, Steam, Uber, Dropbox, Netflix, Salesforce, Google, Facebook, 字节跳动, 今日头条, CNN, 华尔街日报 (Wall Street Journal), 华盛顿邮报 (Washington Post), Dota, 《Designing Data-Intensive Applications》 (DDIA), 《Grokking the System Design Interview》, High Scalability, MySQL, Cassandra, PostgreSQL, DynamoDB, MongoDB, SQLite, Amazon S3, Amazon Redshift, Kafka, Apache Airflow, Piper, Solr, MapReduce, Berkeley DB, Schemeless, C#
## Video description
In this session, Scott proposed a distributed crawler for several use cases.
doc: https://docs.google.com/document/d/1ClrxwAM6RV7JkU3VcRd_Rc3F8LfHb-8kqujGr7HncIs/edit?usp=sharing
[Source](https://www.youtube.com/watch?v=awPOdsnPL-8)
Original transcript [0:00] 好,你需要一个,今天就设计Crawler,比如说Crawler Amazon,你可能需要一个相对来说UIL Discovery,你需要可能Priorically去Crawl,比如说DailyCrawl这种,然后你需要可能是有一个比较Smart的一个Schema Detection,因为你Crawl的话你的Data主动权不在你,Data主动权在第三方软件,比如说Amazon,或者是第三方的Service,Ebay这种,所以说你需要你需要去detect它最新的schema然后你最后的话你这个cloud出来以后你是个real data所以你还是需要一个相对来说把这个real data massage一下变成一个formalized data或者是把data进行处理然后你可以使用好然后这个system design就是后面大家相互之间就是malk interview的时候也要分成就是大概心里面有一个时间规划比如说,Usenace,Use case,CommonSense,Business sense花10分钟Construents可能花个几分钟,Architecture花10分钟,API花10分钟这个时间相对来说可以这么规划但是每一个问题又不一样比如说我们在考论的时候,你就看到Crawler这个use case,API的时间是可以很短就没有什么API,没有什么API可以说的一般来说难点在architecture,并且你如果面system design的话architecture相对来说设计的没有什么问题的话,一般过的概率比较大,所以architecture是比较重要的一部分然后前面这个的话是非常考验,你有没有经验呢因为你这个business sense我觉得从Engineer发展的角度来讲的话这个Constraint和Business Sense可能是能提升越来越多的一个地方这个Experience Engineer和New Programmer很大一个区别就是他们有没有Focus on Business Sense有没有花很多时间去Focus on Business Sense所以这个时间这个要花足够的时间然后最后的话如果你在其中一个Step卡住了你不要急你就说我们把分成这几个step如果我这个timebox比如说我这个API designtimebox成10分钟你把最重要的那部分design完你如果卡住的话你就说我们先去这个database先选完database先设计完如果有时间的话再回去这个API design就跟那个做考试题一样某道题卡住的话你可以说我们timebox我尝试timebox一分钟来解决如果我解决不了的话,我就稍后再回去这个问题这也是非常mature的一种解决方式如果没有时间去解决的话,其实没有人看的,他主要看的是一种signal好,然后我看到有人问说在Glocking System Design的时候有一个同样相似的两个问题,一个是用MySQL解决的,另一个用Cassandra解决的,他就很confused这个其实没有什么confused的我觉得这个是正常因为我们之前讨论过就是not working有两种情况not working一种是fundamentally not working那么肯定是有问题的另一种not working就是它是个working solution但可能不是optimal的那如果是这样的话你的design其实是work的它可能不是optimal而已所以它的use case可能用Cassandra能解决也可能用MySQL能解决只要它那个不影响它的business use case的话其实是没有conflict的但是我只是说Glocking System Design Interview是非常糟糕的一个System Design Interview 非常混乱它有点和那个Coding Website写的下来我一下忘了那个Coding Website叫什么了反正有个Coding Website然后上面Code写的也非常糟糕非常混乱但是Search还挺高的,你比如说你要search一个什么optimisticlocking,然后很有可能它前几名的,但是它上面写的比较混乱,所以你一定要有一种,你需要花点时间去accumulate这种common sense,然后才能意识到它这个内容是不是混乱,如果混乱的话,它其实不是一个好的reference,然后其次的话,好的内容永远都是非常简洁,非常concised的,如果它这个非常混乱的话,就不是一个好内容。好,我们再回到这个system design,就是crawler的system design中crawler的话,这个里面呢,够主要的几个就是非常high availablehigh available和scalable,其实没有必要high available,它需要high scalable因为你这个crawler,比如说你要crawleramazon,amazon可能有一个million的,more than one million url,对吧?你需要crawler的东西非常多,并且你需要在那个scale上面非常flexible你需要在这个skill上非常flexible,它要多的话,你可能多布置点worker去crowd,它要少的话,你可能少布置点worker,你要非常scalable并且这个你稍后问的时候,问这个conscious,你会发现很多时候你是没办法直接估算出来你要crowd的数量级的所以你必须比如说你就看着来是吧,看就是
[5:00] unpractical practice 是吧,根据实验结果来,你先craw,先部署10个机器craw一下,看craw一遍需要多久,然后你可以再增加机器所以这个Crawler一般来说go都是high-stayable然后其次的话这个Crawler还常见的这个requirement就需要Fault Tolerant就是你必须是,比如说它这个Crawler,任何一个机器可能在任何一个地方挂掉。你比如说你可能是Crawl一部分挂掉了你比如说你可能是start的那部分挂掉了你比如说你在schedule的那部分就一上来就挂掉了嗯都是有可能的,所以你必须Fault Tolerant,你可能从任何一个地方无缝连接的重启。那你Fault Tolerant的话,你非常有可能有个Repeated Action,如果你有Repeated Action的话,你必须保证Action是Edempotent。就是你重复做没有那个side effect就是常见的这个crawler go最后一点的话,这个schema需要比较flexible一点flexible的话就是,比如说你crawler的时候,比如说Amazon在那个Amazon的page里面突然增加了一个新的field 是吧 叫这个Compared price,你必须说他加了一个新的field,我能不能detect出来我detect出来的话,我认识不认识,它是不是我认识一个fields,如果它不是我认识的fields的话,我是不是应该让developer或者是新的人知道我计划有一个fields我不认识,我们是不是应该对它进行特殊处理?是不是应该notify the user?所以这个schema相对来说比较flexible?有人叫smart schema,其实没有很多smart的因素在里面,不可能用machine learning来看它这个fields是干嘛的然后有人问说Glocking不是一个System Design的好的resource,你有什么好的resource可以推荐一下吗?我觉得最好的是读paper。其次的话还是我们说的Data Intensive Application,就是15年写的,17年public了是一本书,就是Cambridge的一个PhD写的Martin Kleppman,我觉得是我读的System Design里面最好的一本书,我从里面学到很多东西,你可以把那个做成一个目录数据来读,然后你读完以后他有很多paper也有recommend。可以读很多news这个developer还是挺喜欢写block的,比如说他们做了一个事儿,Uber的早些时候的engineering block也比较好,最近的话有点水,但是早些时候有些engineering block还是比较好的。然后Jobbox有一些,Netflix有一些,然后我记得还有一个常见的,有个叫Hi-Available的一个website,有时候有些比较好的文章就是都是可以读一下,我觉得这个System Design需要日积月累的积累,需要读很多东西。需要很多阅读,需要很多思考,需要很多独立的思考。所以这个跟阅读量是极其相关的,和那个就经验积累也是极其相关的。好,所以这个里面,Troller里面有两个东西是不在讨论范围内的,一个是这个authentication,有的website是你可以就是直接去访问的,但是有的website是要求你login的,你需要有account的。还有一个不在讨论中就是throttle,Troller里面最大的construents,最大的限制因素是什么呢?是server side的throttle,就是他那个,比如说Amazon那块,根据你的IP限制你,或者根据你的那个根据你的那个user account的限制你,这是最大的throto的地方。因为你想你作为一个Amazon的website,你的user访问可能还是比较normal的,比较thin的user的话,可能比如说,一分钟访问,10个page撑死了。如果你一分钟内访问超过10个page,他可能就要throto你了,就reitlimiting你了。如果你一小时内访问了2000个配置,那肯定不是normal user,那肯定就要throughton你。所以这个大部分时间做crawler大部分时间你都是被限制在什么被limit在什么是limit在你要crawl的网站上然后这个它是一个不同的但是你有很多方式来就是guess through it我们也到时候讲讲一点这个你guess through的方式我以前做过两个Crawler project,我想跟你简单聊一下几个Crawler project,就是作为这个research的一个index,context,就是这个Crawler project的context这是我在研究生 读研究生的时候做的一个Crowler Context就是Project,它有点像Personalized News Aggregator我和几个研究生同学一起做的,然后这个呢是在13年做的,所以他这个其实那会儿没有那个今日头条,其实他很像头条,他其实说白了是一个头条,今日头条啊,你从比如说你从CNN Cloud Data你crawled了以后把data通过Solar,Solar是做text processing的Solar处理一下,比如说你把这个Craw Data URL 读取出来,Title 读取出来,Abstract 读取出来,然后Source Website 读取出来,然后有没有 duplication,计算一下有没有 duplication,然后呢你把它Generate成一个Summary,你这个
[10:00] 文章可能好几千,你把Gemery成Summary,然后你把有Duplicate的地方Duplicate掉。比如说CNN报道一个东西,什么Wallstreet Journal也可能报道,然后Washington News也可能报道,Washington Post也可能报道,多个地方报道,你把它duplicate打包起来。然后呢,你最后的话,生成一个Summary,还有专门做这个,就是Text Processing,生成Summary,然后呢,你把这个Data呢,push 到这个website 里面,比如说你可以处理,postpostsending 处理一下,你可以有原始 data,然后你可以通过Personalize 作为一个collaborative filtering recommendation,就是说把这个personalize news recommendation然后你可以允许user login,记录下history,记录下click,记录下email,记录下rating,然后send personalize recommendation,然后email user。这个其实就是头条,但那会儿我还不知道头条,因为我这个做的时候13年做的,所以那会儿,我觉得头条是12年8月份成立的,所以那会儿没有头条。所以这个还是挺有趣的一个地方project。是个研究生project,然后在crawler这块我是用了一个HTML的closure,所以它closure data以后呢它 crawled 以后呢,它是 extract html 的 page然后通过Xpath这个Configuration读取一个Highlight Image然后再读取一个 summary当时是Crawled大概50天,大概Crawled两个G的news,然后大概12000条news,反正还Crawled一些的。就是做这个,这是一个用适的用适的用适。比如说,你去诚纯的CrawlHTML。这是一个Personalized的头条。然后另一个也是研究生时候做的,这个项目也是一个Crawler部分,也是比较有趣的。我那会儿比较喜欢玩Compute Game Delta,我现在也玩Dota。Dota有点像类似于LOL,是一个Multiplayer Game。大概一天有一个million的match,然后它有个API,它有个API公布一大堆JSON,这个JSON就是有一些format,比如说A选了哪些,这个replay里面这个A选了哪些英雄?就是天辉选了哪些英雄,夜言选了哪些英雄?那你这个英雄最后结果是什么样子的?比如说你这个英雄选了ABCD,结果比较好,对面英雄是CDEF,结果比较好。我当时我们做的这个项目就是crawled就是把这个replay,一天1.5个million的replay,craw下来,然后用machine learning做一个Machine Learning 来判断出生存一个offline model,用machine learning 生存一个offline model,我们知道哪些,因为它是 role play,哪些英雄选起来,它胜率比较高。然后呢,Fontaine的地面是做一个微软的一个 C-shop 的一个 program,然后当你就是你按一个截屏键,你按一个special button,就比如截屏键,我就自动的把这个屏结下来,就是告诉你说,我就把这个屏结下来以后,比如说我在玩游戏,我看能不能visualize一下好Data true here on selection.谢谢大家就是这样子的,你可以看到,这是所有能选的英雄池,然后你选择一个英雄是吧?然后我做这个后端是用Machine Learning,把过去的Data做一个Machine Learning Model,前端的就是比如说你按一个截屏键你按一个截屏键以后呢,我就extract它,用非常快的这个phash,使这个phash是可以让它那个非常快的处理下来。然后判断出你选了哪些英雄,然后算了一个就是recommendation,然后让这个recommend一些可能胜率最高的英雄,根据当前情况的胜率最高的英雄,然后再返回回来。所以我们当时的Crawl的Data是我记得是Crawler50万个replay啊,Crawler50万个replay。从7300个玩家里面克用了50万个replay,然后进行这个处理,所以这块也是写了一个crawler,这两个crawler,我今天主要是拿第二个crawler,就是crawlreplay这个crawler,因为它是一个API,它是给你一个非常formalized的一个JSON是一个formalized JSON,它那个schema是比较flexible的,但是它返回了
[15:00] 这个JSON,所以没有很多那种garbage data,所以这个processing比较容易一点。然后,我们今天主要拿这个作为例子。Crawler可能会给你一些constraint,这个完全是根据它怎么样defin的。但比如说我们拿这个Groogamer gameplay作为例子的话,每一个replane大概是20k左右。然后这个一天的replay是1.7个million,当时是1.7个million。然后可以用在这个呢是可以通过就是你estimate的total data size的并且它这个growth也比较好estimate,所以你可以通过一些计算来计算一下你大概就是一天要多少 data,我们在这儿通过计算的话简单计算可以看出这一天是大连3.4gb的data是吧,这是你需要crow的data,那么这个里面你crow就知道这个随便做一个project不管是这个news crawler啊还是这个game replay crawler啊,你就会发现它真正的SOTA是什么它真的搜到是Server-side,你Steam Server,它没有义务去让你一秒钟吃到10的6次方的一个数据它没有义务让你crowd,所以它就限制你是一分钟能crowd,就是你这个count一分钟只能访问一次或者是10秒钟能访问一次那么其实你的这个Botal NAC在这里,你可以用多种方式来做,你可以尝试就是detect它这个这个就是它的搜索在哪儿比如说这个Steam这块它搜索在Different Accounts所以我们就created了20多个accounts是吧 分开crawl然后每个thread有它自己的account 分开crawl然后你可以有的话有的搜索在Different IP 那你可能需要多个nodes的IP去crawl它所以一定要看出它那个crawler这个它那个就是bottleneck在哪里 但一般crawler的bottleneck是在system,如果它system没有,就是它没有bottlediq的话这个网站很有问题啊,你很有可能ddls它了所以它这个最重要的Color里面最重要的Data,最重要的那个Soto,就是Botline是在它这个Server side然后最重要的东西是你Cloud data,因为这个是非常珍贵的,它贵主要贵在时间,因为你老是被throttle,所以贵在时间然后其次的话,很多时候你做crawler的时候,比如说你是为企业端做crawler的,你一般企业端是有agreement的,就是你实际上是跟你要crawler的地方商量好的crawler的。所以你需要做一个good citizen 是吧,不能太多crowd那么你同一个node的话可以crowd多个sites,这样的话你也不会被throttled太多这个limitation也没有什么好讨论的,因为这个如果你想的话这个里面就是一个是QPS,一个是datasize嘛,这个QPS的话,因为你是个internal server你的QPS是完全可控的,你可以直接就 define 就行了,只要能满足你的业务需求就行了。比如说你要求一天内 crowd Amazon 一次,你只要你设置你的 rate 一天内 crowd 完一次就可以了。但是你这个datasize,你其实如果他告你当然好,他如果不告你的话,你就怎么样Practical,你先来10个server,先craw一遍,看花了多久,然后看一下这个datasize如何,然后第二次的话就知道怎么configure,怎么样optimize了然后有人问说怎么知道第三方sortal规则,不知道,你如果没有这个business往来的话,你不知道的,他不给你开这种额外的restriction的话,不给你开额外的这种优惠的话,你不知道的,你就不停的去实验,就比如说我先一秒钟Craw 10个试试,对Throtter的话,那我就一秒钟Craw 5个试试,反正就是去不停的实验,就不停的去configure,optimize.Set-on-Manual其实非常time consuming,这个比较time consuming,做过一个Crawler就知道还是比较time consuming的。我们接着拿GameReplay作为Crawler作为LISA所以GamerPay这个Color作为例子的话,不管什么Color,你一般的algorithm都是固定的,首先你是找一个whitelist of domains,就是这个domain你要去做crawl然后呢,这个domain的话你需要有一些种子UILs,就是一开始需要crawl的UIL,然后你根据这个UIL呢先放到这个没有cloud的一个池子里面然后呢,你asynchronize the crowd URL,把这个data crowd下来以后,然后你把这个Post content,比如说你Post成这个什么样的format data,然后把data存起来把这个contents index起来,然后index起来以后这个如果是HTML的话,它经常会有新的UIL,那么你新的UIL的话,你就把新的UIL放到没有被Crow的地方。然后这块其实有个Q,不管是DFS,DFS都可以,无所谓的,就是你怎么样都可以。一般用message queue的话很有可能是个BFS的一个crown,无所谓,反正你最后的结果都是要crown一遍有的这个cloud相对比较简单,比如说那个game replay 的cloud 的话,其实是个JSON,它那个cloud 的时候,它UIL 是固定死的,就是一二三个人,
[20:00] 3456789,这就是一个递增的一个数字你都不需要,那你这种情况下都不需要去discover是吧,就increase你就行了。然后如果他找不见replay的话,说明这个还没有生存,你就等一下是吧,就等一下等它生存了以后再去crow。但是有的UIL需要discovery,discover这个机制,比如说这个Amazon的这个UIL。需要discover机制好我们看这个... -不好意思,Scott,能够问个问题吗? -你问好了。 -能够往上走一点点吗?就这个strategy这里你如果爬了一个URL对吧那你还需要往深度优先节奏向爬吗?还是就爬一个就不管了对这个呢你要看到architecture这块才理解就是我可以在architecture的时候讲一下这块可以吗好的,谢谢。好,这个Architecture就是Cloud的时候,我们看它其实有很多component的。我这种情况下其实是拿了三个例子一个是news website 一个是steam gameplay 这个use case还有一个是Amazon,我拿其中的news website和steam gameplay作为两个例子,在讲不同的cloud use case。当我们cloud的时候,你首先有个scheduler,这个scheduler就是define你多久cloud一次的,因为比如说你是Amazon一个竞争对手,你有可能是daily去cloud Amazon的website,你要repeat it去 daily crowd Amazon website这是你一个不停的repeat的一个过程,你可能daily cry一遍,有可能是on-demand cry一遍,是吧?这是一点,或者是你archive的话,也需要去dele across news website但是archive可能是一个push-oriented model,可能和这个不太一样啊。然后你决定了哪个cry以后,你会生存一个就是种子,你会把那个seed的那个url的放到metadata里面,然后你会通知这个Message Queue Asynchronize 去通知这个Crawler去开始Craw,Craw完了以后呢你第一步这个Crawler呢会把Crawl的Data放这个raw data table里面然后它会insynchronize 高了pulsor去pulse然后Pulsor呢 会读这个RAWDATA然后看这个format有没有变,它这个format是不是你认识的format然后这个format就是你确定这个format的你认识并且没有unload column的话,你可以把它放到clean data里面然后再把这个Mentadata出这个website靠过了然后Sweeper是防止一些walker突然crush了,比如说crawlercrush了,pulsercrush了,它可以restart他还可以做一些其他的 functionality,我们稍后会看到。所以刚才那个同学的问题就是非常简单,嗯怎么样说,怎么时候Crawl BFS DFS怎么做是吧?你比如说你现在CrowD UIL 1,UIL 1 CloudFund Raw Data里面,你Post的时候Post出23456,你把23456呢这个Postor呢把23456呢Insert到MetaData里面,就是23456相当于是这个 queue 里面说 23456需要被 crowd 然后你同时呢你可以 send 这个 message 说 23456需要被 crowd 那 23456相当于就 queue 到这里面了 然后就随后 crowd但是他每次呢他其实只cloud一个处理一个然后他把以后需要cloud的放到这个database里面说以后等着去接着BigCrop所以是这么一个过程,这样回答你的问题吗?那你怎么控制它的深度呢?它说不控制?不控制深度,你这块的话,你如果想控制深度你可以啊,因为你可以在cloud的时候,比如说这个url深度是1然后你下次craw的时候加1,所以你这个start的时候你以外要变成2,如果它超过10的话你就不craw了我一般来说 crawler 是不控制深度的, crawler 不要 duplicate repeatedly crawler, 就可以谢谢大家因为不控制深度,它可能有的时间特别长,对吧?因为那么多很大,有那么多的products对,你可以控制深度,你可以在这个Record里面加一个深度,depth然后你每次call的时候,你的parentcall的时候,你会把children的depth加1,超过某个depth你就不call了,也是可以的就跟 DFS 一样嘛,就是你就是,这其实是个就跟 Algorithm DFS,一个 single node 的一个 DFS 的 Algorithm 一样嘛,就是你读当前的item,然后你读到的depth,然后它的child是depth+1,如果depth+1超过这个超过你的stretch hold 你就不 crawl了这样解决了你的问题吗谢谢,Clean DataDB是一个什么样的DB?对,我们会稍后讲一下Clean DataDB,就是我这块命名比较糙了Raw data和clean data命名比较糙了,因为我也没想到怎么命名,所以我觉得命名比较糙一点。谢谢大家这块有几个问题啊Message Queue for Pausing 是什么?Message Queue for Pausing 的话是
[25:00] 就说白来是把这个trout的步骤和pause的步骤这两个步骤asynchronize的分开啊这样的话你restart的时候你的granularity比较小你可以把Craw和Pause放上同一个步骤但是如果你出问题的话你要重新crowd,那我们之前谈过这个Brotal Neck 是在 Crowd 的过程,如果你 Pulse 的时候你不认识这个 Schema,你重新 Trigger 这个 Crowd 那你其实浪费了最宝贵的资源就是把这个crawler和poser分开然后Visit的UIL又存在哪里?Database,这一定是存在Database的,因为你需要Fault Tolerant,这个里面任何Database可以挂掉。任何nodes可以挂掉 任何nodes和任何databelle都可以挂掉然后陈销问Pause是什么意思?为什么要Pause?我们可以看一下Poster呢,我们举个例子好了,去DataModel的例子你存的时候你的real data就是一个简单的key value,就是你比如说,ID 然后加一个data 就可能是一个json 可能是一个html你clean data 还是什么呢?你clean data 比如说我几块举了两个例子,如果是game replay 的话你需要有个 URL 是吧你有一个Iteration,就是这几个crowd的一个Iteration,然后你可能有一些你自己还有的知道的case,fields,比如说这个甲方选了哪些英雄,乙方选了哪些英雄,如果是newscrowd的话,你就说这个type,newscrowd,那 UIL 是什么highlight是什么,Uber又撒钱了还是怎么回事啊然后这个main text是什么,然后你的第一个image是在哪里存的,是吧?这是这个clean data,那你从这个raw data到Clean Data是有个过程的,你需要用Pulser把它分开可能我命名有点比较糙吧,这pulsar的意思就是读了这个RAWDATA as JSON或HTMLPause的data,我拼错了,拼错了,这个超过感啊,Pause的data,然后把这个dataPause了以后呢,存成你知道的一种schema为什么要把这个crawler和pulsor分开 因为你比如说这里面最重要的我们再说一遍这Botalign是什么Botalign是从crawler到external website的这个这个是葡萄奶你不希望如果你data已经放到RAWDATA里面你不希望重复这个过程对吧,所以说你希望呢,separate concernSeparate Concert,所以你process your content然后把这个content process以后呢然后把这个raw data process以后呢放到clean data里面所以你这个cloud的过程要fill掉你肯定要restart你肯定死了你没有地方拿到data但如果你datacloud回来你pause的过程fill的话你不需要再重新cloud了首先不需要重新crowd,其次呢,你这个比如说你有不recommend这个schema的话,你也不需要去重新去去也是不需要重新scraw,就是separate concern我把这个这个这个这个graph update 一下所以这个poster 其实就是process content 我看这个同学说的话 就process content 然后这块还有一个typo好有点怒啊好 还有什么问题然后我这块再想说一点就是这个Raw Data呢,你这块可以做Deep,嗯 copy也可以做 shallow copy比如说如果是个JSON的话就没有DeepCopy这个概念但是如果说news的话,比如说要做archive,你要做的是那个webarchive这个service的话,你一定要说deep copy什么意思 你不仅要把HTML copy下来你要把所有其他的resource也copy下来比如CSV file也得copy下来image也得copy下来Video也得copy下来最相当于是fully recopy就是full全部copy一遍就是这个Deep Copy 也是Least Code常见的问题如果他要问的话,你要看你的Use Case,它是需要Shadow Copy还是Deep Copy?如果是Deep Copy的话,那你可能这个context 比如说你这个news的text和news的Nyos Tax和News的Image可能要放到不同的database里面,这个RAWData可能要分开,所以你这个还是要根据你的不同的这个用CaseLine,来考虑。还有一个小问题,Architects解答的starting point在哪里?是从sweep开始还是从crawler开始是从这个scheduler开始谢谢大家Schedule的Input是哪里来的呢?Input 我们在下面稍微有些解释Input就是你可以Add一个Schedule,也可以StartCrawling明白了对应该肯定有个API对,可以把它放在DB里面去,或scut去跑,对吧?对对对,这个如果它是repeat crawler的话,你就是需要
[30:00] 这个Add Schedule的Craw就是相当于Add Crowd的一个Schedule然后你也可以说start crawling,就是开始 degenerate,不停地跑,不停地跑。这个在后面有解释我们看这块有个问题Crawler又要从网上下载page要把page上的run data然后Data会不会在用双边的Network Bambus那你需要就是优化这块就是这块说的是优化问题首先我们设计的时候首先想的是我一般解题是这样讲的,包括解定算法题,我首先讲的是我怎么样把问题解决然后我在想我是不是在用了最小的资源解决这个问题的对吧那我们想一下你需要Crow这个Data,你是不是首先得从第三方软件下载下来,那你Crop完这个电子,你是不是得存储起来,因为你要处理是吧?那你这个是minimum request 你不可能不可能再进行额外的优化我觉得唯一能优化的地方是你把这个Crowed Data,就是那个encrypt一下,你encrypt可以节省一些空间,我觉得可能encrypt一下是挺好的。可能是需要encrypt一下,encrypt一下可以节省很多空间。你说是compress吧?对,compress一下,不是encrypt,compress一下,对,compress一下可以节省很多空间。这样的话可以减少很多空间,这个你其实是一种tradeoff是用空间换时间你减少空间但是增加这个 de-compress de-press 的时间但是这块的话可能这个 crowd 的话可能这个最大的 bottleneck 是就是这个 server 的搜索头,然后其次的 bottleneck 可能就是这个这个时间问题了,不是就是这个你需要存的data size问题了,所以这个compress可能还是提供起business value好,这块的话我们看一下它这个componentsScheduler的话,你需要做这块有同学问说这块为什么有实线和虚线这个所有的虚线是asynchronized的说的虚线是Asynchronized所以你看到这个Asynchronized Notification,就是Send and Forget.CrowAsynchronized, Pause,这都是Asynchronized,所以这个实现的话基本算是Synchronized的,如果它Fill的话,它需要想办法来重新的Restart。先不要担心那个FullTolerant的问题,我们先把这个问题解决了,我后面解释了一些怎么样FullTolerant的问题,怎么样就是怎么样Restart这个问题。为什么Crawler到这个RAWDATA不是Asynchronized?就是你CrawlerData以后,如果你Asynchronized存储Data的话,嗯你存了data以后,你还要把这个data record变成cloud,这个的话也可以synchronize,但是我觉得没有必要,你其实bottomack主要在等这个第三方APM。而且我觉得这个没有必要这个Asynchronize和Synchronize的主要原因Asynchronize的主要原因就是比较Flexible你这个number of worker可以控制,比如说你这个现在靠任务量比较小,你number of worker 可能是10个然后你但是你靠任务比较大的话你Number Worker可以增加100个对吧然后这个主要是比较Flexible一点,一般来说这个你能Synchronize解决,没有额外的问题的话是不Asynchronize,因为会增加额外的复杂度。其次的话,另一点Asynchronize的提供的方式那类主要是separate concern。你像这种情况下,这个crawler和pulsor之间分开就是separate concern。crawler fill的话不会triggerpulsor的问题,pulsor fill的话不会triggerrecrawl。就是separated concern,所以这个好,对Meta data我们一会看它schema,所以我们现在不讲这个Meta data是什么,就是稍微花点时间理解这个high level的东西。好,我们来解释一下这个high level的components。Scheduler主要是Purearticle 或者On-Demand Trigger New Crawling 同时就是InsertSeedUls 开始的起始那些UIL 。Troner 是Statelist 。什么Statelist 呢? 别人传给它Command 是SendAndScat 。它是 asynchronous 去做这个 heavy lifting 的,就是 crowd 这个动作是 heavy lifting,然后这个 crowd 可能是 shadow copy 和 deep copy,所以你这个要根据 use case 跟你的面试官 confirm 一下,或者跟你的 product manager confirm 一下。然后这个 powser 就是 asynchronous 也是 asynchronous 因为它做所有的 heavy work 都是 asynchronous 的。它主要是Pause and Validate Raw Data, 然后把Validate的好的Data放到一个Clean Data Store里面Notification就是 Notification 这个Current Progress 和这个哪一个Process Stagger 了 哪个Process卡着了然后比如说或者你有 Unexpected Schema 这种是 Notification 做的然后然后这个sweeper是monitor progress的,然后它可能会retrigger一个craw,比如crawler挂掉的话,它retrigger一个craw,会 retrigger一个 pausing或者它 generated notification或者是 Generated Summary Report这是这个
[35:00] 就是这几个每个component的functionality这块就是database我们讨论一下,我看到群里面好多讨论的说什么时候用sql,什么时候弄sql,我觉得Sequel,NoSQL相对来说这块呢,我觉得讨论相对比较多了这个其实没有很明显的这个就是比如说你说为什么Facebook会用Cassandra,因为Cassandra满足它的Use Case的话,它确实可以用Cassandra,它也可以用它自己Facebook的TOW Database,是吧?完全是可以有skill来的但是从我个人的经验来讲我觉得软件工程是个寡头政治啊你们组里面是你们组是做这个比如说你们组是做crawler 的你们组最后这个Lead Engineer和另外两个Lead Engineer达成一支了,就用Cassandra比较好,当然Cassandra可能没有绝对性,但是他们就觉得Cassandra满足需求,那他们就用Cassandra了。但这个Design可能后面popular,就是World Room这个Design变得popular了,大家都说因为你们这个公司比较fancy一点,World Room这个Design变得popular,它不一定是最好的Design。它不一定是最好的,这个出现过不止一次了,比如说这个MapReduce刚出现的时候就不停的被学术界抨击,说这个其实是对过去的一种倒退啊,过去的这种新的design,就是这个batch processing的一种倒退。但是Google还是很popular,它处理数据量还是很大,然后它就变成了一个业界的主流的一个batch processing的一个东西。它是不是最好的,我觉得它不是最好的,它这个optimized throughput有很多很多的问题,DS-Fool有很多问题,之前其实有一些,量级不是这个MapReduce这个level的,但是Process其实比MapReduce不管是什么 functionality啊处理要稍微好一点但是它是最简单的MapReduce我觉得相对来说是最简单的一个就是Batch Processing Design所以我觉得就是作为一个这个我插一点点话因为我是做数据库的,一般来说选择数据库要看你的那个use case最重要就像Scott刚才说的如果具体到Facebook为什么会用不强调High Consistency的啊?因为你都有一个用户随便uproll一张图表突变你的朋友看不见他又不知道谢谢大家这就是简单一个道理来的 但是对于一笔交易来说 比如说你银行的交易那如果那比教育不见那就是个大问题所以基本上就是以你的use case来决定你的database类型然后就是如果就没有一个在目前为止还没有一个数据库说它是万能的啦,基本上大家都从中间做个取舍对,好景香你接着说,你接着说。然后那个那个,这是第一个,第二个是刚才是关于什么?嗯Gunst Go 你是我在哪里打断你我忘了你刚才有打电话吗我谢谢大家那没事 先这样吧 好像还有一个点我忘了说了 但我放下来讲了 景祥就和研究生同学就跟我一起做Groove Gamer这个 我们当时也是熬了好久也也做Groove Gamer这个他也是Amazon Redshift的一个大神行 我进去了大家如果是找工作的话可以找景象内推Amazon Redshift好 紧张说得很好就是你这个database其实就是还是根据你的use case来然后根据你的这个你的用途和你的需求来,没有就是one size fits all solution。没有这种one size fits all solution,所以文天是根据你的用途和用途来的,你需要做一些取舍。常见的取舍,比如说你需要不要transactional,transactional这块指的就是Database Level Transaction,我会稍后在周六的时候普及一下Transaction的指示,这个Transaction有Strong Transaction和一些比较Week的TransactionRate话比说,Dirty Rates,Rate after write这种有不同的这个,还有Serialize是最强的strong transaction。然后这个Transactional有三个ObjectTransactional和MultiObjectTransactional,还是相对比较复杂的。然后这个在DistributeDatabase还有个LinearAbility,LinearAbility和Transactional不一样。也是比较复杂的,这个Linearability相对更复杂一点,因为现在这个database从database的角度的没有任何一个database是naturally支持Linearability的。像不管是MySQL啊,或者是这个是没有Linear支持Linearability的。Consensus Service是支持部分Linearability,比如说那个中文字幕:CM
[40:00] paper的话还是支持LinearWrite在你的read是需要configure以后才能linearread支持它可以支持Linear Comparer,但是你需要做Configure,是吧?有些Linearability,Difference in Linearability,Transactional这个有很多值得考虑的地方,这个细节还是挺多的。然后就是如果有transaction的话肯定有mySQL或postgres或者SQL然后如果你有reach-seql query的话有reach-seql query需求比如说你需要group buy啊上海要下来说的会儿的group by 这种你有一些这个SQL的需求的话,一般来说用MySQL也可以,但是你要注意这个reach SQL query,当你把这个SQL sharded的话,就是shared 的的话,这个rich-save core有的是不满足的,你要非常小心的处理,这些怎么样shared 的这个data是需要一些额外处理的。然后如果你的这个access pattern 比较固定的话,比如说就是个key value 的access pattern 的话,或者是这个的话就比较适合用Dynamo或者是S3,我今天查了一下Denimal这个max data size其实是400K,比我想象中的小,我以为是1MB但是他实际上说他这个一个item只有stackDenom 是这样子,一个Item其实就是我们Database一个Record,然后一个Item你要做Attributes,Attributes相当于Column所以它一个肉相当于就是400k,所以这个你需要去注意一下。然后如果它比较大的话,你可能就放到一个Amazon S3上,比如说JSON的话就放到Amazon S3上,就放到一个这个里面。然后MS3是个Object Storage,也连严谨上来说不能算完全是个Database,但是现在这个Database如果你把Database定义成Store和C1的Read的话,它也算是Database一种。然后其次的话你可以考虑的比如说Cassandra这种但是一定要注意细节注意details如果你的data一个row是几百个megabytes,那肯定不能用kafka这种,不能用kafka来publish也不能用这个Dynamo啊这种Cassandra来存,你肯定还是要存到其他地方就处理一下存。然后其次的话,什么时候用NoSQL的?当你的这个schema比较flexible的时候,你可能需要用NoSQL。为什么这个用NoSQL?当你这个schema常变的时候,如果做个SQL的话就知道,你需要不停的去altern table,你就要加一个column,减一个column,有的Sycle支持是比较好的,比如说Postgres这个Altr这个table其实是比较快的如果MySQL的话,你加column,其实它是会把所有的table recreate一遍,就是从copy一遍。所以你这个MySQL如果有个trillion的时候的话,当你alter table的时候你其实需要copy这个trillion data你实际上copy一个可能花几天时间甚至可能有down time所以这个就很有问题所以这也是Uber设计Schemeless,用MySQL设计Schemeless里面把那个Data存成的JSON,然后再自己用Application Logic去Pause的一个原因之一,它这个schema确实不是很flexible所以这是也是做SQL/NoSQL trade-off的一种分析Again,很多时候SQL/NoSQL是可以互助的,就是怎么用都可以,只要你满足business use case都可以,你只不过是一种 trade-off然后除了这种high-level的分析的话,你可能还是要结合一些data 就是结合一些比如说你这个lenten 系列多少啊结合这种具体的这个experiment dataexperiment data 然后去分析然后才知道哪一个好一点像景祥,他说景祥说这个就是今天早些时候在群里面说现在这个SQL read和write差不多能支持100k了,这也和我们就是我之前发的那个SQL lite read可以支持4个million,其实是一致的,因为你加了write以后这样一个数量级还是比较正常的嘛,就是差不多100k左右了,100k qps,就简单的read write差不多100k左右还是可以skill的。我那个就是我说是Transaction per second就是就是全塞生Persetgan跟康里Persetgan又是两个概念因为Chance Session为了保持它整个Transaction是Atomic的那个Course要比单纯的Course要高一些要告不上然后如果你是完全不考虑把前任者全部disable到,那个数据会非常快。你说这个transaction是指那种比如说,two phasetwo-phase lock的 transaction 是吧 就是你read block write, write block read的这种 是吧是,这是最简单,还有你刚才说的可序略化啊。Zerolyze on Tense Action反正大家有概念吧 如果你不理解什么是two face lock 或者是这个这个Transaction Read和QPS,QPS如果是这样的话,QPS会比较宽很多,我们会稍后可能过几周会讲一下这个
[45:00] serialize transactionRead Lock, Write Lock, Two-Face Lock, 可能都会覆盖一下但是你可以认为QPS一般来说可能会比Transaction lightweight一点,因为它不需要做Lock但是 transaction 可能会有 read lock 可能 write lock是这样的吗?对,对,好。 Scott,我方便补充一下吗?好。我之前做过几年Infra,没做过DB,但是跟这边接触的比较多一般来说你如果要设计到Sculptability的话会选no-score的可能性是非常大的这边就讲说你的no-score是基于my-score时限的其他还是算是no sequel而不是MySQL所以其实很好分 假设你需要很多台机器才能support的其实已经不是MySQL而是NoSQL包括DynamicDB他是NoSQL但实际上它的所有的Engine其实跟MySQL的那种 storage engine 是有点像的但是它是2-SQL对对,Denemo好像我记得他Paper里面写的是用Berkeley的一个OpenSource的一个Files好安静他后来已经换了一个引擎,Uber的群里面有一个人在DynamDB待过他提到一个名字叫Tiger什么的 我忘记他Full Name了对,然后Redshift最好,对对,而Redshift最好有一个朋友可能跟景祥一个team他叫段昌在西雅图都是做redshift的我觉得他们对这一块他是挺熟的我认识断潮,他是做比较偏monitor的东西来的对对我看你做了那么有经验你们应该认识不好意思打电了就那个受一点补充吧,就那个那个主要我想就是让大家有一个概念就是就是做数据库的人呢,特别是SQL Database的人呢,其实花了很多时间精力去改善那个整个效能还有那个,就是那个Karennis上面的问题来的所以除非我们真的遇到非常大规模的那个需要处理非常非常大规模的Sloop,不然的话我都会优先考虑用一下SQL Database因为它这样可以大幅减低你的开发成本因为你想想看,如果...因为 NonSQL 它带来那个高性能的代价就是它把很多 SQL Database已经自己处理的事情交给了用户谢谢大家我这块看法和景象是高度一致的,我是非常in favor with Seql 的,因为这个 Seql 它从70年开始提出,经过了50年的发展,那 Seql 它处理了很多比如说Durability,Transaction,它提供了一个非常concept的一个extraction就是你把data给它,它能guarantee data consistent,然后能guarantee durability,所以我这块看法和景象是高度一致的并且SQL的scalability不是一个问题,理论上SQL是支持infinite skill的,只要你事儿的好。比如说Uber最大的数据量使用Skymanis is very sharded to sickle然后Facebook的话,他说的relationship放Facebook的档也是sharded SQL,所以这个这个地方不好意思这个地方可能有个小小的convusion一旦你有买SQL DB然后一旦有sharding然后这样的话从本质上说它其实不是一个SQL DB了虽然他的Storage Engine 可能还是用的Maseco但是他从一个product的假设来看一旦你这么做scale 他其实就不是了这个跟那个google banner不一样对我觉得这块它这个是不是SQL是Dependent这个SQL定义是啥?如果你说是Single Shared Transaction的话它还是满足的,但是它不支持Cross Shared Transaction所以如果你是 multiple project 的 transaction 的话Multiple object transaction的话,你要看它是不是在同一个扇子上如果在同一个上乘的话还是可以支持这个transaction所以这个sql 这些定义其实也是相对模糊的其实SQL这个定义是不模糊的,SQL是什么呢?就是Simple Query Language。他说白了是Stone Breaker当时写这个paper的时候就说他希望把这个computer sentence的工作和application developer的工作分开,Computer Synthesis focus on怎么样做处理 concurrency,怎么样处理这个durability,什么的,就是它处理这些非常hard的部分就是 transaction 的部分,就是 definitely hardware 的部分,就是 data center, sorry,
[50:00] S&S 的工作然后Application Developer就写SQL,就是select from 什么什么什么,这是SQL,叫Simple Query Language它是一个非常好的一个separation, abstract separation to share separate concern. Your applicationDeveloper写的和这个就是这个Infra或者是ComputerScientist写的Fundamentally不一样然后他们提供一个非常简洁的一个Abstract来写但是这个SQL写了以后呢,它取代了之前的什么Hierarchy Database,还有什么Navigation Database,全部被革命掉了革命掉了以后呢,这SICLE因为统治了大概近50年,40年,四五十年,直到2010年左右他出来了各种什么2010年2007年左右他出来了各种什么NoSQL然后他叫NoSQL,当时就说,希望就是不想要SQL,他觉得SQL实在是,你想你如果是存一个data的话,你要把一个车分层组装,把车存成天窗式存在一部分,什么车径存在一部分,你要把它分开拆下来,然后再存起来,取的时候你要把它读出来,然后再把车给拼起来才能使用。所以这个很多人就很反感,所以就叫做NoSQL。所以这个区分其实是非常不严谨的,Sycle,No,Sycle这个区分是非常不严谨的,有很多奇异所以你要看怎么定义这个SQL所以如果他问你说这个面书官问你什么是SQL,什么是NoSQL的话,这个FOMO的定义就是这样的,SQL就是Simple Query LanguageNoSQL就是不是那种传统的relational database但是你说如果你用relations database build一个sharded database,它属于sql, non-sql呢我觉得这个相对比较难说,我觉得像Paul说的一样,这个其实也很难很难定义说它是Sequel,No Sequel,所以你如果他问这个问题,就要首先问他,首先Sequel是什么,在你的心目中Sequel是什么?我觉得这样的就是说的很好因为面试关于有时候他自己的想法跟你的想法不一样如果你说的跟他的很相反的话他会觉得你可能没真的搞清楚所以我觉得把跟面试官沟通的话非常非常重要对,这个Sycle NoSQL定义本身就是一个不清楚的问题,但是可以这么想我觉得不管怎么样,这个SQL它是设计,因为它这个历史比较悠久,所以它设计的时候它focus的问题,单机的问题,single node的问题,然后它focus的是一种Consistent是一种ACID,就是Consistent和Transaction,就是Transaction,这个是没有编的。NodeSicle的话,它可能是cross multiple nodes,这个可能是比较high level的相似点。好,我们讲了好多sql, no sql,因为我觉得这块好多人问啊,这也是hot debated area,我觉得还是有挺多地方可以值得讨论的,我们稍后也会就是dive deep一点,周六时候可能会dive deep一点讲这个细节,然后我们看这个,然后再看这块,回到这个troller这个问题。最怕就是raw data里面呢,它其实你思考一下这个raw data,它就是k value的一个read和write,你比如说你crawl了一个json,crawl了一个html,所以就是keyvalue,所以它这个比如说是这个gameplay的json,因为它固定的是20个kb比较小,你可以放到Amazon S3上,但是也可以放到DenimalDB上,我觉得本质上没有什么问题但是如果它这个data,比如说它的data size和这个expected data size不是很一致的话,太大的话,你可能就得放到Amazon S3上然后你可以放到mongo上,因为你crack下来的json,直接存到mongo上也没有问题。我在GrooveGamer上研究生的时候是把这个这个replay放到mongo上存的,我觉得mongo还是挺坑的然后这个metadata的话你这块需要SQL了,因为它需要一些Durability和Transaction,我们稍后解释它为什么需要Durability和Transaction好然后这个API的话,这块需要两个API,一个是你需要增加一个新的Crow的Schedule,比如说你要Crow这个Game Replay然后你希望多久craw一次或者你要craw这个news website 你希望多久cry一次 多久开始startcry一次 这种然后这个可以repeatly crawl 比如说你是要做snapshot 那就要repeatly crawl你也可以就是craw过就不craw新的了 这个都是可以根据你的business use case来configure的然后你这个start crawling就开始对这个进行crawl你可能有个iteration是吧,你如果是repeated crawling可能有个iteration,如果不是repeated crawling不需要iteration你可能有一些blacklist的UIL,就bookcrowd的UIL然后你可能还有一些start的UIL,有一些UIL的细节你可能放在这里BlackList URL 怎么存出呢? 比较简单,遇见它就直接无视掉就行,不insert data到那个 records 里面就行然后你这个type可能是on-demand,也可能schedule,那你这样的话你可以看到,如果是schedule crowd的话,就是你insert的一个schedule,然后同时start crowd,然后这个type是schedule crowd。就是这个如果on demand的话你比如说有个button一点这个button就开始
[55:00] Carlets也是一种惨天的做法然后这个RowDataTable, 不好意思我想问一下API的问题这两个API有什么本质上的不一样就说两个API都可以用吗假设我要啊Amazon.com看起来好像两个API都可以用是吗?不是,Schedule Cloud其实是你 insert一个 record 到这个Schedule Table 里面比如说我现在每天早上八点craw一遍replace我就insert了这个,然后我有个YAML,比如说specify,开始种子UIL是什么,比如说我可以specify,我靠多少层然后我可以specify哪些是blacklist的哪些是whitelist的什么的这是I-SETI的一个schedule你insert进去以后它如果是on的话它就开始自动的到每天8点的时候开始start crawl了就相当于是schedule crowd是说我是不是应该开始一个固定的cloud流程,一个固定的每天run一次的process。start crowd就是我开始这个cloud这个动作了,我开始先cloud一个website,一个URL,然后新的UIL,再不停的靠新的UIL,然后知道全部靠我一遍谢谢大家我有一个小小的confuse的地方就是因为你第二个API也有这个schedule这个type所以我就有点confused 如果你没有这个我就非常肯定就是先是 schedule 然后你随时可以start crony对,我这块加这个我觉得主要就是区分是谁trigger它的,你是手动trigger的还是schedule trigger的可以没有?因为你很多时候,我作为一个process,这个有点像Airflow,我做过一个用Airflow类似的产品的话很多时候你要区分你现在有个镜头在run,你有个flow在run,那你这个flow是手动驱动呢还是 schedule 驱动呢就有个running running running running running running process 你是手动驱动的还是on demand 驱动的但这个可以没有,不是一个必须要有的东西。谢谢大家我看到这块有几个问题 说add schedule crawl call start crawling 对的这个add schedule crawl呢其实就是把它说我要开始固定的时间craw这些东西了 start crawling就是就是说开始这个具体的这个过程了然后是这样子的然后还有说是先让我把主要思路讲完,然后定期回答一下问题比较好。行,反正我这块已经快讲完了,没事的。我已经快讲完了这块因为crawler相对比较简单好 现在是这样子 所以你这个raw data table 比较简单 就是一个UID一个ID加上这个Data,这个Data可能是HTML,可能是JSON,根据不同的用适case,还有CreditTime。然后这个CleanData的话,就是你把上述这个RAWDATA,Pause从你的Data,比如说你可以加这个ID可以是URL加Iteration的Hash,你只要这个Hash这个Space足够大,你是一般是没有Collision的然后你根据你的这个啊 type 根据你不同的 fields 来存 比如说你这个如果是 gameplay 的话 你可能需要 fields1到10然后如果你是这个news aggregator的话,你可能需要其他的fields然后这块呢 我想说这个Smart Fields Detector 其实没有那么的Smart你如果news website突然加了一个fills叫什么breakingnews然后你不可能用机器告他这个突然就知道这个Break News什么意思,然后自动的去分到一个Fields上,这个是基本不可能的。所以遇见这种不懂的Fields,你可以讲这种事吗?你叫么就ignote,要么就是notify the user或者这种notify user你可以blocking user notify user就是我没法pause了除非你来解决或者是non-blocking就是我正常的按我懂得去pause然后我这个fields我不认识它我就ignore了但我会notify user说我这块有个real data你是不是应该更改一下Fields啊这个是这种常见的解释,有那种dynamic的就是,非常dynamic的就是,因为它是个JSON,比如说你读出来是个JSON,它这个Fields,不管它怎么定义,它是Fields1,我就把这个Fields1自动的copy到这个table里面,就相当于这个它是Fu1我就变成定义从Fu1它是Fu2我就定义从Fu2根据 dynamically detect new fields然后在这个table里面,modify schema,加fills和减fills你可以把整个ultimate下来但我觉得这个不太好,不是一个很好的一个使用情况 除非你这个 因为你现在相当于这样子你database schema是非常重要的一部分但你把database schema depend on third-party service你 third-party service 如果出现问题的话 你database schema就mess up了你需要去手动的去清理这个Math-Sav schema所以一般来说是来自于external的一个data schema是不希望这么flexible的
[60:00] 这种design choice吧好,然后我们再看这个schedule table,schedule table就非常简单,就是Create 一个新的 schedule 比如说你可以 visualize 他说 Scott 开了一个 schedule是 Crowd Replace Website然后他是每天8点开一次然后它有些YAML file,比如说有一些种子file,然后这个schedule可能是开着的,可能是关着的,也可能是pause的这都可以然后有个Iteration,这Iteration就表示比如说当前这个replay的cloud的话我当前replay crowd的话我是第九个iteration就是3月9号,不是6月3号,6月3号这个Iteration,我什么时候开始的,我现在早见了,我现在发现了多少UIL,有多少UIL已经跑过了Craw过了有多少UIL我已经处理过了有的话URL是stuck的就是我没法辨认它的schema 我stuck在那里了就是你可以这样update,然后这个record的话,所有的这个design呢是focus上这个table的这个table是非常stateful一个table,就是说,比如说我现在craw这个games.net url 1,是吧,就是我要craw第一个replay,那么不管是它是在一开始在种子里面的还是被discover service发现了然后把它加到这个种子这个pool里面的那么我首先第一件事说我把它加了一个新的record到这个里面那这个id是用uil和iterationhash 过的,所以这个不会,如果你重复这个动作的话,你是 add important 的,你是没有 set effect 的所以你就可以忽视掉就行了,no up就可以了。然后你现在要查这个games.net 1然后它是第一个Iteration,或者第九个Iteration,它现在是QState,那QState的话就是下一个就是Cloud,就是先把它放进去,然后是Cloud,然后CloudPause成功以后是Process,如果没有Pause成功,你可以变成Stack,会有其他raising也可以变成Stack,对吧?然后你什么时候Created,什么时候Update的,然后有没有NotifyUser,有没有被Delete掉那你就 insert 这里然后这就是说的data model了我们现在看这个Fault Tolerant这个问题因为我们讲过这里面任何一个component都可能fill掉比如说你在schedule的时候fill掉了你需要 scheduler做什么呢?我首先 read yaml 知道哪些去 crop 比如说就是start crawling 是个 unscheduled的cloud 我先read the ammo 知道哪些去crowd 对吧 那么我把那个sit放到这个records里面,我把这个sit insert这里面,全部是q state如果它这个sade已经存在了,比如说之前已经其实尝试启动过了,但是挂掉了,那我就不做任何事情。然后这个craw过程是sand and forget的这个design思想之。我散了以后就忘掉了,散了以后就忘掉了然后同时呢,我在这个Seed全部执行成功以后,在Step2全部执行成功以后我在这个Progress table 里面加一个新的progress row说,我已经开始启动这个games.net.crout,第一个iteration已经启动了那我现在犯的UIL就是我C的UIL 是吧然后后面都是零,因为你刚启动所以你就是都是零你就开始了如果fault掉的话,你可能就得从第一步重新开始反正你重复不管多少次的他的所有的operation都是没有side effect 或是adm potent的好然后这是 schedule,然后一般来说我设计的一个preference,我以前见过是比较好的一个preference是映像是在浴室的浴室的浴室的浴室的浴室就是说他是一个heavy lifter,他是一个worker,他是一个heavy lifter,比如说这种情况下就是crawlerpulser 它要做很多就是 compute heavy 的东西它都是sign and forget,就是stayless,什么意思呢?它挂就挂了谁去重启它是有另一个process去重启它但是他现在做的就是他做的就是他做的这个事就是做的这个事比如说这个scheduler说我要crowd a websitesend了以后就忘掉了然后crawler说我开始crawl这个website然后crawler怪掉了他就不管了 他醒了以后他是没有stated的从counter角度来讲的话,它是需要读这个UIL,比如说我现在读这个需要被counter的UIL,然后如果他 already crowd的话,那我就不做任何事情如果没有cloud的话我就cloud这个url,然后把data放到data table里面然后把这个wrecker说他是craw过的所以当这个这个record,当这个URL变成clog的时候,它一定是完成了第一步和第二步的那这个data一定会放到这个就是cloud好的然后Paul就会问了一个问题说这个scheduler是一台机器还是多台机器?这个scheduler应该是多台机器,因为一台机器的话single-plied failuredistributed scheduler其实是相对比较复杂的一个service我在
[65:00] Salesforce的时候,Salesforce专门有人花了一年时间实践了一个distributed scheduler大家想一下这个D3PIO Scheduler还是有点问题的,你首先那个Consensus是吧,并且我们之前讲过这个 Timer 说的 Clock 是可以 jump back in time 的那你这个time abstraction, guarantee, non-monotonicGuarantee, Guarantee, Guarantee, Guarantee 是个递增呢? 是吧?所以这个Schedule Distributed Scheduler呢, 是可能需要单独设计的一个Service这块呢,Luckily有一个service可以直接解决问题就是Airflow,Airflow其实你可以把它的functionality就只用它就是可以用来做这个distributed schedulerUber内部的Airflow就是Piper嘛,Piper其实说把它就是一个distributed scheduler,就是它有多台机器run scheduler,然后它会保证你在几点几分的时候trigger一个flow,所以你可以自己写一个distributed scheduler,然后满足各种abstraction,比如说monotonic time increasing啊什么的。你可以使用一个比如说是Rflow 用Rflow来做Distributed Scheduler的这个 abstraction 然后这个是然后这一块还有一个问题说如何防止多台机器拿到同一个URL多台机器拿到同一个URL, 为什么呢? 这块所有的crawler, 我们看一下这个design里面这里面所有的Crawler呢是Citless它的State for 是在什么呢?是在这个Meta data里面所以同一个UIL,同一个Iteration,它实际上会变到同一个UIL,同一个Iteration,在Database里面它是同一台Machine的。所以它尽管是不同的crawler和pulsor去处理的但它data存储那块是在同一个machine上,它是shared到同一个machine的所以这种情况下如果有conflict你就直接有一个变成no-op就可以了,有一个处理过,另一个变成no-op就可以了。你比如说你现在这个你cue了一个craw,然后过了10秒钟没有craw,你这个又cue了一个craw,是吧?那你这两个craw就相对来说做同一件事嘛。第一台机器抽完了,它把这个data存到这个share1里面,uilgames.net1第二台机器也crack过来了,它准备存在出,发现这个已经变成crack过来了,那就直接no up就可以了因为它这个其实你思考一下的话,因为这个crawler和pulsor都是stiteless,它Stateful全部在Data,然后它这个Data,同一个UIL一定放在同一台Machine上从一个UIL 同一个Etration 也放在同一个Machine上 没有Conflict所以不怕这个不好意思我还是想澄清一下,因为打字我觉得很难讲清楚,就说你在Schedule里,Core API,或者是AID Schedule的CORONA,你叫了Amazon.com,这是一个要爬的我两个爬虫同时爬然后在同一个时刻,两个人都去买SeqlDB拿拿到同一个122,我们同时开始跑,这样不就有冲突了吗?你计划用的是Message Queue,Message Queue的话是不会的,就是一个workerClaim的话,下一个worker就想去取下一个Message吧?谢谢大家那你用select, 比如说start from这个table然后加个Ware什么的,它很可能会去到同一个。对,start from 可能会同一个,但这块用messageQ是不可能的。Message Queue有点像那个,如果不懂Message Queue的同学,Message Queue有点像,比如说你去星巴克点咖啡,然后他一上来给你个Receive,就相当于Queue了一个Message,到Message Queue里面。然后它后面有三个人做咖啡,然后做咖啡的worker一拿到你的receipt,然后做咖啡的worker拿到泡的receipt,做咖啡的worker才拿到这个景象的receipt他会就是claim了以后去做的,这个message丢了无所谓的,因为我们说过这个,我们后面会讲这个sweeper是专门负责处理这个丢了的message的对我其实问的是schedule的可能会放duplicate的URL到这个message queue里面去哦 那个也没事的 如果你不小心放了 比如说你第一次Cloud 半天没处理 然后又放了一个 那也没事的 因为就是你最后其实所有的这个 我们不看不管是这个Cloud的这个动作还是Pause的动作 它重复两次没事的 所以你可以做两件事 如果它重复了直接覆盖可以 或者它发现这个已经处理过了直接放弃也可以 都可以的谢谢大家谢谢大家就是Duplicate action是没有Site effect的谢谢大家比如说你有两个process 抓到同一个UIL我第一次crout以后放到一个url,就是这个存到这个raw data里面我第二次crout以后我重新覆盖一遍,我发现它已经存过了,我可以重新覆盖,也可以直接bought都可以谢谢大家谢谢大家谢谢大家请不吝点赞 订阅 转发 打赏支持明镜与点点栏目嗯好
[70:00] 这块有同一系统比较好,uniqueness还是data部分的或者说你说白了就是把这个问题去切粪嘛你是个很庞大的问题,你很大的一块问题,然后你把不同的这个切分嘛,你这个这个就是你的conflict是在data上conflict,所以你data呢相同的URL是放在同一个share里面的,你这个share里面就能detect duplicate,detect conflict,然后进行处理。你crawler是没有state的,可能是多crawler在同一个URL的crawl和save,但你最后存到database里面的时候你最后存到database里面的时候,他们会detect到这个conflict,然后他们database程度会进行处理。然后并且另一点的话这个里面所有的这个处理全部都是 Adam Potent 的就是重复是没事的不管是覆盖还是About都无所谓的谢谢大家好,我们接着讲这个Crow,所以Crow部分,Crowler部分是stateless的,那这个poster部分也是类似的,也是stateless的,比如说你需要read一个record需要去posed的,这个poser全部写错了你需要 read 一个 database 去 post 的,如果没有处理过的话或者是如果已经stuck的话就相当于处理过了处理成功处理失败那就不做任何事情然后如果就是这个是怎么detect的呢,这个就是你相当于是你去database一看就发现它处理过或没有处理过,你就不做任何事情。然后如果这个就是你去validate,如果不validate的话你就把这record变成stuck,对吧?然后如果是valid的话你就把那个validate record放到clean data里面,然后把record state变成process然后这个里面任何一步比如说你现在这个data还是不在这个stack和process的话,你这个process down掉了不管你有没有传到这个Record到这个Clean Data里面,这个过程全部被重复一遍。如果这已经存到过就覆盖是吧,然后把它变成处理过。所以这个是当你把它标记成处理过的时候,你一定是以上的步骤全部完成了。谢谢大家以上的部分全部完成了,这个就是你可以repeat,但是你repeat没有setEffect,所以这个我们把它叫做itempotent最后一点就是这个Sweeper, Swipper是相对复杂的一部分,这Sweeper可能要做几件事情比如说他需要读这个 record 在 stock state我们之前说过这个 stock state 就是说有一个新的 schema 他不认识了 是吧 会出现异常了 对吧你可能需要 notify 这个 developer 说我出现了异常 我不知道怎么处理然后你可能可以在这里面把这个RecordMark成notified,我这块专门加了一个Field叫notified,说这个Exception已经被notified了你也可以说我现在看这个 records in queue state,当这个 queue 了很长时间,这 queue 了很长时间可能发生几件事情比如这个这个这个craw的message在message queue里丢失了,有可能craw的crawler machine down掉了。那么我把它找见这个record在queue state,我重新issue一个crawler command,它会重新被crawler去更新,它可能是卡在crowder了,卡crowder什么意思呢?就是没有我们适应去process它,那么我就重新ease一个process command,我不知道它具体哪一步挂了,比如说我这个可能是没有sign message就挂了,可能是message丢了挂了,可能是processor挂了,不管怎么样,我知道它没有被处理,那我就重新处理一次,可能处理多次了,可能多重复处理了,但无所谓,它是没有sign effect的。然后最后的话还有一些额外的feature 比如说你可以去读这个Iteration progress 是吧 你可以updateIteration progress 然后send一个summary notification 那你作为user的话就知道我当前这个Iteration有多少个Fund了然后多少个Stack了 多少个Clar了 多少个Process过了那我就大概知道心里有说大概到什么程度了,然后如果你这个data是在多个地方cross-stardew的话,你可能需要consolidated,然后做一些business处理。谢谢大家如果好这块有问题说啊如果Process stuck了,它是怎么update status的,不update status?比如说我们在这里面,Pulser这一块它可能在第一到第三步任何一个stack的如果stuck的话,它就没有变成process当他过了比如说15分钟后,你这个swiper说对于这个record,他读table说对于这个record他还没有被处理过。它如果没有被处理过的话,那我需要去处理它,那我就easeo一个处理是吧?重新issue一个Post command,然后重新处理,重复这个过程。这个是Infinite Scaleable, 为什么呢? 因为这里面是全部可以shard, 任何东西, 我思考下这个里面, 所有的Heavy Lifting, 不管是Crow, Process, 你可以Infinite Shard
[75:00] 然后如果你有concentrate data,比如说这个schedule也可以share 是吧然后你的Progress也可以shard是吧,你这个是可以Infinite Scale的,所以这个Scalability不是问题,你可以把所有的Data,你可以用,当然Google是用这个MapReduce做的了。Google没有用这个方式做,因为Google这个MapReduce,其实就是从那个,它发明了一个新的这个tool,是吧,MapReduce做的。但是你也可以说这个可以Infinite Scale,就是处理这个Crow这个数据。然后你比如说,比如说面试官要求说你一天Crow一遍Amazon,你第一次呢就先就是experimental弄100个crawler,弄10个pulsor,然后分析一下他这个数据怎么样,performance怎么样,他到底卡在哪块了,是crawler数量不够还是pulsor数量不够,还是他花了两天才把它处理完是吧,然后他如果花两天才处理完,你下次就可以double resource,然后直到把它optimize到一个balance state这样的话就可以满足这个需求,你可能run几次就满足这个需求了,但是它这个应该是可以Infinite Scale的。好,就全部讲完crawler了,大家有什么额外问题吗好我觉得这块有几个核心思想吧如果这个技术比较差的话,有几个核心思想我觉得比较重要。一个是 Adam Potent我不怕的feel 我feel了以后重复一下这个过程我拍crowd的时候fuel了,我fuel了以后重复一下过程,对吧?一个是Item Potent 第二个是Asynchronized Processing 通过Message Queue来实现MessageQL这个目的是什么?目的是两点,一个是separate concern,比如说这块这个crout和pause用Asynchronized Processing 分开如果这个PauseFill的话我不需要再重复这个Crow的这个步骤我可以直接从这个Pause field process继续是吧,这个是Asynchronized Processing with separate Concert,另一部分的话它是可以Dynamically scalable,是吧,就是你比如说你现在这个Crawler数量不够,我加Crawlerpulsor 数量不够,我加pulsor 这两个分开,根据它不同的 load 是 elastic skill这是另一个核心思想吧,还有一个核心思想的话就是这个Machine,这个Compute是StateLess,Crawler和PulserStateLess,StateFold全部在Database,Database是StateFold这是一个常见的一个核心思想吧然后最后一点的话是怎么样处理这个fault tolerance你比如说你需要一个sweeper不停的看到底卡在哪一步了是吧如果没有卡的话都解决了就不错但是你如果卡在那一步的话你是不是re-easeal这个command 是吧re-easeal不管是pause还是re-easeal这个这个crop就可以re-issue这个command然后还是比较小而精的一个design题目我觉得好谢谢大家好,大家有什么问题吗?我看这块有一个问题说,clean data table怎么分辨出什么type的,这是user defined的,你比如说我们看一下clean data,你这个不管是这个News的headline啊,News的matex啊,这肯定是userdefined这个field,不管是比如说这块是headline, image,这都是userdefined的。所以你作为一个user你要根据你的use case来define然后说这个type如果是data type的话,那你就根据这个data的format来define然后还是根据的use case来define的大家有没有什么额外的问题没有额外问题的话就是可以再读一下这个这个也是在这个folder里面如果有额外问题的话可以Q&A写一个问题然后再问一下讨论一下嗯crawler部分还是比较有趣的然后这块有问题说这两个table怎么知道存什么,这个很简单,你crawl的时候根据你的name就知道了,你replay是一个name你的news是一个内容,这两个是完全不同的用适 case,我只是这块解释这个crawler是比较generic,可以craw这个replace JSON,也可以craw这个news website,就是我之前做了几两个projects都可以craw,你可以用crawler,比如说你是个今日头条,你可以用crawlercraw一些CNN 啊,CRAW一些,然后你把它作为一个就是精日头条来实践,是吧?你可以做一个非常powerful的Machine Learning Power的Game Recommendation Engine,是吧?你也需要CRAW那你就 cloud replace 是吧那你这两个 use case 其实完全两种 use case 嘛所以你 cloud 的时候就知道它是哪个 type 又知道它在哪个 table 里面好,今天就到这里了,然后非常感谢大家谢谢大家好,谢谢大家。谢谢大家