Skip to content

Add 'Best Practices for Single Row Lookups' post - #209

Open
nuno-faria wants to merge 1 commit into
apache:mainfrom
nuno-faria:point_queries
Open

nuno-faria wants to merge 1 commit into
apache:mainfrom
nuno-faria:point_queries

Conversation

@nuno-faria

Copy link
Copy Markdown
Contributor

Blog post based on my talk given at the Boston DataFusion meetup in September.

Comment thread .gitignore
!*/blog/
infrastructure-actions/

__pycache__

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local build was adding these folders.

@nuno-faria
nuno-faria requested a review from alamb October 17, 2026 15:58
@alamb

alamb commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

I see this and plan to review it

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @nuno-faria -- this is great. I think it will be very widely read and quite useful

I left a bunch of small comments, but I don't think any of them are required to publish this

Thanks again


## Methodology

The dataset we will be using here is formed by a single Parquet file with 100M

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it is possible, but providing a link here to the parquet file or the generator you used to make one, I think would make this post stronger / more concrete

retrieve a random row from the table by key. Each test builds on the last.


## Storage

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: add the word Parquet to the title somehow. You say this in the text, but i think it would also help to say in the titles again

Maybe something like

Storage (Optimizing Parquet Settings)

[DuckDB]: https://duckdb.org/docs/lts/sql/statements/copy#parquet-options


## Execution

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly here I think it would help to highlight the DataFusion aspect of this

Execution (Optimizing DataFusion Settings)

The dataset we will be using here is formed by a single Parquet file with 100M
rows (unless otherwise stated), using a key (`bigint`, unique) and value
(`varchar`, md5 of the key) columns. Unless otherwise stated, the queries tested
retrieve a random row from the table by key. Each test builds on the last.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might also help to have a specific SQL example of this

select ... FROM ... 

That would also explain if this is a SELECT * or a SELECT col type query

</div>

In summary, at least for sorted files with many unique values, it is better to
**disable both of these structures** when writing Parquet files.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice here to include links / examples of how to enable / set these configurations to make the recommendation concrete

For example a link to ArrowWriterProperties and the parquet writer settings `COPY ... FORMAT paruqet OPTIONS ..) and https://datafusion.apache.org/user-guide/sql/format_options.html#parquet-format-options

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe you can add those links somewhere in the intro to parquet tuning

Comment on lines +331 to +336
# pushdown_filters disabled
FilterExec (output_rows=1)
DataSourceExec (output_rows=1k)

# pushdown_filters enabled
DataSourceExec (output_rows=1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written I found this confusing as the filter expression looks like it gets lost.
I recommend showing where the filter expression goes . perhaps something like this

Suggested change
# pushdown_filters disabled
FilterExec (output_rows=1)
DataSourceExec (output_rows=1k)
# pushdown_filters enabled
DataSourceExec (output_rows=1)
# pushdown_filters disabled
FilterExec key=<VAL> (output_rows=1)
DataSourceExec (output_rows=1k)
# pushdown_filters enabled
DataSourceExec key=<VAL> (output_rows=1)

For both smaller (<10k rows) and larger (>100k rows) reads, we conclude that
**enabling `pushdown_filters` results in a higher throughput**. Curiously,
medium sized reads (between 10k and 100k) are faster with `pushdown_filters`
disabled. It's not clear why this is the case, but I retested this multiple

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is almost certainly some combination of

  1. Different I/O patterns (pushdown filter fetches the predicate columns in a separate I/O)
  2. Filter representation efficiency (until recently, pushdown filter represents filter results as an RLE ype structure, RowSelection).

@hhhizzz and others have been improving this over time

Comment on lines +366 to +367
As of the writing of the post, this feature is currently disabled by default due
to some performance regressions. More info can be found at [#3463] and [#20324].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
As of the writing of the post, this feature is currently disabled by default due
to some performance regressions. More info can be found at [#3463] and [#20324].
As of the writing of the post, this feature is currently disabled because it slows some queries
down. More info can be found at [#3463] and [#20324].

While reusing the logical plan offers an improvement over not reusing anything,
it's clear that **reusing the physical plan leads to a considerably higher
throughput**, doubling or even tripling performance. So when can we expect
prepared statements in native DataFusion that reuse the physical plan? First, we

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems strange to list here the work items that would be needed to implement a DataFusion feature.

I recommend filing a ticket with the details and just refer to it here "See XYZ ticket for details"

Comment on lines +507 to +509
by releasing the current thread to another task. Andrew Lamb has given a talk at
Tokio Conf 2026 titled "[Using Tokio for CPU-Bound Tasks (Works Really Well)]"
which talks about this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nits to make this slightly more concise:

Suggested change
by releasing the current thread to another task. Andrew Lamb has given a talk at
Tokio Conf 2026 titled "[Using Tokio for CPU-Bound Tasks (Works Really Well)]"
which talks about this.
by releasing the current thread to another task. Andrew Lamb gave a talk at
Tokio Conf 2026 titled "[Using Tokio for CPU-Bound Tasks (Works Really Well)]"
about this.

@nuno-faria

Copy link
Copy Markdown
Contributor Author

Thanks @alamb for the review. I will update the post soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants