redis
文章目录
1. 概述
什么是redis?
- Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助
- 它支持存储的 value 类型相对更多,包括 String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合) 和 Hash(哈希),而且这些操作都是原子性的。在此基础上,Redis 支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。Redis 可以周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步
- Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上
redis有什么优点?
- 速度快,因为数据存在内存中,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB
- 支持丰富数据类型,支持 string,list,set,sorted set,hash等
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
redis的回收策略:
- olatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
为什么 Redis 是单线程的?
Redis 的数据存储在内存中,如果数据全都在内存里,单线程的去操作就是效率最高的。
为什么呢?因为多线程的本质就是 cpu 模拟出来多个线程的情况,这种模拟出来的情况就有一个代价,就是上下文的切换,对于一个内存的系统来说,它没有上下文的切换就是效率最高的。因为上下文切换所花费的时间远大于直接从内存中读取数据所花费的时间。
Redis 用单个 cpu 绑定一块内存的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个cpu上完成的,所以它是单线程处理这个事。在内存的情况下,这个方案就是最佳方案。
这里我们一直在强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的 Redis Server 运行的时候肯定是不止一个线程的,例如 Redis 进行持久化的时候会以子进程或者子线程的方式执行。
2. 安装redis
port 6379 #端口可修改为非常用端口
daemonize yes #配置为守护进程方式 后台启动, 默认为 no
logfile "redis-6379.log" #日志文件名称,添加端口号方便标识
dbfilename dump-6379.rdb #rdb文件名,添加端口号方便标识
dir /data/redis/ #数据文件目录
requirepass 123456 #配置连接密码, 默认无密码
pidfile /var/run/redis-6379.pid #指定pid文件,默认文件无端口号,不好区分
appendonly yes #打开aof
# 注释 去除默认的rdb, 防止大量写入时性能消耗
# save 3600 1
# save 300 100
# save 60 10000
官网地址 https://redis.io 根据实际情况下载当前最稳定的版本
//下载依赖包
[root@redis ~]# ls
anaconda-ks.cfg redis-6.2.4.tar.gz
[root@redis ~]# yum -y install gcc gcc-c++ make
//解压包
[root@redis ~]# tar xf redis-6.2.4.tar.gz
[root@redis ~]# ls
anaconda-ks.cfg redis-6.2.4 redis-6.2.4.tar.gz
//make进行编译
[root@redis ~]# cd redis-6.2.4
[root@redis redis-6.2.4]# ls
00-RELEASENOTES CONTRIBUTING INSTALL README.md runtest-cluster sentinel.conf TLS.md
BUGS copYING Makefile redis.conf runtest-moduleapi src utils
CONDUCT deps MANIFESTO runtest runtest-sentinel tests
[root@redis redis-6.2.4]# make
Hint: It's a good idea to run 'make test' ;)
make[1]: Leaving directory '/root/redis-6.2.4/src' //看到这个代表安装成功
//配置redis
[root@redis redis-6.2.4]# cd src/
[root@redis src]# cp redis-cli
redis-cli redis-cli.c redis-cli.d redis-cli.o
[root@redis src]# cp redis-cli
redis-cli redis-cli.c redis-cli.d redis-cli.o
[root@redis src]# cp redis-cli redis-server /usr/bin/
[root@redis src]# which redis-cli
/usr/bin/redis-cli
[root@redis src]# cd ..
[root@redis redis-6.2.4]# cp redis.conf /etc/
//设置密码
[root@redis ~]# vi /etc/redis.conf
requirepass linux123 //将此行取消注释,并在requirepass后面输入你要设置的密码
daemonize yes #配置为守护进程方式 后台启动
//启动redis
[root@redis ~]# nohup redis-server /etc/redis.conf &
[root@redis system]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 127.0.0.1:6379 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::1]:6379 [::]:*
LISTEN 0 128 [::]:22 [::]:*
//配置开机自动启动
[root@redis ~]# cd /usr/lib/systemd/system
[root@redis system]# vi redis.service
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/redis-server /etc/redis.conf
ExecStop=/usr/bin/redis-server /etc/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
[root@apache system]# systemctl enable --Now redis.service
Created symlink /etc/systemd/system/multi-user.target.wants/redis.service → /usr/lib/systemd/system/redis.service.
[root@apache system]# systemctl status redis.service
● redis.service - redis-server
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2022-08-24 19:24:30 CST; 28s ago
Main PID: 14878 (redis-server)
Tasks: 5 (limit: 4744)
Memory: 8.7M
CGroup: /system.slice/redis.service
└─14878 /usr/bin/redis-server 0.0.0.0:6379
Aug 24 19:24:30 apache systemd[1]: Starting redis-server...
Aug 24 19:24:30 apache systemd[1]: Started redis-server.
[root@redis ~]# ss -antl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 127.0.0.1:6379 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::1]:6379 [::]:*
LISTEN 0 128 [::]:22 [::]:*
//登录
[root@redis ~]# redis-cli
127.0.0.1:6379>
127.0.0.1:6379> set a 10
(error) NOAUTH Authentication required. //没登录发现不能设置value
127.0.0.1:6379> auth linux123
OK
127.0.0.1:6379> set a 10
OK
127.0.0.1:6379> get a
"10"
127.0.0.1:6379>
3. redis数据类型
3.1 String
String 数据结构是简单的 key-value 类型,value 其实不仅可以是 String,也可以是数字。需要注意是一个键值最大存储 512MB
127.0.0.1:6379> set myname test
OK
127.0.0.1:6379> get myname
"test"
127.0.0.1:6379> set myname 123
OK
127.0.0.1:6379> get myname
"123"
3.2 Hash
Hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 比如我们可以 Hash 数据结构来存储用户信息,商品信息等等
127.0.0.1:6379> hmset info name "Jane" age 20 title "Senior"
OK
127.0.0.1:6379> hget info name
"Jane"
127.0.0.1:6379> hget info age
"20"
127.0.0.1:6379> hget info title
"Senior"
3.3 List
是 Redis 的简单的字符串列表。list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销
127.0.0.1:6379> LPUSH mylist aa
(integer) 1
127.0.0.1:6379> LPUSH mylist b
(integer) 2
127.0.0.1:6379> LPUSH mylist cc
(integer) 3
127.0.0.1:6379> LPUSH mylist cc
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 3
1) "cc"
2) "cc"
3) "b"
4) "aa"
3.4 Set
Set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 不允许重复元素,可以自动排重的,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的
127.0.0.1:6379> SADD myset 111
(integer) 1
127.0.0.1:6379> SADD myset 111
(integer) 0
127.0.0.1:6379> SADD myset 222
(integer) 1
127.0.0.1:6379> SADD myset 333
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "111"
2) "222"
3) "333"
3.5 Sorted Set
与 set 相比,Sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列
127.0.0.1:6379> ZADD ss 2 a
(integer) 1
127.0.0.1:6379> ZADD ss 3 b
(integer) 1
127.0.0.1:6379> ZADD ss 1 c
(integer) 1
127.0.0.1:6379> zrangebyscore ss 0 3
1) "c"
2) "a"
3) "b"
4. redis数据持久化存储
Redis 是基于内存的数据库,一旦进程退出,数据就会丢失,所以我们需要它也可以把数据写到磁盘上,当 Redis 重启后,可以从磁盘中恢复数据
Redis 提供了两种解决方案将内存中的数据保存到磁盘上。一种是 RDB 持久化,原理是将 Reids 在内存中的全部数据库记录定时 dump 到磁盘上的 RDB 持久化;另外一种是 AOF 持久化,原理是将 Reids 的操作日志以追加的方式写入文件
4.1 RDB(Redis DataBase)快照
RDB 是 Redis 用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照所有数据。恢复时是将快照文件直接读到内存里。
** 快照的生成方式:**
- 使用BGSAVE或者SAVE命令。
- 服务端配置自动触发
- 接收到shutdown指令时进行save
BGSAVE命令:当接收到客户端的BGSAVE命令的时候,redis会调用fork来创建一个子进程,然后子进程负责将快照写入磁盘,而父进程则继续处理命令。
fork命令:当一个进程创建子进程的时候,底层的操作系统会创建这个进程的一个副本,在类unix系统中创建子进程的操作会进行优化:在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了写之后,对被写入的内存的共享才会结束服务
save命令:使用save命令创建快照,redis服务器在快照创建完毕之前将不再响应任何其他的命令,即阻塞状态
例:
[root@node2 ~]# vim /etc/redis.conf
...
save 3600 1 //3600秒内只要有一个键被改变则写入磁盘
save 300 100 //300秒内有100个键被改变则写入磁盘
save 60 10000 //60秒内有一万个键被改变则写入磁盘
//服务器接收到shutdown指令:当redis接收到关闭服务器的请求时,会执行一个save命令,
阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器
...
dbfilename dump.rdb //指定rdb的数据文件
dir ./ //指定rdb的数据路径
RDB 存储的劣势:
-
RDB 方式数据没办法做到实时持久化,该方式是每间隔一段时间做一次备份。因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。所以如果 Redis 意外挂掉,就会丢失最后一次快照后的所有修改;
4.2 AOF(Append Only File)只追加文件
AOF 持久化:将所有客户端执行的写命令记录到日志文件中,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行一次AOF文件所包含的所有写命令,就可以恢复AOF文件的记录的数据集。
例:
[root@node02 ~]# vim /etc/redis.conf
appendonly yes //开启持久化
appendfilename "appendonly.aof" //指定生成文件名称
AOF的缺点:
AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。