Frequently Asked Questions
Common questions and answers about Biscuit index for PostgreSQL.
General Questions
What is Biscuit?
Biscuit is a PostgreSQL index access method designed for LIKE and ILIKE pattern matching queries. It uses bitmap-based indexing to accelerate searches with wildcards (%, _). Performance improvements vary depending on your data characteristics, query patterns, and system configuration.
When should I use Biscuit?
Consider Biscuit when you:
Run frequent LIKE queries with wildcards
Need to search across multiple text columns
Have high-cardinality string columns
Want to optimize queries with pattern matching
Need prefix, suffix, or substring matching
Other options may be better for:
Full-text search with stemming/ranking (use tsvector/GIN)
Very long strings (>256 chars are truncated)
Columns with very few distinct values
Is Biscuit production-ready?
Biscuit includes:
Full CRUD support (INSERT, UPDATE, DELETE)
Automatic index maintenance
Multi-column indexes
Multiple performance optimizations
However, as with any index type, we recommend:
Test thoroughly in your staging environment
Benchmark with your actual data and queries
Monitor performance metrics
Have a rollback plan
Start with non-critical workloads
How much memory does Biscuit use?
Memory usage depends on:
Number of records
Average string length
Number of indexed columns
Character distribution in your data
The index is stored in memory for performance. For large tables, ensure you have adequate RAM. Test with your actual data to determine memory requirements.
Monitor with:
SELECT biscuit_size_pretty('idx_name');
Installation & Setup
How do I install Biscuit?
See the Installation Guide for detailed instructions.
Quick steps:
# Ubuntu/Debian
sudo apt-get install postgresql-server-dev-14 build-essential
git clone https://github.com/crystallinecore/biscuit.git
cd biscuit
make
sudo make install
Then in PostgreSQL:
CREATE EXTENSION biscuit;
Installation fails with “could not load library”
Problem: PostgreSQL can’t find biscuit.so
Solution:
# Find PostgreSQL library directory
pg_config --pkglibdir
# Verify file exists
ls $(pg_config --pkglibdir)/biscuit.so
# If missing, reinstall
cd biscuit
sudo make install
Extension creation fails: “extension does not exist”
Problem: Extension control files not in correct location
Solution:
# Check extension directory
pg_config --sharedir
# Verify files
ls $(pg_config --sharedir)/extension/biscuit*
# Should see:
# biscuit.control
# biscuit--1.0.sql
# If missing, reinstall
sudo make install
Should I install with CRoaring support?
Yes, recommended for production use.
CRoaring provides:
Better memory compression
More efficient set operations
Install:
# Ubuntu/Debian
sudo apt-get install libroaring-dev
# Then build Biscuit
make HAVE_ROARING=1
sudo make install
Without CRoaring, Biscuit uses a fallback implementation that’s slower but still functional.
Index Creation
How do I create a Biscuit index?
Single column:
CREATE INDEX idx_name ON table_name
USING biscuit (column_name);
Multiple columns:
CREATE INDEX idx_multi ON table_name
USING biscuit (col1, col2, col3);
See Quick Start Tutorial for examples.
How long does index creation take?
Build time varies significantly based on:
Table size
Number of columns
Average string length
Available memory
System load
Benchmark with your data to determine actual build times.
For large tables, use CONCURRENTLY:
CREATE INDEX CONCURRENTLY idx_name ON table_name
USING biscuit (column_name);
Can I create partial Biscuit indexes?
Yes! Use a WHERE clause:
-- Index only active users
CREATE INDEX idx_active_users ON users
USING biscuit (username, email)
WHERE status = 'active';
-- Index only recent orders
CREATE INDEX idx_recent_orders ON orders
USING biscuit (customer_name)
WHERE created_at > '2024-01-01';
Benefits:
Smaller index size
Faster queries on filtered data
Reduced maintenance overhead
How many columns can I index?
While there’s no hard limit enforced, indexing many columns increases:
Index size
Build time
Memory usage
Maintenance overhead
Recommendation: Index only columns you frequently query with LIKE patterns.
Test performance with your specific column combinations.
Query Performance
Why is my query not using the index?
Check with EXPLAIN:
EXPLAIN ANALYZE
SELECT * FROM products WHERE name LIKE '%laptop%';
Common causes:
Statistics out of date
ANALYZE products;
Pattern matches too many rows
-- Check selectivity SELECT COUNT(*) FROM table WHERE col LIKE '%pattern%';
Wrong column indexed
-- Check which columns are indexed \d products
Planner estimates sequential scan is faster
-- Test with index forced (testing only!) SET enable_seqscan = off;
Always verify index usage with EXPLAIN ANALYZE before and after creating indexes.
How can I measure query performance?
Use EXPLAIN ANALYZE:
EXPLAIN ANALYZE
SELECT * FROM products WHERE name LIKE '%pattern%';
Look for:
Execution time
Index scan vs sequential scan
Rows processed
Planning time
Compare before and after creating the index to measure improvement.
Performance varies based on:
Data characteristics
Pattern selectivity
System resources
Concurrent load
Always benchmark with your actual workload.
Does Biscuit support case-insensitive search?
Yes, from versions >= 2.1.0:
-- Create index
CREATE INDEX idx_name ON products USING biscuit (name);
-- Query
SELECT * FROM products WHERE name ILIKE '%wireless%';
Can I use OR conditions with Biscuit?
Yes, but be aware of performance:
-- Uses Biscuit (efficient)
SELECT * FROM products
WHERE name LIKE '%laptop%'
OR name LIKE '%desktop%';
-- Better: Combine patterns when possible
SELECT * FROM products
WHERE name LIKE '%laptop%'
OR name LIKE '%desktop%'
OR name LIKE '%tablet%';
PostgreSQL will use bitmap OR to combine results.
Does Biscuit support regular expressions?
No. Biscuit only supports SQL LIKE/ILIKE patterns (%, _).
For regex, use:
PostgreSQL’s
~operator with GIN trigram indexFull-text search with tsvector
Example:
-- Regex (not Biscuit)
SELECT * FROM products WHERE name ~ 'laptop|desktop';
-- LIKE (uses Biscuit)
SELECT * FROM products WHERE name LIKE '%laptop%';
Does Biscuit optimize COUNT(*) queries?
Biscuit includes optimizations for aggregate queries like COUNT(*):
-- May benefit from aggregate optimizations
SELECT COUNT(*) FROM products WHERE name LIKE '%laptop%';
The index can process bitmap operations without fetching tuples, which can improve performance for certain query patterns.
Test with your queries to measure the actual benefit.
Can I use LIMIT with Biscuit?
Yes. Biscuit works with LIMIT clauses:
SELECT * FROM products
WHERE name LIKE '%gaming%'
LIMIT 10;
The index includes optimizations for queries with LIMIT, though the actual performance benefit depends on your data and query patterns.
Index Maintenance
Do I need to manually maintain the index?
No. Biscuit automatically maintains itself:
INSERT: Adds new records
UPDATE: Removes old, adds new
DELETE: Marks as tombstone
PostgreSQL’s VACUUM handles cleanup.
What are tombstones?
Tombstones are deleted records tracked in memory until cleanup.
Check tombstone count:
SELECT biscuit_index_stats('idx_name'::regclass);
Output:
Tombstones: 245
Automatic cleanup triggers at 1000 tombstones.
Manual cleanup:
VACUUM table_name;
-- or
REINDEX INDEX idx_name;
When should I rebuild the index?
Consider rebuilding when:
✅ Significant data changes have occurred
✅ Index statistics show high tombstone counts
✅ Query performance has degraded
✅ After major PostgreSQL version upgrades
How to rebuild:
-- Non-blocking rebuild
CREATE INDEX CONCURRENTLY idx_new ON table_name
USING biscuit (column_name);
DROP INDEX idx_old;
ALTER INDEX idx_new RENAME TO idx_old;
Monitor index health regularly to determine when rebuilding is beneficial.
How do I monitor index health?
Use the diagnostic function:
SELECT biscuit_index_stats('idx_name'::regclass);
Key metrics provided:
Active records: Current valid records
Total slots: Allocated memory slots
Free slots: Reusable slots from deletes
Tombstones: Deleted but not cleaned records
Max length: Longest indexed string
CRUD statistics: Insert/update/delete counts
Create monitoring view:
CREATE VIEW biscuit_health AS
SELECT
indexrelid::regclass as index_name,
biscuit_index_stats(indexrelid) as stats
FROM pg_index
WHERE indexrelid::regclass::text LIKE '%biscuit%';
Monitor these metrics over time to understand index behavior and maintenance needs.
Does Biscuit support parallel operations?
Biscuit includes support for parallel bitmap scans during query execution. Parallel index building is not currently implemented.
Whether parallel queries are used depends on PostgreSQL’s query planner and your configuration settings.
Troubleshooting
Queries are slower than expected
Debug checklist:
Verify index is being used
EXPLAIN ANALYZE SELECT * FROM table WHERE col LIKE '%pattern%'; -- Should show: Index Scan using idx_name
Check pattern selectivity
-- How many rows match? SELECT COUNT(*) FROM table WHERE col LIKE '%pattern%'; -- High match percentage may make index less beneficial
Update statistics
ANALYZE table_name;
Check index health
SELECT biscuit_index_stats('idx_name'::regclass);
Review memory settings
SHOW work_mem; SHOW shared_buffers; -- See [Performance Tuning](performance.md)
Compare with baseline
Measure query time without the index
Document before/after performance
Consider data characteristics
Index build fails with out of memory
Problem: Not enough RAM for index construction
Solutions:
Increase maintenance_work_mem
SET maintenance_work_mem = '2GB'; CREATE INDEX ...
Build during low-traffic period
Consider partial index
-- Index subset of data CREATE INDEX idx_recent ON table USING biscuit (col) WHERE created_at > '2024-01-01';
Add more RAM to server
Crashes or unexpected restarts
Possible causes:
Memory corruption: Ensure CRoaring is properly installed
PostgreSQL crash: Check PostgreSQL logs
Extension bug: Report at GitHub Issues
Recovery:
-- Drop and rebuild index
DROP INDEX idx_name;
CREATE INDEX idx_name ON table USING biscuit (col);
-- Or reindex
REINDEX INDEX idx_name;
“Index is not valid” error
Cause: Index build failed or was interrupted
Solution:
-- Drop invalid index
DROP INDEX idx_name;
-- Rebuild
CREATE INDEX CONCURRENTLY idx_name ON table
USING biscuit (col);
Pattern not matching expected rows
Check pattern syntax:
-- Case-sensitive!
SELECT * FROM products WHERE name LIKE '%Mouse%'; -- Matches "Mouse"
SELECT * FROM products WHERE name LIKE '%mouse%'; -- Matches "mouse"
-- Underscore is single-character wildcard
SELECT * FROM products WHERE sku LIKE 'PROD-___'; -- Exactly 3 chars
Test without index:
SET enable_indexscan = off;
SELECT * FROM products WHERE name LIKE '%pattern%';
-- Compare results
Advanced Topics
Can I use Biscuit with partitioned tables?
Yes! Create indexes on each partition:
-- Create partitioned table
CREATE TABLE orders (
id SERIAL,
customer_name TEXT,
created_at DATE
) PARTITION BY RANGE (created_at);
-- Create partitions
CREATE TABLE orders_2024_q1 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
-- Create Biscuit index on partition
CREATE INDEX idx_orders_2024_q1 ON orders_2024_q1
USING biscuit (customer_name);
Note: Each partition needs its own index.
Does Biscuit work with replication?
Yes. Biscuit indexes work with:
✅ Streaming replication
✅ Logical replication
✅ Hot standby
Caveat: Indexes are built independently on each server.
Can I use Biscuit in read replicas?
Yes, but indexes must be built separately:
-- On primary
CREATE INDEX idx_name ON table USING biscuit (col);
-- On replica (after replication catches up)
-- Index is automatically created from WAL
-- Or rebuild if needed:
REINDEX INDEX idx_name;
How does Biscuit handle NULL values?
NULL values are not indexed.
-- Create index
CREATE INDEX idx_name ON users USING biscuit (middle_name);
-- This uses index
SELECT * FROM users WHERE middle_name LIKE '%Smith%';
-- This does NOT use index (NULL check)
SELECT * FROM users WHERE middle_name IS NULL;
For NULL queries, add separate condition:
WHERE middle_name IS NULL
OR middle_name LIKE '%pattern%'
Does Biscuit support multi-byte characters (Unicode)?
Yes, Biscuit supports UTF-8 encoding:
-- Japanese, Chinese, Arabic, emoji, etc.
CREATE INDEX idx_name ON products USING biscuit (name);
-- Query with multi-byte characters
SELECT * FROM products WHERE name LIKE '%日本%';
Migration & Compatibility
How do I migrate from GIN trigram?
Evaluate first:
-- 1. Create Biscuit index alongside GIN
CREATE INDEX idx_biscuit ON table USING biscuit (col);
-- 2. Test queries with both
EXPLAIN ANALYZE SELECT * FROM table WHERE col LIKE '%pattern%';
-- 3. Compare performance with your actual queries
-- Document query times, index sizes, and maintenance costs
-- 4. If Biscuit meets your needs better, consider transition
DROP INDEX idx_gin_trigram;
-- 5. Keep Biscuit
Different index types have different strengths. Choose based on your specific workload requirements.
Can I have both B-tree and Biscuit on same column?
Yes, and it’s often beneficial:
-- B-tree for exact matches and ORDER BY
CREATE INDEX idx_name_btree ON products (name);
-- Biscuit for LIKE patterns
CREATE INDEX idx_name_biscuit ON products USING biscuit (name);
PostgreSQL will choose the appropriate index automatically.
How do I uninstall Biscuit?
-- 1. Drop all Biscuit indexes
DROP INDEX idx1, idx2, idx3;
-- 2. Drop extension (drops any remaining indexes)
DROP EXTENSION biscuit CASCADE;
# 3. Remove files
sudo rm $(pg_config --pkglibdir)/biscuit.so
sudo rm $(pg_config --sharedir)/extension/biscuit*
Getting Help
Where can I get support?
Documentation: ReadTheDocs
Discussions: GitHub Discussions
Bug Reports: GitHub Issues
Email: sivaprasad.off@gmail.com
How do I report a bug?
Include in your report:
PostgreSQL version:
SELECT version();Biscuit version:
SELECT * FROM pg_extension WHERE extname = 'biscuit';Table schema:
\d table_nameIndex definition:
\d+ index_nameQuery that fails:
EXPLAIN ANALYZE ...Error messages from PostgreSQL logs
Submit at: GitHub Issues
How can I contribute?
We welcome contributions!
Code: Submit pull requests
Documentation: Improve docs
Testing: Test with your data
Ideas: Suggest features
Bugs: Report issues
Is there a Slack/Discord community?
Not yet!
Roadmap
What features are being considered?
Future development may include:
Enhanced parallel index builds
Additional compression options
Extended monitoring capabilities
Long-term possibilities:
Persistent bitmap storage options
Approximate matching capabilities
Additional pattern matching features
Enhanced cloud optimizations
Development priorities are based on community feedback and usage patterns. Features listed here are under consideration but not guaranteed for any specific timeline.
Performance Comparisons
How does Biscuit compare to other indexes?
Performance characteristics vary significantly based on:
Data distribution
Query patterns
Hardware specifications
PostgreSQL configuration
Concurrent workload
General considerations:
B-tree:
Excellent for exact matches and range queries
Good for prefix patterns with concrete characters
Not suitable for suffix or substring patterns
GIN (trigram):
Designed for full-text and trigram matching
Larger index size
Different optimization characteristics
Biscuit:
Optimized for LIKE/ILIKE pattern matching
Bitmap-based approach
In-memory index structures
Multi-column query optimization
Always benchmark with your specific workload to make informed decisions. Test with:
Your actual data
Representative queries
Expected query volume
Production-like hardware
Still Have Questions?
Can’t find your answer?
Check the Installation Guide
Read the Quick Start
Browse Performance Tuning
Contact us: sivaprasad.off@gmail.com
Happy pattern matching! 🚀