所谓的原子性便是对数码的更动也许全体实施,
Redis 事务与过期时间详细介绍
数据库
1:使用redis有哪些好处?
一、Redis事务:
多数据库结构
一个Redis实例可以支持多个数据库,当客户端与服务端连接并指定到某个数据库时,两者的结构如下图所示:
客户端指定数据库
redisServer
和redisClient
是两个结构体,前者拥有数据库数组。当客户端使用SELECT
命令选择数据库是,会把客户端的db
指针指向数据库数组中的一个对象。
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
Redis中支持事务,事务即为当我们需要执行几条命令时,要么这几条命令都不执行,要么都执行:
数据库键空间
Redis的每个数据库都用redisDb
的结构体表示:
redisDb
dict
属性是一个字典,存储所有的键值对,叫做键空间。每个键都是一个字符串对象,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象当中的一种。
一个键空间的例子
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
1、开始事务写入:
键空间操作
在对数据库进行读写操作时,Redis还会执行一些额外的操作来记录一些指标,比如命中率,可以通过INFO
命令查看。
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
multi
键的过期时间
当用EXPIRE
/PEXPIRE
/EXPIREAT
/PEXPIREAT
给键设置过期时间时,过期时间会保存在redisDb
对象的expires
字典中:
保存过期时间
此处dict
和expires
中值相同的键其实是同一个对象,只是为了简化说明所以划成两份。
TTL
命令和PTTL
命令会计算当前时间和expires
中存储的时间的差值然后输出剩下的生存时间。
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
2、然后写入命令,注意写完事务要执行的每条命令之后回车即可,命令会自动入队:
过期键的删除策略
Redis删除过期键的策略结合了惰性删除和定期删除两种。
2:Memcache与Redis的区别都有哪些?**
lpush art:1 hello lpush art:1 nihao
惰性删除
当对键进行之行读取命令时,Redis会判断该键是否过期,如果过期那么就删除,然后继续执行命令。缺点是,如果一个键一直没有被访问,那么就永远停留在内存里,造成资源浪费,因此Redis还采用了定期删除的策略。
(1)、存储方式
3、执行事务:
定期删除
当定期删除被触发时,Redis会遍历各个数据库,然后从expires
字典中随机检查一部分键的过期时间,如果过期了那么就删除过期的键。
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
exec
AOF、RDB和复制功能对过期键的处理
Redis有部份存在硬盘上,这样能保证数据的持久性。
Redis则会保证事务中的所有命令要么都执行,要么都不执行。
生成RDB文件以及AOF重写
当Redis生成RDB文件或者进行AOF重写时,会对键进行检查,过期的键不会写入到文件中。
(2)、数据支持类型
二、Redis过期时间:
载入RDB文件
如果服务器以主服务器模式运行,那么过期的键不会被载入内存;如果服务器以从服务器模式运行,那么过期的键会载入内存。
Memcache对数据类型支持相对简单。
实际开发中经常会遇到一些有时效性的数据,比如缓存,过一段时间就需要删除这些数据,Redis支持设置一个键的过期时间,键过期后,其对应的值也会一并清除。
AOF文件写入
当服务器以AOF持久化模式运行时,如果某个键已经过期但还没有被删除,那么不会有任何影响。当过期键被删除后,Redis会向AOF文件追加一条DEL
命令,显示记录键已被删除。
Redis有复杂的数据类型。
1、新设置一个键值:
复制
当服务器运行在主从复制模式下运行时,当主服务器删除一个键后会向从服务器发送一个DEL
命令;当客户端在从服务器读取时即使碰到过期的键也不会删除,只有收到主服务器发来的DEL
命令后才会把键删除。
(3)、使用底层模型不同
set art:name hello
RDB持久化
Redis提供了RDB持久化功能可以将数据存储在硬盘里,我们可以使用SAVE
或者BGSAVE
命令创建RDB文件,两者的区别是SAVE
命令由服务器进程执行,执行时会阻塞进程,在此期间Redis会阻塞来自客户端的请求,直到RDB文件创建完毕;BGSAVE
命令由子进程执行,执行完毕后会通知服务器进程,因此不会阻塞客户端的请求。
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
2、设置其缓存时间
自动创建RDB文件
处了上述两个命令之外,我们还可以通过配置来让Redis自动创建RDB文件,其本质是自动执行BGSAVE
命令。
#自动执行BGSAVE命令的默认配置
save 900 1
save 300 10
save 60 10000
如果你进行了上面的配置,那么只要满足下面三个条件的任意一个,服务器就会执行BGSAVE
命令:
- 900秒内数据库进行了至少1次修改
- 300秒内数据库进行了至少10次修改
- 60秒内数据库进行了至少10000次修改
自动保存RDB文件的配置存储在redisServer
结构体的saveparams
数组中,其数据结构如图所示:
saveparam数据结构
redisServer
还有一个dirty
属性记录了数据库被修改次数,执行SAVE
或BGSAVE
命令成功后会自动归零。Redis通过一个周期性执行的函数,默认每100毫秒对自动配置以及dirty
属性进行检查,如果达到触发条件就执行BGSAVE
命令。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
expire art:name 20
RDB文件结构
RDB文件(版本6)结构如下图所示,图中的层次只是为了展示方便,实际并不存在。
RDB文件结构
3:redis常见性能问题和解决方案?
3、20秒后,得到其值为空
value的编码
RDB文件中的TYPE
属性决定了value的存储格式,下面依次介绍。
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
get art:name(nil)
字符串对象 [TYPE=REDIS_RDB_TYPE_STRING]
如果字符串对象的编码是REDIS_ENCODING_INT
,那么会以下面的结构存储:
存储结构
比如:
存储结构
如果对象的编码是REDIS_ENCODING_RAW
,那么有两种情况:
- 字符串长度小于等于20字节,其存储结构如下:

