NoSQL-Redis

1 Redis介绍

1.1 NoSQL 产品(key-value)

RDBMS (第一代数据库架构):MySQL,Oracle ,MSSQL,PG
NoSQL (第二代数据库架构):
非关系型,非结构化 ,半结构
		缓存数据库:Redis,MongoDB,Tair...
		文档类:mongoDB(适合大数据量的存储),ES
		列存储:HBASE...
		图形存储:Neo4J...
NewSQL(第三代数据库架构)-----> 分布式数据库架构,把其他数据库的功能融合到一起(MySQL、redis等),合并成一个数据库系统,可以统一管理

缓存产品介绍:
memcached (大公司会做二次开发)
redis
Tair (淘宝)
阿里去IOE: IBM(小型机) Oracle   EMC(存储)

小提示:
redis是一个独立的缓存的数据库的解决方案
解决方案和软件不一样,解决方案可以适应整体的一些业务了
不需要配合第三方的软件,具备了有单机有主从有高可用还有分布式,不管
你用什么样的架构,自己本身就可以解决了
mysql就达不到,需要配合第三方软件MHA等第三方组件才可以

1.2 Redis功能介绍

数据类型丰富     (笔试、面试)  *****
支持持久化       (笔试、面试)  *****
多种内存分配及回收策略
支持弱事务		 (面试)      ****
支持高可用                     ****
支持分布式分片集群 (面试)      *****
缓存穿透\雪崩	(笔试、面试)     *****
Redis API				     **

1.3 企业缓存产品介绍

Memcached(大公司会做二次开发):
优点:高性能读写、单一数据类型、支持客户端式分布式集群、一致性hash
多核结构、多线程读写性能高。(多核多进程有更多连接的并发)
缺点:无持久化、节点故障可能出现缓存穿透、分布式需要客户端实现、跨机房数据同步困难、架构扩容复杂度高
Redis:	优点:高性能读写、多数据类型支持、数据持久化、高可用架构、支持自定义虚拟内存、支持分布式分片集群、单线程读写性能极高
缺点:多线程读写较Memcached慢,所以更加适合单机多实例的环境
新浪、京东、直播类平台、网页游戏

memcache  与redis在读写性能的对比
memcached 适合,多用户访问,每个用户少量的rw
redis     适合,少用户访问,每个用户大量rw	
			
Tair:
优点:高性能读写、支持三种存储引擎(ddb、rdb、ldb)、支持高可用、支持分布式分片集群、支撑了几乎所有淘宝业务的缓存。
缺点:单机情况下,读写性能较其他两种产品较慢

1.4 Redis使用场景介绍

Memcached:多核的缓存服务,更加适合于多用户并发访问次数较少的应用场景
Redis:单核的缓存服务,单节点情况下,更加适合于少量用户,多次访问的应用场景。
Redis一般是单机多实例架构,配合redis集群出现。

2 Redis安装部署

redis官方网站:redis.io  redis.cn

下载:
wget http://download.redis.io/releases/redis-3.2.12.tar.gz

解压:
上传至 /data
tar xzf redis-3.2.12.tar.gz
mv redis-3.2.12 redis

安装:
yum -y install gcc automake autoconf libtool make
cd redis
make

环境变量:
vim /etc/profile 
export PATH=/data/redis/src:$PATH
source /etc/profile 

启动:
redis-server &   (& 后台运行)

连接测试:
redis-cli 
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> get num
10

redis-cli --raw # 进入的时候可以中文显示
> set dog 狗
ok
> get dog
"狗"

3 Redis基本管理操作

3.1 基础配置文件介绍

mkdir /data/6379
cat > /data/6379/redis.conf<<EOF
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dump.rdb
EOF

redis-cli shutdown (面交互关闭redis)
redis-cli set a 1 (面交互执行redis命令)
redis-server /data/6379/redis.conf 
netstat -lnp|grep 63

============配置文件说明============
redis.conf
是否后台运行:
daemonize yes
默认端口:
port 6379
日志文件位置
logfile /var/log/redis.log
持久化文件存储位置
dir /data/6379
RDB持久化数据文件:
dbfilename dump.rdb
==================================
redis-cli
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> get name
"zhangsan"

3.2 redis安全配置

redis默认开启了保护模式,只允许本地回环地址登录并访问数据库。
禁止protected-mode
protected-mode yes/no (保护模式,是否只允许本地访问)

(1)Bind :指定IP进行监听
vim /data/6379/redis.conf
bind 10.0.0.100  127.0.0.1

(2)增加requirepass  {password}
vim /data/6379/redis.conf
requirepass 123456

----------验证-----
方法一:
远程登录:
[root@db03 ~]# redis-cli -h 10.0.0.51 -p 6379 -a 123456
本地登录:
[root@db03 ~]# redis-cli -a 123456
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> exit
方法二:
远程登录:
[root@db03 ~]# redis-cli -h 10.0.0.51 -p 6379 
10.0.0.51:6379> set a 1
(error) NOAUTH Authentication required
10.0.0.51:6379>  AUTH 123456
OK
本地登录:
[root@db03 ~]# redis-cli
127.0.0.1:6379> auth 123456
OK

3.3 在线查看和修改配置

大部分可以做在线配置,为了下回重启服务,还是写在配置文件里比较好,redis默认内存使用是操作系统有多少就使用多少,用到枯竭为止。maxmemory 0,这个0就是没有上限的意思,如果是单节点的redis,使用虚拟机70%的内存

