redis 学习笔记——数据同步、事务
- redis复制原理
///********* redis/src/server.h ****************
struct redisServer {
.....
char *repl_backlog; /* Replication backlog for partial syncs */
long long repl_backlog_size; /* Backlog circular buffer size */
long long repl_backlog_histlen; /* Backlog actual data length */
long long repl_backlog_idx; /* Backlog circular buffer current offset,
that is the next byte will'll write to.*/
long long repl_backlog_off; /* Replication "master offset" of first
byte in the replication backlog buffer.*/
.....
}
///********* redis/src/replication.c ****************
void feedReplicationBacklog(void *ptr, size_t len) {
unsigned char *p = ptr;
server.master_repl_offset += len;
/* This is a circular buffer, so write as much data we can at every
* iteration and rewind the "idx" index if we reach the limit. */
while(len) {
size_t thislen = server.repl_backlog_size - server.repl_backlog_idx;
if (thislen > len) thislen = len;
memcpy(server.repl_backlog+server.repl_backlog_idx,p,thislen);
server.repl_backlog_idx += thislen;
if (server.repl_backlog_idx == server.repl_backlog_size)
server.repl_backlog_idx = 0;
len -= thislen;
p += thislen;
server.repl_backlog_histlen += thislen;
}
if (server.repl_backlog_histlen > server.repl_backlog_size)
server.repl_backlog_histlen = server.repl_backlog_size;
/* Set the offset of the first byte we have in the backlog. */
server.repl_backlog_off = server.master_repl_offset -
server.repl_backlog_histlen + 1;
}
///********* redis/src/replication.c ****************
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
.....
/* Write the command to the replication backlog if any. */
if (server.repl_backlog) {
char aux[LONG_STR_SIZE+3];
/* Add the multi bulk reply length. */
aux[0] = '*';
len = ll2string(aux+1,sizeof(aux)-1,argc);
aux[len+1] = '\r';
aux[len+2] = '\n';
feedReplicationBacklog(aux,len+3);
for (j = 0; j < argc; j++) {
long objlen = stringObjectLen(argv[j]);
/* We need to feed the buffer with the object as a bulk reply
* not just as a plain string, so create the $..CRLF payload len
* and add the final CRLF */
aux[0] = '$';
len = ll2string(aux+1,sizeof(aux)-1,objlen);
aux[len+1] = '\r';
aux[len+2] = '\n';
feedReplicationBacklog(aux,len+3);
feedReplicationBacklogWithObject(argv[j]);
feedReplicationBacklog(aux+len+1,2);
}
}
/* Write the command to every slave. */
listRewind(slaves,&li);
while((ln = listNext(&li))) {
client *slave = ln->value;
/* Don't feed slaves that are still waiting for BGSAVE to start */
if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
/* Feed slaves that are waiting for the initial SYNC (so these commands
* are queued in the output buffer until the initial SYNC completes),
* or are already in sync with the master. */
/* Add the multi bulk length. */
addReplyMultiBulkLen(slave,argc);
/* Finally any additional argument that was not stored inside the
* static buffer if any (from j to argc). */
for (j = 0; j < argc; j++)
addReplyBulk(slave,argv[j]);
}
}
char master_replid[CONFIG_RUN_ID_SIZE+1]; /* Master PSYNC runid. */
long long master_initial_offset; /* Master PSYNC offset. */
- 如果slave节点是新添加或者重启后的,那么就会将offset设置为-1,发送“PSYNC ? -1”给master,master会返回master_replid 、全局的复制offset。然后slave和master就会进行全量重同步。
- 如果slave与master短时间(在master的backlog没有free之前)断开连接又重新连接,slave会将已获取数据的offset和master_replid通过PSYNC命令发送过去,master收到后会比较接收到master_replid与自身的server.replid是否相同以及请求的psync_offset是否在master保存的backlog的缓冲区范围内;
///********* redis/src/replication.c ****************
int masterTryPartialResynchronization(client *c) {
......
if (strcasecmp(master_replid, server.replid) &&
(strcasecmp(master_replid, server.replid2) ||
psync_offset > server.second_replid_offset))
{
/* Run id "?" is used by slaves that want to force a full resync. */
if (master_replid[0] != '?') {
if (strcasecmp(master_replid, server.replid) &&
strcasecmp(master_replid, server.replid2))
{
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
"Replication ID mismatch (Slave asked for '%s', my "
"replication IDs are '%s' and '%s')",
master_replid, server.replid, server.replid2);
} else {
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
"Requested offset for second ID was %lld, but I can reply "
"up to %lld", psync_offset, server.second_replid_offset);
}
} else {
serverLog(LL_NOTICE,"Full resync requested by slave %s",
replicationGetSlaveName(c));
}
goto need_full_resync;
}
/* We still have the data our slave is asking for? */
if (!server.repl_backlog ||
psync_offset < server.repl_backlog_off ||
psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))
{
serverLog(LL_NOTICE,
"Unable to partial resync with slave %s for lack of backlog (Slave request was: %lld).", replicationGetSlaveName(c), psync_offset);
if (psync_offset > server.master_repl_offset) {
serverLog(LL_WARNING,
"Warning: slave %s tried to PSYNC with an offset that is greater than the master replication offset.", replicationGetSlaveName(c));
}
goto need_full_resync;
}
........
//进行部分同步
}
- 全量重同步(SYNC)
- disk-backed:在接到slave的SYNC请求后,会fork一个子进程用来将内存中的数据写入RDB文件,同时会将新来的请求保存在一个临时的内存缓冲区中。待RDB文件完成后,将临时缓冲区的数据与原有的内存数据进行合并并释放临时缓冲区。在写RDB文件的过程中,新来的SYNC请求都会被放到一个队列中,当RDB文件完成后将RDB文件内容发送给队列中的所有slave。
- diskless:在接收到slave的SYNC的请求后,会等待一段时间(也可以配置不等待),等待过程中新来的SYNC请求也都会被放到等待队列中。master会与等待队列中的slave建立连接,将数据直接发送给这些slave。(这种方式的优点在于不写RDB文件,避免了磁盘I/O开销,提升了效率)
- 部分同步(PSYNC)
master在收到slave发来的PSYNC请求(异常情况上面已经讨论过了,这里不再考虑),master会比较slave发来offset与master当前backlog中的offset,将backlog中比slave多出的数据传输给slave。(注:目前redis 4.0 提出的PSYNC 2.0本人还没有深入研究,回头有时间将相关的知识分享出来)
- slave每秒都会ping master一次,并report 复制的情况
- master记录各个slave最后一次ping的timestamp
- 用户配置允许的网络延迟最大值min-slaves-max-lag,以及执行写操作的slave的数量 min-slaves-to-write
- EXEC: 负责触发并执行事务中的所有命令。EXEC命令的回复是一个数组,数组中的每一个元素都是执行事务中的命令所产生的回复,其中回复的顺序和命令发送、执行的先后顺序是一致的。当客户端处于事务状态时,所有传入的命令都会返回一个内容为QUEUED的状态回复。
- MULTI:用于开启一个事务,总是返回OK;MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令被放到一个队列中,不会被立刻执行,当EXEC命令被调用时,队列中的命令依次被执行。
- DISCARD:清空任务队列,并放弃执行事务,并且客户端从事务状态中退出。
- WATCH:为redis事务提供check-and-set(CAS)行为;如果一些key被WATCH,那么对这些key的操作会被监视;如果一个被监视的key在EXEC执行前被修改了,那么整个事务都会被取消,EXEC批量返回空回复(null multi-bulk reply)来表示事务已经失败。单个WATCH命令可以监控多个key。
- 事务错误
- 事务在执行EXEC前,入队的命令可能出错。例如,命令出现语法错误或者其他更为严重的错误,诸如内存超过最大限制之类的错误。
- 命令可能在EXEC调用后失败。例如,事务中的命令可能处理了错误类型的键导致事务失败。
- 回滚(roll back)
- 用CAS(check-and-set)实现乐观锁
- 脚本和事务
redis 学习笔记——数据同步、事务的更多相关文章
- redis学习笔记——主从同步(复制)
在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复 ...
- redis学习笔记 - Pipeline与事务
原文 Redis提供了5种数据结构,但除此之外,Redis还提供了注入慢查询分析,Redis Shell.Pipeline.事务.与Lua脚本.Bitmaps.HyperLogLog.PubSub.G ...
- Redis学习笔记-数据操作篇(Centos7)
一.基本操作 1.插入数据 127.0.0.1:6379> set name cos1eqlg0 OK 这样就在redis中设置了一个key-value键值对 2.查询数据 127.0.0.1: ...
- Redis学习笔记~目录
回到占占推荐博客索引 百度百科 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合). ...
- Redis学习笔记之ABC
Redis学习笔记之ABC Redis命令速查 官方帮助文档 中文版本1 中文版本2(反应速度比较慢) 基本操作 字符串操作 set key value get key 哈希 HMSET user:1 ...
- redis学习笔记(3)
redis学习笔记第三部分 --redis持久化介绍,事务,主从复制 三,redis的持久化 RDB(Redis DataBase)AOF(Append Only File) RDB:在指定的时间间隔 ...
- Redis学习笔记4-Redis配置详解
在Redis中直接启动redis-server服务时, 采用的是默认的配置文件.采用redis-server xxx.conf 这样的方式可以按照指定的配置文件来运行Redis服务.按照本Redi ...
- Redis学习笔记(2)——Redis的下载安装部署
一.下载Redis Redis的官网下载页上有各种各样的版本,如图 但是官网下载的Redis项目不正式支持Windows.如果需要再windows系统上部署,要去GitHub上下载.我下载的是Redi ...
- Redis学习笔记(1)——Redis简介
一.Redis是什么? Remote Dictionary Server(Redis) 是一个开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Value ...
随机推荐
- c#登录时保存账号密码到cookie
登陆界面有用户名.密码输入框,一个’记住账号密码‘的复选框. 1.登录时,勾选‘记住账号密码‘复选框,则会把用户名密码保存在客户端cookie里,保存时间为最大值(直到用户清除浏览器缓存或者取消勾选’ ...
- POJ 3624 Charm Bracelet 背包问题的解决方案
最简单的背包问题,标题应该是除了背包测试中心:您无法打开二维数组.我还没有开的二维.光看数据是不可能的. 太大. 有两种方法来提高全省内存DP: 1 所谓卷的阵列 2 反向表 久没做背包DP,突然认为 ...
- 在windows server2003下安装Redmine
原文:在windows server2003下安装Redmine 参考文献:Windows下使用_Ruby1.9.3安装RedMine2.2.3 参考资料:http://www.redmine.org ...
- jquery无缝滚动效果实现
demo如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- Android项目--浅析系统通讯录中的那些方法
系统通讯录,以前的版本虽然过时了,不过有些东西还是可以用. 1.开启系统联系人添加 /** 添加联系人 */ Intent intent = new Intent(Intent.ACTION_INSE ...
- IEnumerable<T>转换为IList<SelectListItem>
扩展方法IEnumerable<T>转换为IList<SelectListItem> ,提供@Html.DropDownList使用 由于在MVC中经常会使用到@Html. ...
- NET Framework 4.5新特性 数据库的连接加密保护。
NET Framework 4.5新特性 (一) 数据库的连接加密保护. NET Framework 4.5 ado.net数据库连接支持使用SecureString内存流方式保密文本. 一旦使用这 ...
- Android控件RecyclerView与ListView的异同
在我的一篇介绍Android新控件RecyclerView的博客(Android L新控件RecyclerView简介)中,一个读者留言说RecyclerView跟ListView之间好像没有什么不同 ...
- iOS 制作 framework 教程
直接看步骤 废话不多说,哈哈! 1.新建一个静态库工程: 2:取自己喜欢的名字: 3.删除向导所生成工程中的 Target: 3.删除TestFrameWork对应的工程文件夹: 5:删除bulid ...
- ToolBox Analysis & Design
话说小菜过做已近3年,虽出身PHP后项目大多涉及.net,系统也做得比较繁杂,从常见的CMS,企业OA,ERP,也涉及到电商系统的开发定制,爬虫,工具不一而足,其中web系统居多. 由于表现良好,时常 ...