Chapter 04 · Hashes and Object-Like Records

HSET/HGET/HMGET/HGETALL and Modeling Compact Field/Value Records

Model flat object-like records as Redis hashes, prove HSET/HGET/HMGET/HGETALL semantics and costs, and keep schema, TTL, memory, and missing-field boundaries explicit.

Beginner → Intermediate120–150 minutesHash record modeling labRedis Open Source 8.10.1Free/local-firstLast reviewed: September 6, 2026

Learning outcomes

AtlasMart needs a fast read model for a product card: title, category, current display price, inventory label, merchandising status, and a schema-version marker. A developer proposes six independent Redis keys; another serializes the whole record into one JSON-looking String; a third reaches for a hash because the application usually reads or changes individual fields. The right choice begins with exact hash semantics, not the slogan “Redis hashes are objects.” A Redis hash is one Redis key whose value is a flat map of field names to String values. Redis does not infer an application class, validate business types, or create nested objects.

01

Explain a Redis hash as one key containing field/value pairs, with String field names/values and no automatic application schema enforcement.

02

Use HSET, HGET, HMGET, HGETALL, and HLEN while interpreting missing fields, return counts, response size, and command complexity correctly.

03

Distinguish partial-field updates from whole-record reads and explain why HGETALL becomes a latency/network concern as field cardinality grows.

04

Observe memory and internal encoding without treating a current encoding name or threshold as a stable API contract.

05

Carry key TTL, field TTL, ACL, persistence, replication, Cluster, Search/index, and client-retry boundaries into hash modeling decisions.

Exact lab baseline

All Chapter 04 mandatory labs reuse the disposable Chapter 01 environment: Redis Open Source 8.10.1 from Docker Official Image redis:8.10.1, container atlasmart-redis-ch01, standalone topology, host publication 127.0.0.1:6379, TLS disabled only because traffic stays on loopback, default ACL user disabled, named ACL users atlasmart-app and academy-admin, logical database 0, AOF with appendfsync everysec plus RDB snapshots, persistent /data volume, and no explicit Redis maxmemory limit or eviction policy. The application ACL is restricted to ~atlasmart:* and normal read/write/connection categories. The primary interface is the redis-cli shipped in the same 8.10.1 image, so server and CLI versions stay aligned. Redis 8.10 adds newer hash capabilities such as field-expiration helpers and HIMPORT/fieldset-related commands, but this lesson keeps its contract to the published HSET/HGET/HMGET/HGETALL title and treats internal encodings as observations only.

1. One key, many fields: the smallest useful hash model

The key atlasmart:product:1001 can hold a flat collection of fields. HSET creates the hash if the key is absent and creates or overwrites the specified fields. Its integer reply counts new fields added, not fields successfully processed. Updating an existing field therefore returns zero for that field even though the value changed. That is an important observability detail when code incorrectly treats a zero return as failure.

redis-cli · create and partially update a product hash
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:product:1001 sku SKU-1001 name "Trail Bottle" category outdoors price_cents 2499 stock_label in-stock schema_version 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:product:1001 stock_label low-stockdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HLEN atlasmart:ch04:product:1001# Expected shape:# (integer) 6      <- six new fields# (integer) 0      <- field existed; value was still updated# (integer) 6

A hash is object-like because an application can map fields to properties, but Redis does not know that price_cents should be an integer, that category belongs to an enum, or that schema_version is mandatory. Those are application contracts unless additional validation is implemented elsewhere.

2. HGET and HMGET preserve the difference between “missing” and empty

HGET retrieves one field in O(1) expected command complexity. A missing field or missing hash key produces a null/nil reply. HMGET retrieves selected fields in request order and returns a null position for each missing field. This is not the same as an empty String: an application that decodes both to the same host-language value can erase useful state.

redis-cli · project only the fields the request needs
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HGET atlasmart:ch04:product:1001 namedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HGET atlasmart:ch04:product:1001 promotion_codedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HMGET atlasmart:ch04:product:1001 sku name price_cents promotion_codedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HMGET atlasmart:ch04:missing sku name# Expected shape:# "Trail Bottle"# (nil)# 1) "SKU-1001"  2) "Trail Bottle"  3) "2499"  4) (nil)# 1) (nil)        2) (nil)

