redis
redis
- REmote DIctionary Server(远程字典服务器),是以字典结构(操作简单)存储数据的数据库,支持复杂的数据结构,其键的类型为字符串,其值的类型可以为字符串、哈希、列表、集合和有序集合
- 虽然其是作为数据库开发的,但更多人将其用作缓存和任务队列等
- 所有数据存储在内存,但可持久化到磁盘
- Redis不支持数据类型嵌套,例如集合的元素只能是字符串,而不能是集合或其他结构;哈希和列表也同样
- 字符串能容纳的最大大小为500MB即2^32-1位;哈希和列表能容纳的最多元素为2^32-1
- 集合使用哈希表实现,有序集合使用哈希表和跳跃表实现,有序集合在集合的基础上为每个元素都关联了一个分数
- 有序集合与列表的异同:
- 同:有序,不是按照大小自动排序,而是按照插入顺序有序,其中有序集合还可按照分数来进行查找。集合则无序
- 异:列表使用双向链表实现,有序集合使用哈希表和跳跃表实现
- 有序集合常见的使用场景是大数据排序,如游戏的玩家排行榜,所以很少会需要获得键中的全部数据;集合更适用于集合之间的运算
键Key命名规则
redis是键值数据库,键一般最好有个命名空间,如视频播放量这个键可以这么设置:view:video:id,其中view表示观看数,下面的video表示视频观看(有可能之后还有音乐和书籍等等),再下面的id表示这个特定视频的观看量
事务
Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis的最小执行单位,一个事务中的命令要么都执行,要么都不执行。
MULTI //标记一个事务块的开始
SET S a
SET S b
EXEC //执行所有事务块内的命令。
若事务块内有命令出现语法错误,则事务块中的命令都不会执行;若若事务块内有命令出现执行错误,则不影响其他命令的执行
事务中的每个命令的执行结果都是最后一起返回的,所以无法将前一条命令的结果作为下一条命令的参数
WATCH:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断
UNWATCH:取消WATCH命令对所有key的监视。当执行了EXEC后,也会取消对所有key的监视
提示:如果使用WATCH命令监测了一个拥有过期时间的键,该键时间到期自动删除并不会被WATCH命令认为该键被改变
过期时间
EXPIRE设置过期时间,单位秒,PEXPIRE单位为毫秒;TTL返回键的剩余时间,没有过期时间返回-1,没有key返回-2;PERSIST取消对key的过期时间设置
修改配置文件的maxmemory参数,可限制Redis最大可用内存大小(单位是字节),当超出了这个限制时Redis会依据maxmemory-policy参数指定的策略来删除不需要的键直到Redis占用的内存小于指定内存。删除的键可指定为设置了过期时间的键或全部的键,maxmemory-policy可指定为LRU或随机或最快将要过期的
事实上当指定LRU时,Redis并不会准确地将整个数据库中最久未被使用的键删除,而是每次从数据库中随机取3个键并删除这3个键中最久未被使用的键。删除过期时间最接近的键的实现方法也是这样。“3”这个数字可以通过Redis的配置文件中的maxmemory-samples参数设置。
排序
SORT可对列表、集合和有序集合进行排序,默认转换为双精度浮点数进行排序,若无法转换则出错,如对’a’ ‘b’进行排序,此种情况应按照字典序进行排序,加入ALPHA后缀即可,如SORT target ALPHA;默认从小到大进行排序,若从大到小则需要加入后缀DESC;可用LIMIT后缀指定排序范围
BY指定排序的参考,语法:BY 参考键,其中参考键可以为字符串或哈希类型的某个字段(表示为键名->字段名)。参考键中有一个*号来表示待排序的元素值,如SORT ID BY post_*->time,表示根据ID中的元素如id1、id2对应的post_id1和post_id2的时间time来排序
GET指定排序后返回的值而不是返回元素自身的值,同BY一样也使用*作为占位符,但GET可以有多个,而BY只能有一个,同时想返回元素本身用GET #
STORE参数指定将结果保存到的键
排序较为耗时其占用空间,所以开发中使用SORT命令时需要注意以下几点:
- 尽可能减少待排序键中元素的数量(使N尽可能小)
- 使用LIMIT参数只获取需要的数据(使M尽可能小)
- 如果要排序的数据数量较大,尽可能使用STORE参数将结果缓存
消息通知
- 任务队列
redis用list的LPUSH(生产者)和RPOP(消费者)实现,RPOP是非阻塞读,想要得知队列中有没有任务,只能不断地定时读取,当队列中没有任务时,会有很多无意义的读取,若使用阻塞读则可避免以上情况,redis中的阻塞读可用BRPOP(语法:BRPOP key 00位置为超时时间,0则永远阻塞)实现。另:redis没有实现阻塞写BLPUSH - 优先级队列(非优先队列)
BRPOP后可接多个key,如BRPOP key1 key2 key3 0,会优先从队列key1中取数据,若key1为空才会从key2中取,以此类推。称其为非优先队列是因为优先队列是在一个队列中的元素的优先顺序,而优先级队列是在多个队列中的队列优先顺序 - 发布/订阅
“发布/订阅”模式中包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或多个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息。
订阅:SUBSCRIBE channel发布:PUBLISH channel mes - 管道
客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输,即使不需要上一条命令的执行结果,也要等待上一条命令执行完。
Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的
Lua脚本
使用脚本的好处:
- 减少网络开销:5个命令需要向Redis发送5次请求,而使用脚本功能完成同样的操作只需要发送一个请求即可,减少了网络往返时延。
- 原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说在编写脚本的过程中无需担心会出现竞态条件,也就无需使用事务。事务可以完成的所有功能都可以用脚本来实现。
- 复用:客户端发送的脚本会永久存储在Redis中,这就意味着其他客户端(可以是其他语言开发的项目)可以复用这一脚本而不需要使用代码完成同样的逻辑。
在Redis的Lua脚本中不能使用全局变量,只允许使用局部变量以防止脚本之间相互影响,声明局部变量的方法为local变量名。
在Lua中只有nil和false才是假,其余值,包括空字符串和0,都被认为是真值。
Lua约定数组的索引是从1开始的,而不是0。
脚本使用:
- EVAL:格式,
EVAL 脚本内容 key个数 [key..] [arg..],如EVAL "return redis.call('SET', KEYS[1],ARGV[1])" 1 foo bar,等同于SET foo bar - EVALSHA:除了脚本内容外,格式和EVAL一样,只不过脚本内容被替换成了脚本的SHA摘要,避免脚本过大占用较大带宽。这样执行EVALSHA时,先根据脚本摘要在redis服务器中寻找脚本,若有则执行,没有则返回NOSCRIPT。
- 一般先计算脚本的SHA1摘要,并使用EVALSHA命令执行脚本。获得返回值,如果返回“NOSCRIPT"错误则使用EVAL命令重新执行脚本。
持久化
Redis支持两种方式的持久化,一种是RDB方式,另一种是AOF方式。前者会根据指定的规则“定时”将内存中的数据存储在硬盘上,而后者在每次执行命令后将命令本身记录下来。两种持久化方式可以单独使用其中一种,但更多情况下是将二者结合使用。
RDB (Redis database)
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的所有数据生成一份副本并存储在硬盘上,这个过程即为“快照”。Redis会在以下几种情况下对数据进行快照:
- 根据配置规则进行自动快照
save 时间(s) key的个数
- 用户执行SAVE或BGSAVE命令
- 执行FLUSHALL命令
- 执行复制(replication)时
AOF (append only file)
当使用Redis存储非临时数据时,一般需要打开AOF持久化来降低进程中止导致的数据丢失。开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同
集群
复制
作用:
- 一个是读写分离,分担 “master” 的读写压力
- 一个是方便做容灾恢复
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
当一个从数据库启动后,会向主数据库发送SYNC命令。同时主数据库接收到SYNC命令后会开始在后台保存快照(即RDB持久化的过程),并将保存快照期间接收到的命令缓存起来。当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存的命令。以上过程称为复制初始化。复制初始化结束后,主数据库每当收到写命令时就会将命令同步给从数据库,从而保证主从数据库数据一致。
从数据库不仅可以接收主数据库的同步数据,自己也可以同时作为主数据库存在,形成类似的树结构
哨兵
哨兵的作用就是监控Redis系统的运行状况,它的功能包括以下两个:
- 监控主数据库和从数据库是否正常运行。
- 主数据库出现故障时自动将从数据库转换为主数据库,之前的主数据库变为从数据库。
哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。 主从可以自动切换,系统更健壮,可用性更高(可以看作自动版的主从复制)。
系统可以有多个哨兵,哨兵之间相互监控
Cluster
Redis的哨兵模式基本已经可以实现高可用,读写分离,但是在这种模式下每台Redis服务器都存储相同的数据,很浪费内存,也就是说每台 Redis 节点上存储不同的内容