Redis详细介绍

Redis

1.NoSQL

​ NoSQL(Not Only SQL),意即不仅仅是SQL,泛指非关系型的数据库。NoSQL这个技术早期提出,发展至2000年趋势愈发高涨。

RDBC: 关系型数据库 sql语句

2.为什么是NoSQL

​ 随着互联网网站的兴起,传统的关系数据库在处理动态网站,特别是超大规模和高并发的纯动态网站时已经显得不够用了,暴露了很多难以克服的问题。如商城网站中对商品数据频繁查询对热搜商品的排行统计订单超时问题、以及微信朋友圈(音频、视频)存储等相关使用传统的关系型数据库实现显得非常复杂,虽然能实现相应功能,但是在性能上并不乐观。nosql这门技术更好的解决了这些问题。。。

3.NoSQL的四大分类

3.1 键值(K-V)存储数据库

# 1.说明
- 这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。
java: Map<> K-V

# 2.特点
- K-V模型对应IT系统来说的优势在于简单、易部署。
- 但是如果DBA只对部分值进行查询或更新的时候,K-V就显得效率低下了。

# 3.相关产品
- Tokyo Cabinet/Tyrant.
- `Redis` √	key value	内存	快	运行软件-->键盘-->内存-->操作
- `SSDB`	key value	硬盘		-->内存
- Voldemort
- Oracle BDB

3.2 列存储数据库

# 1.说明
- 这部分数据库通常时来应对分布式存储的海量数据

# 2.特点
- 键仍然存在,但是他们的特点是指向了多个列,这些列是由列家族来安排
	
# 3.相关产品
- Cassandra、`HBase`、Riak

3.3 文档型数据库(document)

# 1.说明
- 文档型数据库的灵感来自于Lotus Notes办公软件,二圈它同第一种键值存储相类似的数据模型是版本化的稳定,半结构化的文档以特定的格式存储,比如JSON、文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值,而且文档型数据库比键值数据库的查询效率更高
{"id":"21","name":"张三","age":"23","tags":["帅锅","学霸"],"clazz",{"id":"21"}}
---->document BSON
{"id":"21","name":"张三"}
非常灵活
# 2.特点
- 以文档形式存储

# 3.相关产品
- `MongoDB`更新速度很快、CouchDB、 `MongDB(4~5.x)`	国内也有文档型数据库 SequoiaDB,开源

3.4 图形(Graph)数据库

# 1.说明
- 图像结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。

# 2.特点
- NoSQL数据库没有标注的查询语言,因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API

# 3.相关产品
- Neo4J `InfoGrid` Infinite Graph

4.NoSQL应用场景

  • 数据模型比较简单
  • 需要灵活性更强的IT系统
  • 对数据性能要求较高
  • 不需要高度的数据一致性

5.什么是Redis

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

机制:持久化机制,定期写入到磁盘中

  • 总结:Redis是一个内存型的数据库 缓存 消息中间件

6. Redis特点

  • Redis是一个高性能的key/value内存数据库
  • Redis支持丰富的数据类型 字符串、List、Set、Zset、Hash
  • Redis支持持久化
  • Redis单进程、单线程(事实上,后面的Redis版本6.0支持多线程了)Memechache key value多线程 内部 锁
  • Jvm --> 多线程 synchronized
  • Redis实现分布式锁

7.Redis安装

# 0.准备环境
- vmware 15.x+
- centos 7.x+

# 1.下载redis源码包
- https:/redis.io/
- 企业用的比较多 4.x
- 下载 解压 编译
$ wget http://download.redis.io/releases/redis-4.0.11.tar.gz
$ tar xzf redis-4.0.11.tar.gz
$ cd redis-4.0.11
$ make
# 3.安装gcc
- yum install -y gcc

# 4.进入解压缩目录执行如下命令
- make MALLOC=libc

# 5.编译完后执行如下命令
- make install PREFIX=/usr/redis

# 6.进入/usr/redis/bin目录启动redis(port:6379)
- ./redis-server ../redis.conf
  ps -ef |grep redis查看启动进程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8Lvaav6-1671432930315)(hexo-images/redis.server.png)]

# 9.Redis服务端口默认是 6379

# 10.进入bin目录执行客户端连接操作
- ./redis-cli -p 6379

8.启动redis的服务细节

# 1.redis启动服务的细节
- 直接使用命令 ./redis-server 启动的是redis-server这个shell脚本中的默认配置

# 2.如何在启动redis时指定配置文件启动
- 注意:默认在redos安装完成折后在安装目录没有任何配置文件,需要在源码目录中复制redis.conf配置文件到安装目录
- 先进入源码目录
- cp redis.conf /usr/redis/
- 进入/usr/redis安装目录查看配置文件
- cd /usr/redis
- ls
- 进入bin目录加载配置启动
- ./redis-server ../redis.conf
# 3.可以修改redis默认端口号
- vim redis.conf 修改port

# 4.redis中库的概念
- 库database 用来存储数据的一个基本单元,一个库可以存放K-V键值对 redis中每一个库都有一个唯一名称 | 编号
从0开始
默认库的个数:1个库 库的编号:0-15 默认使用的是0号库
切换库命令: select dbid(库编号)

