请允许我拽一句文化词儿
工欲善其事必先利其器。
这里的器就是我们redis的根本 有什么样的数据结构决定了它适合做什么样的事儿。

------------------------------------------------------------分割线-------------------------------------------------------------
缓存大致可以分为两类:
内存缓存组件:(别问我有哪些 ,没研究过。有兴趣的话 自己去研究)
分布式缓存组件:Redis ,MongoDb(市场占有率正在萎缩因其维护起来比较卵疼),HBase(大数据方向的)

--------------------上干货-------------------------------------------
初识redis
官网地址:https://redis.io/ 没事多逛逛
命令集:http://redisdoc.com/index.htmlRemote Dictionary Server(远程数据服务) 这样看来它更像一个字典,例如大部分人用过的《新华字典》
redis实例 就是在服务器上安装的那个软件
db redis的db更类似于namespace的概念。单例的redis实例中有16个db,注意这里强调单例 在集群模式下 则只有一个db
key 可以称为句柄;用来读取数据
value 有多种数据结构;用来存值。别急 下面会分享的。

持久化:redis支持数据持久化 也别急后面也会分享到

value类型:
1、String -------别方(慌)就是java中的String
它支持
1.1、字符串
1.2、整数
1.3、浮点
它的内部结构
两种:一种是int ;二种是SDS,Simple Dynamic String;
用途:整数数用int ,字符串或字节串用或者浮点数String
如果你懂得c语言的话 哈哈 那么恭喜你可以去看一下这种数据类型的源码了 搜索sds.h 这个文件 这里是String类型数据的结构源码


番外篇 :和SDS有关
二进制安全:
在C语言的字符串里,我们存储数据就必须符合某种编码规则 ,并且除了字符串的末尾之外在字符串的末尾也不能包含空格,否则程序在读取数据时会把空格认为是结尾标识。所以这样一个规定 使得C语言只能保存文本数据而不能保存音频,视频等数据;
由此引入了二进制安全
在此用’\n’来表示结束 替代了空格
这就是二进制安全
(理解的比较粗浅)


如果阅读源码的话会看到这样的结构
struct sttribute((packed))sdshdr5{*******}
这里的sdshdr5 是SDS的header 表示可以存储多大长度的字符串

默认是用sdshdr8 来存储数据的

如何根据header来计算该结构可以存放多长的数据呢?
soooooo!!!!! easy
就sdshdr8来讲 它存储的长度是 2^8-1个 字节(byte也叫bit)
从redis3.2只有SDS有5种结构了
sdshdr5(这个是没有用到的,原因是 在大概87行有这样几个串串

char type = sdsReqType(initlen);//获取字符长长度
 if(type==SDS_TYPE_5 && initlen ==0 ) type=SDS_TYPE_8; //怎么样讲到这里有没有豁然开朗; SDS_TYPE_5被直接转换到SDS_TYPE_8了;
 sdshdr8
 sdshdr16
 sdshdr32
 sdshdr64

----------------------------再码一段番外篇----------------------
假如我们存了一个 “hello”,那么这个里面的属性都做了什么呢?
sdshdr8
len len=5
alloc alloc=5
flag flag= 比如说是001吧 这个flag是sdshdr8的一个标志, 每个header都对应了一个标志;当然实际上可能是另外一个数值,这里仅仅是举例
buf[] [‘h’,‘e’,‘l’,‘l’,‘o’,\0] 实际上是这样的 \0 是出于二进制安全的结尾符 这个结尾符不会计入到len 或alloc长度当中去的

那么这里还会涉及到扩容 这就是另外一回事了 ,有兴趣的话自己研究C语言去
那么String的应用场景是什么呢
ip限制 防止一个ip在单位时间内恶意的多次访问 结合incr使用
另一个关注点是 namespace 为防止大规模使用redis中 key的冲突 尽量在key命名时体现namespace 如user.*****
order.****同时还应注意可以的长度,避免过多的网络开销
2、list 链表
一个key对应多个value
可以从两边读取元素,它具有链表所有的优缺点;
优点:访问链表两端的数据时速度非常快时间复杂度是O(1)
缺点:当数据量非常大时 比如100k;通过下标访问数据的时候 会非常慢
它的操作命令是:
lpush 从左边压入(添加)元素
lpop 从左边弹出元素
rpush 从右边压入(添加)元素
rpop 从右边弹出元素
记住先进后出的原则
比如我们这样操作
lpush mylist 1 2 3 4
rpush mylist 0 -1
lrang mylist 0 -1 可以看到他的数据是这样式儿的
1) 4
2) 3
3) 2
4) 1
5) 0
6) -1
list的数据结构在3.2这个版本遇到了一个分水岭
在3.2之前 value对象内部使用linkedlistziplist
当数据量比较小的时候 使用ziplist 以节省内存空间
当元素个数比较多或单个元素比较长的时候 会使用双向链表linkedlist

在3.2之后 使用quicklist
quicklist对linkedlist和ziplist的两个优点做了汇总
linkedlist
优点: 对两端的数据进行操作速度非常快既复杂度比较低
缺点:但是内存开销比较大
ziplist
优点:内存开销比较小
缺点:因为它是一段连续的内存存储,所以当对其进行数据插入和删除时,会频繁申请内存降低了速度

所以结合这两者的有点quicklist应运而生
其本质是linkedlist 但是它有一个特点:它是基于ziplist的linkedlist 。

有点绕?!我来解释一下:
可以想象成一个链条,这个链条就是linkedlist,然而这个链条中的每一个环却是ziplist。对!!简单粗暴的糅合

有一点需要讲清楚
quicklist里面存储的ziplist里面有两种存储方式
一种是压缩
一种是不压缩的
那么怎么区分呢?
看它的数据结构
quicklist
tail
head
count
len

head指向quicklistnode

quicklistnode中有这些属性
prev
next
zl
其中zl指向ziplist(压缩数据)和quicklistLZF(非压缩数据)
然而quicklistnode又是一个双向链表可以前后互相指向
所以它是这样一个存储方式
总结一下
quicklist中的每个节点时quicklistnode,每一个quicklistnode里都有两种数据结构 这两种结构是 ziplist和quicklistLZF

所以quicklist是一个经过特殊编码的linckedlist

那么它的使用场景是什么呢
1、分布式队列 利用 先进先出 比如生产者和消费者 生产者把消息压入redis的list ----lpush 消费者从redis的list中弹出消息------brpop同步弹出数据 rpop 和 brpop 是同一个命令 只是brpop会阻塞
2、实现栈,当做MQ来实现 利用它后进先出的特性 lpush、lpop
3、队列 lpush rpop