Based on the MLA-C01 exam guideReport an error
Even for the same data, how you store it can change query cost by dozens of times and training speed by several times.
Partitioning means splitting data into folders by a specific key (usually the date). If a query reads only the partitions it needs through a WHERE condition, the rest is not scanned — this is partition pruning.
s3://ml-datalake/events/
├─ year=2026/month=05/day=01/part-0000.parquet
├─ year=2026/month=06/day=24/part-0000.parquet
└─ year=2026/month=06/day=25/part-0000.parquet ← with WHERE day='25', only this is scanned
Folder names in the key=value form such as year= and month= are called Hive-style partitioning. Glue Crawler and Athena recognize this convention automatically and register them as partition columns.
-- Filtering by the partition key scans only that partition's Parquet files
SELECT customer_id, COUNT(*) AS cnt
FROM ml_datalake.events
WHERE year = '2026' AND month = '06' AND day = '25'
GROUP BY customer_id;Criteria for choosing a partition key — choose wrong and the effect is zero.
| Situation | Result | Response |
|---|---|---|
| Partition by a column you filter on often | Pruning kicks in, scan volume drops sharply | Ideal |
| Partition by a column you almost never filter on | Always a full scan | Redesign the partition key |
| Cardinality too high (seconds, IDs) | Millions of small files, metadata explosion | Group into a higher unit (day, month) |
| Cardinality too low (2 values) | Pruning effect is negligible | Consider another key |
The effect of partitioning appears "only when the partition key is used in the query condition." If you partition by date but the query filters only with WHERE customer_id = ..., it fully scans every date partition. At the same time, if you split too finely (e.g., by the second), small files grow into the millions, causing metadata overhead and the "small files problem." A medium-cardinality column such as date (year/month/day) or region is usually the right line.
In ML data storage, the choice of format ties directly to cost and speed. The key is the difference between row-oriented and columnar.
| Format | Method | Characteristics | ML suitability |
|---|---|---|---|
| CSV | Row-based, text | Easy for humans to read, no schema, inefficient | Low |
| JSON | Row-based, text | Nested structure, verbose | Low |
| Parquet | Columnar, binary | Per-column compression and scanning, schema included | High |
| ORC | Columnar, binary | Similar to Parquet, Hive-friendly | High |
| RecordIO | Binary | Input format for SageMaker built-in algorithms | Training only |
There are three reasons Parquet is the de facto standard for analytics and ML.
import pandas as pd
df = pd.read_csv("s3://raw/events.csv")
# Convert to Parquet + Snappy compression (Athena/SageMaker read it quickly)
df.to_parquet(
"s3://ml-datalake/curated/events.parquet",
engine="pyarrow",
compression="snappy",
)Choosing a compression codec
| Codec | Compression ratio | Speed | Splittable | Suited for |
|---|---|---|---|---|
| Snappy | Medium | Fast | Yes, inside Parquet | Training and query data read often (default) |
| GZIP | High | Slow (CPU↑) | Yes, inside Parquet | Archives read rarely |
| GZIP + CSV/JSON | High | Slow | No | Unsuitable for parallel processing |
Snappy has a medium compression ratio but compresses and decompresses fast, so it suits data read often such as queries and training (the Parquet default). Gzip has a high compression ratio that cuts storage cost but uses more CPU — it fits archives that are rarely read. ML training data is read repeatedly, so the Snappy + Parquet combination is usually the answer.
⚠️ Trap: It is not "compression is always good." If you compress CSV/JSON with Gzip, the file becomes non-splittable, so several workers cannot divide up and read a single file in parallel. Parquet is splittable in units of internal blocks (row groups), so it preserves parallelism in distributed training and queries. When handling big data, "is it a splittable format?" matters more than "is it compressed?"
[One Gzip CSV file] Worker1 ████████████████ Worker2 (idle)
→ One worker has to read everything sequentially
[Parquet, several shards] Worker1 ████ Worker2 ████ Worker3 ████ Worker4 ████
→ Split by row group/file and read at the same time
The destination of a storage strategy is "how fast a SageMaker training job can receive data from S3." SageMaker provides input modes for delivering S3 data to the training container.
| Input mode | Behavior | Suitable situation | Cost |
|---|---|---|---|
| File mode | Copies the whole data to the instance disk, then starts training | Small to medium scale where the data fits on disk | Copy wait, disk capacity needed |
| Pipe mode | Streams from S3 and starts training immediately | Large scale — no download wait | Sequential access, full shuffle is hard |
| FastFile mode | Lazily loads from S3 as needed (appears as files) | Large data + File mode convenience | Per-file latency can accumulate |
[File] ████ full copy (tens of minutes) ████ ▶ training starts ──────────▶
[Pipe] ▶ training starts ─── keeps being fed by streaming ───────────▶
[FastFile] ▶ training starts ─── loads only the needed files on demand ──────▶
from sagemaker.inputs import TrainingInput
# Stream large data in with Pipe mode
train_input = TrainingInput(
s3_data="s3://ml-datalake/curated/train/",
input_mode="Pipe", # File / Pipe / FastFile
content_type="application/x-parquet",
)
estimator.fit({"train": train_input})When you place data in S3, it is also important to split it into several files (shards) of moderate size. A single giant file is hard for multiple instances to divide and read in distributed training. SageMaker can hand files out to instances with the ShardedByS3Key distribution type.
| Distribution type | Behavior | When |
|---|---|---|
FullyReplicated | Every instance receives the entire data | Validation set, small data |
ShardedByS3Key | Each instance receives only different objects | Distributed training (but there must be multiple objects) |
⚠️ Trap: Turning on
ShardedByS3Keyis not the end of it. Sharding works at the S3 object level, so applying it to a single 100GB file distributes nothing. The data has to be divided into several objects for it to take effect.
In ML training, data input often becomes a bigger bottleneck than the GPU. If the GPU computes a batch in 0.1 seconds but reading the data takes 0.5 seconds, the GPU sits idle most of the time (GPU starvation). Pipe/FastFile mode, an efficient format like Parquet, and appropriate sharding are all devices to "avoid starving the expensive GPU." That is why a storage strategy is a training cost strategy.
One team kept several TB of click logs in S3 as a single giant CSV and trained in File mode, wasting 40 minutes on data copying for every experiment. When they re-stored the data as Parquet shards partitioned by date and switched to Pipe mode, the copy wait disappeared and doing EDA beforehand with Athena also became easier. A single storage format governed the speed of experiment iteration.
| Question | Judgment |
|---|---|
| What do queries filter by? | Make that column the partition key |
| Are partitions split too finely? | Group into a higher unit (day→month) to prevent small files |
| Do you use all the columns or only some? | If only some, definitely columnar (Parquet) |
| How often is it read? | If often, Snappy; if an archive, GZIP |
| Do several workers read in parallel? | Splittable format + several shards |
| Is the data larger than the instance disk? | Pipe or FastFile mode |
| Is GPU utilization low? | Suspect an input bottleneck — check format, mode, and sharding |
Even for the same 5TB dataset and the same query, the amount read differs like this depending on the layout.
| Storage method | Column selection | Partition pruning | Compression | Actual scan volume (conceptual) |
|---|---|---|---|---|
| Uncompressed CSV, flat storage | Not possible | Not possible | None | All 5TB |
| GZIP CSV, flat storage | Not possible | Not possible | Yes | Physical size shrinks but all columns are read |
| Parquet, flat storage | Possible | Not possible | Yes | By the ratio of needed columns |
| Parquet, date partitioning | Possible | Possible | Yes | Column ratio × partition ratio |
| State | Problem | Response |
|---|---|---|
| One giant file | No parallel reads, sharding is ineffective | Split into several objects |
| Excessively many small files | Request overhead dominates processing time | Merge to an appropriate size (compaction) |
| One file per partition + too many partitions | Metadata explosion | Raise the partition unit (day→month) |
| Several files of moderate size | — | Ideal |
Does the training data fit on the instance disk?
│
├─ Yes ─── Is a full shuffle/random access every epoch important?
│ ├─ Yes → File mode
│ └─ No → File or FastFile
│
└─ No ─── Is access sequential?
├─ Yes → Pipe mode (no download wait)
└─ No (random access to only some files) → FastFile mode
| Observation | Probable cause | Response |
|---|---|---|
| Tens of minutes of waiting before training starts | File mode's full copy | Switch to Pipe/FastFile |
| GPU utilization stays low | Data supply bottleneck | Check format (Parquet), sharding, input mode |
| Adding instances doesn't speed things up | A single object, so nothing is distributed | Split into several shards + ShardedByS3Key |
| Training fails from lack of disk | Data is larger than EBS | Pipe/FastFile or enlarge the volume |
When GPU utilization is low, upgrading the instance type to a bigger GPU is mostly a wrong answer. If the bottleneck is input rather than computation, a more expensive GPU just sits idle longer. Look first at the storage format, input mode, and sharding.
| Clue in the question | Answer |
|---|---|
| "queries by date range but cost is high" | Date Hive partitioning + Parquet |
| "uses only 5 of 100 columns for training" | Parquet (columnar) |
| "training data read repeatedly" | Parquet + Snappy |
| "workers cannot read in parallel" | Gzip CSV is non-splittable — replace the format |
| "data is larger than the instance disk" | Pipe or FastFile mode |
| "GPU utilization is low" | Input bottleneck — check format, mode, sharding |
| "adding instances doesn't make it faster" | Single object — split into several shards |
In the next note we wrap up Week 2 with a comprehensive review of this week's data ingestion and storage.
Click a choice to reveal the answer and explanation.
Question 1
Event data arrives daily and is stored in S3. Most analysis queries filter by date range. What is the most effective storage method to reduce scan costs?
Question 2
You want to store large ML training datasets that are read repeatedly. What is the best format and compression combination?
Question 3
Training data is much larger than instance disk. You want to stream from S3 without copying everything and start training immediately. What SageMaker input mode is suitable?
Question 4
After compressing a CSV file with Gzip for distributed training, multiple workers couldn't read data in parallel. What is the cause?
Question 5
SageMaker training shows low GPU usage and GPU starvation (waiting for data). Which is least relevant to storage/input improvements?