# 5.redis中清除库的指令(删除库中的数据※)
  flushDB  	清空当前库
  flushAll 	清空所有库
  
# 6.redis客户端显示中文
- ./redis-cli -p 6379 --raw

问题记录:

因为电脑连接的是无线网,设置桥接网络前需要在vmware的虚拟网络设置中选择无线网卡

centos更换阿里云镜像,解决下载速度慢问题

cd /etc/yum.repos.d/

mv CentOS-Base.repo CentOS-Base.repo.back  #建议备份或者改名

wget -O CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo 

yum makecache #生成缓存

9.redis的相关指令

9.1数据库操作指令

# 1.DEL指令
- 语法: DEL key[key...]
- 作用: 删除给定的一个或多个key,不存在的key会被忽略
- 可用版本: >=1.0.0
- 返回值: 被删除key的数量
# 2.EXISTS指令
- 语法 exists key
- 作用:检查给定key是否存在
- 返回值:存在返回1否则返回0
# 3.EXPIRE
- 语法: expire key seconds
- 作用: 为给定key 设置生存事件,当key过期时(生存时间为0),它会被自动删除(定时任务...)
- 时间复杂度:O(1)
# 4.KEYS
- 语法: KEYS patter
- 作用: 查找所有符合给定模式pattern 的key
- 语法:
  keys * 匹配数据库中所有key
  keys h*llo 匹配带有hllo字母的key
  keys h?llo
  keys h[ae]llo

# 5.MOVE
- 语法: MOVE key db
- 作用: 将当前数据库的key 移动到给定的数据库db当中
- 返回值: 成功1/失败0

# 6.PRXPIRE
- 语法: pexpire key milliseconds
- 作用: 这个命令和EXPIRE 命令作用类型,但他是以毫秒为单位设置key的生存时间,而不像EXPIRE 命令那样,以秒为单位
- 时间复杂度: O(1)
- 返回值: 1/0

# 7.PEXPOREAT
- 语法: pexpireat key millseconds-timestamp
- 作用: 这个命令和EXPIREAT 命令类似,但它以毫秒为单位设置key 的过期unix时间戳,而不是像EXPIREAT那样,以秒为单位
- 返回值: 如果生存时间设置成功,返回1 当key不存在或没办法设置生存时间时,返回0

# 8.TTL
- 语法: TTL key
- 作用: 以秒为单位,返回给定key 的剩余生存时间(TTL, time to live)
- 返回值:
  当key 不存在时,返回 -2
  当key 存在但没有设置剩余生存时间时,返回-1
  否则,以秒为单位返回key的剩余生存时间
  注意: redis 2.8以前,当key不存在,或者key没有设置剩余生存时间时,命令都返回-1
  
# 9.PTTL
- 语法: PTTL key
- 作用: 以毫秒为单位,返回给定key 的剩余生存时间
- 返回值:
  当key 不存在时,返回 -2
  当key 存在但没有设置剩余生存时间时,返回-1
  否则,以毫秒为单位返回key的剩余生存时间
  注意: redis 2.8以前,当key不存在,或者key没有设置剩余生存时间时,命令都返回-1
 
# 10.RANDOMKEY
- 语法: RANDOMKEY
- 作用: 从当前数据库中随机返回(不删除)一个key
- 返回值: 当数据库不为空,返回一个key,当数据库为空时,返回nil

# 11.RNAME
- 语法:rename key newkey
- 作用:将key改名为newkey,当key和newkey相同时,或者key不存在时,返回一个错误
- 返回值:成功提示OK,失败时返回一个错误

# 12.TYPE
- 语法: TYPE key
- 作用: 返回key 所存储的值的类型
- 返回值:
  none key不存在
  string 字符串
  list 列表
  set 集合
  zset 有序集
  hash 哈希表
  
  key value[stirng,list,set,zset,hash]

9.2 String类型

命令说明
set设置一个key/value
get根据key获得对应的value
mset一次设置多个key value
mget一次获取多个key 的 value
getset获得原始key的值,同时设置新值
strlen获得对应key存储value的长度
append为对应key的value追加内容
getrange截取value的内容
setex设置一个key存活的有效期 秒
10设置一个key存活的有效期 奥妙
setnx存在不做任何操作,不存在任何添加
msetnx原子操作(只要有一个存在失败,不做任何操作)可以同时设置多个key,只要一个失败全部不保存
decr进行数值类型的-操作
decrby根据提供的数值进行减法操作
incr进行数值类型的+1操作
incrby根据提供的数据进行加法操作
incrbyfloat根据提供的数据加入浮点数

9.3 List类型

list 列表 相当于java中list集合 特点:元素有序且可以重复

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnLsktIK-1671432930316)(hexo-images/list.png)]

