Redis

182 阅读6分钟

Redis基础

Redis基础数据结构

Redis 有 5 种基础数据结构, 分别为: string ( 字符串)、list (列表)、hash (字典)、 set ( 集合)和 zset ( 有序集合)

  1. Redis 的字符串是动态字符串,是可以修改的字符串,内部结构的实现类似于Java 的Array List ,采用预分配冗余空间的方式来减少内存的频繁分配

  2. Redis 的列表相当于Java 语言里面的LinkedList , 注意它是链表而不是数组。这意味着list 的插入和删除操作非常快,时间复杂度为0(1 ),但是索引定位很慢,时间复杂度为O(n)。实际上是称之为“快速链表”(quicklist )的一个结构。首先在列表元素较少的情况下,会使用一块连续的内存存储,这个结构是ziplist,压缩列表。它将所有的元素彼此紧挨着一起存储,分配的是-块连续的内存。当数据量比较多的时候才会quick.list。因为普通的链表需要的附加指针空间太大,会浪费空间,还会加重内存的碎片化,比如某普通链表里存的只是int 类型的数据,结构上还需要两个额外的指针prev 和next 。所以Redis 将链表和zip list 结合起来组quick.list,也就是将多个ziplist 使用双向指针串起来使用。

  3. Redis 的字典相当于Java 语言里面的HashMap都是“数组+链表”二维结构

  4. Redis 的集合相当于Java 语言里面的HashSet ,它内部的键值对是无序的、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value 都是一个值NULL

  5. 类似于Java 的SortedSet 和HasMap的结合体, 一方面它是个set,保证了内部value 的唯一性,另一方面它可 以给每个value 赋予一个score ,代表这个value 的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据 结构。

Redis队列

Redis的消息队列不是专业的消息队列,它没有非常多的高级特性,没有ack保证,如果对消息的可靠性有着极高要求, 那么它就不适合使用。

Redis 的list数据结构常用来作为异步消息队列使用,用rpush(在key对应list的尾部添加字符串元素操作人队列)和lpush(在key对应list的头部添加字符串元素操作人队列),用lpop 和rpop 操作出队列。Redis没有相应的机制保证消息的可靠消费,如果发布者发布一条消息,而没有对应的订阅者的话,这条消息将丢失,不会存在内存中。 redis发布订阅模式,一个队列可以被多个消费者同时订阅,当有消息到达时,会将该消息依次发送给每个订阅者,她是一种消息的广播形式,redis本身不做消费者的负载均衡,因此消费效率存在瓶颈;

redis的持久化是针对于整个redis缓存的内容,它有RDB和AOF两种持久化方式,可以将整个redis实例持久化到磁盘,以此来做数据备份,防止异常情况下导致数据丢失。

延时队列:比较适合异步消息处理,将当前冲突的请求扔到另一个队列延后处理以避开冲突。延时队列可以通过Redis 的zset (有序列表)来实现。我们将消息序列化成一个字符串作为zset 的value ,这个消息的到期处理时间作score ,然后用多个线程轮询zset 获取到期的任务进行处理。多个线程是为了保障可用性,万一挂了一个线程还有 其他线程可以继续处理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不会被多次执行。

分布式锁

为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLock或Synchronized)进行互斥控制。在单机环境中,Java中提供了很多并发处理相关的API。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可

Redis的实现方式

使用setnx 和expire 组合在一起的原子指令 SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。 expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁 delete key:删除key

Zookeeper分布式锁的原理

Zookeeper分布式锁应用了临时顺序节点。首选会创建一个创建一个持久节点ParentLock,接着一个客户端0会在持久节点下创建一个临时顺序节点Lock1,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁,如果不是第一个则获取失败,接着便会向lock1注册watch机制,以此类推。利用临时节点的特性,当client0任务完成时,便会调用指令把lock1删除,client1便会立刻收到通知,判断Lock1是不是顺序最靠前的一个。如果是,变获得了该锁。