CONFIG GET *
CONFIG GET requirepass
CONFIG GET r*
CONFIG SET requirepass 123
CONFIG REWRITE # 回写配置文件

3.4 redis持久化(内存数据保存到磁盘)

RDB、AOF持久化保存的位置:在配置中定义
dir “/data/6379”
dbfilename “dump.rdb” # 定义文件名称

开以上两个配置,reids不会持久化,还需要在配置文件添加参数(RDB触发条件):
save 900 1
save 300 10
save 60 10000

添加参数(AOF触发条件)
appendonly yes
appendfsync everysec

配置完成后重启:
redis-cli -a 123 shutdown
redis-server /data/6379/redis.conf

RDB、AOF

RDB 持久化(拿来做备份比较多一些)
	可以在指定的时间间隔内生成数据集的 时间点快照(point-in-time snapshot)。新快照会覆盖老快照
    优点:速度快,适合于用做备份,主从复制也是基于RDB持久化功能实现的。
    缺点:会有数据丢失

rdb持久化核心配置参数:
vim /data/6379/redis.conf
dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

配置分别表示:
900秒(15分钟)内有1个更改
300秒(5分钟)内有10个更改
60秒内有10000个更改
  
AOF 持久化(append-only log file)
	记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 
    AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。
    优点:可以最大程度保证数据不丢
    缺点:日志记录量级比较大


	
AOF持久化配置
appendonly yes
appendfsync always  # 每次操作成功就会记录
appendfsync everysec  # 每秒钟数据就会落盘
appendfsync no 

是否打开aof日志功能
每1个命令,都立即同步到aof 
每秒写1次
写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof.

vim /data/6379/redis.conf
appendonly yes
appendfsync everysec 

面试: 
redis 持久化方式有哪些?有什么区别?
rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能
aof:以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,类似于mysql的binlog

redis-cli -h 10.0.0.51 -p 6379 进入之后
127.0.0.1:6379> set a 1
127.0.0.1:6379> save # 操作的数据会落盘

save bgsave区别:
共同点:都能实现RDB持久化功能
不同点:
	save: 前台,阻塞redis正常写入,直接持久化完成
	bgsave: 后台,开启子线程,异步的持久化功能,不会阻塞redis正常写入

最终的单节点配置文件内容:

daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dump.rdb
bind 10.0.0.61 127.0.0.1
requirepass 123456
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec

4. Redis数据类型(笔试)

4.1 介绍

key:value

String :      字符类型
Hash:         字典类型
List:         列表     
Set:          集合 
Sorted set:   有序集合

在这里插入图片描述
如果要做mysql和redis数据同步,可以使用canal工具

4.2 KEY的通用操作

KEYS * 	 keys a  keys a*	查看已存在所有键的名字   ****
TYPE				返回键所存储值的类型     ****
EXPIRE\ PEXPIRE 		以秒\毫秒设定生存时间    ***
TTL\ PTTL 			以秒\毫秒为单位返回生存时间 ***
PERSIST 			取消生存时间设置            ***
DEL				删除一个key
EXISTS 				检查是否存在
RENAME 				变更KEY名

---例子:
127.0.0.1:6379> set name zhangsan 
127.0.0.1:6379> EXPIRE name 60
(integer) 1
127.0.0.1:6379> ttl name
(integer) 57
127.0.0.1:6379> set a b ex 60
OK
127.0.0.1:6379> ttl a
127.0.0.1:6379> PERSIST a
(integer) 1
127.0.0.1:6379> ttl a
(integer) -1

4.3 String

应用场景
session 共享
常规计数:微博数,粉丝数,订阅、礼物

key:value
(1)
 set name zhangsan 	
(2)
 MSET id 101 name zhangsan age 20 gender m
 等价于以下操作:
 SET id 101 
 set name zhangsan 
 set age 20 
 set gender m
(3)计数器
每点一次关注,都执行以下命令一次
127.0.0.1:6379> incr num
显示粉丝数量:
127.0.0.1:6379> get num

暗箱操作:
127.0.0.1:6379> INCRBY num 10000
(integer) 10006
127.0.0.1:6379> get num
"10006"
127.0.0.1:6379> DECRBY num 10000
(integer) 6
127.0.0.1:6379> get num
"6"

详细的例子:------------------------------------

增
set mykey "test"                为键设置新值,并覆盖原有值
getset mycounter 0              设置值,取值同时进行
setex mykey 10 "hello"          设置指定 Key 的过期时间为10秒,在存活时间可以获取value
setnx mykey "hello"             若该键不存在,则为键设置新值
mset key3  "zyx"  key4 "xyz"    批量设置键

删
del mykey                        删除已有键

改
append mykey "hello"            若该键并不存在,返回当前 Value 的长度
                                该键已经存在,返回追加后 Value的长度
incr mykey                      值增加1,若该key不存在,创建key,初始值设为0,增加后结果为1
decrby  mykey  5                值减少5
setrange mykey 20 dd            把第21和22个字节,替换为dd, 超过value长度,自动补0
查  
exists mykey                    判断该键是否存在,存在返回 1,否则返回0
get mykey                       获取Key对应的value
strlen mykey                    获取指定 Key 的字符长度
ttl mykey                       查看一下指定 Key 的剩余存活时间(秒数)
getrange mykey 1 20             获取第2到第20个字节,若20超过value长度,则截取第2个和后面所有的
mget key3 key4                  批量获取键