命令说明
lpush将某个值加入到一个key列表头部
lpushx同lpush,但是必须要保证这个key存在
rpush将某个值加入到一个key列表末尾
rpushx同rpush,但是必须保证这个key存在
lpop返回和移除列表左边的第一个元素
rpop返回和移除列表右边的第一个元素
lrange获取某一个下标区间的元素
llen获取列表元素个数
lset设置某一个指定索引的值(索引必须存在)
lindex获取某一个指定索引位置的元素
lrem删除重复元素
ltrim保留列表中特定区间内的元素
linsert在某一个元素之前,之后插入新元素

9.4 Set类型

特点:Set类型,set集合,元素无序,不可重复

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KW7G83lD-1671432930317)(hexo-images/set.png)]

命令说明
sadd为集合添加元素
smembers显示集合中所有元素 无序
scard返回集合中元素的个数
spop随机返回一个元素 并将元素在集合中删除
smove从一个集合中向另一个集合移动元素 set集合之间
srem从一个集合中删除一个元素
sismember判断一个集合中是否含有这个元素
srandmember随机返回元素
sdiff去掉第一个集合中其他集合含有的相同元素
sinter求交集
sunion求和集

9.5 ZSet类型

特点:可排序的set集合 排序 不可重复

ZSet 官方 可排序SET sortSet

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6WReQFfo-1671432930317)(hexo-images/zset.png)]

命令说明
zadd添加一个有序集合元素
zcard返回集合的元素个数
zrange 升序 zrevange 降序返回一个范围内的元素
zrangebyscore按照分数查找一个范围内的元素
zrank返回排名
zrevrank倒序排名
zscore显示某一个元素的分数
zrem移除某一个元素
zincrby给某个特定元素加分

9.6 hash类型

redis key(string) value(map)

Map<String,Map<Key,Value>>

特点:vale是一个map结构,存在ket value key无序的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pb2OM2gZ-1671432930318)(hexo-images/hash.png)]

命令说明
hset设置一个key/value对
hget获得一个key对应的value
hgetall获得所有的K-V对
hdel删除某一个K-V对
hexists判断一个key是否存在
hkeys获得所有的key
hvals获得所有的value
hmset设置多个K-V
hmget获得多个K-V
hsetnx设置一个不存在的key的值
hincrby为value进行加法运算
hincrbyfloat为value加入浮点值

10.开启redis远程连接

redis默认不允许远程连接,也就是拒绝所有远程客户端连接,需要开启redis远程连接

1.修改配置开启

vim redis.conf 修改如下配置

bind 0.0.0.0 #允许一切客户端连接

2.配置修改之后重启redis服务

./redis-server ../redis.conf

3.连接不上,记得关闭防火墙

service firewalld stop

4.查看redis进程号

ps -ef | grep redis

11.持久化机制

client redis(内存) —> 内存数据 数据持久化—>内存

Redis官方提供了两种不同的持久化方法来将数据存储到硬盘里面:

  • 快照(Snapshot)
  • AOF(Append Only File)只追加日志文件

11.1 快照(Snapshot)

1.特点

​ 这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认开启持久化方式,保存的的文件是以.rdb后缀结尾的文件,因此这种方式也称之为RDB方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7rnJX3G-1671432930319)(hexo-images/RDB.PNG)]

2.快照生成方式

  • 客户端方式: BGSAVE 和 SAVE 指令
  • 服务器配置自动触发
# 1.客户端方式 BGSAVE ※
- a.客户端可以使用bgsave 命令来创建一个快照,当接收到客户端的bgsave命令时,redis会调用fork 来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令请求

  `fork:当一个进程创建一个子进程的时候,底层的操作系统会创建该进程的一个副本,在类unix系统中创建子进程的操作会进行优化;在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了了写之后,对被写入的内存的共享才会结束服务`

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCJSpURU-1671432930319)(hexo-images/快照snapshot.png)]

# 2.客户端方式 save
- b.客户端还可以使用save命令来创建一个快照,接收到save命令的redis服务器在快照创建完毕之前不再响应任何其他命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fd5Xvioz-1671432930320)(hexo-images/save.png)]

# 3.服务器配置 自动触发
- 如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后触发一次bgsave命令,如果设置多个save配置选项,当任意一个save配置选项条件满足,redis也会触发一次bgsave命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOeGAwSL-1671432930320)(hexo-images/配置文件保存快照.png)]

# 4.服务器接收客户端shutdown指令
- 当redis通过shutdown指令收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行发送的任何命令,并且在save命令执行完毕之后关闭服务器

3.配置生成快照名称和位置

# 1,修改生成快照名称
- db.filename dump.rdb

# 2,修改生成位置
- dir ./

11.2 AOF只追加日志文件

1.特点

​ 这种方式可以将所有客户端执行的写命令记录到日志文件中,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行依次AOF文件所包含的所有写命令,就能回复AOF文件记录的数据集

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oq6n2Uof-1671432930321)(hexo-images/AOF.PNG)]

2.开启AOF持久化

​ 在redis默认配置中AOF持久化机制没有开启,在redis.conf中修改

# 1.开启AOF持久化
- a.修改 appendonly yes 开启持久化
- b.修改 appendfilename "appendonly.aof" 指定生成文件名称

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sf9NqVS5-1671432930322)(hexo-images/配置AOF.png)]

