How to Use Redis With Python – Real Python

Introduction

Redis

  • stands for Remote Dictionary Service
  • is a distributed dictionary
  • enables caching
  • follows a client-server architecture and asses request-response model

Setup

Startup a (local) redis server with redis-server <path-to-redis-config>


Considerations

Databases

  • A single redis server can hold up to 16 databases

Serialization

  • Keys in Redis must always be strings, and all values are natively serialized as bytes.
  • Python objects that are not directly serializable as strings, (e.g. datetime) must first be stringified

Data Types

Hashes

  • A Redis object typically has the following shape:
    • {hashkey: {key1: val1; key2: val2}
    • A ::Redis hash:: is akin to a Python dictionary nested one level deep
  • Functions involving hashes start with r.h*

Lists

  • Redis uses linked-list implementations
    • O(1) adds / deletions
    • O(n) indexing
  • Functions involving list have suffixes with the following meaning:
    • l: Operate from left of list
    • r: Operate from right of list
    • b: Operate in a blocking manner (Disjoint from l and r)

Sets

  • Functions start with r.s*

Pipelining

Allows for the delaying and batching of multiple requests calls to minimise round trip time (RTT) between client and server, hence improving processing speed.

Example Python usage:

with r.pipeline() as pipe:
    # Perform operations using `pipe.*` instead of `r.*`
	  pipe.execute()

Transaction Blocks

  • Transactions to ensure that atomicity of a set of operations is respected.
    • e.g. When a purchase is made, both the npurchased and the quantity has to be updated appropriately, otherwise neither should happen
  • A transaction starts with multi and ends with exec.

Example Python usage:

with r.pipeline() as pipe:
     pipe.multi()
     ...  # Commands that should be a transaction
     pipe.exec()

Watching

  • Redis is designed with the assumption the system has relatively low-collisions. With that in mind, it implements ::optimistic locking:: to scalably handle race conditions.
  • Tends to be used in conjunction with transaction blocks.

In Python,

with r.pipeline() as pipe:
	  # Raise redis.WatchError is `itemid` is modified by some other requests to the server
    # before the transaction completes
    pipe.watch(itemid)
 
	  pipe.unwatch()  # Removes all watches

Snapshotting

Redis reads and writes quickly because everything is done in RAM. Snapshot lets you persist the (full) state of the database to disk.

Recommended way is to specify changes in the config file

save         60 1

Which means create a database snapshot every 60 seconds if at least one write operation occurred in that 60 sec timespan.

Snapshotting is not very effective if you have a large database that has to be constantly available. In that case, use append-only file (AOF)


Additional Perforamance

The C package hiredis allows for a performance boost for some Redis operations.

Simply install Python wrapper: pip install hiredis and gain the performance boost where applicable