Book Image

Instant Redis Optimization How-to

By : Arun Chinnachamy
Book Image

Instant Redis Optimization How-to

By: Arun Chinnachamy

Overview of this book

The database is the backbone of any application and Redis is a next generation NoSQL database which can provide ultra-fast performance if tuned and calibrated correctly. Instant Redis Optimization How-to will show you how to leverage Redis in your application for greater performance and scalability. Instant Redis Optimization How-to will show you how to make the most of Redis. Using real-world examples of Redis as a caching and queuing service, you will learn how to install and calibrate Redis to optimize memory usage, read and write speed, as well as bulk writes and transactions. If you want to use Redis for its blazing fast capabilities, then this book is for you.Instant Redis Optimization How-to shows you how to optimize and scale Redis with practical recipes on installation and calibration for performance and memory optimization as well as advanced features like PUB/SUB. This book starts by providing clear instructions on how to install and fine-tune Redis to work efficiently in your application stack. You will also learn how to maintain persistence, how to optimize Redis to handle different data types, as well as memory usage optimization. You will then learn how to use bulk writes and transactions, as well as publish/subscribe features to get the most out of Redis. Offering best practices and troubleshooting tips, this book will also show you how to manage and maintain performance. This book finishes by recommending the best client libraries for all major programming languages. By the end of this book you will know how to create blazing fast applications using Redis.
Table of Contents (7 chapters)

Optimizing memory (Intermediate)


Being a complete in-memory database, it is important to always have enough memory available for a Redis instance. As RAM is expensive in comparison to other resources, it is critical to optimize usage. In this recipe, we will see how we can optimize memory usage and how to prevent out-of-memory scenarios.

Getting ready

The memory can be used effectively by optimized design of the application, and also through server configurations. Redis provides a few transparent mechanisms to optimize memory usage. We can optimize the memory usage by following one or all the suggestions given in the next section. Few of these are inbuilt into Redis while others depend on the application requirements and design.

How to do it...

  1. Implement CPU/memory tradeoffs. Configure the maximum number of elements and maximum element size for special encoding in the 6379.conf file. Set the following variables:

    hash-max-zipmap-entries 64
    hash-max-zipmap-value 512
    list-max-ziplist-entries 512
    list-max-ziplist-value 64
    zset-max-ziplist-entries 128
    zset-max-ziplist-value 64
    set-max-intset-entries 512
    
  2. Use hashes whenever possible. Maximize the usage of hashes, which along with the special encoding above will increase the performance.

  3. Leverage auto-expiry of keys. Auto-expiry of volatile keys will help us to free memory automatically instead of waiting for an explicit key deletion. Set the expiry attribute to the keys using the following command:

    EXPIRE key Seconds
    

How it works...

In this section, let us discuss how the techniques in the previous section will help us use memory efficiently.

CPU/memory tradeoffs

We already discussed the special encoding used for small aggregate data types such as hashes, lists, sets, and sorted sets. The purpose of these encodings is to reduce the memory usage at the cost of CPU. These encodings are memory-efficient and take up to 10 times less memory. As soon as the size goes higher than the configured maximum size, the data set will be converted to normal encoding, which is completely transparent to the user.

Redis compiled with a 32-bit target leaves a lesser memory footprint compared to the 64-bit build, as the size of the pointer is small, but such an instance will be limited to 4 GB of maximum memory usage. The persistence files are compatible with both 32- and 64-bit instances and interchangeable without any trouble.

Maximize hash usage

Small hashes take less space in memory due to the special encoding discussed in the last section. So it is good to represent data in hashes whenever possible. For instance, if you want to store some data (ID, name, and price) for a product, use a single hash with all required fields instead of using different keys for each field. In this case, storing in a hash field will take three times less memory than storing the data as different keys.

For example, let us consider we want to cache small HTML fragments for our product catalog, in which the product ID ranges from 1,000 to 10,000. To store the fragments, we can use simple keys, with product ID as the key and the fragment as the value.

SET "Product:1000" "HTML snippet for 1000"
SET "Product:1001" "HTML snippet for 1001"
SET "Product:9875" "HTML snippet for 9875"

By using this method, we will end up creating 9,000 records in Redis. But we can save the same records as hash and save significant memory. Let us see how we can do that.

Every time we perform a SET operation, we will split the key into two parts. The first part will be used as the key and the second part as the hash field. In our case, the key name, Product:1000, will be split as key name Product:10 and field name 00. We will use the following command to set it:

HSET Product:10 00 "HTML snippet for 1000"

After adding all 9,000 items, the hash Product:10 will have a maximum of 100 fields and a maximum of 90 hashes in total, such as Product:11 and Product:12. By doing this, we have only 90 hash keys, each having 100 fields. This is a form of implicit presharding of keys. You can even save memory by keeping the key names short. For example, using pdt:10 is more efficient than using product:10.