3.日志追加频率

# 1.alawys 【谨慎使用】
- 说明: 每个redis写命令都要同步写入硬盘,严重降低redis速度
- 解释: 如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会收到硬盘性能的限制;
- 注意:转盘式硬盘在这种频率下200左右个命令/s;固态硬盘: 几万个命令/s
- 警告:使用固态硬盘的用户谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的写入放大问题,导致将固态硬盘的寿命严重减少。

# 2.everysec 【推荐】
- 说明: 每秒执行依次同步显式的将多个写命令同步到硬盘
- 解释: 为了兼顾数据安全和写入性能,可以尝试这种恶略,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每一秒同步一次AOF文件,redis可以保证,即使系统崩盘,用户最多丢失一秒之内产生的数据

# 3.no 【不推荐】
- 说明: 由操作系统决定何时同步
- 解释: 最后使用no选项,将完成由操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响,但是系统崩溃时,会丢失不定数量的数据,另外如果用户磁盘处理写入操作不够快的化,当缓冲区被等待写入硬盘数据填满时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢。

11.3 AOF文件的重写

1.AOF带来的问题

​ AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要回复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了AOF重写机制

2.AOF重写

​ 用来在一定程度上减小AOF文件的体积

3.触发重写方式

# 1.客户端方式触发重写
- 执行BGREWRITEAOF(bgrewriteaof)命令 不会阻塞redis的服务

# 2.服务器配置方式自动触发
- 配置redis.conf中的auto-aof-rewrite-precentage选项
- 如果设置auto-aof-rewrite-precentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么AOF文件体积大于64M,并且aof文件的体积比上一次重写之后体积大了至少一倍时,会自动触发,如果重写过于频繁,用户可以考虑将auto-aof-rewrite-precentage设置为更大

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tCT2JFrM-1671432930329)(hexo-images/AOF重写配置.png)]

4.重写原理

注意: 重写aof文件的操作,并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件,替换原有的文件,这点和快照有点类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2hrBRUUB-1671432930330)(hexo-images/重写流程.png)]

11.4 持久化总结

​ 两种持久化方案可以同时使用,也可以单独使用,在某种情况下也可以都不使用,具体使用哪种持久化方案取决于用户的数据和应用决定。

​ 无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的。备份!!!

12.Java操作Redis

12.1 环境准备

1.引入依赖

    <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.3.0</version>
    </dependency>

2.创建Jedi对象

public static void main(String[] args){
    //1.创建jedis对象
    Jedis jedis = new Jedis("192.168.1.110", 6379)	//redis防火墙关闭 开启远程连接
	     
}

12.2 操作key相关API

//测试key相关
@Test
public void testKeys() {
    //删除一个key
    //jedis.del("name");
    //删除多个key
    //jedis.del("name","age");

    //判断一个key是否存在
    Boolean name = jedis.exists("name");
    System.out.println(name);

    //设置一个key的超时时间 expire pexpire
    //Long age = jedis.expire("age", 100);
    //System.out.println(age);

    //获取一个key的超时时间
    //Long age1 = jedis.ttl("newage");
    //System.out.println(age1);

    //随机获取一个key
    //String s = jedis.randomKey();
    //System.out.println(s);

    //修改key的名称
    //String rename = jedis.rename("age", "newage");
    //System.out.println(rename);

    //查看对应值的类型
    String name1 = jedis.type("name");
    System.out.println(name1);
    String map = jedis.type("map");
    System.out.println(map);
}

12.3 操作String相关API

//测试String相关
@Test
public void testString() {
    //set
    jedis.set("name","zhangsan");
    //get
    String name = jedis.get("name");
    System.out.println(name);
    //mset
    jedis.mset("content","学生","address","上海市");
    //mget
    List<String> mget = jedis.mget("name", "content", "address");
    mget.forEach(value -> System.out.println("V=" + value));

    //getset 获取后设置
    String set = jedis.getSet("name", "小6");
    System.out.println(set);

    //.........

}

12.4 操作List相关API

//测试List相关
@Test
public void testList() {

    /*//lpush
    jedis.lpush("names1","张三","王五","赵六");
    //rpush
    jedis.rpush("names1","小黄黄","小白白","小红花");
    //lrange
    List<String> names1 = jedis.lrange("names1", 0, -1);
    names1.forEach(name -> System.out.println("name = " + name));
    //lpop rpop
    String names11 = jedis.lpop("names1");
    System.out.println(names11);
    //llen
    Long names12 = jedis.llen("names1");
    System.out.println(names12);
    //lset
    jedis.lset("names1", 1,"小黄同学");
    //lindex
    String names13 = jedis.lindex("names1", 1);
    System.out.println(names13);*/

    //linsert 在小黄同学之前插入 小QQ
    jedis.linsert("names1", ListPosition.BEFORE,"小黄同学","小QQ");
}

12.5 操作Set相关API

