redis 事务,持久化,日志,主从,VM

redis目前对事务的支持比较简单,只能保证一个客户端连接发起事务中的命令可以连续执行,而中间不会插入其他客户端的命令。

1.事务

一般情况下,redis接收到一个客户端发送的命令,立刻执行并返回结果。但是当连接发出multi命令时,此连接便进入事务上下文,redis把此连接发送的命令保存到一个列队当中,当此连接发送exec命令时,redis便开始按顺序执行列队中的所有命令,并将所有命令执行的结果大包一起返回给客户端连接,然后此连接便结束事务。

例如:

redis 事务,持久化,日志,主从,VM

例子中可以看出set 命令发出后并没有立即执行,而是存放在消息列队中。调用exec命令,set命令才开始连续执行,最后返回执行结果。

如果取消事务,可以使用discard 命令取消一个事务例如:

redis 事务,持久化,日志,主从,VM

discard命令的作用是清空事务列队中的命令,并退出事务。

另外,redis只能保证事务的每个命令能够连续执行,但是事务中如果有命令执行失败,redis并不会回滚操作。

2.持久化

redis 是基于内存的数据库,内存数据库有个严重的弊端:突然宕机或者断电时,内容的数据不会保存。为了解决这个问题,redis提供两种持久化方式:内存快照(snapshotting)和日志追加(append-only file).

内存快照方式是将内存中的数据以快照的方式写入二进制文件中,默认文件名为dump.rdb

redis每隔一段时间进行一次内存快照操作,客户端使用save或者bgsave命令告诉redis需要做一次快照操作。save命令在主线程中保存内存快照,redis由单线程处理所有请求,执行save命令肯可能阻塞其他客户端请求,从而不能快速的相应请求,所以建议不要使用save命令。另外要注意,内存快照每次都把数据完整的写入到硬盘,而不是只写入增量数据。所以如果数据量大,写入操作比较频繁,从而严重影响性能。

内存快照相关配置:

save  <seconds>  <changes>

上面的配置表示经过seconds秒或数据更改changes次进行一次快照操作。例如下面的配置

save 1000 1

表示 1000秒或者数据更改1次就进行快照。也可以设置多个方案

save 1000 1

save 300 39

save 19 1111

这样,其中一个成立,redis就会进行快照。

如果使用 bgsave 进行快照   只需要直接发送  bgsave 命令就可以了

如果成功会返回

redis 事务,持久化,日志,主从,VM

bgsave命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

而save是直接调用主进程所以会引起阻塞.

日志追加(aof)方式是把增加,修改数据的命令通过write函数追加到文件尾部(默认是appendoly.aof)。redis重启时读取appendonly.aof文件中的所有命令并且执行,从而把数据写入内容中。

另外,操作系统内核的I/O接口可能存在缓存,所以日志追加方式不可能立即写入文件中,这样就有可能丢失部分数据。幸运的是redis提供了解决的方法,通过修改配置文件告诉redis应该在什么时候使用fsync函数强制操作系统把缓存写入磁盘。有一下三种方法:

appendonly yes #启动日志追加持久化方式(yes|no)

#appendfsync always #每次收到增加或者修改命令就立即强制写入磁盘

appendfsync everysec #每秒强制写入磁盘一次

#appendfync no#是否写入磁盘完全依赖操作系统

日志追加方式有效降低数据丢失的风险,同时也带来了另外一些问题,即持久化文件不断变大。例如调用 incr nums 命令100次,文件就会保存100条incr nums 命令,其中99条是多余的,因为回复数据只需要set nums 100

为了压缩日志文件,redis 提供bgrewriteaof命令。当redis收到此命令,就是用类似内存快照方式将内存的数据以命令的方式保存到临时文件中,最后替换原来的日志文件。

3.主从同步

主从同步可以防止主机坏掉导致网站不能正常运行,这种方法即把从机设置为主机即可。redis支持主从同步,而且配置很简单。redis主从同步的有点如下:

master(主)可以有多个slave(从)

多个slave连接到同一个master,slave还可以连接其他slave形成图形结构。

不会阻塞master。当一个或者多个slave与master初次同步数据时,master可以继续处理客户请求。相反,slave在初次同步数据时会阻塞而不能处理客户端的请求(2.2以后不会阻塞)。

主从同步用来提高系统的伸缩性,比如多个slave专门用户客户端的读请求。

在master服务器上禁止数据持久化,只在slave服务器上进行数据持久化。

redis主从同步非常简单,设置好slave(从)服务器后,slave自动和master建立连接,发送SYNC命令。无论是第一次同步建立的连接还是断开后重新建立的连接,master都启动一个后台进程,将内存数据以快照的形式写入文件中,同时master主进程开始收集新的写命令并且缓存起来。master后台进程完成内存快照后,把数据文件发给slave,slave将文件保存到磁盘上,然后把数据加载到内存中。接着master把缓存命令发给slave,后续master收到的写命令都通过开始建立的连接发送给slave,当master与slave断开连接,slave自动重新建立连接。如果master同时收到多个slave发来的请求,其只启动一个进程写数据库镜像,然后发送给所有slave。

redis主从同步过程分为两个阶段,第一阶段如下:

