Redis详解

https://blog.csdn.net/qq_43439968/article/details/110392213

Nosql概述

为什么要用Nosql
我们现在处理什么时代?2020年,大数据时代
我们现在处理什么年代2020年,
压力越来越来越大 适者生存!!一定要逼着自己学习

1,单机 Mysql
在这里插入图片描述

90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够!!
那个时候,更多的去使用静态网页Html-服务器没有太大的压力
考虑一下这种情况下:整个网站的瓶颈是什么?
1 .数据量如果太大,一个机器放不下!!
2. 数据的索引(B+Tree),一个机器内存也放不下
3. 访问量(读写混合),一个服务器承受不了-

只要你开始出现以上的三种情况之一,那么你就需要晋级

Memcached(缓存)+MySql+垂直拆分(读写分离)

网站的80%的情况都是在读,每次都要去查询数据库的话就十分麻烦! 所以说我们就希望减轻数据的压力,我们可以使用缓存来保证效率!
发展过程:优化数据结构和索引–>文件缓存(IO)—》Memcached(当时最热门的技术)/
在这里插入图片描述

3.分库分表+水平拆分+Mysql集群

技术和业务在发展的同时,对人的要求也是越来越高!
本质:数据库就是读和写
早年Mysqm:表锁,十分影响效率!高并发下就会出现很严重的锁的问题
转型Innodb:行锁
User()
慢慢的就开始使用分库分表来解决压力!mysql在那个年代推出了表分区!这个并没有多少公司使用。

Mysql的集群:就满足了那个年代的所有的需求
在这里插入图片描述

4如今最近

技术爆炸:
2010–2020十年之间,世界已经发生了翻天覆地的变化(定位也是一种数据音乐,热榜)
Mysql关系型数据库就不够用了!数量很多,变化的很快~
Mysql有的使用他来组装一些很大的文件 博客,图片!数据库表的很大,小路就低了!如果有一种数据库来专门的处理这种数据
Mysql压力变得就十分小(研究如何让处理这些问题!)大数据的额io压力下,表几乎不能更大

目前的一个基本的互联网项目
在这里插入图片描述

为什么要用Nosql!

用户的个人的信息的,社交网络 ,地理位置,用户自己产生的数据,用户的日志等等爆式的成长!
这时候我们就要是哟个Nosql数据库的,Nosql可以很好的的处理以上的情况

什么Nosql

NoSQL
Nosql=Not Only sql
泛指非关系型数据库的,随着web2.0互联网的诞生 传统的关系型号数据库很难对付web2,0的时代!!尤其式超大规模的高并发的社区!暴露出来很对难以克服的问题 Nosql在当今的 大数据环境下发展的十分迅速 redis是发展的最快的的 而且是我们当下要掌握的一个技术。
.
.
多的数据类型的用户的 个人的信息,社交网络,地理位置,这些数据的类型的存储不需要一个固定的格式!不需要多月的操作就可以横向扩展的!

Map《String,Object》使用键值对的来控制!

  • Nosql的特点
    解耦!
  1. 方便扩展(数据之间没有关系,很好的扩展)
  2. 大数据高性能的(Redis一秒写8万次,读取十一万,弄上去了的缓存记录级,是一种细颗粒度的缓存,性能会比较高!)
  3. 数据类型是多样型的!(不需要事先社会设计数据库!!!随去随用!!如果数据量十分大的表,很多人就无法设计了)
  1. 传统的RDBMS和NoSQL
    传统的RDBMS
    结构化组织
    SQL
    数据和关系都存在单独的表中
    操作 数据的定义语言
    严格的一致性
    基础的事务
    。。。。

Nosql
不仅仅是数据
没有固定的的查询语言
键值对存储,列存储,文档存储,图形数据库存储(社交关系)
最终一致性
CAB定理和BASE
高性能,高可用,高可扩
。。。

在这里插入图片描述
真正在公司中的实践:nosq加RDBMS一起使用才是最强的,阿里巴巴的架构演进
技术没有高低,就看你如何使用(提升内功,,不要在意表面,思维的提升)

Nosql的四大分类

kv键值对

  • 美团:Redis+Tair
  • 新浪Redis
  • 阿里 百度 Redis+memecache

档案里的数据库(bson格式和json一样)

  • MongoDB(一般必须掌握)

    • MongoDB是一个基本分布式文件存贮的数据库,C++编写,主要来处理大量的文档!
    • MongoDB是一个介于关系型数据库和非关系型数据中间的产品!MongoDb是非关系型数据库中功能最丰富的。最像关系型数据库的!!
    • ConthDB
列存储数据库
  • HBase
  • 分布式文件系统
图关系数据库

在这里插入图片描述

  • 他不是村图形的,放的是关系,比如 :朋友圈社交网络,广告推荐!!
  • Ne04 InfoGrid

四者对比
在这里插入图片描述

Redis入门

概述

Redis 是什么?

Redis 就是远程字典服务
是一个开源的使用ANS C语言的编写。支持网络,可给予内存的也可以持久化的日志型,key-value的数据库数据库,并且提供多种的APi
免费和开源的!!是当下的最热门的NoSQl技之一!也被人们称之为结构化数据库!!

Redis能干吗?

  1. 内存存储,持久化,内存中是断电即失,所以说持久化很重要(rdb aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图分析
  5. 计时器 计数器(浏览器)
  6. 。。。。

特性:

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务


官网:https://redis.io/
中文网:http://www.redis.cn/

  1. 下载地址:通过官网下载即可
    在这里插入图片描述
    注意:Wdinow 在 Github上下载(停更很久了)
    Redis推荐都是子在linux服务器上搭建额,我们是基于linux学习

Windows安装

  1. 下载安装包:https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100

  2. 下载完毕后得到压缩包
    在这里插入图片描述
    3.解压到自己的 电脑上的环境目录下的就可以!!

在这里插入图片描述

  1. 开启Redis ,双击运行服务即可
    在这里插入图片描述

  2. 使用redis客户单来连接redis
    在这里插入图片描述
    官方推荐使用redis在linux上使用
    在这里插入图片描述
    在这里插入图片描述

Linux安装

1.下载安装包 http://www.redis.cn/

在这里插入图片描述
下载好后

  1. 用java -verson显示运行的环境如果有问题
    使用 linux命令 vim /etc/profile 编辑运行的环境
    在这里插入图片描述
    source /etc/profile 刷新资源

  2. 下载的解压包放在home文件夹下

cd /home
ls

  1. 解压

移动 压缩包位置 mv redis -5.0.8.tar.gz /opt
cd /opt
ls
tar -zxvf redis -5.0.8.tar.gz
ls
cd redis -5.0.8

  1. 进入解压后的文件,可以看到我们的redis配置文件

在这里插入图片描述

  1. 基本的环境安装、

yum install gcc-c++
yum install gcc-c++ tcl 6.0.6版本的
gcc -v 查看版本信息
执行make命令自动安装配置
执行make install
在这里插入图片描述
在这里插入图片描述

  1. redis 默认安装的路径usr/local/bin
    在这里插入图片描述

6.将redis配置文件,复制到我们当前目录下

在这里插入图片描述

在这里插入图片描述
7.redis默认不是后台启动的,修改配置文件
vim redis.conf

在这里插入图片描述
8.通过指定的配置文件启动redis服务
在这里插入图片描述
9. 使用 redis-cli 进行测试连接
在这里插入图片描述
10. 查看redis进程是否开启

在这里插入图片描述
11. 如何让关闭redis服务器?
在这里插入图片描述
12.再次查看进程是否保存
在这里插入图片描述

  1. 后面我们 会使用单机多Redis启动集群测试!!

测试性能

redis-benchmark是一个压力测试工具!!
官方自带的性能测试工具
redis-benchmark参数命令!!
在这里插入图片描述
简单测试:

# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

结果:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210606201237907.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubm V0L20wXzUyOTQ2MTA0,size_16,color_FFFFFF,t_70)

基础的知识

redis默认有16个数据库
cd /usr/local/bin
ps -ef|grep redis
ls
cd kconfig/
vim redis.conf 可以看到redis默认的数据库有16个

在这里插入图片描述

默认使用的是第0个数据库

可以使用select 进行数据库的切换

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

在这里插入图片描述
清除当前的数据库
清楚全部数据库fushall
在这里插入图片描述

Redis 是单线程的!

明白Redis是很快的 官方表示,Redis是基于内存操作,cpu不是redis性能瓶颈,Reids的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就是用单线程来实现!
所以就使用了单线程!

Redis是c语言的 ,官方的数据为100000+的QPS,完全不比同样是使用的key-vale的Memecache差!

Redis为什么单线程还那么块?
1.误区:高性能的服务器 一定是单线程的?
2. 误区:多线程(CPU上下文切换!)一定比单线程的效率高!
3. CPU>内存>硬盘的速度要有所了解!!
4. 核心:redis是所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程的(cpu上下文会切换:耗时的操作!!),对于内存系统来说,如果没有上下文切换效率就是最高的!!多次读写都是在一个cpu上的,在内存情况下,这个就是最佳的方案!

五大基本数据类型

​ Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

redis-key

在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。

