Redis(二)—— 五大基本数据类型和三大特殊数据类型、过期策略
一、五大基本数据类型
1.1 String字符串类型
1、常用命令
#=================添加操作、删除操作、查询操作============= cj:15>set key1 7/25 # 新增一个key-value "OK" cj:15>del key1 # 删除一个key-value "1" cj:15>set key2 Tuesday "OK" cj:15>get key2 # 获取value值 "Tuesday" cj:15>append key2 ", OMG" # 追加字符串,返回追加后的字符串长度 "12" cj:15>get key2 "Tuesday, OMG" cj:15>append key3 "I'm a new key" # 如果向一个不存在的key去追加,相当于set一个新的 "13" cj:15>get key3 "I'm a new key" cj:15>strlen key3 # 获取字符串长度 "13"随着数据类型的丰富,redis里能放的东西越来越多,但毕竟内存还是很宝贵的,不能一直占着,所以就要加过期时间。如果大家都不设置过期时间的话,redis也要给自己兜底,这就用到了计算机里的经典理论——淘汰策略(先进先出啊、LRU啊等等)。最适合redis的是LRU(最近最少使用),这样留下的都是热点数据!
cj:15>set key4 Wednesday "OK" cj:15>expire key4 10 # 给key4设置过期时间,单位:秒 "1" cj:15>ttl key4 # time to live ,还剩多少生命值,单位:秒 "3" cj:15>ttl key4 # 当key过期时,ttl一直保持为-2 "-2" cj:15>ttl key4 "-2" cj:15>type key4 # 查无此键 "none" cj:15>type key3 # 返回value类型 "string" cj:15>incr key3 # 自增操作必须用在int上 "ERR value is not an integer or out of range" cj:15>set integerKey 5 "OK" cj:15>incr integerKey # 自增1 "6" cj:15>incrby integerKey 3 # 自增规定步长 "9" cj:15>decr integerKey # 自减1 "8" cj:15>decrby integerKey 2 # 自减规定步长 "6" cj:15>set key4 "hello,morning!" "OK" cj:15>getrange key4 0 5 # 返回字符串[0,5]范围内的字符串 "hello," cj:15>getrange key4 0 -1 # 返回完整字符串,相当于get key4命令 "hello,morning!" cj:15>setrange key4 0 good, # 替换指定位置开始的字符串 "14" cj:15>get key4 "good,,morning!" cj:15>setex key5 30 "set with expire" # setex=set with expire,新建键值对的同时设定过期时间 "OK" cj:15>ttl key5 "27" cj:15>setnx key5 "newKey5" # setnx=set if not exits,新建键值对的时候会判断是否已经存在,如果存在则不会覆盖(多用于分布式锁/乐观锁) "0" cj:15>get key5 # 发现并没有被新的值覆盖 "set with expire" cj:15>set key6 "original" # 对比单纯的set命令 "OK" cj:15>set key6 "new" "OK" cj:15>get key6 # 新的值覆盖旧的值 "new" cj:15>批量操作:
cj:15>mset k1 v1 k2 v2 k3 v3 # 批量设置key-value "OK" cj:15>mget k1 k2 k3 # 批量获取 1) "v1" 2) "v2" 3) "v3" cj:15>msetnx k1 v2 k4 v4 #批量设置,是一个原子操作,和事务一样,要么都成功要么都失败。因为k1已经存在了,所以整个操作失败,即使k4不存在也不会set成功。 "0" # 返回0,不成功 cj:15>getset k1 v1-1 # 先get再set,这样方便我们阅读 "v1" cj:15>get k1 "v1-1" cj:15>getset k5 v5 # 当get的值不存在时,返回null null cj:15>get k5 "v5"redis可以定义对象
方式(一):以JSON格式(其实就是key-value格式)保存一个对象
cj:15>set user:1 {name:dd,age:25} # 1号用户 "OK" cj:15>keys * 1) "k1" 2) "k2" 3) "user:1" 4) "k3" 5) "k5" cj:15>get user:1 "{name:dd,age:25}"方式(二): 把对象的两个属性定义成两个key
cj:15>get user:1 "{name:dd,age:25}" cj:15>mset user:1:name dd user:1:age 4 "OK" cj:15>mget user:1:name user:1:age 1) "dd" 2) "4" # 此种方式实际上定义了两个key cj:15>keys * 1) "user:1:age" 2) "user:1:name"注意,如果采用第一种方式,那么无法单独取出字段哦~
cj:15>set user:1 {name:dd,age:25} "OK" cj:15>mget user:1:name user:1:age # 此种方式不可获得如上定义的name和age字段 1) null 2) null cj:15>keys * 1) "user:1" cj:15>get user:1 # 只能通过全部get的方式 "{name:dd,age:25}"2、使用场景
数字int类型的string作为计数器,比如统计文章的浏览量,用incr key命令实现浏览量+1。你想啊浏览量这个东西如果每次+1你都写一次磁盘太浪费资源了,所以就先写到缓存里,再定时刷进磁盘。
比如统计粉丝数量,用decr key实现粉试数量-1。可以进行如下定义“id为794329用户的粉丝量为2”
set user:794329 {fansNum:2}1.2 List类型
- list在redis里其实是一个链表,所以才有头插和尾插操作。当删掉了最后一个元素时,这个链表也就不存在了
- 通过给list设置“先进先出”等规则,可以将list当做消息队列、堆、栈、阻塞队列使用。
- 所有命令以“L”开头
- list中的值是可以重复的,set不可以重复
1、常用命令
#####################增加操作、查看操作############################ cj:15>LPUSH mylist string1 #LPUSH是头插法(自己画图感受) "1" cj:15>LPUSH mylist string2 "2" cj:15>LPUSH mylist string3 "3" cj:15>LPUSH mylist string3 # string类型不能有重复的key,但是list可以有重复的值 "4" cj:15>LRANGE mylist 0 -1 # 打印全部的list,注意无法用get去获取全部的list 1) "string3" 2) "string3" 3) "string2" 4) "string1" cj:15>LRANGE mylist 1 2 # 从左到右打印[1,2]区间的list 1) "string3" 2) "string2" ######################删除操作####################### cj:15>RPUSH mylist right # 尾插法,插到最右边 "5" cj:15>LPOP mylist # 删除最左边的元素 "string3" cj:15>RPOP mylist # 删除最右边的元素 cj:15>LRANGE mylist 0 -1 # 先看一下有两个string3 1) "string3" 2) "string2" 3) "string1" 4) "string3" cj:15>Lrem mylist 1 string3 # 指定下标删除元素“1”代表移除从左数1个string3 "1" cj:15>LRANGE mylist 0 -1 1) "string2" 2) "string1" 3) "string3" ###################其他常见操作############### cj:15>Lindex mylist 0 # 下标为0的元素 "string2" cj:15>Lindex mylist 1 "string1" cj:15>Lindex mylist 4 # 下标为4没有元素 null cj:15>Llen mylist # list长度 "3" cj:15>Ltrim mylist 1 2 # 截取[1,2]部分,剩下的直接扔了 "OK" cj:15>LRANGE mylist 0 -1 1) "string1" 2) "string3" cj:15>rpoplpush mylist newlist #弹出mylist最右边元素,放到newlist最左边。如果没有newlist那就自动创建 "string3" cj:15>get newlist # 不能用get哦 "WRONGTYPE Operation against a key holding the wrong kind of value" cj:15>lrange newlist 0 -1 1) "string3" cj:15>lpop newlist # 删掉newlist所有元素 "string3" cj:15>exists newlist # 那么这个newlist也就不存在了(因为list底层是一个链表) "0" cj:15>exists mylist "1"###############指定下标更新操作################ cj:15>lset mylist 0 "heihei" # 替换下标为0的元素 "OK" cj:15>lrange mylist 0 -1 1) "heihei" 2) "string3" 3) "string2" 4) "string1" cj:15>lset mylist 8 "OK?" # 替换下标不存在的元素,报错 "ERR index out of range" ###################插入操作######################## cj:15>LINSERT mylist after "string3" "after3" # after表示插入string3的后面(从左到右) "5" cj:15>LINSERT mylist before "string1" "before1" # before表示插入string1的前面(从左到右) "6" cj:15>lrange mylist 0 -1 1) "heihei" 2) "string3" 3) "after3" 4) "string2" 5) "before1" 6) "string1"2、使用场景
模拟消息队列。甚至可以用ltrim命令截取部分信息。
1.3 set类型
1、常用命令
######################添加操作################ cj:15>sadd myset 1 "1" cj:15>sadd myset 2 "1" cj:15>sadd myset 4 "1" cj:15>sadd myset 3 "1" cj:15>sadd myset 8 "1" cj:15>sadd myset 0110 "1" ####################查看操作#################### cj:15>smembers myset # 并不是按照添加的顺序输出的,说明set的底层是用C++写的hashmap 1) "8" 2) "4" 3) "2" 4) "1" 5) "0110" 6) "3" cj:15>scard myset # set的元素个数 "6" cj:15>SISMember myset 9 # 是否是set的元素,“1”表示是 "0" cj:15>SISMember myset 8 "1" #######################删除操作################# cj:15>srem myset 3 "1" cj:15>srandmember myset # 随机抽取一个元素(抽奖) "2" cj:15>srandmember myset "0110" cj:15>srandmember myset "1" cj:15>spop myset # 随机删除一个元素 "2" cj:15>spop myset 3 # 随机删除3个元素 1) "4" 2) "1" 3) "0110" cj:15>smove myset newset 8 # 把元素“8”从myset移动到newset中去,没有newset就新建一个 "1" cj:15>smembers newset 1) "8"集合特有的一些操作:求并集,交集,差集
cj:15>sadd myset1 a "1" cj:15>sadd myset1 b "1" cj:15>sadd myset1 c "1" cj:15>sadd myset2 aa "1" cj:15>sadd myset2 bb "1" cj:15>sadd myset2 c "1" cj:15>smembers myset1 1) "b" 2) "c" 3) "a" cj:15>smembers myset2 1) "bb" 2) "aa" 3) "c" cj:15>SUNION myset1 myset2 # 求并集 1) "b" 2) "c" 3) "a" 4) "aa" 5) "bb" cj:15>Sdiff myset1 myset2 # 求myset1相对于myset2的差集 1) "b" 2) "a" cj:15>Sinter myset1 myset2 # 求交集 1) "c"2、使用场景
- 微博的共同关注
- 关注的人是不重复且无序的,刚好用set去存储。然后求两个set的交集
- 推荐好友
- 求你的关注列表这个set和你关注的某个人他的关注列表这个set的交集
1.4 Hashmap哈希类型
- 本质和string类型没有区别。也是key-value,只不过是一堆key-value存在一个hashmap里。直接理解成java里的new HashMap<String,Object>,同样key不能重复
- 最佳应用:存储变更的数据,尤其是用户的信息
- 对象存储用哈希!虽然string也可以存对象,但是不是最佳方法(太麻烦了!)
1、常用命令
##################增加、查看操作#################### cj:15>hset myhash k1 v1 k2 v2 k3 v3 #hset等同于hmset,可以同时存储多对key-value "3" cj:15>hget myhash k1 "v1" cj:15>hmget myhash k1 k2 k3 # 批量查看 1) "v1" 2) "v2" 3) "v3" cj:15>hgetall myhash # 查看所有键值对 1) "k1" 2) "v1" 3) "k2" 4) "v2" 5) "k3" 6) "v3" ##################删除操作############### cj:15>hdel myhash k2 "1" cj:15>hgetall myhash 1) "k1" 2) "v1" 3) "k3" 4) "v3" cj:15>hlen myhash # 键值对个数 "2" cj:15>hexists myhash k1 # 是否存在某个key "1" cj:15>hexists myhash k2 "0" #############等同于java中hashmap的keyset()和values()方法############# cj:15>hkeys myhash # 所有key 1) "k1" 2) "k3" cj:15>hvals myhash # 所有value 1) "v1" 2) "v3" ##################测试自增自减############ cj:15>hset myhash k5 5 "1" cj:15>hincrby myhash k5 1 # 自增特定步长 "6" cj:15>hincrby myhash k5 -1 # 自减特定步长 "5" cj:15>hdecrby myhash k5 1 # 没有hdecrby命令,自减的话用自增命令把值设为负数 "ERR unknown command `hdecrby`, with args beginning with: `myhash`, `k5`, `1`, " cj:15>hdecr myhash k5 # 也没有自-1命令,只有特定步长命令 "ERR unknown command `hdecr`, with args beginning with: `myhash`, `k5`, " cj:15>hincr myhash k5 # 也没有自+1命令,只有特定步长命令 "ERR unknown command `hincr`, with args beginning with: `myhash`, `k5`, " ###################修改操作##### cj:15>hsetnx myhash k5 7 # hsetnx表示已经存在就不要添加了 "0" cj:15>hset myhash k5 7 # 但是可以修改/覆盖 "0" cj:15>hgetall myhash 1) "k1" 2) "v1" 3) "k3" 4) "v3" 5) "k5" 6) "7" # 修改成功 ###################存储java对象################### cj:15>hset userMap name jj age 20 "2" cj:15>hgetall userMap 1) "name" 2) "jj" 3) "age" 4) "20"2、使用场景
储变更的数据(java对象),尤其是用户的信息
1.5 Zset类型
- 底层是linkedHashMap,把元素添加进去以后,哈希表给自动排好序了
- 有序的set,可以通过score这个指标去排序。有一些set的命令不能直接拿来用哦,比如smembers不能直接变成zmembers去用
- 要求我们向zset中添加元素时,带着score这个指标,用来作为排序的标准
1、常用命令
###################添加操作#################### cj:15>zadd salaryset 5000 qq 3000 dd 8000 mm 500 kk "4" ####################(升序、降序)查看操作################## cj:15>zrange salaryset 0 -1 # 自动按照工资的升序排序(kk最低,mm最高) 1) "kk" 2) "dd" 3) "qq" 4) "mm" cj:15>zrevrange salaryset 0 -1 # 自动按照工资的降序排序 1) "mm" 2) "qq" 3) "dd" 4) "kk" cj:15>zrevrangebyscore salaryset +inf -inf #按照[正无穷,负无穷]区间降序排列,注意前面的参数一定要大于后面的参数 1) "mm" 2) "qq" 3) "dd" 4) "kk" cj:15>zrevrangebyscore salaryset +inf -inf withscores # 并输出对应的score 1) "mm" 2) "8000" 3) "qq" 4) "5000" 5) "dd" 6) "3000" 7) "kk" 8) "500" cj:15>zrangebyscore salaryset -inf 7000 withscores # 升序,只看7000以下的 1) "kk" 2) "500" 3) "dd" 4) "3000" 5) "qq" 6) "5000" ########################删除操作##################3 cj:15>zrem salaryset kk "1" ######################统计元素个数操作#################### cj:15>zcard salaryset # 统计所有元素个数 "3" cj:15>zcount salaryset 300 600 # 统计score在[300,600]这个区间内所有元素个数。至于开区间和闭区间的设置自己去查哦!开区间可能参数要(300 600)这样写 "0" cj:15>zcount salaryset 300 7000 "2"2、使用场景
班级成绩表排序,消息权重排序(重要消息权重大)、工资表排序
二、3大特殊数据类型
2.1 Geospatial类型——周围的人
1、只有6个命令
# 语法: geoadd key的名称 经度 纬度 给坐标起个名字 # 注意一定要先经度再纬度,不然范围错了会报错 cj:11>geoadd china:city 116.40 39.90 beijing "1" cj:11>geoadd china:city 121.47 31.23 shanghai "1" # 查看 cj:11>geopos china:city beijing shanghai 1) 1) "116.39999896287918091" 2) "39.90000009167092543" 2) 1) "121.47000163793563843" 2) "31.22999903975783553" # 计算两个城市间距离 单位:m米 km千米 mi英里 ft英尺 cj:11>geodist china:city beijing shanghai km "1067.3788"GEORAIUDS命令和GEORADIUSBYMEMBER命令的区别:
- 这两个命令都是以指定位置为中心,画个圆。
- GEORADIUS的参数是指定经纬度,通常用于“寻找周围的人”。
- GEORADIUSBYMEMBER的参数是位置集合里的某个成员
# 语法 GEORADIUS 要查找的集合的key值 经度 纬度 半径 半径的单位 cj:11>GEORADIUS china:city 110 30 500 km 1) "chongqing" 2) "xian" # withdist参数打印距离,withcoord 参数打印经纬度,count 1参数指定要查找几个。比如附近1000km的人有200个,但我们只想看离我们最近的100个 cj:11>GEORADIUS china:city 110 30 500 km withdist withcoord count 1 1) 1) "chongqing" 2) "342.2210" 3) 1) "106.49899989366531372" 2) "29.52000010403299513" # 语法 GEORADIUSBYMEMBER 要查找的集合的key值 集合里某个位置的名字 半径 半径的单位 cj:11>GEORADIUSBYMEMBER china:city beijing 1000 km withdist 1) 1) "beijing" 2) "0.0000" 2) 1) "xian" 2) "910.8533"GEOHASH命令就是将位置转换成哈希值,基本上用不到这个命令。
2、使用场景
我的位置、周围的人、打车距离的计算