1.slave服务器主动连接到master服务器

2.slave服务器发送sycn命令到master服务器请求同步·

3.master服务器备份数据库到rdb文件

4.master服务器把rdb文件传输给slave服务器

5。slave服务器清空数据库数据,把rdb文件数据导入数据库

完成第一个阶段,接下来master服务器把用户所有更改数据的操作,通过命令的形似转发给slave服务器,slave服务器只需要执行master服务器发送过来的命令就可以达到同步的效果。

相对于mysql来说,redis主从复制的配置很简答,只需要在slave服务器配置文件下添加

slaveof   master服务器ip:端口

例如:

slaveof 192.168.1.123:6379

4.虚拟内存

redis的数据保存在内存中,可能出现物理内存不足的情况。物理内存不足时,redis会使用虚拟内存(VM)。

VM是redis2.0新加的功能,之前redis把数据库中的所有数据放在内存中,随着redis的不断运行,使用内存会越来越大,最终导致内存不住。redis的VM与操作系统的VM相似,把很少访问的vlaue保存到磁盘中。与此同时,redis把value对应的key放在内存中,为了能够让redis快速定位到被被换出的value所在的磁盘位置,从而将其导入的内存中。

操作系统也有虚拟内存功能,为什么redis要重复‘制造*‘呢?主要原因有两点:

1.操作系统的VM是基于页的概念,比如linux系统中内个页是4kb,而redis大多数对象远小于4kb,一页行可能有多个redis对象。另外redis的集合对象类型与list,set可能存放在多个页上面。故redis自己实现可能达到控制换入的粒度。

2.redis将交换到磁盘的对象压缩,保存到磁盘的对象可以取出指针和对象元素信息。一般压缩后的对象比内存中对象小10倍,这样redis的vm比操作系统的VM少做很多I/O操作。

配置redis的VM

#开启VM

vm-enabled yes

#交换出来的value文件保存路径

vm-swap-file /tmp/redis.swap

#redis使用最大内存上线,超过后开始使用交换空间  单位字节

vm-max-memory  268435456

#设置每个页面的大小为32字节

vm-page-size 32

#最多在文件使用多少页面,swap文件的大小等于vm-page-size * vm-page

vm-pages 134217728

#用于执行value对象换出换入的工作线程数。0表示不使用工作线程

vm-max-threads 4

redis的vm只把value交换到磁盘中,而key依然存储在内存中,目的是让开启VM的redis和完全使用内存的redis性能基本保持一致。如果由于太多key而造成内存不住问题,redis的VM并不能解决。

和炒作系统一样,redis也按照页交换对象。一页只能保存一个对象,但是一个对象可以保存在多页中。

当redis使用的内存没有超过设置的vm-max-memory之前,不把任何value交换到磁盘中;当超过最大内存限制后,redis根据一下算法寻找一个对象交换到磁盘中:

swappability=age*log(size_in_memory);

其中age代表这个对象距离上一次被访问的时间,size_in_memry是这个对象在内存中占用的空间大小。redis采取的策略是把那些很少访问,而且占用内存又比较大的对象交换的齿盘空间中,但是第二个因素所占的权重更低,所以在公式中取log值。因为交换大对象时,需要占用更多的I/O和cpu资源。

应该更具自己的应用设置vm-page-size,设置太大浪费磁盘空间,设置太小照成swap文件出现过多碎片。

vm-max-threads 表示用于交换任务的工作线程数量,建议不要将vm-max-threads设置为0,设置为0时交换过程在主线程进行,从而阻塞其他用户。但也不是设置越大越好,因为太多的工作线程导致操作系统使用更多的时间来切换线程,从而降低效率。推荐吧vm-max-threads设置为服务器的cpu核心数。

为了理解VM子系统如何工作,需要了解对象在swap文件中如何存储。

swap文件中采用rdb文件的存储格式。swap文件被分割成固定数量的页,每页占用指定的数量的字节空间。在redis.conf中更具自己业务需求配置一下两个参数:

vm-page-size 设置每页的大小,默认值为32.

vm-pages设置能够使用的页数,默认值为134217728

redis在内存中保存一个bitmap以映射这些页是否被占用,每bit代表一个对应磁盘空间的页是否被占用。内存中保存这样一份映射表极大的增强了redis性能,同时,对内存的使用又非常少。

在持久化备份的时候,也会备份swap 中的数据只不过在备份时swap变为只读了,父进程和后台主进程只能访问swap。而父进程不能将value换入。

当一个value 交换到磁盘时,value对应的redis object 将被VM Pointer,VM pointer保存value在磁盘的信息。

交换过程:

从内存交换到swap中:

第一步。计算保存这个对象需要占用swap文件中多少页

第二步:在swap中寻找一段连续的空白页空间保存这个对象

第三步:把对象写入swap

从swap中交换到内存中

从swap文件交换到内存比较简单,因为在VM Pointer 中已记录对象在swap文件的页起始地址和占用页数,只要更具VM Pointer 把磁盘的对象换入内存即可。

上一篇:redis add 'vm.overcommit_memory = 1' to /etc/sysctl.conf


下一篇:Redis 实现了自己的 VM