下面学习的命令:

  • exists key:判断键是否存在
  • del key:删除键值对
  • move key db:将键值对移动到指定数据库
  • expire key second:设置键值对的过期时间
  • type key:查看value的数据类型
127.0.0.1:6379> keys * # 查看当前数据库所有key
(empty list or set)
127.0.0.1:6379> set name qinjiang # set key
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> move age 1 # 将键值对移动到指定数据库
(integer) 1
127.0.0.1:6379> EXISTS age # 判断键是否存在
(integer) 0 # 不存在
127.0.0.1:6379> EXISTS name
(integer) 1 # 存在
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> keys *
1) "age"
127.0.0.1:6379[1]> del age # 删除键值对
(integer) 1 # 删除个数
127.0.0.1:6379> set age 20

OK

127.0.0.1:6379> EXPIRE age 15 # 设置键值对的过期时间


(integer) 1 # 设置成功 开始计数

127.0.0.1:6379> ttl age # 查看key的过期剩余时间

(integer) 13

127.0.0.1:6379> ttl age

(integer) 11

127.0.0.1:6379> ttl age

(integer) 9

127.0.0.1:6379> ttl age

(integer) -2 # -2 表示key过期,-1表示key未设置过期时间


127.0.0.1:6379> get age # 过期的key 会被自动delete

(nil)

127.0.0.1:6379> keys *



"name"


127.0.0.1:6379> type name # 查看value的数据类型

string



在这里插入图片描述

String

普通的set、get直接略过。

常用命令及其示例:

APPEND key value: 向指定的key的value后追加字符串
在这里插入图片描述

127.0.0.1:6379> set msg hello 
OK 
127.0.0.1:6379> append msg " world" 
(integer) 11 
127.0.0.1:6379> get msg 
“hello world”

DECR/INCR key: 将指定key的value数值进行+1/-1(仅对于数字)
在这里插入图片描述

127.0.0.1:6379> set age 20 
OK 
127.0.0.1:6379> incr age 
(integer) 21 
127.0.0.1:6379> decr age 
(integer) 20

INCRBY/DECRBY key n: 按指定的步长对数值进行加减

127.0.0.1:6379> INCRBY age 5
(integer) 25 
127.0.0.1:6379> DECRBY age 10 
(integer) 15

INCRBYFLOAT key n: 为数值加上浮点型数值

127.0.0.1:6379> INCRBYFLOAT age 5.2 
“20.2”
STRLEN key: 获取key保存值的字符串长度

127.0.0.1:6379> get msg 
“hello world” 
127.0.0.1:6379> STRLEN msg 
(integer) 11

GETRANGE key start end: 按起止位置获取字符串(闭区间,起止位置都取)

127.0.0.1:6379> get msg 
“hello world” 
127.0.0.1:6379>GETRANGE msg 0 -1 #表示截取的全部值
127.0.0.1:6379> GETRANGE msg 3 9 
“lo worl”

SETRANGE key offset value:用指定的value 替换key中 offset开始的值

127.0.0.1:6379> set msg hello
OK
127.0.0.1:6379> setrange msg 2 hello
(integer) 7
127.0.0.1:6379> get msg
"hehello"
127.0.0.1:6379> set msg2 world
OK
127.0.0.1:6379> setrange msg2 2 ww
(integer) 5
127.0.0.1:6379> get msg2
"wowwd"

GETSET key value: 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
在这里插入图片描述

127.0.0.1:6379> GETSET msg test 
“hello world”
SETNX key value: 仅当key不存在时进行set

127.0.0.1:6379> SETNX msg test 
(integer) 0 
127.0.0.1:6379> SETNX name sakura 
(integer) 1

SETEX key seconds value: set 键值对并设置过期时间
在这里插入图片描述

## setex(set with expire)# 设置过期的时间
# setnx(set if not exist )# 不存在再设置
127.0.0.1:6379> setex name 10 root 
OK 
127.0.0.1:6379> get name 
(nil)

MSET key1 value1 [key2 value2…]: 批量set键值对
在这里插入图片描述
在这里插入图片描述

127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 
OK

MSETNX key1 value1 [key2 value2…]: 批量设置键值对,仅当参数中所有的key都不存在时执行

127.0.0.1:6379> MSETNX k1 v1 k4 v4 
(integer) 0

MGET key1 [key2…]: 批量获取多个key保存的值

127.0.0.1:6379> MGET k1 k2 k3 
1) “v1” 
2) “v2” 
3) “v3”

PSETEX key milliseconds value: 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间

String类似的使用场景:value除了是字符串还可以是数字,用途举例:

  • 计数器
  • 统计多单位的数量:uid:123666:follow 0
  • 粉丝数
  • 对象存储缓存

List

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等。

在这里插入图片描述
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN

  • LPUSH/RPUSH key value1[value2…]从左边/右边向列表中PUSH值(一个或者多个)。
  • LRANGE key start end 获取list 起止元素==(索引从左往右 递增)==
  • LPUSHX/RPUSHX key value 向已存在的列名中push值(一个或者多个)
  • LINSERT key BEFORE|AFTER pivot value 在指定列表元素的前/后 插入value
  • LLEN key 查看列表长度
  • LINDEX key index 通过索引获取列表元素
  • LSET key index value 通过索引为元素设值
  • LPOP/RPOP key 从最左边/最右边移除值 并返回
  • RPOPLPUSH source destination 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部
  • LTRIM key start end 通过下标截取指定范围内的列表
  • LREM key count value List中是允许value重复的 count > 0:从头部开始搜索 然后删除指定的value 至多删除count个 count < 0:从尾部开始- 搜索… count = 0:删除列表中所有的指定value。
  • BLPOP/BRPOP key1[key2] timout 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
  • BRPOPLPUSH source destination timeout 和RPOPLPUSH功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
    代码示例:
    在这里插入图片描述
    在这里插入图片描述

移除指定个数的value
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
rpoplpush # 移除列表的最后一个元素
在这里插入图片描述
lset 将列表中指定下标的值替换为另一个值,更新操作
在这里插入图片描述
将某个具体的value值插入到列中,你指定的元素的前后?
在这里插入图片描述

---------------------------LPUSH---RPUSH---LRANGE--------------------------------
127.0.0.1:6379> LPUSH mylist k1 # LPUSH mylist=>{1}

(integer) 1

127.0.0.1:6379> LPUSH mylist k2 # LPUSH mylist=>{2,1}

(integer) 2

127.0.0.1:6379> RPUSH mylist k3 # RPUSH mylist=>{2,1,3}

(integer) 3

127.0.0.1:6379> get mylist # 普通的get是无法获取list值的

(error) WRONGTYPE Operation against a key holding the wrong kind of value

127.0.0.1:6379> LRANGE mylist 0 4 # LRANGE 获取起止位置范围内的元素



"k2"

"k1"

"k3"

127.0.0.1:6379> LRANGE mylist 0 2

"k2"

"k1"

"k3"

127.0.0.1:6379> LRANGE mylist 0 1

"k2"

"k1"

127.0.0.1:6379> LRANGE mylist 0 -1 # 获取全部元素

"k2"

"k1"

"k3"


---------------------------LPUSHX---RPUSHX-----------------------------------


127.0.0.1:6379> LPUSHX list v1 # list不存在 LPUSHX失败

(integer) 0

127.0.0.1:6379> LPUSHX list v1 v2

(integer) 0

127.0.0.1:6379> LPUSHX mylist k4 k5 # 向mylist中 左边 PUSH k4 k5

(integer) 5

127.0.0.1:6379> LRANGE mylist 0 -1



"k5"

"k4"

"k2"

"k1"

"k3"


---------------------------LINSERT--LLEN--LINDEX--LSET----------------------------


127.0.0.1:6379> LINSERT mylist after k2 ins_key1 # 在k2元素后 插入ins_key1

(integer) 6

127.0.0.1:6379> LRANGE mylist 0 -1



"k5"

"k4"

"k2"

"ins_key1"

"k1"

"k3"

127.0.0.1:6379> LLEN mylist # 查看mylist的长度

(integer) 6

127.0.0.1:6379> LINDEX mylist 3 # 获取下标为3的元素

"ins_key1"

127.0.0.1:6379> LINDEX mylist 0

"k5"

127.0.0.1:6379> LSET mylist 3 k6 # 将下标3的元素 set值为k6

OK

127.0.0.1:6379> LRANGE mylist 0 -1

"k5"

"k4"

"k2"

"k6"

"k1"

"k3"


---------------------------LPOP--RPOP--------------------------


127.0.0.1:6379> LPOP mylist # 左侧(头部)弹出

"k5"

127.0.0.1:6379> RPOP mylist # 右侧(尾部)弹出

"k3"


---------------------------RPOPLPUSH--------------------------


127.0.0.1:6379> LRANGE mylist 0 -1



"k4"

"k2"

"k6"

"k1"

127.0.0.1:6379> RPOPLPUSH mylist newlist # 将mylist的最后一个值(k1)弹出,加入到newlist的头部

"k1"

127.0.0.1:6379> LRANGE newlist 0 -1

"k1"

127.0.0.1:6379> LRANGE mylist 0 -1

"k4"

"k2"

"k6"