4.4 hash类型(字典类型)

应用场景:
数据库缓存
存储部分变更的数据,如用户信息等。
最接近mysql表结构的一种类型
主要是可以做数据库缓存。

存数据:
hmset stu_zs  id 101 name zhangsan age 20 gender m
hmset stu_102 id 102 name zhangsan1 age 21 gender f

取数据:
HMGET stu id name age gender
HMGET stu1 id name age gender

select concat("hmset city_",id," id ",id," name ",name) from world.city limit 10 into outfile 'tmp/hmset.txt'

手工方式模拟:mysql的热点表数据,灌入到redis HASH类型
在这里插入图片描述

4.5 LIST(列表)

特点:
	key  	value
	dong	 [day7..day3,day2,day1]
				    0        4       5      6(-1)
应用场景
消息队列系统
微信朋友圈,即时消息展示
比如sina微博
在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。
但是做了限制不能超过5000个ID,因此获取ID的函数会一直询问Redis。
只有在start/count参数超出了这个范围的时候,才需要去访问数据库。
系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。
SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,
而主页或第一个评论页是不会麻烦到硬盘上的数据库了。

微信朋友圈:
127.0.0.1:6379> LPUSH wechat "today is nice day !"
127.0.0.1:6379> LPUSH wechat "today is bad day !"
127.0.0.1:6379> LPUSH wechat "today is good  day !"
127.0.0.1:6379> LPUSH wechat "today is rainy  day !"
127.0.0.1:6379> LPUSH wechat "today is friday !"

[5,4,3,2,1]
 0 1 2 3 4 

[e,d,c,b,a]
0 1 2 3  4

127.0.0.1:6379> lrange wechat  0 0
1) "today is friday !"
127.0.0.1:6379> lrange wechat  0 1
1) "today is friday !"
2) "today is rainy  day !"
127.0.0.1:6379> lrange wechat  0 2
1) "today is friday !"
2) "today is rainy  day !"
3) "today is good  day !"
127.0.0.1:6379> lrange wechat  0 3
127.0.0.1:6379> lrange wechat  -2 -1
1) "today is bad day !"
2) "today is nice day !"

==================================
增 
lpush mykey a b             若key不存在,创建该键及与其关联的List,依次插入a ,b, 若List类型的key存在,则插入value中
lpushx mykey2 e             若key不存在,此命令无效, 若key存在,则插入value中
linsert mykey before a a1   在 a 的前面插入新元素 a1
linsert mykey after e e2    在e 的后面插入新元素 e2
rpush mykey a b             在链表尾部先插入b,在插入a
rpushx mykey e              若key存在,在尾部插入e, 若key不存在,则无效
rpoplpush mykey mykey2      将mykey的尾部元素弹出,再插入到mykey2 的头部(原子性的操作)
删
del mykey                   删除已有键 
lrem mykey 2 a              从头部开始找,按先后顺序,值为a的元素,删除数量为2个,若存在第3个,则不删除
ltrim mykey 0 2             从头开始,索引为0,1,2的3个元素,其余全部删除
改
lset mykey 1 e              从头开始, 将索引为1的元素值,设置为新值 e,若索引越界,则返回错误信息
rpoplpush mykey mykey       将 mykey 中的尾部元素移到其头部
查
lrange mykey 0 -1           取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
lrange mykey 0 2            从头开始,取索引为0,1,2的元素
lrange mykey 0 0            从头开始,取第一个元素,从第0个开始,到第0个结束
lpop mykey                  获取头部元素,并且弹出头部元素,出栈
lindex mykey 6              从头开始,获取索引为6的元素 若下标越界,则返回nil

4.6 SET 集合类型(join union)

应用场景:
案例:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。
Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,
对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

创建集合:
127.0.0.1:6379> sadd lxl pg1 jnl baoqiang gsy alexsb
(integer) 5
127.0.0.1:6379> sadd jnl baoqiang ms bbh yf wxg
(integer) 5

场景一: lxl和jnl结婚,邀请各路好友,统计人数
1. 集合的并集
127.0.0.1:6379> SUNION lxl  jnl
1) "gsy"
2) "yf"
3) "alexsb"
4) "bbh"
5) "jnl"
6) "pg1"
7) "baoqiang"
8) "ms"
9) "wxg"
127.0.0.1:6379> 
	
......
场景二:有一天,lxl做饭放盐放的不对,jnl不愿意,吵架了,调节一下。
2. 交集:找共同好友
127.0.0.1:6379> SINTER lxl jnl
1) "baoqiang"

........
场景三: jnl经常性的拿做菜事情挑事,打一架吧,各自拉自己的好友干架,排除共同的好友
3. 差集
127.0.0.1:6379> SDIFF jnl lxl
1) "wxg"
2) "yf"
3) "bbh"
4) "ms"
127.0.0.1:6379> 
127.0.0.1:6379> SDIFF lxl jnl
1) "jnl"
2) "pg1"
3) "gsy"
4) "alexsb"
.....

4.7 SortedSet(有序集合)

应用场景:
排行榜应用,取TOP N操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,
这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即 可。

特点:
		key			value
		s_set		100w   20w 10w  1w
						(sldya, smlt, lzhd,lg )
应用场景: 乐排行榜
音乐  例子(phb排行榜,相当于一个key):
# 添加歌曲,并且给歌曲初始分数
127.0.0.1:6379> zadd phb 0 ghsy  0 lg 0 hktk
(integer) 3