存储结构
如果字符串长度大于20字节,那么字符串会经过压缩再存储,其结构如下:
存储结构
`REDIS_RDB_ENC_LZF`表示经过LZF算法压缩。
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
4、查看一个值还有多久被删除:
列表对象 [TYPE=REDIS_RDB_TYPE_LIST]
对应的value的编码是REDIS_ENCODING_LINKEDLIST
,其存储结构如图:
存储结构
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
ttl art:name
集合对象 [TYPE=REDIS_RDB_TYPE_SET]
对应的value的编码是REDIS_ENCODING_HT
,其存储结构类似列表对象:
存储结构
(4) 尽量避免在压力很大的主库上增加从库
返回的是单位为秒
INTSET编码的集合对象 [TYPE=REDIS_RDB_TYPE_SET_INTSET]
value保存的是一个整数集合,Redis会把整数集合转换为一个字符串对象,然后把转换后出的字符串对象写入RDB文件。
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...
5、设置其缓存时间
哈希表对象 [TYPE=REDIS_RDB_TYPE_HASH]
对应的value的编码是REDIS_ENCODING_HT
,其存储结构如图:
存储结构
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
pexpire art:name 20000
有序集合对象 [TYPE=REDIS_RDB_TYPE_ZSET]
对应的value的编码是REDIS_ENCODING_SKIPLIST
,其存储结构如图:
存储结构
4:redis的并发竞争问题如何解决?
6、以1970年到现在的时间设置过期时刻:
ZIPLIST编码的列表、哈希表或有序集合 [TYPE=REDIS_RDB_TYPE_LIST_ZIPLIST,REDIS_RDB_TYPE_HASH_ZIPLIST,REDIS_RDB_TYPE_ZSET_ZIPLIST]
value保存的是一个压缩列表对象,Redis会把它转换为一个字符串对象,然后把转换后出的字符串对象写入RDB文件。
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
expireat art:name 1351858600 (时间为秒)pexpireat art:name 1351858600 (时间为毫秒)
AOF持久化
RDB持久化功能是把数据库的键值对存储到文件中,AOF持久化功能则是把服务器执行的写命令以Redis的命令请求协议格式存储到文件中。
在了解AOF持久化过程前,我们需要先了解Redis的进程模型,其本质是一个事件循环模型,流程图如下:
事件循环模型
当执行完一个写命令后,服务器会把命令以协议格式追加到AOF缓冲区的末尾,在每一个事件循环结束前会将AOF缓冲区中的内容写到AOF文件。
需要注意的是,写入文件不代表写入磁盘,现代操作系统中为提高效率,通常在写文件时会把数据写入内存缓冲区,等到缓冲区满时或者超过一定时间后才会真正写入磁盘,这一操作称为同步。
虽然每次事件循环都会写入AOF文件,但是并不是每次都会执行同步操作,Redis提供了appendfsync
配置项,有以下3个选项:
always
每一次事件循环都进行同步everysec
每隔一秒进行同步no
由操作系统决定是否同步
还原数据时,Redis会创建一个没有网络连接的伪客户端,依次执行AOF文件中的命令。执行完毕后数据库就恢复了之前的状态。
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
AOF重写
如果AOF文件中存储的命令不断增长下去,那么AOF文件会越来越大,载入时间会越来越长。Redis提供了一种重写AOF文件的方法,可以把多条命令合并成一条(或几条)生成一个新的AOF文件,并替换原文件。
RPUSH list "A" "B"
RPUSH list "C"
RPUSH list "D" "E"
LPOP list
LPOP list
RPUSH list "F" "G"
上面的5条语句经过重写后可以合并成下面一行命令:
RPUSH list "C" "D" "E" "F" "G"
Redis通过读取数据库当前键值对的方式进行AOF文件重写,而不是分析原来的AOF文件。
重写过程中会产生大量的写操作,为了不阻塞进程,Redis在子进程中进行重写工作。这样导致的一个问题是,当AOF重写正在进行时,可能会有新的命令被服务器执行,导致新的AOF文件和数据库状态不一致。Redis的解决方法是:除了AOF缓冲区外再增加一个AOF重写缓冲区,这个缓冲区只在开启子进程后使用,服务器把执行的写命令同时追加到这两个缓冲区的末尾,当子进程完成重写后通知服务器进程,服务器把重写缓冲区中的内容全部写入新的AOF文件,然后原子地替换原来的AOF文件。
2.服务器角度,利用setnx实现锁。
自动AOF重写
当serverCron函数执行时,它会检查以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:
- 没有RDB持久化或AOF持久化在执行;
- 没有AOF重写在进行;
- 当前AOF文件大小要大于
server.aof_rewrite_min_size
(默认为1MB),或者在redis.conf配置了auto-aof-rewrite-min-size
大小; - 当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比(在配置文件设置了
auto-aof-rewrite-percentage
参数,不设置默认为100%)
对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。
客户端
在网络连接部分,Redis使用了I/O多路复用技术,用单线程接收和响应客户端的请求。每个客户端对应一个redisClient
类型的结构体,多个客户端以链表的形式存储在redisServer
对象中。可以使用CLIENT list
命令查看所有的客户端信息,其中age
属性是客户端连接到服务器的秒数。
id=2 addr=127.0.0.1:35400 fd=6 name= age=8 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
可以使用CLIENT SETNAME name
为当前连接的客户端设置一个名字。
id=2 addr=127.0.0.1:35400 fd=6 name=hello age=603 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
5:redis事物的了解CAS(check-and-set 操作实现乐观锁)?
标志
redisClient
结构体中有一个重要的属性flags
,主要有以下几种标志常量:
REDIS_MASTER
表示客户端代表的是一个主服务器REDIS_SLAVE
表示客户端代表是的一个从服务器REDIS_LUA_CLIENT
表示客户端是专门处理Lua脚本的伪客户端REDIS_BLOCKED
表示客户端正在被BRPOP
、BLPOP
等命令阻塞REDIS_MULTI
表示客户端正在执行事务REDIS_FORCE_AOF
强制服务器将当前命令写入AOF文件REDIS_FORCE_REPL
强制主服务器将当前命令复制给所有从服务器
flags
属性可以是多个常量的组合,比如REDIS_LUA_CLIENT|REDIS_FORCE_AOF
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis中事务的实现特征:
服务器
当服务器接收到客户端发来的命令后,会在命令表中查找对应的命令,其数据结构如图所示:
命令表
每一个命令对应一个redisCommand
类型的对象,其中:
name
表示命令的名称proc
指向具体的实现函数arity
命令的参数个数,命令本身也是参数。如果该值是负数,如-N,则表示参数的数量大于等于Nsflags
命令的属性,比如是不是可写命令,是不是只读命令每种属性由一个字符表示,比如wm
表示这是一个可写命令(w
),并且可能需要占用大量内存,服务器需要进行内存空间的检查(m
),SET
命令的属性就是wm
服务器查找命令时是忽略大小写的。
1). 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
命令执行器
在执行命令前,服务器会执行一些检查工作,比如命令参数个数是否正确、客户端是否通过了身份验证、如果服务器开启了maxmemory
功能,那么还要检查内存占用情况并按需回收等步骤。
在命令执行后,服务器也会执行一些后续工作,以下列出部分:
- 如果开启了慢日志查询功能,服务器会检查是否要为刚才执行的命令添加一条慢查询日志
- 如果开启了AOF功能,那么就写入到AOF缓冲区
- 如果由其他从服务器正在复制当前这个服务器,那么服务器会把刚才执行的命令传播给所有从服务器
2). 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。