Redis provides versatile data structures, but the humble hash truly shines for representing objects and encoding relationships. Supporting over 4 billion fields per hash out of the box, hashes are blazing fast thanks to constant time lookups provided by commands like HEXISTS.

In this comprehensive guide, we‘ll unpack the how and why of Redis HEXISTS, including example applications, usage statistics, performance trade-offs, internals and advanced implementation details that demonstrate why understanding this API is a must for any full stack or backend developer.

Hashes in Redis – A Primer

Before jumping into HEXISTS, let‘s recap hashes in Redis…

Hash Basics

A Redis hash maps field-value pairs to a key:

KEY_NAME
  - FIELD foo -> "bar"
  - FIELD deep -> {json}

The key acts as a namespace for the hash. Hashes are useful when:

  • Encoding objects
  • Storing semi-structured data
  • Representing entity relationships

Tradeoffs vs Other Structures

Hashes have less overhead than equivalent JSON documents or other structures. But they lack nesting capabilities. Redis 5 brings modules to fill gaps.

Redis Hash Commands

Common commands include:

  • HSET/HMSET – Set field value(s)
  • HGET – Get field value
  • HGETALL – Get all field-value pairs
  • HEXISTS – Check if field exists (Our topic!)

With this foundation, let‘s now focus on field existence checking with HEXISTS.

Diving Into HEXISTS In Depth

The HEXISTS command returns 1 if a hash has a specified field or 0 if it does not. Simple enough by itself, but as we‘ll see HEXISTS enables elegant data manipulation patterns.

Speed and Use Cases

With O(1) time complexity in Redis 6+, HEXISTS is fast. This speed powers use cases like:

  • Input Validation – check submitted fields exist
  • Write Path Gating – only update known fields
  • Read Path Caching – cache non-existence to avoid reads

Syntax and Examples

Syntax:

HEXISTS key field 

Returns: 1 if field exists, 0 if not

Example:

> HEXISTS user:1001 name # 1 
> HEXISTS user:1001 age # 0

Easy! Now we can explore more applied examples.

Input Validation with HEXISTS

Let‘s assume our app stores user profiles in Hashes:

USER:123
  - name bob
  - age 24 
  - city LA

If Bob updates his profile, we need to validate his input before saving changes as profile fields vary per user.

This is a perfect application for HEXISTS.

new_data = {
  ‘name‘: ‘Bobby‘, 
  ‘fun_times‘: 5555 # Invalid!
}

key = f‘USER:{user_id}‘

for field in new_data:
  if not redis.HEXISTS(key, field):
    raise ValidationError(f‘Field {field} does not exist‘)  

# Data clean, save updates  
redis.HMSET(key, mapping=new_data)

We use HEXISTS to guarantee Bob only updates pre-existing fields.

Field Vivification With HEXISTS

Vivification means "creation on access". We can use HEXISTS to vivify hash fields automatically:

def get_profile_field(user_id, field):
  key = f‘USER:{user_id}‘

  if not redis.HEXISTS(key, field):
     redis.HSET(key, field, None) # Create with temp value

  return redis.HGET(key, field) 

print(get_profile_field(123, ‘favorite_band‘)) # None

By checks for existence before access, fields pop into existence dynamically using only Redis hashes!

Usage Trends

How prevalent are hashes and by extension HEXISTS in real world Redis usage? Redis surveys give us insight.

Across 100 billion requests:

  • 56% simple Strings vs 44% complex structures
  • Of structured requests, 25% are Hashes (11% overall)

Given 1 in 10 requests use hashes, understanding HEXISTS is clearly important!

The Internals of HEXISTS

The speed and efficiency of HEXISTS comes down to Redis internals: hashes use highly specialized encodings.

Specifically, the hash object encoding used includes a dictionary mapping field names to positions in a contiguous array of field values.

KEY_NAME -> Object Encoding
  - dictionary: 
    --‘name‘ -> index 0
    --‘age‘-> index 1
  - array: 
    -- ‘John‘
    --  20

This allows constant time lookups of the field position, then O(1) array access of the value via index.

Comparing encodings, normal Redis keys use a dict encoding optimized for sparse key name spaces and easy deletion. Hashes trade this for ultra-dense field encoding at the cost of removability.

HEXISTS Performance & Cost Analysis

Given the internal structure powering O(1) performance, what are the precise space/time tradeoffs of using HEXISTS?

Below are Big-O complexities and exact byte costs of common hash operations with Redis 6:

Operation Time Complexity Bytes Per Field
HSET O(1) 2 bytes overhead + value size
HEXISTS O(1)
HGET O(1)
HLEN O(1)
HDEL single field O(N) scanning
HDEL all fields O(1)

Given hashes store metadata, field names and values contiguously in memory without overhead, the excellent performance of HEXISTS comes at low cost!

Implementing Hashes Effectively

Now that we understand HEXISTS internals, let‘s discuss best practices for real world usage.

Watch your Hash Size

Uncapped hashes in Redis can lead to blown out memory usage and performance cliffs. Set size limits per hash with LRU eviction to avoid outages.

Pipeline Commands

Redis pipelining batches requests over persistent connections for better throughput:

import redis

pipe = r.pipeline()

pipe.hexists(‘foo‘, ‘bar‘) 
pipe.hget(‘foo‘, ‘bar‘)

pipe.execute()

Optimize Serialization

Using lightweight proto serializations reduces parsing/network overheads vs JSON.

Read Replicas

Redirect reads to Redis read replicas to isolate query load from writes.

Conclusion

We covered a lot of ground across HEXISTS use cases, internals and implementation best practices.

To summarize, the HEXISTS command provides lightning fast hash field existence checking in O(1) time. This powers everything from input validation to automatic vivification.

Hashes may seem simple, but reveal many complexities. Ultimately, dedicating focus to all aspects of hashes will pay dividends in building faster and more scalable systems.

I enjoyed diving deeper on hashes and hope this guide unlocked new insight into your Redis toolbox! Please share any other questions below.

Similar Posts