Kafka 篇解决了”数据怎么稳稳地流起来”,Spark 篇解决了”离线数据怎么算得快”,Flink 篇解决了”实时数据怎么算得准”。但海量结构化数据落下来之后,怎么存得下、查得快、还能随时横向扩展?关系型数据库在亿级行、TB 级数据面前,分库分表与加索引都越来越吃力。HBase 用”列式 + LSM 树 + 自动分片”这套组合,成为 Hadoop 生态里扛海量写入与随机点查的存储底座。本文把它的核心原理与最容易被坑的 RowKey 设计一次讲透——与 Kafka/Spark/Flink 三篇零技术点重叠,构成大数据平台的完整拼图。
一、为什么需要 HBase先说清楚 HBase 解决的是哪类问题。传统 MySQL 在以下场景会明显吃力:
痛点
MySQL 的表现
HBase 的解法
写入吞吐
行级事务 + B+ 树随机写,高并发写入易抖
LSM 树顺序写,写吞吐随节点线性扩展
水平扩展
分库分表业务侵入大,扩容要迁移
Region 自动分裂,扩节点即扩容量
稀疏宽表
上百列大多为空,行存储浪费严重
列式存储,空列不占空间
半结构化
加字段要 DDL,sc ...
“为什么我的页面会卡?”很多前端性能问题,追根溯源都落在同一条链路上:浏览器的渲染管线。理解从 URL 输入到首屏呈现的每一步,你才能判断哪里该优化、为什么 transform 比 top 流畅、为什么一段看着无害的循环会引发卡顿。本文把渲染原理、重排重绘、合成层与浏览器缓存串成一条线,配可复用的优化清单,帮你把”感觉慢”变成”知道为什么慢、怎么治”。
一、从输入 URL 到页面呈现:完整链路在讨论优化之前,先建立全局视角。当用户在地址栏敲下回车,浏览器大致经历以下阶段:
阶段
主要动作
常见瓶颈
URL 解析与导航
解析协议/域名/路径,交给网络进程
重定向过多
DNS 解析
浏览器缓存 → 系统缓存 → hosts → 递归查询
DNS 查询慢
建立连接
TCP 三次握手;HTTPS 还需 TLS 握手
握手 RTT、证书链
发送请求/接收响应
HTTP 请求头、响应体(HTML)
首字节时间 TTFB
解析与构建
解析 HTML 构建 DOM,请求并解析 CSS/JS
CSS/JS 阻塞
渲染
Layout → Paint → Composi ...
“JS 是单线程的”这句话只说对了一半。真正让它扛住海量并发的,是事件循环(Event Loop)这套调度机制。理解了宏任务与微任务的排队规则,setTimeout 为什么不准、await 之后代码什么时候执行、页面为什么会卡顿,这些问题会一次性讲清楚。本文从底层模型讲到 async/await 实战,配可运行的输出推演,帮你把异步的”直觉”换成”确定性”。
一、为什么 JavaScript 必须是异步的JavaScript 诞生于浏览器,天生要操作 DOM。如果允许多线程同时修改同一棵 DOM 树,就得引入复杂的锁机制——这显然不现实。于是它选择了单线程模型:同一时刻只有一段 JS 代码在执行。
但单线程有个致命问题:遇到耗时操作(网络请求、定时器、文件读取)如果傻等,页面就会彻底卡死。解决方案就是非阻塞:把耗时任务交给浏览器(宿主环境)去办,主线程继续跑后面的代码,等结果回来了再通知主线程执行回调。
概念
说明
调用栈(Call Stack)
单线程执行 JS 的”工作台”,函数调用入栈、返回出栈
宿主环境(Host)
浏览器 / Node.js,负责定时器、 ...
GC 篇讲的是”对象怎么回收”,本文讲的是”类怎么进来”——一个 .class 文件从磁盘上的字节流,到内存中可以被 new 出来的 Class 对象,中间要经历什么?为什么 Tomcat 能在同一台机器上部署两个不同版本的 Spring?为什么 ClassNotFoundException 和 NoClassDefFoundError 长得像却完全是两回事?这一切的答案都藏在类加载机制里。本文把加载过程、双亲委派、破坏双亲委派的经典案例、自定义类加载器一次讲透。
一、类的生命周期:从字节流到可用对象一个类的完整生命周期是:加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载。前五个阶段合起来就是”类加载过程”,其中验证、准备、解析又合称连接(Linking)。
注意:加载、验证、准备、初始化这四个阶段的开始顺序是确定的,但不要求同步完成——验证、准备、解析常常交叉进行(解析可以在初始化之后才做,这是 Java 支持运行时绑定的基础)。
1.1 加载(Loading)阶段做什么加载阶段只做三件事:
通过全限定名获取字节流:从 jar、网络、动态代理生成,甚至 JSP ...
Kafka 篇解决了”数据怎么稳稳地流起来”,Spark 篇解决了”离线数据怎么算得快”,但当业务要求秒级甚至毫秒级出结果——实时风控、实时大屏、实时告警——微批架构的天花板就出来了。Flink 用”把批看成流的特例”这一套反向思路,成为实时计算的事实标准。本文把 Flink 最核心也最容易踩坑的几块:时间语义、Watermark、窗口、状态、Checkpoint,一次讲透。
一、为什么需要”真正的”流处理Spark Structured Streaming 用”微批”(把流切成一个个小批)模拟流处理,工程上优雅,但有三个先天限制:
限制
微批的表现
Flink 的解法
延迟下限
延迟 ≥ 批间隔,调到 100ms 以下调度开销急剧上升
纯流模型,逐条处理,毫秒级
事件驱动
每个批次都要调度、拉取、执行一轮
常驻算子,数据来了直接算
反压与背压
批间排队,压力在批边界积累
数据流内置反压(credit-based)
在架构演进上,有两套经典方案:
Lambda 架构:同一套逻辑写两遍——批层(Spark)保证最终准确, speed 层(Storm/F ...
很多团队写批处理任务的第一反应是 MapReduce,但真正跑起来才发现:一个简单的 WordCount 要拆成 Map 和 Reduce 两个阶段,中间结果全部落盘 HDFS,稍复杂的迭代算法要串起十几个 MR 作业,每一个都在读写磁盘。Spark 用 DAG 执行引擎 + 基于内存的计算 把这个问题彻底重做了一遍。本文从原理到实战,把 Spark 最容易被问、最容易踩坑的部分一次讲透。
一、为什么需要 Spark:MapReduce 的三大痛点MapReduce 是第一代批处理引擎,但它天生”腿短”:
痛点
MapReduce 的表现
Spark 的解法
中间结果落盘
每个 MR 作业的输出写 HDFS,下游再读一遍
中间结果保留在内存,必要时才溢写磁盘
表达能力弱
只有 Map / Reduce 两个算子,Join、排序要手写
80+ 算子(flatMap、reduceByKey、join…),代码量降一个量级
延迟高
JVM 冷启动 + 磁盘 IO,分钟级延迟
DAG 调度 + 线程级任务模型,秒级~亚秒级
一句话总结:MapReduce 把计 ...
面试里问完并发、集合,下一个必考的就是 I/O 与网络编程。本文不重复线程池、《Java 并发编程核心》里的 volatile/CAS/AQS,也不展开集合源码,而是聚焦一个独立主题:Java 到底怎么读写数据、怎么用少量线程扛住海量连接。读完你能回答:BIO 为什么扛不住 C10K?Selector 凭什么一个线程管几万个连接?零拷贝究竟”零”掉了什么?
一、为什么 I/O 模型是 Java 工程师的分水岭写业务代码时,I/O 往往被框架封装得无影无踪(Spring MVC 一个 @RequestMapping 就完事)。但一旦要写中间件、网关、RPC 框架、消息队列客户端,或者排查”线程一路涨到几万、GC 频繁、CPU 却不高”的线上问题,I/O 模型的理解深度就直接决定了你的定位能力。
I/O 的本质只有一句话:数据在「用户空间」与「内核空间」之间的搬运。所有模型差异,都是”这段搬运由谁触发、要不要阻塞线程、要不要来回切换”的组合。
二、先把概念厘清:阻塞/非阻塞、同步/异步这两组词最容易被混用,必须彻底分开看,它们描述的是两个正交维度:
阻塞 vs 非阻塞:描述”应用程序发 ...
集合是 Java 面试和日常开发出场率最高的知识点,没有之一。本文不聊并发容器(ConcurrentHashMap、CopyOnWriteArrayList 的细节已在《Java 并发编程核心》一文中拆解过),而是聚焦单线程场景下的集合骨架:ArrayList 为什么扩容是 1.5 倍?HashMap 为什么容量必须是 2 的幂?链表为什么树化阈值是 8?LinkedHashMap 如何三行代码实现 LRU?把这些问题一次讲透。
一、集合框架全景图Java 集合框架以两个顶层接口为根:Collection(单元素)和 Map(键值对)。
123456789Iterable └── Collection ├── List(有序、可重复):ArrayList / LinkedList / Vector ├── Set(不可重复):HashSet / LinkedHashSet / TreeSet └── Queue(队列):ArrayDeque / PriorityQueue / LinkedListMap(独立体系) ├── HashMap → Linke ...
Vue 2 用 Object.defineProperty 做响应式,遇到”新增属性不更新””数组索引改了没反应”只能靠 $set 打补丁。Vue 3 把底层换成了 Proxy,从根上解决了这些坑。本文把响应式的”任督二脉”——依赖收集与派发更新——彻底拆开,再看 Composition API 怎么把逻辑组织得更干净。
一、为什么 Vue 2 的响应式不够用Vue 2 在初始化时递归遍历 data,用 Object.defineProperty 给每个属性加 getter/setter。这带来三个绕不开的硬伤:
痛点
现象
Vue 2 的妥协方案
新增属性无响应
this.obj.newKey = 1 视图不更新
必须用 this.$set(obj, 'newKey', 1)
删除属性无响应
delete this.obj.key 视图不更新
必须用 this.$delete(obj, 'key')
数组索引/length 监听不到
arr[0] = x / arr.length = 0 不触发
重写数组 7 个方法(push ...
很多系统一开始用”直接调接口”把服务串起来,随着流量和依赖变多,耦合、峰值、故障扩散会接踵而至。Kafka 不是单纯的”消息中间件”,它更像一套分布式的提交日志系统。本文从原理到实战,把 Kafka 最容易被问、最容易踩坑的部分一次讲透。
一、为什么需要 Kafka:消息队列解决了什么在一个典型后端系统里,订单服务可能要同时通知库存、积分、风控、物流。如果全部用同步 RPC 调用,任何一个下游抖动都会拖垮下单链路。消息队列的价值可以用一张表概括:
痛点
同步直连
引入消息队列(Kafka)
耦合
A 直接依赖 B/C/D,改一个要动一片
只依赖 Topic,下游可随时增减
峰值
流量洪峰直接打垮数据库
broker 缓冲,消费者按能力消费(削峰填谷)
失败扩散
库存超时导致下单失败
消息持久化,下游恢复后继续消费
能力扩展
新增消费者要改调用方
直接加 Consumer 即可扩容
Kafka 的”本领”不止异步解耦:它还天然支持流处理(Kafka Streams / Flink 接入)、事件溯源、日志聚合和多副本容灾。当你需要的不是”发个通知”,而 ...































