Redis source code reading-master-Slave replication-slave perspective, redis source code Slave perspective
Redis master-slave Replication
To improve performance and system availability, apsaradb for Redis performs master-slave replication, which can relieve the pressure on the master database and provide services when the master database fails. The master-slave replication of Redis is asynchronous replication. The returned results are two different from those of the client and the synchronous command to the slave database. The master database does not care about the execution results of the slave database, slave databases are discarded directly and are not returned to the master database. Redis master-slave replication is simple and efficient, but it is not reliable.
Redis master-slave replication is asynchronous; full synchronization (or incremental synchronization) + command Propagation
Slave Server
Initialize the Slave Server startup configuration. Set the synchronization status (repl_state) of the master database host (masterhost) and Slave Server based on the slaveof configuration. Listen to the client link like all servers and enable background tasks.
Background scheduled tasks include: triggering AOF rewrite, RDB snapshot, redis monitoring, status collection, master-slave synchronization related scheduled tasks, etc.
Scheduled tasks in the background of master-slave synchronization include, the slave Database connects to the master database, the slave database reconnects to the master database, the slave database sends synchronization progress to the master database, the master database sends heartbeat packet to the slave database, the master database deletes timeout, the slave database clears the synchronization buffer, and the master database refresh slave database status
Connect the slave database to the master database
After the slave database links to the master database, the preparation and interaction before synchronization are enabled, and the slave database changes its status along with the interaction with the master database. The following source code looks at the entire process (the code has been deleted)
Void syncWithMaster (aeEventLoop * el, int fd, void * privdata, int mask, the slave Database Synchronization status is changed from REPL_STATE_CONNECT => REPL_STATE_CONNECTING *. The slave database sends PING to the master database to ensure that the synchronization operation can be performed * send PING, waiting for receiving PONG */if (server. repl_state = REPL_STATE_CONNECTING) {// modify the synchronization status REPL_STATE_CONNECTING => REPL_STATE_RECEIVE_PONG server. repl_state = slave; err = sendSynchronousCommand (SYNC_CMD_WRITE, fd, "PING", NULL) ;}/ ** receives the PONG response and modifies the status REPL_STATE_RECEIVE_PONG => REPL_STATE_SEND_AUTH */if (server. repl_state = REPL_STATE_RECEIVE_PONG) {err = sendSynchronousCommand (SYNC_CMD_READ, fd, NULL); server. repl_state = REPL_STATE_SEND_AUTH;}/** there are multiple similar branches, for example: * Send authentication information * send information such as the port and IP address synchronized from the database to the master database, directly to the synchronization Command Branch * // ** the slave database will first try incremental synchronization (send psync command + current synchronization progress repl_offset ), this situation occurs after the slave database and master database are disconnected from the network. * In other words, if the slave database is connected to the master database for the first time, incremental synchronization does not exist (after all, you have not synchronized it before, * If the slave Database Synchronization progress lags behind the master Database Synchronization buffer (repl_backlog), full synchronization is also performed, subsequent instructions */if (server. repl_state = REPL_STATE_SEND_PSYNC) {// send incremental synchronization Request command if (slaveTryPartialResynchronization (fd, 0) = PSYNC_WRITE_ERROR) {err = sdsnew ("Write error sending the PSYNC command. "); goto write_error;} server. repl_state = REPL_STATE_RECEIVE_PSYNC; return;}/** receive incremental synchronization request results */psync_result = equals (fd, 1); if (psync_result = PSYNC_WAIT_REPLY) return; /* Try again later... * // ** incremental synchronization is not supported, that is, psync command is not supported. if Redis2.8 or above is supported, * full synchronization is performed and the sync command is sent */if (psync_result = PSYNC_NOT_SUPPORTED) {// send the sync command if (syncWrite (fd, "SYNC \ r \ n", 6, server. repl_syncio_timeout * 1000) =-1) {}}/** after the synchronization command is sent, call back the readSyncBulkPayload method to obtain the synchronized data from the master database */if (aeCreateFileEvent (server. el, fd, AE _READABLE, readSyncBulkPayload, NULL) = AE _ERR) {}/ ** modify the synchronization status to REPL_STATE_TRANSFER, that is, the synchronization data transmission status */server. repl_state = REPL_STATE_TRANSFER; return ;}
The above code is a bit long. Here we will summarize the steps: 1) slave sends its own information to the master; 2) Try incremental synchronization, and wait for the master to send back the synchronized data; 3) if incremental synchronization or incremental synchronization fails, full synchronization is performed and the synchronization data is waiting for the master to send back.
Full Synchronization
1. Send the sync command from the slave database to the master database, initiate full synchronization, and wait for the data to return
2. The master database receives the sync command, saves the memory data to the rdb file, and sends the file content to the slave database.
3. Receive the synchronized data from the database and save it to the local rdb file. Finally, write the content of the rdb file to the memory database. This completes full synchronization.
The above is a brief description of the synchronization process, but it is not that simple. For example, when multiple slave initiates a full synchronization request, the master database only performs bgsave once to save the memory snapshot to the rdb file.
Rdb is a persistent type of redis and a snapshot of the current redis memory data. So for full synchronization, what the master database sends to the slave database is the value (key => value) corresponding to each key in the memory ).
Step 3 of the following source code analysis (the code has been deleted)
Void readSyncBulkPayload (aeEventLoop * el, int fd, void * privdata, int mask) {// read the first row and obtain the size of the synchronized data volume if (server. repl_transfer_size =-1) {if (strncmp (buf + 1, "EOF:", 4) = 0 & strlen (buf + 5)> = CONFIG_RUN_ID_SIZE) {} else {// synchronize the size of the data volume server. repl_transfer_size = strtol (buf + 1, NULL, 10);} return;} // read data nread = read (fd, buf, readlen ); // update the last synchronization communication time server. repl_transfer_lastio = server. unixtime; // number of files saved Data to the local rdb file if (write (server. repl_transfer_fd, buf, nread )! = Nread) {} if (eof_reached) {// load data from the rdb file to the memory serverLog (LL_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory "); if (rdbLoad (server. rdb_filename )! = C_ OK) {} // after synchronization, slave modifies the master database information and begins to receive commands from the master database to spread replicationCreateMasterClient (server. repl_transfer_s); serverLog (LL_NOTICE, "MASTER <-> SLAVE sync: Finished with success");} return;}/** after synchronization, slave modifies the MASTER database information, start receiving master database command spread */void replicationCreateMasterClient (int fd) {server. master = createClient (fd); // set the server where the listener receives the data sent from the master database. master-> flags | = CLIENT_MASTER; server. master-> authenticated = 1; server. repl_state = REPL_STATE_CONNECTED; // modify the slave Database Synchronization status. The two parameters ** reploff and replrunid are required for incremental synchronization */server. master-> reploff = server. repl_master_initial_offset; // total number of master synchronization buffers (current slave Database Synchronization progress) memcpy (server. master-> replrunid, server. repl_master_runid, sizeof (server. repl_master_runid); // Save the master database runid}Command Propagation
After full synchronization (incremental synchronization) is completed, the master database accepts client commands and modifies the data. To keep the master and slave data consistent, these commands must also be executed on the slave database, what should I do? Of course, the client does not modify all slave databases one by one, but asynchronously sends the commands to all slave databases after the commands are successfully executed by the master database.
This part of the main work lies in the master database, so we won't talk about it anymore. Let's talk about it from the next master database perspective. Receiving commands from the master database from the slave database is actually the same as running commands from other clients. The slave database only checks whether commands sent from the master database are sent and updates the synchronization progress, the master database does not care about the result of executing commands from the slave database. Therefore, the slave database does not send execution results to the master database, and network I/O is omitted.
Heartbeat Detection
As we said in the beginning, Redis will have a scheduled task executed in the background, and the slave database will send ack + synchronization progress to the master database every second. The master database will also send PING commands regularly to detect the survival of all slave databases.
/** The slave database sends the ack + synchronization progress * psync ack repl_off */if (server. masterhost & server. master &&! (Server. master-> flags & CLIENT_PRE_PSYNC) replicationSendAck ();/** the master database regularly sends the PING command to detect the survival of all slave databases */if (replication_cron_loops % server. repl_ping_slave_period) = 0) {ping_argv [0] = createStringObject ("PING", 4); replicationFeedSlaves (server. slaves, server. slaveseldb, ping_argv, 1); decrRefCount (ping_argv [0]);}Risks
Redis's master-slave replication is very efficient, and there are not many fancy things. Based on Asynchronous synchronization, the client does not need to wait for the synchronization results, but it also brings some risks.
1) the master database sends only one command to the slave database. If the command times out and is lost, the slave database does not receive the command, causing inconsistency;
2) The slave database memory is full, and the master database cannot know it, and the synchronization progress will still be updated when commands are received from the slave database;
3) asynchronous replication brings about synchronization gaps, resulting in inconsistencies in a short period of time, which should be handled according to specific business;
4) when the client modifies the data in the slave database, the master will also be inconsistent. You can set the slave database to read-only;
Nonsense
The slave database has not been fully synchronized
Slave: Master. I want incremental synchronization (psync? -1)
Master: exo me? You have not synchronized all data. No, you must synchronize all data.
Slave: OK, master. Full Sync)
Master: Agree. Wait.
(Slave, etc)
Master: Next, rdb
Slave: accept!
(After the Slave full synchronization is complete, it goes online and commands are spread)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
......
After the master-slave network is disconnected and reconnected
Slave: Master, I just got disconnected. Did I miss something? I want to incrementally synchronize it (psync Master_id repl_off)
Master: Where did the waves go? Ask you not to answer me. All right, incremental synchronization. Wait.
Slave: OK, master.
(Slave, etc)
Master: Then, these are the commands you have not executed.
Slave: accept!
(After the Slave incremental synchronization is complete, it goes online and commands are spread)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
......
After the master-slave network is disconnected and reconnected, the slave lags behind too much.
Slave: Master, I just got disconnected. Did I miss something? I want to incrementally synchronize it (psync Master_id repl_off)
Master: Where did the waves go? Ask you not to answer me. No, you are behind (repl_off is too old). Let's talk about it first.
Slave: OK, master. Full Sync)
Master: Agree. Wait.
(Slave, etc)
Master: Next, rdb
Slave: accept!
(After the Slave full synchronization is complete, it goes online and commands are spread)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
Master: This is the command I just executed. Run it.
Slave: Okay (I won't tell you if the execution is finished)
......
Link to this article: Redis master-Slave replication-Slave perspective
Original article, reprinted