MySQL 扛住了交易订单的实时写入,却在” yesterday 全站 GMV 按省份聚合”这种分析查询里反复超时。ClickHouse 就是为这种”海量数据、复杂聚合、秒级返回”场景而生的列式 OLAP 数据库。本文把 ClickHouse 最容易被问、最容易踩坑的原理与设计讲清楚。
一、为什么需要 ClickHouse:OLTP 与 OLAP 的冲突MySQL / PostgreSQL 擅长的是 OLTP(联机事务处理),行存、强事务、点查写入快。BI / 监控 / 埋点分析需要的是 OLAP(联机分析处理),列存、高吞吐扫描、聚合快。两者设计目标不同,硬用 MySQL 做分析会遇到三大瓶颈:
维度
OLTP(MySQL)
OLAP(ClickHouse)
数据组织
行存储,一次读一整行
列存储,只读需要的列
写入模型
面向单行增删改,事务强一致
面向批量追加,最终一致
典型查询
SELECT * WHERE id = ?
SELECT province, sum(amount) FROM ... GROUP BY province
索引结构
B+Tr ...
本文是《Java 从入门到精通》系列第五篇,默认读者已经掌握类与对象、异常、集合基础。全文代码均在 JDK 8 与 JDK 17 上实测通过,涉及版本差异的地方会明确标注。建议边读边把代码粘进 IDE 跑一遍,尤其是 ==、intern()、BigDecimal 构造器这几段,光看结论很容易记反。文中出现的 0.1 + 0.2 != 0.3 不是 bug,而是 IEEE 754 的必然结果。
一、String 的本质:不可变对象与内存布局1.1 不可变性是如何实现的String 的不可变(immutable)不是靠”约定”,而是靠三重硬约束:类被 final 修饰无法被继承、内部存储数组被 private final 修饰、以及没有任何对外暴露的修改入口——所有看起来会”改”字符串的方法(concat、replace、substring、toUpperCase)在 JDK 7u6 之后全部返回新对象。
12345678910111213141516public final class String implements java.io.Serializable, Compara ...
本篇假设你已经掌握类与对象、继承、重写与多态,并在机器上装好了 JDK 8 或更高版本。文中所有示例均为可直接 javac 编译运行的最小片段,涉及字节码的地方会给出 javap 的验证方式,建议边读边跑。如果你还在纠结「什么时候该用抽象类、什么时候该用接口」,读完第二、三章自然会有一套可复用的判断标准。
面向对象入门时我们习惯把「类」理解成数据与行为的打包,但真正决定代码可维护性的,是类型之间如何约定契约。抽象类负责纵向的「是什么」,接口负责横向的「能干什么」,内部类解决「只在某个上下文里才有意义的类型」,枚举解决「取值有限且不可变」的状态建模。本篇沿着这条主线,把语法背后的字节码与设计取舍一次讲透。
一、抽象类:把「不完整的实现」合法化1.1 abstract 的基本语法与约束被 abstract 修饰的方法没有方法体,被 abstract 修饰的类不能 new。两者之间是一条单向约束:有抽象方法的类必须声明为抽象类,但抽象类完全可以没有任何抽象方法。后者的用意是「这个类表达的是一个概念,不允许直接落地成对象」,例如 HttpServlet 即便没有抽象方法也不允许直接实例化。
...
本文是《Java 从入门到精通》系列第三篇,建议先掌握第二篇的变量、运算符与流程控制。全文围绕”JVM 到底怎么实现面向对象”展开,不只是罗列语法。文中所有代码均基于 JDK 17 语法,涉及 JDK 16+ 特性的地方会单独标注。示例代码可直接复制到任意 main 方法中运行验证。
一、从面向过程到面向对象1.1 两种编程思维的根本差异面向过程(Procedure Oriented)关注的是步骤:把事情拆成一、二、三几个动作,用函数把动作串起来,数据以参数形式在函数间流动。C 语言是典型代表。业务膨胀后有两个致命问题:数据与操作数据的逻辑分散各处,改一次数据结构所有函数都要改;函数靠参数耦合,调用链变长后排查问题如同走迷宫。
面向对象(Object Oriented)关注的是职责:先找出业务中有哪些”事物”,每个事物持有哪些数据、能提供哪些行为,再把数据与行为打包成整体。程序运行时,本质上就是一堆对象在互相发消息。最大收益是把变化关进笼子里——数据结构的修改被限制在类内部,对外契约(方法签名)不变,调用方无需感知。
用一个转账场景感受一下差别。面向过程写法里,账户数据是一组游离的变 ...
很多初学者的学习路径是:背下 int 是 4 个字节、float 精度低、switch 能匹配 String,然后就开始写业务代码。直到某天线上金额对不上、循环多跑了一轮、i = i++ 的结果和想象的不一样、Integer.MAX_VALUE + 1 变成了负数,才意识到”背下来的语法”和”真正理解的机制”之间隔着一条鸿沟。本篇不打算把语法手册再抄一遍,而是把每一条语法规则背后的内存布局、二进制编码、字节码执行顺序挖出来,让你在面试中被追问”为什么”时,依然能答得上来。
一、标识符、关键字、保留字与代码规范1.1 标识符的合法性与命名约定标识符(identifier)是我们给类、方法、变量、包起的名字。Java 语言规范对标识符的硬性约束只有三条:可以由字母、数字、下划线 _、美元符 $ 组成;不能以数字开头;不能与关键字(keyword)重名。这里最容易踩的第一个坑是——中文其实可以作为标识符,因为 Java 的字符集是 Unicode,int 年龄 = 18; 是能编译通过的,但没有任何团队会允许你这么做。
真正有约束力的是命名约定(convention),它不是编译器强制的, ...
之前写过浏览器渲染原理与前端性能优化,回答的是”资源到了浏览器之后,怎么跑得快”;这篇补上另一半——资源是怎么被高效造出来的。项目从几十个模块膨胀到几千个之后,Webpack 时代的冷启动动辄一两分钟、改一行代码等半天才热更新,开发体验肉眼可见地垮掉。Vite 用”启动时不打包、按需编译、预构建依赖”三板斧把冷启动压到秒级。本文把它的核心机制一次讲透:依赖预构建、HMR 原理与失效排查、生产期为什么换 Rollup、以及一份可以直接抄的工程化优化清单。
一、为什么是 Vite:先看清 Webpack 慢在哪Webpack dev 模式的工作方式是“先打包,再启动”:必须从入口出发,把整个模块图解析、转换、拼接成一个或多个 bundle,服务器才能响应第一个请求。项目越大,这个”打包”阶段越长——而你在浏览器里第一眼只看到了登录页。
Vite 的思路完全相反:“先启动,再按需编译”。dev 阶段它根本不打包业务代码,而是启动一个开发服务器,浏览器请求哪个模块,就实时编译哪个模块返回原生 ESM。页面只加载用到的模块,项目再有 1 万个模块,冷启动时间几乎不涨。
维度
Webp ...
很多人学 Java 的第一天是这样度过的:下载 JDK、一路点下一步、配环境变量、写个 HelloWorld、控制台打印出 Hello, World!,然后觉得自己已经”入门”了。可是一旦换台电脑、换个系统、接手一个 Maven 工程,问题就接踵而至:javac 不是内部或外部命令、UnsupportedClassVersionError、ClassNotFoundException、NoClassDefFoundError、依赖下不下来、jar 跑不起来……每一个都能卡住新手一整天。根本原因在于:绝大多数教程只教”怎么做”,从不讲”为什么这么做”。本系列的目标就是把这条底层链路彻底打通,让你知其然更知其所以然。
一、Java 技术体系全景1.1 JDK、JRE、JVM 三者到底是什么关系这是面试与自学中最经典的开场题,也是理解整个 Java 生态的地基。三者的关系可以用一句话概括:JVM 是执行引擎,JRE 是运行环境,JDK 是开发工具包,逐层包含。
12345678910111213141516┌───────────────────────────────────────── ...
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,负责定时器、 ...













