你们好,我是南橘,从接触java到如今也有差很少两年时间了,两年时间,从一名连java有几种数据结构都不懂超级小白,到如今懂了一点点的进阶小白,学到了很多的东西。知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了一些日常学习和面试中的重点(自我认为),但愿给你们带来一些帮助java
这篇文章的出现,首先要感谢一我的三太子敖丙 ,就是他的文章让我发现,原来Redis的知识如此的多姿多彩。恩恩,他的文章,我是期期都看mysql
这是这篇文章的思惟导图,由于用的是免费版的软件,因此有很多水印,须要原版的能够问我要程序员
要学习Redis基础知识,首先要知道Redis的五种基础数据结构,咱们先从这五种数据结构的使用场景和一些工做中(面试中)容易出现的点来介绍面试
是redis中最基本的数据类型,一个key对应一个valueredis
适用状况:算法
一、缓存: 经典使用场景,把经常使用信息,字符串,图片或者视频等信息放到redis中,redis做为缓存层,mysql作持久化层,下降mysql的读写压力。
2.计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据能够一步落地到其余的数据源。
3.session:经过redis实现session共享
复制代码
对于Java中的HashMap,自己是一种KV对结构,如 value={{field1,value1},......fieldN,valueN}},很是好理解sql
适用状况:数据库
HashMap做为缓存,相比于string更节省空间的维护缓存信息,适合存储如用户信息,视频信息等
复制代码
底层用字典dict实现数组
Redis 的链表至关于 Java 语言里面的 LinkedList缓存
适用状况:
一、List在Redis中既能够作队列也能够作栈使用,它的插入和删除操做很是快,时间复杂度为 0(1),可是索引定位很慢,时间 复杂度为 O(n)。
二、能够做为微博的时间轴,有人发布微博,用lpush加入时间轴,展现新的列表信息。
三、能够实现阻塞队列,左进右出的队列组完成设计
复制代码
list底层使用quickList(快速链表)实现
在早期的设计中, 当列表对象中元素的长度比较小或者数量比较少的时候,采用ziplist来存储,当列表对象中元素的长度比较大或者数量比较多的时候,则会转而使用双向列表linkedlist来存储。
这两种存储方式都各有优缺点
3.2版本更新以后,list的底层实现变成了quickList
quickList是 zipList 和 linkedList 的混合体,它将 linkedList 按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。
Redis 的集合至关于 Java 语言里面的 HashSet ,它内部的键值对是无序的、惟一 的。 它的内部实现至关于一个特殊的字典,字典中全部的 value 都是一个值 NULL 当集合中最后一个元素被移除以后,数据结构被自动删除,内存被回收。
适用状况:
一、标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者相似标签的能够给推荐关注的事或者关注的人。
二、点赞,或点踩,收藏等,能够放到set中实现
三、能够用来存储在某活动中中奖的用户 ID ,由于有去重功能,能够保证同 一个用户不会中奖两次。
复制代码
它相似于 Java SortedSet HashMap 的结合体, 方面它是个 set ,保证 了内部 value 的惟性,另方面它可 以给每一个 value 赋予一个 score ,表明 这个 value 的排序权重。它的内部实现 用的是一种叫做“跳跃表”的数据 结构。
仍是和上期同样,从一位大佬那边借过来一张图片给你们展现,什么是跳跃表
从这张图片,咱们能够看出来:跳跃表的底层是一个顺序链表,每隔一个节点有一个上层的指针指向下一个节点,并层层向上递归。这样设计成相似树形的结构,可使得对于链表的查找能够到达二分查找的时间复杂度。
skiplist他不要求上下两层链表之间个数的严格对应关系,他为每一个节点随机出一个层数。好比上图第三个节点的随机出的层数是4,那么就把它插入到四层的空间上,而第四个节点随机出的层数是1,那它就只存在第一层空间上。
ziplist是由一系列特殊编码的连续内存块组成的顺序存储结构,相似于数组,ziplist在内存中是连续存储的,可是不一样于数组,为了节省内存 ziplist的每一个元素所占的内存大小能够不一样
ziplist将一些必要的偏移量信息记录在了每个节点里,使之能跳到上一个节点或下一个节点。
除了这五大基础数据结构,Redis还有更加专业的数据结构 HyperLogLog(基数统计的算法)、Geo(地理位置系列)、Pub\Sub(消息队列)、Pipeline(管道)、BloomFiler(布隆过滤器),都在不一样的地方有用到,有些我会在下文向你们介绍,还有一些深刻的我没有了解这么多,你们能够去敖丙的文章中进一步了解
能够将屡次IO往返时间缩减为一次,前提是pipleline执行的指令之间没有因果关系
管道(pipeline)能够一次性发送多条命令并在执行完后一次性将结果返回,pipeline 经过减小客户端与redis的通讯次数来实现下降往返延时时间,并且Pipeline 实现的原理是队列,而队列的原理是时先进先出,这样就保证数据的顺序性。
注意:pipeline机制能够优化吞吐量,但没法提供原子性/事务保障
随着互联网技术的飞速发展,愈来愈多的单体架构已经转型成了分布式架构,,分布式架构确实能带来性能和效率上的提高,可是也会带来数据一致性的问题。
分布式锁,就是解决分布式架构中数据一致性的专用武器,分布式锁须要知足一下三个方面方可放心使用:
排他性:在同一时间只会有一个客户端能获取到锁,其它客户端没法同时获取
避免死锁:这把锁在一段有限的时间以后,必定会被释放(正常释放或异常释放)
高可用:获取或释放锁的机制必须高可用且性能佳
目前,我所知道的分布式锁大概有三种主流方式实现,分别是zookpeer,redis,还有本地数据库,今天我就介绍一下如何用redis实现分布式锁。
基于Redis实现的锁机制,主要是依赖redis自身的原子操做
setnx争抢锁,再用expire添加过时时间
你没有看错,就是这么简单,若是惧怕不妥,好比争抢锁的时候尚未设置过时时间就忽然宕机之类的问题,能够直接用jedis等封装好的RedisTemplate把setnx和expire合成一条指令使用。
首先,单点故障的问题不可避免 其次,由于使用锁的客户端,和redis服务器,不在一块儿啊!时间是有延迟的,咱们只能依靠redis的TTL命令来查询锁的剩余时间,而后根据这个剩余时间来判断锁是否超时。 然而在一般的计算机系统中,很难获取到一个可靠的时间。
其实若是咱们使用的场景不须要那么强的安全性精确性,这样也足够用了,至少我如今的公司够用了(一个排名前列的第三方支付企业)。可是,精益求精是程序员们的本质,RedLock的出如今必定程度上解决了这个问题。
我不是很了解RedLock,只能稍微给你们介绍一下,流程以下:
- 客户端获取当前时间,生成一个随机值做为锁的值(目的是更加精确的得到时间)
- 依次尝试在全部5个redis上取得同一个锁(使用相似单机redis锁的方法, 使用一样的key和同一个随机值) 获取锁的操做自己须要设定一个比较小的超时时间(如5-50ms), 防止在一个挂掉的redis上浪费太多时间 若是一个redis不可用,要尽快开始尝试下一个
- 客户端计算获取锁一共用了多长时间,经过用当前时间减去第1步获得的时间 若是客户端获取了多数redis上的这个锁(3到五个5),而且这时尚未超过锁的超时时间, 这个锁就算是获取成功了
- 若是锁获取成功了,有效时间就按锁超时时间-获取锁花费时间算
- 若是失败,尝试在全部redis上解除锁 (解除锁的操做是一段lua script,删除一个key若是key的value是第1步生成的随机值)
固然,它也不能解决问题,可是, redis锁只会在比较极端的状况下出错,若是不是须要特别精确,只须要保证绝大多数可靠的时候,能够放心使用redis集群或者redlock。
解决了分布式锁的问题,可是仍是没有解决各类天灾人祸的问题,因此,这就须要Redis集群出马了
Redis中有主从机制,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。先讲一下Redis主从同步的流程:
同时,在2.8版本以后,Redis能够自动判断是须要全量同步仍是增量同步,效率比较高,增量同步其实就是在完成全量同步后,开始新复制时向主服务器发送PSYNC( )命令(runid是上次复制的主服务器id,offset是从服务器的复制偏移量),主服务器会根据这个两个参数来决定作哪一种同步,判断服务器id是否和本机相同,复制偏移量是否在缓冲区中。
关于这些集群的东西一章内容确定写不完,我会在之后的文章里向你们介绍
Redis的本职工做是缓存,可是因为它多才多艺,成为队列也不错,有一些阻塞式的API让其有能力作消息队列;另外,作消息队列的其余特性例如FIFO(先入先出)也很容易实现,只须要一个List对象从头取数据,从尾部塞数据便可
在Redis中,能够利用 sorted-set 来作延时队列
zadd key score1 value1 score2 value2
可是,Redis的延时队列没法返回ACK,因此须要本身实现
Redis有两种持久化的方式,分别是RDB和AOF
RDB作镜像全量持久化、AOF作增量持久化,由于RDB会耗费较长时间,不够实时,在停机的时候会致使大量有效数据丢失,因此须要AOF来配合使用,在redis实例重启时,会使用RDB持久化文件从新构建内存,再使用AOF重放近期的操做指令来实现完整恢复重启以前的状态。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。RDB提供了三种机制来触发持久化
一、save触发方式---客户端发起save请求
执行save命令期间,Redis不能处理其余命令,直到RDB过程完成为止
二、bgsave触发方式--客户端发起bgsave请求
执行该命令时,Redis会在后台异步进行快照操做,快照同时还能够响应客户端请求
三、自动触发
自动触发是由咱们的配置文件来完成的,在redis.conf文件中配置,你们能够去了解一下,这里就不写那么多东西了
全量备份老是耗时的(随机的传说老是好的???),有时候咱们提供一种更加高效的方式AOF,Redis会将每个收到的写命令都经过write函数追加到文件中,就是日志记录。
和RDB同样,AOF也有三种同步机制:
一、always:同步持久化 每次发生数据变动会被当即记录到磁盘 性能较差但数据完整性比较好
二、everysec:每秒同步,异步操做,每秒记录 若是一秒内宕机,有数据丢失
三、no:从不一样步
Redis自己的机制是AOF持久化开启存在AOF文件时,优先加载AOF文件。AOF文件不存在时候,加载RDB文件。加载AOF\RDB文件后,Redis启动成功。AOF\RDB文件存在错误时,Redis启动失败并打印错误信息。
不要问AOF和RDB用哪一个,个人经验就是,全都用。 RDB同步快,可是要损失最多五分钟的内容,AOF同步慢,可是每秒同步的状况下最多损失1s的内容,损失的内容也能够经过日志去找回。
AOF日志中能够进行sync属性的配置,若是不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据,但在高性能要求下每次都sync是不现实的,通常都使用定时sync,好比1s一次,这个时候就最多丢失1s的数据。
emmmm,第二篇文章也慢慢的写完了,在写文章的过程当中,真的是发现本身懂得越多,不懂越多,并且由于公司周末要加班,因此这将Redis分红两篇来完成,但愿你们能够喜欢~~ 同时须要思惟导图的话,能够联系我,毕竟知识越分享越香!
![]()