# 给歌曲进行打分
127.0.0.1:6379> ZINCRBY phb 100  fsk
"100"
127.0.0.1:6379> ZINCRBY phb  90   lg
"90"
127.0.0.1:6379> ZINCRBY  phb  80   hktk
"80"

#  从头到尾进行按分数进行排名
127.0.0.1:6379> ZREVRANGE phb 0 -1 
1) "fsk"
2) "lg"
3) "hktk"

#  排名的同时也显示分数
127.0.0.1:6379> ZREVRANGE topN 0 2 withscores
1) "fsk"
2) "100"
3) "lg"
4) "90"
5) "hktk"
6) "80"

=================================================
增
zadd myzset 2 "two" 3 "three"       添加两个分数分别是 2 和 3 的两个成员
删
zrem myzset one two                 删除多个成员变量,返回删除的数量
改
zincrby myzset 2 one                将成员 one 的分数增加 2,并返回该成员更新后的分数
查 
zrange myzset 0 -1 WITHSCORES       返回所有成员和分数,不加WITHSCORES,只返回成员
zrank myzset one                    获取成员one在Sorted-Set中的位置索引值。0表示第一个位置
zcard myzset                        获取 myzset 键中成员的数量
zcount myzset 1 2                   获取分数满足表达式 1 <= score <= 2 的成员的数量
zscore myzset three                 获取成员 three 的分数
zrangebyscore myzset  1 2               获取分数满足表达式 1 < score <= 2 的成员
#-inf 表示第一个成员,+inf最后一个成员
#limit限制关键字
#2  3  是索引号
zrangebyscore myzset -inf +inf limit 2 3  返回索引是2和3的成员
zremrangebyscore myzset 1 2         删除分数 1<= score <= 2 的成员,并返回实际删除的数量
zremrangebyrank myzset 0 1              删除位置索引满足表达式 0 <= rank <= 1 的成员
zrevrange myzset 0 -1 WITHSCORES        按位置索引从高到低,获取所有成员和分数
#原始成员:位置索引从小到大
      one  0  
      two  1
#执行顺序:把索引反转
      位置索引:从大到小
      one 1
      two 0
#输出结果: two  
       one
zrevrange myzset 1 3                获取位置索引,为1,2,3的成员
#相反的顺序:从高到低的顺序
zrevrangebyscore myzset 3 0             获取分数 3>=score>=0的成员并以相反的顺序输出
zrevrangebyscore myzset 4 0 limit 1 2    获取索引是1和2的成员,并反转位置索引

5. Redis消息模式

介绍:
消息模式是为了帮助解决在架构中,资源有效利用方面提供有效的协调。
redis的消息模式有类中形式:消息队列(这种不好在reids中模拟,因为没有消费者和生产者,还需要程序辅助),发布订阅(在redis中比较好模拟这种方式)。
消息模式理解:
    北京制作面包,海淀、朝阳、昌平这三个区每天各自提供200个面包,但是每个区消费水平不一样,海淀区只能消费100个面包,剩余100个,朝阳区消费100个面包,剩余100个面包。昌平消费400个面包,欠200个面包,这显然不合理。
     这个时候找一个代理商(面包超市),把三个区的面包都集中到一个面包超市,然后给人们送面包,这个时候就解决上面的问题了。

在这里插入图片描述
在这里插入图片描述
程序不会过于依赖于物理硬件,降低耦合度
在这里插入图片描述
发布订阅功能:

pulisher 发布者
channel 频道
subscribe 订阅者

在这里插入图片描述模拟发布订阅:
模拟一:打开两个redis界面,a先订阅对方,b对方发送消息向a,a接收到b发送到的消息在这里插入图片描述

模拟二:打开四个reids界面,模拟两个人聊天
模拟二:打开四个窗口,模拟两个人聊天

提示: 如果要使用高级的功能,建议使用kafka、rabbtimq等

=================================================
PUBLISH channel msg
    将信息 message 发送到指定的频道 channel
SUBSCRIBE channel [channel ...]
    订阅频道,可以同时订阅多个频道
UNSUBSCRIBE [channel ...]
    取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道
PSUBSCRIBE pattern [pattern ...]
    订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所   有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有 以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类
PUNSUBSCRIBE [pattern [pattern ...]]
    退订指定的规则, 如果没有参数则会退订所有规则
PUBSUB subcommand [argument [argument ...]]
    查看订阅与发布系统状态
注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,之前发送的不会缓存,必须Provider和Consumer同时在线。

发布订阅例子:
窗口1:
127.0.0.1:6379> SUBSCRIBE baodi 
窗口2:
127.0.0.1:6379> PUBLISH baodi "jin tian zhen kaixin!"
订阅多频道:
窗口1:
127.0.0.1:6379> PSUBSCRIBE wang*
窗口2:
127.0.0.1:6379> PUBLISH wangbaoqiang "jintian zhennanshou "

在这里插入图片描述

6. Redis事务

redis的事务是基于队列实现的。
mysql的事务是基于事务日志和锁机制实现的。
redis是乐观锁机制。

redis事务和mysql事务的区别?
简要回答:
redis的事务是基于: 队列实现的,rdis是乐观锁机制,仅实现原子性的保证,属于弱事务支持。
MySQL的事务是基于: 十五日至悲观锁机制、MVCC、ISOLASTION等机制一起保证,强事务支持。


开启事务功能时(multi)
multi 
command1      
command2
command3
command4
exec 
discard