Fields are still binary-safe Strings. The application may parse price_cents as an integer, but Redis will happily accept price_cents = "unknown" through ordinary HSET. Numeric hash commands introduced in Lesson 2 enforce numeric parsing only for the field they operate on.

3. HGETALL is complete—and O(N)

HGETALL returns every field and value, so the reply contains twice as many scalar items as the field count. Its complexity is O(N) in the number of fields and current command metadata classifies it as a slow-category read. That does not mean every small HGETALL is bad. It means record size is part of the API contract. If one hash grows from 12 fields to 500,000 fields, a once-cheap request can become a large server traversal, response allocation, network transfer, client parse, and garbage-collection event.

redis-cli · compare projection with whole-record retrieval
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HLEN atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HMGET atlasmart:ch04:product:1001 sku name price_centsdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HGETALL atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin COMMAND INFO HGETALL HMGET HGET HSET HLEN# Read COMMAND INFO for current complexity/categories rather than memorizing an old cheat sheet.
Deliberately wrong approach

A team calls HGETALL for every product card because the first test hash has six fields. The bug is not that HGETALL exists; it is that the API has no field-cardinality budget. Repair it by projecting required fields with HGET/HMGET for hot paths, measuring response bytes and tail latency, and reserving HGETALL for bounded records or administrative inspection.

4. Key TTL and field TTL are separate lifecycle layers

Chapter 02 established key-level expiration. A hash key can still have one key TTL through EXPIRE. Redis 7.4 added expiration for individual hash fields. These are different clocks: deleting the key removes every field; expiring one field removes only that field. Ordinary HSET overwriting a field clears that field's field-level expiration, while operations that conceptually mutate the existing field value, such as numeric increments, leave the field TTL intact. Lesson 4 proves these rules in detail.

redis-cli · observe key TTL separately from a field TTL
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app EXPIRE atlasmart:ch04:product:1001 3600docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HEXPIRE atlasmart:ch04:product:1001 120 FIELDS 1 stock_labeldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HTTL atlasmart:ch04:product:1001 FIELDS 2 stock_label name# Expected shape shortly after execution:# 1# 1# about 3600# about 120, then -1 for name (field exists but has no field TTL)

Do not infer tenant isolation from either mechanism. ACLs (Access Control Lists) authorize commands and key patterns; they do not become field-level application authorization simply because hashes have fields.

5. Memory evidence is useful; encoding folklore is not

Redis may use different internal encodings for small versus large hashes, and Redis 8.10 continues to evolve hash internals. OBJECT ENCODING is diagnostic evidence about the current server, not a data-model guarantee. MEMORY USAGE estimates bytes attributed to the key/value object on that build and allocator. Neither tells you the total process resident set size (RSS), replication buffers, AOF buffers, Search indexes, fork copy-on-write headroom, or client memory.

redis-cli · record current encoding and memory instead of assuming thresholds
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin OBJECT ENCODING atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin MEMORY USAGE atlasmart:ch04:product:1001docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO serverdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO memory# Keep the observed encoding name in the lab notes, but do not design around it as an API.

The useful engineering question is not “which encoding is fastest in a blog post?” It is “what is this record's field count/value-size distribution, how does it behave on the pinned version, and what happens to p95/p99 latency and memory as it grows?”

6. Wrong type, schema drift, and update semantics

If the key holds a non-hash value, hash commands return a WRONGTYPE error. That protects the Redis type boundary, not the application schema. Inside a valid hash, Redis will not stop one producer from storing schema_version=banana. Schema evolution therefore needs explicit versioning, validation, and deployment compatibility.

redis-cli · controlled wrong-type and schema-drift failures
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch04:wrongtype "not-a-hash"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HGET atlasmart:ch04:wrongtype name# Expected: WRONGTYPE ...docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:product:1001 price_cents unknowndocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HGET atlasmart:ch04:product:1001 price_cents# Expected: "unknown" -- ordinary HSET does not enforce integer schema.# Repair fixture:docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:product:1001 price_cents 2499 schema_version 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch04:wrongtype

That failure is educational because it separates two invariants: Redis enforces the key's data type, while the application must enforce the meaning and allowed representation of individual fields.

7. Hands-on lab: define a bounded AtlasMart hash contract

