一、Stringredis
经常使用命令:数据库
除了get、set、incr、decr mget等操做外,Redis还提供了下面一些操做:数组
获取字符串长度服务器
往字符串append内容数据结构
设置和获取字符串的某一段内容并发
设置及获取字符串的某一位(bit)app
批量设置一系列字符串的内容ide
应用场景:网站
String是最经常使用的一种数据类型,普通的key/value存储均可以归为此类,value其实不只是String,spa
也能够是数字:好比想知道何时封锁一个IP地址(访问超过几回)。INCRBY命令让这些变得很容易,经过原子递增保持计数。
实现方式:
m,decr等操做时会转成数值型进行计算,此时redisObject的encoding字段为int。
二、Hash
经常使用命令:
hget,hset,hgetall 等。
应用场景:
咱们简单举个实例来描述下Hash的应用场景,好比咱们要存储一个用户信息对象数据,包含如下信息:
用户ID,为查找的key,
存储的value用户对象包含姓名name,年龄age,生日birthday 等信息,
若是用普通的key/value结构来存储,主要有如下2种存储方式:
第一种方式将用户ID做为查找key,把其余信息封装成一个对象以序列化的方式存储,
如:set u001 "李三,18,20010101"
这种方式的缺点是,增长了序列化/反序列化的开销,而且在须要修改其中一项信息时,须要把整个对象取回,而且修改操做须要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称做为惟一标识来取得对应属性的值,
如:mset user:001:name "李三 "user:001:age18 user:001:birthday "20010101"
虽然省去了序列化开销和并发问题,可是用户ID为重复存储,若是存在大量这样的数据,内存浪费仍是很是可观的。
那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,
并提供了直接存取这个Map成员的接口,
如:hmset user:001 name "李三" age 18 birthday "20010101"
也就是说,Key仍然是用户ID,value是一个Map,这个Map的key是成员的属性名,value是属性值,
这样对数据的修改和存取均可以直接经过其内部Map的Key(Redis里称内部Map的key为field), 也就是经过
key(用户ID) + field(属性标签) 操做对应属性数据了,既不须要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。
这里同时须要注意,Redis提供了接口(hgetall)能够直接取到所有的属性数据,可是若是内部Map的成员不少,那么涉及到遍历整个内部Map的操做,因为Redis单线程模型的缘故,这个遍历操做可能会比较耗时,而另其它客户端的请求彻底不响应,这点须要格外注意。
实现方式:
上面已经说到Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不一样实现,这个Hash的成员比较少时Redis为了节省内存会采用相似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
三、List
经常使用命令:
lpush,rpush,lpop,rpop,lrange,BLPOP(阻塞版)等。
应用场景:
Redis list的应用场景很是多,也是Redis最重要的数据结构之一。
咱们能够轻松地实现最新消息排行等功能。
Lists的另外一个应用就是消息队列,能够利用Lists的PUSH操做,将任务存在Lists中,而后工做线程再用POP操做将任务取出进行执行。
实现方式:
Redis list的实现为一个双向链表,便可以支持反向查找和遍历,更方便操做,不过带来了部分额外的内存开销,Redis内部的不少实现,包括发送缓冲队列等也都是用的这个数据结构。
RPOPLPUSH source destination
命令 RPOPLPUSH 在一个原子时间内,执行如下两个动做:
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
将 source 弹出的元素插入到列表 destination ,做为 destination 列表的的头元素。
若是 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,能够把这种特殊状况视做列表的旋转(rotation)操做。
一个典型的例子就是服务器的监控程序:它们须要在尽量短的时间内,并行地检查一组网站,确保它们的可访问性。
redis.lpush "downstream_ips", "192.168.0.10"
redis.lpush "downstream_ips", "192.168.0.11"
redis.lpush "downstream_ips", "192.168.0.12"
redis.lpush "downstream_ips", "192.168.0.13"
Then:
next_ip = redis.rpoplpush "downstream_ips", "downstream_ips"
BLPOP
假设如今有 job 、 command 和 request 三个列表,其中 job 不存在, command 和 request 都持有非空列表。考虑如下命令:
BLPOP job command request 30 #阻塞30秒,0的话就是无限期阻塞,job列表为空,被跳过,紧接着command 列表的第一个元素被弹出。
1) "command" # 弹出元素所属的列表
2) "update system..." # 弹出元素所属的值
为何要阻塞版本的pop呢,主要是为了不轮询。举个简单的例子若是咱们用list来实现一个工做队列。执行任务的thread能够调用阻塞版本的pop去获取任务这样就能够避免轮询去检查是否有任务存在。当任务来时候工做线程能够当即返回,也能够避免轮询带来的延迟。
四、Set
经常使用命令:
sadd,srem,spop,sdiff ,smembers,sunion 等。
应用场景:
Redis set对外提供的功能与list相似是一个列表的功能,特殊之处在于set是能够自动排重的,当你须要存储一个列表数据,又不但愿出现重复数据时,set是一个很好的选择,而且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
好比在微博应用中,每一个人的好友存在一个集合(set)中,这样求两我的的共同好友的操做,可能就只须要用求交集命令便可。
Redis还为集合提供了求交集、并集、差集等操做,能够很是方便的实
实现方式:
set 的内部实现是一个 value永远为null的HashMap,实际就是经过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的缘由。
五、Sort Set
经常使用命令:
zadd,zrange,zrem,zcard等
使用场景:
以某个条件为权重,好比按顶的次数排序.
ZREVRANGE命令能够用来按照得分来获取前100名的用户,ZRANK能够用来获取用户排名,很是直接并且操做容易。
Redis sorted set的使用场景与set相似,区别是set不是自动有序的,而sorted set能够经过用户额外提供一个优先级(score)的参数来为成员排序,而且是插入有序的,即自动排序。
好比:twitter 的public timeline能够以发表时间做为score来存储,这样获取时就是自动按时间排好序的。
好比:全班同窗成绩的SortedSets,value能够是同窗的学号,而score就能够是其考试得分,这样数据插入集合的,就已经进行了自然的排序。
另外还能够用Sorted Sets来作带权重的队列,好比普通消息的score为1,重要消息的score为2,而后工做线程能够选择按score的倒序来获取工做任务。让重要的任务优先执行。
须要精准设定过时时间的应用
好比你能够把上面说到的sorted set的score值设置成过时时间的时间戳,那么就能够简单地经过过时时间排序,定时清除过时数据了,不只是清除Redis中的过时数据,你彻底能够把Redis里这个过时时间当成是对数据库中数据的索引,用Redis来找出哪些数据须要过时删除,而后再精准地从数据库中删除相应的记录。
实现方式:
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是全部的成员,排序依据是HashMap里存的score,使用跳跃表的结构能够得到比较高的查找效率,而且在实现上比较简单。
六、消息订阅Pub/Sub
Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你能够设定对某一个key值进行消息发布及消息订阅,
当一个key值上进行了消息发布后,全部订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用做实时消息系统,好比普通的即时聊天,群聊等功能。
客户端1:subscribe rain
客户端2:PUBLISH rain "my love!!!"
(integer) 2 表明有几个客户端订阅了这个消息
七、Transactions
谁说NoSQL都不支持事务,虽然Redis的Transactions提供的并非严格的ACID的事务(好比一串用EXEC提交执行的命令,在执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行),可是这个Transactions仍是提供了基本的命令打包执行的功能(在服务器不出问题的状况下,能够保证一连串的命令是顺序在一块儿执行的,中间有会有其它客户端命令插进来执行)。
Redis还提供了一个Watch功能,你能够对一个key进行Watch,而后再执行Transactions,在这过程当中,若是这个Watched的值进行了修改,那么这个Transactions会发现并拒绝执行。
Session 1
(1)第1步
redis 127.0.0.1:6379> get age
"10"
redis 127.0.0.1:6379> watch age
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379>
Session 2
(2)第2步
redis 127.0.0.1:6379> set age 30
OK
redis 127.0.0.1:6379> get age
"30"
redis 127.0.0.1:6379>
Session 1
(3)第3步
redis 127.0.0.1:6379> set age 20
QUEUED
redis 127.0.0.1:6379> exec
(nil)
redis 127.0.0.1:6379> get age
"30"
redis 127.0.0.1:6379>
第一步,Session 1 尚未来得及对age的值进行修改
第二步,Session 2 已经将age的值设为30
第三步,Session 1 但愿将age的值设为20,但结果一执行返回是nil,说明执行失败,以后咱们再取一下age的值是30,这是因为Session 1中对age加了乐观锁致使的。