TairHash is an in-house extended data structure of Tair that allows you to specify the time-to-live (TTL) and version number for each field. When a single user logs in from multiple devices, storing each session as a Redis string forces you to concatenate the user ID and device type into one key — for example, User_1_phone. TairHash eliminates this workaround by mapping the session structure directly to its data model: set key to the user ID, field to the device type, and value to the session token. Because TairHash supports TTL settings for both keys and fields, tokens expire independently per device with no extra application logic.
Why standard Redis strings fall short
For single-device sessions, Redis strings work well. Multi-device sessions expose three problems:
| Problem | Impact |
|---|---|
| String concatenation | Your application must build and parse composite keys like user_1_phone on every read and write |
| Encoding overhead | Repeated key parsing increases CPU and code complexity |
| Storage duplication | The user ID prefix is repeated across every device-specific key |
TairHash resolves all three: one key per user, one field per device, one TTL per field.
Use cases
Field-level TTL in TairHash fits several session management patterns:
-
Multi-device token management: Store one token per device under a single user key. Each token expires independently based on that device's session activity.
-
Active session tracking: Use
EXHGETALLto list all live sessions for a user. Expired fields are automatically excluded, so the result always reflects the current active state. -
Automatic session cleanup: Set a short TTL on tokens at login. When a user is inactive on a device longer than the TTL, that session is removed without any scheduled cleanup job.
Key commands
| Purpose | Command | Description |
|---|---|---|
| Write a token with TTL | EXHSET key field value EX seconds |
Sets a field-level TTL in seconds |
| Read all active tokens | EXHGETALL key |
Returns only fields that have not expired |
Prerequisites
Before you begin, ensure that you have:
-
A Tair instance (Alibaba Cloud Redis with Tair extensions enabled)
-
Python 3.8 or later
-
tair-py installed (
pip3 install tair) — source available at github.com/alibaba/tair-py
Store and retrieve session tokens
The following example stores tokens for two users across multiple devices, waits 6 seconds, then retrieves the remaining active tokens. After 6 seconds, user1's phone token (TTL = 5 s) has expired and is excluded from the results.
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import os
import time
from tair import Tair
from tair import ResponseError
def get_tair() -> Tair:
# Connect to a Tair instance.
# host: endpoint of your Tair instance
# port: default is 6379
# username: leave blank to use the default account
# password: read from environment variable TAIR_PASSWORD
return Tair(
host="r-bp************.redis.rds.aliyuncs.com",
port=6379,
db=0,
username="",
password=os.environ.get("TAIR_PASSWORD"),
)
def add_user_token(tair: Tair, user_id: str, device: str, token: str, ttl_seconds: int) -> bool:
# Store a session token using EXHSET with a per-field TTL.
# key = user_id (groups all devices for one user)
# field = device (identifies the device type)
# value = token (the session token)
# ex = TTL in seconds
try:
result = tair.exhset(user_id, device, token, ex=ttl_seconds)
return result == 1
except ResponseError as e:
print(e)
return False
def print_active_tokens(tair: Tair, user_id: str):
# EXHGETALL returns only fields that have not yet expired.
for entry in tair.exhgetall(user_id):
print("{}:{}".format(user_id, entry))
if __name__ == "__main__":
tair = get_tair()
user_1 = "user1"
user_2 = "user2"
# Write four tokens with different TTLs.
add_user_token(tair, user_1, "phone", "token_123", 5) # expires in 5 s
add_user_token(tair, user_1, "pad", "token_124", 10) # expires in 10 s
add_user_token(tair, user_2, "pad", "token_456", 10) # expires in 10 s
add_user_token(tair, user_2, "pc", "token_457", 10) # expires in 10 s
# Wait 6 seconds. user1's phone token (TTL = 5 s) will have expired.
print("Waiting 6 seconds...")
time.sleep(6)
# Only 3 of the 4 tokens are returned; user1/phone has expired.
print_active_tokens(tair, user_1) # prints user1/pad only
print_active_tokens(tair, user_2) # prints user2/pad and user2/pc
Expected output:
Waiting 6 seconds...
user1:{field: pad, value: token_124}
user2:{field: pad, value: token_456}
user2:{field: pc, value: token_457}
user1/phone is absent because its 5-second TTL elapsed before the query. The remaining three tokens, each set with a 10-second TTL, are still active.