Friday, September 13, 2019

Using Redis as Data Store

Redis, if you're not familiar with it, is a high performance, in-memory, key-value database.  It is transient in nature, meaning it doesn't have a built-in means to store data to non-volitile memory (you know solid state drives, hard disk, optical disk, magnetic tape, floppy disk, barrel memory, clay cuneiform).  Redis has it's own protocol that is for the most part ASCII/UTF-8 based with support for non-ASCII/UTF-8 keys and values.  Redis has a relatively simplistic security model with a single optional password for access.  Further Redis doesn't support in-transit encryption directly and relies on firewall and encrypted proxy layers instead.

There are a handful of use cases for Redis.  Most of these focus on increasing the performance of web applications by holding data in memory instead of a more complex data store like a relational database, or NoSQL store like Mongo.  Even thought it's relatively straight forward, Redis has advanced features like transactions and clustering with automatic fail-over.  Advanced features aside, I'd like to focus the caching use case with a couple different models, such as Write-Through and Least Recently Used.

Write-Through Caching

Write-Through Caching (WTC) works best when Redis has enough memory to store all of the data you will use.  Items in a WTC typically are never evicted, beause a WTC doesn't lazy-load.  From this standpoint you could call a WTC an in-memory database that trades performance for reliability.  Redis counters this with sentinal capabilities.  But automatic-failover doesn't help much if you have a complete power failure in your data center(s), or need to relocate your servers, and don't have a hibernate of option.  So, you'll need to write-through to your backing store and have a method to resume from a full stop.

What is write-through you ask?  Basically, write creates, updates, and deletes to Redis when ever you would update your backing store.  This way the two are always synchronized.  Redis supports transactions, so it is possible to implement a distributed commit strategy. Think of something like this:

begin_redis_transaction();
update_redis();
if (update_backing_store() is successful) 
    commit_redis_transaction();
else
    roll_back_transaction();

When you read from a WTC, just query Redis, you don't need to do anything with the backing store.  Except, if your Redis server stops for some reason, you'll need to pull all of the records from the backing store and update Redis w/o the write-through logic above.

Some of the benefits of a WTC:
  • Guaranteed performance - reading is constant you don't pay a price for cache misses, because if it's not there, it doesn't exist.
  • Guaranteed accuracy - the cache doesn't rely only on item life expectancy to update with new values.
WTC Considerations:
  • Memory - the Redis data store must be big enough to hold all of your cacheable data
  • Resumption - after a catastrophic failure, it will take some time to reload the Redis store from the backing store, more for the more items you have.