Hive数仓分层——国内大数据就业洞察

Scala 来写 Spark 清洗逻辑,(因为 Scala 是强类型的,代码更严谨,且是 Spark 的原生语言)。

一、 数据仓库整体设计

数据来源

数据通过爬虫(如 Python 脚本)获取,来源包括智联招聘、Boss 直聘、拉勾网等。

分层逻辑

  1. ODS 层 (原始数据层):存储爬虫抓下来的原始 CSV/JSON 数据,保持原貌,按天分区。
  2. DWD 层 (明细数据层):核心清洗层。(用spark清洗)将"15k-25k"这样的字符串薪资转换为数字,统一城市名称,清洗学历和经验要求。
  3. DWS 层 (汇总数据层):按业务主题聚合。例如按"城市"、"学历"、"公司性质"进行统计。
  4. ADS 层 (应用数据层):直接面向报表的数据。例如"大数据岗位薪资城市排行榜"、"学历对薪资的影响分析"。

**二、**总体工作流 (Workflow)

  1. 爬虫 (Python)
    1. 抓取数据 -> 存为 JSON Lines 文件(jobs.json,一行一个 JSON 对象)。
  2. ODS 层 (Hive)
    1. 建表存原始 JSON 字符串。
    2. LOAD DATA 加载数据。
  3. 清洗环节 (Spark - Scala)
    1. 读取 ODS 表 -> 解析 JSON -> 清洗数据 (正则/类型转换) -> 生成 DataFrame。
    2. 将结果写入 DWD 表。
  4. DWD 层 (Hive)
    1. 只负责建表(定义最终结构),接受 Spark 写入的数据。
  5. DWS/ADS 层 (Hive SQL)
    1. 写 SQL 聚合分析。

三、各层详细操作指南(此处为操作示例,仅作参考)

Step 1: ODS 层 (保持原样**,**操作数据存储层)

设计目标:原样存储爬虫抓取的岗位数据,防止数据丢失。

数据格式:文本文件,以逗号或制表符分隔。

这一层不需要变,还是存原始 JSON 串。

复制代码
-- Hive SQL
CREATE TABLE IF NOT EXISTS ods.ods_job_csv (
    json_record STRING COMMENT '原始数据'
)
PARTITIONED BY (dt STRING)
STORED AS TEXTFILE;

-- 如果表已存在则删除
DROP TABLE IF EXISTS ods.ods_job_csv;

-- 创建符合CSV结构的表
CREATE TABLE IF NOT EXISTS ods.ods_job_csv (
    position_name    STRING COMMENT '职位名称',
    company_name     STRING COMMENT '公司名称',
    experience_req   STRING COMMENT '工作经验要求',
    education_req    STRING COMMENT '学历要求',
    work_location    STRING COMMENT '工作地点',
    salary           STRING COMMENT '薪酬',
    publish_time     STRING COMMENT '发布时间',
    job_description  STRING COMMENT '职位描述或任职要求',
    update_time      STRING COMMENT '更新时间',
    city_district    STRING COMMENT '城市地区',
    company_welfare  STRING COMMENT '公司福利',
    company_size     STRING COMMENT '公司规模',
    company_type     STRING COMMENT '公司类型',
    company_tags     STRING COMMENT '公司标签',
    detail_json      STRING COMMENT '区经验学历'
)
COMMENT '原始大数据岗位数据ODS层'
PARTITIONED BY (dt STRING)
STORED AS TEXTFILE;

-- 加载数据
LOAD DATA LOCAL INPATH '/data/job_all.csv' INTO TABLE ods.ods_job_csv PARTITION(dt='2025-12-10');
Step 2: DWD 层 (只建表)

先定义好清洗后的格式。

复制代码
-- Hive SQL CREATE TABLE IF NOT EXISTS dwd.dwd_job_detail (

job_name STRING,

company_name STRING,

min_salary DOUBLE,

max_salary DOUBLE,

city STRING, education STRING )

PARTITIONED BY (dt STRING) STORED AS PARQUET; -- 推荐 Parquet
Step 3: Spark 清洗 (核心 - Scala 实现)

需要创建一个 Scala Object(例如 JobDataEtl.scala)。

Scala 处理 JSON 的核心逻辑

ODS 里是一列字符串,我们需要用 Spark SQL 的内置函数 get_json_object 把变成多列。

