Redis 想必大伙应该都知道,而且肯定是用到了的。我这里就不一一赘述了。
那么我们就来聊一聊Redis 的来源,主要用于做什么,有哪些优势等等.
一、Redis 是什么?
来源:据说是08 年的时候有一个意大利西西里岛的小伙子,在创建一个网站时候,发现的有些由于读写的操作频繁,一开始是考虑的Mysql,但是发现在并发高的时候,发现怎么优化数据库都效果不明显,因为数据库存储的是磁盘(瓶颈也在此,磁盘的读写熟速度导致的),因此就决定放弃数据库(Mysql),自己去实现一个列表结构的数据库,但是这个数据并不是存储在磁盘上,而是在内存上,我们都知道内存的读写速度跟磁盘差距很大的,因IC也提高了数据库的get/set速度了,因此在通过利用C语言重写这个内存数据库,因为内存的服务宕机时,数据是不会保存的,因此还加上了数据的持久化。这样09年的时候,Redis就诞生。Redis( REmote DIctionary Service:远程字典服务)
二、Redis的定位及特性
在通常情况下如果需要数据存储的话,我们一般都是考虑传统的关系型数据库的(如:Mysql,SqlServer,Oracle等.)
关系型的数据的特点:1、一般以表结构形式存储,二维模式 2、存储的结构化,数据需要有固定的数据结构来适应表结构存储 3、各表之间有一定的关联关系(外键等) 4、一般都支持Sql等复杂表之间的关联查询,只是有些语法上存在一些差异 5、支持事物 。不足:1、扩容的话,支持垂直扩容,增加磁盘,不支持动态扩容,水平扩容支持相对复杂(分库分表等技术) 2、表结构修改较为复杂,数据结构相对固定 3、读写速度相对来说会比较慢,尤其在高速读写时,磁盘的I/O会是一定的瓶颈。
在这样的情况下,No sql (非关系型)就应运而生了。其特点: 1、存储非固定的结构,可文本,图形,视频等 2、表之间无关系 3、也保证的数据的最终一致性 4、支持海量的数据存储且保证了高并发下的读写效率 5、支持分布式,能够对数据进行分片存储,扩容相对简单。
No sql 根据不通的存储类型,又有各种类型的数据库。K-V 结构:Redis,MemcacheDB等 ,文档:MongoDb 等等...
今天我们主要聊聊Redis,Redis的主要特点:1、数据类型丰富(String,Hash,Set, List 等)2、单机与分布式 3、支持持久化,数据的过期策略 4、支持多种语言 5、集群,高可用等
三、Redis 安装与基本操作
安装:Redis的安装,我的上一篇Redis安装已经说明了,就不再赘述了(Redis 安装)
基本操作
1、启动:redis-server /usr/local/soft/redis-5.0.5/redis.conf
2、选择数据库 select 1 (默认有16个库,0,1,..15)
3、清空当前数据库-> flushdb / flushall (清空所有)
4、set key value 、get key
具体命令参考:http://redisdoc.com/index.html
四、Redis的基本数据类型
1、String 字符类型
a、字符类型应该算是最常用的类型了。set / get 就是对String 类型的操作命令了。
b、String 可以存储的类型包括:字符串,整数,浮点型
c、语法:set key value [expiration EX seconds|PX milliseconds][NX|XX] 具体参考官网哈
设置失效时间,基于此我们可以设置分布式锁,自动释放锁等操作
d、存储原理 :因为Redis是K-V 结构的,是通过hashTable 实现的,每个键值对都会有一个dictEntry。如下数据结构
key 是个字符串,而redis中没有使用C中的字符数组,而是使用SDS, 而value 也不是作为字符串字存储的,而是存储在redisObject中。
可以使用 type 命令来查看对外的类型 - > type key
在字符串内部其实还有根据value,又分了几种编码类型
1、int,存储 8 个字节的长整型(long,2^63-1)。
2、embstr, 代表 embstr 格式的 SDS(Simple Dynamic String 简单动态字符串),
存储小于 44 个字节的字符串。
3、raw,存储大于 44 个字节的字符串(3.2 版本之前是 39 字节)
什么是SDS (SDS其实根据存储的长度,又分了好几种如:sdshdr5(2^5)sdshdr8(2^8) ...)
sds特点:1、如果需要会对sds进行动态扩容,不必担心溢出问题 2、计算字符的长度时时间复杂度o(1). 3、可以存储任意类型字符(如:\0) 4、可以通过判断len判断字符是否结束
为什么redis需要使用SDS来实现存储字符串呢?
因为redis 是通过C语言实现的,而在C中是通过char[] 数组实现字符串存储的,而char[]是有结束标记的('\0')因此如果当字符串(音频,视频,图片等文件时转二进制时)中存在(\0)再去存储的话,就会有问题的。因此需要只有从新自己实现了。
1、使用字符数组时,必须分配足够的空间,否则会溢出的
2、如果需要计算字符的长度,那么就需要遍历数组的,时间复杂度是o(n).
3、C 中数组的字符长度的变更需要重新做内存分配
embstr 和 raw 的区别?
embstr 只会分配1次内存空间,raw 需要分配2次内存空间。因此与 raw 相比,embstr 的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。embstr 的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个RedisObject 和 SDS 都需要重新分配空间,因此 Redis 中的 embstr 实现为只读 。embstr编码方式和raw编码方式在3.0版本之前是以39字节为分界的,也就是说,如果一个字符串值的长度小于等于39字节,则按照embstr进行编码,否则按照raw进行编码。而在3.2版本之后,则变成了44字节为分界。
int 和 embstr 什么时候转化为 raw?
1、int ,当int存储的不再是整数或者超过了int范围(2^63-1),如一开始存整数1,后期有append 字符a.那么存储就会修改为raw.。 还有就是一开始是:embstr 存存储 (只读的),后面append 一个value(需要先修改为raw).那么也会重新分配内存,则类型转为了raw.因此哪怕字符串没超过了44。而且内部的编码转换不会逆转(只能从小的转到大的)
应用场景:缓存,分布式锁,全局ID ,IP 限流等..
2、hash 哈希
hset h1 f 6
hset h1 e 5
hmset h1 a 1 b 2 c 3 d 4
存储类型:包含键值对的无序散列表。value 只能是字符串,不能嵌套其他类型
Hash 不适合的场景:
1、Field 不能单独设置过期时间
2、没有 bit 操作
3、需要考虑数据量分布的问题(value 值非常大的时候,无法分布到多个节点)
存储字符串,Hash 与 String 的主要区别?
1、把所有相关的值聚集到一个 key 中,节省内存空间
2、只使用一个 key,减少 key 冲突
3、当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗
很显然Hash 也是K-V 结构,有点类似于HashMap ,当存储Hash数据类型,底层实现的2种数据结构:
ziplist:OBJ_ENCODING_ZIPLIST( 当键和值长度都小于64个字节时,有压缩列表结构存储。压缩列表->特殊编码的双向链表)
hashtable:OBJ_ENCODING_HT(当键值对数量小于512字节时,哈希表)
3、List 列表
该结构:可以充当队列和栈
存储原理:早期数据量不大的时候,用的是ZipList, 3.2的版本之后即统一用quickLis(存储了一个双向链表,每个节点都是一个ZipList)
一个QuickList 是由多个quicklistNode组成,每个quicklistNode又是由多个ZipList组成
4、Set 集合 (String 类型的无序集合,最大存储数量 2^32-1(40 亿左右))
添加一个或者多个元素 (sadd myset a b c)
存储实现原理:Redis 用 intset 或 hashtable 存储 set。如果元素都是整数类型,就用 intset 存储。
如果不是整数类型,就用 hashtable(数组+链表的存来储结构)
KV 怎么存储 set 的元素?key 就是元素的值,value 为 null。
应用场景:随机获取元素、相互关注,点赞等
5、Zset 集合(有序)
每个元素都有个分数(score),一般当 元素数量小于 128 个或所有 member 的长度都小于 64 字节 用ZipList 存储,内部根据score俩移动进行排序 ,否则就通过跳表(skipList + dict)存储
什么叫跳表(skipList)?
原理:当新增一个值时,会计算该值的level(姑且理解为层数)每一层都是一个链表,通过计算的level ,从哪一层开始比较。
1、如;新增23 ,计算的level 是 2 那么从第二层开始比较
23 与 7 比较 ->大于 则比较19 ->大于比较 26 小于
2、那么下一层,比较22-> 大于,那么在22 与 26 之间
等等还有其他的类型,后续补上... 在这里还是得感谢gupao的老师,帮我理解这些内容。
以上内容如有叙述不当的,还请各位看官大神们多多指点..