---------------------------LTRIM--------------------------


127.0.0.1:6379> LTRIM mylist 0 1 # 截取mylist中的 0~1部分

OK

127.0.0.1:6379> LRANGE mylist 0 -1



"k4"

"k2"


初始 mylist: k2,k2,k2,k2,k2,k2,k4,k2,k2,k2,k2

---------------------------LREM--------------------------


127.0.0.1:6379> LREM mylist 3 k2 # 从头部开始搜索 至多删除3个 k2

(integer) 3


删除后:mylist: k2,k2,k2,k4,k2,k2,k2,k2

127.0.0.1:6379> LREM mylist -2 k2 #从尾部开始搜索 至多删除2个 k2

(integer) 2


删除后:mylist: k2,k2,k2,k4,k2,k2

---------------------------BLPOP--BRPOP--------------------------


mylist: k2,k2,k2,k4,k2,k2

newlist: k1


127.0.0.1:6379> BLPOP newlist mylist 30 # 从newlist中弹出第一个值,mylist作为候选



"newlist" # 弹出

"k1"

127.0.0.1:6379> BLPOP newlist mylist 30

"mylist" # 由于newlist空了 从mylist中弹出

"k2"

127.0.0.1:6379> BLPOP newlist 30

(30.10s) # 超时了


127.0.0.1:6379> BLPOP newlist 30 # 我们连接另一个客户端向newlist中push了test, 阻塞被解决。



"newlist"

"test"

(12.54s)


小结

  • list实际上是一个链表,before Node after , left, right 都可以插入值

  • 如果key不存在,则创建新的链表

  • 如果key存在,新增内容

  • 如果移除了所有值,空链表,也代表不存在

  • 在两边插入或者改动值,效率最高!修改中间元素,效率相对较低

应用:

消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)

Set

Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

Redis中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

  • SADD key member1[member2…] 向集合中无序增加一个/多个成员
  • SCARD key 获取集合的成员数
  • SMEMBERS key 返回集合中所有的成员
  • SISMEMBER key member 查询member元素是否是集合的成员,结果是无序的
  • SRANDMEMBER key [count] 随机返回集合中count个成员,count缺省值为1
  • SPOP key [count] 随机移除并返回集合中count个成员,count缺省值为1
  • SMOVE source destination member 将source集合的成员member移动到destination集合
  • SREM key member1[member2…] 移除集合中一个/多个成员
  • SDIFF key1[key2…] 返回所有集合的差集 key1- key2 - …
  • SDIFFSTORE destination key1[key2…] 在SDIFF的基础上,将结果保存到集合中==(覆盖)==。不能保存到其他类型key噢!
  • SINTER key1 [key2…] 返回所有集合的交集
  • SINTERSTORE destination key1[key2…] 在SINTER的基础上,存储结果到集合中。覆盖
  • SUNION key1 [key2…] 返回所有集合的并集
  • SUNIONSTORE destination key1 [key2…] 在SUNION的基础上,存储结果到及和张。覆盖
  • SSCAN KEY [MATCH pattern] [COUNT count] 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分
    代码示例
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    随机抽取元素
    在这里插入图片描述
    随机删除set集合中的一个元素
    在这里插入图片描述
    将一个指定的值,移动到另一个set集合中

在这里插入图片描述
求俩个集合的差集 交集,以及并集
在这里插入图片描述

---------------SADD--SCARD--SMEMBERS--SISMEMBER--------------------
127.0.0.1:6379> SADD myset m1 m2 m3 m4 # 向myset中增加成员 m1~m4

(integer) 4

127.0.0.1:6379> SCARD myset # 获取集合的成员数目

(integer) 4

127.0.0.1:6379> smembers myset # 获取集合中所有成员



"m4"

"m3"

"m2"

"m1"

127.0.0.1:6379> SISMEMBER myset m5 # 查询m5是否是myset的成员

(integer) 0 # 不是,返回0

127.0.0.1:6379> SISMEMBER myset m2

(integer) 1 # 是,返回1

127.0.0.1:6379> SISMEMBER myset m3

(integer) 1


---------------------SRANDMEMBER--SPOP----------------------------------


127.0.0.1:6379> SRANDMEMBER myset 3 # 随机返回3个成员



"m2"

"m3"

"m4"

127.0.0.1:6379> SRANDMEMBER myset # 随机返回1个成员

"m3"

127.0.0.1:6379> SPOP myset 2 # 随机移除并返回2个成员

"m1"

"m4"


将set还原到{m1,m2,m3,m4}


---------------------SMOVE--SREM----------------------------------------


127.0.0.1:6379> SMOVE myset newset m3 # 将myset中m3成员移动到newset集合

(integer) 1

127.0.0.1:6379> SMEMBERS myset



"m4"

"m2"

"m1"

127.0.0.1:6379> SMEMBERS newset

"m3"

127.0.0.1:6379> SREM newset m3 # 从newset中移除m3元素

(integer) 1

127.0.0.1:6379> SMEMBERS newset

(empty list or set)


下面开始是多集合操作,多集合操作中若只有一个参数默认和自身进行运算

setx=>{m1,m2,m4,m6}, sety=>{m2,m5,m6}, setz=>{m1,m3,m6}

-----------------------------SDIFF------------------------------------


127.0.0.1:6379> SDIFF setx sety setz # 等价于setx-sety-setz
差集


"m4"

127.0.0.1:6379> SDIFF setx sety # setx - sety

"m4"

"m1"

127.0.0.1:6379> SDIFF sety setx # sety - setx

"m5"


-------------------------SINTER---------------------------------------


共同关注(交集)

127.0.0.1:6379> SINTER setx sety setz # 求 setx、sety、setx的交集



"m6"

127.0.0.1:6379> SINTER setx sety # 求setx sety的交集

"m2"

"m6"


-------------------------SUNION---------------------------------------


127.0.0.1:6379> SUNION setx sety setz # setx sety setz的并集



"m4"

"m6"

"m3"

"m2"

"m1"

"m5"

127.0.0.1:6379> SUNION setx sety # setx sety 并集

"m4"

"m6"

"m2"

"m1"

"m5"


Hash(哈希)

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。

  • Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。

  • HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0

  • HMSET key field1 value1 [field2 value2…] 同时将多个 field-value (域-值)对设置到哈希表 key 中。

  • HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。

  • HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。

  • HGET key field value 获取存储在哈希表中指定字段的值

  • HMGET key field1 [field2…] 获取所有给定字段的值

  • HGETALL key 获取在哈希表key 的所有字段和值

  • HKEYS key 获取哈希表key中所有的字段
    HLEN key 获取哈希表中字段的数量

  • HVALS key 获取哈希表中所有值

  • HDEL key field1 [field2…] 删除哈希表key中一个/多个field字段

  • HINCRBY key field n 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段

  • HINCRBYFLOAT key field n 为哈希表 key 中的指定字段的浮点数值加上增量 n。

  • HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。
    代码示例
    删除指定的key字段!对应的value值也就消失了!
    在这里插入图片描述
    获取hash表字段的数量
    在这里插入图片描述
    判断hash中的指定的字段是否存在?
    在这里插入图片描述
    #只获得所有的filed
    #获得所有的value
    在这里插入图片描述
    指定增量以及减少量 以及如果不存在时设置
    在这里插入图片描述

hash变更的数据 user name age ,尤其是用户之类的,经常变动的信息!hash更加适合于对象的存储,String更佳适合字符串的存储!

------------------------HSET--HMSET--HSETNX----------------
127.0.0.1:6379> HSET studentx name sakura # 将studentx哈希表作为一个对象,设置name为sakura
(integer) 1
127.0.0.1:6379> HSET studentx name gyc # 重复设置field进行覆盖,并返回0
(integer) 0
127.0.0.1:6379> HSET studentx age 20 # 设置studentx的age为20
(integer) 1
127.0.0.1:6379> HMSET studentx sex 1 tel 15623667886 # 设置sex为1,tel为15623667886
OK
127.0.0.1:6379> HSETNX studentx name gyc # HSETNX 设置已存在的field
(integer) 0 # 失败
127.0.0.1:6379> HSETNX studentx email 12345@qq.com
(integer) 1 # 成功
----------------------HEXISTS--------------------------------

127.0.0.1:6379> HEXISTS studentx name # name字段在studentx中是否存在

(integer) 1 # 存在

127.0.0.1:6379> HEXISTS studentx addr

(integer) 0 # 不存在


-------------------HGET--HMGET--HGETALL-----------

127.0.0.1:6379> HGET studentx name # 获取studentx中name字段的value

"gyc"

127.0.0.1:6379> HMGET studentx name age tel # 获取studentx中name、age、tel字段的value



"gyc"

"20"

"15623667886"

127.0.0.1:6379> HGETALL studentx # 获取studentx中所有的field及其value

"name"

"gyc"

"age"

"20"

"sex"

"1"

"tel"

"15623667886"

"email"

"12345@qq.com"


--------------------HKEYS--HLEN--HVALS--------------

127.0.0.1:6379> HKEYS studentx # 查看studentx中所有的field



"name"

"age"

"sex"

"tel"

"email"

127.0.0.1:6379> HLEN studentx # 查看studentx中的字段数量

