小菜鸟

java菜鸟号正在起航

HBase 深度解析:Bigtable 的开源实现,PB 级结构化数据的答案

数据量到了 PB 级,关系型数据库就扛不住了——分库分表太复杂、单表查询太慢、列扩展要改 schema。

HBase 就是为这个场景设计的:一个建立在 HDFS 之上的分布式列式数据库,专治”海量结构化数据 + 高并发读写”。

它是 Google Bigtable 的开源实现——数据模型一样、架构一样、连核心概念都是对应的。

HBase 的数据模型:一张三维的稀疏大表

HBase 的数据模型和 Bigtable 一样,可以理解为:

一个多维的、稀疏的、分布式的映射表。

(RowKey, Column Family:Qualifier, Timestamp) → Value

四个核心概念:

概念 说明
RowKey 行唯一标识,按字典序排序,所有查询依赖它
列族(Column Family) 表创建时定义,是存储和权限的基本单位
列(Qualifier) 列族下的具体字段,可以动态添加
时间戳(Timestamp) 每个单元格可存多个版本,按时间戳区分

RowKey 为什么重要? HBase 没有二级索引,所有查询要么靠 RowKey 精确匹配,要么靠 RowKey 范围扫描。RowKey 设计直接决定了查询能不能跑得快。

列族为什么重要? 同一列族的数据物理上存在一起。查询时只扫目标列族,不用读整行。

系统架构:Master 管调度,RegionServer 存数据

HBase 是主从架构,四个角色分工明确:

系统架构

角色 管什么
HMaster 表创建/删除、Region 分配、负载均衡、故障恢复
RegionServer 处理读写请求、管理 Region、刷写 MemStore、合并 StoreFile
ZooKeeper Master 选举、监控 RegionServer 上下线、存元数据入口
HDFS 持久化存储(StoreFile + HLog)

关键设计:HMaster 不参与读写请求。 客户端直接连 RegionServer 读写数据,Master 只做调度和管理,压力小。

阅读全文 »

Hive SQL 优化:慢 SQL 排查路径 + 对应的优化手段

一条 Hive SQL 跑得慢,原因可能出在任何一个环节——全表扫描、小文件太多、Join 数据倾斜、Reduce 数量不对、内存不够。

优化的前提是先定位问题。排查路径是:

EXPLAIN 看执行计划 → 看有没有全表扫描/不合理 Join → 看数据分布是否倾斜 → 调参数

先看执行计划:EXPLAIN

优化之前,先用 EXPLAIN 看 Hive 打算怎么跑这条 SQL。

EXPLAIN SELECT max(sal), deptno FROM emp GROUP BY deptno;

执行计划里重点关注:

看什么 说明
阶段依赖 Stage 之间的顺序,看有没有不必要的依赖
操作符树 TableScan → Filter → GroupBy → …,看有没有全表扫描
是否有 Reduce 阶段 有些查询根本不需要 Reduce(比如 SELECT *),强行走 MR 就慢了
分区过滤 分区表有没有走分区过滤

EXPLAIN 能告诉你”Hive 打算怎么执行”,然后你才能判断”这个计划有没有问题”。

现象1:全表扫描 → 加分区过滤 + 列裁剪

现象: 执行计划里出现 TableScan 扫了整张表,Filter 条件里没有分区列。

原因: 查询时没带分区条件,或者分区条件写错了。

解决:

-- 错误: 全表扫描
SELECT * FROM orders WHERE amount > 100;

-- 优化:加上分区条件
SELECT * FROM orders WHERE dt='2026-07-01' AND amount > 100;

--  优化:只查需要的列,不要 SELECT *
SELECT order_id, amount FROM orders WHERE dt='2026-07-01';

相关配置:

-- 开启 Fetch 抓取,简单查询不走 MR
SET hive.fetch.task.conversion=more;

现象2:小数据量也跑 MR → 开本地模式

现象: 数据量只有几 MB,但任务还是要启动 MapReduce,光启动就花了 30 秒。

原因: Hive 默认不管数据大小都走 MR。

解决: 开启本地模式,小数据直接在单节点处理。