//测试SET相关
@Test
public void testSet() {
    //sadd
    jedis.sadd("set1", "a", "b", "c", "d", "e");
    //smembers
    Set<String> set1 = jedis.smembers("set1");
    set1.forEach(set -> System.out.println("set = " + set));

    //scard set长度
    Long set1Length = jedis.scard("set1");
    System.out.println(set1Length);

    /*//spop 随机删除一个元素
    String spop = jedis.spop("set1");
    System.out.println(spop);

    //smove 两个集合之间移动元素
    Long smove = jedis.smove("set1", "set2", "b");
    System.out.println(smove);

    //srem  删除元素
    Long srem = jedis.srem("set1", "a", "b");
    System.out.println(srem);*/

    //sismember 判断集合中是否有该元素
    Boolean sismember = jedis.sismember("set2", "b");
    System.out.println(sismember);

    //srandmember 随机返回2个元素
    List<String> set11 = jedis.srandmember("set1", 2);
    set11.forEach(set -> System.out.println("set = " + set));

    //sdiff 去掉第一个集合中与其他集合含有的相同元素
    Set<String> sdiff = jedis.sdiff("set1", "set2");
    sdiff.forEach(s -> System.out.println(s));
    System.out.println("=================");
    //sinter 求交集
    Set<String> sinter = jedis.sinter("set1", "set2");
    sinter.forEach(v -> System.out.println(v));

    System.out.println("------------------");
    //sunion 求合集
    Set<String> sunion = jedis.sunion("set1", "set2");
    sunion.forEach( v -> System.out.println(v));

}

12.6 操作Zset相关API

//测试ZSet相关
@Test
public void testZSet() {
    //zadd
    //将元素 和 其对应分数存入一个map然后存入zset集合
    /*Map<String,Double > map = new HashMap<>();
    map.put("小黄",10.00);
    map.put("小红",9.00);
    map.put("小蓝",8.00);
    map.put("小白",7.00);
    jedis.zadd("zset1",map);*/

    //zcard 返回集合的元素个数
    Long zset1 = jedis.zcard("zset1");
    System.out.println("count = " + zset1);

    //zrange 升序 返回一个范围内的元素 zrevange降序(按分数)
    Set<String> zset = jedis.zrange("zset1", 0, -1);
    zset.forEach(value -> System.out.println("value = " + value));
    System.out.println("================");
    Set<String> rev = jedis.zrevrange("zset1", 0, -1);
    rev.forEach(value -> System.out.println("value = " + value));
    System.out.println("-------------");
    //zrangebyscore 按照分数查找一个范围内的元素
    Set<String> zrangeByScore = jedis.zrangeByScore("zset1", 0, 10);
    zrangeByScore.forEach(element -> System.out.println("element=" + element));

    //zrank 返回排名
    System.out.println("-------zrank-------");
    Long zrank = jedis.zrank("zset1", "小黄");
    //0 1 2 3
    System.out.println("rank=" + zrank);
    //zrevrank
    Long zrevrank = jedis.zrevrank("zset1", "小黄");
    System.out.println("zrvrank=" + zrevrank);

    //zscore
    Double zscore = jedis.zscore("zset1", "小黄");
    System.out.println("score=" + zscore);

    //zrem 移除某一个或多个元素
    /*Long zrem = jedis.zrem("zset1", "小白", "小蓝");
    System.out.println("zrem=" + zrem);*/

    //zincrby 给某个特定元素加分
    Double zincrby = jedis.zincrby("zset1", 10.00, "小黄");
    System.out.println("zincrby=" + zincrby);
}

12.7 操作Hash相关API

//测试Hash相关
@Test
public void testZSet() {
    //hset
    /*Map<String, String> map = new HashMap<>();
    map.put("name", "小黄");
    map.put("age", "23");
    map.put("address", "湖北省武汉市");
    map.put("hobby", "网抑云音乐");
    Long hash1 = jedis.hset("hash1", map);
    //返回传入的map长度
    System.out.println(hash1);*/

    //hgetAll 获得所有的K-V对
    Map<String, String> maps = jedis.hgetAll("hash1");
    Set<String> set = maps.keySet();
    set.forEach(kv -> System.out.println(kv + ":" + maps.get(kv)));

    //hdel  删除某一个K-V对
    Long hdel = jedis.hdel("hash1", "hobby");
    System.out.println("-------hdel--------");
    System.out.println(hdel);

    //hexists 判断一个key是否存在
    Boolean hexists = jedis.hexists("hash1", "name");
    System.out.println("-------hexists--------");
    System.out.println(hexists);

    //hkeys 获得所有的key
    Set<String> hash1 = jedis.hkeys("hash1");
    System.out.println("-------hkeys--------");
    hash1.forEach(key -> System.out.println(key));

    //hvals 获得所有的value
    List<String> values = jedis.hvals("hash1");
    System.out.println("-------hvals--------");
    values.forEach(value -> System.out.println(value));

    //hmset
    Map<String, String> map = new HashMap<>();
    map.put("name", "XQ");
    map.put("age", "24");
    map.put("address", "湖北省黄冈市");
    map.put("hobby", "QQ音乐");
    String hash2 = jedis.hmset("hash2", map);
    System.out.println("-------hmset--------");
    System.out.println(hash2);

    //hmget
    List<String> hmget = jedis.hmget("hash2", "name", "address");
    System.out.println("-------hmget--------");
    hmget.forEach(value -> System.out.println(value));

    //hsetnx 设置一个不存在的key的值
    Long hsetnx = jedis.hsetnx("hash2", "wait", "who");
    System.out.println("-------hsetnx--------");
    System.out.println(hsetnx);

    //hincrby 为value进行加法运算
    Long aLong = jedis.hincrBy("hash2", "age", 3);
    System.out.println("-------hincrby--------");
    System.out.println(aLong);

    //hincrbyfloat 为value加入浮点值
    Double aDouble = jedis.hincrByFloat("hash2", "age", 1.1);
    System.out.println("-------hincrbyfloat--------");
    System.out.println(aDouble);
}

