The road to caching-the principle of Caffeine

how to use

Caffeine Cache refers to the Guava Cache API, and the usage is basically the same.

Cache<String, Object> cache = Caffeine.newBuilder()
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .maximumSize(1000)
            .build();

// cache.put("key1", "val");

Object val = cache.get("key1", key -> {
     return key + "_new";
});

System.out.println(val);
复制代码

Regarding other usages of Caffeine, I wo n’t go into details. Let ’s take a look at the advantages of Caffeine compared to other local cache frameworks. Some of the algorithms and ideas can bring us great help in programming.

Caffeine optimization

In addition to providing rich functions like Guava Cache, Caffeine uses a more efficient W-TinyLFU  algorithm for cache elimination  .
The W-TinyLFU algorithm combines the LRU and LFU algorithms. Let us first review these two algorithms.

LRU algorithm

As  mentioned in  LRUHashMap , LRU (the least recently used) uses a queue to store data items, and each time it will move the accessed data to the head of the team, and directly eliminate the data at the end of the team when it is eliminated. However, sporadic and periodic batch operations will cause the LRU hit rate to drop sharply.

LFU algorithm

LFU (least frequently used). His core idea is ** "If a data is used a few times in the most recent period, then the possibility of being used in a period of time in the future is also very small", ** will record the number of data access, when it needs to be eliminated During operation, the data with the least number of accesses is eliminated.

The difference between LFU and LRU algorithm is that the elimination rule of LRU is based on the access time, while LFU is based on the number of visits.
**
LFU algorithm elimination icon:

image.png


Disadvantages: The
LFU algorithm performs cache elimination based on the number of times. Still taking hot data as an example, one day a star XXX was derailed. The word XXX was searched for 100,000 times. But the relevant data of XXX star derailment is still in the cache, this data may take a long time to be eliminated.
In addition, the LFU algorithm requires extra storage space to record the number of accesses, and the storage consumption is also very large when the data volume is very large.

W-TinyLFU algorithm

Expiry strategy

The LFU algorithm needs to additionally record the number of visits. The easiest way is to use a large hashmap to store the number of visits of each data. However, when the amount of data is very large, the space occupied by the hashmap is also very large.
In W-TinyLFU, the data will first enter the Window LRU. After being eliminated from the Window LRU, it will enter the filter to filter. When the new data is higher than the data to be evicted, this data will be accepted by the cache. The purpose of this is mainly to accumulate a certain access frequency for new data, so as to pass through the filter and enter the subsequent cache segment.

W-TinyLFU using the Count-Min Sketch algorithm as the filter, the algorithm is the Bloom filter is a variant of.

**
Here is a brief review. The idea of ​​the Bloom filter is to create an array (similarly, it can also be bytes), hash multiple times for each data, and finally set the array position after the hash to 1 (array [ hash% length] = 1), the data is not stored directly, so as to determine whether the data may be duplicated. The Count-Min Sketch algorithm is also similar. Create different arrays based on different hash algorithms, hash multiple times for each data, and add +1 to the hash index position of the corresponding array of the hash algorithm. Since the hash algorithm conflicts, then When the count is finally taken, the smallest value in all the arrays can be taken.

image.png

(Count-Min Sketch algorithm)

In the implementation of Caffeine, a Long type array will be created first, the size of the array is 2, the size of the array is the number of data, if your cache size is 100, he will generate a long The size of the array is the power of 2 closest to 100, which is 128. In addition, Caffeine divides the 64-bit Long type into 4 segments, each segment is 16 bits, and is used to store the data access frequency count corresponding to 4 hash algorithms.

Segmented LRU (SLRU)

For long-term data retention, W-TinyLFU uses a segmented LRU strategy. Initially, a data item store is stored in the trial segment (ProbationDeque), and when it is subsequently accessed, it will be promoted to the protected segment (ProtectedDeque) (the protected segment accounts for 80% of the total capacity). After the protection section is full, some data will be eliminated back to the trial section, which may also cascade to trigger the elimination of the trial section. This mechanism ensures that hot data with small access intervals is preserved, and cold data with few repeated visits is recovered.

Read and write optimization

Guava Cache will be mixed with cache elimination operations when reading and writing, so it will waste some performance during read and write operations. In Caffine, these event operations are asynchronous, he submitted these events to the queue. Then, the default ForkJoinPool.commonPool (), or configure the thread pool yourself, perform the queue extraction operation, and then perform subsequent elimination and expiration operations. Each read and write operation has its own queue.

readBuffer

The read queue uses RingBuffer (reference: the high-performance lock-free queue Disruptor you should know ). To further reduce read concurrency, multiple RingBuffers (striped ring buffer) are used to hash to the corresponding RingBuffer by thread id. A significant feature of the ring cache is that it does not need to perform GC, and directly overwrites the expired data.
When a RingBuffer is full, it will trigger an asynchronous execution operation, and subsequent writes to the ring buffer will be discarded until the ring buffer can be used, so readBuffer records the read cache transaction is lossy. Because reading the record is to optimize the driving strategy, allowing him to lose.

writeBuffer

The write queue uses the traditional bounded queue ArrayQueue.

At last

Finally, a picture is used to describe the process from data generation to elimination in caffeine:

image.png

Guess you like

Origin juejin.im/post/5e9c558e5188256bdf72c324