摘要: 前言 这一篇文章将讲述Redis中的list类型命令,一样也是经过demo来说述,其余部分这里就不在赘述了。 项目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list 案例 demo功能是队列,整个demo的大体页面以下。html
这一篇文章将讲述Redis中的list类型命令,一样也是经过demo来说述,其余部分这里就不在赘述了。java
项目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-listgit
demo功能是队列,整个demo的大体页面以下。左边是存储到Redis中的数据,右边是从Redis中弹出的数据。github
首先定义一个存储list的keyredis
private static final String LIST_KEY = "list:1";
队列的key就用list:1算法
redis操做对象编程
private RedisTemplate redisTemplate; //string 命令操做对象 private ValueOperations valueOperations; //list 命令操做对象 private ListOperations listOperations;
list在Redis中的结构能够看下图(图片来源于Redis in Action)。安全
命令介绍服务器
命令 | 用例 | 描述 |
---|---|---|
LPUSH | LPUSH key value [value ...] | 将全部指定的值插入到存于 key 的列表的头部。 若是 key 不存在,那么在进行 push 操做前会建立一个空列表。 |
LPUSHX | LPUSHX key value | 只有当 key 已经存在而且存着一个 list 的时候,在这个 key 下面的 list 的头部插入 value。 |
接下来看看demo中头部插入的功能,点击下图中头部插入按钮,而后在弹出框中填入数字0,点击提交后整个头部插入流程结束。能够看到左边的队列数据出现了一条{"data":"0"} 数据,在数据{"data":"1"} 上面。网络
来看看后台的方法
@RequestMapping(value = "/leftPop",method = RequestMethod.GET) public Object leftPop(){ return listOperations.leftPop(LIST_KEY); }
若是须要在Redis中操做,能够敲下面的命令
lpush list:1 "{\"data\":\"0\"}"
命令介绍
命令 | 用例 | 描述 |
---|---|---|
RPUSH | RPUSH key value [value ...] | 向存于 key 的列表的尾部插入全部指定的值。若是 key 不存在,那么会建立一个空的列表而后再进行 push 操做。 |
RPUSHX | RPUSHX key value | 将值 value 插入到列表 key 的表尾, 当且仅当 key 存在而且是一个列表。 |
接下来看看demo中尾部插入的功能,点击下图中尾部插入按钮,而后在弹出框中填入数字11,点击提交后整个新增流程结束。能够看到左边的队列数据出现了一条{"data":"11"} 数据,在数据{"data":"10"}下面。
来看看后台的方法
@RequestMapping(value = "/rightPop",method = RequestMethod.GET) public Object rightPop(){ return listOperations.rightPop(LIST_KEY); }
若是须要在Redis中操做,能够敲下面的命令
rpush list:1 "{\"data\":\"11\"}"
一样先看看相关的获取值命令
命令 | 用例 | 描述 |
---|---|---|
LRANGE | LRANGE key start stop | 返回存储在 key 的列表里指定范围内的元素。 |
LINDEX | LINDEX key index | 返回列表里的元素的索引 index 存储在 key 里面。 |
LLEN | LLEN key | 返回存储在 key 里的list的长度。 |
后台查询方法,将新增的内容查询出来
@RequestMapping(value = "/getList",method = RequestMethod.GET) public List getList(){ List list = listOperations.range(LIST_KEY, 0, -1); //能够用size获取成员长度 //listOperations.size(LIST_KEY); return list; }
命令 | 用例 | 描述 |
---|---|---|
LPOP | LPOP key | 移除而且返回 key 对应的 list 的第一个元素。 |
BLPOP | BLPOP key [key ...] timeout | 它是命令 LPOP 的阻塞版本,这是由于当给定列表内没有任何元素可供弹出的时候, 链接将被 BLPOP 命令阻塞。 |
接下来看看头部弹出的功能,点击下图中头部弹出按钮,能够看到左边的队列顶部数据减小了,在右边弹出的数据出现了左边队列数据消失的数据。
来看看后台的方法
@RequestMapping(value = "/leftPop",method = RequestMethod.GET) public Object leftPop(){ return listOperations.leftPop(LIST_KEY); }
若是须要在Redis中操做,能够敲下面的命令
lpop list:1
命令 | 用例 | 描述 |
---|---|---|
RPOP | RPOP key | 移除并返回存于 key 的 list 的最后一个元素。 |
BRPOP | BRPOP key [key ...] timeout | 它是 RPOP 的阻塞版本,由于这个命令会在给定list没法弹出任何元素的时候阻塞链接。 |
接下来看看尾部弹出的功能,点击下图中尾部弹出按钮,能够看到左边的队列尾部数据减小了,在右边弹出的数据出现了左边队列数据消失的数据。
来看看后台的方法
@RequestMapping(value = "/rightPop",method = RequestMethod.GET) public Object rightPop(){ return listOperations.rightPop(LIST_KEY); }
若是须要在Redis中操做,能够敲下面的命令
rpop list:1
命令 | 用例 | 描述 | |
---|---|---|---|
LINSERT | LINSERT key BEFORE\ | AFTER pivot value | 把 value 插入存于 key 的列表中在基准值 pivot 的前面或后面。 |
LREM | LREM key count value | 从存于 key 的列表里移除前 count 次出现的值为 value 的元素。 | |
LSET | LSET key index value | 设置 index 位置的list元素的值为 value。 | |
LTRIM | LTRIM key start stop | 修剪(trim)一个已存在的 list,这样 list 就会只包含指定范围的指定元素。 | |
RPOPLPUSH | RPOPLPUSH source destination | 原子性地返回并移除存储在 source 的列表的最后一个元素(列表尾部元素), 并把该元素放入存储在 destination 的列表的第一个元素位置(列表头部)。 | |
BRPOPLPUSH | BRPOPLPUSH source destination timeout | BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本。 |
这两个命令做用实际上是相同的,只不过BRPOPLPUSH是阻塞的,当没有数据时,会一直阻塞,直到有数据。
在Redis官方文档中,用RPOPLPUSH命令举了两个例子,一个是Reliable queue(安全的队列 ),另外一个是Circular list(循环列表)。
Redis一般都被用作一个处理各类后台工做或消息任务的消息服务器。 一个简单的队列模式就是:生产者把消息放入一个列表中,等待消息的消费者用 RPOP 命令(用轮询方式), 或者用 BRPOP 命令(若是客户端使用阻塞操做会更好)来获得这个消息。
然而,由于消息有可能会丢失,因此这种队列并是不安全的。例如,当接收到消息后,出现了网络问题或者消费者端崩溃了, 那么这个消息就丢失了。
RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH) 提供了一种方法来避免这个问题:消费者端取到消息的同时把该消息放入一个正在处理中的列表。 当消息被处理了以后,该命令会使用 LREM 命令来移除正在处理中列表中的对应消息。
另外,能够添加一个客户端来监控这个正在处理中列表,若是有某些消息已经在这个列表中存在很长时间了(即超过必定的处理时限), 那么这个客户端会把这些超时消息从新加入到队列中。
RPOPLPUSH 命令的 source 和 destination 是相同的话, 那么客户端在访问一个拥有n个元素的列表时,能够在 O(N) 时间里一个接一个获取列表元素, 而不用像 LRANGE那样须要把整个列表从服务器端传送到客户端。
上面这种模式即便在如下两种状况下照样能很好地工做: 有多个客户端同时对同一个列表进行旋转(rotating):它们会取得不一样的元素,直到列表里全部元素都被访问过,又从头开始这个操做。 有其余客户端在往列表末端加入新的元素。
这个模式让咱们能够很容易地实现这样一个系统:有 N 个客户端,须要接二连三地对一批元素进行处理,并且处理的过程必须尽量地快。 一个典型的例子就是服务器上的监控程序:它们须要在尽量短的时间内,并行地检查一批网站,确保它们的可访问性。
值得注意的是,使用这个模式的客户端是易于扩展(scalable)且安全的(reliable),由于即便客户端把接收到的消息丢失了, 这个消息依然存在于队列中,等下次迭代到它的时候,由其余客户端进行处理。
约瑟夫问题(有时也称为约瑟夫斯置换),是一个出如今计算机科学和数学中的问题。在计算机编程的算法中,相似问题又称为约瑟夫环。
人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人以后,执行下一我的。 对剩下的人重复该过程,从下一我的开始,朝同一方向跳过相同数量的人,直到只剩下一我的,并被释放。
问题即,给定人数、起点、方向和要跳过的数字,选择初始圆圈中的位置以免被处决。
来自维基百科 https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98
思路
定义一个list key为josephus,利用
RPOPLPUSH josephus josephus
命令来构造循环链表,每当数到3时,使用rpop
rpop josephus
命令弹出
代码实现
public class JosephusProblem extends RedisBaseConnection { @Test public void test() { //构造数据 for (int i = 1; i <= 41; i++) { listOperations.leftPush("josephus", String.valueOf(i)); } int index = 1; while (listOperations.size("josephus") > 0) { //当数到3时,弹出 if (index == 3) { System.out.println(listOperations.range("josephus", 0, -1)); System.out.println("当前被杀的人是:" + listOperations.rightPop("josephus")); index = 0; } else { listOperations.rightPopAndLeftPush("josephus", "josephus"); } index++; } } }
整个代码步骤以下
运行结果有点长,这里只截图最后一部分的结果,以下
约瑟夫问题代码请点击JosephusProblem.java
建议学习的人最好每一个命令都去敲下,加深印象。下面诗句送给大家。
纸上得来终觉浅,绝知此事要躬行。————出自《冬夜读书示子聿》