HDEL and HEXISTS
A Redis hash is a map of field-value pairs stored under a single key, and two of the most fundamental operations on that map are removing an entry and checking whether one exists. HDEL deletes one or more fields from a hash, and HEXISTS tells you whether a specific field is present without fetching its value. Together they let you manage hash contents precisely — clearing stale data, pruning temporary fields, and guarding code paths that depend on a field being set.
Overview / How it works
Internally, a Redis hash is backed by one of two structures depending on its size: a compact listpack (a tightly packed sequence of field-value pairs) for small hashes, or a full hash table once the hash grows past hash-max-listpack-entries (128 by default) or a value exceeds hash-max-listpack-value (64 bytes by default). This matters for HDEL and HEXISTS because their cost depends on which representation backs the key: in a listpack, a lookup or delete scans linearly through the packed entries; in a hash table, it’s a direct O(1) hash lookup per field. Redis handles this transparently — you never choose the representation yourself, and a hash silently converts from listpack to hash table as it grows (it never converts back).
HEXISTS key field is a pure read: it performs a lookup for field inside the hash stored at key and returns 1 if the field is present or 0 if it is not — including the case where key itself doesn’t exist at all. It never errors for a missing key or field; a missing field is simply not an error condition in Redis.
HDEL key field [field ...] removes one or more fields from the hash and returns the number of fields that were actually removed — fields you name that don’t exist are silently skipped and not counted. Because every Redis command runs atomically on the single-threaded event loop, a multi-field HDEL is guaranteed to remove all of its existing target fields in one indivisible step; no other client can observe the hash in a partially-deleted state. If HDEL removes the last remaining field in a hash, Redis deletes the key entirely — an empty hash is not left behind. This is the same behavior you’ll see with lists, sets, and sorted sets: Redis never stores an empty container.
Syntax
HDEL key field [field ...]
HEXISTS key field
| Argument | Meaning |
|---|---|
key |
The name of the hash key to operate on. |
field |
One or more field names within the hash. HDEL accepts multiple; HEXISTS accepts exactly one. |
| Command | Return value | Time complexity |
|---|---|---|
HDEL |
Integer — count of fields actually removed | O(N) where N is the number of fields to be removed |
HEXISTS |
Integer — 1 if the field exists, 0 otherwise |
O(1) |
Examples
Example 1: Basic delete and existence check
HSET user:1001 name "Ada" email "ada@example.com" age "36"
HEXISTS user:1001 name
HEXISTS user:1001 phone
HDEL user:1001 age
HEXISTS user:1001 age
HGETALL user:1001
Output:
(integer) 3
(integer) 1
(integer) 0
(integer) 1
(integer) 0
1) "name"
2) "Ada"
3) "email"
4) "ada@example.com"
The hash starts with three fields. HEXISTS confirms name is present and phone is not, both without touching any value. HDEL removes age and returns 1 because exactly one field was deleted; a follow-up HEXISTS confirms it’s gone, and HGETALL shows the hash now has only two fields left.
Example 2: Deleting multiple fields, some of which don’t exist
HSET session:abc123 user_id "1001" ip "203.0.113.5" device "mobile" temp_flag "1"
HDEL session:abc123 device temp_flag nonexistent_field
HGETALL session:abc123
Output:
(integer) 4
(integer) 2
1) "user_id"
2) "1001"
3) "ip"
4) "203.0.113.5"
Three field names were passed to HDEL, but the return value is 2, not 3 — device and temp_flag existed and were removed, while nonexistent_field was silently ignored because it was never there. This is normal, expected behavior, not an error.
Example 3: A hash that disappears when emptied
HSET cart:5001 item:100 "2" item:200 "1" item:300 "5"
HEXISTS cart:5001 item:200
HDEL cart:5001 item:200
EXISTS cart:5001
HDEL cart:5001 item:100 item:300
EXISTS cart:5001
Output:
(integer) 3
(integer) 1
(integer) 1
(integer) 1
(integer) 2
(integer) 0
After removing item:200, the cart key still exists with two fields left, so EXISTS returns 1. Once the remaining two fields are deleted with a single HDEL call, the hash has nothing left in it — Redis removes the key itself, so the final EXISTS check returns 0. There is no such thing as an empty hash sitting in the keyspace.
How it works step by step
When you issue HDEL key field1 field2, Redis: (1) looks up key in the main keyspace dictionary; if it doesn’t exist or doesn’t hold a hash, it returns 0 or a WRONGTYPE error respectively; (2) for each named field, checks whether it exists in the underlying listpack or hash table and removes it if found, incrementing an internal counter for each successful removal; (3) after processing all fields, checks whether the hash is now empty — if so, it deletes the key from the keyspace entirely, which also cancels any TTL that was set on it; (4) returns the counter as an integer reply. The whole operation happens without yielding to any other command, so a concurrent client can never see the hash half-deleted.
HEXISTS key field is simpler: Redis looks up key, and if it holds a hash, performs a single lookup for field within that structure — an O(1) hash table probe once the hash has converted from its compact listpack form. No value is ever read or copied, which is what makes it cheaper than fetching a value with HGET just to test presence.
Common Mistakes
Mistake 1: Using HGET to test for existence.
HSET account:2002 balance "0"
HGET account:2002 balance
Output:
(integer) 1
"0"
Application code that does something like if (!value) after an HGET will treat the string "0" as “field missing” in many languages, even though the field is set. Use HEXISTS account:2002 balance to test presence — it returns 1 regardless of what the stored value is, avoiding the ambiguity entirely.
Mistake 2: Calling HDEL/HEXISTS on a key holding the wrong type.
SET counter:1001 "5"
HDEL counter:1001 field
Output:
OK
(error) WRONGTYPE Operation against a key holding the wrong kind of value
Every key has exactly one type. counter:1001 holds a plain string, so any hash command against it fails with WRONGTYPE. Check the key’s type with TYPE counter:1001 if you’re unsure, or use a namespaced naming convention (like user:1001:profile for hashes vs. counter:1001 for strings) so mismatches are obvious at a glance.
Mistake 3: Assuming HDEL‘s return value equals the number of field names you passed. As shown in Example 2, HDEL only counts fields that actually existed and were removed. Code that asserts HDEL key a b c must return 3 will break the moment one of those fields was already absent — treat the return value as “how many were removed,” not “how many were requested.”
Best Practices
- Use
HEXISTSinstead ofHGETwhen you only need a presence check — it’s cheaper and avoids falsy-value bugs with values like"0"or empty strings. - Remember that deleting the last field of a hash with
HDELdeletes the key itself; don’t write code that expects an empty hash to remain queryable afterward. - Batch related deletions into a single
HDEL key field1 field2 field3call rather than multiple round trips — it’s both faster and atomic as one step. - Check a key’s type with
TYPE keybefore running hash commands against keys whose origin you’re unsure of, to avoidWRONGTYPEerrors in production. - Don’t rely on
HDEL‘s return value to detect “key doesn’t exist” versus “field doesn’t exist” — both produce the same0. UseEXISTS keyorTYPE keyfirst if you need to distinguish them.
Practice Exercises
- Create a hash
product:4004with fieldsname,price, anddiscount_code. UseHEXISTSto confirmdiscount_codeis set, then remove it withHDELonce the promotion ends, and confirm withHEXISTSthat it’s gone whilenameandpriceremain. - Build a hash
session:xyz789with four fields representing a login session. Delete three of them in a singleHDELcall that also names one field that was never set, and predict the integer it returns before running it. - Create a hash with exactly one field, delete that field with
HDEL, then runEXISTSon the key and explain in your own words why the result is0.
Summary
HDEL key field [field ...]removes one or more fields from a hash and returns how many were actually removed, ignoring fields that weren’t present.HEXISTS key fieldchecks whether a field exists, returning1or0, without ever reading or transferring the field’s value.HEXISTSruns in O(1);HDELruns in O(N) for N fields being removed.- Deleting the last field of a hash removes the key itself — there’s no such thing as a persisted empty hash.
- Both commands are safe to call on a nonexistent key: they return
0, not an error. Only calling them against a key of the wrong type raisesWRONGTYPE. - Prefer
HEXISTSoverHGETfor presence checks to avoid falsy-value bugs in application code.