13 Spring Boot 整合redis

Spring Boot Data Redis提供了RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate的子类,两个方法一致,不同之处主要体现在操作的数据类型不同,RedisTemplate的两个泛型都是Object,意味着存储的key和valuedd都可以是一个对象,而StringRedisTemplate的两个泛型都是String ,意味着它的key和value都只能是String类型

RedisTemplate(Object,Object) :自动序列化,自动反序列化

StringRedisTemplate(String,String):

​ 注意:使用RedisTemplate默认是将对象序列化到Redis中,所以存入的对象必须实现序列化接口

13.1 环境准备

1.引入依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置application.properties

#redis
spring.redis.host=192.168.1.115
spring.redis.port=6379
spring.redis.database=0

Ubuntu配置Redis

https://www.cnblogs.com/yunlongaimeng/p/10260573.html

github的window版本redis:

https://github.com/MicrosoftArchive/redis/tags

Redis中分布式缓存实现

1.什么是缓存?(cache)

​ 定义:计算机内存中的一段数据

2.内存中数据的特点

  • 读写快
  • 断电立即丢失

3.缓存解决了什么问题?

  • 提高了网站的吞吐量(处理请求的能力),提升网站运行效率
  • 核心解决:缓存的存在是用来减轻数据库的访问压力

4.并不是所有数据都加入缓存更好!

  • 注意:使用缓存时 一定是数据库中的数据极少发生修改,更多用于查询这种情况 居住地址 XX市 XX县 XX村

5.本地缓存和分布式缓存区别?

本地缓存:存在应用服务器内存中数据称之为本地缓存 local cache

分布式缓存:存储在当前应用服务器内存之外的数据称之为分布式缓存 distribute cache

集群:将同一个服务器的多个节点放在一起共同对系统提供服务的过程称之为集群。

分布式:有多个不同的服务集群共同对系统提供服务,这个系统称之为分布式系统(Distribute System)

6.利用MyBatis自身缓存结合redis实现分布式缓存

  • a.mybatis中的应用级缓存(二级缓存), SqlSessionFactory级别缓存,所有会话共享

  • b.如何开启(二级缓存)

    • mapper.xml 存在应用服务器中,本地缓存

      • <cache/
        
    • mapper开启本地缓存后,前提(实体类必须序列化)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCpigep9-1671432930330)(D:\blog-hexo\hexo-images\maper-cache.png)]

  • c.查看cache标签的实现

    结论:mybatis底层默认使用的是org.apache.ibatis.cache.impl.PerpetualCache 实现

  • d.

    • 1.通过mybatis的默认cache源码实现得知,可以通过实现cache接口,并对里面的方法进行实现,完成自定义缓存

    • 2.使用RedisCache实现

      <cache type="xxx.xxx.RedisCache"/>
      
      public class RedisCache implements Cache {
      
  • Cache Hit Ratio(缓存命中)

Redis分布式缓存(二)

1.缓存在项目中应用

a.如果项目中表查询之间没有任何关联查询使用现在的这种缓存方式没有任何问题

b.现有缓存方式在表连接查询过程中一定存在问题

2.在mybatis的缓存中如何要解决关联关系时更新缓存信息的问题?

<cache-ref namespace="xxx"/>	//用来将多个具有关联关系查询缓存放在一起处理,共享缓存

Redis分布式缓存(三)

  • 缓存优化策略

    • 对放入redis中hash的key进行优化,key的长度不能太长

      key:-1502223786:2621597331:com.hch.dao.UserDao.findAllUser:0:2147483647:select id,name,age,birthday
      from t_user:SqlSessionFactoryBean

    • 尽可能将key设计的简洁一些?

      算法:MD5处理 加密

      特点:

      ​ 1.任何的文件字符串等经过MD5处理之后,都会生成32位16进制字符串

      ​ 2.不同内容的文件经过MD5加密,加密结果一定不一致 经典面试题:aa.txt bb.txt (判断这个两个文件内容是否一致)

      ​ 使用MD5对这两个文件加密,得出的结果如果是一致,那么文件里的内容一定是一样的

      ​ 3.相同内容的文件多次经过MD5生成结果始终一致

      推荐:在redis整合mybatis缓存过程中,建议将key进行MD5加密处理

  • 面试相关概念

    1)什么是缓存穿透?(缓存击穿)

    ​ 定义:客户端查询了一个DB没有的数据记录,导致缓存在这种情况下无法利用,称之为缓存穿透

    id=-1
    id=int最大值+Mathron
    

    MyBatis中cache解决了缓存穿透,将数据库中没有查询到结果的也进行缓存

    2)什么是缓存雪崩?

    ​ 定义:在系统运行的某一时刻(临界点),突然系统中的缓存全部失效,恰好在这一时刻涌来大量的客户端请求,导致所有模块缓存无法利用,大量的请求涌向DB,极端情况下,数据库阻塞或挂起

    ​ 缓存存储时:业务系统非常大,模块多,业务数据不同,不同模块在放入缓存时,都会设置一个缓存超时时间

    ​ 解决方案:

    ​ 1.缓存永久存储,简单粗暴(不推荐

    ​ 2.加入缓存的超时,针对于不同的业务数据,一定要设置不同的超时时间

    3)项目中有没有遇到,如何解决?

