Redis
一、Nosql概述
1、单机MySQL的年代!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3x1zBy5-1619428568636)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210220202018085.png)]
90年代,一个基本的网站访问量一般不会太大,单个数据库完全够用,更多的用的是静态HTML~服务器没有压力,
思考一下,整个网站的瓶颈期是什么?
1、数据量如果太大,一个机器放不下!
2、数据的索引(B+ Tree),一个机器内存也放不下
3、访问量(读写混合),一个服务器承受不了~
只要出现以上的三种情况之一,必须要晋级!
2、Memcached(缓存)+MySQL + 垂直拆分
网站的80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据库的压力,我们可以使用缓存来保证效率!
发展过程:
- 优化数据结构和索引
- 优化文件缓存(IO)
- Memcached( 热 )
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBJbhCjm-1619428568638)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210220203105476.png)]
3、分库分表 + 水平拆分 + MySQL集群
技术和业务都在发展的同时,对人的要求也越来越高!
数据库本质:(读 写)
早年:MyISAM:表锁(形象效率)
转战:lnnodb:行锁
慢慢的开始使用分库分表来解决写的压力!MySQL推出了表分区!(并没有很多公司使用)MySQL的集群,很好满足那个年代的需求)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MRImKI3S-1619428568639)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210224184647933.png)]
二、什么是NoSQL
NoSQL
NoSQL = Not Only SQL(不仅仅是SQL)
- 关系型数据库:表格,行、列
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!
很多数据类型用户的个人信息,社交网络,地理位置。这些数据的存储不需要固定的格式!不需要多余的操作就可以横向扩展的!
1、NoSQL特点
解耦!
- 方便扩展,(数据之间没有关系,方便扩展)
- 大数据量高性能,(Redis 一秒写8万次,读取11万)
- 数据类型多样性的!(不需要事先设计数据库,随取随用。如果数据量十分大的表,很多人就无法设计了)
- 传统的RDBMS和NoSQL
- 传统的 RDBMS
- 结构化组织
- SQL
- 数据库和关系都存在单独的表中
- 严格的一致性
- 基础的事务
- NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- 传统的 RDBMS
2、NoSQL的四大分类
KV键值对
- 新浪:Redis
- 美团:Redis + Tair
- 阿里,百度:Redis + memecache
文档型数据库(bson格式和 json 格式)
- MongoDB(必须掌握)
- MongoDB是一个基于分布式文件存储的数据库,C++ 编写主要用来处理大量的文档
- MongoDB是一个介于关系型数据库和非关系型数据库中的中间产品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库
- ConthDB
列存储数据库
- HBase
- 分布式文件系统
图形关系型数据库
- 不是存图形的,放的关系,比如:朋友圈社交网络,广告推荐!
- Neo4j,InfoGrid
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o9WRMvEA-1619428568640)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210225190240370.png)]
三、Redis入门
概述
Redis是什么?
Redis(Remote Dictionary Server)即远程字典服务!
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oCgatIl7-1619428568641)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226123253052.png)]
免费和开源!是当下最热门的NoSQL技术之一,也被人们称之为结构化数据库
Redus能干嘛?
- 内存存储、持久化、内存中是断电即失,所以说持久化很重要(rdb、aof)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(浏览量)
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
学习中需要用到的东西
官网:https://redis.io/
中文网:https://www.redis.net.cn/
Windows安装
-
下载安装包:https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
-
得到压缩包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfA271RH-1619428568642)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226130802652.png)]
-
解压并打开服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCZGpu13-1619428568644)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226130839839.png)]
-
打开数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dIhF6mBx-1619428568646)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226130918064.png)]
使用redis客户单来链接Redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJaJPnFD-1619428568648)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226131059474.png)]
Window下使用确实简单,但是Redis推荐我们使用Linux去开发
Linux安装
-
从官网下载Linux版本的压缩包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uO3p96ao-1619428568649)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226135311885.png)]
-
放到Linux系统上,解压文件:
tar -zxvf 文件名
得到Redis[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oJld99dB-1619428568650)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226135514164.png)]
-
进入解压后的文件,可以看到Redis的配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTFKsq1w-1619428568652)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226135707155.png)]
-
基本环境安装
yum install gcc-c++
make
make install
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zgak2SkO-1619428568653)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226140410422.png)]
-
redis默认安装路径
usr/local/bin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZoUL5PY-1619428568654)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226141637077.png)]
-
将Redis配置文件复制到当前目录下
cp /usr/redis-6.2.0/redis.conf fconfig
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrLoOhBQ-1619428568655)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226141719696.png)]
-
redis默认不是后台启动的。要修改配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ber8fTrv-1619428568657)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226142208974.png)]
-
启动Redis服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gkfQ1XAl-1619428568658)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226142715200.png)]
-
使用redis-cli进行连接测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yX4NPCnA-1619428568659)(C:\Users\Andy\AppData\Roaming\Typora\typora-user-images\image-20210226142804240.png)]
-
查看Redis的进程是否开启
ps -ef|grep redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x7ETsTMf-1619428568660)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226142958065.png)]
-
如何关闭Redis服务呢?
shutdown
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgW64f12-1619428568662)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226143158354.png)]
-
再次查看进程是否启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLTPSYsA-1619428568663)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210226143302352.png)]
测试性能
redis-bennchmark是一个压力测试工具。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCypkr0b-1619428568664)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210227191355722.png)]
测试:redis-benchmark -h localhost -p 6379 -c 100 -n 100000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m5z7tmbT-1619428568667)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210227192456260.png)]
基本知识
redis默认有16个数据库
可以使用 select
切换数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Un7cVpeG-1619428568668)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210227193823687.png)]
Redis 是单线程的!
Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络的带宽,既然可以使用单线程来实现,所以就使用了单线程。
Redis是C语言写的,官方提供的数据为100000+的QPS,完全不比同样的使用key-value的Memecache差!
- Redis 为什么单线程 还这么快?
-
误区1:高性能的服务器一定是多线程的?
-
误区2:多线程(CPU上下文会切换)一定比单线程效率高!
核心:redis是将所有的数据全部放到内存中的,所以说使用单线程效率就是最高的,因为多线程会让CPU进行上下切换,非常耗时的操作,对于内存系统来说,没有上下文切换 那效率就是最高的,多次读写都是在一个CPU上的 , 在内存情况下,这个就是最佳情况的。
四、五大数据类型
Redis-Key
# 切换数据库
127.0.0.1:6379> select 2
OK
# 查看数据库大小
127.0.0.1:6379[2]> dbsize
(integer) 0
127.0.0.1:6379[2]>
# 查看当前数据库所有的keys
127.0.0.1:6379[2]> keys *
# 清空当前数据库的key
127.0.0.1:6379[2]> flushdb 清空所有:(flushdb)
String(字符串)
###################################################################################################
127.0.0.1:6379> set key1 v1 # 设置值
OK
127.0.0.1:6379> get key1 # 获取值
"v1"
127.0.0.1:6379> exists key1 # 判断某一个值是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello" # 追加字符串,如果当前key不存在,就相当于setkey
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1 # 获取字符串长度
(integer) 7
###################################################################################################
127.0.0.1:6379> incr views # 自增1
(integer) 1
127.0.0.1:6379> incr views # 自增1
(integer) 2
127.0.0.1:6379> get views # 查看
"2"
127.0.0.1:6379> decr views # 自减1
(integer) 1
127.0.0.1:6379> decr views # 自减1
(integer) 0
127.0.0.1:6379> incrby views 10 # 设置增加数量
(integer) 10
127.0.0.1:6379> decrby views 5 # 设置减少数量
(integer) 5
###################################################################################################
127.0.0.1:6379> set key1 aaronsaifei # 设置key1的值
OK
127.0.0.1:6379> get key1
"aaronsaifei"
127.0.0.1:6379> getrange key1 0 3 # 截取查看字符串
"aaro"
127.0.0.1:6379> getrange key1 0 -1 # 查看全部字符串
"aaronsaifei"
###################################################################################################
# 替换
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xxx # 替换指定位置的字符串
(integer) 7
127.0.0.1:6379> get key2
"axxxefg"
###################################################################################################
# setex(set with expire) # 设置过期时间
# setnx(set if not exist) # 此名不存在就设置,存在就不设置,设置成果返回1,设置失败返回0
127.0.0.1:6379> setex key3 30 "hello" # 设置过期时间30秒
OK
127.0.0.1:6379> ttl key3 # 查看剩余时间
(integer) 25
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey "redis" # 如果名字存在就设置,不存在就不设置
(integer) 1
127.0.0.1:6379> setnx mykey "Mongdb"
(integer) 0
127.0.0.1:6379> get mykey
"redis"
###################################################################################################
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时设置多个
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3 # 同时查看多个
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k2 v2 k3 v3 k4 v4 # 同时设置时看是否存在
(integer) 0 # msetnx 是一个原子性的操作,要么一起成功要么一起失败!
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
###################################################################################################
# 对象
set user:1{name:zhangsan,age:3}
# 这里的key是一个巧妙的设计: user:{id}:{filed},如此设计Redis是完全可以的
127.0.0.1:6379> mset user:1:name aaron user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "aaron"
2) "2"
###################################################################################################
# 先get再set getset
127.0.0.1:6379> getset v1 redis
(nil)
127.0.0.1:6379> getset v1 mongedb
"redis"
127.0.0.1:6379> get v1
"mongedb"
数据结构是相同的!
String 类似的使用场景:value除了是我们的字符串还是我们的数字
- 计数器
- 统计多单位的数量
List(集合)
基本的数据类型,是个列表。
在Redis里面,我们可以把list玩成,栈,队列,阻塞队列
注:所有的List命令都是用I开头的
Redis不区分大小写操作!
###############################################################################
# 将一个值或者多个值插入到列表头部(左)
127.0.0.1:6379> lpush list one # 放到列表的左边
(integer) 1
127.0.0.1:6379> lpush list two # 放到列表的左边
(integer) 2
127.0.0.1:6379> lpush list three # 放到列表的左边
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 查看列表的所有值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list right # 放到列表的右边
(integer) 4
127.0.0.1:6379> lrange list 0 -1 # 查看列表的所有值
1) "three"
2) "two"
3) "one"
4) "right"
##########################################################################
# 移入移除
127.0.0.1:6379> lpop list # 从左侧删除一个值
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list # 从右侧删除一个值
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
#############################################################################
# 查看列表中的值
127.0.0.1:6379> lindex list 0 # 通过下标获取某一个值
"two"
127.0.0.1:6379> lindex list 1 # 通过下标获取某一个值
"one"
#############################################################################
# 查看列表的长度
llen
127.0.0.1:6379> llen list
(integer) 3
#############################################################################
# 移除指定的值
取关 uid
127.0.0.1:6379> lrem list 1 one # 删除list中1个one
(integer) 1
#############################################################################
# 修剪:trim
127.0.0.1:6379> Rpush mylist "hello0"
(integer) 1
127.0.0.1:6379> Rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> Rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> Rpush mylist "hello3"
(integer) 4
127.0.0.1:6379> Rpush mylist "hello4"
(integer) 5
127.0.0.1:6379> ltrim mylist 1 2 # 通过下标截取指定的长度,但是List已经被改变了
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
#############################################################################
# 移除列表的最后一个元素,并将它添加到一个新的元素rpoplpush
127.0.0.1:6379> lrange mylist 0 -1 # 查看 mylist 列表
1) "hello0"
2) "hello1"
3) "hello2"
127.0.0.1:6379> rpoplpush mylist myotherlist # 截取列表最后一个到新列表中
"hello2"
127.0.0.1:6379> keys *
1) "myotherlist"
2) "mylist"
127.0.0.1:6379> lrange mylist 0 -1 # 查看 mylist 列表
1) "hello0"
2) "hello1"
127.0.0.1:6379> lrange myotherlist 0 -1 # 查看 myotherlist 列表
1) "hello2"
#############################################################################
# 更新:lset,(前提是存在)
127.0.0.1:6379> exists list # 先判断值是否存在
(integer) 0
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lset list 0 item # 更新已存在的值的具体下表,前提需要存在
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
#############################################################################
# 将某个具体的值 插入某个列表中的后面或者前面:linsert before/after
127.0.0.1:6379> LINSERT mylist before word astir # “word”前面插入新值“astir”
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "astir"
3) "word"
127.0.0.1:6379> linsert mylist after word new # “word”后面插入新值“new”
(integer) 4
小结
- List实际上是一个列表,在before和after,left,reight都可以插入值。
- 如果key不存在,创建新的列表,如果key存在,就是新增内容。
- 如果移除了所有的值,空链表,也代表不存在。
- 在两边插入或者改动值效率最高!,执行中间元素,效率会低一点点~
可以用List来做消息排队!消息队列(Lpush Rpop), 栈(Lpush Lpop)
Set(无序集合)
Set中的值是不能重复的
属于无序不重复集合
#############################################################################
127.0.0.1:6379> sadd myset hello # set集合中添加值
(integer) 1
127.0.0.1:6379> sadd myset aaron # set集合中添加值
(integer) 1
127.0.0.1:6379> sadd myset aaronstduy # set集合中添加值
(integer) 1
127.0.0.1:6379> smembers myset # 查看指定set的所有值
1) "aaronstduy"
2) "hello"
3) "aaron"
127.0.0.1:6379> SISMEMBER myset hello # 判断指定set的某一个值是不是在集合中
(integer) 1
127.0.0.1:6379> sismember myset as
(integer) 0
#############################################################################
127.0.0.1:6379> scard myset # 查个数
(integer) 2
127.0.0.1:6379> srem myset hello # 移除指定元素
(integer) 1
#############################################################################
127.0.0.1:6379> srandmember myset # 随机抽选一个值
"hello"
#############################################################################
# 随机删除值
127.0.0.1:6379> spop myset
"hellosd"
127.0.0.1:6379> smembers myset
1) "hello"
2) "aaron"
#############################################################################
# 将一个指定的值,移动到另外的set集合中
127.0.0.1:6379> smove set2 myset2 hello
(integer) 1
#############################################################################
微博、B站、共同关注(并集)
数字集合类、
- 差集
- 交集
- 并集
127.0.0.1:6379> SDIFF key1 key2 # sdiff: 差集,以第一为准与第二个对比不同
1) "a"
2) "b"
127.0.0.1:6379> SINTER key1 key2 # sinter: 交集,显出两个集合中相同的值
1) "c"
127.0.0.1:6379> SUNION key1 key2 # sunion: 并集,查看所有。
1) "b"
2) "c"
3) "e"
4) "d"
5) "a"
微博和B站,A用户将所有关注的人放在一个set集合中、!将粉丝也放到一个集合中。
共同关注可以用(sinter)交集
Hash(哈希)
Map集合,key-value! 这个值是一个Map集合,
本质和String类型没有太大区别,还是一个简单的key-value
#############################################################################
127.0.0.1:6379> hset myhash fidld1 aaron # 创建Hash集合 key-value
(integer) 1
127.0.0.1:6379> hget myhash fidld1 # 通过key获取value值
"aaron"
127.0.0.1:6379> hmset myhash fidld1 hello fidld2 world # 一次设置多个key-value(重复直接覆盖掉)
OK
127.0.0.1:6379> keys *
1) "myhash"
127.0.0.1:6379> hmget myhash fidld1 fidld2 # 一次查看多个值,通过key来查看
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash # 查看集合中所有的值
1) "fidld1"
2) "hello"
3) "fidld2"
4) "world"
#############################################################################
127.0.0.1:6379> hdel myhash fidld1 # 删除值
(integer) 1
127.0.0.1:6379> hgetall myhash # 查看值
1) "fidld2"
2) "world"
127.0.0.1:6379> hlen myhash # 获取hash表的字段数量
(integer) 3
#############################################################################
127.0.0.1:6379> HEXISTS myhash fiksd # 判断hash中指定字段是否存在!
(integer) 0
127.0.0.1:6379> hexists myhash fidld3 # 判断hash中指定字段是否存在!
(integer) 1
#############################################################################
127.0.0.1:6379> hkeys myhash # 获取hash表中所欲的key
1) "fidld2"
2) "fidld1"
3) "fidld3"
127.0.0.1:6379> hvals myhash # 获取hash表中所有values
1) "wordl"
2) "hello"
3) "hes"
#############################################################################
# 自增:incr 自减:decr
127.0.0.1:6379> hset myhash fidld4 5
(integer) 1
127.0.0.1:6379> HINCRBY myhash fidld4 1 # 自增
(integer) 6
127.0.0.1:6379> hdecrby myhash fidld4 1 # 自减,可以用自增设置-1,与自减效果相同
(integer) 5
#############################################################################
127.0.0.1:6379> hsetnx myhash fidld4 hello # 如果存在创建成功
(integer) 0
127.0.0.1:6379> hsetnx myhash fidld6 hello # 如果失败创建失败
(integer) 1
关于Hash的应用
hash变更的数据 user name age,尤其是用户信息之类的,和经常变动的信息。
Hash更适合存储对象,String 更加适合字符串的存储。
Zset(有序集合)
在Set的基础上,增加了一个值,
#############################################################################
127.0.0.1:6379> zadd myset 1 one # 添加值
(integer) 1
127.0.0.1:6379> zadd myset 2 two # 添加值
(integer) 1
127.0.0.1:6379> zadd myset 3 three # 添加值
(integer) 1
127.0.0.1:6379> zrange myset 0 -1 # 查看所有集合中的值
1) "one"
2) "two"
3) "three"
#############################################################################
# 排序
127.0.0.1:6379> zadd salary 2500 xiaohong # 创建值,keys用数值
(integer) 1
127.0.0.1:6379> zadd salary 3000 zhangsan # 创建值,keys用数值
(integer) 1
127.0.0.1:6379> zadd salary 500 aaron # 创建值,keys用数值
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf # 排序 从小到大
1) "aaron"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrevrange salary 0 -1 # 排序 从大到小
1) "zhangsan"
2) "xiaohong"
3) "aaron"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores # 排序,带上值
1) "aaron"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "3000"
127.0.0.1:6379> zrangebyscore salary -inf 2000 withscores # 排序从最小值到指定的值()带成绩
1) "aaron"
2) "500"
#############################################################################
# 移除rem中的元素
127.0.0.1:6379> zrange salary 0 -1 # 查看值
1) "aaron"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1 # 查看值
1) "aaron"
2) "zhangsan"
127.0.0.1:6379> zcard salary # 获取有序集合中的个数
(integer) 2
#############################################################################
127.0.0.1:6379> zcount myset 1 3 # 获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2 # 获取指定区间的成员数量
(integer) 2
Zset的案例思路:set排序,存储班级成绩表,工资表排序!(普通消息设置成1,重要消息设置成2,带权重进行判断。)
五、三种特殊数据类型
geospatial地理位置
朋友的定位,附近的人,打车距离计算?
Redis的Geo在Redis3.2版本就推出了,这个功能可以推算地理位置的信息,比如两个地方的距离,方圆几里的人
经纬度查询网址:https://jingweidu.bmcx.com/
只用六个命令
- GEOADD
- GEODIST
- GEOHASH
- GEOPOS
- GEORADIUS
- GEORADIUSBYNEMBER
所有的数据都应该录入:china:city,才会让结果更加请求!
# getadd 添加地理位置
#######################################################################################
# 两极无法直接添加,我们一般会下载城市数据,直接通过Java程序一次性导入
# 参数 key 值(纬度、经度、名称)
127.0.0.1:6379> geoadd china:city 36.61853 114.49339 handan
(error) ERR invalid longitude,latitude pair 36.618530,114.493390
# 有效的经度从-180度到180度
# 有效的纬度从-85.05112878度到85.05112878度
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误
127.0.0.1:6379> geoadd china:city 114.49339 36.61853 handan
(integer) 1
127.0.0.1:6379> geoadd china:city 114.53952 38.03647 shijiazhuang
(integer) 1
127.0.0.1:6379> geoadd china:city 114.507132 37.06787 xingtai
(integer) 1
127.0.0.1:6379> geoadd china:city 116.23128 40.22077 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.48941 31.40527 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 120.21201 30.2084 hangzhou
(integer) 1
# geopos-从key里返回所有给定位置元素的位置(经纬度)
#######################################################################################
127.0.0.1:6379> geopos china:city beijing handan # 查看两地的经纬度
1) 1) "116.23128265142440796"
2) "40.22076905438526495"
2) 1) "114.49338823556900024"
2) "36.61852980102143817"
127.0.0.1:6379> geopos china:city beijing # 查看一个地区的经纬度
1) 1) "116.23128265142440796"
2) "40.22076905438526495"
# geodist 命令-返回两个给定位置之间的距离
#######################################################################################
两人之间的距离!单位:
- m 表示单位为米
- km 表示单位为千米
- mi 表示单位为英里
- ft 表示单位为英尺
127.0.0.1:6379> geodist china:city beijing shanghai km # 查看北京到上海的具体,单位为:km
"1088.6444"
# georadius 命令-以给定经纬度为中心,找出某一半径内的元素
#######################################################################################
127.0.0.1:6379> georadius china:city 110 30 1000 km # 以 110 30 这个经纬度为中心,寻找房源1000km内的城市
1) "hangzhou"
2) "handan"
3) "xingtai"
4) "shijiazhuang"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist # 显示到中间距离的位置
1) 1) "hangzhou"
2) "982.5796"
2) 1) "handan"
2) "846.1122"
3) 1) "xingtai"
2) "889.9896"
4) 1) "shijiazhuang"
2) "986.6274"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord # 显示他人的定位信息
1) 1) "hangzhou"
2) 1) "120.21200805902481079"
2) "30.20839995425554747"
2) 1) "handan"
2) 1) "114.49338823556900024"
2) "36.61852980102143817"
3) 1) "xingtai"
2) 1) "114.50713187456130981"
2) "37.06786995982209731"
4) 1) "shijiazhuang"
2) 1) "114.53952223062515259"
2) "38.03647056060376741"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist withcoord count 2 # 筛选除指定的结果
1) 1) "handan"
2) "846.1122"
3) 1) "114.49338823556900024"
2) "36.61852980102143817"
2) 1) "xingtai"
2) "889.9896"
3) 1) "114.50713187456130981"
2) "37.06786995982209731"
# georadiusbymember-命令 找出位于指定范围内的元素,中心点是由给定的元素决定的
###################################################################################
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km # 找出位于指定元素周围的内容
1) "handan"
2) "xingtai"
3) "shijiazhuang"
4) "beijing"
# geohash 命令 - 返回一个或多个位置元素的geohash表示
###################################################################################
127.0.0.1:6379> geohash china:city beijing handan #将二维的经纬度,转换成一维的字符串(如果两个字符串越接近,说明越近)
1) "wx4sucvncn0"
2) "ww92m65w3k0"
####################################################################################
127.0.0.1:6379> zrange china:city 0 -1 # 查看地图中全部元素
1) "hangzhou"
2) "shanghai"
3) "handan"
4) "xingtai"
5) "shijiazhuang"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "hangzhou"
2) "shanghai"
3) "handan"
4) "xingtai"
5) "shijiazhuang"
hyperloglog
什么是基数?
A(1、3、5、7、8、7)
B(1、3、5、7、8)
基数为:5
不重复的元素, 可以接受误差!
简介
Redis 2.8.9版本就更新了Hyperloglog数据结构!
Redis Hyperloglog 基数统计的算法!
**优点:**占用的内存是固定的,2^64不同的元素的技术,只需要12KB的内存!如果要从内存角度来比较的话, Hyperloglog首选!
网页的UV(一个人访问一个网站多次,但还是算作是一个人!)
传统的方式。set保存用户的id,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户ID,就会比较麻烦。我们的目的是为了计数,而不是保存用户ID。
0.81%的错误率!统计uv任务,可以忽略不计!
测试使用
######################################################################################
127.0.0.1:6379> pfadd mykey a b c d e f g h i g k m i # 创建第一组元素
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计 第一组元素的数量,相同的只数一次
(integer) 11
127.0.0.1:6379> pfadd mykey2 i j z x c v b n m # 创建第二组元素
(integer) 1
127.0.0.1:6379> pfcount mykey2 # 统计 第二组元素的数量,相同的只数一次
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并成新的:mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 # 统计新的 mykey3
(integer) 16
如果允许容错:那么一定可以使用 Hyperloglog!
如果不允许容错:就使用 set 或者自己的数据类型即可!
Bitmaps
位存储
统计用户信息,活跃、不活跃,登录,未登录,打卡,365打卡,两个状态的都可以用 Bitmaps!
Bitmaps 位图,数据结构!都是操作二进制位进行记录,就只有进行 0 和 1 两个状态!
365天 = 365 bit 1字节 = 8 bit 46个字节左右!
测试使用 使用Bitmap 来记录 周一到周日的打卡!
周一:0 周二:1 周三:0 周四:1 周五:0 周六:0 周日:1
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
########################################################################################
# 查看某一天是否打卡
127.0.0.1:6379> getbit sign 1 # 查看星期一是否打卡
(integer) 0
127.0.0.1:6379> getbit sign 4 # 查看星期四是否打卡
(integer) 1
127.0.0.1:6379>
########################################################################################
# 统计打卡的天数
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录就可以看到是否有全勤。
(integer) 3
六、事务
**Redis单条命令式保存原子性,但是事务不保证原子性。**要么同时成功要么同时失败(原子性)!
所有的命令在事务中,并没有直接被执行。只有执行命令的时候才会执行!Exec
Redis事务没有隔离级别的概念!
事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性,顺序性,排他性!执行一些列的命令!
redis的事务:
- 开启事务( multi )
- 命令入队( … )
- 执行事务( exec )
正常执行事务
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k2 v2 # 命令入队
QUEUED
127.0.0.1:6379(TX)> get k2 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k3 v3 # 命令入队
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK
放弃事务
127.0.0.1:6379(TX)> set k1 v1 # 开启事务
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD # 取消事务
OK
编译型异常(代码有问题!命令有错!)事务中所有的命令都不会被执行!
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3 # 错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec # 编译型错误,所有命令都不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k2
(nil)
运行时异常(1/0)如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的。错误命令抛出异常!
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> incr k1 # 错误命令
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) (error) ERR value is not an integer or out of range # 报错,因为是语法错误,所以其他可以执行。
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
监控!Watch(面试常问!)
悲观锁
- 很悲观,什么时候都会出问题,无论做什么都会加锁。
乐观锁
- 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候 去判断一下,在此期间是否有人修改过这个数据。
- 获取 version
- 更新的时候比较 version
Redis 监视测试
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视 money 对象
OK
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用 watch 可以当做 redis 的乐观锁操作!
127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec # 执行之间,另外一个线程修改money的值,这个时候正常执行就会执行失败。
(nil)
####################################################################################
# 如果发现事务执行失败,就先解锁(unwatch)
127.0.0.1:6379> unwatch # 解锁
OK
127.0.0.1:6379> watch money # 加锁(获取最新的值,再去加锁)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec # 执行(比对监视的值是否发生了变化。发生变化执行失败,没有变化 执行成功)
1) (integer) 990
2) (integer) 30
127.0.0.1:6379>
七、Jedis
我们要使用 Java 来操作 Redis
什么是 jedis 是官方推荐 java 连接开发工具!使用 Java 操作 Redis 中间件!如果你要使用 Java 来操作 redis ,那么一定要对 Jedis 十分的熟悉。
测试
<dependencies>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
编码测试
- 连接数据库
public class TestPing {
public static void main(String[] args) {
// 1、new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
// Jedis 所有的命令就是之前学习的所有指令
System.out.println(jedis.ping());
}
}
输出:PONG
-
常用的API
- String
- List
- Set
- Hash
- Zset
所有的API命令和上面的命令是一样的。
- 操作命令
- 断开连接.
事务测试
public class TestTX {
public static void main(String[] args) {
// 创建连接
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.flushDB(); // 清除数据
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","word");
jsonObject.put("name","aaron");
// 开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
// 开启乐观锁
jedis.watch(result);
try {
multi.set("user1",result);
multi.set("user2",result);
multi.set("user3",result);
int i = 1/0;
multi.exec();
}catch (Exception exception){
multi.discard(); // 放弃事务
exception.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
System.out.println(jedis.get("user3"));
jedis.close(); // 关闭连接
}
}
}
八、SpringBoot整合
SpringBoot 操作数据,spring-data jpa jdbc mongdb redis!
SpringData 也是和 SpringBoot 起名的项目
说明:在 SpringBoot2.x之后,原来使用的Redis被替换为了lettuce?
jedis: 采用的是直连,多个线程操作的话,是不安全的,如果想要避免不安全的,就使用 jedis pool连接池! 更像 BIO 模式
lettuce: 采用 netty, 实例可以再多个线程中进行共享,不存在线程不安全的情况。可以减少线程数量, 更像 NIO 模式
源码分析
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") //我们可以自己定义一个redisTemplate来替换这个默认的!
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 默认的RedisTemplate 没有过多的设置,redis 对象都是需要序列化的!
// 两个类型都是Object类型,后面需要强制转化
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean // 由于 String 是Redis中最常使用的类型,所以单独提出来了一个Bean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合测试
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置连接
# 配置Redis spring.redis.host=127.0.0.1 spring.redis.port=6379
-
测试!
// 获取连接对象 RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); connection.flushDb(); // opsForValue() 操作字符串 // opsForList() 操作List // opsForHash() // opsForSet() // opsForZSet() // opsForGeo() // opsForHyperLogLog() redisTemplate.opsForValue().set("name","Aaron"); System.out.println(redisTemplate.opsForValue().get("name"));java
对象做值
对象类必须序列化
创建对象User
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private String name;
private int age;
}
进行测试
@Test
public void tests() throws JsonProcessingException {
// 真实开发一般都使用json来传递对象
User name = new User("aaron", 3);
String jsonUser = new ObjectMapper().writeValueAsString(name);
redisTemplate.opsForValue().set("user",jsonUser);
System.out.println(redisTemplate.opsForValue().get("user"));
}
自定义RedisTemplate
@Configuration
public class RedisConfig {
// 编写我们自己的redisTemplate 这是一个固定的模板
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 为了自己开发方便,一般直接使用<String Object>
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 序列化配置
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
// 字符串的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(serializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
Redis配置文件详解
启动的时候,就通过配置文件来启动!
单位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKhnRj83-1619428568671)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305111038919.png)]
- 配置文件 unit单位对大小写不敏感
包含
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeA1OL1Q-1619428568672)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305111350090.png)]
- 就好比我们学习Spring、Import、include
网路配置
bind 127.0.0.1 # 绑定的IP
protected-mode yes # 保护模式
port 6379 # 端口设置
通用 GENERAL
daemonize yes # 以守护方式运行,默认是 no,我们需要自己开启为 yes
pidfile /var/run/redis_6379.pid # 如果以后台的方式运行,我们需要指定一个 pid 文件!
# 日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice #日志的文件位置名。
databases 16 # 数据库的数量
always-show-logo no # 是否总是显示Logo
SNAPSHOTTING 快照
持久化,在规定的时间内执行了多久的操作,则会执行持久化的文件 .rdd文件 .aof文件
Redis 是内存数据库,如果没有持久化,那么就会断电及失!
save 3600 1 # 如果900秒内 至少有1个key进行了修改,那我们就进行持久化操作
save 300 100 # 如果300秒内 至少有100个key进行了修改,那我们就进行持久化操作
save 60 10000 # 如果60秒内 至少有10000个key进行了修改,那我们就进行持久化操作
# 我们之后学习持久化,会自己定义这个测试
stop-writes-on-bgsave-error yes # 持久化如果出错了,是否还需要继续进行工作。
rdbcompression yes # 是否压缩 rdb文件,需要消耗一些CPU的资源
rdbchecksum yes # 保存rdb文件的时候,进行检查校验
dir ./ # rdb 保存的目录
主从复制 REPLICATION
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQvTmHaV-1619428568673)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305201731733.png)]
填写主机的IP和端口:replicaof <masterip> <masterport>
填写主机的密码:masterauth <master-password>
安全 SECURITY
127.0.0.1:6379> config set requirepass "123456" # 设置密码
127.0.0.1:6379> auth 123456 # 登录
127.0.0.1:6379> config get requirepass # 查看
1) "requirepass"
2) "123456"
限制 CLIENTS
maxclients 10000 # 设置能连接redus最大的客户端的数量
maxmemory <bytes> # redis 配置最大的内存容量
maxmemory-policy noeviction # 内存到达上限的策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DDl1ymjb-1619428568674)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305114759870.png)]
APPEND ONLY MODE模式 aof配置
appendonly no # 默认不开启aof模式的,因为默认是rdb方式持久化的,在大部分所有的情况下,rdb完全够用了!
appendfilename "appendonly.aof" # 持久化文件的名字。
# appendfsync always # 每次修改都会同步,消耗性能
appendfsync everysec # 每秒执行一次 sync 可能会丢失1秒的数据!
# appendfsync no # 不同步,sync 这个时候操作系统自己同步数据,速度是最快的
具体的配置,我们在Redis持久化中去给大家详细的讲解。
Redis持久化
面试和工作。持久化都是重点!
Redis 是内存数据库,如果不将内存中的数据库状态保持到磁盘,那么一旦服务器进程退出,服务器的数据库状态也会消失,所以Redis提供了持久化功能!
RDB( Redis DataBase)
什么是RDB
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWWh24f0-1619428568675)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305115817304.png)]
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是将的Snapshot快照,他回复时是将快照文件直接读到内存中,
Redis会单独创建(fork)一个子进程来进行持久化,会现将数据写入到一个临时文件夹中,待持久化过程快结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行任何IO操作,这就确保了极高的性能,如果需要进行大规模的恢复,且对数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加高效,RDB的缺点是最后一次持久化后的数据可能丢失,我们默认的就是RDB,一般情况下不需要修改这个配置
rdb保存的文件是dump.rdb
有时候在生产环境我们会将这个文件进行备份!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugZRnw1o-1619428568677)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305120728902.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Agp9DpTa-1619428568678)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305120904834.png)]
测试:只要60秒内修改了5次key,就会触发 rdb 操作
触发机制
- save的规则满足的情况下,会自动触发rd规则
- 执行flushdb命令,也会触发我们的rdb规则
- 退出redis,也会产生 rdb 文件!
备份就自动生成一个dump.rdb文件
如何恢复rdb文件
-
只需要将rdb文件放到我们的redis启动目录下就可以了,redis启动的时候就会自动检查dump.rdb 恢复其中的数据。
-
查看需要存放的位置
127.0.0.1:6379> config get dir 1) "dir" 2) "/usr/local/bin" # 如果在这个目录下存在 dump,rdb 文件,启动就自动回复其中的数据
几乎就他自己的默认配置就够用了,但是我们还是需要学习
优点:
- 适合大规模的数据恢复
- 如果你对数据的完整性要求不高
缺点:
- 需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有了
- fork进程的时候,会占用一定的内存空间!
AOF(Append Only File)
将我们的所有命令都记录下来,history,恢复的时候就把这个文件全部再执行一遍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LMgv3JTn-1619428568680)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305174258574.png)]
- 以日志的形式来记录每个写的操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次完成数据的恢复工作。
- AOF保存的是 appendonly.aof 文件
appendonly
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqwozQjR-1619428568682)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305174840217.png)]
默认不开启,我们需要手动进行配置!只需要appendonly改为yes就开启了aof.cong
从起就可以生效aof文件
-
如果 aof 配置文件有错,这个时候 Redis 是启动不起来的,我们需要修复这个 aof 文件
-
redis 给我们提供了一个工具
redis-check-aof
- 修复命令
redis-check-aof --fix appendonly.aof
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMQ81VVN-1619428568683)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305180432193.png)]
- 修复命令
-
如果文件正常,重启就可以直接恢复了
重写规则
aof 默认就是文件的无痕追加,文件就会越来越大!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7b4YdELb-1619428568685)(C:\Users\Andy\AppData\Roaming\Typora\typora-user-images\image-20210305181237701.png)]
如果aof文件大于64m,太大了! fork一个新的进程来将我们的文件进行重写!
优点和缺点
优点:
appendonly no # 默认不开启aof模式的,因为默认是rdb方式持久化的,在大部分所有的情况下,rdb完全够用了!
appendfilename "appendonly.aof" # 持久化文件的名字。
# appendfsync always # 每次修改都会同步,消耗性能
appendfsync everysec # 每秒执行一次 sync 可能会丢失1秒的数据!
# appendfsync no # 不同步,sync 这个时候操作系统自己同步数据,速度是最快的
- 每一次修改都同步,文件的完整性会更加好
- 每秒同步一次,可能会丢失一秒的数据
- 从不同步,效率最高
缺点:
- 相对于数据文件来说,aof 远远大于 rdb,修复的速度也比 rdb 慢
- Aof 运行效率也要比 rdb 慢,所以我们 redis 默认的配置就是 rdb 持久化!
Redis 发布订阅
Redis 发布订阅 (pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
Redis 客户端可以订阅任意数量的频道
订阅/发布消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w471F8iN-1619428568686)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305182223924.png)]
命令
这些命令被广泛用于构建即使通信应用。比如网络聊天室和实时广播,实时提醒等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KyWujH4-1619428568687)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305182631675.png)]
测试
订阅端:
127.0.0.1:6379> SUBSCRIBE aaron
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "aaron"
3) (integer) 1
# 等待读取推从的信息
1) "message" # 消息
2) "aaron" # 哪个频道的消息
3) "hello aaron" # 消息的具体内容
1) "message"
2) "aaron"
3) "hello redis"
发送端:
127.0.0.1:6379> PUBLISH aaron "hello aaron" # 发布者发送消息到频道
(integer) 1
127.0.0.1:6379> PUBLISH aaron "hello redis" # 发布者发送消息到频道
(integer) 1
原理
Redis 是使用C实现的,通过分析Redis源码里的 pubsub.c文件,了解发布和订阅机制的底层实现,加深对Redis 的理解
Redis 通过 PUBLISH、SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能。
通过SUBSCRIBE 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个 channel,而字典的值是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE 命令的关键,就是将客户端添加给定 channel 的订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub从字面上理解就是发布(Publish)与订阅(Subscribe), 在 Redis 中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到响应的消息,这一功能最明显的用法就是做信息系统,比如普通的即时聊天,群聊等功能。
使用场景
- 实时消息系统!
- 实时聊天(频道当作聊天室,将信息会先给所有人即可!)
- 订阅,关注系统都是可以的!
稍微复杂的场景我们就是使用消息队列 中间件 MQ
Redis 主从复制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OYHWLq8e-1619428568689)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305191324035.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1CcB6kG-1619428568690)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305191352287.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBXmDvc6-1619428568691)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305191403946.png)]
主从复制,读写分离! 80%的情况下都是在进行操作! 减缓服务器的压力! 架构中经常使用!一主二从!只要在公司中,主从复制就是必须要使用的,因为真实的项目中不可能单机使用Redis
环境配置
只配置从库,不用配置主库!
查看当前库的信息
127.0.0.1:6379> info replication # 查看当前库的信息
# Replication
role:master # 角色 master
connected_slaves:0 # 没有丛机
master_failover_state:no-failover
master_replid:08ef2c2ae04cedb5b9be631f86f876e2bedf3b4a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
复制3个配置文件修改对应的信息
- 端口
- pid名字
- log文件名字
- dump.rdb 名字
修改完毕之后,启动3个集群,可以通过进程信息查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-asHJZ1FV-1619428568692)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305194013684.png)]
一主二从
**默认情况下,每台 Redis 服务器都是主节点;**一般情况下只用配置从机就好了,
一主(79)二从(80)
在从机进行配置
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # 认主机
OK
127.0.0.1:6380> info replication # 查看信息
# Replication
role:slave # 当前角色是从机
master_host:127.0.0.1 # 可以看到主机的信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:42
slave_priority:100
slave_read_only:1
connected_slaves:0
master_failover_state:no-failover
master_replid:3f99c112eafd5cdb2d39ef4738c95daf70eb8ef8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
在主机查看
127.0.0.1:6379> info replication
# Replication
role:master # 当前是主机
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=84,lag=1 # 查看从机,6380 状态为在线!
master_failover_state:no-failover
master_replid:3f99c112eafd5cdb2d39ef4738c95daf70eb8ef8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
如果两个都配置完了,是有两个从机的
真正的主从配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,命令是暂时的!
细节
主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存!
主机写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chaCDPWx-1619428568693)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305213144810.png)]
从机读,不能写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DdJw4Qq0-1619428568695)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210305213219499.png)]
测试:主机断开连接,从机依旧连接到主机的,但是依旧没有写的操作,这个时候,主机如果回来了,从机依旧可以直接取到主机写的信息!
如果是使用命令行 配置的主从,如果重启就会变会主机。只要变回从机,立马就会从主机中获取值!
复制原理
Slave 启动成功连接到 master 后会发送一个sync命令
Master 接到命令,启动后台的存盘进程,同时收集所有接收的用于修改数据的命令,在后台进程执行完毕后,master将传送整个数据文件到slave,并完成一次完全同步
- 全量复制:而slave 服务在接收到数据库文件的数据后,将其存盘并加载到内存中。
- 增量复制:Master 继续将新的所有手机到的修改的命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
层层链路
上一个M链接下一个S
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mcC6x2GQ-1619428568697)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210306101704320.png)]
如果没有老大,这时候能不能自己选择一个做老大呢?
如果主机断开,可以使用此命令可以上自己变成主机,其他节点就可以手动连接到最新的这个主节点(手动)
SLAVEOF no one
如果这个时候老大回来了,那就会重新连接。
哨兵模式(自动选举)
概念
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务器不可用,这不是一种推荐的方式,更多的时候,我们优先考虑哨兵模式,Redis从2.8开始正式提供Sentinel(哨兵)构架来解决这个问题
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票自动将从库转换为主库。
哨兵模式是一种特殊的模式。首先Redis提供了哨兵模式的命令。哨兵是一个独立的进程,作为进程。它会独立运行,其原理是哨兵通过发过来的命令,等待Redis服务器响应。从而监控运行的多个Redis实例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-era75OhH-1619428568698)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210306185148824.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hX72HYNg-1619428568699)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210306185220706.png)]
42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
> 在主机查看
```bash
127.0.0.1:6379> info replication
# Replication
role:master # 当前是主机
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=84,lag=1 # 查看从机,6380 状态为在线!
master_failover_state:no-failover
master_replid:3f99c112eafd5cdb2d39ef4738c95daf70eb8ef8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
如果两个都配置完了,是有两个从机的
真正的主从配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,命令是暂时的!
细节
主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存!
主机写
[外链图片转存中…(img-chaCDPWx-1619428568693)]
从机读,不能写
[外链图片转存中…(img-DdJw4Qq0-1619428568695)]
测试:主机断开连接,从机依旧连接到主机的,但是依旧没有写的操作,这个时候,主机如果回来了,从机依旧可以直接取到主机写的信息!
如果是使用命令行 配置的主从,如果重启就会变会主机。只要变回从机,立马就会从主机中获取值!
复制原理
Slave 启动成功连接到 master 后会发送一个sync命令
Master 接到命令,启动后台的存盘进程,同时收集所有接收的用于修改数据的命令,在后台进程执行完毕后,master将传送整个数据文件到slave,并完成一次完全同步
- 全量复制:而slave 服务在接收到数据库文件的数据后,将其存盘并加载到内存中。
- 增量复制:Master 继续将新的所有手机到的修改的命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
层层链路
上一个M链接下一个S
[外链图片转存中…(img-mcC6x2GQ-1619428568697)]
如果没有老大,这时候能不能自己选择一个做老大呢?
如果主机断开,可以使用此命令可以上自己变成主机,其他节点就可以手动连接到最新的这个主节点(手动)
SLAVEOF no one
如果这个时候老大回来了,那就会重新连接。
哨兵模式(自动选举)
概念
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务器不可用,这不是一种推荐的方式,更多的时候,我们优先考虑哨兵模式,Redis从2.8开始正式提供Sentinel(哨兵)构架来解决这个问题
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票自动将从库转换为主库。
哨兵模式是一种特殊的模式。首先Redis提供了哨兵模式的命令。哨兵是一个独立的进程,作为进程。它会独立运行,其原理是哨兵通过发过来的命令,等待Redis服务器响应。从而监控运行的多个Redis实例
[外链图片转存中…(img-era75OhH-1619428568698)]
[外链图片转存中…(img-hX72HYNg-1619428568699)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z79MJaxq-1619428568700)(F:\Y2课程\Y2课程体系\Redis笔记\img\image-20210306185310684.png)]