内存:掉电易失,数据要做持久化
单机自己持久化/主从复制
redis存储层描述:1.快照/副本(RDB) 2.日志(AOF)
RDB(Redis DB):
时点性:例如每个小时落成一个文件,拍成一个快照。
1.堵塞:redis不对外提供服务。内存把所有的键值对一个个写到磁盘的文件中去,db.file(数据的归属时间点是开始的时刻)。
2.非堵塞:redis进程继续对外提供服务。将数据落地过程中,在写入db.file的过程中,内存还有键值对在做修改,成本高,数据很难把握落点是哪个时点,造成时点混乱。
linux系统当中的知识:
-管道的概念,把前边命令的输出,作为后边命令的输入:
->ls -l /etc 显示etc目录下的内容
->ls -l /etc | more (管道,分批显示,按空格往下翻屏,慢慢看)
->num=0 (linux系统中定义一个变量)
->echo $num(echo打印出变量的值)
->((num++)) (双小括号,可以发生数值计算)
->echo $num (1)
->((num++)) | echo ok ("|"是管道,分隔开左右两边,管道左边没有做加法)
Linux的管道:
1.衔接前一个命令的输出作为后一个命令的输入
2.管道会触发创建子进程(以上命令分别创建了两个进程,)。
3.$$优先级高于管道
->echo $$ (两个$$是取当前进程的id号输出)
->echo $$ | more(值跟上面一样)
->echo $BASHPID(跟两个$,$$同一个功能)
->echo $BASHPID | more(值会变)
linux父子进程,父进程能不能看到子进程?
->echo $$ (114017)
->echo $num(1)
->/bin/bash(启动一个新的进程)
->echo $$(114120)
->pstree (看进程的结构,2个bash,确实是一个子进程)
->echo $num (没有值)
上述步骤说明,常规思想,进程是要做数据隔离的。
->exit(退出子进程,回到父进程)
->pstree
->echo $num(1)
->export num(用export定义一个变量,全局变量是一样的)
->/bin/bash(去到子进程)
->pstree
->echo $num(1取到)
以上说明,父进程是可以让子进程看到数据的(export num)。
->exit
->echo $num(1)
->vi test.sh
#!/bin/bash (启动一个子进程去读下面的指令)
echo $$
echo $num
num=999
echo num:$num
sleep 20
echo $num
->chmod +x test.sh
->ll (test.sh 脚本变绿,代表可以执行)
->./test.sh &(&符,让进程在后台运行)
以上说明,export导出的环境变量,子进程的修改不会影响到父进程,父进程的修改也不会破坏子进程。
创建进程的成本:1.速度 2.内存空间够不够
linux系统调用:fork(),速度快,空间占用小,实现了copy on write(linux的fork系统调用,速度快,内存空间占用少,实现了copy on write)
redis的rdb的底层原理是调用了linux的fork()系统调用,快速、在占用内存空间少的情况下,拷贝了一个线程。
redis里边要调用fork,linux内核就把进程拷贝出来一份,拷贝的不是数据,拷贝的是虚拟地址的映射关系。内存中变量x不需要创建两份,创建的速度非常快。
copy on write是内核机制(写时复制,创建子进程并不发生复制):
优点:创建进程变快了。根据经验,不可能父子进程把所有数据都改一遍。
linux的两个进程,虚拟地址和物理地址都是隔离的,所以父进程在做增删改时,会先在内存开辟一个空间,父进程的变量a指向了这个新的空间;但另一个子进程里,变量a的指针指向的内存还是原来的,基于此(写时复制,copy on write),redis实现快照。
redis解决RDB快照,真正的做法是(fork+copy on right机制):
1.某个时刻点,fork方式,创建子进程。(数据没有真的拷贝出来,只是指向的)
2.如果父进程发生修改,会触发内核级的写时复制(Copy on right),一个新的值先进入物理内存,然后让子进程指向新的地址。
写数据时,是由子进程去写数据。由于增删改发生在父进程,子进程看不到它的变化,父进程数据的变化不会影响子进程在某时刻记录的变量的指针指向的内存里的值。
RDB:
-save触发前台阻塞(比如:要关机维护,数据存全量,关机关服务器)
-bgsave触发后端非阻塞,fork创建子进程
-配置文件中给出bgsave的规则:用save这个标识
->cd /etc/redis/
->vi 6379.conf
save 60 10000(达到60s时操作数大于10000,触发RDB)
save 300 10(达到300s时操作数大于10,触发RDB)
save 900 1(达到900s时操作数大于1,触发RDB)
dnfilename dump.rdb(rdb文件命名)
dir /var/lib/redis/6379(rdb文件存哪)
RDB的弊端:
1.不支持拉链,只有一个dump.rdb
2.丢失数据相对多,时点与时点之间窗口数据容易丢失。比如8点得到一个rdb,9点刚要落一个rdb,挂机了,就丢了1h的数据。
相对的,rdb的优点:二进制,类似java中的序列化(把内存里的字节数组直接搬到磁盘当中去,恢复也直接恢复),恢复的速度相对快。
基于RDB的弊端,发展出了AOF(Append On the File,向文件追加):
-Redis的写操作记录到文件中->丢失数据少
-RDB和AOF可以同时开启->但如果开启了AOF,只会用AOF恢复->redis4.0以后,AOF中包含RDB的全量(这部分二进制,恢复快),增加记录新的写操作。
AOF:里面是记录一条一条的日志,恢复数据时一条条执行,弊端慢,体量无限变大,优点丢失数据少。
场景:redis运行了10年,且开启了AOF。10年头,redis挂了,请问1.AOF多大?;2.恢复要多久?3.恢复会不会溢出?
设计一个方案:让日志、AOF足够小。如果日志、优点能保住,AOF还是可以用的。
hdfs中,fsimage+edits.log(日志的内容写到镜像里),目的:让日志只记录增量,间隔一段时间的日志edits.log定时滚到fsimage里去,有一个合并的过程。
4.0以前如果做的:重写,删除抵消的命令和合并重复的命令。如重写会抵消掉的创建删除key命令,多条重复的记录可以被一条记录代替的命令。最终也是一个纯指令的日志文件。
4.0以后,重写,但是先将老的数据RDB到AOF文件中,讲增量的以指令的方式append到AOF中,AOF是一个混合体,利用了RDB的快,利用了日志的全量。(8点触发的,把8点用rdb的方式写到日志了,8点以后的,用AOF的方式)。在redis5.0的配置文件中,这个功能也是默认开启的,aof-use-rdb-preamble yes
Redis是内存数据库,写操作会触发I/O,就会变慢,有三个级别:
配置文件,Append only mode
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec(默认使用“每秒”的级别)
-No:redis不调用flush,内核的buffer什么时候满,什么时候向磁盘写,可能丢一个buffer大小的内容
-Always:redis增一个,就调用一次flush,就写进内核的buffer,buffer往磁盘写,顶多掉一条数据。
-everysec每秒:redis每秒调一次flush
所有对磁盘对硬件的I/O操作都需要调内核
实操:
->ps -ef | grep redis
->vi /etc/redis/6379.conf
daemonize no(不让它作为后台服务运行,让它在前台阻塞着执行)
#logfile /var/log/redis_6379.log(把日志文件注释掉,让日志打到屏幕上)
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
aof-use-rdb-preamble no(先把混合关闭)
日志写在 /var/lib/redis/6379目录下,
->cd /var/lib/redis/6379
->rm -rf ./*(先清掉所有,等于redis启动的时候不能加载到dump,rdb也加载不到aof文件)
->redis-server /etc/redis/6379.conf
->vi appendonly.aof(是空的)
copy一个screen,
->redis-cli
->set k1 hello
->vi appendonly.aof
(appendonly.aof新增以下信息)
*2(*下面有几个元素组成:SELECT和0)
$6($描述元素有几个字符组成)
SELECT
$1
0
*3
$3
set
$2
k1
$5
hello
->bgsave(后台background生成dump.rdb)
->vi dump.rdb
"REDIS"开头,存了二进制序列化的内容
->redis-check-rdb dump.rdb
->set k1 a
->set k1 b
->set k1 c
->vi appendonly.aof(最终内存存的是c,等于日志增加了很多无用的命令)
->BGREWRITEAOF(重写,减少aof的体积,把没用的剔除掉,方便未来加载的速度变快,不需要执行无意义的指令)
->vi appendonly.aof
----------------------------------------------------------------------
->(ctrl+c,退出服务端)
->exit(退出客户端)
->rm -rf ./*(删掉所有的rdb,aof文件)
->vi /etc/redis/6379.conf
aof-use-rdb-preamble yes(开启混合,触发重写之后才会变成混合体,aof文件会多一个“REDIS”标识,还有二进制)
->redis-server /etc/redis/6379.conf
->vi appendonly.aof(空)
->redis-cli
->set k1 a
->set k1 b
->set k1 c
->set k1 d
->BGREWRITEAOF(重写)
->vi appendonly.aof(“REDIS”开头,有一段rdb的存储)
->set k1 w(增量)
->set k1 m(增量)
->vi appendonly.aof(上面两条记录会以明文的方式,追加到下面去)
以上方式为:增量日志+全量时点数据,这种方式恢复速度快,
->bgsave(后台触发rdb,产生dump.rdb文件)
->bgrewriteaof(又做了一次重写,就把所有增量合成一个时点)
->vi dump.rdb
->vi appendonly.aof(与dump.rdb是一个东西)
->set k1 a
->set k1 asdas
->set k1 sdfsdf
->vi appendonly.aof
->flushall(清空内存)
->keys *
->vi appendonly.aof(文件最后日志也有一个flushall)
->bgrewriteaof(触发,就等于appendonly.aof文件是空的)
可以手动把appendonly.aof最后的flushall删除掉,让未来服务器重启时,数据不要丢
aof重写自动执行,配置文件/etc/redis/6379.conf里, 两参数:
auto-aof-rewrite-percentage 100(达到100%触发重写)
auto-aof-rewrite-min-size 64mb(达到64兆触发重写)