Redis
November 21, 2024About 12 min
Redis
1. What is cache penetration? How to solve it?
- Answer: Um, let me think. Cache penetration refers to querying a certain non-existent data. Since the storage layer cannot find the data, it does not write to the cache, which will cause this non-existent data to query the DB every time, potentially leading to the DB crashing. This situation is likely an attack. The solution we usually use is a Bloom filter to solve it.
2. Can you introduce Bloom filters?
- Answer: Um, it is like this. A Bloom filter is mainly used to check whether an element is in a set. We used a Bloom filter implemented by Redisson. Its underlying principle is to first initialize a relatively large array, which stores binary 0 or 1. Initially, all are 0. When a key comes in, after three hash calculations, it finds the index of the data by taking the modulus of the array length, and then changes the original 0 in the array to 1. In this way, the positions of the three arrays can indicate the existence of a key. The lookup process is the same. Of course, Bloom filters may produce a certain amount of false positives, and we can generally set this false positive rate to not exceed 5%. In fact, this false positive is inevitable; otherwise, the length of the array would have to be increased. A false positive rate within 5% is generally acceptable for most projects, so it won't overwhelm the database under high concurrency.
3. What is cache breakdown? How to solve it?
- Answer: Um! Cache breakdown means that for keys with an expiration time set, when the cache expires at a certain point in time, a large number of concurrent requests come for this key at that moment. These requests find that the cache has expired and generally load data from the backend DB and reset it to the cache. At this time, a large number of concurrent requests may instantly overwhelm the DB. There are two solutions: First, you can use a mutex lock: when the cache expires, do not immediately load the DB, but first use Redis's SETNX to set a mutex lock. When the operation returns successfully, then perform the load DB operation and reset the cache; otherwise, retry the method to get the cache. The second solution is to set the logical expiration of the current key, roughly as follows: 1) When setting the key, set an expiration time field to be stored in the cache, without setting an expiration time for the current key; 2) When querying, check whether the time has expired after retrieving the data from Redis; 3) If it has expired, open another thread for data synchronization, and the current thread returns data normally, which may not be the latest. Of course, both solutions have their pros and cons: if strong consistency of data is chosen, it is recommended to use the distributed lock solution, but the performance may not be as high, and there may be deadlock issues. If logical deletion of the key is chosen, then high availability should be prioritized, with relatively high performance, but strong consistency in data synchronization cannot be achieved.
4. What is cache avalanche? How to solve it?
- Answer: Um! Cache avalanche means that when setting the cache, the same expiration time is used, causing the cache to expire simultaneously at a certain moment, and all requests are forwarded to the DB, which causes the DB to be under excessive pressure and collapse. The difference from cache breakdown is that avalanche involves many keys, while breakdown involves the cache of a specific key. The main solution is to spread out the cache expiration times. For example, you can add a random value, such as 1-5 minutes, to the original expiration time. In this way, the repetition rate of each cache's expiration time will be reduced, making it difficult to trigger a collective failure event.
5. How to synchronize MySQL data with Redis as a cache? (Dual-write consistency)
- Answer: Um! Let me talk about this project I recently worked on, which has the functionality of xxxx (according to my resume), requiring the database to maintain high consistency with Redis due to high timeliness requirements. We used read-write locks to ensure strong consistency. We used a read-write lock implemented by Redisson. When reading, a shared lock is added, which ensures that read-read is not mutually exclusive, while read-write is mutually exclusive. When we update data, we add an exclusive lock. It is mutually exclusive for both read-write and read-read, which ensures that while writing data, other threads cannot read the data, avoiding dirty data. It is important to note that the same lock must be used for both read and write methods.
6. How does this exclusive lock ensure mutual exclusion for read-write and read-read?
- Answer: In fact, the exclusive lock also uses SETNX at the bottom, which ensures that only one thread can operate the locked method at the same time.
7. Have you heard of delayed double deletion? Why not use it?
- Answer: Delayed double deletion means that for write operations, we first delete the data in the cache, then update the database, and finally delay the deletion of the data in the cache. However, it is not easy to determine how long this delay should be. During the delay, dirty data may appear, and it cannot guarantee strong consistency, so we did not adopt it.
8. How to synchronize MySQL data with Redis as a cache? (Dual-write consistency)
- Answer: Um! Let me talk about this project I recently worked on, which has the functionality of xxxx (according to my resume). Data synchronization can have a certain delay (which meets the needs of most business requirements). We used Alibaba's Canal component to implement data synchronization: no need to change business code, just deploy a Canal service. The Canal service disguises itself as a MySQL slave node. When MySQL data is updated, Canal reads the binlog data and then retrieves the data through the Canal client to update the cache.
9. How is data persistence done in Redis as a cache?
- Answer: Redis provides two ways of data persistence: 1) RDB; 2) AOF.
10. What is the difference between these two persistence methods?
- Answer: RDB is a snapshot file. It writes the data stored in Redis memory to disk. When the Redis instance crashes and recovers data, it can recover data from the RDB snapshot file. AOF stands for append-only file. When Redis executes write commands, they are all stored in this file. When the Redis instance crashes and recovers data, it will execute the commands in this file again to recover the data.
11. Which of these two methods recovers faster?
- Answer: RDB recovers faster because it is a binary file and has a smaller size when saved. However, it may lose data. We usually also use AOF to recover data in projects. Although AOF recovery is a bit slower, the risk of data loss is much smaller. In the AOF file, a disk flushing strategy can be set. We set it to batch write commands once per second.
12. What are the data expiration strategies in Redis?
- Answer: Um~, Redis provides two data expiration deletion strategies. The first is lazy deletion. After setting the expiration time for the key, we do not manage it. When the key is needed, we check whether it has expired. If it has expired, we delete it; otherwise, we return the key. The second is periodic deletion. This means that every period of time, we check some keys and delete the expired keys. There are two modes for periodic cleanup: 1) SLOW mode, which is a scheduled task with a default execution frequency of 10Hz, and each execution does not exceed 25ms. This can be adjusted by modifying the hz option in the redis.conf configuration file; 2) FAST mode, where the execution frequency is not fixed, and each event loop will attempt to execute, but the interval between two executions is not less than 2ms, and each execution does not exceed 1ms. Redis's expiration deletion strategy is a combination of lazy deletion and periodic deletion.
13. What are the data eviction strategies in Redis?
- Answer: Um, there are many types provided in Redis, and the default is noeviction, which does not delete any data and directly reports an error when there is insufficient internal memory. This can be set in the Redis configuration file. There are two very important concepts: one is LRU, and the other is LFU. LRU means least recently used. It uses the current time minus the last access time. The larger this value, the higher the eviction priority. LFU means least frequently used. It counts the access frequency of each key. The smaller the value, the higher the eviction priority. We set allkeys-lru in the project, which evicts the least recently used data, keeping frequently accessed keys in Redis.
14. If the database has 10 million data, and Redis can only cache 200,000 data, how to ensure that the data in Redis are all hot data?
- Answer: Um, let me think. We can use the allkeys-lru (which evicts the least recently used data) eviction strategy. The remaining data will be frequently accessed hot data.
15. What happens when Redis runs out of memory?
- Answer: Um~, this depends on what the Redis eviction strategy is. If it is the default configuration, Redis will directly report an error when the memory runs out. We set the allkeys-lru strategy to keep the most frequently accessed data in the cache.
16. How is distributed locking implemented in Redis?
- Answer: Um, Redis provides a command SETNX (SET if not exists). Since Redis is single-threaded, after using this command, only one client can set a value for a certain key. Other clients cannot set this key when it is not expired or deleted.
17. How do you control the effective duration of the distributed lock in Redis?
- Answer: Um, indeed. The SETNX instruction in Redis is not easy to control. We used a framework called Redisson to implement it. In Redisson, you need to manually lock and can control the lock's expiration time and waiting time. When a business that holds the lock has not yet completed execution, Redisson introduces a watchdog mechanism. This means that every period of time, it checks whether the current business still holds the lock. If it does, it increases the holding time of the lock. When the business execution is completed, it needs to release the lock. Another benefit is that under high concurrency, a business may execute quickly. When client 1 holds the lock, client 2 will not be immediately rejected. It will continuously try to acquire the lock. If client 1 releases it, client 2 can immediately hold the lock, thus improving performance.
18. Is the distributed lock implemented by Redisson reentrant?
- Answer: Um, it is reentrant. This is to avoid deadlock. This reentrancy is actually an internal check to see if the current thread holds the lock. If it does, it will count; if it releases the lock, it will decrease the count. When storing data, a hash structure is used, where the large key can be customized according to its business, and the small key is the unique identifier of the current thread, while the value is the number of reentrances of the current thread.
19. Can the distributed lock implemented by Redisson solve the master-slave consistency problem?
- Answer: This cannot be done. For example, when thread 1 successfully acquires the lock, the master node's data will be asynchronously replicated to the slave node. If the master node holding the Redis lock crashes, the slave node is promoted to the new master node. If thread 2 comes in now and tries to acquire the lock, it will successfully acquire the lock on the new master node, leading to the situation where two nodes hold the same lock simultaneously. We can use the red lock provided by Redisson to solve this problem. Its main function is that the lock should not be created on just one Redis instance, but should be created on multiple Redis instances, and it requires that the lock is successfully created on the majority of Redis nodes. In the red lock, it is required that the number of Redis nodes must be more than half. This can avoid the problem where thread 1 successfully acquires the lock and the master node crashes, leading to thread 2 successfully acquiring the lock on the new master node. However, if the red lock is used, because it requires adding locks on multiple nodes simultaneously, the performance becomes very low, and the operational maintenance cost is also very high. Therefore, we generally do not directly use the red lock in projects, and it has also been temporarily deprecated by the official.
20. If the business must ensure strong consistency of data, how should this be solved?
- Answer: Um~, Redis itself supports high availability. Achieving strong consistency will significantly affect performance. Therefore, if there is a high requirement for strong consistency in the business, it is recommended to use distributed locks implemented by ZooKeeper, which can guarantee strong consistency.
21. What are the solutions for Redis clusters, do you know?
- Answer: Um~~, there are three cluster solutions provided in Redis: master-slave replication, sentinel mode, and Redis sharding cluster.
22. Can you introduce master-slave synchronization?
- Answer: Um, it is like this. The concurrency capability of a single-node Redis has an upper limit. To further improve the concurrency capability of Redis, a master-slave cluster can be built to achieve read-write separation. Generally, it is one master and multiple slaves, where the master node is responsible for writing data, and the slave nodes are responsible for reading data. After the master node writes data, it needs to synchronize the data to the slave nodes.
23. Can you explain the process of master-slave synchronization?
- Answer: Um~~, okay! Master-slave synchronization is divided into two stages: full synchronization and incremental synchronization. Full synchronization refers to the first time the slave node establishes a connection with the master node, using full synchronization. The process is as follows:
- First: The slave node requests the master node to synchronize data, carrying its own replication id and offset.
- Second: The master node determines whether it is the first request, mainly based on whether the master node and the slave node have the same replication id. If not, it indicates that it is the first synchronization, and the master node will send its own replication id and offset to the slave node to keep the information consistent.
- Third: At the same time, the master node will execute BGSAVE, generate the RDB file, and send it to the slave node for execution. The slave node first clears its own data and then executes the RDB file sent by the master node, thus maintaining consistency.
- Of course, if requests still reach the master node during the RDB generation execution, the master node will record them in a buffer as commands, which is a log file. Finally, this log file is sent to the slave node, ensuring that the master node and the slave node are completely consistent. Later, when synchronizing data, it relies on this log file, which is full synchronization.
- Incremental synchronization refers to when the slave node service restarts, the data becomes inconsistent. At this time, the slave node will request the master node to synchronize data. The master node will still determine that it is not the first request. If it is not the first, it will obtain the offset value from the slave node, and then the master node will get the data after the offset value from the command log and send it to the slave node for data synchronization.
24. How to ensure high concurrency and high availability of Redis?
- Answer: First, a master-slave cluster can be built, along with using the sentinel mode in Redis. The sentinel mode can achieve automatic fault recovery of the master-slave cluster, which includes monitoring of master-slave services, automatic fault recovery, and notifications. If the master fails, Sentinel will promote a slave to master. When the faulty instance recovers, it will also be the new master. At the same time, Sentinel also acts as a service discovery source for Redis clients. When a fault transfer occurs in the cluster, it will push the latest information to the Redis clients. Therefore, most projects will adopt the sentinel mode to ensure high concurrency and high availability of Redis.
25. Are you using Redis in a single point or cluster, and which cluster?
- Answer: Um! We were using master-slave (1 master 1 slave) with sentinel. Generally, a single node does not exceed 10G of memory. If Redis memory is insufficient, independent Redis master-slave nodes can be allocated to different services. It is best not to do sharding clusters because maintaining clusters is relatively troublesome, and the heartbeat detection and data communication between clusters consume a lot of network bandwidth, and Lua scripts and transactions cannot be used.
26. How to solve the split-brain problem in Redis clusters?
- Answer: Um! This is rarely seen in projects, but the split-brain problem is like this. We are currently using Redis's sentinel mode cluster. Sometimes, due to network and other reasons, a split-brain situation may occur, meaning that the Redis master node and the Redis slave node and Sentinel are in different network partitions, causing Sentinel to fail to perceive the master through heartbeat. Therefore, a slave is promoted to master through election, resulting in two masters, just like a split brain. This will cause clients to continue writing data to the old master, and the new node cannot synchronize data. When the network recovers, Sentinel will demote the old master to slave, and then synchronize data from the new master, which will lead to a large amount of data loss in the old master. Regarding the solution, I remember that in the Redis configuration, you can set: first, you can set the minimum number of slave nodes, for example, at least one slave node must be present to synchronize data; second, you can set the delay time for master-slave data replication and synchronization. If the requirements are not met, requests will be rejected, which can avoid a large amount of data loss.
27. What is the role of sharded clusters in Redis?
- Answer: Sharded clusters mainly solve the problem of massive data storage. There are multiple masters in the cluster, each master stores different data, and each master can also be set with multiple slave nodes, which can further increase the high concurrency capability of the cluster. At the same time, each master monitors each other's health status through ping, similar to the sentinel mode. When clients request, they can access any node in the cluster, and ultimately, they will be forwarded to the correct node.
28. How is data stored and retrieved in a sharded cluster in Redis?
- Answer: Um~, in a Redis cluster, it is like this: Redis clusters introduce the concept of hash slots, with 16,384 hash slots. Each master node in the cluster is bound to a certain range of hash slots. The key is hashed using CRC16 and then taken modulo 16,384 to determine which slot to place it in, and then the corresponding node is found through the slot for storage. The logic for retrieving values is the same.
29. Redis is single-threaded, but why is it still so fast?
- Answer: Um, there are several reasons for this~~~
- Completely memory-based and written in C language.
- Uses a single thread to avoid unnecessary context switching and race conditions.
- Uses a multiplexing I/O model with non-blocking I/O. For example, BGSAVE and BGREWRITEAOF are executed in the background, not affecting the normal use of the main thread, and do not cause blocking.
30. Can you explain the I/O multiplexing model?
- Answer: Um~~, I/O multiplexing refers to using a single thread to listen to multiple sockets simultaneously, and when a certain socket is readable or writable, it gets notified, thus avoiding ineffective waiting and making full use of CPU resources. Currently, the I/O multiplexing is implemented using the epoll mode, which notifies the user process when the socket is ready and writes the ready sockets into the user space, eliminating the need to traverse each socket to check if it is ready, thus improving performance. The network model of Redis uses I/O multiplexing combined with event handlers to handle multiple socket requests, such as providing connection response handlers, command reply handlers, and command request handlers. After Redis 6.0, to improve performance, multi-threading is used in the command reply handler to process reply events, and multi-threading is also used in the command request handler to increase command conversion speed, while the command execution remains single-threaded.