Skip to content

Pre-release documentation

This describes Beacon 2.0.0-rc3, a release candidate. Behavior documented here may still change before 2.0.0 ships, and some of it is not in any released build yet. For the current stable release, see the 1.8.0 documentation.

Speed Up Slow Queries

A slow query usually reads more bytes than necessary. Help the engine to skip data. A faster read is not the answer.

1. Find the cause

Run EXPLAIN first. It shows what the planner pushes down into the scan:

sql
EXPLAIN SELECT count(*) FROM read_parquet('obs/*.parquet') WHERE depth < 50;

Look for the filter and the column list at the scan node. A predicate above the scan runs after the read. Such a predicate prunes nothing.

2. Read fewer columns

SELECT * on a file with hundreds of variables decodes hundreds of columns. Name only the columns that you need. Projection pushdown then does the rest:

sql
-- reads a handful of columns instead of everything
SELECT time, latitude, temperature FROM read_netcdf('argo/**/*.nc');

This helps most on a wide scientific file. It also helps on object storage, because each extra column adds network traffic.

3. Filter on the columns that prune

A filter on a coordinate column lets a reader skip row groups, chunks and whole files. The coordinate columns are time, depth, latitude and longitude:

sql
SELECT time, temperature
FROM read_zarr('sst/*/zarr.json')
WHERE time >= '2024-01-01' AND time < '2024-02-01';

Use a plain comparison on a raw column. A predicate inside a function that the reader cannot interpret runs after the read. It therefore prunes nothing.

4. Fix the layout

  • Partition the files Hive-style, for example year=2024/month=01/. Declare the partition columns with PARTITIONED BY. Beacon then skips whole directories. See CREATE EXTERNAL TABLE.
  • Do not use very many small files. The cost of each file dominates. A merge into fewer files helps more than any change to a query.
  • Use a cloud-optimized format for data on object storage. See Query Data on S3.

5. Merge large array collections with Atlas

Do you query a large NetCDF or Zarr collection often? Then convert it to Atlas. This gives the largest gain. Atlas keeps statistics for each dataset. Beacon drops the datasets that cannot match a predicate. It drops them before it reads any array data. It then reads only the arrays that you select.

sql
CREATE EXTERNAL TABLE sensor_atlas
STORED AS ATLAS
LOCATION 'collections/*/atlas.json';

6. Compute once, query many times

Does the same expensive aggregate run often? Then compute it once.

A materialized view caches the result. You refresh it on demand:

sql
CREATE MATERIALIZED VIEW monthly_means AS
SELECT date_trunc('month', time) AS month, avg(temperature) AS t
FROM read_netcdf('argo/**/*.nc')
GROUP BY 1;

REFRESH MATERIALIZED VIEW monthly_means;

Use a managed table for a working subset that you also change:

sql
CREATE TABLE hot_profiles AS
SELECT * FROM read_netcdf('argo/**/*.nc') WHERE temperature > 25;

7. Stream instead of collect

A query can plan fast but return slowly. The client then holds too much data. Read batches instead of the whole result:

python
import adbc_driver_flightsql.dbapi as flight_sql

conn = flight_sql.connect("grpc+tls://beacon.example.com:32011")
cursor = conn.cursor()
cursor.execute("SELECT * FROM obs")

while (batch := cursor.fetch_record_batch().read_next_batch()) is not None:
    process(batch)

See Export Query Results.

Server-side tuning

The server adds more settings. They cover concurrency, memory, disk spill, reader caches and object store listings. Performance Tuning documents them.

Released under the AGPL-3.0 License.