4条语句作为一个组,并没有真正执行,而是被放入同一队列中。
如果,这是执行discard,会直接丢弃队列中所有的命令,而不是做回滚。
exec
当执行exec时,对列中所有操作,要么全成功要么全失败

127.0.0.1:6379> set a b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a b
QUEUED
127.0.0.1:6379> set c d
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK

reids乐观锁实现(模拟买票)
北京----上海   ticket=1
#  在事务中添加watch,监视watch,如果在事务执行期间,发现ticket发生变化,事务在EXEC是会失败(nil)
session 1
watch ticket
multi
decr ticket
exec 

session 2:
multi
decr ticket
exec

在开启事务之后,执行命令,并没有直接在内存中修改,而是先放在队列中去,执行完EXEC之后才会在内存中修改。
在这里插入图片描述

7. Redis的key的通用操作和服务管理命令

KEYS *(危险,生产环境不要操作!!!)keys a  keys a*  查看已存在所有键的名字   ****

TYPE			返回键所存储值的类型 ****
127.0.0.1:6379> hmset test id 101 name zhangsan
ok
127.0.0.1:6379>  type test
hash

EXPIRE\ PEXPIRE	以秒\毫秒设定生存时间 ***
127.0.0.1:6379> EXPIRE test 100      # 给test设置存活时间为100秒
(integer) 1

TTL\ PTTL		以秒\毫秒为单位返回生存时间 ***
127.0.0.1:6379>  ttl test
81

PERSIST		 	取消生存时间设置  ***
127.0.0.1:6379> PERSIST	test
(integer) 1

DEL 			 删除一个key
EXISTS			检查是否存在
RENAME	   		变更KEY名



Info    主要关注cpu、内存的信息
Client list   查看正在连接的会话
Client kill ip:port
config get *   获取配置信息
CONFIG RESETSTAT 重置统计
CONFIG GET/SET 动态修改
Dbsize   查看当前系统中有多少个键值对
FLUSHALL 清空所有数据 (非常危险的操作,生产不要使用!!!!)
select 1    进入指定的库(0-15库,默认是0号库)

# redis默认有16个库(0-15),不能删除,只能清空库里面的键值对
FLUSHDB 清空当前库

MONITOR 监控实时指令
# 把监控的日志(记录每一步的操作,类似于审计),输入到/tmp/mom.log
redis-cli -a 1 -h 10.0.0.61 -p 6379 monitor >>/tmp/mon.log&
==========================================
# 测试
reids-cli  -a  123456
127.0.0.1:6379>  monitor
ok    # 下面的内容为监控内容
# 下面记录的就是另一个窗口记录的内容
1653734706.458376 [0 127.0.0.1:54674] "AUTH" "123456"
1653734706.459256 [0 127.0.0.1:54674] "COMMAND"

# 另外打开一个reids窗口
reids-cli -a 1   # 从登录reids里面,monitor就开始记录了

把monitor关掉:ps -ef | grep redis  # 关掉monitor的进程即可
============================================
SHUTDOWN 关闭服务器
关闭数据库:(危险操作,生产不要执行)
redis-cli -a 123456shutdown

mysql如果做审计?
general log 通用日志
audit log(企业版才有)
还有某些中间件上去做

8. Redis主从复制(Master-Replicaset)

8.1 原理

主库比较主动

1. 副本库通过slaveof 10.0.0.51 6379命令,连接主库,并发送SYNC给主库 
2. 主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
3. 副本库接收后会应用RDB快照
4. 主库会陆续将中间产生的新的操作,保存并发送给副本库
5. 到此,我们主复制集就正常工作了
6. 再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库.
7. 所有复制相关信息,从info信息中都可以查到.即使重启任何节点,他的主从关系依然都在.
# redis-2.8+版本的PSYNC
8. 如果发生主从关系断开时,从库数据没有任何损坏,在下次重连之后,从库发送PSYNC给主库
9. 主库只会将从库缺失部分的数据同步给从库应用,达到快速恢复主从的目的
=========================================================
从库宕机五分钟,然后有从新与主节点进行连接
这五分钟从库相比主库少数据200条,这是从库
会采取:
1.  redis-2.8版本之前,从库会发起一个sync命令,主库全备份,从库重新构建
2.  redis-2.8版本之后,从库会发起一个psync命令,只备份少的那200条数据,应用到从库即可

在这里插入图片描述

8.2 主从数据一致性保证

了解一写即可

min-slaves-to-write 1    #  至少有一台数据被写入了,这样才会认为这次同步才完成
min-slaves-max-lag  3  #  被写入数据的slave,访问延时低于3秒钟

8.3 主库是否要开启持久化

如果主库不开持久化,有可能主库重启操作,造成所有主从数据全部丢失!
解释:
因为主库宕机了,重启之后没有数据库,虽然从库有数据,但是从库会重新向主库发起SYNC,这时从库数据也为全部丢失了。

9. 主从复制实现

redis单独使用主从的比较少,一般配合sentinel高可用架构来实现的

1、环境:
准备两个或两个以上redis实例

mkdir /data/638{0..2}

配置文件示例:
cat >> /data/6380/redis.conf <<EOF
port 6380
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
requirepass 123
masterauth 123       # 主库不用加masterauth,这个是为哨兵准备的环境
EOF


