Step-by-Step Guide: Bucketing in Hive
Hive bucketing diagram showing buckets and files
what is Bucketing (Clustering)?
Bucketing (also called clustering) divides table data into a fixed number of files or "buckets" based on the hash of one or more columns. Bucketing helps with efficient joins, sampling, and more predictable file sizes.
Each bucket is stored as a separate file (or set of files) inside the table directory.
Bucketing is orthogonal to partitioning — you can use both together.
When to use bucketing:
To optimize joins on a commonly-joined column.
To enable efficient sampling (TABLESAMPLE BUCKET).
To limit number of files created during ingest and create more even data distribution.
Hive installed and configured with access to HDFS.
Hive Metastore working.
Basic knowledge of HiveQL and HDFS commands.
Basic syntax to create a bucketed table:
CREATE TABLE users_bucketed (
id INT,
name STRING,
email STRING
)
CLUSTERED BY (id) INTO 8 BUCKETS
STORED AS ORC;
Key points:
CLUSTERED BY (col1, col2, ...) INTO N BUCKETS declares bucketing columns and number of buckets.
Choose storage format (ORC/Parquet) for better performance.
To ensure data is physically placed into buckets correctly, follow these steps.
Enable bucketing enforcement:
SET hive.enforce.bucketing = true;
(Optional but recommended) Set the number of reducers equal to the number of buckets (depends on Hive/version):
SET mapreduce.job.reduces = 8; -- older property name
-- or
SET mapreduce.job.reduces = 8; -- modern clusters may use the same property or rely on automatic reducers
Insert data into the bucketed table using an INSERT statement. Use DISTRIBUTE BY (or CLUSTER BY) to control how rows are sent to reducers:
INSERT INTO TABLE users_bucketed
SELECT id, name, email
FROM users_source
DISTRIBUTE BY id;
Notes:
DISTRIBUTE BY ensures rows with the same bucket key go to the same reducer (and thus the same bucket file).
Some Hive versions automatically handle reducers and bucketing when hive.enforce.bucketing=true is set; behavior can vary by Hive release — check your Hive docs if unsure.
List the table location on HDFS to see bucket files:
hdfs dfs -ls /user/hive/warehouse/users_bucketed
You should see files like 000000_0, 000001_0, … corresponding to bucket files.
In Hive, you can also run simple checks for approximate distribution:
SELECT id % 8 AS bucket_id, COUNT(*)
FROM users_bucketed
GROUP BY id % 8;
(Replace % expression with a hashing expression suitable for your data if needed.)
When two tables are bucketed by the same column and have the same number of buckets, Hive can perform a bucket map-join which avoids expensive shuffles.
Create two tables with same bucketing:
CREATE TABLE orders_bucketed (
order_id INT,
user_id INT,
amount DOUBLE
)
CLUSTERED BY (user_id) INTO 8 BUCKETS STORED AS ORC;
CREATE TABLE users_bucketed (
user_id INT,
name STRING
)
CLUSTERED BY (user_id) INTO 8 BUCKETS STORED AS ORC;
After loading both tables with hive.enforce.bucketing=true and ensuring they have same bucket count, run join:
SET hive.optimize.bucketmapjoin = true;
SELECT u.name, SUM(o.amount)
FROM users_bucketed u
JOIN orders_bucketed o
ON u.user_id = o.user_id
GROUP BY u.name;
If conditions are met, Hive can use bucketed join optimizations and reduce shuffle overhead.
Bucketing makes sampling easy and repeatable using TABLESAMPLE syntax.
SELECT * FROM users_bucketed TABLESAMPLE(BUCKET 1 OUT OF 8) WHERE ...;
This reads only the specified bucket(s), which is much faster than scanning the entire table for sampling experiments.
You can combine both for better performance:
CREATE TABLE logs (
event_time STRING,
event_type STRING,
message STRING
)
PARTITIONED BY (dt STRING)
CLUSTERED BY (event_type) INTO 8 BUCKETS
STORED AS ORC;
Load data into partitions and buckets (dynamic/static partitioning + bucketing) — ensure relevant Hive settings are enabled (e.g., hive.exec.dynamic.partition, hive.exec.dynamic.partition.mode, hive.enforce.bucketing).
Mismatched bucket counts: For optimal bucketed joins, both tables must have the same number of buckets — otherwise Hive cannot use bucket map-join.
Too many buckets: Leads to many small files and poor performance. Pick bucket count based on data size (e.g., 8, 16, 32) and cluster resources.
Small buckets (tiny files): Compact buckets using INSERT OVERWRITE TABLE ... to merge files.
Bucketing not applied: Ensure hive.enforce.bucketing=true and use DISTRIBUTE BY or set reducers appropriately during INSERT.
Version differences: Bucketing behavior may differ between Hive versions — consult your Hive distribution docs if results look unexpected.
Use ORC/Parquet as storage format with bucketing for best I/O and compression.
Choose bucket count that matches cluster parallelism and data size (not too high).
Align bucket counts and bucket columns across tables that will be frequently joined.
Combine partitioning (for coarse-grain pruning) with bucketing (for join/sampling performance).
Enable hive.enforce.bucketing during writes to guarantee physical bucketing.
CREATE TABLE t_bucketed (id INT, col STRING) CLUSTERED BY (id) INTO 8 BUCKETS STORED AS ORC;
SET hive.enforce.bucketing = true;
SET mapreduce.job.reduces = 8; -- make reducers match bucket count where required
INSERT INTO TABLE t_bucketed SELECT id, col FROM t_source DISTRIBUTE BY id;
SHOW TABLES;
DESCRIBE FORMATTED t_bucketed;
Bucketing divides data into fixed buckets to improve join performance and sampling efficiency. Create bucketed tables with CLUSTERED BY ... INTO N BUCKETS, write data with hive.enforce.bucketing=true and use DISTRIBUTE BY or proper reducer settings to ensure data lands in correct buckets. Combine with partitioning for maximum query performance.