SET hive.exec.mode.local.auto=true;
SET hive.exec.mode.local.auto.inputbytes.max=134217728;  -- 128MB

现象3:Join 慢 → 小表走 MapJoin,大表处理倾斜

现象: Join 阶段特别慢,Reduce 卡在 99% 不动。

原因1:小表 Join 大表,但没走 MapJoin

小表数据量不大(< 25MB),完全可以加载到内存里做 MapJoin,避免 Shuffle。

-- 自动开启 MapJoin
SET hive.auto.convert.join=true;
SET hive.mapjoin.smalltable.filesize=25000000;  -- 25MB

原因2:大表 Join 大表,数据倾斜

某个 Join Key 的数据量特别大(比如 user_id='guest' 有 1 亿条),全挤到一个 Reduce 任务。

解决方案:

① 开启倾斜 Join 自动优化

SET hive.optimize.skewjoin=true;
SET hive.skewjoin.key=100000;

② 手动加盐分散

-- 给倾斜 key 加随机前缀,分散到多个 Reduce
SELECT /*+ SKEWJOIN('orders') */
  t1.user_id, t2.order_id
FROM users t1
JOIN orders t2
ON CASE WHEN t1.user_id IN ('hot_id1', 'hot_id2')
        THEN concat(cast(rand()*10 as int), '_', t1.user_id)
        ELSE t1.user_id END = t2.user_id;

原因3:Join 顺序不对

Hive 从右到左执行 Join,大表放右边可以减少中间数据量。

-- 错误: 大表在左
SELECT * FROM big_table JOIN small_table ON ...;

-- 正确: 大表在右
SELECT * FROM small_table JOIN big_table ON ...;

现象4:Group By 慢 → Map 端预聚合 + 处理倾斜

现象: GROUP BY 阶段卡住,某个 Reduce 处理的数据量远超其他。

原因1:没有开启 Map 端聚合

所有数据直接 Shuffle 到 Reduce,没有在 Map 端做预聚合。

SET hive.map.aggr=true;

原因2:分组键倾斜

某个分组值的数据量特别大(比如 city='北京' 有 1000 万条)。

解决: 加盐两次聚合

-- 第一步:加盐局部聚合
SELECT substr(city, 2) AS city, SUM(amount) AS total
FROM (
  SELECT concat(cast(rand()*10 as int), '_', city) AS city, amount
  FROM orders
) t
GROUP BY city;

-- 第二步:去盐全局聚合
SELECT city, SUM(total) AS final_total
FROM 上一步结果
GROUP BY city;

现象5:任务整体慢 → 调并行度和 Reduce 数量

现象: 任务耗时很长,但看执行计划发现多个 Stage 是独立的,串行执行浪费时间。

解决:开并行执行

SET hive.exec.parallel=true;
SET hive.exec.parallel.thread.number=8;

现象: Reduce 阶段太慢,或者 Reduce 数量不合理。

Reduce 数量的决定因素:

Reduce 数 = 输入数据量 / hive.exec.reducers.bytes.per.reducer

调小 bytes.per.reducer → Reduce 数量增多 → 并行度提高
调大 bytes.per.reducer → Reduce 数量减少 → 小文件减少

-- 调整每个 Reduce 处理的数据量(默认 256MB)
SET hive.exec.reducers.bytes.per.reducer=128000000;  -- 128MB

查找算法全解析:从线性到哈希的高效搜索策略

查找是数据处理中的核心操作,目的是从数据集合中找到满足特定条件的元素。根据数据结构和搜索策略的不同,查找算法的效率差异显著。本文详细解析线性查找、树查找、哈希查找等经典算法,涵盖原理、实现、复杂度及适用场景。

线性查找:遍历式搜索

线性查找是最基础的查找方式,通过逐个遍历数据元素实现搜索,无需数据有序。

1. 顺序查找(Sequential Search)

原理

遍历数据集合,将每个元素与目标值比较,找到则返回位置,否则返回不存在。