(integer) 5

127.0.0.1:6379> HVALS studentx # 查看studentx中所有的value

"gyc"

"20"

"1"

"15623667886"

"12345@qq.com"


-------------------------HDEL--------------------------

127.0.0.1:6379> HDEL studentx sex tel # 删除studentx 中的sex、tel字段

(integer) 2

127.0.0.1:6379> HKEYS studentx



"name"

"age"

"email"


-------------HINCRBY--HINCRBYFLOAT------------------------

127.0.0.1:6379> HINCRBY studentx age 1 # studentx的age字段数值+1

(integer) 21

127.0.0.1:6379> HINCRBY studentx name 1 # 非整数字型字段不可用

(error) ERR hash value is not an integer

127.0.0.1:6379> HINCRBYFLOAT studentx weight 0.6 # weight字段增加0.6

"90.8"



Zset(有序集合)

在set的基础上,增加了一个值,set k1 v1

zset k1 score1 v1

不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。

score相同:按字典顺序排序
有序集合的成员是唯一的,但分数(score)却可以重复

  • ZADD key score member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
  • ZCARD key 获取有序集合的成员数
  • ZCOUNT key min max 计算在有序集合中指定区间score的成员数
  • ZINCRBY key n member 有序集合中对指定成员的分数加上增量 n
  • ZSCORE key member 返回有序集中,成员的分数值
  • ZRANK key member 返回有序集合中指定成员的索引
  • ZRANGE key start end 通过索引区间返回有序集合成指定区间内的成员
  • ZRANGEBYLEX key min max 通过字典区间返回有序集合的成员
  • ZRANGEBYSCORE key min max 通过分数返回有序集合指定区间内的成员==-inf 和 +inf分别表示最小最大值,只支持开区间()==
  • ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量
  • ZREM key member1 [member2…] 移除有序集合中一个/多个成员
  • ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员
  • ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员
  • ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员
  • ZREVRANGE key start end 返回有序集中指定区间内的成员,通过索引,分数从高到底
  • ZREVRANGEBYSCORRE key max min 返回有序集中指定分数区间内的成员,分数从高到低排序
  • ZREVRANGEBYLEX key max min 返回有序集中指定字典区间内的成员,按字典顺序倒序
  • ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
  • ZINTERSTORE destination numkeys key1 [key2 …] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将score相加作为结果的score
  • ZUNIONSTORE destination numkeys key1 [key2…] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
  • ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)
    代码示例

添加一个值/添加多个值
在这里插入图片描述
排序
在这里插入图片描述

倒序列
127.0.0.1:6379> zrevrangebyscore salary +inf -inf withscores
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
5) "kuangshen"
6) "500"

正序
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "kuangshen"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"

移除rem中的元素 ,移除有序集合中指定的元素的/获取有序集合中 的个数
在这里插入图片描述
获取指定区间的成员变量!
在这里插入图片描述

其余的一些Api,通过饿哦们的学习,你们剩下的如果工作中有需要,这个时候,你们可以查官方文档!!
案列思路:set排序 存储班级成绩表,工资表排序
普通消息,1.重要消息2.带权重进行判断!
排行榜应用实现

-------------------ZADD--ZCARD--ZCOUNT--------------
127.0.0.1:6379> ZADD myzset 1 m1 2 m2 3 m3 # 向有序集合myzset中添加成员m1 score=1 以及成员m2 score=2..
(integer) 2
127.0.0.1:6379> ZCARD myzset # 获取有序集合的成员数
(integer) 2
127.0.0.1:6379> ZCOUNT myzset 0 1 # 获取score在 [0,1]区间的成员数量
(integer) 1
127.0.0.1:6379> ZCOUNT myzset 0 2
(integer) 2
----------------ZINCRBY--ZSCORE--------------------------

127.0.0.1:6379> ZINCRBY myzset 5 m2 # 将成员m2的score +5

"7"

127.0.0.1:6379> ZSCORE myzset m1 # 获取成员m1的score

"1"

127.0.0.1:6379> ZSCORE myzset m2

"7"


--------------ZRANK--ZRANGE-----------------------------------

127.0.0.1:6379> ZRANK myzset m1 # 获取成员m1的索引,索引按照score排序,score相同索引值按字典顺序顺序增加

(integer) 0

127.0.0.1:6379> ZRANK myzset m2

(integer) 2

127.0.0.1:6379> ZRANGE myzset 0 1 # 获取索引在 0~1的成员



"m1"

"m3"

127.0.0.1:6379> ZRANGE myzset 0 -1 # 获取全部成员

"m1"

"m3"

"m2"


testset=>{abc,add,amaze,apple,back,java,redis} score均为0

------------------ZRANGEBYLEX---------------------------------

127.0.0.1:6379> ZRANGEBYLEX testset - + # 返回所有成员



"abc"

"add"

"amaze"

"apple"

"back"

"java"

"redis"

127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 0 3 # 分页 按索引显示查询结果的 0,1,2条记录

"abc"

"add"

"amaze"

127.0.0.1:6379> ZRANGEBYLEX testset - + LIMIT 3 3 # 显示 3,4,5条记录

"apple"

"back"

"java"