cat >>   /data/6381/redis.conf <<EOF
port 6381
daemonize yes
pidfile /data/6381/redis.pid
loglevel notice
logfile "/data/6381/redis.log"
dbfilename dump.rdb
dir /data/6381
requirepass 123
masterauth 123    # 告诉从库,连接主库的时候用123这个密码
EOF


cat >>   /data/6382/redis.conf <<EOF
port 6382
daemonize yes
pidfile /data/6382/redis.pid
loglevel notice
logfile "/data/6382/redis.log"
dbfilename dump.rdb
dir /data/6382
requirepass 123
masterauth 123
EOF


启动:
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf

主节点:6380
从节点:6381、6382

2、开启主从:
6381/6382命令行:

redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380
redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380


3、查询主从状态
 redis-cli -p 6380 -a 123 info replication
 redis-cli -p 6381 -a 123 info replication
 redis-cli -p 6382 -a 123 info replication

4. 解除主从身份
#  解除某一个从库
[root@db01 6380]# redis-cli -p 6382 -a 123 SLAVEOF no one

在这里插入图片描述

10. Redis-sentinel(哨兵)

redis自带高可用功能和组件,不需要借助第三方软件
注意: 下面的例子在一台物理服务器上,开启的多实例进行演示,生产环境中每个节点放在一个台物理服务器上

1、监控
2、自动选主,切换(6381 slaveof no one)
   采用的是raft分布式一致性协议进行选主:数据节接近主,可以和大部分节点联系,少数服从多数。
   (RAFT  Paxos)
3、重构主从关系
4、应用透明 
5、自动处理故障节点

sentinel搭建过程(如果每个节点服务器上都有Sentinel,把下面内容修改一下,在其他服务器上执行即可,修改端口号,还有选举也该一下,改成半数以上)

mkdir /data/26380
cd /data/26380

vim sentinel.conf
port 26380
dir "/data/26380"
# mymaster是为master起的名称,最后的1是选票,如果是一主两从那么,有三个sentinel,如果有半数以上sentinel认为主节点宕机,才会在slave中选择主节点。如果只有一个Sentinel,如果配置写2,在重启Sentinel的话,会起不来。
sentinel monitor mymaster 127.0.0.1 6380 1    
# 监控mymaster的时候,5秒内没有心跳,则就表明master节点宕机了
sentinel down-after-milliseconds mymaster 5000
#  sentinel访问集群,需要每个节点的密码
sentinel auth-pass mymaster 123   

启动:
[root@db01 26380]# redis-sentinel /data/26380/sentinel.conf  &>/tmp/sentinel.log &

==============================
如果有问题:
1、重新准备1主2从环境
2、kill掉sentinel进程
3、删除sentinel目录下的所有文件
4、重新搭建sentinel
======================================
停主库测试:

[root@db01 ~]# redis-cli -p 6380 shutdown
[root@db01 ~]# redis-cli -p 6381 info replication

启动源主库(6380),看状态。

如果修改了Sentinel的配置,需要重启Sentinel:
1. 停止sentinel
    杀掉sentinel进程或者
    redis-cli -p 26380 shutdown 
2. 启动Sentinel
    redis-sentinel /data/26380/sentinel.conf &>/tmp/sentinel.log &


Sentinel管理命令:
redis-cli -p 26380
PING :返回 PONG 。
SENTINEL masters :列出所有被监视的主服务器
SENTINEL slaves <master name> 

SENTINEL get-master-addr-by-name <master name> : 返回给定名字的主服务器的 IP 地址和端口号。 
SENTINEL reset <pattern> : 重置所有名字和给定模式 pattern 相匹配的主服务器。 
SENTINEL failover <master name> : 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移。


自行了解: 
consul + redis 

图解哨兵

master在宕机之前:
在这里插入图片描述
master在宕机之后:
app是直接连接Sentinel,不直接连master后者slave
在这里插入图片描述
虽然master已经宕机,但是Sentinel还在监控旧master,如果master恢复,会被当作集群的一个slave
在这里插入图片描述
旧master恢复后:

旧master恢复后,成为集群的slave,还可以添加一台新机器,然后加入到主从里,然后就成为集群里的slave了
在这里插入图片描述

每个节点后都启动一个Sentinel:
上面的多实例的例子中,因为在一台物理服务器上,开启的多实例,所以之开启了一个Sentinel,生产环境中,每一个节点都启动一个Sentinel
在这里插入图片描述
在这里插入图片描述

11. Redis Cluster

集群中没有使用sentinel,那谁监控那,高可用如何实现的? 集群中的节点互相监控心跳,如果5秒没有心跳,则认为宕机,就会切换master。

11.1 介绍

分布式能力(读写性能)

1、在多分片节点中,将16384个槽位,均匀分布到多个分片节点中
2、存数据时,将key做crc16(key),然后和16384进行取模,得出槽位值(0-16383之间)
3、根据计算得出的槽位值,找到相对应的分片节点的主节点,存储到相应槽位上
4、如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储

高可用:

在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof的功能,同时当主节点down,实现类似于sentinel的自动failover的功能。

1、redis会有多组分片构成(3组)
2、redis cluster 使用固定个数的slot存储数据(一共16384slot)
3、每组分片分得1/3 slot个数(0-5500  5501-11000  11001-16383)
4、基于CRC16(key) % 16384 ====》值 (槽位号)。

图解redis-cluster:

7000和7003是一对主从
7001和7004是一对主从
7002和7005是一对主从

slot可以理解为数据的存储单元。 crc16(name)=13265121(假设是这个值) name做一个crc16的hash运算,然后在和16384做模运算(取余运算)

