DuckDBの拡張: 空間分析、リモートHTTPFS Parquet & Iceberg統合

目次(19 項目)
DuckDBの組み込みインプロセス分析データベースエンジンは、ローカルデータ処理において比類のないパフォーマンスを提供します。しかし、その真の力は、堅牢な拡張エコシステムを通じて解き放たれます。このガイドでは、地理空間分析のためのspatial、HTTP(S)経由でのリモートParquetファイルの直接クエリのためのhttpfs、およびApache Icebergテーブルメタデータの検査のためのicebergという3つの重要な拡張機能の実用的なアプリケーションについて詳しく説明します。これらのアーキテクチャの基盤を探り、実行可能なコード例を提供し、パフォーマンスに関する考慮事項を議論し、一般的な本番環境での落とし穴を強調します。
DuckDB拡張機能アーキテクチャの概要
DuckDBの設計は、モジュール性と拡張性を最優先しています。コアエンジンは、基本的なSQL解析、最適化、実行機能を提供します。拡張機能は、以下のような新しい機能を追加することで、このコアを強化します。
- 新しいデータ型: 空間データ用の
GEOMETRYなど。 - 新しい関数:
ST_IntersectsやREAD_PARQUETのようなSQL関数。 - 新しいストレージ形式: Parquet、CSV、JSON、および特殊な形式のサポート。
- 新しいプロトコル: S3、GCS、および汎用HTTP(S)アクセス用の
httpfsなど。
拡張機能は動的にロードされるため、ユーザーはコアエンジンを肥大化させることなく、特定の分析ニーズに合わせてDuckDB環境を調整できます。この「機能は自分で持ち込む」モデルにより、スリムで高性能な基盤を確保しつつ、オンデマンドで広範な機能を提供します。
拡張機能を使用するための一般的なワークフローには、2つのSQLコマンドが含まれます。
INSTALL <extension_name>;: 拡張機能のバイナリをダウンロードしてインストールします。通常、初回はインターネット接続が必要です。LOAD <extension_name>;: インストールされた拡張機能を現在のDuckDBセッションにロードし、その関数と型を利用可能にします。
この分離により、効率的な管理が可能になり、必要なコンポーネントのみがリソースを消費するようになります。
1. spatial拡張機能による空間分析
地理空間データは、ロジスティクスや都市計画から環境モニタリングまで、あらゆる場所で利用されています。spatial拡張機能は、強力なGIS機能をDuckDBに直接もたらし、別の地理空間データベースを必要とせずに、ローカルデータに対して複雑な空間クエリを可能にします。
コアコンセプト
spatial拡張機能は、点、線、ポリゴン、およびマルチジオメトリを表すことができるGEOMETRYデータ型を導入します。これは、標準的な地理空間データ形式と操作をサポートしています。
- Well-Known Text (WKT): ベクトルジオメトリオブジェクトを表す人間が読めるテキストマークアップ。例:
POINT (10 20)、POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))。 - Well-Known Binary (WKB): 同じジオメトリオブジェクトのバイナリ形式で、ストレージと転送により効率的です。
- GeoJSON: 地理データ構造をエンコードするためのJSONベースの形式。
この拡張機能は、これらの形式間の変換、ジオメトリの作成、および空間関係(例: 交差、包含、距離)の実行のための関数を提供します。
インストールとロード
INSTALL spatial;
LOAD spatial;
例: GeoJSONのロードと空間交差の実行
点のデータセット(例: センサーの位置)とポリゴンのセット(例: 行政区域)があるシナリオを考えてみましょう。どのセンサーがどの境界内にあるかを見つけたいとします。
まず、デモンストレーション用にサンプルGeoJSONデータを生成しましょう。
import duckdb
import json
# Sample GeoJSON data
# Polygon 1: A square area
polygon_geojson_1 = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]
]]
},
"properties": {"name": "Area A"}
}
# Polygon 2: Another square area, slightly offset
polygon_geojson_2 = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[5, 5], [5, 15], [15, 15], [15, 5], [5, 5]
]]
},
"properties": {"name": "Area B"}
}
# Points: Some inside, some outside, some in overlap
point_geojson_1 = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [2, 2]},
"properties": {"sensor_id": "S001"}
}
point_geojson_2 = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [7, 7]},
"properties": {"sensor_id": "S002"}
}
point_geojson_3 = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [12, 12]},
"properties": {"sensor_id": "S003"}
}
point_geojson_4 = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [20, 20]},
"properties": {"sensor_id": "S004"}
}
# Create DuckDB in-memory database
con = duckdb.connect(database=':memory:', read_only=False)
# Install and load spatial extension
con.execute("INSTALL spatial;")
con.execute("LOAD spatial;")
# Create tables and insert data
con.execute("""
CREATE TABLE polygons (
id INTEGER,
name VARCHAR,
geometry GEOMETRY
);
""")
con.execute("""
CREATE TABLE points (
id INTEGER,
sensor_id VARCHAR,
geometry GEOMETRY
);
""")
# Insert polygons
con.execute(f"""
INSERT INTO polygons (id, name, geometry) VALUES
(1, '{polygon_geojson_1['properties']['name']}', ST_GeomFromGeoJSON('{json.dumps(polygon_geojson_1['geometry'])}')),
(2, '{polygon_geojson_2['properties']['name']}', ST_GeomFromGeoJSON('{json.dumps(polygon_geojson_2['geometry'])}'));
""")
# Insert points
con.execute(f"""
INSERT INTO points (id, sensor_id, geometry) VALUES
(1, '{point_geojson_1['properties']['sensor_id']}', ST_GeomFromGeoJSON('{json.dumps(point_geojson_1['geometry'])}')),
(2, '{point_geojson_2['properties']['sensor_id']}', ST_GeomFromGeoJSON('{json.dumps(point_geojson_2['geometry'])}')),
(3, '{point_geojson_3['properties']['sensor_id']}', ST_GeomFromGeoJSON('{json.dumps(point_geojson_3['geometry'])}')),
(4, '{point_geojson_4['properties']['sensor_id']}', ST_GeomFromGeoJSON('{json.dumps(point_geojson_4['geometry'])}'));
""")
# Perform spatial intersection query
print("Sensors intersecting with polygons:")
result = con.execute("""
SELECT
p.sensor_id,
poly.name AS polygon_name
FROM
points AS p,
polygons AS poly
WHERE
ST_Intersects(p.geometry, poly.geometry);
""").fetchdf()
print(result)
# Example: Calculate distance between two points
print("\nDistance between S001 and S002:")
distance_result = con.execute("""
SELECT
ST_Distance(
(SELECT geometry FROM points WHERE sensor_id = 'S001'),
(SELECT geometry FROM points WHERE sensor_id = 'S002')
) AS distance;
""").fetchdf()
print(distance_result)
con.close()
アーキテクチャの説明:
ST_GeomFromGeoJSON関数はGeoJSON文字列を解析し、DuckDBの内部GEOMETRY表現に変換します。この表現は空間操作に最適化されています。次に、ST_Intersects関数が空間結合を実行し、ジオメトリ間の重なりを効率的に判断します。多数のジオメトリを含む複雑なクエリの場合、DuckDBのクエリ最適化ツールは空間インデックス(利用可能で拡張機能によって有効になっている場合。ただし、PostGISのような明示的な空間インデックス作成はDuckDBの主要な機能ではなく、空間結合の内部最適化に依存します)を活用します。
パフォーマンスに関する考慮事項
- データ表現: ジオメトリを
GEOMETRY型として直接保存する方が、WKT/WKB/GeoJSON文字列を繰り返し解析するよりも効率的です。 - 単純化: 視覚化や精度の低い分析の場合、
ST_Simplifyのような関数を使用して複雑なジオメトリを単純化すると、処理時間を大幅に短縮できます。 - バウンディングボックスフィルター: 非常に大規模なデータセットの場合、バウンディングボックスの交差(
ST_Intersects(ST_Envelope(geom1), ST_Envelope(geom2)))を使用して事前にフィルタリングすることで、より高価な正確な交差チェックを行う前に、重ならないジオメトリを迅速に排除できます。 - メモリ: 地理空間操作は、特に複雑なポリゴンではメモリを大量に消費する可能性があります。DuckDBに十分なメモリが割り当てられていることを確認してください。
2. httpfs拡張機能によるリモートHTTPFS Parquet
クラウドオブジェクトストレージ(S3、GCS、Azure Blob Storage)にリモートで保存されているデータ、または標準HTTP(S)経由でアクセスするデータは、最新のデータアーキテクチャにとって基本的な要件です。httpfs拡張機能を使用すると、DuckDBはリモートソースからParquet、CSV、JSONファイルを、ファイル全体をダウンロードすることなく直接クエリできます。
アーキテクチャの説明
httpfs拡張機能は、HTTP Rangeリクエストを活用して動作します。DuckDBは、リモートファイル全体をダウンロードする代わりに、クエリに必要なデータブロックまたはカラムチャンクに対応する特定のバイト範囲をリクエストします。これは、特に以下の点で効率的です。
- カラムプルーニング: クエリが少数のカラムのみを選択する場合、DuckDBはそれらの特定のカラムのバイト範囲のみをフェッチします。
- 述語プッシュダウン: Parquetファイルに埋め込まれたメタデータまたは統計(例: カラムの最小/最大値)に対して
WHERE句を評価できる場合、DuckDBは述語を満たさない行グループ全体をスキップし、さらに少ないデータをフェッチできます。
この「ゼロETL」アプローチにより、ネットワーク転送が最小限に抑えられ、レイテンシが削減され、DuckDBがデータレイク上で直接強力な分析エンジンとして機能できるようになります。
インストールとロード
INSTALL httpfs;
LOAD httpfs;
例: リモートParquetファイルのクエリ
S3でホストされているNYCタクシー乗車記録データなど、公開されているParquetデータセットをクエリします。
import duckdb
import os
# Create DuckDB in-memory database
con = duckdb.connect(database=':memory:', read_only=False)
# Install and load httpfs extension
con.execute("INSTALL httpfs;")
con.execute("LOAD httpfs;")
# Configure S3 credentials if accessing private buckets.
# For public buckets, these are not strictly necessary, but good practice for consistency.
# Replace with your actual credentials or environment variables.
# con.execute("SET s3_access_key_id='YOUR_ACCESS_KEY_ID';")
# con.execute("SET s3_secret_access_key='YOUR_SECRET_ACCESS_KEY';")
# con.execute("SET s3_region='us-east-1';") # Or your bucket's region
# Example: Querying a public Parquet file from S3
# This URL points to a small sample of NYC Yellow Taxi data for 2023-01
s3_parquet_url = "s3://nyc-tlc/trip data/yellow_tripdata_2023-01.parquet"
print(f"Querying remote Parquet file: {s3_parquet_url}")
# Query 1: Count total rows (demonstrates full scan if no pushdown)
print("\nQuery 1: Count total rows")
result_count = con.execute(f"SELECT COUNT(*) FROM '{s3_parquet_url}';").fetchdf()
print(result_count)
# Query 2: Select specific columns and apply a filter (demonstrates column pruning and predicate pushdown)
print("\nQuery 2: Select specific columns and filter by trip distance")
result_filtered = con.execute(f"""
SELECT
vendor_id,
tpep_pickup_datetime,
trip_distance,
total_amount
FROM
'{s3_parquet_url}'
WHERE
trip_distance > 10
LIMIT 10;
""").fetchdf()
print(result_filtered)
# Query 3: Aggregate data (demonstrates more complex processing)
print("\nQuery 3: Average trip distance by vendor")
result_avg_distance = con.execute(f"""
SELECT
vendor_id,
AVG(trip_distance) AS avg_distance
FROM
'{s3_parquet_url}'
GROUP BY
vendor_id;
""").fetchdf()
print(result_avg_distance)
con.close()
アーキテクチャの説明:
READ_PARQUET('s3://...')が実行されると、DuckDBのhttpfs拡張機能はまずParquetファイルのフッターとスキーマメタデータを読み取ります。このメタデータには、カラム型、行グループ境界、統計に関する情報が含まれています。クエリ最適化ツールは、この情報を使用して、クエリを満たすために必要なバイト範囲(特定のカラムと行グループに対応)を決定します。これらの特定の範囲のみがHTTP(S)経由でフェッチされ、データ転送が最小限に抑えられます。たとえば、「クエリ2」では、DuckDBはvendor_id、tpep_pickup_datetime、trip_distance、およびtotal_amountカラムのみをフェッチし、trip_distanceの最小/最大範囲が> 10と重ならない行グループをスキップする可能性があります。
パフォーマンスに関する考慮事項
- ネットワークレイテンシと帯域幅:
httpfsの主なボトルネックはネットワークです。リモートストレージへの高レイテンシまたは低帯域幅は、クエリパフォーマンスに直接影響します。 - リージョンの近接性: ネットワークレイテンシを最小限に抑えるために、DuckDBインスタンス(またはそれを実行しているマシン)と同じクラウドリージョンにデータを保存してください。
- カラムプルーニング: 必要なカラムのみを選択するように常にしてください(
SELECT col1, col2ではなくSELECT *)。これは、幅の広いテーブルにとって最も重要なパフォーマンス向上です。 - 述語プッシュダウン: 適切な統計(例: 頻繁にフィルタリングされるカラムの最小/最大値)を使用してParquetファイルを設計し、これらの統計を活用できる
WHERE句を使用してください。 - Parquetファイルサイズ:
httpfsは大きなファイルを処理できますが、多数の小さなParquetファイルは、より多くのメタデータ読み取りとHTTPリクエストのためにオーバーヘッドが増加する可能性があります。最適なParquetファイルサイズは、通常128MBから1GBの範囲です。 - キャッシュ: DuckDBはリモートデータをローカルにキャッシュできるため、同じデータに対する繰り返しのクエリのパフォーマンスが向上します。
セキュリティ上の考慮事項
- 認証情報: プライベートS3/GCSバケットの場合、認証情報(
s3_access_key_id、s3_secret_access_key、s3_region、s3_endpointなど)が安全に管理されていることを確認してください。本番環境でハードコーディングすることは避けてください。環境変数、IAMロール(EC2/ECSの場合)、または一時的な認証情報を使用してください。 - パブリックアクセス: バケットへのパブリック読み取りアクセスを許可する場合は注意してください。機密性のないデータのみが公開されていることを確認してください。
- HTTPS: 転送中のデータを暗号化するために、リモートアクセスには常にHTTPSを使用してください。
3. iceberg拡張機能によるIceberg統合
Apache Icebergは、大規模で高性能な分析テーブル向けに設計されたオープンなテーブル形式です。スキーマ進化、隠れたパーティショニング、パーティション進化、タイムトラベルなどの機能を提供します。DuckDB用のiceberg拡張機能を使用すると、Icebergテーブルのメタデータを検査およびクエリし、その後、基になるデータファイルをクエリできます。
アーキテクチャの説明
Icebergテーブルは単一のファイルではなく、メタデータによって整理されたファイルのコレクションです。主要なコンポーネントは次のとおりです。
- テーブルメタデータファイル: テーブルの現在のスナップショットを指します。
- マニフェストリストファイル: スナップショットのマニフェストファイルをリストします。
- マニフェストファイル: パーティションまたはテーブル全体を構成するデータファイル(Parquet、ORC、AVRO)をリストします。
- データファイル: 実際のデータ。通常はParquet形式です。
DuckDB iceberg拡張機能は、主にIcebergメタデータの読み取りに焦点を当てています。iceberg_scan()を使用すると、DuckDBはIcebergメタデータファイル(テーブルのルートメタデータロケーションから開始)を解析して、テーブルのスキーマ、パーティショニング、および実際のデータファイルの場所を理解します。次に、httpfs拡張機能(データファイルがリモートの場合)を使用してこれらのデータファイルを読み取ります。
重要な注意点: DuckDB iceberg拡張機能は現在読み取り専用です。Icebergテーブルを作成、変更、または書き込むことはできません。その主な使用例は、既存のIcebergデータセットの迅速な検査と分析クエリです。
インストールとロード
INSTALL iceberg;
LOAD iceberg;
例: Icebergテーブルのメタデータの検査とデータのクエリ
この例では、S3に保存されている既存のIcebergテーブルを想定します。もし持っていない場合は、SparkまたはFlinkを使用してダミーを作成するか、公開されているIcebergサンプルを指すことができます。ここでは架空のパスを使用します。
import duckdb
import os
# Create DuckDB in-memory database
con = duckdb.connect(database=':memory:', read_only=False)
# Install and load httpfs and iceberg extensions
con.execute("INSTALL httpfs;")
con.execute("LOAD httpfs;")
con.execute("INSTALL iceberg;")
con.execute("LOAD iceberg;")
# Configure S3 credentials if accessing private buckets
# con.execute("SET s3_access_key_id='YOUR_ACCESS_KEY_ID';")
# con.execute("SET s3_secret_access_key='YOUR_SECRET_ACCESS_KEY';")
# con.execute("SET s3_region='us-east-1';")
# Path to the Iceberg table's metadata directory (e.g., s3://your-bucket/your-iceberg-table/metadata)
# Replace with a real Iceberg table path if you have one.
# For demonstration, we'll use a placeholder.
# A real Iceberg table path would look like: s3://bucket/path/to/table
# The iceberg extension expects the path to the table directory, not the metadata directory specifically.
iceberg_table_path = "s3://duckdb-iceberg-sample/nyc_taxi_trips" # Example public Iceberg table
print(f"Accessing Iceberg table at: {iceberg_table_path}")
# Query 1: Inspect Iceberg table schema and metadata
# The iceberg_scan function allows you to query the table directly.
print("\nQuery 1: Inspecting Iceberg table schema and a few rows")
try:
result_schema = con.execute(f"""
SELECT *
FROM iceberg_scan('{iceberg_table_path}')
LIMIT 5;
""").fetchdf()
print(result_schema)
except duckdb.Error as e:
print(f"Error querying Iceberg table: {e}")
print("Please ensure the Iceberg table path is correct and accessible.")
# Query 2: Perform an aggregation on the Iceberg table data
print("\nQuery 2: Average trip distance from Iceberg table")
try:
result_avg_distance_iceberg = con.execute(f"""
SELECT
vendor_id,
AVG(trip_distance) AS avg_distance
FROM
iceberg_scan('{iceberg_table_path}')
GROUP BY
vendor_id;
""").fetchdf()
print(result_avg_distance_iceberg)
except duckdb.Error as e:
print(f"Error querying Iceberg table: {e}")
print("Please ensure the Iceberg table path is correct and accessible.")
con.close()
アーキテクチャの説明:
iceberg_scan()が呼び出されると、DuckDBはまず指定されたiceberg_table_path内のmetadataディレクトリを見つけます。次に、最新のversion-hint.textまたは直接最新のvX.metadata.jsonファイルを読み取り、現在のスナップショットを特定します。スナップショットから、マニフェストリストファイルを読み取り、それがマニフェストファイルを指します。最後に、マニフェストファイルは実際のデータファイル(例: Parquetファイル)へのパスを提供します。その後、DuckDBはhttpfs拡張機能を使用してこれらの個々のデータファイルを読み取り、httpfsセクションで説明されているようにカラムプルーニングと述語プッシュダウンを適用します。この多段階のメタデータ解決はユーザーには透過的であり、ユーザーは単にiceberg_scan()関数をクエリするだけです。
制限事項とユースケース
- 読み取り専用: 主な制限は、DuckDBの
iceberg拡張機能が読み取り専用であることです。Icebergテーブルのデータの作成、追加、更新、削除には使用できません。 - メタデータ検査: Icebergテーブルのスキーマ、パーティショニング、データファイルの場所を迅速に理解するのに優れています。
- アドホック分析: Sparkのような分散エンジンを起動することなく、既存のIcebergデータセットに対してアドホッククエリと分析を実行するのに最適です。
- ローカル開発/テスト: Icebergデータの一部に対するローカル開発とテストに役立ちます。
パフォーマンス最適化とメモリ管理
DuckDBのパフォーマンスは、効率的なリソース利用に大きく依存します。
- メモリ制限: 特に大規模なデータセットを扱う場合、DuckDBが利用可能なすべてのRAMを消費するのを防ぐために、メモリ制限を明示的に設定します。
sql
PRAGMA memory_limit='8GB'; -- Set to 8 Gigabytes - スレッド数: DuckDBが並列処理に使用するスレッド数を制御します。
sql
PRAGMA threads=4; -- Use 4 threads - 外部アクセス:
httpfsとicebergがリモートリソースにアクセスするには、外部アクセスを有効にする必要があります。sqlSET enable_external_access=true; - カラムナー処理: DuckDBはカラムナーデータベースです。常に必要なカラムのみを選択してください。これは、特に
httpfsの場合、ネットワークI/Oを最小限に抑えるため、パフォーマンスにとって非常に重要です。 - 述語プッシュダウン:
WHERE句が可能な限り選択的であることを確認してください。DuckDBの最適化ツールは、これらの述語をデータソース(例: Parquetファイルの統計)にプッシュダウンして、読み取るデータ量を削減します。 - データ型: 適切なデータ型を使用してください。空間データの場合、ジオメトリが
GEOMETRY型として保存されていることを確認してください。 - 空間インデックス(暗黙的): DuckDBにはPostGISのような明示的な
CREATE SPATIAL INDEX構文はありませんが、そのクエリ最適化ツールは空間結合を効率的に処理するように設計されています。非常に大規模な空間データセットの場合、ジオメトリを単純化したり、バウンディングボックスフィルターを使用したりするために前処理を検討してください。 - Parquetファイル最適化:
httpfsとicebergの場合、基になるParquetファイルが適切に最適化されていることを確認してください。- 行グループサイズ: 128MB〜1GBの行グループを目指します。
- カラム統計:
WHERE句で使用されるカラムに統計(最小/最大)が存在することを確認してください。 - 圧縮: 効率的な圧縮(例: Snappy、ZSTD)を使用してください。
よくある落とし穴と本番環境での問題
-
拡張機能が見つからない/ロードされない:
- 問題:
Error: Catalog Error: Function with name 'ST_Intersects' does not exist. - 原因: 拡張機能が現在のセッションで
INSTALLまたはLOADされていない。INSTALLはダウンロードし、LOADはアクティブ化する。 - 修正: セッションの開始時に
INSTALL <extension_name>;とLOAD <extension_name>;を実行します。INSTALL中はインターネット接続を確認してください。
- 問題:
-
HTTPFS認証情報と権限:
- 問題:
Error: IO Error: S3 Error [AWS_ERROR_S3_ACCESS_DENIED]または[AWS_ERROR_S3_INVALID_ACCESS_KEY_ID] - 原因: 不正確なS3/GCS認証情報、
s3_access_key_id、s3_secret_access_key、s3_regionに対するSETコマンドの欠落、またはバケット/オブジェクトに対するIAM権限の不足。 - 修正: 認証情報を確認します。IAMロール/ユーザーに
s3:GetObjectおよびs3:ListBucket権限があることを確認します。GCSの場合、gcs_access_key_idおよびgcs_secret_access_keyが設定されているか、サービスアカウント認証情報を使用していることを確認します。
- 問題:
-
ネットワークレイテンシとエグレスコスト:
- 問題: リモートデータに対するクエリが遅い、またはクラウド料金が予期せず高い。
- 原因: DuckDBとリモートストレージ間のネットワークレイテンシが高い、または非効率なクエリによる過剰なデータ転送(エグレス)。
- 修正: DuckDBをデータと同じ場所に配置します(同じクラウドリージョン)。カラムプルーニング(
SELECT specific_cols)と述語プッシュダウン(WHERE filter_conditions)でクエリを最適化します。エグレスコストを監視します。
-
メモリ枯渇:
- 問題:
Error: Out of Memory: Failed to allocate X bytes - 原因: DuckDBが複雑な操作(例: 大規模な結合、複雑な空間操作、または十分なフィルタリングなしで非常に大きなParquetファイルを読み取る)のために、あまりにも多くのデータをメモリにロードしようとする。
- 修正:
PRAGMA memory_limit='XGB';を設定します。中間結果のサイズを減らすためにクエリを最適化します。httpfsの場合、強力な述語プッシュダウンとカラムプルーニングを確実にします。可能であれば、データをチャンクで処理することを検討してください。
- 問題:
-
データ型の不一致(空間):
- 問題:
Error: Conversion Error: Could not convert string '...' to GEOMETRY. - 原因:
ST_GeomFromWKTの入力文字列、`ST_
- 問題:
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

モダンWebアプリにおけるDuckDB-Wasm:クライアントサイドOLAP、Parquetストリーミング、そして超高速ダッシュボード
モダンWebアプリにおけるduckdb-wasmの包括的なガイド。クライアントサイドOLAP、Parquetストリーミング、そして本番環境レベルのアーキテクチャとコード例による超高速ダッシュボードについて解説します。
Read more
2026年のカラム型データ: Apache Arrowメモリレイアウト、Parquetストレージ、DuckDBベクタライゼーション
2026年のカラム型データについて、Apache Arrowメモリレイアウト、Parquetストレージ、DuckDBベクタライゼーションを、本番環境レベルのアーキテクチャとコード例で網羅的に解説する総合ガイドです。
Read moreClickHouse vs DuckDB: アーキテクチャの深掘りと本番環境ベンチマーク
2つの主要なカラムナ分析データベースエンジンを比較します。DuckDBの組み込みベクトル化とClickHouseの分散リアルタイムOLAPの使い分けを解説します。
Read more