目录
1、Hset
2、Hget
有序集合
zadd
zscan
zrange(key,0,-1)
zrangebyscore()
zrem
zremrangebyscore(key,score1,score2)
zcard
pipeLine
对List操作的命令
lrange
rpush
lpush
lpop
rpop
使用场景
1.String类型的应用场景
2.list类型的应用场景
3.set类型的应用场景
4.zset(sorted set)类型的应用场景
什么是二进制安全的?
1、Hset
hset(key, field, value)
将哈希表 hash 中域 field 的值设置为 value。
如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行 HSET 操作。
如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。
当 HSET 命令在哈希表中新创建 field 域并成功为它设置值时, 命令返回 1 ; 如果域 field 已经存在于哈希表, 并且 HSET 命令成功使用新值覆盖了它的旧值, 那么命令返回 0 。
2、Hget
hget(key, field):返回名称为key的hash中field对应的value
hgetall(key):返回名称为key的hash中所有的键(field)及其对应的value
HGET 命令在默认情况下返回给定域的值。
如果给定域不存在于哈希表中, 又或者给定的哈希表并不存在, 那么命令返回 nil 。
----------------------------------------------------------------------------------------------------------------------
看了这个shell提示再结合概念理解起来就清楚很多了。
1、hset一条数据,并看懂其存储的数据结构。
2、这时候我们修改这个field中的value
这里的key就相当于tablename,如果域已经在hash表中,hset时会将旧值改为新值。
对于实际业务来说:
如果新增ope_id,定时器会定时刷新到redis的key = basicOpe中,
final List<MOpeD> basicOpeList = dao2.find(MOpeD.class, "FROM MOpeD where validFlg = 'Y'");
basicOpeList.forEach(ope -> {
pipelined.hset("basicOpe", String.valueOf(ope.getOpeKey()), JacksonUtil.toJSONStr(ope));
//可以看出key是basicOpe,field是ope_key,value是ope_id
basicOpeData.put(String.valueOf(ope.getOpeKey()), JacksonUtil.toJSONStr(ope));
});
我新增一笔被刷新到redis中
如下图:
可以看到下面就是一长串字符串就是 value。
实际业务是这样的,如果我在表中update了我新增的那笔valid_flg这个栏位。
等下一次定时器run完后,我再次查看这个key的这个field,竟然还存在。郁闷的我再次翻看 hset的定义:
如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。
也就是说,我update的是value中的一个字段,fileld并未改变。所以old value不会被new value覆盖的。(7/31)
通过上面的例子的实践确实是如此,通过翻看代码,在业务层获取basicOpe的时候并不是hget而是hgetALL。
再看下图:
hgetAll得到了两行数据,从这个也可以看出hget 和hgetall的区别到底在哪里?
hgetall返回的是所有的域和值,本例来说,
“1) “00666”” - >key
“2) “00625””-> value
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
最后再review代码时发现,hset bisicOpe的代码:
final List<MOpeD> basicOpeList = dao2.find(MOpeD.class, "FROM MOpeD where validFlg = 'Y'");
basicOpeList.forEach(ope -> {
pipelined.hset("basicOpe", String.valueOf(ope.getOpeKey()), JacksonUtil.toJSONStr(ope));
//可以看出key是basicOpe,field是ope_key,value是ope_id
basicOpeData.put(String.valueOf(ope.getOpeKey()), JacksonUtil.toJSONStr(ope));
});
从上面的代码可以看出,每次往basicOpe写值时的sql是塞valid_flg = 'Y'的。当在界面上删除这个ope时,db中将valid_flg赋值为N。这样basicOpe再也获取不在这条数据了,所以也不会有 “如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。”
为了解决这个问题,应该在实际的业务代码中加入,增量删除valid_flg = 'N’的数据:具体做法很简单,只需要找到valid_flg = ‘N’的field,然后hdel即可,如下:
public void loadOpeBasicData(Jedis jedis){ try { //3.Ope final List basicOpeList = dao2.find(MOpeD.class, "FROM ***"); List basicOpeListToFlush = basicOpeList.stream().filter(mMopeD -> mMopeD.getValidFlg().equals("Y")).collect(Collectors.toList());// to flush List basicOpeListToDel = basicOpeList.stream().filter(mMopeD -> mMopeD.getValidFlg().equals("N")).collect(Collectors.toList()); //to delete basicOpeListToFlush.forEach(ope -> { jedis.hset("basicOpe", String.valueOf(ope.getOpeKey()), JacksonUtil.toJSONStr(ope)); }); basicOpeListToDel.forEach(ope ->{ jedis.hdel("basicOpe",String.valueOf(ope.getOpeKey()));//删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略. }); } catch (Exception e) { logger.error(LoggerUtil.stackTraceToString(e)); e.printStackTrace(); } }
此外到前辈的这片博文:。目前我们的项目中仍有hgetAll的语法,因为实际业务之需求。
目前Coids QPS,时而也有OPS飙升的情况,不过目前redis的内存还够用,至于后续,随着业务的其他需要增多redis的使用,我想hgetall也是需要改善的。
------------------------------------------------------update 2019年7月31日09:22:03-----------------------------------------------
在实际业务中,遇到baiscMachinespec 这个key中有脏数据导致alarm发不出。后来查看了原因:
用户在界面维护了一笔数据,比如新增了machineName = ASSM0399 aliaslinename = ‘SIBW03’。用户某天删除了machineName = ASSM0399的那一笔数据,然后新增了machineName = ASSM0398 aliaslinename = ‘SIBW03’的一笔,对于machinename维护角度来说这没有问题,可是对RTM业务来说这样维护是会产生一对多的问题。baiscMachinespec的 field 是machinename。value 是 “"{"aliaslinename":"OLBX0648","department":"xe5xaex9exe8xa3x85xe7xa7x91","machinename":"AOPS0609"}"”
形如这种的value.
如何选取合适的filed 是需要考虑的
-----------------------update 2019年7月19日13:40:21---------------------------------------
有序集合
是set的一个升级版本,在set的基础上增加了一个顺序属性,这一属性在添加修改元素时可以指定,每次指定后zset会自动安装指定值重新调整顺序。可以理解为一张表,一列存value,一列存顺序。操作中的key理解为zset的名字。
Zset的最大元素数是2^32-1。
对于已经有序的zset,仍然可以使用SORT命令,通过指定ASC|DESC参数对其进行排序。
通过客户端help查看其用法
127.0.0.1:6380> help @sorted_set ZADD key [NX|XX] [CH] [INCR] score member [score member ...] summary: Add one or more members to a sorted set, or update its score if it already exists since: 1.2.0 ...............
zadd
ZADD key score member [[score member] [score member] ...]
127.0.0.1:6380> zadd testKey 1 var1(integer) 1127.0.0.1:6380> zadd testKey 2 var2(integer) 1127.0.0.1:6380> zadd testKey 3 var3(integer) 1 127.0.0.1:6380> zrange testKey 0 -1 WITHSCORES1) "var1"2) "1"3) "var2"4) "2"5) "var3"6) "3" add 同一个score,会写入新的kv127.0.0.1:6380> zadd testKey 3 var3_1(integer) 1127.0.0.1:6380> zrange testKey 0 -1 withscores1) "var1"2) "1"3) "var2"4) "2"5) "var3"6) "3"7) "var3_1"8) "3" add旧的value到新的socre中? var3已经是有序集的成员,那么更新这个 var3 的score 值,并通过重新插入,来保证该 var3在正确的位置上。如下例子所示 127.0.0.1:6380> zadd testKey 4 var3(integer) 0127.0.0.1:6380> zrange testKey 0 -1 withscores1) "var1"2) "1"3) "var2"4) "2"5) "var3_1"6) "3"7) "var3"8) "4"
有序集合可以干什么呢?
排行榜、有序时间以及评论+分页(评论是元素,score是分值,分页时给出score,按分页只需给传不同的score,动态分页也可以实现,在翻页时有新评论也不会有影响 ) 都可以采用这种集合来处理。
这个有序是如何实现的呢?
1、查看类型
127.0.0.1:6380> type testKey zset
2、查看底层数据结构
127.0.0.1:6380> OBJECT encoding testKey "skiplist"
skiplist的底层原理是什么呢?
https://mp.weixin.qq.com/s/6rVgWdb8wG9d-xInkguv_w
当value的字节超过64字节时会存成skiplist,否则会直接存为ziplist。
127.0.0.1:6380> zadd k1 3.5 apple 7.2 orange 1.6 banana(integer) 3127.0.0.1:6380> OBJECT encoding k1"ziplist"