旅行社网站模版,emlog怎么换wordpress,外贸网站建设推广公司价格,咸阳网站设计链表结构是 Redis 中一个常用的结构#xff0c;它可以存储多个字符串#xff0c;而且它是有序的#xff0c;能够存储 2 的 32 次方减 1 个节点#xff08;超过 40 亿个节点#xff09;。
Redis 链表是双向的#xff0c;因此即可以从左到右#xff0c;也可以从右到左遍历…链表结构是 Redis 中一个常用的结构它可以存储多个字符串而且它是有序的能够存储 2 的 32 次方减 1 个节点超过 40 亿个节点。
Redis 链表是双向的因此即可以从左到右也可以从右到左遍历它存储的节点链表结构如下图所示。
由于是双向链表所以只能够从左到右或者从右到左地访问和操作链表里面的数据节点。但是使用链表结构就意味着读性能的丧失所以要在大量数据中找到一个节点的操作性能是不佳的因为链表只能从一个方向中去遍历所要节点。
比如从查找节点 10 000 开始查询它需要按照节点 1、节点 2、节点 3……直至节点 10 000这样的顺序查找然后把一个个节点和你给出的值比对才能确定节点所在。如果这个链表很大如有上百万个节点可能需要遍历几十万次才能找到所需要的节点显然查找性能是不佳的。
而链表结构的优势在于插入和删除的便利因为链表的数据节点是分配在不同的内存区域的并不连续只是根据上一个节点保存下一个节点的顺序来索引而已无需移动元素。其新增和删除的操作如下图所示。
上述的的阿拉伯数字代表新增的步骤而汉字数字代表删除步骤。
1 新增节点 对插入图中的节点 4 而言先看从左到右的指向先让节点 4 指向节点 1 原来的下一个节点也就是节点 2然后让节点 1 指向节点 4这样就完成了从右到左的指向修改。再看从右到左先让节点 4 指向节点 1然后节点 2 指向节点 4这个时候就完成了从右到左的指向那么节点 1 和节点 2 之间的原有关联关系都已经失效这样就完成了在链表中新增节点4的功能。
2 删除节点 对删除图中的节点 3 而言首先让节点 2 从左到右指向后续节点然后让后续节点指向节点 2这样节点 3 就脱离了链表也就是断绝了与节点 2 和后继节点的关联关系然后对节点 3 进行内存回收无须移动任何节点就完成了删除。
由此可见链表结构的使用是需要注意场景的对于那些经常需要对数据进行插入和删除的列表数据使用它是十分方便的因为它可以在不移动其他节点的情况下完成插入和删除。而对于需要经常查找的使用它性能并不佳它只能从左到右或者从右到左的查找和比对。
因为是双向链表结构所以 Redis 链表命令分为左操作和右操作两种命令左操作就意味着是从左到右右操作就意味着是从右到左。
Redis关于链表的命令
上表所示的链表命令其中以“l”开头的代表左操作以“r”开头的代表右操作。对于很多个节点同时操作的需要考虑其花费的时间链表数据结构对于查找而言并不适合于大数据而 Redis 也给了比较灵活的命令对其进行操作。
Redis关于链表的操作命令
这里展示了关于 Redis 链表的常用命令只是对于大量数据操作的时候我们需要考虑插入和删除内容的大小因为这将是十分消耗性能的命令会导致 Redis 服务器的卡顿。对于不允许卡顿的一些服务器可以进行分批次操作以避免出现卡顿。
需要指出的是之前这些操作链表的命令都是进程不安全的因为当我们操作这些命令的时候其他 Redis 的客户端也可能操作同一个链表这样就会造成并发数据安全和一致性的问题尤其是当你操作一个数据量不小的链表结构时常常会遇到这样的问题。
为了克服这些问题Redis 提供了链表的阻塞命令它们在运行的时候会给链表加锁以保证操作链表的命令安全性如下表所示。
链表的阻塞命令 当使用这些命令时Redis 就会对对应的链表加锁加锁的结果就是其他的进程不能再读取或者写入该链表只能等待命令结束。加锁的好处可以保证在多线程并发环境中数据的一致性保证一些重要数据的一致性比如账户的金额、商品的数量。
不过在保证这些的同时也要付出其他线程等待、线程环境切换等代价这将使得系统的并发能力下降。
Redis 链表阻塞操作命令
在项目中虽然阻塞可以有效保证了数据的一致性但是阻塞就意味着其他进程的等待CPU 需要给其他线程挂起、恢复等操作更多的时候我们希望的并不是阻塞的处理请求所以这些命令在实际中使用得并不多。
使用 Spring 去操作 Redis 链表的命令这里继续保持代码关于 RedisTemplate 的配置在此基础上获取 RedisTemplate 对象然后输入以下代码。
public static void testList() {ApplicationContext applicationcontext new ClassPathXmlApplicationContext(applicationContext.xml);RedisTemplate redisTemplate applicationcontext.getBean(RedisTemplate.class);try {//删除链表以便我们可以反复测试redisTemplate.delete(list);//把node3插入链表listredisTemplate. opsForList ().leftPush (list, node3);ListString nodeList new ArrayListString();for (int i 2; i 1; i--){nodeList.add(nnode i);}//相当于lpush把多个价值从左插入链表redisTemplate.opsForList().leftPushAll(list, nodeList);//从右边插入一个节点redisTemplate.opsForList().rightPush(list, node4);//获取下标为0的节点String nodel (String) redisTemplate.opsForList() .index(list, 0);//获取链表长度long size redisTemplate.opsForList ().size (listn);//从左边弹出一个节点String lpop (String) redisTemplate.opsForList().leftPop(list);//从右边弹出一个节点String rpop (String) redisTemplate.opsForList().rightPop(list);//注意需要使用更为底层的命令才能操作linsert命令//使用linsert命令在node2前插入一个节点redisTemplate.getConnectionFactory().getConnection().lInsert(list.getBytes(utf-8),RedisListCommands.Position.BEFORE,node2.getBytes(utf-8),before_node.getBytes(utf-8));//使用linsert命令在node2后插入一个节点redisTemplate.getConnectionFactory().getConnection().linsert(list.getBytes(utf-8),RedisListCommands.Position.AFTER,node2.getBytes(utf-8), after_node.getBytes(utf-8));//判断list是否存在如果存在则从左边插入head节点redisTemplate.opsForList().leftPushlfPresent(list, head);//判断list是否存在如果存在则从右边插入end节点redisTemplate.opsForList().rightPushlfPresent(list, end);//从左到右或者下标从0到10的节点元素List valueList redisTemplate.opsForList().range(list, 0, 10);nodeList.clear();for (int i 1; i 3; i) {nodeList.add(node);}//在链表左边插入三个值为node的节点redisTemplate.opsForList().leftPushAll.(list, nodeList);//从左到右删除至多三个node节点redisTemplate.opsForList().remove(list, 3,node);//给链表下标为0的节点设置新值redisTemplate.opsForList().set(list,0, new_head_value);} catch (UnsupportedEncodingException ex) {ex.printStackTrace();}//打印链表数据printList(redisTemplate, list);
}
public static void printList(RedisTemplate redisTemplate, String key) {
//链表长度Long size redisTemplate.opsForList().size(key);
}这里所展示的是 RedisTemplate 对于 Redis 链表的操作其中 left 代表左操作right 代表右操作。有些命令 Spring 所提供的 RedisTemplate 并不能支持比如 linsert 命令这个时候可以使用更为底层的方法去操作正如代码中的这段
// 使用linsert命令在node2前插入一个节点
redisTemplate.getConnectionFactory().getConnection().lInsert(list.getBytes(utf-8),
RedisListCommands.Position.BEFORE,node2.getBytes(utf-8),
before_node.getBytes(utf-8));在多值操作的时候往往会使用 list 进行封装比如 leftPushAll 方法对于很大的 list 的操作需要注意性能比如 remove 这样的操作在大的链表中会消耗 Redis 系统很多的性能。
Redis 还有对链表进行阻塞操作的命令这里 Spring 也给出了支持代码如下所示。
public static void testBList() {ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);RedisTemplate redisTemplate applicationContext.getBean(RedisTemplate.class);// 清空数据可以重复测试redisTemplate.delete (list1);redisTemplate.delete (list2);//初始化链表 list1ListString nodeList new ArrayListString();for (int i1; i5; i) {nodeList.add(node i);}redisTemplate.opsForList().leftPushAll(list1, nodeList);// Spring 使用参数超时时间作为阻塞命令区分等价于 blpop 命令并且可以设置时间参数 redisTemplate.opsForList().leftPop (list1, 1, TimeUnit.SECONDS);// Spring 使用参数超时时间作为阻塞命令区分等价于 brpop 命令并且可以设置时间参数redisTemplate.opsForList().rightPop(list1, 1, TimeUnit.SECONDS);nodeList.clear();// 初始化链表 list2for (int i1; i3; i) {nodeList.add(dato i);}redisTemplate.opsForList().leftPushAll(list2, nodeList);// 相当于 rpoplpush 命令弹出 list1 最右边的节点插入到 list2 最左边redisTemplate.opsForList().rightPopAndLeftPush(list1,list2);// 相当于brpoplpush命令注意在 Spring 中使用超时参数区分 redisTemplate.opsForList().rightPopAndLeftPush(list1, list2,1,TimeUnit.SECONDS);// 打印链表数据printList(redisTemplate, list1);printList(redisTemplate, list2);
}这里展示了 Redis 关于链表的阻塞命令在 Spring 中它和非阻塞命令的方法是一致的只是它会通过超时参数进行区分而且我们还可以通过方法设置时间的单位使用还是相当简单的。注意它是阻塞的命令在多线程的环境中它能在一定程度上保证数据的一致而性能却不佳。