目录
一.高级特性
1.发布订阅模式
publish 发布
subscribe 订阅
unsubscribe 取消订阅
2.redis事务
redis的单个命令是原子性的,但是多个命令我们该怎么保证原子性呢
事务命令
- multi 开启一个事务 会把后续的操作命令放进队列
- exec 执行事务
- discard 取消事务
- watch 相当于cas乐观锁机制
发生异常情况
- 编译异常:在exec前出现编译异常,事务会discarded也就是会取消这个事务。
- 运行异常:异常前的可以执行成功,异常的无法执行。(我们会发现这跟我们想的原子性不同)
那么如何保证原子性呢? 引入lua脚本
3.lua
为什么使用lua脚本
- 一次发送多个命令,减少网络开销
- 保证原子性
- lua file 可以实现命令的复用
redis中执行lua脚本
redis> eval lua-script key-num [key1 key2 key3.....] [value1value2 value3 ....]
- eval代表执行Lua语言的命令。
- lua-script代表Lua语言脚本内容。
- key-num表示参数中有多少个key,需要注意的是Redis中key是从1开始的,如果没有key的参数,那么写0。
- [key1 key2 key3...]是key作为参数传递给Lua语言,也可以不填,但是需要和key-num的个数对应起来。
- [value1 value2 value3...…]这些参数传递给Lua语言,它们是可填可不填的。
在lua中执行redis命令
redis.call(command, key [param1,param2...])
- command是命令,包括set、get、del等。
- key是被操作的键。
- param1,param2...代表给key的参数。
命令 set zs 500 用lua脚本执行的话是
192.168.115.132:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 zs 500
OK
192.168.115.132:6379> get zs
"500"
4.redis为什么这么快
- 与内存交互,没有磁盘IO
- IO多路复用
- 单线程
- 底层数据结构巧妙设计 比如跳表
二.过期策略
因为我们的redis是一个内存型数据库,我们的数据都是放在内存里面的!但是内存是有大小的! redis,里有个很重要的配置文件,redis.conf里面有 个配置,
# maxmemory <bytes> redis占用的最大内存
如果我们不淘汰,那么它的数据就会满,满了肯定就不能再放数据,发挥不了redis的作用!
过期策略就是把之前设置的expired的key,把已经过期了的淘汰掉。
如果把redis比作冰箱,数据比作菜,那么过期策略就是要把过期了的菜扔掉。
惰性过期
惰性过期是一种被动过期,就是只有当我们访问或者操作key的时候才会判断它是否过期。如果过期了就淘汰。
该策略就可以最大化地节省CPU资源,因为它平时不会去判断,所以也没有啥cpu损耗,因为只有访问的时候我才去判断一下!
但是却对内存非常不友好。因为你不实时过期了,该过期删除的就可能一直堆积在内存里面!极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
特点:对cpu比较优化 但是对内存不友好。
源码(expireIfNeeded db.c文件下1302行)
int expireIfNeeded(redisDb *db, robj *key) {
if (!keyIsExpired(db,key)) return 0;
if (server.masterhost != NULL) return 1; /* Delete the key */
server.stat_expiredkeys++;
propagateExpire(db,key,server.lazyfree_lazy_expire);
notifyKeyspaceEvent(NOTIFY_EXPIRED, "expired",key,db->id);
int retval = server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) : dbSyncDelete(db,key);
if (retval) signalModifiedKey(NULL,db,key);
return retval;
定期过期
惰性这种又会可能导致大量无效数据堆积在内存里面,我们总得有个办法来解决吧!不能让他一直堆在内存里面啊!
所以我们就有了一个定期过期策略,虽然实时性比不上定时的,但是也足够解决垃圾数据大量堆积在内存的这种情况!

redis的hash默认使用的是ht[0],ht[1]不会初始化和分配空间。
哈希表dictht是用链地址法来解决碰撞问题的。如果节点数量比哈希表的大小要大很多的话,那么哈希表就会退化成多个链表,哈希表本身的性能优势就不再存在,在这种情况下需要扩容。 Redis里面的这种操作叫做rehash。
那么它怎么做rehash的? ht[0],ht[1]
定期策略是定时去扫描一下key有没有过期。
定多久扫描一次由谁来决定?
redis 时间事件 ServerCron 隔多久去执行一次,hz(配置文件可配)每秒会执行多少次。hz10(默认的)代表100ms执行一次ServerCron。