preface
Redis is an in-memory database, so the data is in memory. If there is a sudden outage, the data will be lost (assuming that non-volatile memory is not used), and Redis provides a persistence mechanism to prevent this from happening
If you use Redis only as a cache and don’t need to quickly generate cached data after an outage, you can eliminate persistence and improve performance
Redis provides the following two methods of persistence:
- Redis database (RDB) : periodically saves data in memory ata certain point to disks
- AOF (Append only File) : Persists data by continuously incrementing redis write commands
The principle and implementation are described below
RDB
The snapshot principle
Redis implements data preservation by persisting snapshots taken in memory at a certain point in time to disk
How does Redis “capture” a snapshot of a moment in time?
A simple idea is that during persistence, no requests are processed, that is, the save command is executed
However, this can block online business. It is better to persist while responding to customer requests, also known as bgsave commands
In snapshot persistence mode, ensure that consistent snapshots are obtained
What does that mean? In the process of persistence, if the first half of the memory is persisted and the second half is persisted, the second half of the memory may be written to the first half and the second half of the data at the same time. The first half has been persisted and will not be modified. In this way, the data of the two parts are “inconsistent”
Here’s an example:
- I started out with A=1, B=2
- Serialize A=1 to disk
- A request changes A and B to 2 in memory, where A=1 on disk
- Continue serialization, write B=2 to disk
- The problem is that there is illegal data on disk, because there is never A time in memory when A=1, B=2, i.e
An older version of A
andA new version of B
It’s mixed up
So how do you get a consistent snapshot?
- Mysql Innodb’s repeatable read isolation level uses MVCC to ensure that each read operation obtains consistent data without blocking other read and write requests
- Redis uses multiple processes
When writing copy
(Copy on write) technology to achieve
When the Redis server receives the BGSave command, the fork function is called to produce a child process that creates the RDB file, and the main process continues to respond to the client request
When a child process is created, it shares data segments with its parent process, which is a memory saving mechanism of the operating system
Only when a process needs to make changes to the data will it make a copy for its own use and modify the copied page. In other words, the pages traversed by the child process during snapshot generation are not modified, and are always just forked out.
The page just forked out is definitely a snapshot of consistency at that point in time, so there is no problem serializing it to disk
To save time
At what point does Redis trigger a child fork to generate a snapshot file?
To save the configuration file, run the following command:
save 900 1
save 300 10
save 60 3600
Copy the code
The above configuration means that Redis automatically executes the bgsave command when:
900 seconds
At leastA key
Send changes (add, modify, delete)300 seconds
At leastTen key
Send a change60 seconds
At least3600个key
Send a change
Why do I need to configure multiple rules?
Imagine if there were only intermediate rules (at least 10 key changes in 300 seconds)
If only 9 keys change in redis, then no matter how long it takes, those 9 keys will never be persisted, because the condition for sending at least 10 changes in 300 seconds is never met
Therefore, it is better to have a save XXX 1 configuration pocket in the Save configuration item to ensure that all changes in redis can be persisted within a certain period of time
If the number of changes is large in a short period of time, the persistence will be carried out at an interval of 300 seconds based on the unique configuration. If it goes down, changes made between the last persistence and the outage are lost. That’s up to 300 seconds of data lost
Therefore, in the Save configuration item, it is better to have a save XXX (less than 300 seconds) XXX (more than 10) configuration, so that more changes in a short time can be made earlier and more frequently. If you go down, you’ll only lose data at shorter intervals
How do you do that? It’s very simple
The following use go code examples, but no special syntax, does not affect the understanding
Redis maintains two variables:
dirty
: How many modifications have been performed since the last BGSavelastsave
: Time of the last BGSave
And the configured rule saveConfig:
Type saveConfig struct {Change int // how many keys are changed save 60 3600 time int // how many keys are changed save 60 3600 time}Copy the code
Each configuration is tried in each event loop, and bgSave is executed if a configuration condition is met
Interval := time.now ().sub (lastsave) // Try each configuration for saveConfig := range saveConfigs {// If a configuration condition is met, run bgSave if dirty >= saveConfig.Change && Interval >= saveConfig.Time {bgSave () break}}}Copy the code
AOF
Unlike RDB, which stores data in Redis as a snapshot, AOF records the state of the database by storing write commands executed by Redis. If the AOF file records all the commands ever, you can recover the data by replaying them in an empty Redis
Synchronization strategies
A write command goes through the following three steps from generation to writing to AOF file:
Command append, file write, file synchronization
After a write command is executed, it is appended to the memory buffer
At the end of the event loop, the pseudo-code for an event loop looks like this:
Func eventLoop() {for {// processFileEvents() {// processFileEvents(); ProcessTimeEvents () // flushAppendOnlyFile()}}Copy the code
At the beginning of the event loop, the file event, the client request read and write, is performed, which is where the command append is done
Before the end of an event loop, Redis uses the appendfsync value to decide what to do with the aOF command previously added to the memory buffer:
-
Always: Writes all the contents of the AOF_buf buffer and synchronizes the aOF file
- Since each event loop synchronizes data to disk, which is known to be much slower than memory, this configuration
The lowest efficiency
, butThe highest security
Because you can only lose data for one event loop if it goes down
- Since each event loop synchronizes data to disk, which is known to be much slower than memory, this configuration
-
Everysec: Writes all the contents of the AOF_buf buffer to the AOF file. If the time since the last synchronization is more than 1 second, the synchronization is performed
- The frequency of synchronization has changed from every time cycle to every second,
Improved efficiency
In the event of an outage, at most 1 second of data will be lost
- The frequency of synchronization has changed from every time cycle to every second,
-
No: All the contents of the AOF_buf buffer are written to the AOF file, but synchronization is not performed. The synchronization time is determined by the operating system
- In terms of efficiency
The fastest
So there is no need to wait for data to be synchronized to disk each time, but when to synchronize data is uncontrollable, yesThe risk of losing data over a long time range
- In terms of efficiency
File write and synchronization: In order to improve the efficiency of modern operating systems, when users write some data to disk (file write), the operating system usually stores the data in the memory buffer temporarily, and then flushes the data to disk after it fills up or exceeds a certain period of time. The operating system also provides file synchronization functions
It is worth noting that when configured to always, the disk is not synchronized every time a command is written, but once after an event loop. Multiple commands may be executed in a single event loop
Appendfsync is typically configured to everysec in production environments to preserve high performance while minimizing data loss
AOF rewrite
As the program runs, aOF files become larger and larger. If left unprocessed, database restarts or downtime using AOF files will take longer and longer to restore, even exceeding disk capacity limits. So Redis will periodically slim down aOF files to hold only the necessary data
So what is unnecessary data?
As an example, suppose the following six commands were historically executed against list
rpush list "A" // ["A"]
rpush list "B" // ["A","B"]
rpush list "C" // ["A","B","C"]
lpop list // ["B","C"]
lpop list // ["C"]
rpush list "D" "E" // ["C","D","E"]
Copy the code
But these six commands can be replaced with one:
rpush list "C" "D" "E"
Copy the code
As a result, both footprint and recovery time are reduced
Rewriting can be done in two ways:
-
Analyze the content about list in the existing AOF file and rewrite it
-
Read the list in memory and replace the list in the AOF file with rpush XXX
- Just like the one up here
rpush list "C" "D" "E"
- Just like the one up here
Obviously, the second method is simple and efficient, unlike the first method, which needs to design a complex algorithm to compare and process AOF files
Redis is a single-threaded application, and if you put aOF overrides on the main thread, redis will not be able to process customer responses during the overrides
To avoid this, Redis puts the work of rewriting the AOF file into the child process. This has the following advantages:
- The main thread can continue to provide external services, not affected by the aOF file rewrite
- The child process is based on the data at the moment of the fork and is not affected by subsequent actions of the main thread, which is a general advantage of the forked child approach
The child process generates a new AOF file based on the memory snapshot
The main thread continues to receive and execute new write commands during the subprocess rewrite. It may be that the actual state of the database after the subprocess finishes rewriting is inconsistent with the rewritten AOF file. Therefore, it is necessary to append the new write command during this period to the rewritten AOF file, and then replace the rewritten AOF file with the original AOF file, so that the REWRITING of AOF is complete
In order to solve the problem of data inconsistency, redis set up aof rewrite buffer, during the period of the child to rewrite, new write command in addition to be written to the original aof file, will be written to aof rewrite the buffer, so that after the child finished rewriting, can know what is the new order, will these new command appended to rewrite good aof files
Why does the new command need to be written to the original AOF file? The original AOF persistence logic is guaranteed to work properly and will not be affected if the rewrite fails
Appending the new command to the rewritten aOF file does not need to be executed in the child process, but in the main thread, because
- These new write commands won’t be many and won’t affect the main thread too much
- If you use child processes, you also need to consider how to merge new commands during appending, and ultimately need a synchronous operation to merge
Mixed persistence
If RDB is used alone, a lot of data may be lost, but if AOF is used alone, data recovery is much slower than RDB. So Redis 4.0 introduced hybrid persistence, which puts RDB files together with incremental AOF log files. In this case, the AOF log is the incremental update log from the end of RDB persistence to the current moment, which is usually small
This hybrid persistence approach includes RDBThe advantage of quick recovery
And AOFThe advantage of not losing large amounts of data
conclusion
- The RDB uses the copy-on-write technology to capture snapshots for persistence. You need to take various conditions into account when configuring and saving the snapshots
- Configure AOF synchronization policies based on service requirements. To avoid large files, you need to rewrite AOF files
- Hybrid persistence combines the best of both