If you are planning to store more than 100 fields in a hash, to take advantage of memory optimization, make sure the number is less than the value configured in 6379.conf.

hash-max-zipmap-entries 256
hash-max-zipmap-value 1024

Tip

Caution

Once the limit is crossed, the data set will be converted to normal encoding and the memory saving will be lost.

The memory saving achieved using this method is phenomenal, making Redis the most memory-efficient data store. When benchmarking with a proper HTML snippet and 9,000 products, the result of memory usage is as follows:

  • When stored as key-value pair, memory used was around 8.3 MB

  • When optimized using hashes, memory used was around 1.1 MB

Auto expiry

Auto expiry is used to delete a key after a certain time. We can set a timeout on a key using the EXPIRE command. The error in the expiry is 0 to 1 millisecond in the latest Redis build (Version 2.6). The command is:

EXPIRE key Seconds

After the timeout, the key gets deleted automatically. Such keys are called volatile keys in Redis. The timeout can be cleared using the SET or GETSET commands. A volatile key can be made persistent using the PERSIST command. All other commands such as INCR or HSET, which do not replace the value, will leave the timeout intact. If a key gets renamed using the RENAME command, the timeout is also transferred to the new key name.

The expiry information of the keys is stored in UNIX time and it is heavily dependent on the computer clock. This makes sure the time flows even when Redis is not running.

Tip

Caution

While moving the RDB file between two computers, the time desync between the two computers can affect the expiration.

Internally, Redis performs expirations in two ways, passive and active. In the passive method, the expired key gets deleted when accessed by the client. This is not perfect as the expired keys that are never accessed will keep using memory. In the active mode, Redis performs sampling of 100 records per second and deletes all the expired keys. If the sampling results have more than 25 percent expired keys, the process continues.

There's more...

If you are planning to use Redis as a smarter MemCached replacement or purely as a caching server, this section provides more information on how we can use memory efficiently to accomplish that. Let us note what a simple caching system should support:

  • Faster access to the cache data

  • Auto-expiration of old caches

  • Easier invalidation of caches, which is critical for any caching system

  • Should use minimum resources and have the ability to control the amount of resources

Auto-expiring caching system

Let us discuss how Redis can serve as a perfect caching server, meeting all the basic requirements mentioned previously, in the case of caching an HTML page of the product catalog. The catalog has two types of pages, list pages and product-information pages.

As we have already seen, Redis is ultrafast, having the ability to serve more than 10,000 requests per second with no trouble, when deployed in entry-level servers.

In a typical system, a process checks Redis for a valid cache. If available, it delivers the cached HTML data to the client. If not available in Redis, the process generates the HTML and adds it to Redis as a cache to serve future requests efficiently. The cache needs to be refreshed periodically with fresh data. In this case, the auto-expiry feature in Redis comes in handy. Let us assume that our pricing data is valid for a maximum of three hours. Then, we can set the expiry for the product pages and list pages to three hours (10,800 seconds). This ensures that our cache gets refreshed every three hours.

The most common challenge to any caching system is cache invalidation and invalidating all dependencies. Taking our previous example, we have got a new price for only one product and now both the list page and the particular product page become invalid. It is critical to invalidate the cache before the expiry happens. It is unwise to invalidate the whole cache and recreate them. We need to invalidate only the cache of pages dependent on the changed product. For this purpose, Redis sets can be used to keep track of dependencies and only the dependent cache data need be expired.

Redis stores the cache in the memory. If we have millions of products in our catalog, we do not want to buy and install RAM to support our whole catalog. We want to limit the memory used while removing the least accessed cache to make space for new caches. Redis provides a simple solution to this problem. We can control the maximum memory Redis can use for its data through /etc/redis/6379.conf.

maxmemory <<bytes>>

Tip

If you have limited memory but would like to store more records, use hashes as described in the last section.

Other parameters can be used to specify the algorithm to be used to reclaim the memory. The following are as per the Redis documentation:

  • volatile-lru: This removes a key among the ones with an expire set, trying to remove keys not recently used.

  • volatile-ttl: This removes a key among the ones with an expire set, trying to remove keys with little time to live.

  • volatile-random: This removes a random key among the ones with an expire set

  • allkeys-lru: This parameter is like volatile-lru but will remove all kinds of keys, both normal keys or keys with an expire set

  • allkeys-random: This parameter is like volatile-random but will remove all kinds of keys, both normal keys and keys with an expire set

For our example, volatile-lru makes sense as it will delete the keys with an expiry set and are the least recently used. This way we can limit the amount of memory used by Redis when used as a caching server.