[TOC] # Redis使用及API ## 01/ 客户端连接 ### 1、 用redis自带的命令行客户端 ~~~ [root@notrue-centos redis]# bin/redis-cli -h notrue-centos -p 6379 redis notrue-centos:6379> ping PONG redis notrue-centos:6379> ~~~ ### 2、 或者用redis的api客户端连接 > 新建一个maven工程,导入jedis的maven依赖坐标 ~~~ <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.2</version> <type>jar</type> <scope>compile</scope> </dependency> ~~~ > 然后写一个类用来测试服务器跟客户端的连通性: ~~~ public class RedisClientConnectionTest { public static void main(String[] args) { // 构造一个redis的客户端对象 Jedis jedis = new Jedis("pinshutang.zicp.net", 6379); String ping = jedis.ping(); System.out.println(ping); } } ~~~ ## 2 Redis的数据功能 ### 2.1 String类型的数据(常作为缓存使用) > 1/插入和读取一条string类型的数据 ~~~ redis notrue-centos:6379> set sessionid-0001 "zhangsan" OK redis notrue-centos:6379> get sessionid-0001 "zhangsan" ~~~ > 2/对string类型数据进行增减(前提是这条数据的value可以看成数字) ~~~ DECR key INCR key DECRBY key decrement INCRBY key increment ~~~ > 3/一次性插入或者获取多条数据 ~~~ MGET key1 key2 MSET key1 value1 key2 value2 ….. ~~~ > 4/在插入一条string类型数据的同时为它指定一个存活期限 ~~~ setex bancao 10 weige ### bancao这条数据就只会存活10秒钟,过期会被redis自动清除 ~~~ > 应用:将一个自定义的对象比如product存入redis > 实现方式一:将对象序列化成byte数组 ~~~ /** * 将对象缓存到redis的string结构数据中 * @throws Exception * */ @Test public void testObjectCache() throws Exception{ ProductInfo p = new ProductInfo(); p.setName("苏菲"); p.setDescription("angelababy专用"); p.setCatelog("unknow"); p.setPrice(10.8); //将对象序列化成字节数组 ByteArrayOutputStream ba = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(ba); //用对象序列化流来将p对象序列化,然后把序列化之后的二进制数据写到ba流中 oos.writeObject(p); //将ba流转成byte数组 byte[] pBytes = ba.toByteArray(); //将对象序列化之后的byte数组存到redis的string结构数据中 jedis.set("product:01".getBytes(), pBytes); //根据key从redis中取出对象的byte数据 byte[] pBytesResp = jedis.get("product:01".getBytes()); //将byte数据反序列出对象 ByteArrayInputStream bi = new ByteArrayInputStream(pBytesResp); ObjectInputStream oi = new ObjectInputStream(bi); //从对象读取流中读取出p对象 ProductInfo pResp = (ProductInfo) oi.readObject(); System.out.println(pResp); } ~~~ > 实现方式二: > 将对象转成json字符串来存取 /** * 将对象转成json字符串缓存到redis的string结构数据中 */ @Test public void testObjectToJsonCache(){ ProductInfo p = new ProductInfo(); p.setName("ABC"); p.setDescription("刘亦菲专用"); p.setCatelog("夜用型"); p.setPrice(10.8); //利用gson将对象转成json串 Gson gson = new Gson(); String pJson = gson.toJson(p); //将json串存入redis jedis.set("prodcut:02", pJson); //从redis中取出对象的json串 String pJsonResp = jedis.get("prodcut:02"); //将返回的json解析成对象 ProductInfo pResponse = gson.fromJson(pJsonResp, ProductInfo.class); //显示对象的属性 System.out.println(pResponse); } ## 2.2 List数据结构 ### 2.2.1 List图示 ![](https://box.kancloud.cn/d24d21df81027017b44c2c125800bc7f_157x165.png) ### 3.2.2 List功能演示 ~~~ #从头部(左边)插入数据 redis>LPUSH key value1 value2 value3 #从尾部(右边)插入数据 redis>RPUSH key value1 value2 value3 #读取list中指定范围的values redis>LRANGE key start end redis> lrange task-queue 0 -1 读取整个list #从头部弹出一个元素 LPOP key #从尾部弹出一个元素 RPOP key #从一个list的尾部弹出一个元素插入到另一个list RPOPLPUSH key1 key2 ## 这是一个原子性操作 ~~~ ### 2.2.3 List的应用案例demo > 1 需求描述 > 任务调度系统: > 生产者不断产生任务,放入task-queue排队 > 消费者不断拿出任务来处理,同时放入一个tmp-queue暂存,如果任务处理成功,则清除tmp-queue,否则,将任务弹回task-queue ![](https://box.kancloud.cn/74748498002520b4c94a4fc483bbd718_444x174.png) > 2 代码实现 > 1/ 生产者 > ——模拟产生任务 ~~~ public class TaskProducer { // 获取一个redis的客户端连接对象 public static Jedis getRedisConnection(String host, int port) { Jedis jedis = new Jedis(host, port); return jedis; } public static void main(String[] args) { Jedis jedis = getRedisConnection("angelababy", 6379); Random random = new Random(); // 生成任务 while (true) { try { // 生成任务的速度有一定的随机性,在1-2秒之间 Thread.sleep(random.nextInt(1000) + 1000); // 生成一个任务 String taskid = UUID.randomUUID().toString(); // 往任务队列"task-queue"中插入,第一次插入时,"task-queue"还不存在 //但是lpush方法会在redis库中创建一条新的list数据 jedis.lpush("task-queue", taskid); System.out.println("向任务队列中插入了一个新的任务: " + taskid); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ~~~ > 2/ 消费者 > ——模拟处理任务,并且管理暂存队列 ~~~ public class TaskConsumer { public static void main(String[] args) { Jedis jedis = new Jedis("angelababy", 6379); Random random = new Random(); while (true) { try { // 从task-queue中取一个任务,同时放入"tmp-queue" String taskid = jedis.rpoplpush("task-queue", "tmp-queue"); // 模拟处理任务 Thread.sleep(1000); // 模拟有成功又有失败的情况 int nextInt = random.nextInt(13); if (nextInt % 7 == 0) { // 模拟失败的情况 // 失败的情况下,需要将任务从"tmp-queue"弹回"task-queue" jedis.rpoplpush("tmp-queue", "task-queue"); System.out.println("-------任务处理失败: " + taskid); } else { // 模拟成功的情况 // 成功的情况下,将任务从"tmp-queue"清除 jedis.rpop("tmp-queue"); System.out.println("任务处理成功: " + taskid); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ~~~ > 上述机制是一个简化版,真实版的任务调度系统会更加复杂,如下所示: > (增加了一个专门用来管理暂存队列的角色,以便就算消费者程序失败退出,那些处理失败的任务依然可以被弹回task-queue) ![](https://box.kancloud.cn/57c691d7bef60c4924f32ad599b106ee_442x184.png) ## 2.3 Hash数据结构 ### 2.3.1 Hash图示 > Redis中的Hashes类型可以看成具有String Key和String Value的map容器 ![](https://box.kancloud.cn/d490724442dcb1b5dd8955cb2858c761_202x131.png) ### 2.3.2 Hash功能演示 > 1、往redis库中插入一条hash类型的数据 ~~~ redis> HSET key field value ~~~ > 举例: ~~~ redis 127.0.0.1:6379> hset user001:zhangsan iphone 6 (integer) 1 redis 127.0.0.1:6379> hset user001:zhangsan xiaomi 7 (integer) 1 redis 127.0.0.1:6379> hset user001:zhangsan meizu 8 (integer) 1 ~~~ > 在redis库中就形成了这样一条数据: ![](https://box.kancloud.cn/12fc72620c82722a8dd415ae7d246492_397x195.png) > 2、从redis库中获取一条hash类型数据的value > 取出一条hash类型数据中所有field-value对 ~~~ redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "iphone" 2) "6" 3) "xiaomi" 4) "7" 5) "meizu" 6) "8" ~~~ > 取出hash数据中所有fields ~~~ redis 127.0.0.1:6379> HKEYS user001:zhangsan 1) "iphone" 2) "xiaomi" 3) "meizu" ~~~ > 取出hash数据中所有的value ~~~ redis 127.0.0.1:6379> hvals user001:zhangsan 1) "6" 2) "7" 3) "8" ~~~ > 取出hash数据中一个指定field的值 ~~~ redis 127.0.0.1:6379> hget user001:zhangsan xiaomi "8" ~~~ > 为hash数据中指定的一个field的值进行增减 ~~~ redis 127.0.0.1:6379> HINCRBY user001:zhangsan xiaomi 1 (integer) 8 ~~~ > 从hash数据中删除一个字段field及其值 ~~~ redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "iphone" 2) "6" 3) "xiaomi" 4) "7" 5) "meizu" 6) "8" redis 127.0.0.1:6379> HDEL user001:zhangsan iphone (integer) 1 redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "xiaomi" 2) "7" 3) "meizu" 4) "8" ~~~ ## 2.4 Set数据结构功能 > 集合的特点:无序、无重复元素 ### 1、 插入一条set数据 ~~~ redis 127.0.0.1:6379> sadd frieds:zhangsan bingbing baby fengjie furong ruhua tingting (integer) 6 redis 127.0.0.1:6379> scard frieds:zhangsan (integer) 6 redis 127.0.0.1:6379> ~~~ ### 2、获取一条set数据的所有members ~~~ redis 127.0.0.1:6379> smembers frieds:zhangsan 1) "fengjie" 2) "baby" 3) "furong" 4) "bingbing" 5) "tingting" 6) "ruhua" ~~~ ### 3、判断一个成员是否属于某条指定的set数据 ~~~ redis 127.0.0.1:6379> sismember frieds:zhangsan liuyifei #如果不是,则返回0 (integer) 0 redis 127.0.0.1:6379> sismember frieds:zhangsan baby #如果是,则返回1 (integer) 1 ~~~ ### 4、求两个set数据的差集 > #求差集 ~~~ redis 127.0.0.1:6379> sdiff frieds:zhangsan friends:xiaotao 1) "furong" 2) "fengjie" 3) "ruhua" 4) "feifei" ~~~ > #求差集,并将结果存入到另一个set ~~~ redis 127.0.0.1:6379> sdiffstore zhangsan-xiaotao frieds:zhangsan friends:xiaotao (integer) 4 ~~~ > #查看差集结果 ~~~ redis 127.0.0.1:6379> smembers zhangsan-xiaotao 1) "furong" 2) "fengjie" 3) "ruhua" 4) "feifei" ~~~ ### 5、 求交集,求并集 > #求交集 ~~~ redis 127.0.0.1:6379> sinterstore zhangsan:xiaotao frieds:zhangsan friends:xiaotao (integer) 2 redis 127.0.0.1:6379> smembers zhangsan:xiaotao 1) "bingbing" 2) "baby" ~~~ > #求并集 ~~~ redis 127.0.0.1:6379> sunion frieds:zhangsan friends:xiaotao 1) "fengjie" 2) "tangwei" 3) "liuyifei" 4) "bingbing" 5) "ruhua" 6) "feifei" 7) "baby" 8) "songhuiqiao" 9) "furong" 10) "yangmi" ~~~ ## 2.5 sortedSet(有序集合)数据结构 ### 2.5.1 sortedSet图示 > sortedset中存储的成员都有一个附带的分数值 > 而redis就可以根据分数来对成员进行各种排序(正序、倒序) > sortedSet存储内容示意图 ![](https://box.kancloud.cn/73ca5773a43305c388ceeb5d3f120a7a_377x183.png) ### 2.5.2 SortedSet功能演示 > 1、往redis库中插入一条sortedset数据 ~~~ redis 127.0.0.1:6379> zadd nansheng:yanzhi:bang 70 liudehua 90 huangbo 100 weixiaobao 250 yangwei 59 xiaotao (integer) 5 ~~~ > 2、 从sortedset中查询有序结果 > #正序结果 ~~~ redis 127.0.0.1:6379> zrange nanshen:yanzhi:bang 0 4 1) "xiaotao" 2) "liudehua" 3) "huangbo" 4) "weixiaobao" 5) "yangwei" ~~~ > #倒序结果 ~~~ redis 127.0.0.1:6379> zrevrange nanshen:yanzhi:bang 0 4 1) "yangwei" 2) "weixiaobao" 3) "huangbo" 4) "liudehua" 5) "xiaotao" ~~~ > 3、 查询某个成员的名次 > #在正序榜中的名次 ~~~ redis 127.0.0.1:6379> zrank nanshen:yanzhi:bang xiaotao (integer) 0 ~~~ > #在倒序榜中的名次 ~~~ redis 127.0.0.1:6379> zrevrank nanshen:yanzhi:bang xiaotao (integer) 4 ~~~ > 4、修改成员的分数 ~~~ redis 127.0.0.1:6379> zincrby nanshen:yanzhi:bang 300 xiaotao "359" redis 127.0.0.1:6379> zrevrank nanshen:yanzhi:bang xiaotao (integer) 0 ~~~ ## 06/ Redis应用案例(1) > 案例: > Lol盒子英雄数据排行榜: > 1、 在redis中需要一个榜单所对应的sortedset数据 > 2、 玩家每选择一个英雄打一场游戏,就对sortedset数据的相应的英雄分数+1 > 3、 Lol盒子上查看榜单时,就调用zrange来看榜单中的排序结果 ## 07/ Redis应用案例(2) > 实现步骤: > 1、 每来一个用户创建购物车,就对购物车中的每一件商品在redis中插入一条以商品名称为key的sortedset,成员为同时出现在购物车中的其他商品,分数为1 > 2、 每新产生一个购物车,就对车中的商品更新redis中的sortedset,将同时出现的商品的分数增1 > 3、 推荐时,用户放入一件商品到购物车,则从这件商品对应的sortedset中查询分数最高的同现商品,推荐给该用户 > 设计思想如下,详见《代码》 ![](https://box.kancloud.cn/e6f9fecd701367736ff0dc0eb30c7fde_555x358.png)