1 服务端和客户端的使用
1.1 服务端使用
1.1.1 启动
- 前台启动
redis-server - 后台启动
- 需要修改 redis.conf 配置文件
daemonize yes //表示开启后台守护
bind 192.168.2.101 // 绑定IP - 启动命令
redis-server /**/redis.conf
- 需要修改 redis.conf 配置文件
- docker方式启动
docker run -d -p 6379:6379 redis(或redis的image id)
1.1.2 使用客户端进行关闭的方式
- 客户端连接时关闭
redis-cli shutdown - 客户端连接后关闭
shutdown
1.2 客户端使用
1.2.1 连接redis服务
- redis-cli
redis-cli -h 192.168.2.101 -p 6379
-h:即redis.conf中bind的值,默认127.0.0.1
-p:指定端口,默认就是6379 - docker
docker exec -it redis的ContainerId redis-cli - 测试连接
ping //若返回pone则表示连接无误
1.2.2 退出客户端
输入quit即可
2 数据类型
2.1 String:字符串
- 应用场景很广泛,比如可以利用其自增属性,实现主键自增功能。
2.1.1 设置值
- 单个设置
语法:set key value
- 批量设置
语法:mset key1 value1 key2 value2
2.1.2. 获取值
- 单个获取
语法:get key
- 批量获取
语法:mget key1 key2
2.1.3 获取并设置值(先获取旧值再设置新值)
语法:getset key newValue
2.1.4 删除key
语法:del key1 key2 ...
返回:成功删除的个数
2.1.5 自增
语法1:incr key //默认增长量为1
语法2:incrby key increment //自定义每次增长量
注意:如果key不存在则自动创建,且初始值为1
2.1.6 自减
语法1:decr key //默认减少量为1
语法2:decrby key decrement //自定义减少量
2.1.7 追加拼接
语法:append key value
返回:拼接后value的长度
2.1.8 获取字符串长度
语法:strlen key
2.2 Hash:哈希
- 哈希类型,为字段(key)设置属性(field)
- 可以用来存储一个对象,解决了使用String类型保存对象不便的问题。
2.2.1 设置字段属性
语法1:hset key field fieldValue //单个设置
语法2:hmset key field1 field1Value field2 field2Value ...
2.2.2 获取字段属性
语法1:hget key field //单个获取
语法2:hmget key field1 field2 ... //批量获取
2.2.3 删除字段属性
语法:hdel key field1 field2 ...
2.2.4 字段属性增加
语法:hincrby key field fieldIncrement
注意:没有默认自增1;也没有减少计算,但是可以用增加一个负数来实现
2.2.5 获取所有字段属性
语法:hkeys key
2.2.6 获取所有字段属性值
语法:hvals key
2.2.7 判断某个属性是否存在
语法:hexists key field
返回:存在1,不存在0
2.2.8 获取某个字段(key)的属性(field)的个数
语法:hlen key
2.3 List:列表
- 列表类型采用的是可以双向操作的链表来存储数据
- 它是有序的,有序的实现方式是通过索引下标
- 应用场景有评论、消息队列、时间轴等
2.3.1 添加数据
语法1:lpush key value1 value2 ... //从左侧添加
语法2:rpush key value1 value2 ... //从右侧添加
语法3:linsert key before/after pivot(中心点) value
解释:在从左往右第一个值为pivot的数据的before/after插入数据value
2.3.2 获取数据
语法1:lrange key start stop
解释:start和stop为列表的索引,从0开始,-1表示最后一个
语法2:lindex key index
解释:获取指定索引的数据
2.3.3 删除数据
语法1:lpop key //移除最左边数据
语法2:rpop key //移除最右边数据
返回:被移除的数据
语法3:lrem key count value
返回:成功删除的个数
解释:指要删除count个值等于value的数据,count大于0,从左往右删;count小于0则从右往左删
2.3.4 获取长度
语法:llen key
2.3.5 削减列表
语法:ltrim key start stop
解释:start和stop为索引,表示该列表只保留start-stop的数据
2.3.6 数据转移
语法:rpoplpush sourceList destinationList
解释:将source列表右边弹出一个数据,以lpush的方式移入到destination列表
2.4 Set:集合
- 特点是唯一、无序
- 应用场景有标签、社交等
2.4.1 添加成员
语法:sadd key member1 member2 ...
2.4.2 删除成员
语法1:srem key member1 member2 ... //删除指定成员
语法2:spop key [count] //随机弹出count个,count默认为1
2.4.3 获取集合成员
语法:smembers key
2.4.4 判断成员是否在集合中
语法:sismember key member
返回:存在则返回1;不存在则返回0
2.4.5 集合运算
- 并集运算
语法:sunion key1 key2 ... - 交集运算
语法:sinter key1 key2 ... - 差集运算
语法:sdiff key1 key2 ...
2.4.6 集合长度(成员个数)
语法:scard key
2.5 SortedSet:有序集合
- 特点是唯一、有序
- 又叫 zset
- 有序的实现方式是通过分值,即为每个元素设置分数,根据分数大小实现排序
- 应用场景有社交、排行榜系统、商品筛选等
2.5.1 添加成员
语法1:zadd key score1 member1 score2 member2 ...
注意:如果成员重复则后面的覆盖前面的,且之后的返回成功数为0,即不计入成功数,因为第一次添加时已经计入了
2.5.2 查看成员
语法1:zrange key start stop [WITHSCORES]
语法2:zrevrange key start stop [WITHSCORES]
解释:zrange表示按分数从低到高输出指定范围的成员,WITHSCORES表示同时输出分数,zrevrange表示倒序,即分数从高到低输出
语法3:zrangebyscore key minScore maxScore [withscores] [limit offset count]
例如:zrangebyscore math 72 90 limit 0 2
解释:输出 [72, 90] 分之间的两名成员
2.5.3 查看成员分数
语法:zscore key member
注意:一次只能查看一个成员
2.5.4 查看成员排名(即成员位置)
语法1:zrank key member
语法2:zrevrank key member //降序排名
2.5.5 删除成员
语法1:zrem key member1 member2 ...
语法2:zremrangebyscore key min max
例如:zremrangebyscore math 70 80
解释:删除math中 [70, 90] 分之间的成员
2.5.5 给成员添加分数
语法:zincrby key increment member
2.5.6 查看成员个数
语法:zcard key
3 通用命令
3.1 key的匹配查找
语法:keys pattern
- keys *:获取所有的key
- keys n*:获取以n开始的key
- keys *n*:获取包含n的key
3.2 判断某个key是否存在
语法:exists key
返回:存在1,不存在0
3.3 查看key的数据类型
语法:type key
3.4 对key进行重命名
语法:rename key
3.5 设置key的生存时间
- 设置生存时间
语法:expire key second - 查看生存时间
语法:ttl key
返回:-1表示永久;-2表示已清除 - 清除生存时间的设置
语法:persist key
3.6 清空缓存
语法:flushall
4 redis的持久化方式和持久化策略
4.1 概述
redis是一个内存数据库,所有的数据都保存在内存中,这样就存在一个风险,那就是一旦redis异常退出,数据将不复存在。对此redis有两种数据持久化方式(RDB和AOF),用于将内存中的数据保存到磁盘文件中。
- 根据两种持久化方式的组合,redis有4种持久化策略
- RDB(数据快照模式)
定期存储,保存的是数据本身 - AOF(追加模式)
每次修改数据时,同步到硬盘,保存的是数据变更记录 - 同时关闭RDB和AOF
当希望将数据只保存在内存中时,可以将这两个策略都关闭 - 同时开启RDB和AOF【推荐】
当redis重启的时候,AOF文件会用于重建原始数据(AOF优先级高)
4.2 RDB:redis database
- 默认的持久化方式
- RDB定时备份内存中的数据,服务启动的时候可以从RDB中恢复数据集
4.2.1 优点
- 适用于备份,方便恢复不同版本的数据
- 适用于容灾恢复,备份文件可以在其他服务器恢复
- 数据保存比AOF快
- 最大化Redis的性能,备份的时候是启动子进程进行备份,父进程不需要执行IO操作
4.2.2 缺点
存在数据丢失的风险,因为如果Redis异常关闭,由于到上个保存点的数据未来得及备份,故而造成这部分数据丢失
4.2.3 触发RDB备份
4.2.3.1 手动触发
指通过在客户端中发送一条命令的方式,让服务端执行RDB备份,生成备份文件,备份命令有两种:
- 同步命令
save命令,这种方式会阻塞服务端当前的服务,使服务端暂时去执行备份命令,期间服务端不再接受客户端的请求,故而一般不会去使用 - 异步命令
bgsave命令,通过fork一个子进程去执行备份命令,服务端主进程依旧可以正常工作
4.2.3.2 自动触发
指的是通过在redis.conf中配置相应的备份触发策略,使达到策略中的条件时,服务端自动调用bgsave命令实现备份。
相关配置项有:
- save
save 900 1
save 300 10
save 60 10000
表示如果900秒内至少有1个key发生改变时,触发bgsave的调用 - dbfilename
dbfilename dump.rdb
指定备份文件的文件名,默认为dump.rdb - rdbcompression
rdbcompression yes
是否启用压缩,默认启用,此时在rdb备份文件生成时,如果遇到字符串对象且该字符串对象占用字节数超过20,则对该字符串采用LZF算法进行压缩 - stop-writes-on-bgsave-error
stop-writes-on-bgsave-error yes
默认开启,表示调用gbsave过程中遇到错误时,将停止备份 - dir
dir ./
配置备份文件的存放路径 - rdbchecknum
rdbchecksum yes
是否使用CRC64校验算法检验RDB文件是否有损坏,默认开启,关闭可提高性能
4.3 AOF:appendonly file
基于命令操作日志,指的是当有命令更新命令,如set key value,del key等,就会将该命令保存到AOF缓存区,然后在配置的时间节点写入到磁盘文件中
4.3.1 优点
- 数据可靠性比RDB的更好,适用于做完整的数据备份
- 一些误操作可以直接在AOF备份文件中进行回退
4.3.2 缺点
备份文件较RDB的要大,恢复效率不及RDB,不适用于快速可用的容灾恢复
4.3.3 AOF启用
serverCore函数
在redis中,常规操作由serverCore函数实现,而它的每一次调用,都会尝试进行 AOF 或 RDB 持久化操作
延迟写 和 fsync 函数
当数据写入文件时,系统内核(UNIX)通常先将该数据复制到一个缓冲区中,并且如果该缓冲区尚未写满,则先不将其放到输出队列,只有等到该缓冲区写满,或者内核需要重用该缓存区以便存放其他磁盘块数据时,再将其放到输出队列,然后再等到该数据到达队首时,才进行真正的I/O操作,这就是写延迟 -- delayed write。
fsync函数的特点是,可以等待数据真正写入到文件中。
以下为启用AOF策略的相关配置项(均在redis.conf中):
- appendonly
appendonly no
表示是否开启AOF策略,默认不开启 - appendfliename
appendfilename "appendonly.aof"
生成的aof备份文件的文件名,默认是appendonly.aof - hz
hz 10
hz参数可以用来指定每秒中执行serverCore的次数,默认每秒执行10次 - appendfsync
appendfsync everysec
当有一个key更新命令时,首先将该命令日志写入到AOF缓存区,再将缓存区中的内容写入到AOF备份文件中,appendfsync指定的是写入备份文件的频率以及写入的方式,它有三个选项:- everysec
指的是每隔1秒,就将AOF缓存区的操作命令日志写入到AOF文件中,写入时会调用fsync函数,即完成磁盘同步 - always
指每次serverCore执行时,就刷新AOF缓存区一次,并且每次写入AOF时都调用fsync,完成磁盘同步,不建议,因为它十分消耗性能 - no
不做控制,由操作系统决定什么时候刷新缓冲区,并且每次写入AOF时不调用fsync,由系统自动进行磁盘同步
- everysec
4.3.4 AOF重写
4.3.4.1 重写的作用
可以对AOF备份文件进行优化和压缩,比如有以下更新key的操作命令:
set key1 value1
set key 2 value2
del key1
经过重写,只会保留:
set key 2 value2
4.3.4.2 重写的说明
- 重写的执行也是通过fork一个子进程进行的
4.3.4.3 重写的触发
4.3.4.3.1 手动触发
通过在客户端中发送bgrewriteaof命令,就可以触发重写
4.3.4.3.2 自动触发
指在redis.conf中进行触发重写条件的相关配置,当达到重写触发的条件时,服务端将进行重写。
以下是触发重写的相关配置项:
- no-appendfsync-on-rewrite
no-appendfsync-on-rewrite no
有一种情况,主进程在执行将AOF缓冲区操作命令刷到AOF文件的过程时,同时还有一个AOF重写命令被调用了,而二者均需要对AOF文件进行写操作,且如果重写命令先进行,这样主进程就出现了阻塞的情况。
no-appendfsync-on-rewrite参数的作用就是为了解决这种情况,他的默认取值是no,表示当进行重写时依旧要进行appendfsync,即主进程形成阻塞;当取值yes时,则相当于appendfsync的取值时no,即主进程写入AOF文件时,不调用fsync函数,只是将数据放入到缓冲区,这样就不存在阻塞的情况了。
设置为yes时,如果redis宕机,则尚未真正执行I/O写入到AOF文件的那些缓冲区的数据都将丢失,在linux系统的默认设置下,最多会丢失30秒的数据。 - auto-aof-rewrite-percentage
auto-aof-rewrite-percentage 100
表示当AOF文件相较于上一版本AOF文件大小的百分比达到多少时将触发AOF重写,如auto-aof-rewrite-percentage 选项配置为 100,上一版本的 aof 文件大小为 100M,那么当AOF文件达到 200M 的时候,触发 AOF 重写 - auto-aof-rewrite-min-size
auto-aof-rewrite-min-size 64mb
当AOF文件超过64M(默认)时,将进行重写
5 主从模式
指一个主数据库Master,设置多个从数据库Slave的主从组合方式 [注:Slave还可以继续拥有自己的Slave],以此可以实现读写分离、哨兵模式[当Master宕机,Slave可以成为新的Master]等功能
5.1 主从搭建
可以使用不同的机器,通过ip区分主从;也可以使用同一个机器,通过port区分主从,比如使用后者方式进行搭建,只需要对后者配置上slaveof配置项,指明他的Master机器的ip和port即可,其中slaveof的设置可以直接在slave的redis-cli中直接指定,但是这种方式是临时性的,重启slave时失效,另外一种就是在slave的redis.conf中进行配置,永久有效。
5.2 读写分离
指在Redis的主从节点中,Master可读可写,而Slave只读不可写,所有的写只能在Master中进行,适用于少量写,大量读的场景。
过期key的处理
slave不会对key进行过期处理,master才会,当master对某个key进行过期清除时,将发送一条del命令给slave,以模拟key过期
5.3 主从复制
指的是为了保证数据库的数据的一致性,需要将Master的数据复制到Slave中,其中复制数据的方式又分为全量同步和增量同步两种
5.3.1 全量同步
全量同步一般发生在Slave初始化阶段,具体步骤如下
- slave连接上master后,会发送一条psync (旧版本为sync)指令给master
- master接收到psync指令后,会执行bgsave命令生成rdb文件,同时将从[master]客户端中接收到的所有的写命令,缓存在内存中。
- 待bgsave命令执行完成,master向所有的slave发送rdb快照
- 在接收到快照之前,slave一直使用旧数据提供服务,此时将丢弃所有的旧数据,开始载入收到的快照
- master将快照发送完毕之后,开始将缓存的写命令发送给slave
- slave完成快照的载入后,开始接收命令请求,并执行来自master缓冲区的写命令
5.3.2 增量同步
- 增量同步指的是Slave初始化完成后,开始正常工作时将Master的写操作进行同步的过程。
- 同步过程主要就是当Master每执行一条写操作命令,就会将该写命令发送给Slave,Slave接收并执行该写命令。
5.3.3 断点续传
- 从redis2.8版本开始支持,指的是在主从复制过程中,如果slave和master发生异常断开连接,再次连接时将从上次复制的断点开始继续复制,而不是从头开始复制。
- 原理
在主从复制中,Master会在内存中维护一个backlog,同时Master和Slave的内存中都维护着一个复制偏移量 replication offset 和 master run id。当连接断开重连时,Master会检测PSYNC中的master run id和replication offset,如果二者的id相同,且offset在有效范围内,则开始断点续传。
由于id和offset都保存在内存中,所以无论Master还是Slave,如果是直接宕机,而非单纯的网络中断,则将无法断点续传,只能进行全量同步。
5.3.4 无磁盘化复制
指的是在全量复制过程中,对于rdb的创建,直接在磁盘中进行,然后发送给slave,而无需落地磁盘后在发送给slave,相关配置项如下:
- repl-diskless-sync
repl-diskless-sync no
是否开启,默认不开启 - repl-diskless-sync-delay
repl-diskless-sync-delay 5
默认等待5秒后才开始复制,为了确保slave完成连接,即slave端能够进行到待传送的目标队列中
6 集群架构
指多个主从模式结合起来,组成的彼此能够互相通信的集群,各节点分布式部署,可以解决单点故障、实现负载均衡
6.1 集群搭建
有三台机器bigdata1、bigdata2、bigdata3,每台机器上有6379和6380两个redis
6.1.1 配置redis.conf
- cluster-enabled yes
表示开启集群 - cluster-node-timeout 15000
网络抖动配置项,生产环境中,网络可能会常出现问题,该配置项表示这段时间一直无法访问,则该节点fail,进行主从切换,如果未设置此配置项,可能会造成频繁的主从切换 - cluster-config-file bigdata1-6379
集群中每个结点都会生成一个自己的集群配置文件,这个文件最好每个redis都不一样 - slaveof
将该配置项注释,由redis自动分配master和slave节点
6.1.2 环境准备
- redis-tri.rb
在src目录下,是redis的集群管理器工具,可以使用它启停、管理集群 - ruby 和 rubygems
redis-tri.rb是由ruby语言编写,使用它需要先要安装ruby和rubygems(ruby通信接口):
sudo yum -y install ruby
sudo yum -y install rubygems
可使用ruby -v检查是否安装成功 - redis.gem
让集群管理器和redis进行通信,还需要安装redis驱动 redis-3.2.1.gem:
gem install redis-3.2.1.gem
这种方式需要提前准备好驱动,也可以直接在线安装:gem install redis,但它要求ruby在2.2.2版本以上。
6.1.3 清空数据
- 启动所有的redis
- 进入redis-cli,执行命令:
flushall
全新安装的redis可以跳过,如果是复制于其他已经使用过的redis,可能存在rdb文件,如果未删除该rdb直接启动,则启动的redis中有缓存数据存在,而cluster的创建要求所有节点都是空的不能有数据,所以必须先进行上面的清除操作
6.1.4 启动集群
./redis-trib.rb create --replicas 1 192.168.2.101:6379 192.168.2.101:6380 192.168.2.102:6379 192.168.2.102:6380 192.168.2.103:6379 192.168.2.103:6380
- --replicas 1 表示每个master中slave的个数,后面有6个cluster节点,将会自动生成3个master和3个slave
- cluster node必须要使用ip的形式,不能直接使用域名,否则可能会报错
6.1.5 验证
- 连接集群
redis-cli -h (任一cluster node的ip) -p port -c
-c:表示连接到集群 - 查看集群信息
cluster info - 查看集群节点
cluster nodes
6.2 负载均衡:插槽机制
redis cluster的负载均衡是通过插槽机制实现的
6.2.1 插槽计算
对key的有效部分进行CRC16算法计算出对应的哈希值,再将哈希值对16834进行取余,最终的结果就是这个key的插槽值
有效部分
- 指的是key中如果有{},则{}中的内容就是有效部分,否则整个key就是有效部分
- 比如:key = {hello}world,有效部分就是hello
- 比如:key = helloworld,有效部分就是helloworld
6.2.2 插槽分配
- 由插槽值计算可知,集群的插槽数有 16384(0 ~ 16383) 个,每个redis负责处理一部分的插槽。
- 可以使用redis-trib.rb将未分配的插槽分给节点,或者将已经分配的插槽转移到指定节点(插槽中的数据同时也会被转移到该指定节点)。