14.Redis主从复制

主从复制?

​ 主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据

无法解决: 1.master主节点出现故障转移问题

主从复制架构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e73Geirb-1671432930331)(D:\blog-hexo\hexo-images\主从复制架构图.png)]

搭建主从复制

# 1.准备三台机器并修改配置
- master
  port 7000
  bind 0.0.0.0
 
- slave1
  port 7001
  bind 0.0.0.0
  slaveof masterip masterport
 
- slave2
  port 7002
  bind 0.0.0.0	
  slaveof masterip masterport 

ps -ano | findstr 6379

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9bfy527Y-1671432930331)(D:\blog-hexo\hexo-images\主从复制master.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZE4WQ8Fo-1671432930332)(D:\blog-hexo\hexo-images\主从复制从节点只读.png)]

注意:从节点是只读的

window下启动三个服务进行测试

redis-server.exe master/redis.windows.conf
redis-server.exe slave1/redis.windows.conf
redis-server.exe slave2/redis.windows.conf

15.Redis哨兵机制

15.1 哨兵Sentinel机制

​ Sentinel(哨兵)是redis的高可用解决方案:由一个和多个Sentinel实例 组成的Sentinel系统可以监视任意多个主服务器,以及这些服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线的主服务器属下的某个从服务器升级为新的主服务器,简单的说哨兵就是带有自动故障转移功能的主从架构

无法解决:1.单节点并发压力问题 2.单节点内存和磁盘物理上限问题

15.2 哨兵架构原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7j9zms1h-1671432930332)(D:\blog-hexo\hexo-images\哨兵架构.png)]

15.3 搭建哨兵架构

# 1.在主节点上创建哨兵配置
- 在master对应redis.conf同目录下新建sentinel.conf文件(名字绝对不能错)

# 2.配置哨兵,在sentinel.conf文件中输入配置
- sentinel monitor 被监控数据库名字(自己取)  ip port 1(几个哨兵)

# 3.启动哨兵模式进行测试
- redis-sentinel /myredis/sentinel.conf (linux)
$ redis-server.exe sentinel/sentinel.conf --sentinel (windows)

  说明: 这个后面的数字2 是指当有两个及以上的sentinel服务检测到master宕机,才会去执行主从切换的功能

https://blog.csdn.net/liuchuanhong1/article/details/53206028

15.4 通过SpringBoot操作Redis集群

哨兵监控集群时遇到报错问题:

MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

经过查看,发现是权限的问题,windows下要以管理员权限运行redis服务

遇到:远程连接被拒绝

注意:默认哨兵没有开启远程连接权限,如果没有开启,是无法连接,直接拒绝的,需要在sentinel中配置开启运行远程连接

	bind 0.0.0.0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzkkRSKF-1671432930332)(D:\blog-hexo\hexo-images\sentinel开启允许远程连接.png)]

16.Redis集群

16.1 集群

​ Reds在3.0之后开始支持Cluster模式,目前redis的集群支持节点的自动发现,支持slave-master选举和容错,支持在线分片(sharing shard)等特性。 reshard重新分配hash槽

16.2 集群架构图

​ PING ----> PONG PINGPONG协议(二进制协议)

16.3 集群细节

  • 所有的Redis节点彼此互联(ping-pong机制),内部使用二进制协议优化传输速度和带宽
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效
  • 客户端和redis节点直连,不需要中间层proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
  • redis-cluster把所有物理节点映射到[0-16383] slot(槽)上,cluster 负责维护 node<—>slot<—>value

Redis最大的物理节点(master)数不能超过16384

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEbvSvuv-1671432930333)(D:\blog-hexo\hexo-images\redis集群.png)]

16.4 集群搭建

判断一个集群的节点是否可用,是集群中的所用节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就挂掉了,是所以搭建redis集群时建议节点个数为奇数个,搭建集群至少需要三个主节点,三个从节点,至少需要六个节点。