Create a product-card hash whose hot read uses a three-field projection. Record field count, whole-record response shape, key TTL, one temporary field TTL, current encoding, and memory usage. Then overwrite the temporary field and confirm its field TTL is cleared. This is a disposable fixture under the atlasmart:ch04: prefix only.

redis-cli · chapter record-model evidence card
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch04:lab:productdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:lab:product sku LAB-1 name "Lab Mug" price_cents 1599 category kitchen stock_label in-stock schema_version 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app EXPIRE atlasmart:ch04:lab:product 1800docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HEXPIRE atlasmart:ch04:lab:product 60 FIELDS 1 stock_labeldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HMGET atlasmart:ch04:lab:product sku name price_centsdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HLEN atlasmart:ch04:lab:productdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin MEMORY USAGE atlasmart:ch04:lab:productdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin OBJECT ENCODING atlasmart:ch04:lab:productdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HTTL atlasmart:ch04:lab:product FIELDS 1 stock_labeldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HSET atlasmart:ch04:lab:product stock_label backorderdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app HTTL atlasmart:ch04:lab:product FIELDS 1 stock_label# Final HTTL should be -1 because HSET overwrote the expiring field.

Verification checklist:

  • The hash contains exactly six fields and HMGET returns only the requested three in request order.
  • Key TTL is positive while the temporary stock_label field initially has its own positive field TTL.
  • Overwriting stock_label with HSET clears only that field expiration; the key TTL remains positive.
  • You record MEMORY USAGE/OBJECT ENCODING as environment evidence rather than universal constants.
  • Cleanup deletes only atlasmart:ch04:lab:product and the lesson fixtures.
redis-cli · cleanup Lesson 1 fixtures
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DEL atlasmart:ch04:lab:product atlasmart:ch04:product:1001 atlasmart:ch04:wrongtype

8. Production judgment

Hashes fit flat records when callers commonly read or mutate individual fields and the field set remains operationally bounded. The attractive O(1) HGET/HSET operations do not make a huge unbounded hash free: whole-record commands are O(N), large replies consume network/client memory, persistence and replication must propagate writes, and one hot hash key can concentrate traffic on one Cluster slot. Search can index selected hash fields, but the index adds memory and write work; a hash itself does not create secondary-query capability.

On retries, distinguish idempotent assignments from increments. HSET of the same deterministic value is naturally repeatable; numeric increments are not. Under replication or failover, later chapters will measure acknowledged-write bounds rather than assuming the standalone result implies durability. For security, ACL key patterns can restrict atlasmart:* keys but do not authorize one hash field differently from another.

9. Summary and next step

A Redis hash is one key containing flat field/value pairs. HSET creates or replaces fields, HGET/HMGET project selected fields, HGETALL retrieves the whole record at O(N), and HLEN exposes field cardinality. Missing fields are null, not empty strings. Key TTL and field TTL are independent lifecycle layers, and internal encoding is version-sensitive implementation evidence. Next, you will use numeric hash commands to update individual fields atomically without turning Redis Strings into a strongly typed financial database.

Check your understanding

  1. Why can HSET return 0 even when the value changed?
  2. Why is HMGET safer than HGETALL for a hot endpoint that needs only three fields?
  3. Does a hash enforce that price_cents is an integer?
  4. What happens to an expiring hash field when HSET overwrites that field?
  5. Why should OBJECT ENCODING not drive a permanent schema contract?
Review the answers

1. The integer reply counts newly added fields. Updating an existing field can change its value while contributing zero new fields.

2. It bounds requested fields and reply size to the projection instead of transferring every field in the record.

3. No. Ordinary HSET accepts String bytes. Numeric commands enforce numeric parsing only when invoked.

4. Its field-level expiration is cleared; this is separate from the hash key's key-level TTL.

5. Internal encodings and thresholds are implementation/version details; observe and benchmark them on the pinned server instead of treating them as API guarantees.

Authoritative references

  • Redis hashes — hash data model and current command family
  • HSET — field creation/overwrite and return semantics
  • HGET — single-field retrieval and missing-field behavior
  • HMGET — projection order, null entries, and complexity
  • HGETALL — whole-hash retrieval and O(N) cost
  • HEXPIRE — field expiration and overwrite-clears-expiration rule
  • Redis 8.10 release notes — current pinned server baseline

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.