127.0.0.1:6379> ZRANGEBYLEX testset (- [apple # 显示 (-,apple] 区间内的成员

"abc"

"add"

"amaze"

"apple"

127.0.0.1:6379> ZRANGEBYLEX testset [apple [java # 显示 [apple,java]字典区间的成员

"apple"

"back"

"java"


-----------------------ZRANGEBYSCORE---------------------

127.0.0.1:6379> ZRANGEBYSCORE myzset 1 10 # 返回score在 [1,10]之间的的成员



"m1"

"m3"

"m2"

127.0.0.1:6379> ZRANGEBYSCORE myzset 1 5

"m1"

"m3"


--------------------ZLEXCOUNT-----------------------------

127.0.0.1:6379> ZLEXCOUNT testset - +

(integer) 7

127.0.0.1:6379> ZLEXCOUNT testset [apple [java

(integer) 3


------------------ZREM--ZREMRANGEBYLEX--ZREMRANGBYRANK--ZREMRANGEBYSCORE--------------------------------

127.0.0.1:6379> ZREM testset abc # 移除成员abc

(integer) 1

127.0.0.1:6379> ZREMRANGEBYLEX testset [apple [java # 移除字典区间[apple,java]中的所有成员

(integer) 3

127.0.0.1:6379> ZREMRANGEBYRANK testset 0 1 # 移除排名0~1的所有成员

(integer) 2

127.0.0.1:6379> ZREMRANGEBYSCORE myzset 0 3 # 移除score在 [0,3]的成员

(integer) 2


testset=> {abc,add,apple,amaze,back,java,redis} score均为0

myzset=> {(m1,1),(m2,2),(m3,3),(m4,4),(m7,7),(m9,9)}

----------------ZREVRANGE--ZREVRANGEBYSCORE--ZREVRANGEBYLEX-----------

127.0.0.1:6379> ZREVRANGE myzset 0 3 # 按score递减排序,然后按索引,返回结果的 0~3



"m9"

"m7"

"m4"

"m3"

127.0.0.1:6379> ZREVRANGE myzset 2 4 # 返回排序结果的 索引的2~4

"m4"

"m3"

"m2"

127.0.0.1:6379> ZREVRANGEBYSCORE myzset 6 2 # 按score递减顺序 返回集合中分数在[2,6]之间的成员

"m4"

"m3"

"m2"

127.0.0.1:6379> ZREVRANGEBYLEX testset [java (add # 按字典倒序 返回集合中(add,java]字典区间的成员

"java"

"back"

"apple"

"amaze"


-------------------------ZREVRANK------------------------------

127.0.0.1:6379> ZREVRANK myzset m7 # 按score递减顺序,返回成员m7索引

(integer) 1

127.0.0.1:6379> ZREVRANK myzset m2

(integer) 4


mathscore=>{(xm,90),(xh,95),(xg,87)} 小明、小红、小刚的数学成绩

enscore=>{(xm,70),(xh,93),(xg,90)} 小明、小红、小刚的英语成绩

-------------------ZINTERSTORE--ZUNIONSTORE-----------------------------------

127.0.0.1:6379> ZINTERSTORE sumscore 2 mathscore enscore # 将mathscore enscore进行合并 结果存放到sumscore

(integer) 3

127.0.0.1:6379> ZRANGE sumscore 0 -1 withscores # 合并后的score是之前集合中所有score的和
 


"xm"

"160"

"xg"

"177"

"xh"

"188"


127.0.0.1:6379> ZUNIONSTORE lowestscore 2 mathscore enscore AGGREGATE MIN # 取两个集合的成员score最小值作为结果的

(integer) 3

127.0.0.1:6379> ZRANGE lowestscore 0 -1 withscores



"xm"

"70"

"xg"

"87"

"xh"

"93"


三种特殊数据类型

  • geospatial
  • hyperloglog
  • bitmaps

geospatial地理位置

可以推算地理位置的信息,两地之间的距离,方圆几公里之内的人

需要注意:

  • 地球南北两极无法直接添加,
  • 一般会下载城市数据,直接通过Java程序一次性导入
  • 有效经纬度范围从-180到180,超出范围时会返回错误,
  • key由(纬度,经度,名称)构成
    在这里插入图片描述

可以查询一些测试数据:http://www.jsons.cn/lngcodeinfo/0706D99C19A781A3

官方文档:https://www.redis.net.cn/order/3685.html

getadd

#getadd 添加地理位置
# 规则:两极无法直接添加 我们一般的会下载城市数据 直接通过Java程序一次性导入
# 有效的精度从-180度到180度
# 有效的的维度从-85.05112878度到85.05112878
# 参数key 值(维度,精度,名称)
127.0.0.1.6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1.6379>  geoadd china:city 121.47 31.23 shanghai
(integer)1
127.0.0.1.6379> geoadd china::city 106.50 29.53 chongqing 114.05 22.52  shenzheng
(integer) 2

geopos 获取当前的定位

在这里插入图片描述

127.0.0.1.6379>geopos china:city beijing#获取指定城市的精度和维度
。。。。
127.0.0.1.6379>geopos china:city beijing chongqing 
。。。。
127.0.0.1:6379> geodist key:city beijing shanghai
"1088645.3557"
127.0.0.1:6379> geodist key:city beijing shanghai km
"1088.6454"
127.0.0.1:6379> 

geodist 获取的当前的两人之间的距离

两个人之间的距离!!
单位

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

在这里插入图片描述

georadius 以给定的经纬度为中心

** georadius 以给定的经纬度为中心**,找出某一半径内的元素
我附近的人?(获取所有附近的人的地址,定位!)通过半径来查询!
获得指定的人数,200
所有数据应该都录入:china:city,才会让结果更加请求!!

127.0.0.1:6379>  georadius china:city  110 30 1000km #以110 30这个维度为中心。寻求方圆1000米的城市
127.0.0.1:6379> georadius china:city 110 30  500km withdist #显示到中心距离的位置。

127.0.0.1:6379> georadius china:city 110 30 500km withcoord #显示他人的定位信息
127.0.0.1:6379> georadius china:city 110 30 500km withcoord   count 1  #筛选出指定的结果
diusbymember 获取指定范围的城市

Georadiusbymember 获取指定范围的城市
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
指定成员的位置被用作查询的中心。
关于 GEORADIUSBYMEMBER 命令的更多信息, 请参考 GEORADIUS 命令的文档。

例子

# 找出指定元素周围的元素
georadiusbymember  china:city beijing 1000km
georadiusbymember china:city shanghai 400km
geohash命令 返回一个或者多个位置元素的Geohash表示

该命令将返回11个字符串的Geohash字符串

# 将二维的经纬度转换为一维的字符串,如果两个字符串越来越接近,那么则距离越接近
127.0.0.1:6379> geohash china:city beijing chongqing
geo底层原理的实现原理就是Zset!我i们使用zset命令来操作的geo!!

在这里插入图片描述

`

127.0.0.1:6379> zrange china:city 0 -1 #查看地图中所有全部的元素
127.0.0.1:6379> zrem china:city beijing #移除指定的元素!!

Hyperloglog

 什么是基数
 A{1,3,5.7,8,7}
 B{1,3,5,7,8}
 基数(   不重复的元素     )=5 可以接受误差

简介
是一种概率数据结构,计数唯一事物,从技术上讲估计一个集合的基数,通常
计数唯一项需要使用成比例的内存,因为需要记录使用过的元素,以免多次记录,但是hyperloglog的算法可以用内存换精度,虽然有误差,但是误差小于1%,算法的神奇之处在于只需要很小的内存,最大也不超过12k,类似集合的功能,能记录2^64的计数,从内存的角度来说,hyperloglog是首选

能用在网页的UV

127.0.0.1:6379>Pfadd mykey a b c d e f g h i j #添加一些元素
127.0.0.1:6379>pfcount  mykey   #统计元素个数
127.0.0.1:6379>Pfadd mykey f  g s d f d k  j #添加一些元素
127.0.0.1:6379>pfmergm mykey3 mykey mykey2 #合并mykey和mykey2

在这里插入图片描述
如果允许容错,那么一定可以Hyperloglog
如果不允许容错,就使用set或者自己的数据类型即可
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

Bitmaps

位存储

统计用户信息,活跃,不活跃!登录,未登录!打卡,365打卡!都可以使用bitmaps!bitmaps位图,数据结构!都是操作二进制位来进行记录,即使只有0和1两个状态
365天=365bit 1字节=8bit 46个字节左右!

测试
在这里插入图片描述

使用bitmap来记录周一到周日的打卡
周一:1 周二 :0 周三:0 周四:1.。。

在这里插入图片描述
查看某一天是否打卡
在这里插入图片描述
统计操作,统计打卡的天数
在这里插入图片描述

事务

Redis单条命令保存原子性的,但是事务不保证原子性!!

Redis事物本质:一组命令的集合!一个事物中所有的的命令都会被序列化,在事务执行的过程中,会按照执行的顺序执行!!
一次性 顺序性 排他性 执行一些列的命令!

--  队列 set set set 执行 --

redis事务的没有隔离级别的概念!
所有的命令在事务的中,并没有直接执行!只有发起执行命令的时候才会执行!
== redis单条事务命令式保存原子性的,但是事务不保存原子性==、

redis的事务:

  • 开启事务(multi)
  • 命令入队(。。。。)
  • 执行事务(exec)
正常执行事务!

在这里插入图片描述

放弃事务
127.0.0.1:6379> multi
127.0.0.1:6379>set k1 v1
127.0.0.1:6379>set k2 v2
127.0.0.1:6379>set k4 v4
127.0.0.1:6379>discard  #取消事务
127.0.0.1:6379>get k4      #队列中的事务都不会执行!
编译型的异常(代码有问题!命令有错),事务中的所有的命令都不i会被执行!

在这里插入图片描述

运行时异常(1/0)如果事务队列中存在语法性,那么执行命令的时候,其他命令都是可以正常执行的。错误的命令抛出异常

在这里插入图片描述

监控(watch 面试常问 乐观锁)

悲观锁:

  • 很悲观,什么时候都会出现问题,无论做什么都会加锁!
    乐观锁:
  • 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
  • 获取version !
  • 更新的时候比较version

在这里插入图片描述

正常执行

127.0.0.1:6379>set money 100

127.0.0.1:6379>set out 0  #花出去0元
127.0.0.1:6379>watch money #监视money
127.0.0.1:6379> multi    #开启事务
127.0.0.1:6379>decrby money 20   这边消费了20
127.0.0.1:6379>incrby  out 20   那边加20
127.0.0.1:6379>exec

测试多线程修改值,使用watch可以当作redis的乐观操作!!

127.0.0.1:6379> watch money  #监视 money
OK
127.0.0.1:6379> decrby money 10
(integer) 70
127.0.0.1:6379> incrby money 10
(integer) 80
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec  执行之前,另外一个线程修改了我们的值,这个时候,就会导致事务执行失败
(nil)

如果修改失败,获取最新的值就好
在这里插入图片描述

Jedis

我们要使用java来操作Redis

  什么是jedis  是redis官方推荐的java连接开发工具!使用java操作Redis中间件!如果你要使用java操作redis,那么就要对jedis十分的熟练!

测试

1.导入对应的依赖

<dependencies>
    <!--导入jedis的包-->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.76</version>
    </dependency>
</dependencies>

2.编码测试

  • 连接数据库
  • 操作命令
  • 断开连接!
package conn.kuang;

import redis.clients.jedis.Jedis;

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());
    }
}

输出:

"C:\Program Files\jdk11\jdk-11.0.6\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=57410:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\day16\kuang-redis\target\classes;D:\mvnjar\redis\clients\jedis\3.2.0\jedis-3.2.0.jar;D:\mvnjar\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\mvnjar\org\apache\commons\commons-pool2\2.6.2\commons-pool2-2.6.2.jar;D:\mvnjar\com\alibaba\fastjson\1.2.76\fastjson-1.2.76.jar conn.kuang.TestPing
PONG

Process finished with exit code 0

常用的Api

对key操作的命令
package conn.kuang;

import redis.clients.jedis.Jedis;

import java.util.Set;

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());
        System.out.println("清空数据:"+jedis.flushDB());
        System.out.println("判断某个键值是否存在:"+jedis.exists("username"));

        System.out.println("新增的<'username','kuangshen'>的键值对:"+jedis.set("username","kuangshen"));
        System.out.println("新增的<'password','password'>的键值对:"+jedis.set("password","password"));
        System.out.println("系统中所有的 键如下");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除键password:"+jedis.del("password"));
        System.out.println("判断键password是否存在"+jedis.exists("password"));
        System.out.println("查看键username所存储的值的类型"+jedis.type("username"));
        System.out.println("随机返回key空间的一个"+jedis.randomKey());
        System.out.println("重命名key:"+jedis.rename("username","name" ));
        System.out.println("取出改后的name:"+jedis.get("name"));
        System.out.println("搜索引查询"+jedis.select(0));
        System.out.println("返回当前选择数据库中的所有的key的数目:"+jedis.dbSize());
        System.out.println("删除所有数据库中的key"+jedis.flushAll());
    }
}

String
package conn.kuang;

import redis.clients.jedis.Jedis;

import java.util.Set;
import java.util.concurrent.TimeUnit;


public class TestPing2 {
    public static void main(String[] args){
        //1.new jedis 对象即可
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
       jedis.flushDB();
        System.out.println("======增加数据======");
        System.out.println(jedis.set("key1","value1"));
        System.out.println(jedis.set("key2","value2"));
        System.out.println(jedis.set("key3","value3"));
        System.out.println("删除键key2:"+jedis.del("key2"));
        System.out.println("获取键key2:"+jedis.get("key2"));
        System.out.println("修改key1:"+jedis.set("key1","valueChanged"));
        System.out.println("获取key1的值:"+jedis.get("key1"));
        System.out.println("在key3后面加入值"+jedis.append("key3","end"));
        System.out.println("key3的值"+jedis.get("key3"));
        System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
        System.out.println("获取多个键值对"+jedis.mget("key01","key02","key03"));
        System.out.println("删除多个键值对"+jedis.del("key01","key02"));
        System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
        jedis.flushDB();
        System.out.println("======新增键值对防止覆盖原先的值======");
        System.out.println(jedis.setnx("key1","value1"));
        System.out.println(jedis.setnx("key2","value2"));
        System.out.println(jedis.setnx("key2","value-new"));
        System.out.println(jedis.get("key1"));
        System.out.println(jedis.get("key2"));

        System.out.println("===========新增键值对并设置有效时间==================");
        System.out.println(jedis.setex("key3",2,"value3"));
        System.out.println(jedis.get("key3"));
try {
    TimeUnit.SECONDS.sleep(3);
}
catch(Exception e){
e.printStackTrace();
}
     System.out.println(jedis.get("key3"));
     System.out.println("=======获取值,更新为新的值============");
     System.out.println(jedis.getSet("key2","key2GetSet"));
     System.out.println(jedis.get("key2"));
     System.out.println("获得key2的值的字符串" +jedis.getrange("key2",2,4)) ;

    }
}

list

package conn.kuang;

import redis.clients.jedis.Jedis;

import java.util.concurrent.TimeUnit;

public class TestPing3List {
public static void main(String[] args) {
//1.new jedis 对象即可
Jedis jedis = new Jedis(“127.0.0.1”, 6379);
//jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
jedis.flushDB();
System.out.println(“添加一个list=”);
jedis.lpush(“collections”,“ArrayList”,“vector”,“stack”,“hashMap”,“weakHashMap”,“linkList”);
jedis.lpush(“collections”,“HashSet”);
jedis.lpush(“collections”,“ThreeSet”);
jedis.lpush(“collections”,“TreeMap”);
System.out.println(“collections的内容:”+jedis.lrange(“collections”,0,-1));
System.out.println("========");
//删除列表指定的值。第二个参数为删除的个数(有重复时),后add进去的值先被删除,类似于出栈
System.out.println(“删除指定元素的个数:”+jedis.lrem(“collections”,2,“hashMap”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“删除下表0-3区间之外的元素”+jedis.ltrim(“collections”,0,3));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“collections列表出栈(左端)”+jedis.lpop(“collections”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“collections 添加元素从列表的右端,与lpush相对应”+jedis.rpush(“collections”,“hashput”));
System.out.println(“collections的内容”+jedis.lrange(“collections”,0,-1));
System.out.println(“修改collections指定下标的内容”+jedis.lset(“collections”,1,“LinkedList”));
System.out.println(" collections的内容"+jedis.lrange(“collections”,0,-1));
System.out.println("
");

    System.out.println("collections的长度"+jedis.llen("collections"));
    System.out.println("获取collections下标为二的的元素"+jedis.lindex("collections",2));


    System.out.println("=================");
    jedis.lpush("sortedList", "3","2","9","8","4","6");
    System.out.println("排序前:"+jedis.lrange("sortedList",0,-1));
    System.out.println(jedis.sort("sortedList"));
    System.out.println("排序后"+jedis.lrange("sortedList",0,-1));

}

}

Set
package conn.kuang;

import redis.clients.jedis.Jedis;


public class TestPing3Set {
    public static void main(String[] args) {
        //1.new jedis 对象即可
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
        jedis.flushDB();
        System.out.println("===============向集合中添加元素(不重复)================");
        System.out.println(jedis.sadd("eleSet","1","2","3","4","5","6"));
        System.out.println(jedis.sadd("e6"));
        System.out.println(jedis.sadd("e6"));
        System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
        System.out.println("删除一个元素1"+jedis.srem("elsSet","1"));

        System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
        System.out.println("删除俩个元素"+jedis.srem("elsSet","2","3"));
        System.out.println("eleSet的所有的元素为"+jedis.smembers("eleSet"));
        System.out.println("随机的移除一个元素"+jedis.spop("eleSet"));
        System.out.println("随机的移除一个元素"+jedis.spop("eleSet"));
        System.out.println("eleSet的所有的元素为"+jedis.smembers("sleSet"));

        System.out.println("eleSet包含元素的个数"+jedis.scard("eleSet"));
        System.out.println("3是否在eleSet中"+jedis.sismember("sleSet","3"));
        System.out.println("2是否在eleSet中"+jedis.sismember("sleSet","2"));
        System.out.println("4是否在eleSet中"+jedis.sismember("sleSet","4"));
        System.out.println("eleSet的所有的元素为"+jedis.smembers("sleSet"));

        System.out.println("=============================================");
        System.out.println(jedis.sadd("eleSet1","e1","e2","e3","e4","e5","e6"));
        System.out.println(jedis.sadd("eleSet2","e1","e2","e3","e0","e5","e8"));
        System.out.println("将eleSet1中删除e1并且存入eleSet3中:"+jedis.smove("eleSet1","eleSet3","5"));
        System.out.println("将eleSet1中删除e2并且存入eleSet3中:"+jedis.smove("eleSet2","eleSet3","2"));
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
        System.out.println("=============集合运算=======================");
        System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
        System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
        System.out.println("eleSet1和eleSet2之间的交集"+jedis.sinter("eleSet1","eleSet2"));
        System.out.println("eleSet3和eleSet2之间的并集"+jedis.sunion("eleSet3","eleSet2"));
        System.out.println("eleSet3和eleSet2之间的差集"+jedis.sdiff("eleSet3","eleSet2"));
        jedis.sinterstore("eleSet4","eleSet1","eleSet2");
        System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
     

    }
}

Hash
package conn.kuang;

import redis.clients.jedis.Jedis;

import java.util.HashMap;


public class TestPing3Hash {
    public static void main(String[] args) {
        //1.new jedis 对象即可
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //jedis 所有的命令就是我们之前学过的所有指令,所以指令很重要
        jedis.flushDB();
        HashMap<String, String> map = new HashMap<>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        //添加名称hash(key)的hash元素
        jedis.hmset("hash",map);
        //向名称为hash的hash中添加key为key5 value为value5元素
        jedis.hset("hash","key5","value5");
        System.out.println("数列hash所有的键值为"+jedis.hgetAll("hash"));
        System.out.println("散列的所有的值为"+jedis.hvals("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6"+jedis.hincrBy("hash","key6", 2));
        System.out.println("散列hash的所有的键值对为"+jedis.hgetAll("hash"));
        System.out.println("删除一个或者多个键值对"+jedis.hdel("hash","key2"));
        System.out.println("散列的所有的键值对为"+jedis.hgetAll("hash"));
        System.out.println("散列hash所有的键值对的个数为"+jedis.hlen("hash"));
        System.out.println("判断hash中是否存在key2"+jedis.hexists("hash","key2"));
        System.out.println("判断hash中是否在key3"+jedis.hexists("hash","key3"));
        System.out.println("获取hash中是否存在key3"+jedis.hmget("hash","key3"));
        System.out.println("获取hash中是否存在key3"+jedis.hmget("hash","key3","key4"));
        System.out.println("");
        System.out.println("");



    }
}

Zset
事务
package conn.kuang;

import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

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", "world");
        jsonObject.put("name", "kuangshen");
        //开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        
       try {

           multi.set("user1", result);
           multi.set("user2", result);

           int i=1/0;//代码抛出异常事务执行失败
           multi.exec();//执行事务
       }catch(Exception e){
           multi.discard();//放弃事务
           e.printStackTrace();
       }finally{
           System.out.println(jedis.get("user1"));
           System.out.println(jedis.get("user2"));
        jedis.close();//关闭连接
    }
    }
}

SpringBoot

在这里插入图片描述
源码分析:

@Bean
@ConditionalOnMissingBean(name = {"redisTemplate"}) // 我们可以自己定一个redisTemplate来替换这个默认的!
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    // 默认的RedisTemplate没有过多的设置,redis对象都是需要序列化的
    // 两个泛型都是Object,Object类型的,我们后续使用需要强制转换成<String,Object>
    RedisTemplate<Object, Object> template = new RedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}
 
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

整合测试一哈

  1. 导入依赖
<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
		</dependency>
  1. 配置连接
#配置redis
spring.redis.host=127.0.0.1
sping.redis.port=6379

3.测试!!!

package com.kuang;


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisSpringboot02ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
	@Test
	void contextLoads() {
		//redisTemplate
		// opsForValue  操作字符串  类似 String
        //opsForList  操作List  类似list
		//opsForSet
		//opsForHash
		//opsForZSet
		// opsForGeo


		//除了基本的操作,我们常用的方法可以都可以直接通过redisTemplate操作,比如事务。和基本的CRUD


		//获取连接对象
//		RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//		connection.flushDb();
//		connection.flushAll();

		redisTemplate.opsForValue().set("mykey","kuangshenshuojava");
		System.out.println(redisTemplate.opsForValue().get("mykey"));

	}

}

在这里插入图片描述

在这里插入图片描述
关于对象的保存:

在这里插入图片描述

我们来编写一个自己的RedisTemplete
package com.kuang.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration

public class RedisConfig {
//这个是我给大家写好的一个固定的模板,大家在企业中,拿去就可以使用
    //编写我们自己的 redisTemplate
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> stringRedisTemplate(RedisConnectionFactory factory) {
//为了我们自己使用开发方便,一般直接使用《String,object》
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();

        template.setConnectionFactory(factory);
       //json序列化配置
        Jackson2JsonRedisSerializer<Object> oJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
          ObjectMapper om=new  ObjectMapper();
          om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
          om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
          //String的序列化配置
          StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
          //key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value序列化方式采用jackson
        template.setValueSerializer(oJackson2JsonRedisSerializer);
       //hash的value序列化方式采用jackson
        template.setHashKeySerializer(oJackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

Redis,conf详情

启动的时候,就通过配置文件来启动
!在这里插入图片描述

  1. 配置文件unit单位 对大小写不敏感,

包含

在这里插入图片描述
就好比我们学Spring import includ

网络

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

logfile "" #日志的文件位置
database 16  #数据库的数量  默认是16个数据库
always-show-logo yes  #是否总是显示log

快照
持久化 在规定的时间内,执行了多少次操作,则会持久化到文件 rdb.aof
redis 是内存数据库,如果没有持久化,那么数据断电即失

#如果 900秒内至少一个1key  进行了修改,我们即进行持久化操作
save 900 1
# 如果300秒内至少100个key  进行了修改,我们即进行持久化操作
save 300 10
# 如果60秒内至少10000个key  进行了修改,我们即进行持久化操作
save 60 10000
#我们之后学习持久化,会自己定义这个测试

stop-writes-on-bgsave-error yes  # 持久化 如果出错,redis是否还继续工作(持久化失败后是否停止写入操作)


rdbcompression yes #是否压缩rdb 文件,需要消耗一些cpu资源

rdbchecksum yes #保存rdb文件的时候,进行错误的检查效验!

dir ./#rdb文件保存的目录

REPLICATION复制,我们面讲解主从复制的时候再进行解析

security 安全性

可以在这里设置redis的密码,默认是没有密码

127.0.0.1:6379> config set requirepass "123456" #设置redis的密码

127.0.0.1:6379>  config get requirepass  #获取redis的密码
1) "requirepass"
2) "123456"
127.0.0.1:6379> ping
PONG

127.0.0.1:6379> auth 123456  #登录使用密码
OK
127.0.0.1:6379>  config get requirepass
1) "requirepass"
2) "123456"

限制 CL TENTS

maxclients 10000 #设置能连接上redis 的最打客户端的数量
 maxmemory <bytes>#redis 配置最大的内存数量
 
 maxmemory-policy noeviction #内存达到上限之后的处理
  1. volatile-lru:只对设置了过期时间的key进行Lru(默认值)
  2. allkeys-lru :删除lru算法的key
  3. volatile-random:随机删除即将过期的key
  4. allkeys-random:随机删除
  5. volatile-ttl:删除即将过期的
  6. noevition:永不过期,返回错误

APPEND ONLY 模式 aof配置

appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下 rdb够用

appendfilename "appendonly.aof" #持久化的文件的名字


# appendfsync always  #每次修改都会  sync 消耗性能
appendfsync everysec #每秒执行一次sync 可能会丢失这ls的数据
# appendfsync no #不执行 sync  这个时候操作系统自己同步数据,速度最快!

具体的配置,在redis的持久化 中 演示

Redis持久

Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能!

RDB(redis DataBase)

什么是RDB
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
一旦触发rdb操作就会生成 dump.rdb文件
在这里插入图片描述

触发机制

  1. save的规则满足的情况下,会自动触发rdb规则
  2. 执行flushall命令,也会触发我们的rdb规则
  3. 退出redis ,也会产生rdb文件!
    备份就会自动生成一个dump.rdb文件
    在这里插入图片描述

如何恢复rdb文件!!

  1. 只需要将rdb文件放在我们的redis启动目录就可以,redis启动的时候会自动的检查dump.edb恢复其中的数据!
  2. 查看需要存在的位置
0.0.1:6379>config get dir
1)"dir"
2)"/usr/local/bin" #如果在这个目录下存在  dump.rdb文件 ,启动就会自动的恢复其中的数据

几乎就他自己默认的配置就足够用了,但是我们还是需要去学习!!

优点:
1.适用大规模的数据恢复
2.对数据的完整性要求不高!
缺点:

  1. 需要一定的时间的间隔操作!如果redis意外的宕(dang)机了,这个最后的一次修改数据就没了!
  2. fork进程的时候,会占用一定的内存空间!!
AOF(Append Only File)追加文件

将我们的所有的命令都记录下来。history,恢复的时候就是把这个文件全部执行一遍!!!

是什么
在这里插入图片描述

以日志的形式来记录每个写操作,将Redis执行过的所有的命令记录下来(读操作不记录),只许追加的文件但不许改写文件,redis启动初会读取该配置文件重新构造数据,换言之,redis重启的话就会根据日志文件的内容将写的指令从前到后执行一次完成数据的恢复。
Aop保存的是appendonly.aof文件

append

在这里插入图片描述
默认是不开启的,需要手动配置!我们只需要将appendonly改为yes就开启了aof
重启,redis就生效了!
如果这个aof文件有错位,这个时候redsi是启动不起来的,我们需要修复这个aof文件
redis 给我们提供了一个工具 redis-check-aof–fix

在这里插入图片描述
此处我故意使用 vim appendonly.aof 来破坏了这个文件
在这里插入图片描述
修复命令
在这里插入图片描述

如果文件正常,重启就可以直接恢复了
在这里插入图片描述

重写规则说明

aof默认就是文件的无限追加,文件就会越来越大!!

在这里插入图片描述
如果aof文件大于64mb ,打打了! fork一个新的进程,来将我们的文件进行重写!

优点和缺点

在这里插入图片描述
扩展:

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

优点:

  1. 每一次修改都同步,文件的完整会更好!
  2. 每秒同步一次,可能会丢失一秒的
  3. 从不同步,效率最高!
    缺点:
  4. 相对数据文件来说,aof远远大于rdb,修复的速度也要比rdb慢
  5. Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化

Redis发布订阅

通信 队列
Redis发部订阅(pub/sub)是一种通信模式模式:发送者(pub)发送消息,订阅者(sub)接受消息。
Redis客户端可以订阅任意数量的频道
订阅/发部消息图:
第一个:消息发送者,第二个:频道 第三个:消息订阅者!!
在这里插入图片描述
下图展示了频道的channel1,以及订阅这个频道的三个客户端 client2 client5 和client1之间的关系:
在这里插入图片描述
当新的消息通过publish命令发送给频道channel1时,这个消息就会被发送给订阅他的三个客户端:
在这里插入图片描述

命令

这些命令被广泛 用于构建即时通信应用的,比如网络聊天室(chatroom)和实时广播,实时提醒等。

在这里插入图片描述

测试

订阅端

127.0.0.1:6379> subscribe kuangshen #订阅一个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshen"
3) (integer) 1
#等待读取推送的信息
1) "message"
2) "kuangshen"# 哪个频道的消息
3) "nihao"# 频道发消息的内容


发送端



127.0.0.1:6379> publish kuangshen "nihao"  #发送者 发部消息到频道
(integer) 1
127.0.0.1:6379>

原理

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

在这里插入图片描述

使用场景:

  1. 实时消息系统!
  2. 实时聊天!(频道当作聊天室,将信息回显给所有人)
  3. 订阅,关注系统是可以的

Redis主从复制

主从复制。是指的将一台Redis服务的数据,复制到其他的Redis服务器,前者称为u主节点(master/leader),后者称为从节点(slace/follower);数据的复制是单向的,只能由从节点到从节节点 Master以写为u主 ,slave以读为主

默认情况下,每台Redis服务器都是主节点
况且一个主节点可以有多个从节点(或者没有从节点)但是一个从节点只能有一个主节点,(类似于一个爸爸只能有两个亲儿子,而不能一个儿子有两个亲爹)
主从复制的作用主要包括:

  1. 数据庸于:主从复制实现了数据的热备份,是持久化之外的一种数据庸于方式,
  2. 事故恢复:当主节点出现问题的时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的额庸余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供读写服务,由从节点提供读服务(即写redis数据时应用连接主节点,读Redis数据时,应用连接节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点,分担读负载,可以大大提高Redis服务器的并发量。
  4. 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵模式的基础,因此说主从复制是Redis高可用的基础。

一般来说:要将Redis运用于工程项目中,只使用一台Redis是不可能的:原因如下:

  1. 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力比较大;
  2. 从容量上,单个redis服务器内存容量有限,就算一台REdis服务器内存容量为256G也不能将所有内存用作redis 存储内存,一般来说。单台REdsi最大使用内存不应该超过20G
    电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点就是多读少写.
    对于这种场景,我们可以使用如下架构:
    在这里插入图片描述

主从复制,读写分离!百分之八十的情况下都是在进行读取操作!减缓服务器的压力!!框架中经常使用!!一主二从!!

只要在公司中,主从复制是必须使用的,因为项目中不可能单机使用redis

环境配置

只配置从库,,不用配置主库!!
在这里插入图片描述
复制3个配置文件,然后修改对应的信息

  1. 端口

  2. pid名字

  3. log 文件名

  4. dump.rdb名字

  5. 先复制配置文件
    在这里插入图片描述

  6. 编辑配置文件(第一个)
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
配置文件的修改(第二个)
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
第三个。。。。
四个。。。
在这里插入图片描述

一主二从

默认的情况下,每台redis服务器都i是主节点我们一般情况下只用配置从机就好了!
认老大!一主(79)二从(80,81)
在这里插入图片描述
第二步认老大

在这里插入图片描述
真实的主从配置应该是在配置文件中配置,这样的化是永久的,我们这里使用的命令,是暂时的
在这里插入图片描述
在这里插入图片描述

细节
主机可以写,从机不能写只能读!主机中的所有的信息和数据,会被从机保存。
主机写:
在这里插入图片描述
从机只能读内容:
在这里插入图片描述

测试:主机断开连接,从主机旧连接到主的,但是没有写操作,这个时主机如果回来了,从机依旧可以直接获取到主机写的信息!!

如果是使用命令行,来配置的主从。这个 时候从机如果重启了,就会变回主机!!!!只要变回从机,立刻就会从主机获取值!

复制原理

在这里插入图片描述

层层链路

上一个M连接下一个S!
在这里插入图片描述
这个时候也可以完成我们的额主从复制

如果没有老大了,这个时候能不能选择一个老大出来? 手动

谋朝篡位

如果主机断开了连接,我们可以使用slaveof no one 让自己变成主机!!其他的节点就可以手动的连接到最新的这个主节点(手动)!如果这个老大修复了,那就重新连接(重新认老大)

哨兵模式(自动选取老大的的模式)

哨兵模式

(自动选取老大的模式)

概述

主从切换技术的方法是:当主服务宕机后。需要手动的把一台服务器切换为一台主服务器,这就需要人工的干预,费时费力,还会造成一段时间的内服务不可用,这不是一种推荐的方式,更多的时候,我们优先考虑哨兵模式,Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。
谋朝篡位是一种特殊的模式,首先Redis‘提供了哨兵的命令,哨兵是一个独立的进程!!作为进程,他会独立的运行,其原理是哨兵通过发送命令,等大Redis’服务的响应,从而监控运行的多个redis实例,

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

在这里插入图片描述

测试
我们目前的状态 就是一主二从

1.编写哨兵模式的配置文件 sentinel.conf

#sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1

后面的这个数字1,代表主机挂机了,slave 投票看让谁接替主机,票数最多的,会成为主机!

在这里插入图片描述

在这里插入图片描述
2. 启动哨兵

redis-sentinel  konfig/sentinel.conf

在这里插入图片描述
在这里插入图片描述
如果Master节点断开了,这个时候就会从主机中随机选择一个服务器!(这里是一个投票的算法)

在这里插入图片描述
哨兵日志:
在这里插入图片描述

如果主机此时回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则!

哨兵模式

优点:

  1. 哨兵集群,基于主从复制模式,所有的主从配置优点,他全有
  2. 主从可以切换,故障可以转移,系统的可用性会更好
  3. 哨兵模式就是主从模式的升级吧,手动到自动,更加健壮!
    缺点:
  4. Redis不好在线扩容的,集群的容量一旦达到上限,在线扩容就十分麻烦!
  5. 实现哨兵模式的配置其实是很麻烦的,里面有很多的选择!

哨兵模式的全部配置!

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

Redis缓存穿透雪崩

在这里插入图片描述

在这里插入图片描述

缓存的穿透(查不到)

概念

在这里插入图片描述

j解决的方案

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

在这里插入图片描述

缓存击穿(量太大,缓存过期)

微博服务器宕机(60是 60.1 0.1s–>>)

在这里插入图片描述

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

缓存雪崩

概念

在这里插入图片描述

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

在这里插入图片描述

小结
视频连接:
https://www.bilibili.com/video/BV1S54y1R7SB?p=36&spm_id_from=pageDriver

在这里插入图片描述

http://www.redis.cn/ redis官网中文

http://www.redis.cn/ Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

在这里插入图片描述

redis实际操作

结合网页找出相对应的方法,决定用生什么集合https://www.runoob.com/redis/hashes-hmset.html
然后StringRedisTemplate相对应的集合中的方法中找到想用方法

@Service
public class RedisService {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IOEventService ioEventService;

//设置value
    public void setValue(String keyPrefix, EnumRedisKey redisKey, String value) {
        String key = redisKey.buildKey(keyPrefix);
        if (redisKey.getExpire() == null) {
            redisTemplate.opsForValue().set(key, value);
        } else {
            redisTemplate.opsForValue().set(key, value, redisKey.getExpire());
        }
    }
    //获取value

    public Optional<String> getValue(String keyPrefix, EnumRedisKey redisKey) {
        String key = redisKey.buildKey(keyPrefix);
        String value = redisTemplate.opsForValue().get(key);
        return Optional.ofNullable(value);
    }
//
    private void setExpire(String keyPrefix, String key, EnumRedisKey redisKey) {
        if (redisKey.getExpire() != null) {
            ioEventService.execute(keyPrefix, "设置redis过期时间", () -> {
                redisTemplate.expire(key, redisKey.getExpire());
            });
        }
    }
//list集合中的右边添加一个value
    public void leftPush(String keyPrefix, EnumRedisKey redisKey, String value) {
        String key = redisKey.buildKey(keyPrefix);
        redisTemplate.opsForList().leftPush(key, value);
        setExpire(keyPrefix, key, redisKey);
    }
//hash集合方法添加一个key 和一个map的key以及value
    public void hashPut(String keyPrefix, EnumRedisKey redisKey, String field, String value) {
        String key = redisKey.buildKey(keyPrefix);
        redisTemplate.opsForHash().put(key, field, value);
        setExpire(keyPrefix, key, redisKey);
    }
//获取hash对应的key以map的key(Stirng field)
    public String hashGet(String keyPrefix, EnumRedisKey redisKey, String field) {
        String key = redisKey.buildKey(keyPrefix);
        return (String) redisTemplate.opsForHash().get(key, field);
    }
//移除hash对用的key 以及map key对应得value
    public void hashRemove(String keyPrefix, EnumRedisKey redisKey, String field) {
        String key = redisKey.buildKey(keyPrefix);
        redisTemplate.opsForHash().delete(key, field);

    }
//hash集合一次获取多个值
    public Map<String, String> hashMGet(String keyPrefix, EnumRedisKey redisKey, List<String> field) {

        String key = redisKey.buildKey(keyPrefix);
//给它一个想要指定得泛型
        HashOperations<String, String, String> hash = redisTemplate.opsForHash();
        HashMap<String, String> map = new HashMap<>();
//
        List<String> values = hash.multiGet(key, field);
//将从list取出得keys 和values 一一放入map集合中;
        if (values == null) {
            return map;
        } else {
            for (int i = 0; i < field.size(); i++) {
                String mapValue = values.get(i);
                String mapKey = field.get(i);
                if (mapValue != null) {
                    map.put(mapKey, mapValue);
                }
            }
            return map;
        }
    }
//获取list集合指定得范围得数
    public List<String> getListOfRang(String keyPrefix, EnumRedisKey redisKey, int start, int stop) {


        String key = redisKey.buildKey(keyPrefix);

        List<String> range = redisTemplate.opsForList().range(key, start, stop);
        return range;
    }

    public void deleteFromList(String keyPrefix, EnumRedisKey redisKey, String value) {

        String key = redisKey.buildKey(keyPrefix);
        redisTemplate.opsForList().remove(key, 0, value);//删除以value为key得列表


    }

//删除指定得key对应得value
    public void delete(String keyPrefix, EnumRedisKey redisKey) {
        String key = redisKey.buildKey(keyPrefix);
        redisTemplate.delete(key);

    }
//string,如果不存在就设置key,存在就不修改
    public boolean setIfAbsent(String keyPrefix, EnumRedisKey redisKey, String value) {

        String key = redisKey.buildKey(keyPrefix);
        Boolean expired = redisTemplate.opsForValue().setIfAbsent(key, value);
        setExpire(keyPrefix,key,redisKey);
        return expired;
    }

    public long atomicAddValue(EnumRedisKey redisKey) {


        Long increment = redisTemplate.opsForValue().increment(redisKey.getRedisKey());
        return increment;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值