前言 Java 作为一门历史悠久的编程语言,一直在不断演进。本文按照 LTS(长期支持)版本 组织,介绍 Java 8 到 Java 25 的重要新特性,帮助开发者快速了解每个版本可以用哪些功能。 💡 每个特性后标注的版本号表示该特性正式可用的最低版本。 Java 8 LTS (2014) - 里程碑式更新 Java 8 是 Java 历史上最重要的版本,引入了函数式编程的核心概念。 Lambda 表达式 是什么? 一种简洁的匿名函数写法,把"行为"像数据一样传递。 // ❌ 传统写法 Collections.sort(list, new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.compareTo(s2); } }); // ✅ Lambda 写法 Collections.sort(list, (s1, s2) -> s1.compareTo(s2)); // ✅ 方法引用 Collections.sort(list, String::compareTo); Lambda 语法: (参数1, 参数2) -> { return 表达式; } // 完整形式 (s1, s2) -> s1.compareTo(s2) // 单行可省略 return 和大括号 s -> s.toUpperCase() // 单参数可省略括号 () -> System.out.println("Hello") // 无参数 Stream API 是什么? 对集合数据进行流水线式处理的 API。 ...
Mysql Technical Blog
MySQL 技术要点 一、MySQL 架构 MySQL 采用分层架构设计,主要分为 Server 层 和 存储引擎层。 1.1 Server 层 组件 职责 连接器 管理客户端连接、身份认证、权限校验 分析器 词法分析(识别关键字、表名、列名)、语法分析(构建语法树) 优化器 生成执行计划、选择最优索引、决定 JOIN 顺序 执行器 调用存储引擎 API 执行查询,返回结果集 1.2 存储引擎层 MySQL 支持插件式存储引擎,常用引擎对比: 引擎 事务支持 锁粒度 适用场景 InnoDB ✓ 行锁 OLTP、高并发读写 MyISAM ✗ 表锁 只读或读多写少 Memory ✗ 表锁 临时表、缓存 InnoDB architecture 二、索引机制 2.1 索引数据结构 类型 特点 适用场景 B+ Tree 有序、支持范围查询、树高稳定 主键索引、普通索引 Hash O(1) 查找、不支持范围查询 等值查询(Memory 引擎) Full-Text 倒排索引、支持自然语言搜索 全文检索 2.2 索引分类 按物理存储: ...
DDD Strategic and Tactical Design
领域驱动设计(DDD) 领域驱动设计(Domain-Driven Design, DDD)是一种以领域模型为核心的软件开发方法论,旨在应对复杂业务系统的设计与演进。DDD 分为战略设计和战术设计两个层面:战略设计关注系统整体的边界划分与团队协作模式,战术设计则聚焦于领域模型的具体实现模式。 1. 战略设计 战略设计的目标是从整体上划分系统的限界上下文(Bounded Context),并在每个上下文中发展出清晰一致的通用语言(Ubiquitous Language)。 1.1 领域与子域 领域(Domain):系统所关注的问题范围,代表业务活动和规则的集合。 子域(Subdomain):领域的组成部分,将复杂业务划分为更小、更聚焦的单元。 子域通常分为三类: 类型 说明 核心域(Core Domain) 业务的核心竞争力所在,需要重点投入 支撑域(Supporting Subdomain) 支撑核心业务运作,但非核心竞争力 通用域(Generic Subdomain) 通用功能,可采用现成方案 领域分析发生在问题空间(Problem Space),而领域模型的设计与实现属于解决方案空间(Solution Space): 问题空间:识别业务目标和边界,关注"做什么"。 解决方案空间:将需求转化为可实现的设计和模型,关注"怎么做"。 1.2 限界上下文(Bounded Context) 限界上下文是领域模型的语义边界。在该边界内,所有的概念、对象和规则都有明确且一致的含义。 同一个术语在不同上下文中可能代表不同含义,而在同一上下文中则保持语义一致。 例如,“账户"在用户上下文中可能指用户登录凭证,而在财务上下文中则指资金账户。限界上下文的划分有助于避免模型的混淆和污染。 1.3 通用语言(Ubiquitous Language) 通用语言是限界上下文内部领域专家与开发人员共享的统一语言。它通过领域模型来表达业务规则、行为和约束。 每个限界上下文都有自己独立的通用语言 通用语言不能跨上下文混用 代码、文档、沟通都应使用通用语言 1.4 上下文映射(Context Mapping) 当不同的限界上下文需要交互时,必须通过上下文映射来完成语言的"翻译"和语义对齐。上下文映射定义了上下文之间的关系、通信模式以及模型转换方式。 常见的上下文映射模式包括: 模式 说明 合作关系(Partnership) 两个团队共同协调开发,相互依赖 共享内核(Shared Kernel) 共享部分模型代码,需谨慎管理 客户-供应商(Customer-Supplier) 上游供应、下游消费,下游可提需求 遵奉者(Conformist) 下游完全遵从上游模型 防腐层(ACL) 下游建立转换层隔离上游模型 开放主机服务(OHS) 上游提供标准化协议供多方使用 发布语言(Published Language) 使用标准化的数据交换格式 1.5 防腐层(Anti-Corruption Layer) 下游上下文在使用上游上下文的数据或服务时,应建立一个防腐层(ACL)。防腐层负责: ...
Mysql Slow Query Filesort Optimization
IM 消息拉取接口的 SQL 优化记录 1. 背景与问题 在开发 IM 系统的“拉取历史消息”功能时,我发现当单张消息表(messages)的数据量达到百万级,且某个热点会话(conversation_id)拥有超过 10 万条消息时,接口响应出现明显卡顿。 环境信息: 数据库:MySQL 8.0+ (InnoDB) 数据量:单表 100 万行,大会话 10 万条信息 场景:用户查看最新的 20 条消息(Top-N 查询) 慢查询 SQL: SELECT * FROM messages WHERE conversation_id = 'chat_hot' ORDER BY seq DESC LIMIT 20; 2. 排查过程 2.1 慢日志抓取 开启 MySQL 慢查询日志(log_output=FILE, long_query_time=0.1),捕获到该语句的执行情况: # Query_time: 0.148528 Lock_time: 0.000003 Rows_sent: 20 Rows_examined: 151676 异常点:为了返回 20 条数据,MySQL 实际扫描了 15 万行数据。扫描/返回比极低,说明索引效率存在严重问题。 2.2 执行计划分析 (EXPLAIN) 执行 EXPLAIN 查看当前索引使用情况: 分析结论: 当前表只有单列索引 idx_conversation。 MySQL 虽然能通过索引快速定位到该会话的所有消息(15万条)。 但由于索引中没有包含排序字段 seq,MySQL 必须将这 15 万条数据加载到 Server 层的内存(Sort Buffer)中进行文件排序(Filesort),如果Sort Buffer不够大还会用到磁盘。 排完序后,再截取前 20 条。这一步消耗了大量的 CPU 和内存带宽。 3. 优化方案 为了消除 Using filesort,需要利用 B+ 树索引的天然有序性。 ...
Kafka Design Philosophy
深入理解 Apache Kafka 设计哲学 前言 Kafka 是一个分布式流处理平台,但它更像是 分布式提交日志 而非传统的消息队列。这种设计哲学贯穿了 Kafka 的每一个核心组件。本文将深入剖析 Kafka 的设计精髓。 一、存储层设计:为磁盘而生 1.1 日志结构存储 Kafka 采用 只追加写(Append-Only) 的日志结构,这带来了几个关键优势: 特性 传统数据库 (B-Tree) Kafka (Log) 写入复杂度 O(log N) O(1) 读取复杂度 O(log N) O(1) 磁盘访问模式 随机 I/O 顺序 I/O 适合场景 随机查询 流式处理 1.2 顺序 I/O 的威力 对于磁盘来说,O(log N) 并不等同于"接近常数时间"。每次磁盘寻道约需 10ms,且无法并行执行。即使少量随机访问也会导致性能急剧下降。 顺序写磁盘: ~600MB/s 随机写磁盘: ~100KB/s 差距: 约 6000 倍! 1.3 拥抱操作系统 Page Cache Kafka 不在 JVM 堆内存中维护缓存,而是将数据直接写入操作系统的 Page Cache: graph TD subgraph JVM [JVM 进程] Logic[业务逻辑 不缓存数据] end subgraph Kernel [操作系统内核空间] PC(Page Cache 利用空闲内存 / 自动管理) FS[(磁盘文件系统)] end Logic -->|直接写入| PC PC -.->|异步刷盘 / 后写技术| FS style JVM fill:#f9f9f9,stroke:#333,stroke-width:2px style Kernel fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style PC fill:#b3e5fc,stroke:#0277bd这样做的好处: ...
Rockscache Consistency
深入解析 RocksCache:如何优雅地解决缓存与数据库一致性问题 本文深入剖析 RocksCache 的设计思想与核心实现,带你理解这个首创的缓存一致性解决方案。 前言 在分布式系统中,缓存是提升性能的利器,但也是一致性问题的重灾区。你是否曾经遇到过这样的困扰: 明明更新了数据库,为什么缓存里还是旧数据? 用了「先更新DB,再删缓存」策略,为什么还是会出现不一致? 如何在保证一致性的同时,还能保持高性能? 今天介绍的 RocksCache,是一个来自 DTM Labs 的开源项目,它通过一套精巧的设计,在不引入版本号的前提下,优雅地解决了缓存与数据库的一致性难题。 一、经典的缓存一致性问题 1.1 常见的缓存策略 最常用的缓存管理策略是 Cache-Aside(旁路缓存): 读取:先查缓存 → 缓存命中则返回 → 未命中则查DB → 写入缓存 → 返回 更新:更新DB → 删除缓存 这个策略看似简单,却隐藏着一个致命的并发问题。 1.2 并发场景下的数据不一致 考虑以下时序: 时间 →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ 线程A(读请求): 查DB(v1) ─────────────────────────────→ 写缓存(v1) (网络延迟) 线程B(写请求): 更新DB(v2) → 删除缓存 问题:线程A 查询到 v1 后,发生了网络延迟。此时线程B 完成了更新并删除缓存。但线程A 的写缓存操作在删除之后执行,导致缓存中存储了旧值 v1。 这就是著名的 “删除后写入” 问题,常规的「先更新DB再删缓存」策略无法解决。 1.3 传统解决方案的局限 方案 描述 缺点 延迟双删 删除缓存后,延迟一段时间再删一次 延迟时间难以确定,仍有不一致窗口 版本号 每条数据带版本号,写入时比较版本 侵入业务,改造成本高 分布式锁 读写都加锁 性能差,热点数据成为瓶颈 订阅 binlog 通过 Canal 等订阅 DB 变更 架构复杂,延迟较高 有没有一种方案,既能保证一致性,又不侵入业务,还能保证高性能? ...
K8s Troubleshooting
前言 最近在本地虚拟机环境(CentOS 7)搭建 Kubernetes 集群运行微服务 Demo 时,遇到一个非常诡异的“灵异事件”。 起因:由于宿主机休眠,我挂起(Suspend)了一段时间虚拟机。 现象:恢复运行后,集群状态看起来一切正常(Node Ready,Pod Running),但访问 NodePort 暴露的服务时,直接报 502 Bad Gateway。 排查过程极其曲折,从 HTTP 协议一路查到 Linux 内核参数,最终发现是操作系统在网络重置时的“安全机制”坑了 Kubernetes。本文记录了完整的排查思路,希望能帮大家避坑。 🕵️♂️ 第一阶段:表象排查(Application Layer) 首先,我尝试访问前端服务: curl -I http://192.168.6.141:30007/ # HTTP/1.1 502 Bad Gateway 初步分析: 502 通常意味着网关(Service/Kube-proxy)找不到后端 Pod,或者连接被拒绝。 检查 Pod 状态:kubectl get pods -A 显示所有 Pod 均为 Running 且 Ready (1/1)。应用没挂。 检查 Service 关联:kubectl get ep frontend 显示 Endpoints 存在且 IP 正确(如 10.244.104.23:8080)。 看日志:查看 Frontend Pod 日志,没有报错,甚至显示 Server started。 奇怪点:应用活着,配置没动过,重启前好好的,恢复后就断了。 ...