Redis(二):Jedis操作Redis
Jedis入门
在Maven中,添加如下依赖即可使用:
<dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>compile</scope> </dependency>
1.简单配置-连接操作
/**简单连接Redis方式 * 1)创建连接 * 2)使用连接进行操作 * 3)关闭连接 */ @Test public void connectionTest() { //1)创建连接 Jedis connection = new Jedis("192.168.36.130", 6379); connection.auth("123456"); //2)使用连接进行操作 connection.set("name", "武大郎"); connection.set("age","26"); System.out.println(" 姓名:"+connection.get("name")+" 年龄:"+connection.get("age")); //3)关闭连接 connection.close(); }
2.连接池配置-连接池操作
/** *连接池连接Redis方式 * * 1)创建连接池配置对象 * 2)配置相应的值 * 3)通过配置对象创建连接池对象 * 4)通过连接池获取连接 * 5)执行操作 * 6)释放连接 */ @Test public void connectionPoolTest() { //1 创建连接池配置对象 JedisPoolConfig config = new JedisPoolConfig(); //2 进行配置-四个配置 config.setMaxIdle(2);//最小连接(空闲最大连接) config.setMaxTotal(10);//最大连接数 config.setMaxWaitMillis(2 * 1000);//最大等待时间 config.setTestOnBorrow(true);//获取连接时测试连接是否畅通 JedisPool pool = new JedisPool(config, "192.168.36.130", 6379, 2 * 1000, "123456"); //3 通过配置对象创建连接池对象 Jedis connection = pool.getResource(); //4 通过连接池获取连接 //5 执行操作-设置值和获取值 connection.set("name", "武大郎-testPool"); connection.set("age","26"); System.out.println("姓名:"+connection.get("name")+" 年龄:"+connection.get("age")); //6 释放连接 connection.close();//释放和关闭公用一个方法,底层自动判断,如果是连接池环境则释放,否则关闭. pool.close();; }
Redis数据类型
Redis中的数据类型有:
- 字符串(String)
- 列表(list)
- 有序集合(sorted set)
- 散列(hash)
下面就分别介绍这五种数据类型及其相应的操作命令。(示例文档)
Jedis数据结构操作
工具抽取
package net.riking.redis.connection.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisConnectionUtils { private static JedisPool jedisPool = null; private JedisConnectionUtils(){}; private static JedisPool getJedisPool () { if(jedisPool==null){ synchronized(JedisConnectionUtils.class){ if(jedisPool==null){ //1 创建连接池配置对象 JedisPoolConfig config = new JedisPoolConfig(); //2 进行配置-四个配置 config.setMaxIdle(3);//最小连接(空闲最大连接) config.setMaxTotal(10);//最大连接数 config.setMaxWaitMillis(2 * 1000);//最大等待时间 config.setTestOnBorrow(true);//获取连接时测试连接是否畅通 //3 通过配置对象创建连接池对象 jedisPool = new JedisPool(config, "192.168.36.130", 6379, 2 * 1000, "123456"); } } } return jedisPool; } //获取连接 public static Jedis getResource() { return getJedisPool ().getResource(); } //释放连接 public static void closeResource(Jedis jedis) { if (jedis != null) { jedis.close(); } } }
String(字符串)
String是简单的 key-value 键值对,value 不仅可以是 String,也可以是数字。String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。
应用场景
String是最常用的一种数据类型,普通的key/value存储都可以归为此类,这里就不所做解释了。
Jedis的代码示例:
/** * exists: 判断 K 是否存在 * set: 设置 K 与 V 的值 * get: 通过 K 获取 V 的值 * keys: 获取所有的 K * expire: 设置 KV 键值对的过期时间 * ttl: 获取 KV 键值对 剩余过期时间 * @throws Exception */ @Test//key public void keyValueTest() throws Exception { Jedis jedis = JedisConnectionUtils.getResource(); //获取连接 System.out.println("清空数据:" + jedis.flushAll()); //清空所有数据 //执行的操作 System.out.println("key为name是否存在值:" + jedis.exists("name"));//false System.out.println("key为age是否存在值:" + jedis.exists("age"));//false System.out.println("插入key为name的值:" + jedis.set("name", "武大郎")); System.out.println("插入key为age的值:" + jedis.set("age", "26")); System.out.println("再次判断key为name是否存在值:" + jedis.exists("name"));//true System.out.println("再次判断key为age是否存在值:" + jedis.exists("age"));//true System.out.println("获取key为name的值:" + jedis.get("name"));//武大郎 System.out.println("获取key为age的值:" + jedis.get("age"));//26 jedis.append("name", "烧饼");//向后追加值 System.out.println("追加后获取key为name的值:" + jedis.get("name"));//武大郎烧饼 System.out.println("删除key为name的值:" + jedis.del("name"));//删除值 System.out.println("删除后判断key为name是否存在值:" + jedis.exists("name"));//false System.out.println("获取所有的key:"); //name age jedis.keys("*").stream().forEach(System.out::println); System.out.println("设置key为age的过期时间:10秒"); jedis.expire("age", 10);//设置过期时间 System.out.println("睡眠阻塞:5秒"); Thread.sleep(5000); System.out.println("获取key为age剩余过期时间:"+jedis.ttl("age"));//获取剩余过期时间 JedisConnectionUtils.closeResource(jedis); //释放连接 }
Redis关于String类型的常见命令如下表:
序号 | 命令 | 说明 |
---|---|---|
1 | SET key value | 此命令设置指定键的值。 |
2 | GET key | 获取指定键的值。 |
3 | GETRANGE key start end | 获取存储在键上的字符串的子字符串。 |
4 | GETSET key value | 设置键的字符串值并返回其旧值。 |
5 | GETBIT key offset | 返回在键处存储的字符串值中偏移处的位值 |
6 | MGET key1 [key2..] | 获取所有给定键的值 |
7 | SETBIT key offset value | 存储在键上的字符串值中设置或清除偏移处的位 |
8 | SETEX key seconds value | 使用键和到期时间来设置值 |
9 | SETNX key value | 设置键的值,仅当键不存在时 |
10 | SETRANGE key offset value | 在指定偏移处开始的键处覆盖字符串的一部分 |
11 | STRLEN key | 获取存储在键中的值的长度 |
12 | MSET key value [key value …] | 为多个键分别设置它们的值 |
13 | MSETNX key value [key value …] | 为多个键分别设置它们的值,仅当键不存在时 |
14 | PSETEX key milliseconds value | 设置键的值和到期时间(以毫秒为单位) |
15 | INCR key | 将键的整数值增加1 |
16 | INCRBY key increment | 将键的整数值按给定的数值增加 |
17 | INCRBYFLOAT key increment | 将键的浮点值按给定的数值增加 |
18 | DECR key | 将键的整数值减1 |
19 | DECRBY key decrement | 按给定数值减少键的整数值 |
20 | APPEND key value | 将指定值附加到键 |
List(列表)
Redis列表是简单的字符串列表,按插入顺序排序,可重复的数据,简单的说就是一个链表或者说是一个队列。可以从头部或尾部向Redis列表添加元素。列表的最大长度为2^32 - 1,也即每个列表支持超过40亿个元素。
Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
应用场景
Redis List的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表、粉丝列表等都可以用Redis的list结构来实现,再比如有的应用使用Redis的list类型实现一个简单的轻量级消息队列,生产者push,消费者pop/bpop。
Jedis的代码示例:
@Test //List public void listTest() { Jedis jedis = JedisConnectionUtils.getResource(); System.out.println("清空数据:" + jedis.flushAll()); jedis.lpush("nameList","武大郎","张三","李四","王五","灿灿","朱云","程印","程印"); assert 8==jedis.llen("nameList"); jedis.lrange("nameList",0,-1).stream().forEach(System.out::println); //从第一个到最后一个,负数代表倒数第几个 assert "武大郎".equals(jedis.rpop("nameList"));//从尾部取出一个元素 assert 7==jedis.llen("nameList"); System.out.println("------------------------------------------------"); jedis.lrem("nameList",2,"程印");//删除从左数2两个"程印"元素 jedis.lrem("nameList",0,"张三"); //0表示删除全部"张三"元素 assert "朱云".equals(jedis.lpop("nameList"));//从头部加入一个元素 jedis.lrange("nameList",0,-1).stream().forEach(System.out::println); //从第一个到最后一个,负数代表倒数第几个 jedis.del("nameList");//清空nameList JedisConnectionUtils.closeResource(jedis); //释放连接 }
Redis中关于list的命令如下表:
序号 | 命令 | 说明 |
---|---|---|
1 | BLPOP key1 [key2 ] timeout | 删除并获取列表中的第一个元素,或阻塞,直到有一个元素可用 |
2 | BRPOP key1 [key2 ] timeout | 删除并获取列表中的最后一个元素,或阻塞,直到有一个元素可用 |
3 | BRPOPLPUSH source destination timeout | 从列表中弹出值,将其推送到另一个列表并返回它; 或阻塞,直到一个可用 |
4 | LINDEX key index | 通过其索引从列表获取元素 |
5 | LINSERT key BEFORE/AFTER pivot value | 在列表中的另一个元素之前或之后插入元素 |
6 | LLEN key | 获取列表的长度 |
7 | LPOP key | 删除并获取列表中的第一个元素 |
8 | LPUSH key value1 [value2] | 将一个或多个值添加到列表 |
9 | LPUSHX key value | 仅当列表存在时,才向列表添加值 |
10 | LRANGE key start stop | 从列表中获取一系列元素 |
11 | LREM key count value | 从列表中删除元素 |
12 | LSET key index value | 通过索引在列表中设置元素的值 |
13 | LTRIM key start stop | 修剪列表的指定范围 |
14 | RPOP key | 删除并获取列表中的最后一个元素 |
15 | RPOPLPUSH source destination | 删除列表中的最后一个元素,将其附加到另一个列表并返回 |
16 | RPUSH key value1 [value2] | 将一个或多个值附加到列表 |
17 | RPUSHX key value | 仅当列表存在时才将值附加到列表 |
Set(集合)
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
Jedis的代码示例:
@Test public void setTest(){ Jedis jedis = JedisConnectionUtils.getResource(); System.out.println("清空数据:" + jedis.flushAll()); String nameSet = "nameSet"; jedis.sadd(nameSet,"武大郎","张三","李四","王五","灿灿","朱云","程印","索隆"); // 游标初始值为0 String cursor = ScanParams.SCAN_POINTER_START; ScanParams scanParams = new ScanParams(); // scanParams.match(nameSet);// 匹配以 test:xttblog:* 为前缀的 key //scanParams.count(7); jedis.sscan(nameSet,cursor,scanParams).getResult().stream().forEach(System.out::println); assert 8 == jedis.scard(nameSet); //获得元素个数 jedis.sadd(nameSet,"程印");//添加重复的元素将失效 assert 8 == jedis.scard(nameSet); String s= jedis.srandmember(nameSet);//随机获取一个元素 assert jedis.sismember(nameSet,s);//是否为集合成员 String nameSet2 = "nameSet2"; jedis.sadd(nameSet2,"武大郎","张三","李四","王五","灿灿","朱云","小程印"); System.out.println("------------------------------------------------"+jedis.sdiff(nameSet,nameSet2).size()); jedis.sscan(nameSet2,cursor,scanParams).getResult().stream().forEach(System.out::println); assert jedis.sdiff(nameSet,nameSet2).size() == 2;// (nameSet-nameSet2)元素 assert jedis.sinter(nameSet,nameSet2).size() == 6;//交集 assert jedis.sunion(nameSet,nameSet2).size() == 9;//并集 jedis.del(nameSet,nameSet2); jedis.close(); }
Redis中关于Set的命令如下表:
序号 | 命令 | 说明 |
---|---|---|
1 | SADD key member1 [member2] | 将一个或多个成员添加到集合 |
2 | SCARD key | 获取集合中的成员数 |
3 | SDIFF key1 [key2] | 减去多个集合 |
4 | SDIFFSTORE destination key1 [key2] | 减去多个集并将结果集存储在键中 |
5 | SINTER key1 [key2] | 相交多个集合 |
6 | SINTERSTORE destination key1 [key2] | 交叉多个集合并将结果集存储在键中 |
7 | SISMEMBER key member | 判断确定给定值是否是集合的成员 |
8 | SMOVE source destination member | 将成员从一个集合移动到另一个集合 |
9 | SPOP key | 从集合中删除并返回随机成员 |
10 | SRANDMEMBER key [count] | 从集合中获取一个或多个随机成员 |
11 | SREM key member1 [member2] | 从集合中删除一个或多个成员 |
12 | SUNION key1 [key2] | 添加多个集合 |
13 | SUNIONSTORE destination key1 [key2] | 添加多个集并将结果集存储在键中 |
14 | SSCAN key cursor [MATCH pattern] [COUNT count] | 递增地迭代集合中的元素 |
Hash(字典)
Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
Jedis的代码示例:
@Test public void hashTest(){ Jedis jedis = JedisConnectionUtils.getResource(); System.out.println("清空数据:" + jedis.flushAll()); String hashKey = "user"; //hset设置一个键值对 jedis.hset(hashKey,"name","武大郎"); HashMap<String, String> map = new HashMap<>(); map.put("age","26"); map.put("sex","男"); //设置多个键值对 jedis.hmset(hashKey,map); //获得键值对的个数 Long hlen = jedis.hlen(hashKey); System.out.println("Hash Size in redis:: "+hlen); assert hlen==3; System.out.println("得到全部键值对"+jedis.hgetAll(hashKey)); //得到全部键值对 List<String> user = jedis.hmget(hashKey,"name","age");//得到部分Value值 System.out.println("Stored string in redis:: "+ user); assert user.get(0).equals("武大郎"); //删除键值 jedis.del(hashKey); jedis.close(); }
Redis关于Hash类型的常见命令如下表:
序号 | 命令 | 说明 |
---|---|---|
1 | HDEL key field2 [field2] | 删除一个或多个哈希字段。 |
2 | HEXISTS key field | 判断是否存在散列字段。 |
3 | HGET key field | 获取存储在指定键的哈希字段的值。 |
4 | HGETALL key | 获取存储在指定键的哈希中的所有字段和值 |
5 | HINCRBY key field increment | 将哈希字段的整数值按给定数字增加 |
6 | HINCRBYFLOAT key field increment | 将哈希字段的浮点值按给定数值增加 |
7 | HKEYS key | 获取哈希中的所有字段 |
8 | HLEN key | 获取散列中的字段数量 |
9 | HMGET key field1 [field2] | 获取所有给定哈希字段的值 |
10 | HMSET key field1 value1 [field2 value2 ] | 为多个哈希字段分别设置它们的值 |
11 | HSET key field value | 设置散列字段的字符串值 |
12 | HSETNX key field value | 仅当字段不存在时,才设置散列字段的值 |
13 | HVALS key | 获取哈希中的所有值 |
Sorted Set(有序集合)
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
Sorted-Set的使用场景:
- 大型在线游戏的积分排名;
- 构建索引数据;
Jedis的代码示例:
序号 | 命令 | 说明 |
---|---|---|
1 | ZADD key score1 member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
2 | ZCARD key | 获取有序集合的成员数 |
3 | ZCOUNT key min max | 计算在有序集合中指定区间分数的成员数 |
4 | ZINCRBY key increment member | 有序集合中对指定成员的分数加上增量 increment |
5 | ZINTERSTORE destination numkeys key [key ...] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
6 | ZLEXCOUNT key min max | 在有序集合中计算指定字典区间内成员数量 |
7 | ZRANGE key start stop [WITHSCORES] | 通过索引区间返回有序集合指定区间内的成员 |
8 | ZRANGEBYLEX key min max [LIMIT offset count] | 通过字典区间返回有序集合的成员 |
9 | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] | 通过分数返回有序集合指定区间内的成员 |
10 | ZRANK key member | 返回有序集合中指定成员的索引 |
11 | ZREM key member [member ...] | 移除有序集合中的一个或多个成员 |
12 | ZREMRANGEBYLEX key min max | 移除有序集合中给定的字典区间的所有成员 |
13 | ZREMRANGEBYRANK key start stop | 移除有序集合中给定的排名区间的所有成员 |
14 | ZREMRANGEBYSCORE key min max | 移除有序集合中给定的分数区间的所有成员 |
15 | ZREVRANGE key start stop [WITHSCORES] | 返回有序集中指定区间内的成员,通过索引,分数从高到低 |
16 | ZREVRANGEBYSCORE key max min [WITHSCORES] | 返回有序集中指定分数区间内的成员,分数从高到低排序 |
17 | ZREVRANK key member | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
18 | ZSCORE key member | 返回有序集中,成员的分数值 |
19 | ZUNIONSTORE destination numkeys key [key ...] | 算给定的一个或多个有序集的并集,并存储在新的 key 中 |
20 | ZSCAN key cursor [MATCH pattern] [COUNT count] | 迭代有序集合中的元素(包括元素成员和元素分值) |
Redis的特性
多数据
提供16个数据库(0-15),默认为0号数据库,可是通过select index
选择。
事务
和关系型数据库一样,Redis也提供事务性操作:
- DISCARD 丢弃在MULTI之后发出的所有命令(放弃事务,回滚)
- EXEC 执行MULTI后发出的所有命令(提交事务)
- MULTI 标记事务块的开始(开始事务)
- UNWATCH 取消 WATCH 命令对所有 key 的监视。
- WATCH key [key …] 监视给定的键以确定MULTI / EXEC块的执行
Redis中示例代码如下:
@Test public void transactionTest(){ Jedis jedis = JedisConnectionUtils.getResource(); System.out.println("清空数据:" + jedis.flushAll()); jedis.select(1);//提供16个数据库(0-15),默认为0号数据库,可是通过select index选择。 Transaction transaction = jedis.multi();//开启事务 transaction.set("name", "武大郎"); System.out.println(transaction.get("name")); transaction.exec();//提交事务 System.out.println(jedis.get("name")); transaction = jedis.multi();//开启事务 transaction.set("name", "武大郎烧饼"); System.out.println(transaction.get("name")); transaction.discard();//撤销事务 System.out.println(jedis.get("name")); jedis.close(); }