查看博客:https://www.cnblogs.com/thirteen-zxh/p/9187875.html

创建集群

ruby redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ijEUNr6K-1671432930333)(D:\blog-hexo\hexo-images\集群.png)]

redis-cli -c -p 7000

这种情况一般是因为启动redis-cli时没有设置集群模式所导致。

采用集群模式登录redis客户端

redis-cli -c -p 7000

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-word9Kjv-1671432930334)(D:\blog-hexo\hexo-images\集群测试.png)]

查看集群状态

# 1.查看集群状态 check 【原始集群下任意节点】
- redis-trib.rb check 127.0.0.1:7000 (windows下)
- ./redis-trib.rb check 127.0.0.1:7000 (linux下)

# 2.集群节点状态说明
- 主节点
  主节点存在hash slots(哈希槽),且主节点的哈希槽 没有交叉
  主节点不能删除
  一个主节点可以有多个从节点
  主节点宕机时多个副本之间自动选举主节点
  
- 从节点
  从节点没有哈希槽
  从节点可以删除
  从节点不负责数据的写,只负责数据的同步,即使写也会被重定向到主节点

添加主节点

# 1.添加主节点 add-node 新加入节点 集群中任意节点
- redis-trib.rb add-node  127.0.0.1:7006 127.0.0.1:7000(windows)
- ./redis-trib.rb add-node  127.0.0.1:7006 127.0.0.1:7000(linux)
- 注意:
  1.该节点必须以集群模式启动
  2.默认情况下该节点就是以master节点形式添加

添加从节点

# 1.添加从节点 add-node --slave 新节点 集群中任意节点
- redis-trib.rb add-node --slave  127.0.0.1:7006 127.0.0.1:7000(windows)
- ./redis-trib.rb add-node --slave  127.0.0.1:7006 127.0.0.1:7000(linux)
- 注意:
  当添加副本节点时没有指定主节点,redis会随机给副本节点较少的主节点添加当前副本节点
  
# 2.为确定的master节点添加主节点 add-node --slave --master-id master节点id 新加入节点 集群任意节点
- ./redis-trib.rb add-node --slave 	--master-id 6a4b6721416e89822e0482028a4dac1bd950ac11 127.0.0.1:7006 127.0.0.1:7000

删除副本节点

# 1.删除节点 del-node 集群中任意节点 删除节点id
- ./redis-trib.rb del-node 127.0.0.1:7002 3f71b479f778d1fd5f9eaa7e78bdabb55304a992
- 注意:
  1.被删除节点必须是从节点或没有被分配hash solts 的节点

集群在线分片

# 在线分片 reshard 集群中任意节点 
- ./redis-trib.rb reshard 127.0.0.1:7000

17.Redis实现分布式Session管理

17.1 管理机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tT8YlN7V-1671432930334)(D:\blog-hexo\hexo-images\msm.png)]

​ redis的sesssion管理是利用Spring提供的session管理解决方案,将一个应用session交给redis存储,整个应用中所有的session请求都会去redis中获取对应的session数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Owe8hs6g-1671432930335)(D:\blog-hexo\hexo-images\nginx管理session.png)]

- RSM:Redis Session Manager

- 整合:基于某个应用的整合
  原理:
  		1.基于应用方式session管理

17.2 开发session管理

1、引入依赖

<!--spring-session-data-redis-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

2、开发Session管理配置类

@Configuration
@EnableRedisHttpSession
public class RedisSessionManager {
}

3、打包测试即可

18 分布式Session的解决方案

  • 使用cookie来完成(不安全)
  • 使用Nginx中的ip绑定策略,同一个IP只能在指定的同一个机器访问(不支持负载均衡)
  • 利用数据库同步session(效率不高)
  • 使用tomcat内置的session同步(同步可能会产生延迟)
  • 使用Token替代session
  • 使用spring-session以及集成好的解决方案,存放在redis/Memcached中
18.1 Memcached

memcached是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,并且被许多网站使用。这是一套开放源代码软件,以BSD license授权发布。
​ memcached缺乏认证以及安全管制,这代表应该将memcached服务器放置在防火墙后。
​ memcached的API使用三十二比特的循环冗余校验(CRC-32)计算键值后,将数据分散在不同的机器上。当表格满了以后,接下来新增的数据会以LRU机制替换掉。由于memcached通常只是当作缓存系统使用,所以使用memcached的应用程序在写回较慢的系统时(像是后端的数据库)需要额外的代码更新memcached内的数据。

18.2 spring-session的思想

参考博客: https://www.cnblogs.com/youzhibing/p/7348337.html

​ 设计一个Filter(过滤器),利用HttpServletRequestWrapper,实现自己的getSession()方法,接管创建和管理session数据的工作。这便是spring-session的实现思路。

SpringBoot 中使用spring-session

其他

  • 解决centos6 无法使用yum源的问题

https://blog.csdn.net/fly19920602/article/details/114320181

  • 重新创建集群

    因为克隆的虚拟机redis集群出现了问题,所以选择重新创建集群

https://blog.csdn.net/qq_24392951/article/details/101351101

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值