目录
前置的连接
1.Redis String
1.1 介绍
1.2 命令
1.2.1 赋值与取值
1.2.2 追加赋值
1.2.3 同时设置/获取多个键值
2.Hash类型
2.1 需求
2.2 使用string的问题
2.3 redis hash介绍
2.4 命令
2.4.1 赋值与取值
2.4.2 判断字段是否存在
2.4.3 删除字段
2.4.4 只获取字段名或字段值
2.4.5 获取字段数量
Java测试用例
3.Redis list
3.1 介绍
3.2 命令
3.2.1 向列表两端增加元素
3.2.2 从列表两端弹出元素
3.2.3 获取列表中元素的个数
3.2.4 获取列表片段
测试用例
3.2.5 删除列表中指定的值
测试用例
3.2.6 获得/设置指定索引的元素值
测试用例
3.2.7 只保留列表指定片段,指定范围和LRANGE一致
3.2.8 向列表中插入元素
测试用例
3.2.9 将元素从一个列表转移到另一个列表中
测试用例
4.Redis Set
4.1 介绍
4.2 命令
4.2.1 增加/删除元素
4.2.2 获得集合元素
5.Redis sorted set
5.1 介绍
5.2 命令
5.2.1 增加元素
5.2.2 获取元素的分数
测试用例
5.2.3 获得排名在某个范围的元素列表
5.2.4 获得指定分数范围的元素
测试用例
5.2.5 增加某个元素的分数,返回值是更改后的分数
5.2.6 获得指定分数范围内的元素个数
5.2.7 获得集合中元素的数量
5.2.8 按照排名范围删除元素
5.2.9 按照分数范围删除元素
测试用例
5.2.10 获取元素的排名
测试用例
前置的连接
在本机的Idea当中的项目的application.yaml配置文件中配置好:
在SSH工具当中连接好服务器并且启动redis-server和redis-cli
可以先自己随便用几个命令测试一下能不能连上
1.Redis String
1.1 介绍
redis中没有使用C语言的字符串表示,而是自定义一个数据结构叫SDS(simple dynamic string)即简单动态字符串。
打开下载的redis源码包,找到src下的sds.h文件查看sds源码:
struct sdshdr { //字符串长度 unsigned int len; //buf数组中未使用的字节数量 unsigned int free; //用于保存字符串 char buf[]; };c语言对字符串的存储是使用字符数组,遇到'\0'字符则认为字符串结束,redis的字符串可以存储任何类型的数据,因为任何类型数据都可以表示成二进制,sds结构中的char buf[]就是存储了二进制数据。
redis的字符串是二进制安全的,什么是二进制安全?简单理解就是存入什么数据取出的还是什么数据。redis中的sds不像c语言处理字符串那样遇到'\0'字符则认证字符串结束,它不会对存储进去的二进制数据进行处理,存入什么数据取出还是什么数据。
1.2 命令
1.2.1 赋值与取值
SET key value GET key1.2.2 追加赋值
APPEND key value #APPEND的作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 SET key value。返回值是追加后字符串的总长度。1.2.1和1.2.2两个命令对应的Java代码
/** * String类别 * 测试set和get以及append */ @Test public void testString(){ //set和get redisTemplate.opsForValue().set("name","zhangsan"); String value = (String) redisTemplate.opsForValue().get("name"); //追加值 redisTemplate.opsForValue().append("name","1111111"); String value1 = (String) redisTemplate.opsForValue().get("name"); System.out.println("append之前" + value); System.out.println("append之后" + value1); //获取字符串长度 //Redis存储:Redis以字节为单位存储字符串,返回的长度是字节数 //中文字符:在UTF-8编码中,每个中文字符占用3个字节 //数字字符:在UTF-8编码中,每个数字字符占用1个字节 Long size = redisTemplate.opsForValue().size("name"); System.out.println("追加后的Value长度为" + size); }在Idea里面运行之后,在Linux服务器当中也可以查询到:
1.2.3 同时设置/获取多个键值
MSET key value [key value …] MGET key [key …]对应的Java代码:
/** * 批量设置和获取 */ @Test public void multiSetAndGet(){ //批量设置 HashMap<String, Object> map = new HashMap<>(); map.put("stuNo","123"); map.put("gender","male"); map.put("name","lisi"); redisTemplate.opsForValue().multiSet(map); //批量获取 List<String> keys = new ArrayList<>(); keys.add("stuNo"); keys.add("gender"); keys.add("name"); List<Object> values = redisTemplate.opsForValue().multiGet(keys); for (int i = 0; i < keys.size(); i++) { System.out.println(keys.get(i) + "---" + values.get(i)); } }服务器查询:
2.Hash类型
2.1 需求
使用 redis存储用户信息,考虑使用string或其它方案。
{"birthday":1557743055507,"ename":"zhangsan","districtId":2,"sex":1,"id":1,"edate":1557743055507,"age":18,"sal":8000.0}
2.2 使用string的问题
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下:
保存、更新:
User对象 ---> json(string) ----> redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,hash可以很好的解决这个问题。
2.3 redis hash介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
2.4 命令
2.4.1 赋值与取值
HSET key field value 一次只能设置一个字段值 HGET key field 一次只能获取一个字段值 HMSET key field value [field value ...] 一次可以设置多个字段值 HMGET key field [field ...] 一次可以获取多个字段值 HGETALL keyHSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0.
对应的Java代码(单个)
/** * 哈希的单个的存和取 */ @Test public void hashGetAndSet(){ //设置单个字段 hashOps().put("user","username","zhangsan"); //获取单个字段 String val = (String) hashOps().get("user", "username"); System.out.println(val); }多个字段的代码在下面和其他命令在一起
2.4.2 判断字段是否存在
HEXISTS key field HSETNX key field value 当字段不存在时赋值,类似HSET,区别在于如果字段已经存在,该命令不执行任何操作。2.4.3 删除字段
可以删除一个或多个字段,返回值是被删除的字段个数
HDEL key field [field ...]2.4.4 只获取字段名或字段值
HKEYS key HVALS key2.4.5 获取字段数量
HLEN keyJava测试用例
/** * 哈希操作 */ @Test public void opsHash(){ //批量设置字段 HashMap<String, Object> map = new HashMap<>(); map.put("username","张三"); map.put("email", "zhangsan@example.com"); map.put("phone", "13800138000"); map.put("city", "北京"); redisTemplate.opsForHash().putAll("user01",map); //批量获取字段 List<Object> fields = new ArrayList<>(); fields.add("username"); fields.add("email"); fields.add("phone"); fields.add("city"); List<Object> values = redisTemplate.opsForHash().multiGet("user01",fields); System.out.println(values); //判断字段是否存在 boolean hasUsername = redisTemplate.opsForHash().hasKey("user01","username"); System.out.println(hasUsername); //如果没有该字段,将该字段设置进去 Boolean b = redisTemplate.opsForHash().putIfAbsent("user01", "gender", "male"); System.out.println("性别字段不存在,设置是否成功" + b); //删除字段 Long delete = redisTemplate.opsForHash().delete("user01", "gender"); System.out.println("删除了" + delete + "行数据"); System.out.println("删除后的user01的值为:" + values); //获取所有字段名 Set<Object> keyOfUser01 = redisTemplate.opsForHash().keys("user01"); //获取所有字段值 List<Object> valueOfUser01 = redisTemplate.opsForHash().values("user01"); System.out.println("user01的字段名为" + keyOfUser01); System.out.println("user01的字段值为" + valueOfUser01); //获取字段数量 Long size = redisTemplate.opsForHash().size("user01"); System.out.println("字段数量:" + size); }然后接着执行其他命令:
3.Redis list
3.1 介绍
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。
3.2 命令
3.2.1 向列表两端增加元素
返回值是增加的元素个数
LPUSH key value [value ...] RPUSH key value [value ...]3.2.2 从列表两端弹出元素
返回值是弹出的元素个数
LPOP key RPOP keyLPOP命令从列表左边弹出一个元素,会分两步完成,第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。
3.2.3 获取列表中元素的个数
LLEN key3.2.4 获取列表片段
LRANGE key start stopLRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素。
测试用例
/** * 列表操作 */ @Test public void listOp(){ String key = "list:0"; //向左添加元素 Long leftPush = redisTemplate.opsForList().leftPush(key, "1"); //批量向左(也可以使用集合) // List<Object> items = new ArrayList<>(); // items.add("4"); // items.add("5"); // Long leftPushAll = redisTemplate.opsForList().leftPushAll(key, items); Long leftPushAll = redisTemplate.opsForList().leftPushAll(key, "2", "3"); //向右添加单个元素 Long rightPush = redisTemplate.opsForList().rightPush(key, "4"); //向右批量添加多个元素(也可以用集合) Long rightPushAll = redisTemplate.opsForList().rightPushAll(key, "5", "6"); //3,2,1,4,5,6 //从左弹出元素 Object leftPopResult = redisTemplate.opsForList().leftPop(key); System.out.println("左边弹出结果: " + leftPopResult); // "3" //从右弹出元素 Object rightPopResult = redisTemplate.opsForList().rightPop(key); System.out.println("右边弹出结果: " + rightPopResult); // "6" //获取列表长度 Long size = redisTemplate.opsForList().size(key); System.out.println("执行完弹出操作后列表的长度:" + size); //获取列表片段 List<Object> range = redisTemplate.opsForList().range(key, 0, 4); System.out.println(range); //获取全部元素 List<Object> allElements = redisTemplate.opsForList().range(key, 0, -1); System.out.println(allElements); }执行的结果(没全部给出):
3.2.5 删除列表中指定的值
LREM key count valueLREM命令会删除列表中前count个值为value的元素,返回实际删除的元素个数。根据count值的不同,该命令的执行方式会有所不同:
当count>0时, LREM会从列表左边开始删除
当count<0时, LREM会从列表后边开始删除
当count=0时, LREM删除所有值为value的元素
测试用例
/** * 列表的删除操作 */ @Test public void delList(){ String key = "test:list"; redisTemplate.opsForList().rightPushAll(key, "a", "b", "c", "a", "b", "c", "a"); // count > 0: 从左边开始删除2个"a" Long removed1 = redisTemplate.opsForList().remove(key, 2, "a"); System.out.println("从左边删除2个a: " + removed1); // count < 0: 从右边开始删除1个"c" Long removed2 = redisTemplate.opsForList().remove(key, -1, "c"); System.out.println("从右边删除1个c: " + removed2); // count = 0: 删除所有"b" Long removed3 = redisTemplate.opsForList().remove(key, 0, "b"); System.out.println("删除所有b: " + removed3); List<Object> result = redisTemplate.opsForList().range(key, 0, -1); System.out.println("剩余元素: " + result); }3.2.6 获得/设置指定索引的元素值
LINDEX key index LSET key index value测试用例
/** * 根据索引获取元素 */ @Test public void getWithIndex() { String key = "list:index"; redisTemplate.opsForList().rightPushAll(key, "6", "5", "1", "2"); //获取指定索引的元素 Object element = redisTemplate.opsForList().index(key, 2); System.out.println("索引2的元素: " + element); // "1" // 支持负数索引(从右边开始) Object lastElement = redisTemplate.opsForList().index(key, -1); // 最后一个元素 //设置指定索引的元素 redisTemplate.opsForList().set(key, 2, "2"); // 验证修改结果 List<Object> all = redisTemplate.opsForList().range(key, 0, -1); System.out.println("修改后: " + all); // ["6", "5", "2", "2"] }3.2.7 只保留列表指定片段,指定范围和LRANGE一致
LTRIM key start stop3.2.8 向列表中插入元素
LINSERT key BEFORE|AFTER pivot value该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。
测试用例
/** * 插入元素 */ @Test public void insertOp() { String key = "list:insert01"; redisTemplate.opsForList().rightPushAll(key, "3", "2", "1"); //LINSERT在指定元素后面插入 Long afterResult = redisTemplate.opsForList().rightPush(key, "3", "4"); System.out.println("在3后面插入4: " + afterResult); //LINSERT在指定元素前面插入 Long beforeResult = redisTemplate.opsForList().leftPush(key, "2", "9"); System.out.println("在2前面插入9: " + beforeResult); List<Object> result = redisTemplate.opsForList().range(key, 0, -1); System.out.println("插入后: " + result); // ["3", "4", "9", "2", "1"] }3.2.9 将元素从一个列表转移到另一个列表中
RPOPLPUSH source destination测试用例
/** * 移动元素 */ @Test public void testMoveOperations() { String sourceKey = "list"; String destKey = "newList"; redisTemplate.opsForList().rightPushAll(sourceKey, "3", "4", "2"); //从source右边弹出,插入到dest左边 Object movedElement = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destKey); System.out.println("移动的元素: " + movedElement); // "2" System.out.println("source列表: " + redisTemplate.opsForList().range(sourceKey, 0, -1)); System.out.println("dest列表: " + redisTemplate.opsForList().range(destKey, 0, -1)); }4.Redis Set
4.1 介绍
在集合中的每个元素都是不同的,且没有顺序。
集合类型和列表类型的对比:
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。
Redis还提供了多个集合之间的交集、并集、差集的运算。
4.2 命令
4.2.1 增加/删除元素
SADD key member [member ...] SREM key member [member ...]4.2.2 获得集合元素
SMEMBERS key判断元素是否在集合中,无论集合中有多少元素都可以极速的返回结果。
SISMEMBER key member/**------------------------------------------------------------------------------------------- * set * 添加元素 */ @Test public void testAddOperations() { String key = "set:1"; //SADD-添加单个元素 Long addResult1 = redisTemplate.opsForSet().add(key, "a"); System.out.println("添加单个元素结果: " + addResult1); // 1(新增) //SADD-添加多个元素 Long addResult2 = redisTemplate.opsForSet().add(key, "b", "c"); System.out.println("添加多个元素结果: " + addResult2); // 2(新增b和c) //添加已存在的元素 Long addResult3 = redisTemplate.opsForSet().add(key, "a"); System.out.println("添加已存在元素结果: " + addResult3); // 0(未新增) //使用集合批量添加 Object[] arrayList = {"d","e","f"}; Set<Object> items = new HashSet<>(Arrays.asList(arrayList)); Long addResult4 = redisTemplate.opsForSet().add(key, items.toArray()); System.out.println("批量添加结果: " + addResult4); // 3 Set<Object> members = redisTemplate.opsForSet().members(key); System.out.println("集合所有元素: " + members); // ["a", "b", "c", "d", "e", "f"] }5.Redis sorted set
5.1 介绍
在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
1、二者都是有序的。
2、二者都可以获得某一范围的元素。
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存。
5.2 命令
5.2.1 增加元素
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
ZADD key score member [score member ...]5.2.2 获取元素的分数
ZSCORE key member测试用例
/**----------------------------------------------------------------------------------- * sorted_set * 添加 */ @Test public void testSortedSetAddOperations() { String key = "sorted_set:01"; //DefaultTypedTuple:Spring Data Redis 提供的数据结构,用于封装有序集合(sorted set)中的元素及其分数 //作用:在 Redis 的有序集合操作中同时存储成员(member)和分数(score) //ZADD-添加单个元素 Boolean addResult1 = redisTemplate.opsForZSet().add(key, "zhangsan", 80.0); System.out.println("添加单个元素结果: " + addResult1); // true //ZADD-添加多个元素 DefaultTypedTuple<Object> tuple1 = new DefaultTypedTuple<>("lisi", 89.0); DefaultTypedTuple<Object> tuple2 = new DefaultTypedTuple<>("wangwu", 94.0); Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>(); tuples.add(tuple1); tuples.add(tuple2); Long addResult2 = redisTemplate.opsForZSet().add(key, tuples); System.out.println("批量添加元素数量: " + addResult2); // 2 //更新已存在元素的分数 Boolean updateResult = redisTemplate.opsForZSet().add(key, "lisi", 97.0); System.out.println("更新分数结果: " + updateResult); // false(更新返回false) }5.2.3 获得排名在某个范围的元素列表
ZRANGE key start stop [WITHSCORES]照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
ZREVRANGE key start stop [WITHSCORES]照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
如果需要获得元素的分数的可以在命令尾部加上WITHSCORES参数
5.2.4 获得指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
测试用例
/** * 获取排名 */ @Test public void testRangeOperations() { String key = "sorted_set:02"; redisTemplate.opsForZSet().add(key, "zhangsan", 80.0); redisTemplate.opsForZSet().add(key, "lisi", 97.0); redisTemplate.opsForZSet().add(key, "wangwu", 94.0); redisTemplate.opsForZSet().add(key, "zhaoliu", 88.0); redisTemplate.opsForZSet().add(key, "qianqi", 91.0); //获取某个范围内的元素排名 //ZRANGE-升序排名 Set<Object> ascRange = redisTemplate.opsForZSet().range(key, 0, 2); System.out.println("升序前3名: " + ascRange); // ["zhangsan", "zhaoliu", "wangwu"] //ZREVRANGE-降序排名 Set<Object> descRange = redisTemplate.opsForZSet().reverseRange(key, 0, 2); System.out.println("降序前3名: " + descRange); // ["lisi", "wangwu", "qianqi"] //带分数返回(升序) Set<ZSetOperations.TypedTuple<Object>> ascWithScores = redisTemplate.opsForZSet() .rangeWithScores(key, 0, 1); for (ZSetOperations.TypedTuple<Object> tuple : ascWithScores) { System.out.println("元素: " + tuple.getValue() + ", 分数: " + tuple.getScore()); } //带分数返回(降序) Set<ZSetOperations.TypedTuple<Object>> descWithScores = redisTemplate.opsForZSet() .reverseRangeWithScores(key, 0, 1); //获取所有元素 Set<Object> all = redisTemplate.opsForZSet().range(key, 0, -1); Set<Object> allDesc = redisTemplate.opsForZSet().reverseRange(key, 0, -1);//降序 System.out.println("所有元素:" + all); System.out.println("所有元素(降序):" + allDesc); //分页获取,获取指定范围内的元素 int page = 2; int pageSize = 2; int start = (page - 1) * pageSize; int end = start + pageSize - 1; Set<Object> pageData = redisTemplate.opsForZSet().range(key, start, end); System.out.println("分页获取:" + pageData); }加上分数:
分页获取:
5.2.5 增加某个元素的分数,返回值是更改后的分数
ZINCRBY key increment member5.2.6 获得指定分数范围内的元素个数
ZCOUNT key min max5.2.7 获得集合中元素的数量
ZCARD key5.2.8 按照排名范围删除元素
ZREMRANGEBYRANK key start stop5.2.9 按照分数范围删除元素
ZREMRANGEBYSCORE scoreboard start stop测试用例
/** * 统计范围内的元素数量 */ @Test public void testCountOperations() { String key = "sorted_set:06"; redisTemplate.opsForZSet().add(key, "A", 80.0); redisTemplate.opsForZSet().add(key, "B", 85.0); redisTemplate.opsForZSet().add(key, "C", 90.0); redisTemplate.opsForZSet().add(key, "D", 95.0); redisTemplate.opsForZSet().add(key, "E", 100.0); //ZCOUNT-统计分数范围内的元素数量 Long count = redisTemplate.opsForZSet().count(key, 80, 90); System.out.println("分数80-90的元素数量: " + count); // 3 //ZREMRANGEBYRANK - 按排名范围删除 Long removedByRank = redisTemplate.opsForZSet().removeRange(key, 0, 1); System.out.println("删除排名0-1的数量: " + removedByRank); // 2 //ZREMRANGEBYSCORE - 按分数范围删除 Long removedByScore = redisTemplate.opsForZSet().removeRangeByScore(key, 90, 100); System.out.println("删除分数90-100的数量: " + removedByScore); // 2 //删除元素(可以批量) Long removedMultiple = redisTemplate.opsForZSet().remove(key, "A", "C", "nonexistent"); System.out.println("删除多个元素结果: " + removedMultiple); }5.2.10 获取元素的排名
从小到大 ZRANK key member 从大到小 ZREVRANK key member测试用例
/** * 获取元素排名 */ @Test public void testRankOperations() { String key = "sorted_set:07"; redisTemplate.opsForZSet().add(key, "zhangsan", 80.0); redisTemplate.opsForZSet().add(key, "lisi", 97.0); redisTemplate.opsForZSet().add(key, "wangwu", 94.0); //ZRANK-升序排名(从0开始) Long ascRank = redisTemplate.opsForZSet().rank(key, "lisi"); System.out.println("lisi升序排名: " + ascRank); // 2 //ZREVRANK-降序排名(从0开始) Long descRank = redisTemplate.opsForZSet().reverseRank(key, "zhangsan"); System.out.println("zhangsan降序排名: " + descRank); // 2 //不存在的元素返回null Long nonExistRank = redisTemplate.opsForZSet().rank(key, "nonexistent"); System.out.println("不存在元素排名: " + nonExistRank); // null //获取前几名的排名信息 Map<String, Long> rankMap = new HashMap<>(); Set<Object> top3 = redisTemplate.opsForZSet().reverseRange(key, 0, 2); for (Object member : top3) { Long rank = redisTemplate.opsForZSet().reverseRank(key, member); rankMap.put(member.toString(), rank); } System.out.println("前三名排名: " + rankMap); }