1、Redis安装
首先下载获得redis-3.0.4.tar.gz后将它放入我们的Linux目录/opt,然后执行解压命令
tar -zxvf redis-3.0.4.tar.gz,解压完成后出现文件夹:redis-3.0.4
进入目录:cd redis-3.0.4,在redis-3.0.4目录下执行make命令
[root@localhost redis-3.0.4]# make
错误解析:需要安装C的编译器:
安装yum -y install gcc-c++
yum -y install gcc-c++
错误解析网络问题:
解决方法:
1、vim /etc/resolv.conf 添加如下/
nameserver 192.168.128.2
nameserver 8.8.8.8
2、关闭linux关闭防火墙
systemctl stop firewalld.service
再次make
[root@localhost redis-3.0.4]# make
错误提示:jemalloc/jemalloc.h:没有那个文件或目录
解决办法
make distclean
再次make
[root@localhost redis-3.0.4]# make
[root@localhost redis-3.0.4]# make install 安装
2、查看默认安装目录
源码包安装的软件。一般都在usr/local/bin下。redis采用源码包安装,进入usr/local/bin下
- redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何,服务启动起来后执行
- redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
- redis-check-dump:修复有问题的dump.rdb文件
- redis-cli:客户端,操作入口
- redis-sentinel:redis集群使用
- redis-server:Redis服务器启动命令
3、redis启动
1、修改redis.conf文件将里面的daemonize no 改成 yes,让服务在后台启动,将默认的redis.conf拷贝到自己定义好的一个路径下,比 如/myconf
2、启动Redis:/usr/local/bin目录下运行redis-server,运行拷贝出存放了自定义conf文件目录下的redis.conf文件
3、开启服务 redis-server redis.conf
4、开启客户端: redis-cli -p 6379
5、关闭redis的服务
shutdonw
exit
6、查看redis进程
ps –ef |grep redis
注:单实例关闭:redis-cli shutdown ,多实例关闭,指定端口关闭:redis-cli -p 6379 shutdown
4、Redis启动后杂项基础讲解
- 默认数据库的数量
默认16个数据库,类似数组下表从零开始,初始默认使用零号库,设置数据库的数量,默认数据库为0,可以使用SELECT <命令在连接上指定数据库id databases 16)
- dbsize查看当前数据库的key的数量,直接输入keys * 也可以查看当前数据库key的数量
- flushdb:清空当前库
- Flushall;通杀全部库
5、Redis数据类型
Redis的五大数据类型
Redis字符串(String) 、Redis列表(List)、Redis集合(Set)、Redis哈希(Hash)、Redis有序集合Zset(sorted set)
1、string(字符串)
String(字符串)string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
- 常见基本指令 set/get/del/append/strlen
127.0.0.1:6379[1]> set username zhangsan
127.0.0.1:6379[1]> get username
127.0.0.1:6379[1]> append username feng
127.0.0.1:6379[1]> get username
127.0.0.1:6379[1]> strlen username
- 数字指令 Incr/decr/incrby/decrby指令操作:表示key对应的值是数字才能进行加减
127.0.0.1:6379[1]> set k1 2
127.0.0.1:6379[1]> incr k1
127.0.0.1:6379[1]> incrby k1 3
127.0.0.1:6379[1]> decrby k1 2
- 范围指令 getrange/setrange
getrange:获取指定区间范围内的值,类似between......and的关系从零到负一表示全部setrange设置指定区间范围内的值
setrange:修改key对应的值,从值得开始下标开始,连续几个字符被替换
127.0.0.1:6379[1]> set username zhangsanfeng
127.0.0.1:6379[1]> getrange username 0 -1
127.0.0.1:6379[1]> getrange username 0 3
127.0.0.1:6379[1]> setrange username 0 xxx 有三个字符被替换
- key过期指令 setex(set with expire)键秒值/setnx(set if not exist)
setex:设置带过期时间的key,动态设置。语法:setex 键 秒值 真实值
setnx:只有在 key 不存在时设置 key 的值。如果某个key存在则无效
127.0.0.1:6379[1]> set k1 10 v4 zhangsanfeng
127.0.0.1:6379[1]> ttl k1
127.0.0.1:6379[1]> setnx k1 v11
127.0.0.1:6379[1]> get k1
- 设置多个key-value指令mset/mget/msetnx (m:more)
mset:同时设置一个或多个 key-value 对,mget:获取所有(一个或多个)给定 key 的值。
msetnx:同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
127.0.0.1:6379[1]> mset k1 v1 k2 v2 k3 v3
127.0.0.1:6379[1]> mget k1 k2 k3
127.0.0.1:6379[1]> msetnx k1 v11 k2 v22 k3 v33 无效,因为k1 \ k2 \k3都存在
2、hash(哈希)
Hash(哈希)Redis hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>
在hash类型中,也是一个键值对,key不变 v又是一个键值对
- hset/hget/hmset/hmget/hgetall/hdel/hlen
hset : 写入元素到集合 hget:获取集合key的值
127.0.0.1:6379> hset user id 1 user是key value 是 id 1 value是一个键值对
127.0.0.1:6379> hget user id
hmset:批量给value写入值 hmget:批量获取key中的值
127.0.0.1:6379> hmset user id 1 name zhangsan
OK
127.0.0.1:6379> HMGET user id name
1) "1"
2) "zhangsan"
hgetall获取某个key的所有value
127.0.0.1:6379> HGETALL user
hdel:删除hash中某个键值对
127.0.0.1:6379> HDEL user id
127.0.0.1:6379> HGETALL user
hlen:获取hashsize
127.0.0.1:6379> HLEN user
- hexists 判断某个key 对应的value中的 某个key是否存在
127.0.0.1:6379> HEXISTS user name 判断key是user 的值中 是否含有name的键
- Hkeys 命令用于获取哈希表中的所有域(field)。
- hvals命令 获取哈希表中所有值。
127.0.0.1:6379> HKEYS user
1) "name"
127.0.0.1:6379> HVALS user
1) "zhangsan"
- hincrby/hincrbyfloat
hincrby:为哈希表 key 中的指定字段的整数值加上增量 increment。
127.0.0.1:6379> HSET user age 2
(integer) 1
127.0.0.1:6379> HINCRBY user age 5
hincrbyfloat: 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
127.0.0.1:6379> HINCRBYFLOAT user age 0.5
- hsetnx 只有在字段 field 不存在时,设置哈希表字段的值。
127.0.0.1:6379> HSETNX user age 20 不成功 因为age存在了
3、list(列表)
List(列表)Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。它的底层实际是个链表
- lpush/rpush/lrange/Llen 命令
lpush向左添加元素,rpush向右添加元素,lrange:显示集合元素,Llen 获取集合的长度
127.0.0.1:6379> lpush list01 0 1 2 3 4 5
127.0.0.1:6379> LRANGE list01 0 -1
127.0.0.1:6379> rpush list01 0 1 2 3 4 5
127.0.0.1:6379> LRANGE list01 0 -1
- lpop/rpop
lpop从 list 的头部删除元素,并返回删除元素,rpop从list 的尾部删除元素,并返回删除元素
127.0.0.1:6379> lpop list01
127.0.0.1:6379> rpop list01
- lindex,按照根据下标找出对应的元素,下标从0开始
127.0.0.1:6379> lindex list01 3 #3表示集合里面的某个元素
- lrem 删除元素 语法: lrem key n value 根据参数n(个数) 的值,移除列表中与参数
value相等的元素。
127.0.0.1:6379> lpush list01 0 1 2 3 3 3 3
127.0.0.1:6379> lrem list01 2 3 #删除集合里面2个3
- Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
Ltrim 命令基本语法: LTRIM KEY_NAME START STOP
127.0.0.1:6379> LPUSH list02 0 1 2 3 4 5 6 7 8 9
(integer) 10
127.0.0.1:6379> LTRIM list02 3 5
OK
127.0.0.1:6379> lrange list02 0 -1
结果
1) "6"
2) "5"
3) "4"
- rpoplpush Rpoplpush 命令用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回。
语法:rpoplpush 源列表 目的列表
127.0.0.1:6379> lrange list02 0 -1
1) "6"
2) "5"
3) "4"
127.0.0.1:6379> LRANGE list01 0 -1
(empty list or set)
127.0.0.1:6379> RPOPLPUSH list02 list01
"4"
- Lset命令: 通过索引来设置元素的值。基本语法:LSET KEY_NAME INDEX VALUE
127.0.0.1:6379> LRANGE list02 0 -1
1) "6"
2) "5"
127.0.0.1:6379> LSET list02 1 x #下标1的元素是5 用x 替换
127.0.0.1:6379> LRANGE list02 0 -1
1) "6"
2) "x"
- Linsert 命令用于在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作。
语法: LINSERT key BEFORE|AFTER pivot value
127.0.0.1:6379> LPUSH list01 1 2 3 4 5
127.0.0.1:6379> LINSERT list01 before 2 xxx #在元素2的前面插入xxx
List性能总结
它是一个字符串链表,left、right都可以插入添加;如果键不存在,创建新的链表; 如果键已存在,新增内容;如果值全移除,对应的键也就消失了。链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
4、set(集合)
Set(集合)Redis的Set是string类型的无序集合。它是通过HashTable实现实现的
- sadd/smembers/sismember
语法: sadd 集合名 数据, smembers:显示集合内容,sismember 判断集合中是否有某个元素
127.0.0.1:6379> SADD set01 1 1 2 2 3 4
127.0.0.1:6379> SMEMBERS set01
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> SMEMBERS set01 1 返回1 表示集合中有某个元素
127.0.0.1:6379> SMEMBERS set01 5 返回0 集合中没有5元素
127.0.0.1:6379> sismember set01 2 判断集合里面是否有某个元素
- scard 获取集合里面的元素的个数
127.0.0.1:6379> SCARD set01
- srem :删除集合中的某个元素
语法: srem key value(某个元素)
127.0.0.1:6379> SCARD set01 4 删除集中中为4的元素
-
Srandmember 命令用于返回集合中的一个随机元素。从 Redis 2.6 版本开始, Srandmember 命令接受可选的 count 参数:
- 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
- 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值
127.0.0.1:6379> SADD set02 1 2 3 4 5 6 7 8 9
(integer) 9
127.0.0.1:6379> SRANDMEMBER set02 3
1) "7"
2) "3"
3) "2"
- SPOP 命令将随机元素从集合中移除并返回,注意: Srandmember 则仅仅返回随机元素,而不对集合进行任何改动
127.0.0.1:6379> SPOP set02 随机删除一个元素
- Smove 命令将指定成员 member 元素从 source 集合移动到 destination 集合
语法:SMOVE SOURCE DESTINATION MEMBER
127.0.0.1:6379> SMOVE set02 set01 9 把集合set02中的元素9移动到set01集合中
5、zset(sorted set:有序集合)
zset(sorted set:有序集合) Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
- ZADD key score1 member1 score2 member2 向有序集合添加一个或多个成员,或者更新已存在成员的分数
127.0.0.1:6379> ZADD set03 60 v1 70 v2 80 v3
- ZRANGE key start stop withscores 通过索引区间返回有序集合成指定区间内的成员
127.0.0.1:6379> ZRANGE set03 0 -1
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> ZRANGE set03 0 -1 withscores
1) "v1"
2) "60"
3) "v2"
4) "70"
5) "v3"
6) "80"
- ZREM key member [member ...] 移除有序集合中的一个或多个成员
127.0.0.1:6379> ZREM set03 v3
(integer) 1
127.0.0.1:6379> ZRANGE set03 0 -1 withscores
1) "v1"
2) "60"
3) "v2"
4) "70"
- ZSCORE key member 返回有序集合中某个成员的分数值
127.0.0.1:6379> ZSCORE set03 v2
结果:"70"
- ZCOUNT key min max 计算在有序集合中指定区间分数的成员个数
127.0.0.1:6379> ZCOUNT set03 60 70
(integer) 2
- ZRANK key member 返回有序集合中指定成员的索引
127.0.0.1:6379> ZRANK set03 v1
索引为(integer) 0
127.0.0.1:6379> ZRANK set03 v2
索引为 (integer) 1
- ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底
127.0.0.1:6379> ZREVRANGE set03 0 -1
1) "v2"
2) "v1"
- ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序
127.0.0.1:6379> zrevrangebyscore set03 90 60
1) "v2"
2) "v1"
6、解析配置文件 redis.conf
1、Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程daemonize no
2、redis的计量单位 units单位配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,对大小写不敏感
3、includes 和我们的Struts2配置文件类似 包含其他的子模块,struts配置文件的拆分s,可以通过includes包含,redis.conf可以作为总闸,包含其他redis配置文件
4、当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定 pidfile /var/run/redis.pid
5、指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字 port 6379
6、当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 timeout 300
7、绑定的主机地址 bind 127.0.0.1
8、tcp-keepalive 单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60,意思是60秒没有对redis做任何操作,则断开
9、loglevel verbose 日志级别, Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
10、logfile stdout 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
11、databases 16 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
12 SNAPSHOTTING快照
save 秒钟 写操作次数,RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
13、rdbcompression yes 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
14、 rdbchecksum yes 在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
15、dbfilename dump.rdb 指定本地数据库文件名,默认值为dump.rdb
16、dir ./ 指定本地数据库存放目录
17、 SECURITY安全 可以给redis登陆设置密码
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"
127.0.0.1:6379> config set requirepass "123"
OK
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> ping
PONG
18、LIMITS限制
maxclients 128 设置redis同时可以与多少个客户端进行连接。默认情况下为10000个客户端。当你无法设置进程文件句柄限制时,redis会设置为当前的文件句柄限制值减去32,因为redis会为自身内部处理逻辑留一些句柄出来。如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。
maxmemory 设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明你的redis有从redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素
maxmemory-policy 过期策略
(1) volatile-lru:使用LRU算法移除key,只对设置了过期时间的键
(2) allkeys-lru:使用LRU算法移除
(3) volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
(4) allkeys-random:移除随机的
(5)volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
(6)noeviction:不进行移除。针对写操作,只是返回错误信息
lru :least recently user缓存过期清洁策略
maxmemory-samples 设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小, redis默认会检查这么多个key并选择其中LRU的那个
19 、appendonly no 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
20、appendfilename appendonly.aof 指定更新日志文件名,默认为appendonly.aof
21、appendfsync everysec 指定更新日志条件,共有3个可选值
no:表示等操作系统进行数据缓存同步到磁盘(快) always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示每秒同步一次(折衷,默认值)
22、include /path/to/local.conf 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
7、redis持久化
1、RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
注:fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,
在每次redis服务器启动的时候,会自动把dump.rdb这个文件的键值对 全部读取到内存
rdb 保存的是dump.rdb文件,配置如下
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
测试数据 2分钟以内,更变key 10次或者以上 就会自动生成dump.rdb
save 900 1
save 120 10
save 60 10000
测试方法:可以先删除dump.rdb, 然后在登陆redis客户端。2分钟内改变10次,如添加10个key,看看是否生成dump.rdb文件
rm -rf dump.rdb
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK
127.0.0.1:6379> set k6 v6
OK
127.0.0.1:6379> set k7 v7
OK
127.0.0.1:6379> set k8 v8
OK
127.0.0.1:6379> set k9 v9
OK
127.0.0.1:6379> set k10 v10
OK
退出。查看bin下的redis文件:发现自动生成,证明dump.rdb生效
思考1:在2分钟内敲完10个以上的key后,接着执行flushall 会发生什么情况呢?
思考2:在2分钟内敲完10个以上的key后,执行 shutdown 关机?
瞬间清除内存的所有key,相当于改变了所有的key,那么文件也会保存(更新),保存的是一个空的dump.rdb,此时的dump.rdb是一个空文件了,这种操作是失误性的操作,可以杜绝,可以先备份一个dump.rdb文件。名字叫dump.rdb.bak。如果原dump.rdb文件出了问题,删除即可,然后备份文件名改成dump.rdb即可
小总结
#### 优势
适合大规模的数据恢复,对数据完整性和一致性要求不高
#### 劣势
在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性
注意:在测试的时候,如果在规定的时间没有写符合条件的n个key,这个时候执行shutdown,也会保存,因为执行shutdown指令是会先把数据保存到本地的,shutdown 默认采取 shutdown save模式,也可以手动指定 shutdown nosave不保存。如果是意外退出不会,什么是意外退出呢?如杀掉redis服务器的进程,或者是ctrl+c直接宕掉redis服务器
2、AOF(Append Only File)
1、思想:内存每写一条,就备份一条,时间间隔是1秒钟,缺点:文件大,写操作频繁。
- 以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),
- 只许追加文件但不可以改写文件,redis启动之初会读取该文件(aof文件)重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
- aof保存的是appendonly.aof文件
2、AOF启动、修复、恢复
1)、配置修改:备份原先redis.conf,名字为redis-aop.conf ,修改redis-aop.conf 把appendonly no,改为yes
2)、启动redis服务,发现自动生成appendonly.aof
3)、启动redis客户端
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> flushall
说明:这个时候 dump.rdb文件是没有这些k和value的 因为flushall了。为了进一步测试aof,可以删除dump.rdb文件
rm -rf dump.rdb
然后再次登陆redis客户端
127.0.0.1:6379> keys *
思考1:这个时候有没有keys的内容呢?
这里可以发现keys 仍然没有,难道是aof没有写吗?不是的,这个文件可以打开看看,里面有k和v 但是最后又flushall。那么在每次加载的时候,flushall执行的是删除,所以key和value被删除了。我们可以打开appendonly.aof这个文件,删除flushall这行,然后执行, 然后再启动redis服务和客户端看看
127.0.0.1:6379> keys *
思考2:下面我们在aof文件里面随便乱写。然后重启redis服务看看,是否能够启动
[root@localhost bin]# ./redis-cli -p 6379 发现根本就不能启动redis服务
因此redis首先加载的是aof,aof和rdb可以同时存在,服务启动先加载aof,rdb之前我们修改是2分钟改key 10次,aof默认的规则是每一秒钟往aof文件写一次操作的指令。
解决此问题。需要修复aof文件
[root@localhost bin]# ./redis-check-aof --fix append.aof
再次启动
[root@localhost bin]# ./redis-cli -p 6379
- aof原理rewrite
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
- aof优势和劣势
1、aof持久化的策略(优势)
每修改同步:appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好
每秒同步:appendfsync everysec 异步操作,每秒记录 ,如果一秒内宕机,有数据丢失不同步:appendfsync no 从不同步
2、aof劣势
相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb,aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
8、持久化总结(Which one)
官网建议
1)、RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
2)、AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大
3)、只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
4)、同时开启两种持久化方式
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
5)、性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。AOF好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite是将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。如果不Enable AOF ,仅靠Master-Slave Replication实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构
9、Redis的事务
10、Redis的复制(Master/Slave)
1)概念:也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机,Master以写为主,Slave以读为主
注:redis的主从复制解决的问题就是:读写分离和容灾恢复
2)实验:一主二从,一台主机两台从机
从库连接到主库的语法:slaveof 主库IP 主库端口
缺点是:每次与master断开之后,都需要重新连接,除非你配置主机ip到重机的redis.conf文件里面,每次启动自动去读
3)步骤:
拷贝多个redis.conf文件
[root@localhost bin]# cp redis.conf redis6379.conf
[root@localhost bin]# cp redis.conf redis6380.conf
[root@localhost bin]# cp redis.conf redis6381.conf
修改每个配置文件的信息
1、redis6379.conf
daemonize yes
port 6379
redis6379.pid 保存进程文件的文件名
logfile 6379.log
其它两个配置文件修改 略
分别启动它们
[root@localhost bin]# ./redis-server redis6379.conf
[root@localhost bin]# ./redis-server redis6380.conf
[root@localhost bin]# ./redis-server redis6381.conf
- 一主二仆:一台主机,两台备机(6379主机,6380和6381是备机)
创建三个会话窗口
2台从机连接到主机
分别在三个会话窗口执行 info replication。这个时候主机是master 而两个从机已经变成slave了
主机6379 写一条命令,2台从机负责取
思考1:主机先set k1到k4,从机再连接到主机,主机然后set k5,从机能取到k5,那么从机是否能取到k1 到k4的数据呢? 因为是先主机存k1 到k4,然后从机在连接主机的,k5当然可以取到,那么k1到k4其实也可以取到,因为从机连接主机的时候,它是一条条指令的读取的
思考2:主从机都去set一个k6是否可以操作
思考3:主机宕机情况,那么从机是否会变成主机(把主机shutdown,然后看从机状态)
思考4:如果主机恢复后,主机是否还是主机,如果是那么这个时候主机写一条命令,2个从机是否能收到
思考5:如果从机宕机了,主机这个set k9,从机是否能收到,如果从机连接了,是否能收到
- 反客为主
SLAVEOF no one:使当前数据库停止与其他数据库的同步,转成主数据库
实验1:6379是主机,6380和6381是从机。主机6379宕机,6380和6381还是从机,在6380上执行SLAVEOF no one,此时6380已经变成主机了,让6381去连接6380这个主机。这个时候是6380带着6381混了
思考1:如果这个时候6379复活,是否是主机,如果不是,能否收到6381主机的写指令的值。如果收不到怎么办?
总结:复制原理
1)、slave启动成功连接到master后会发送一个sync命令,Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
2)、全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
3)、增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
4)、但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
11、哨兵模式(sentinel)
1)概念:反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
2)实验操作:6379带着6380和638181,配置一主二从,新建sentinel.conf文件,名字绝不能错,然后在该文件填写内容
语法:sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1
说明:数字 1表示选举,某个slaver得到超过1票则成成为Master节点
启动哨兵:redis-sentinel sentinel.conf
配置如下
sentinel monitor host6379 127.0.0.1 6379 1
启动哨兵
[root@localhost bin]# ./redis-sentinel sentinel.conf
此时把6379执行shutdown,然后看看投票结果
复制的缺点
1)复制延时
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。