在这里插入图片描述
在这里插入图片描述
其实主从不能放到同一台物理服务器上:
redis-cluster 一般有3-4 个分片,比如下图中有三个分片,两两一组,每个分片里面都是一个高可用的一个环境,防止宕机如果三台物理机,每个主从在不同的物理机上

app去连接7000、7001、7002这三个任何一个节点都可以,如果槽位号虽不在当前服务其上,那么也可以找到对应的服务器,比较智能,因为每台服务器上也存在其他服务器槽位好的信息。
在这里插入图片描述

小提示: 集群中没有使用sentinel,那谁监控那? 集群中所有的节点互相监控心跳

11.2 规划搭建过程

6个redis实例,一般会放到3台硬件服务器
注:在企业规划中,一个分片的两个分到不同的物理机,防止硬件主机宕机造成的整个分片数据丢失。
端口号:7000-7005

安装集群插件

EPEL源安装ruby支持
yum install ruby rubygems -y
使用国内源
gem sources -l     # 查看当前的源,gem是ruby的下载工具,类似于python的pip
gem sources -a http://mirrors.aliyun.com/rubygems/   # 替换成国内的源
gem sources  --remove https://rubygems.org/    # 删掉国外的源
gem sources -l    # 查看当前的源
gem install redis -v 3.3.3    # 安装reids的驱动,ruby连接reids的驱动

