redis事务
事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制。
并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
事务特性
满足事务一致性和隔离性
非原子性
在redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行,并不会回滚。非持久性
在 RDB 模式下,服务器可能在事务执行之后、RDB文件更新之前的这段时间失败,所以RDB模式下的Redis事务也是不持久的;在AOF的”总是SYNC“模式下,事务的每条命令在执行成功之后,都会立即调用fsync或fdatasync将事务数据写入到AOF文件。但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功。所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的。
缺点
遇到有查询的情况穿插在事务中间,不会返回结果:设置事务开始标志后,所有的命令都是queued,即使是查询指令。 如果后续的更新操作需要依赖于前面的查询指令,那redis事务就无法有效的完成任务。
事实上,事务中的每条命令都与redis服务器进行了一次网络交互。
小优化:
对于所有的get/set操作,可使用现有的mget/mset指令;对于各种不同类型的更新操作,可使用lua脚本将命令打包后,发送到服务器端一次执行。
事务执行命令
multi -> 启动一个事务
incr t1 -> 添加该命令到队列中
incr t2 -> 添加该命令到队列中
discard -> 事务回滚
exec -> 执行
watch命令(基于CAS的乐观锁)
在multi命令执行之前,可以指定待监控的Keys,然而在执行exec时,如果被监控的Keys发生修改,exec将放弃执行该事务队列中的所有命令。
多客户端并发执行情况下出现竞态争用,伪代码如下:
val = get mykey
val = val + 1
set mykey $val
客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加1后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。
watch命令可用于提供基于CAS的乐观锁,伪代码如下:
watch mykey
val = get mykey
val = val + 1
multi
set mykey $val
exec
如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的exec命令将执行失败。
读写键空间时的维护操作
在读取一个键之后(读操作和写操作都要对键进行读取),服务器会根据键是否存在来更新服务器的键空间命中(hit) 次数或键空间不命中(miss)次数,这两个值可以在INFO stats 命令的keyspace_hits属性和keyspace_misses属性中查看。
在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用Object idletime <key> 命令可以查看键key的闲置时间。
如果服务器在读取一个键时发现该键已经过期,那么服务器会先删除这个过期键,然后才执行余下的其他操作。
服务器每次修改一个键之后,都会对脏( dirty ) 键计数器的值增1 ,这个计数器会触发服务器的持久化操作。
如果服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据库通知。
键删除策略
redis使用惰性删除
和定期删除
两种策略来删除过期的键:惰性删除策略只在碰到过期键时才进行删除操作,定期删除策略则每隔一段时间主动查找并删除过期键。通过配合使用这两种策略,服务器可以很好地在合理使用CPU时间和避免浪费内存空间之间取得平衡。
数据库通知
可以让客户端通过订阅通知的方式,来获知数据库中键的变化,以及数据库中命令的执行情况。通知有两种类型:
-
键空间通知(关注某个键执行了什么命令)
subscribe __keyspace@0__:aaa //客户端订阅键空间通知,0指0号数据库
-
键事件通知(关注某个命令被什么键执行了)
subscirbe __keyevent@0__:set //客户端订阅键事件通知
服务器配置的notify-keyspace-events选项决定了服务器所发送通知的类型:
想让服务器发送所有类型的键空间通知和键事件通知,可以将选项的值设置为AKE。
想让服务器发送所有类型的键空间通知,可以将选项的值设置为AK。
想让服务器发送所有类型的键事件通知,可以将选项的值设置为AE。
想让服务器只发送和字符串键有关的键空间通知,可以将选项的值设置为K$。
想让服务器只发送和列表键有关的键事件通知,可以将选项的值设置为El。
redis性能
设置key回收策略
redis默认配置不会回收key,当redis内存使用率超过可用内存的95%时,会触发内存交换(严重影响性能);通过设置maxmemory为系统可用内存的45%或95%和设置maxmemory-policy可以比较准确的限制redis最大内存使用率,在绝大多数场景下可确保redis不会进行内存交换。
若是启用了rdb持久化功能,应该设置maxmemory值为系统可使用内存的45%。因为rdb需要一倍的内存来复制整个数据集,也就是说如果当前已使用45%,在持久化期间会变成95%(45%+45%+5%),其中5%是预留给其他的开销。 如果没开启rdb持久化功能,maxmemory最高能设置为系统可用内存的95%。
当used_memory达到最大阈值maxmemory时,会触发数据淘汰,即回收key。
info evicted_keys可以看到淘汰的key数量。淘汰策略取决于maxmemory-policy:
volatile-lru: //使用LRU算法从已设置过期时间的数据集合中淘汰数据
volatile-ttl: //从已设置过期时间的数据集合中挑选即将过期的数据淘汰
volatile-random: //从已设置过期时间的数据集合中随机挑选数据淘汰
allkeys-lru: //使用LRU算法从所有数据集合中淘汰数据
allkeys-random: //从数据集合中任意选择数据淘汰
noenviction: //禁止淘汰数据
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。