什么是redis
redis是远程字典服务(Remote Dictionary Server )的简写,是一个完全开源的高性能的Key-Value数据库,提供了丰富的数据结构如string、Hash、List、SetSortedset等等。数据是存在内存中的,同时Redis支持事务、持久化、LUA脚本、发布订阅、缓存淘汰、流技术等特性,提供了主从模式、Redis Sentinel和Redis cluster集群架构方案。
安装redis
yum方式安装
sudo yum install redis
sudo systemctl enable redis
sudo systemctl start redis
yum方式版本比较落后,无法安装最新版的redis,如果需要安装最新版本的redis需要使用源码安装的方式
源码方式安装
# 检查环境依赖
yum install gcc -y
# 下载Redis源码,一般下载到/opt目录下,/opt/software
wget http://download.redis.io/releases/redis-7.0.2.tar.gz
# 解压缩到当前目录(/opt/module)
tar zxvf /opt/software/redis-7.0.2.tar.gz
# 编译并安装 redis (默认安装在/usr/local/bin目录下)
make && make install
# 启动redis
redis-server
安装目录(/usr/local/bin)中的文件结构
配置redis.conf文件
# 进入redis解压目录
cd redis-7.0.2
# 备份redis.conf文件
cp redis.conf redis.conf.bak
# 将redis改为后台执行,替换daemonize no为daemonize yes
sed -i 's/^daemonize no/daemonize yes/' "redis.conf"
# 将redis改为可在外网访问,替换bind 127.0.0.1 -::1为bind 0.0.0.0
sed -i 's/^bind 127.0.0.1 -::1/bind 0.0.0.0/' "redis.conf"
# 配置redis密码,这里使用root作为密码,替换requirepass foobared为requirepass root
sed -i 's/^# requirepass foobared/requirepass root/' "redis.conf"
# 按照配置文件要求启动redis服务
redis-server redis.cnof
redis数据类型
数据类型 | 解释 | 作用 |
---|---|---|
String | 字符串类型 | 二进制安全的数据类型,可以用来存储任何类型的数据 |
List | 列表类型 | Redis列表是简单的字符串列表,按照插入顺序排序 |
Hash | 哈希表 | 存储键值对,使用存储对象 |
Set | 集合类型 | String类型的无序集合,集合中不能存在重复的数据。底层通过hash来实现 |
ZSet | 有序集合 | String类型的有序集合,集合中不能存在重复的数据。每个元素关联一个double类型的分数,底层通过hash来实现。 |
GEO | 地理空间 | 地理位置,坐标、距离等 |
HyperLogLog | 基数统计 | 在输入元素数量或体积非常大时,计算基数所需时间和空间总是固定的 |
Bitmap | 位图 | 二进制的bit数组 |
Bitfield | 位域 | 连续的比特位 |
Stream | 流 | 用于消息队列(一般不是用) |
String类型底层原理
String类型是redis最基本的数据类型,
SDS 简单动态字符串,SDS有的三个属性int len、int free、char buf[]。
len保存了字符串的长度
free表示buf数组中未使用的字节数量
buf数组则是保存字符串的每一个字符元素
Redis与C原生字符串相比:
1、C原生字符串不会记录长度,每次获取字符串长度时间复杂度都为O(n),Redis读取len中的数值即可,时间复杂度为O(1)。
2、C原生字符串拼接,如果分配空间不够会造成缓存区溢出的情况,SDS会现根据len中的数值判断空间是否足够,如果不够会进行相应的空间扩展,所以不会出现缓存区溢出的情况。
3、SDS提供空间预分配和惰性空间释放两种策略,在给字符串分配空间时,分配的空间比字符串实际所需空间大很多,这样能减少字符串增长带来的内存重新分配次数。当字符串被缩短时,SDS不会立即收回空间,而是通过free属性将空余的空间记录下来,等后面需要使用的时候再释放这些空间。
空间分配原则:当修改字符串后的长度len小于1MB,就会预分配和len一样长度的空间,即len=free;若是len大于1MB,free分配的空间大小就为1MB。
举个例子:
如果进行修改之后, SDS 的 len 将变成 13 字节, 那么程序也会分配 13 字节的未使用空间, SDS 的 buf 数组的实际长度将变成 13 + 13 + 1 = 27 字节(额外的一字节用于保存空字符)。
如果进行修改之后, SDS 的 len 将变成 30 MB , 那么程序会分配 1 MB 的未使用空间, SDS 的 buf 数组的实际长度将为 30 MB + 1 MB + 1 byte 。
List类型底层源码分析
redis3,2版本后,List类型通过quickList作为默认底层实现,quickList本质上是一个双端链表,可以认为是linkedList和ZipList的结合体。
LinkedList
LinkedList是一个普通的链表,LinkedList每一个节点的空间都是不连续的,所以可能造成过多的空间碎片。
ziplist
ziplist是为了节省内存而开发的一种压缩列表数据结构
zlbytes: 4字节,记录 ziplist 整个结构体的占用空间大小。用于内存重分配或计算zlend位置。
zltail: 4字节, 记录整个 ziplist 中最后一个 entry 的偏移量。用于快速定位到尾节点。
zllen: 4字节, 记录 entry 的数量,该值被固定为 2^16 - 1。 大于这个值就必须要遍历整个结构了。
entry: 真正存数据的结构。
zlend: 1字节, 为 ziplist 的结束标识。
entry节点结构
prelen:记录了前一个节点的长度。通过这个值,可以进行指针计算,从而跳转到上一个节点。如果前一节点的长度小于254字节,则prelen属性需要用1字节长的空间来保存;如果前一节点的长度大于等于254字节,则需要用5字节长的空间来保存。
encoding:记录了节点的content属性所保存数据的类型以及长度。编码的最高位用于标识数据类型(整数或字节数组),其余位用于表示内容的长度。
entry-data:保存节点的值
quicklist
quicklist存储了一个双向列表,每个列表的节点是一个ziplist
quicklist同样采用了linkedlist的双端列表特性,然后quicklist中的每个节点又是一个ziplist,所以quicklist就是综合平衡考虑了空间碎片和读写性能两个维度。使用quicklist需要注意以下2点:
1、如果ziplist中的entry个数过少,极端情况就是只有1个entry,此时就相当于退化成了一个普通的linkedlist。
2、如果ziplist中的entry过多,那么也会导致一次性需要申请的内存空间过大,而且因为ziplist本身的就是以时间换空间,所以会过多entry也会影响到列表对象的读写性能。
ziplist中的entry个数可以通过参数list-max-ziplist-size来控制
Redis持久化
redis为什么需要持久化?
Redis持久化的意义就是为了保证突然宕机,内存数据不会全部丢失。
RDB机制
RDB持久化是周期性的把redis当前内存中的全量数据写入到一个快照文件中
快照文件是一个二进制文件,包含了 Redis 在某个时间点内的所有数据。
在redis6.0之前,RDB的频率为15分钟1个变化、5分钟10个变化,1分钟1万个变化,
在redis6.2之后,RDB的频率为1小时1个变化,5分钟100个变化,1分钟1万个变化。
自动触发
再配置文件redis.conf中修改以下配置:
修改快照时间
save 5 2
修改文件保存路径
dir 文件路径
修改文件名称
dbfilename 文件名称.rdb
进入redis检查配置文件是否成功
config get save
config get dir
config get dbfilename
RDB会读取dump.rdb文件进行恢复,所以服务要与备份分机隔离。不可以把备份文件dump.rdb与生茶redis服务器放在同一台机器上,必须分开各自存储,以防止生产机物理损坏后备份文件也跟着挂了。
手动触发
手动执行有两个命令:save和bgsave
save:阻塞主进程,直到生成新的RDB文件;执行save命令期间,Redis不能处理其他命令。在生产中严禁使用该命令。
bgsave:异步生成RDB文件,fork子进程去生成新的RDB文件,主进程不阻塞。