为什么要安装ruby环境?
要搭建集群的话,需要使用一个工具(脚本文件),这个工具在redis解压文件的源代码里(redis-trib.rb)。因为这个工具是一个ruby脚本文件,所以这个工具的运行需要ruby的运行环境,就相当于java语言的运行需要在jvm上。所以需要安装ruby。redis的版本和ruby包的版本最好保持一致。本地安装:gem install --local redis-3.3.5.gem (http://rubygems.org/downloads/redis-3.3.5.gem)。 下载地址为:
https://rubygems.org/gems/redis/versions/

集群节点准备

mkdir /data/700{0..5}
cat > /data/7000/redis.conf <<EOF
port 7000
daemonize yes
pidfile /data/7000/redis.pid
loglevel notice
logfile "/data/7000/redis.log"
dbfilename dump.rdb
dir /data/7000
protected-mode no
cluster-enabled yes   # 开启集群
cluster-config-file nodes.conf  # 集群自定义的配置文件,不需要改,redis自己维护的
cluster-node-timeout 5000  # 如果5秒没有心跳,代表节点宕机了,另一个节点接管,高可用。集群中没有使用sentinel,那谁监控那? 集群中所有的节点互相监控心跳
appendonly yes
EOF

cat >> /data/7001/redis.conf <<EOF
port 7001
daemonize yes
pidfile /data/7001/redis.pid
loglevel notice
logfile "/data/7001/redis.log"
dbfilename dump.rdb
dir /data/7001
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

cat >> /data/7002/redis.conf <<EOF
port 7002
daemonize yes
pidfile /data/7002/redis.pid
loglevel notice
logfile "/data/7002/redis.log"
dbfilename dump.rdb
dir /data/7002
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF


cat >>  /data/7003/redis.conf <<EOF
port 7003
daemonize yes
pidfile /data/7003/redis.pid
loglevel notice
logfile "/data/7003/redis.log"
dbfilename dump.rdb
dir /data/7003
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF


cat >> /data/7004/redis.conf <<EOF
port 7004
daemonize yes
pidfile /data/7004/redis.pid
loglevel notice
logfile "/data/7004/redis.log"
dbfilename dump.rdb
dir /data/7004
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

cat >> /data/7005/redis.conf <<EOF
port 7005
daemonize yes
pidfile /data/7005/redis.pid
loglevel notice
logfile "/data/7005/redis.log"
dbfilename dump.rdb
dir /data/7005
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

启动节点

redis-server /data/7000/redis.conf 
redis-server /data/7001/redis.conf 
redis-server /data/7002/redis.conf 
redis-server /data/7003/redis.conf 
redis-server /data/7004/redis.conf 
redis-server /data/7005/redis.conf 


[root@db01 ~]# ps -ef |grep redis
root       8854      1  0 03:56 ?        00:00:00 redis-server *:7000 [cluster]     
root       8858      1  0 03:56 ?        00:00:00 redis-server *:7001 [cluster]     
root       8860      1  0 03:56 ?        00:00:00 redis-server *:7002 [cluster]     
root       8864      1  0 03:56 ?        00:00:00 redis-server *:7003 [cluster]     
root       8866      1  0 03:56 ?        00:00:00 redis-server *:7004 [cluster]     
root       8874      1  0 03:56 ?        00:00:00 redis-server *:7005 [cluster]  

将节点加入集群管理

 # create 创建集群,--replicas 1 副本的个数是一个,意思是每个主库有一个副本,如果为2个副本需要9个节点,一主两从,127.0.0.1:7000 127.0.0.1:7001  127.0.0.1:7002前三个为主节点,127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005后三个代表的是从节点。127.0.0.1:7000 和127.0.0.1:7003是一对,以此类推
# 注意: 7000,7001,7002是主节点,7003,7004,7005是对应的从节点。主从节点不能是在同一台物理机的
#  第一个ip是管理节点 127.0.0.1:7000
redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005  

在这里插入图片描述

集群状态查看

# 7000为管理集群的节点(一般是创建集群的第一个节点为管理节点,做集群管理需要连上7000,执行内部的一些命令)
# 集群主节点状态
redis-cli -p 7000 cluster nodes | grep master
# 集群从节点状态
redis-cli -p 7000 cluster nodes | grep slave

# 登录到redis
> cluster info   查看集群健康状态
> cluster nodes  查看集群节点健康状态

下图中127.0.0.1:7000 myself,master,因为连接的是7000端口,所以前面会有一个myself

在这里插入图片描述

11.3 集群节点管理

需求:因为redis的负载很高,所以添加一个新的分片
7006 、7007为一组,组成高可用,7006为master,7007为slave

(1)增加新的节点

mkdir /data/7006
mkdir /data/7007
cat > /data/7006/redis.conf <<EOF
port 7006
daemonize yes
pidfile /data/7006/redis.pid
loglevel notice
logfile "/data/7006/redis.log"
dbfilename dump.rdb
dir /data/7006
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

cat >  /data/7007/redis.conf <<EOF
port 7007
daemonize yes
pidfile /data/7007/redis.pid
loglevel notice
logfile "/data/7007/redis.log"
dbfilename dump.rdb
dir /data/7007
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

redis-server /data/7006/redis.conf 
redis-server /data/7007/redis.conf 

在这里插入图片描述

(2)添加主节点

redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000     # 将127.0.0.1:7006添加到127.0.0.1:7000所管理的集群

在这里插入图片描述
需要把前三个节点的槽位均匀的给新的加的节点 16384/4 = 4096
相当于给新添加的节点4096个槽位

(3)转移slot(重新分片)

redis-trib.rb reshard 127.0.0.1:7000   # 127.0.0.1:7000是集群管理节点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(4)添加一个从节点

redis-trib.rb add-node --slave --master-id 9b28b4e497cddb64c21007ba3c78abe390b2f950 127.0.0.1:7007 127.0.0.1:7000

在这里插入图片描述
在这里插入图片描述

11.4 删除节点

直接删除节点,是删除不掉了,必须把槽位数在分给其他的节点,这样才能删除,比如新节点7006节点的槽位数为 0-1364 5461-6826 10923-12287,这些槽位数是之前三个节点分给这个新节点的。所以需要把三部分的节点原路返回,然后才能三处节点

将需要删除节点slot移动走

redis-trib.rb reshard 127.0.0.1:7000

0-1364 5461-6826 10923-12287
1365      1366     1365

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面的操作与上面的操作类似,把7006节点上这两部分槽位5461-6826 10923-12287分别分给7001节点、7002节点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除一个节点

删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点
注意查看一下节点id,对应id之后再进行操作:
redis-cli -p 7000 cluster nodes | grep master
redis-cli -p 7000 cluster nodes | grep slave

redis-trib.rb del-node 127.0.0.1:7006 9b28b4e497cddb64c21007ba3c78abe390b2f950
redis-trib.rb del-node 127.0.0.1:7007 0295a43d6ac09e57233a2511ca68f03be77d20cd
删除之后,这个节点就不能重新加入这个集群了,需要重新搭建,然后在加入集群中
---------------------
设置redis最大内存
config set maxmemory 102400000
---------------------

12. Redis的多API支持

python为例
yum install -y python36 
python3 -V
yum install -y python36-pip
pip3 install redis     #  安装redis驱动,为了让python连接redis
pip3 install redis-py-cluster 	# 安装redis驱动

12.1 Redis单实例连接操作

对redis的单实例进行连接操作

[root@db01 ~]# redis-server /data/6379/redis.conf 

python3
>>>import redis      # 导入驱动
>>>r = redis.StrictRedis(host='10.0.0.61', port=6379, db=0,password='123456')
>>>r.set('a', '1')
>>>r.get('a')

12.2 Redis哨兵连接操作

sentinel集群连接并操作

[root@db01 ~]# redis-server /data/6380/redis.conf
[root@db01 ~]# redis-server /data/6381/redis.conf
[root@db01 ~]# redis-server /data/6382/redis.conf 
[root@db01 ~]# redis-sentinel /data/26380/sentinel.conf &
--------------------------------
## 导入redis sentinel包
>>>from redis.sentinel import Sentinel  
##指定sentinel的地址和端口号
>>>sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1)  
##测试,获取以下主库和从库的信息
>>>sentinel.discover_master('mymaster')  
>>>sentinel.discover_slaves('mymaster')  

12.3 Redis配置读写分离操作

配置读写分离

#写节点
>>>master = sentinel.master_for('mymaster', socket_timeout=0.1,password="123")  
#读节点
>>>slave = sentinel.slave_for('mymaster', socket_timeout=0.1,password="123")  
###读写分离测试   key     
>>>master.set('oldboy', '123')  
>>>slave.get('oldboy')  

13.4 Rediscluster集群连接

python连接rediscluster集群测试

使用

python3
>>>from rediscluster import RedisCluster  
>>> startup_nodes = [{"host":"127.0.0.1", "port": "7000"},{"host":"127.0.0.1", "port": "7001"},{"host":"127.0.0.1", "port": "7002"}]  
### Note: decode_responses must be set to True when used with python3  
>>>rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)  
>>>rc.set("foo", "bar")  
True  
>>> print(rc.get("foo"))  
'bar'

13. Redis概念

13.1 缓存穿透

概念
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。

解决方案
采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。

13.2 缓存雪崩

概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

解决方案
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。

13.3 缓存击穿

概念
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
解决方案
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值