复制代码
package com.yourproject.etl`

`import org.apache.spark.sql.{SparkSession, SaveMode}`
`import org.apache.spark.sql.functions._`

`object JobDataEtl {`
`  def main(args: Array[String]): Unit = {`

`    // 1. 初始化 SparkSession (支持 Hive)`
`    val spark = SparkSession.builder()`
`      .appName("JobDataCleaning_Scala")`
`      .enableHiveSupport() // 必须开启`
`      .getOrCreate()`

`    import spark.implicits._ // 导入隐式转换,可以使用 $"column" 写法`

`    val dt = "2025-10-07" // 可以通过 args(0) 传入`

`    // 2. 读取 ODS 层数据`
`    val odsDf = spark.sql(s"SELECT json_record FROM ods.ods_job_json WHERE dt='$dt'")`

`    // 3. 解析 JSON & 数据清洗`
`    // 假设 JSON 结构是: {"job": "大数据开发", "salary": "15-25K", "company": "阿里", "city": "杭州"}`
`    val cleanedDf = odsDf`
`      // 3.1 第一步:先把 JSON 字段抠出来`
`      .select(`
`        get_json_object($"json_record", "$.job").as("raw_job_name"),`
`        get_json_object($"json_record", "$.salary").as("raw_salary"),`
`        get_json_object($"json_record", "$.company").as("company_name"),`
`        get_json_object($"json_record", "$.city").as("raw_city"),`
`        get_json_object($"json_record", "$.education").as("raw_education")`
`      )`
`      // 3.2 第二步:对字段进行精细化清洗 (正则、类型转换)`
`      .withColumn("min_salary", regexp_extract($"raw_salary", "(\\d+)-", 1).cast("double") * 1000)`
`      .withColumn("max_salary", regexp_extract($"raw_salary", "-(\\d+)K", 1).cast("double") * 1000)`
`      .withColumn("job_name", trim($"raw_job_name")) // 去除首尾空格`
`      .withColumn("city", split($"raw_city", "-").getItem(0)) // "北京-朝阳" -> "北京"`
`      .withColumn("education", `
`         when($"raw_education".contains("本"), "本科")`
`        .when($"raw_education".contains("硕"), "硕士")`
`        .otherwise("其他")`
`      )`
`      // 3.3 第三步:选出最终符合 DWD 表结构的列`
`      .select(`
`        $"job_name",`
`        $"company_name",`
`        $"min_salary",`
`        $"max_salary",`
`        $"city",`
`        $"education"`
`      )`

`    // 4. 写入 DWD 层`
`    // 注意:Spark 写入 Hive 分区表时,最好配置 dynamic partition`
`    spark.conf.set("hive.exec.dynamic.partition", "true")`
`    spark.conf.set("hive.exec.dynamic.partition.mode", "nonstrict")`

`    // 将数据写入 DWD 表的指定分区`
`    // 这里的 logic 是:我们在 DataFrame 里没有 dt 列,但要写到 dt='$dt' 分区`
`    // 方法 A:直接 insertInto (要求 DataFrame 列顺序和 Hive 表完全一致,除了分区列)`
`    println(s"正在写入数据到 DWD 层,分区: $dt ...")`
    
`    cleanedDf.write`
`      .mode(SaveMode.Overwrite)`
`      .insertInto(s"dwd.dwd_job_detail partition(dt='$dt')") `
`      // 注意:上面的写法取决于 Spark 版本,更稳妥的方式是把 dt 加到 DataFrame 最后,然后 partitionBy("dt")`

`    /* 稳妥的写法:`
`    cleanedDf.withColumn("dt", lit(dt))`
`      .write`
`      .mode(SaveMode.Overwrite)`
`      .format("hive")`
`      .partitionBy("dt")`
`      .saveAsTable("dwd.dwd_job_detail")`
`    */`

`    println("清洗完成!")`
`    spark.stop()`
`  }`
`}`
`
Step 4 DWS 层(汇总数据层)

设计目标:按业务主题聚合,减少 ADS 层计算量。

主要主题:城市主题、岗位分类主题。

复制代码
create database if not exists dws;`

`-- 主题1:城市大数据岗位指标汇总表`
`CREATE TABLE dws.dws_city_job_summary (`
`    city            STRING,`
`    job_count       INT COMMENT '岗位数量',`
`    avg_salary      DOUBLE COMMENT '平均月薪',`
`    company_count   INT COMMENT '招聘公司数',`
`    top_education   STRING COMMENT '需求最多的学历'`
`)`
`PARTITIONED BY (dt STRING);`

`-- 数据聚合`
`INSERT OVERWRITE TABLE dws.dws_city_job_summary PARTITION (dt='2025-10-07')`
`SELECT`
`    city,`
`    COUNT(job_id) as job_count,`
`    AVG(avg_salary) as avg_salary,`
`    COUNT(DISTINCT company_name) as company_count,`
`    -- 这里用一个简单的逻辑取Top1学历,实际可用窗口函数`
`    '本科' as top_education `
`FROM dwd.dwd_job_detail`
`WHERE dt = '2025-10-07'`
`GROUP BY city;`

`-- 主题2:岗位分类汇总表(看哪类工作最赚钱)`
`CREATE TABLE dws.dws_category_job_summary (`
`    job_category    STRING,`
`    avg_salary      DOUBLE,`
`    avg_experience  DOUBLE,`
`    job_count       INT`
`)`
`PARTITIONED BY (dt STRING);`

`INSERT OVERWRITE TABLE dws.dws_category_job_summary PARTITION (dt='2025-10-07')`
`SELECT `
`    job_category,`
`    AVG(avg_salary) as avg_salary,`
`    AVG(experience_min) as avg_experience,`
`    COUNT(1) as job_count`
`FROM dwd.dwd_job_detail`
`WHERE dt = '2025-10-07'`
`GROUP BY job_category;`
`
Step5ADS 层(应用数据服务层)

设计目标:生成最终报表结果。

复制代码
create database if not exists ads;`

`-- 指标1:全国大数据岗位高薪城市 Top10`
`CREATE TABLE ads.ads_top10_salary_city (`
`    dt              STRING,`
`    rank            INT,`
`    city            STRING,`
`    avg_salary      DECIMAL(10,2),`
`    job_count       INT`
`);`

`INSERT OVERWRITE TABLE ads.ads_top10_salary_city`
`SELECT`
`    '2025-10-07' as dt,`
`    row_number() OVER (ORDER BY avg_salary DESC) as rank,`
`    city,`
`    CAST(avg_salary AS DECIMAL(10,2)),`
`    job_count`
`FROM dws.dws_city_job_summary`
`WHERE dt='2025-10-07' AND job_count > 10 -- 过滤掉样本太少的城市`
`LIMIT 10;`

`-- 指标2:学历的薪资回报率分析`
`CREATE TABLE ads.ads_education_salary_analysis (`
`    dt              STRING,`
`    education       STRING,`
`    avg_salary      DECIMAL(10,2),`
`    job_share_rate  DECIMAL(10,4) COMMENT '岗位占比'`
`);`

`INSERT OVERWRITE TABLE ads.ads_education_salary_analysis`
`SELECT`
`    '2025-10-07' as dt,`
`    education,`
`    CAST(AVG(avg_salary) AS DECIMAL(10,2)),`
`    CAST(COUNT(1) / SUM(COUNT(1)) OVER() AS DECIMAL(10,4)) as job_share_rate`
`FROM dwd.dwd_job_detail`
`WHERE dt='2025-10-07'`
`GROUP BY education;`
`

数据已经干净地躺在 DWD 表里了,接下来就可以回到 Hive 界面写 SQL 了。

复制代码
-- DWS 层聚合`
`INSERT OVERWRITE TABLE dws.dws_city_stat PARTITION(dt='2025-10-07')`
`SELECT city, avg(min_salary) `
`FROM dwd.dwd_job_detail `
`WHERE dt='2025-10-07' `
`GROUP BY city;`
`

**四、**Scala 项目开发小贴士

依赖管理

需要用 Maven 或 SBT 来管理依赖。记得引入 spark-core 和 spark-sql。

复制代码
<dependency>`
`    <groupId>org.apache.spark</groupId>`
`    <artifactId>spark-sql_2.12</artifactId>`
`    <version>3.x.x</version> <!-- 你的 Spark 版本 -->`
`</dependency>`
`

打包与运行

Scala 代码不能像 Python 那样直接 python code.py 运行。

  1. 步骤 1: 在 IDEA 里编写代码。
  2. 步骤 2: 用 Maven/SBT 把项目打成 jar 包(例如 etl-1.0.jar)。
  3. 步骤 3: 把 jar 包上传到服务器。
  4. 步骤 4: 提交任务:
复制代码
spark-submit \`
`  --class com.yourproject.etl.JobDataEtl \`
`  --master yarn \`
`  ./etl-1.0.jar`
`

总结

  • ODS: 存脏数据(JSON 串)。
  • Spark (Scala): 读 ODS -> 解析 JSON (get_json_object) -> 清洗逻辑 -> 写 DWD。
  • DWD: 存干净数据(Schema 明确)。
  • DWS/ADS: 纯 SQL 分析。
相关推荐
qq_348231856 小时前
每日复盘超短20251213
大数据
Sui_Network6 小时前
Sui 主网升级至 V1.61.2
大数据·前端·人工智能·深度学习·区块链
神算大模型APi--天枢6466 小时前
自主算力筑基 垂域模型破局:国产硬件架构下的行业大模型训练与微调服务实践
大数据·人工智能·深度学习·架构·硬件架构
九河云6 小时前
云计算+大数据+IoT:构建企业数字化底座的三大支柱
大数据·物联网·云计算
yumgpkpm6 小时前
hadoop集群搭建 (超详细) 接入Impala、Hive,AI 大模型的数据底座
hive·hadoop·mysql·zookeeper·flink·kafka·hbase
郑州光合科技余经理6 小时前
解决方案:全球化时代下的海外版外卖系统
大数据·开发语言·前端·javascript·人工智能·架构·php
武子康6 小时前
大数据-185 Logstash 7 入门实战:stdin/file 采集、sincedb/start_position 机制与排障
大数据·后端·logstash
派大鑫wink6 小时前
Python 大数据毕业设计:电影票房可视化分析系统(Flask+Echarts + 爬虫实战)
大数据·python·课程设计