Docker ComposeでHadoopとHiveを使ってみた
目的
プロジェクトでHadoopを利用するので、色々勉強したくて手軽に使える環境を作ってみました。
筆者の知識
- Dockerは使える
- Hadoop、Hiveは初めて使う
利用するデータ
警視庁が公開している交通事故統計情報を使います。2019年から2023年の全国の交通事故のデータが年度ごとのcsvファイルにまとまっています。今回は2019年から2021年の本票のデータを使用します。(2022年からCSVファイルの形式が変わっているため)
出典:
データの下準備
Hiveテーブルを作る際に年度でパーティションを切るために各年度のディレクトリ配下にファイルをおきます。パーティションについてはのちほどHiveテーブル作成とあわせて説明します。
traffic_accident_data/
- year=2019/
- honhyo_2019.csv
- year=2020/
- honhyo_2020.csv
- year=2021/
- honhyo_2021.csv
Docker Compose
x-hadoop-variables: &hadoop_environment
CORE-SITE.XML_fs.default.name: hdfs://namenode
CORE-SITE.XML_fs.defaultFS: hdfs://namenode
HDFS-SITE.XML_dfs.namenode.rpc-address: namenode:8020
HDFS-SITE.XML_dfs.replication: "1"
services:
hive:
image: apache/hive:3.1.3
hostname: hive
ports:
- 10000:10000
- 10002:10002
environment:
<<: *hadoop_environment
HIVE_VERSION: 3.1.3
SERVICE_NAME: hiveserver2
namenode:
image: apache/hadoop:3
hostname: namenode
command: ["hdfs", "namenode"]
ports:
- 9870:9870
environment:
<<: *hadoop_environment
ENSURE_NAMENODE_DIR: "/tmp/hadoop-hadoop/dfs/name"
datanode:
image: apache/hadoop:3
hostname: datanode
command: ["hdfs", "datanode"]
environment: *hadoop_environment
それぞれapache公式のDockerイメージから作成しました。環境変数は下記の公式ドキュメントを参考にしています。
HDFSにデータを転送する
docker compose cpコマンドでローカルのcsvをNameNodeコンテナにコピーします。
$ docker compose cp traffic_accident_data/ namenode:/tmp
NameNodeコンテナにログインします。
$ docker compose exec -it namenode /bin/bash
HDFSにCSVファイルをPUTします
// HDFSにディレクトリ作成
$ hdfs dfs -mkdir /hadoop
// CSVを転送
$ hdfs dfs -put ./traffic_accident_data/ /hadoop/
// 確認
$ hdfs dfs -ls -R /hadoop/traffic_accident_data
drwxr-xr-x - hadoop supergroup 0 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2019
-rw-r--r-- 1 hadoop supergroup 69767380 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2019/honhyo_2019.csv
drwxr-xr-x - hadoop supergroup 0 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2020
-rw-r--r-- 1 hadoop supergroup 56580583 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2020/honhyo_2020.csv
drwxr-xr-x - hadoop supergroup 0 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2021
-rw-r--r-- 1 hadoop supergroup 55851877 2024-09-26 14:40 /hadoop/traffic_accident_data/year=2021/honhyo_2021.csv
Hiveテーブルを作成する
Hiveサーバのコンテナにログインします。
$ docker compose exec -it hive /bin/bash
beelineコマンドでHiveサーバにログインします。
$ beeline -u jdbc:hive2://localhost:10000/
HQLを実行しHiveテーブルを作成します
CREATE TABLE IF NOT EXISTS traffic_accidents (
document_type INT,
prefecture_code INT,
police_station_code INT,
ticket_number VARCHAR(20),
accident_details VARCHAR(255),
deaths INT,
injuries INT,
route_code VARCHAR(10),
direction VARCHAR(10),
location_code VARCHAR(10),
municipality_code VARCHAR(10),
occurrence_year INT,
occurrence_month INT,
occurrence_day INT,
occurrence_hour INT,
occurrence_minute INT,
day_night VARCHAR(10),
weather VARCHAR(50),
terrain VARCHAR(50),
road_condition VARCHAR(50),
road_shape VARCHAR(50),
roundabout_diameter DECIMAL(5,2),
traffic_signal VARCHAR(10)
)
PARTITIONED BY (year STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://namenode/hadoop/traffic_accident_data/'
TBLPROPERTIES ("skip.header.line.count"="1");
いくつかポイントを解説すると、
CREATE TABLE IF NOT EXISTS traffic_accidents (
document_type STRING,
prefecture_code INT,
report_number STRING,
...
元のCSVのカラムが日本語なので独自に英語のカラム名を定義しています。
CSVのカラムが多いので今回は23カラムまでを取り込みました。
PARTITIONED BY (year STRING)
年度でパーティションを切るためにキーを指定しています。実際にパーティションにcsvをロードするのはこのあとに実行するクエリになります。
TBLPROPERTIES ("skip.header.line.count"="1");
CSVファイルの1行目をヘッダ行として無視して読み込みます.
テーブルを作成できたら年度ごとのCSVファイルをパーティションにロードします。
ALTER TABLE traffic_accidents ADD
PARTITION (year='2019') LOCATION 'hdfs://namenode/hadoop/traffic_accident_data/year=2019'
PARTITION (year='2020') LOCATION 'hdfs://namenode/hadoop/traffic_accident_data/year=2020'
PARTITION (year='2021') LOCATION 'hdfs://namenode/hadoop/traffic_accident_data/year=2021'
クエリ実行
年度ごとの事故件数
> SELECT year, COUNT(*) AS total_accidents
FROM traffic_accidents
GROUP BY year
ORDER BY year;
+-------+------------------+
| year | total_accidents |
+-------+------------------+
| 2019 | 381237 |
| 2020 | 309178 |
| 2021 | 305196 |
+-------+------------------+
2019年->2020年で事故件数が大きく減少してますね。
天候別の事故件数
> SELECT
weather,
SUM(CASE WHEN year = 2019 THEN weather ELSE 0 END) AS weather_sum_2019,
SUM(CASE WHEN year = 2020 THEN weather ELSE 0 END) AS weather_sum_2020,
SUM(CASE WHEN year = 2021 THEN weather ELSE 0 END) AS weather_sum_2021
FROM traffic_accidents
WHERE year IN (2019, 2020, 2021)
GROUP BY weather
ORDER BY weather;
+----------+-------------------+-------------------+-------------------+
| weather | weather_sum_2019 | weather_sum_2020 | weather_sum_2021 |
+----------+-------------------+-------------------+-------------------+
| 1 | 252210 | 202322 | 208656 |
| 2 | 162064 | 127722 | 111530 |
| 3 | 134580 | 122649 | 111423 |
| 4 | 720 | 456 | 312 |
| 5 | 14775 | 9990 | 17780 |
+----------+-------------------+-------------------+-------------------+
コード定義は以下のとおり。
雨の日は事故が多いイメージがありますが、曇りの日と同じくらいなんですね。
市街地などの状況別
> SELECT
terrain,
SUM(CASE WHEN year = 2019 THEN terrain ELSE 0 END) AS terrain_sum_2019,
SUM(CASE WHEN year = 2020 THEN terrain ELSE 0 END) AS terrain_sum_2020,
SUM(CASE WHEN year = 2021 THEN terrain ELSE 0 END) AS terrain_sum_2021
FROM traffic_accidents
WHERE year IN (2019, 2020, 2021)
GROUP BY terrain
ORDER BY terrain;
+----------+-------------------+-------------------+-------------------+
| terrain | terrain_sum_2019 | terrain_sum_2020 | terrain_sum_2021 |
+----------+-------------------+-------------------+-------------------+
| 1 | 186213 | 153396 | 155005 |
| 2 | 231820 | 189632 | 183136 |
| 3 | 237342 | 182898 | 175869 |
+----------+-------------------+-------------------+-------------------+
コード定義は以下の通り
人口が少ない地域の方が事故が多いですね。スピードを出しやすいからでしょうか。
※各コード定義は
https://www.npa.go.jp/publications/statistics/koutsuu/opendata/2019/codebook_2019.pdf から抜粋
Discussion