代码实现
public static int sequentialSearch(int[] arr, int target) {
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == target) {
            return i; // 找到目标,返回索引
        }
    }
    return -1; // 未找到
}
性能分析
  • 时间复杂度:
    • 最好:O (1)(目标在第一个位置)。
    • 最坏 / 平均:O (n)(需遍历全部元素)。
  • 空间复杂度:O (1)(仅需常量空间)。
适用场景
  • 无序数据集合(如未排序的数组、链表)。
  • 数据量小(大规模数据效率低)。

2. 二分查找(Binary Search)

原理
阅读全文 »

会话管理:Cookie 与 Session 详解

HTTP 协议是无状态的,即服务器无法自动关联多次请求是否来自同一客户端。为了实现用户登录状态保持、购物车等功能,需要通过CookieSession技术维持客户端与服务器之间的会话状态。本文将详细解析这两种技术的原理、使用方法及核心区别。

会话管理的必要性

无状态的 HTTP 协议导致服务器无法识别连续请求的关联性。例如:

  • 用户在电商网站添加商品到购物车后,切换页面时服务器无法记住之前的选择;
  • 用户登录后,访问其他页面时服务器无法验证其已登录状态。

会话管理技术通过在客户端或服务器端存储标识信息,将多次请求关联为一个 “会话”,从而实现状态保持。

Cookie:客户端存储的会话标识

Cookie 是服务器发送给客户端的小型文本数据,由浏览器存储在本地(文件或内存中)。当客户端再次访问同一服务器时,浏览器会自动携带 Cookie 到请求中,使服务器识别客户端身份。

  1. 服务器发送 Cookie:服务器在响应中通过 Set-Cookie 头字段将 Cookie 发送给浏览器。
    示例响应头:Set-Cookie: userId=123; Path=/; Max-Age=3600
  2. 浏览器存储 Cookie:浏览器将 Cookie 保存到本地(内存或磁盘,取决于有效期)。
  3. 客户端携带 Cookie:客户端再次请求同一服务器时,通过 Cookie 头字段将 Cookie 回传给服务器。
    示例请求头:Cookie: userId=123
阅读全文 »

Kafka 消息丢失与重复消费问题全解析:原因与解决方案

Kafka 作为分布式消息系统,消息的可靠性(不丢失)和一致性(不重复)是核心需求。但在实际使用中,由于配置不当、网络异常或故障处理等原因,可能出现消息丢失或重复消费的问题。本文将从消息丢失重复消费两个维度,详细分析原因及解决方案。

消息丢失问题

消息丢失可能发生在生产者发送Kafka 集群存储消费者消费三个环节,需针对性解决。

生产者端消息丢失

原因分析
  • acks 配置不当:
    • acks=0:生产者不等待 Kafka 确认,直接发送下一条消息。若 Broker 崩溃或网络中断,消息可能未写入磁盘而丢失。
    • acks=1:仅 Leader 写入成功后确认。若 Leader 写入后未同步到 Follower 就崩溃,新 Leader(从 Follower 选举)会丢失该消息。
  • 异步发送缓冲区溢出
    异步发送时,消息先存入缓冲区(buffer.memory),若缓冲区满且 block.on.buffer.full=false(默认 true 已废弃,由 max.block.ms 替代),生产者会丢弃消息。
  • 未启用重试机制
    网络波动等临时故障导致发送失败时,若 retries=0,生产者不会重试,消息丢失。
解决方案
  • 合理设置 acks
    关键业务场景设置 acks=-1(或 acks=all),确保 Leader 和所有 ISR 中的 Follower 均写入成功后才确认,从源头避免丢失。
  • 优化异步发送配置:
    • 设置 max.block.ms=60000(默认 60 秒),缓冲区满时阻塞等待,而非丢弃。
    • 调整 buffer.memory(默认 32MB)和 batch.size(默认 16KB),避免缓冲区频繁满溢。
  • 启用重试并合理配置:
    • 设置 retries=3(默认重试次数极高,可根据业务调整),配合 retry.backoff.ms=100(重试间隔),应对临时故障。

Kafka 集群(消息队列)消息丢失

原因分析
阅读全文 »
0%