私自身、業務の一環として大量のログを扱う場面があります。その際、もっと簡単に使用できて汎用性のある分析ツールがないか考えていました。
そこで調べてみた結果、DuckDBに白羽の矢が立ちました。
データ分析の現場では、大量のデータを効率的に処理できるツールの需要が高まっています。従来のRDBMSでは重すぎる、かといってBigQueryのようなクラウドサービスを使うほどでもない、そんな中間的なニーズに応えるデータベースとして注目を集めているのがDuckDBです。
本記事では、DuckDBの特徴から実際の導入・使用方法まで、実体験を交えながら紹介していきます。
DuckDBとは
DuckDBは、OLAP(分析処理)に特化したインメモリ分析データベースです。2019年にオープンソースとして公開され、「分析のためのSQLite」というコンセプトで開発されています。
以下のような特徴があります。
- 軽量かつ高速
- 豊富なデータ形式サポート
- SQLの高い互換性
導入・実践
インストール
公式インストールページはこちらです。他のインストール方法が載っています。
筆者はhomebrewを使用してインストールしています。
brew install duckdb
本記事では、DuckDBは v1.3.1 で進めていきます
検証ファイル
こちらは生成AIで作成してもらったログjsonファイルになります。
{"timestamp":"2025-08-01T09:00:23.685Z","status_code":201,"user_id":"user_00425","endpoint":"/home","message":"New record added"}
{"timestamp":"2025-07-06T04:37:37.831Z","status_code":403,"user_id":null,"endpoint":"/help","message":"Forbidden access"}
{"timestamp":"2025-07-05T03:11:48.922Z","status_code":200,"user_id":null,"endpoint":"/api/auth/logout","message":"User authenticated"}
{"timestamp":"2025-07-12T03:20:38.234Z","status_code":201,"user_id":null,"endpoint":"/home","message":"New record added"}
{"timestamp":"2025-07-11T04:49:26.449Z","status_code":200,"user_id":null,"endpoint":"/api/settings","message":"User authenticated"}
{"timestamp":"2025-07-18T23:44:46.939Z","status_code":204,"user_id":null,"endpoint":"/about","message":"Update completed"}
{"timestamp":"2025-07-18T09:06:24.734Z","status_code":200,"user_id":"user_11746","endpoint":"/api/reports","message":"Search results returned"}
{"timestamp":"2025-07-09T17:03:48.701Z","status_code":403,"user_id":null,"endpoint":"/api/auth/logout","message":"Insufficient permissions"}
{"timestamp":"2025-07-30T05:22:34.571Z","status_code":200,"user_id":null,"endpoint":"/contact","message":"Search results returned"}
{"timestamp":"2025-07-27T10:31:18.351Z","status_code":200,"user_id":null,"endpoint":"/api/search","message":"Request processed successfully"}
基本操作
実際にDuckDBを使用してログ解析してみましょう。
DuckDBを起動
duckdb
ファイルの読み込み
単数
select * from read_json('logs.json');
┌┌──────────────────────────┬─────────────┬────────────┬──────────────────┬────────────────────────────────┐
│ timestamp │ status_code │ user_id │ endpoint │ message │
│ varchar │ int64 │ varchar │ varchar │ varchar │
├──────────────────────────┼─────────────┼────────────┼──────────────────┼────────────────────────────────┤
│ 2025-08-01T09:00:23.685Z │ 201 │ user_00425 │ /home │ New record added │
│ 2025-07-06T04:37:37.831Z │ 403 │ NULL │ /help │ Forbidden access │
│ 2025-07-05T03:11:48.922Z │ 200 │ NULL │ /api/auth/logout │ User authenticated │
│ 2025-07-12T03:20:38.234Z │ 201 │ NULL │ /home │ New record added │
│ 2025-07-11T04:49:26.449Z │ 200 │ NULL │ /api/settings │ User authenticated │
│ 2025-07-18T23:44:46.939Z │ 204 │ NULL │ /about │ Update completed │
│ 2025-07-18T09:06:24.734Z │ 200 │ user_11746 │ /api/reports │ Search results returned │
│ 2025-07-09T17:03:48.701Z │ 403 │ NULL │ /api/auth/logout │ Insufficient permissions │
│ 2025-07-30T05:22:34.571Z │ 200 │ NULL │ /contact │ Search results returned │
│ 2025-07-27T10:31:18.351Z │ 200 │ NULL │ /api/search │ Request processed successfully │
├──────────────────────────┴─────────────┴────────────┴──────────────────┴────────────────────────────────┤
│ 10 rows 5 columns │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
複数
ワイルドカードを使用して複数読み込みが可能です。
filename
をつけることで、どのファイルから読み込んだデータか表示されるようになります。
select * from read_json('*.json', filename = true);
┌──────────────────────────┬─────────────┬────────────┬──────────────────┬────────────────────────────────┬─────────────┐
│ timestamp │ status_code │ user_id │ endpoint │ message │ filename │
│ varchar │ int64 │ varchar │ varchar │ varchar │ varchar │
├──────────────────────────┼─────────────┼────────────┼──────────────────┼────────────────────────────────┼─────────────┤
│ 2025-08-01T09:00:23.685Z │ 201 │ user_00425 │ /home │ New record added │ logs-2.json │
│ 2025-07-06T04:37:37.831Z │ 403 │ NULL │ /help │ Forbidden access │ logs-2.json │
│ 2025-07-05T03:11:48.922Z │ 200 │ NULL │ /api/auth/logout │ User authenticated │ logs-2.json │
│ 2025-07-12T03:20:38.234Z │ 201 │ NULL │ /home │ New record added │ logs-2.json │
│ 2025-07-11T04:49:26.449Z │ 200 │ NULL │ /api/settings │ User authenticated │ logs-2.json │
│ 2025-07-18T23:44:46.939Z │ 204 │ NULL │ /about │ Update completed │ logs-2.json │
│ 2025-07-18T09:06:24.734Z │ 200 │ user_11746 │ /api/reports │ Search results returned │ logs-2.json │
│ 2025-07-09T17:03:48.701Z │ 403 │ NULL │ /api/auth/logout │ Insufficient permissions │ logs-2.json │
│ 2025-07-30T05:22:34.571Z │ 200 │ NULL │ /contact │ Search results returned │ logs-2.json │
│ 2025-07-27T10:31:18.351Z │ 200 │ NULL │ /api/search │ Request processed successfully │ logs-2.json │
│ 2025-08-01T09:00:23.685Z │ 201 │ user_00425 │ /home │ New record added │ logs.json │
│ 2025-07-06T04:37:37.831Z │ 403 │ NULL │ /help │ Forbidden access │ logs.json │
│ 2025-07-05T03:11:48.922Z │ 200 │ NULL │ /api/auth/logout │ User authenticated │ logs.json │
│ 2025-07-12T03:20:38.234Z │ 201 │ NULL │ /home │ New record added │ logs.json │
│ 2025-07-11T04:49:26.449Z │ 200 │ NULL │ /api/settings │ User authenticated │ logs.json │
│ 2025-07-18T23:44:46.939Z │ 204 │ NULL │ /about │ Update completed │ logs.json │
│ 2025-07-18T09:06:24.734Z │ 200 │ user_11746 │ /api/reports │ Search results returned │ logs.json │
│ 2025-07-09T17:03:48.701Z │ 403 │ NULL │ /api/auth/logout │ Insufficient permissions │ logs.json │
│ 2025-07-30T05:22:34.571Z │ 200 │ NULL │ /contact │ Search results returned │ logs.json │
│ 2025-07-27T10:31:18.351Z │ 200 │ NULL │ /api/search │ Request processed successfully │ logs.json │
├──────────────────────────┴─────────────┴────────────┴──────────────────┴────────────────────────────────┴─────────────┤
│ 20 rows 6 columns │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
WHERE句を使用した絞り込み
エラーコードのみを表示させるようにしてみましょう。
select * from read_json('logs.json') where status_code >= 400;
┌──────────────────────────┬─────────────┬─────────┬──────────────────┬──────────────────────────┐
│ timestamp │ status_code │ user_id │ endpoint │ message │
│ varchar │ int64 │ varchar │ varchar │ varchar │
├──────────────────────────┼─────────────┼─────────┼──────────────────┼──────────────────────────┤
│ 2025-07-06T04:37:37.831Z │ 403 │ NULL │ /help │ Forbidden access │
│ 2025-07-09T17:03:48.701Z │ 403 │ NULL │ /api/auth/logout │ Insufficient permissions │
└──────────────────────────┴─────────────┴─────────┴──────────────────┴──────────────────────────┘
テーブル作成
毎回直接ファイルを読み込むだけでなく、読み込みファイルを元にテーブルを作成できます。
-- テーブル作成
create table logs as select * from read_json('logs.json');
-- テーブル読み込み
select * from logs;
テーブルを作成することでパフォーマンスの大幅向上が見込めます。
所感
サーバーの起動や設定ファイルの編集が不要で、インストールも簡単ですぐに使い始められる手軽さは大きなメリットです。
データ分析の初期段階で「とりあえず試してみる」際の障壁が非常に低いです。
また実際に100万件のデータを対象に集計処理を試したところ、とても高速に検索ができました。RDBが行指向に対し、DuckDBは列指向なのでそこにも差が出てきているのかと思います。
さいごに
DuckDBは、データ分析における「ちょうど良い」ソリューションとして非常に有用なツールです。
今回はjsonファイルを使用しましたが、他にも多くの拡張子に対応しています。詳しくは参考サイト公式から確認してみてください。
導入も簡単であるため、分析ツールとして気軽に試してみてはいかがでしょうか。
参考サイト
GoQSystemでは一緒に働いてくれる仲間を募集中です!
ご興味がある方は以下リンクよりご確認ください。