redis的基本操作And数据持久化方式以及redis实现mybatis缓存

Redis
1.NoSql
# NoSql(Not Only Sql),不仅仅是sql,泛指非关系型数据库
2.NoSql的诞生
	随着互联网web2.0网站的兴起,传统的关系型数据库在高并发和特大规模的纯动态网站已经显得力不从心,暴露了很多难以克服的问题,如图片、音频、视频的存储等,传统数据库只能存储结构化的数据,而对于非结构的数据支持不够完善,NoSql这门技术,更好的解决了这些问题,它告诉世界不仅仅是sql.
3.NoSql数据库的四大分类
# 键值对(key-value)存储数据库
	这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据.
优点:
	key/value模型对于IT系统来说,优势在于简单、易部署.
缺点:
	如果DBA(database admin)只对部分值进行查询或更新的时候,key/value就显得效率低下.
常见数据库:
	Redis
# 列存储数据库
	这类数据库通常是应对分布式存储的海量数据.
特点:
	键依然存在,但是它是指向了多个列.这些列是由列家族来安排的
常见数据库:
	HBase
# 文档型数据库
	文档型数据库的灵感来源于Lotus Notes办公软件,它的存储同键值存储相类似
	Bson--->Json  {"id":"1","name":"554"}
优点:
	该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如Json.文档型数据库可以看做是键值数据库的升级版,允许之间嵌套键值.而且文档型数据库比键值数据库的查询效率更高
缺点:
	相较而言,事务支持不友好
常见数据库:
	MongDb
# 图形(Graph)数据库---图片 音频 视频  (一般会放到文件服务器)
	图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上
** NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型.许多NoSQL数据库都有REST式的数据接口或者查询API.如:Neo4J、InfoGrid、Infinite Graph.
4.NoSql适用场景
1.数据库类型比较简单
2.需要灵活性更强的IT系统
3.对数据性能要求较高(读)
4.不需要高度的数据一致性 NoSql弱化事务/没有事务
5.对于给定key,比较容易映射复杂值的环境(redis)
5.什么是Redis
# redis是一个开源(bsd许可)的内存数据结构存储,用作数据库、缓存和消息代理.
6.Redis的特点
1.Redis是一个高性能key/value内存型数据库
2.Redis支持丰富的数据类型(String,list,set,zset,hash)
3.Redis支持数据持久化---将内存中的数据持久化到磁盘
4.Redis单线程,单进程且效率高(不支持并发操作)
7.Redis安装
1.下载redis
2.准备一台机器安装redis(redis的底层由c编写)
3.将redis上传到linux系统中并解压4.编译安装redis   
	a)进入redis,解压安装包  
    b)安装gcc (yum install -y gcc)
    c)执行make命令     
    	如果出现 zmalloc.h:50:31:致命错误:jemalloc/jemalloc.h:没有那个文件或目录   
        make MALLOC=libc    
    d)安装 make install 会直接安装到当前的redis的源码包,日后找起来不方便 
        make install PREFIX=/usr/redis  
    e)进入redis的安装目录 
    bin 用来存放redis中可执行二进制文件 启动 关闭 等文件 
    启动redis服务脚本:    redis-server
                        ./redis-server
    连接redis客户端脚本:  redis-cli
                       ./redis-cli -p 端口号 -u 主机ip       
    注意:redis 默认端口号为 6379

在这里插入图片描述

8.Redis中key相关指令
redis存储的数据是以 key value 形式存储 (key都为string)
1.删除指定的key
	del key1 key2 ...   删除一到多个key
2.查看key
	keys *				查看所有的key
	keys h?llo			?  代表匹配任意一个字符
	keys h*llo			*  代表匹配零到多个字符
	keys h[ae]llo		[] 匹配一个[]中包含的字符
3.判断某个key是否存在
	exists key ...		判断一个key或多个key是否存在
4.给已经存在的key设置过期时间
	expire key(单位 秒)			如验证码的过期时间 	
5.切换库(redis只有16个库,默认使用的是0库)
	select 库的编号(可用编号 0-15) 
6.移动key到指定的DB
	move key 库的编号
7.给已经存在的key设置过期时间
	pexpire key(单位 毫秒)
8.查看key的过期时间
	ttl key 返回单位 秒
	注意:
		返回 -1  代表key永久存在
		返回 -2  代表key不存在
		返回 >=0 代表key过期时间
9.查看key过期时间
	pttl key 返回单位 毫秒
		返回 -1  代表key永久存在
		返回 -2  代表key不存在
		返回 >=0 代表key过期时间
10.随即返回一个key
	randomkey
11.修改key的名字
	rename key newkey
12.查看key对应值的类型
	type key
	值的类型:
		string
		list
		set
		zset
		hash
9.Redis中常用的数据库相关的指令
说明 :  
	使用redis的默认配置器动redis服务后,默认会存在16个库,下标从0-15
可以使用select 库的编号 来选择一个redis的库
1.	清空当前的库   flushdb
2.	清空全部的库   flushall
3.  ./redis-cli -p 端口号 -u 主机ip --raw  展示中文

10.Redis的 String 类型的操作 key value

1.命令

命令说明
set设置一个key/value
get根据key获得对应的value
mset一次设置多个key value
mget一次获得多个key的value
getset返回原始key的值,同时设置新值
strlen获得对应key存储value的长度
append为对应key的value追加内容 返回值是追加内容后字符串的长度
getrange截取value的内容(下标0 开始)
setex设置一个key存活的有效期(秒)新添加key的同时添加过期时间
psetex设置一个key存活的有效期(豪秒)新添加key的同时添加过期时间
setnx存在不做任何操作,不存在添加
msetnx可以同时设置多个key,原子操作(只要有一个key存在就不做任何操作)
decr进行数值类型的-1操作
decrby根据提供的数据进行减法操作
Incr进行数值类型的+1操作
incrby根据提供的数据进行加法操作
incrbyfloat根据提供的数据加入浮点数

11.Rdeis的List类型的操作

1.图示

在这里插入图片描述

2.命令

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

12.Redis的Set类型的操作

1.图示

在这里插入图片描述

2.命令

命令说明
sadd为集合添加元素
smembers显示集合中所有元素 无序
scard返回集合中元素的个数
spop随即删除n个元素,并返回对应删除的n个元素 n—>数字
smove从一个集合中向另一个集合移动元素 smove source(原始集合) destination(目标集合) member(元素)
srem从集合中删除指定的member(1-n个)
sismember判断一个集合中是否含有这个元素
srandmember随机返回元素 默认返回一个,可以手动指定返回元素的个数
sdiff去掉第一个集合中其它集合含有的相同元素
sinter求交集
sunion求和集

13.Redis的ZSet类型的操作

1.图示 ZSet ===> ScoreSet(分数set) ===> SortSet(排序set)

在这里插入图片描述

2.命令

命令说明
zadd添加一个有序集合元素
zcard返回集合的元素个数
zrange返回一个范围内的元素(zrange zset 0 -1 withscores 遍历所有并展示分数)
zrangebyscore按照分数查找一个范围内的元素
zrank返回排名(返回index)
zrevrank倒序排名(返回index)
zscore显示某一个元素的分数
zrem移除某一个元素
zincrby给某个特定元素加分

14.Redis的Hash类型的操作

1.图示
在这里插入图片描述

2.命令

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

15.Redis中的小细节
1.redis中的端口号默认为 6379
	1.如何修改redis默认端口:
		a)将源码包中的redis.conf 移动到 redis的安装目录/bin 下
        b)vim redis.conf  修改  port 自定义端口号
		注意: 
			想要配置的端口号生效,必须启动redis的同时加载配置文件
			./redis-server redis.conf
        	./redis-cli -p 自定义端口号
2.redis中默认存在16个库
	1.修改库的数量
		vi redis.conf
		databases 自定义数量(小于等于16)
3.redis以 后台进程启动
	vi redis.conf
	daemonize(守护进程) yes (后台启动)
4.redis可视化操作工具
	redis-desk-manager 客户端工具
	#注意:
		#默认redis服务器只能被本地连接访问 
		#想要远程访问,必须开启远程访问权限
		#vim redis.conf
		#修改 bind 0.0.0.0   所有客户端都可以访问

16.Redis中的持久化机制
1.什么是Redis的持久化
#将redis中的数据存储到硬盘里面
#在redis中的操作的是内存中的数据	
2.Redis两种持久化方式
#快照(snapshotting)也称RDB      快照持久化(默认开启)
#AOF(append only file)         只追加文件
3.两大持久化机制
  1. 快照持久化

    定义:
    	这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认持久化方式,保存的文件是以.rdb形式结尾的文件因此这种方式也称之为RDB方式
    
    a)	快照持久化也是redis中的默认开启的持久化方案, 根据redis.conf中的配置,快照将被写入dbfilename指定的文件里面(默认是dump.rdb文件中)
    b)	根据redis.conf中的配置,快照将保存在dir选项指定的路径上
    c)	创建快照的几种方式
    
    1.客户端可以使用BGSAVE命令来创建一个快照,当接收到客户端的BGSAVE命令时,redis会调用fork¹来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令请求
    	#名词解释 : fork当一个进程创建子进程的时候,底层的操作系统会创建该进程的一个副本,在类unix系统中创建子进程的操作会进行优化:在刚开始的时候,父子进程共享相同内存,直到父进程或子进程对内存进行了写之后,对被写入的内存的共享才会结束服务(不会阻塞redis服务)
    
    2.客户端还可以使用SAVE命令来创建一个快照,接收到SAVE命令的redis服务器在快照创建完毕之前将不再响应任何其他的命令
    	#注意 :  SAVE命令并不常用,使用SAVE命令在快照创建完毕之前,redis处于阻塞状态,无法对外服务
    
    3.当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器
    
    4.如果用户在redis.conf中设置了save配置选项,redis会在save选项条件满足之后自动触发一次BGSAVE命令,如果设置多个save配置选项,当任意一个save配置选项条件满足,redis也会触发一次BGSAVE命令
    save   时间()  key变化次数
    save    900          1   
    save    300          10   
    save     1          10000
    
  2. AOF持久化(默认不开启)

    定义:
    	这种方式可以将客户端执行的所有写命令记录到日志文件中
    
    1.开启AOF   vim redis.conf
    	appendonly yes                   开启
    	appendfilename "appendonly.aof"  日志名字为appendonly.aof
    	日志文件生成的位置与快照的路径一致
    2.日志的同步频率
    	# appendfsync always
    	appendfsync everysec(默认使用)
    	# appendfsync no
    
    选项同步频率
    always每个redis写命令都要同步写入硬盘,严重降低redis速度
    everysec每秒执行一次同步显式的将多个写命令同步到磁盘
    no由操作系统决定何时同步

    三种日志记录频率的详细分析 :

    1.如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制;
    	注意 :  转盘式硬盘在这种频率下200左右个命令/s ; 固态硬盘(SSD) 几百万个命令/s;
    	警告 :  使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的写入放大问题,导致将固态硬盘的寿命从原来的几年降低为几个月
    
    2.为了兼顾数据安全和写入性能,用户可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis可以保证,即使系统崩溃,用户最多丢失一秒之内产生的数据(推荐使用这种方式)
    
    3.最后使用no选项,将完全有操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响但是系统崩溃时,会丢失不定数量的数据,另外如果用户硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘数据填满时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢(不推荐使用)
    

    3.AOF重写

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

    1.重写AOF文件两种方式

    a. 执行BGREWRITEAOF命令
    
    b. 配置redis.conf中的auto-aof-rewrite-percentage选项
    

    a. 执行BGREWRITEAOF命令

    1. redis调用fork ,现在有父子两个进程 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
    
    2. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
    
    3. 当子进程把快照内容已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
    
    4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
    
    	#注意 :  
        重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,替换原有的文件这点和快照有点类似。(AOF重写过程完成后会删除旧的AOF文件,删除一个体积达几十GB大的旧的AOF文件可能会导致系统随时挂起 )
    

    b. 配置redis.conf中的auto-aof-rewrite-percentage选项

    1. AOF重写也可以使用auto-aof-rewrite-percentage 100
       和auto-aof-rewrite-min-size 64mb来自动执行BGREWRITEAOF.
    	
       #说明: 
       	如果设置auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%),会自动触发,如果重写过于频繁,用户可以考虑将auto-aof-rewrite-percentage设置为更大
    

    4.两种持久化方案的总结

AOF持久化既可以将丢失的数据的时间降低到1秒(甚至不丢失任何数据),那么我们还有什么理由不是用AOF呢?


~~~javascript
#注意 : 
	这个问题实际上并没有这么简单,因为redis会不断将执行的写命令记录到AOF文件中,所以随着redis运行,AOF文件的体积会不断增大,在极端情况下甚至会用完整个硬盘,还有redis重启重新执行AOF文件记录的所有写命令的来还原数据集,AOF文件体积非常大,会导致redis执行恢复时间过长
	两种持久化方案既可以同时使用,又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定
 无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方)
17.SpringData操作Redis
  1. 使用spring-data操作redis

    Spring-Data-Redis项目(简称SDR)对Redis的Key-Value数据存储操作提供了更高层次的抽象,类似于Spring Framework对JDBC支持一样。

    Spring Data Redis使得在Spring应用中读写Redis数据库更加容易

  2. 连接Redis服务(单机)

    在Spring Data Redis中通过org.springframework.data.redis.connection包中的RedisConnection和RedisConnectionFactory类来获取Redis连接。
    在这里插入图片描述

  3. 连接redis服务集群
    在这里插入图片描述

  4. RedisTemplate的支持

    熟悉Spring的JdbcTemplate对象的话,应该大概能猜出来RedisTemplate的作用了,RedisTemplate对象对RedisConnection进行了封装,它提供了连接管理,序列化等功能,它对Redis的交互进行了更高层次的抽象。

    key类型操作

    ValueOperationsRedis String/Value 操作
    ListOperationsRedis List 操作
    SetOperationsRedis Set 操作
    ZSetOperationsRedis Sort Set 操作
    HashOperationsRedis Hash 操作

    value约束操作

    BoundValueOperationsRedis String/Value key 约束
    BoundListOperationsRedis List key 约束
    BoundSetOperationsRedis Set key 约束
    BoundZSetOperationsRedis Sort Set key 约束
    BoundHashOperationsRedis Hash key 约束
  5. 创建Springboot项目

  6. 引入相关依赖

    <dependency>
    	<groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  7. Springboot相关配置

    server:
      port: 8989
    spring:
      redis:
        database: 0               #连接redis的哪个库
        host: 192.168.132.16      #主机ip
        port: 7000	              #端口号
    
  8. 自动注入

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate; #对String类型良好的支持[推荐使用]
    
  9. 测试redisTemplate

    4.1 简单的String操作
    在这里插入图片描述
    4.2 简单的List操作
    在这里插入图片描述
    4.3 简单的Set操作
    在这里插入图片描述
    4.4 简单的ZSet操作
    在这里插入图片描述
    4.5 简单的Hash操作
    在这里插入图片描述

  10. 注意

    ​ RedisTemplate和StringRedisTemplate,不同之处在于StringRedisTemplate的Key-Value序列化使用的是StringRedisSerializer, RedisTemplate对象是默认使用JdkSerializationRedisSerializer实现

    ​ 使用StringRedisTemplate操作Redis之后的结果是友好的

18.Redis实现mybatis的缓存
# 什么是缓存
	缓存是计算机内存里的一段数据
# 缓存数据的特点
	由于缓存数据在内存中 所以读写缓存的速度非常的快
# 为什么项目中要使用缓存
	1.在项目中添加缓存可以在一定程度上减轻数据库的压力
    2.可以提高现有网站中的查询效率 加快网站的响应速度
# 项目中是否所有的数据都要使用缓存
	否  加入缓存的数据: 查询比较多 增删改比较少
# 在现有的项目中如何应用缓存
	a.借助于mybatis自身提供的缓存技术
		开启缓存: 在指定模块的mapper.xml中加入如下配置即可:
        <!-- 在mapper文件中开启缓存 -->
        <cache/>
            #注意:
        		放入缓存中的对象必须实现对象序列化接口
# mybatis自身缓存的缺点
	a.自身实现缓存,缓存的数据会占用一定应用服务器内存 导致应用服务处理请求的速度可能变慢
      这种缓存也成为本地缓存
    b.本地缓存在集群架构下不能实现缓存共享
# 解决本地缓存存在的问题
	可以实现分布式缓存服务器 redis
1.什么样的数据适合添加缓存

在这里插入图片描述

2.Redis实现mybatis缓存机制步骤
1.导入jar
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
2.现有项目中如何使用redis作为分布式缓存实现
	a.在当前项目实现mybatis提供cache接口 public class 实现类 implements Cache(接口)
	b.使用自定义cache
		<cache type="自定义cache全限定名"/>
3.创建工具类,通过自定义的工厂工具类获取StringRedisTemplate / RedisTemplate
4.引用序列化工具类,用于SpringRedisTemplate转化获取值的类型
5.要想使用RedisTemplate实现缓存更友好展示,需要改变本身的序列化方式为SpringRedisTemplate的序列化方式
3.自定义工厂
package com.xkdgx.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * ApplicationContextUtils class
 *
 * @author L-JiaHui
 * @date 2020/3/13
 */
@Component /**当前类交由spring工厂管理*/
public class ApplicationContextUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    /**获取当前的spring工厂*/
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**根据id获取对应的bean*/
    public static Object getBean(String id){
        Object bean = applicationContext.getBean(id);
        return bean;
    }

    /**根据类型获取对应的bean*/
    public static Object getBean(Class clazz){
        Object bean = applicationContext.getBean(clazz);
        return bean;
    }

    /**根据id+类型获取对应的bean*/
    public static Object getBean(String id,Class clazz){
        Object bean = applicationContext.getBean(id, clazz);
        return bean;
    }
}
4.序列化工具类(直接拿来用)
package com.xkdgx.util;
import java.io.*;
public class SerializeUtils {
    //序列化对象为字符串  其它类型--->转化为String
    public static String serialize(Object obj)  {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream;
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(obj);
            String string = byteArrayOutputStream.toString("ISO-8859-1");
            objectOutputStream.close();
            byteArrayOutputStream.close();
            return string;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //反序列化为对象   String--->其它类型
    public static Object serializeToObject(String str){
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(str.getBytes("ISO-8859-1"));
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Object object = objectInputStream.readObject();
            objectInputStream.close();
            byteArrayInputStream.close();
            return object;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
5.自定义实现类(使用SpringRedisTemplate—缓存)需要引用序列化工具类
package com.xkdgx.controller;
import com.xkdgx.util.ApplicationContextUtils;
import com.xkdgx.util.SerializeUtils;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.locks.ReadWriteLock;

/**
 *  RedisCache1 class
 *
 * @author L-JiaHuui
 * @date 2020/3/13
 * */
public class RedisCache1 implements Cache {

    /**
     *  注意:
     *      1.必须提供有参构造方法且有一个String id 的参数
     *      2. id:当前mapper.xml的namespace
     *      3.getId 返回的就是该id
     * */
    private String id;
    /**有参构造*/
    public RedisCache1(String id) {
        this.id = id;
    }

    /**返回当前的namespace*/
    @Override
    public String getId() {
        return id;
    }

    /**
     *  添加缓存:
     *      参数1:key     参数2:value
     *      添加缓存需要注入StringRedisTemplate,由于当前类是交由mybatis实例化,
     *      所以需要自定义工具类从当前的工厂中获取到StringRedisTemplate
     * */
    @Override
    public void putObject(Object key, Object value) {
        /**获取StringRedisTemplate*/
        StringRedisTemplate stringRedisTemplate = (StringRedisTemplate) ApplicationContextUtils.getBean(StringRedisTemplate.class);
        /**StringRedisTemplate操作hash*/
        HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
        /**
         *   StringRedisTemplate:
         *   key-value  必须都为String
         *   value 可能为---> 对象、集合、地址
         *   将查询到的值转化为字符串(使用序列化工具类将其序列化)
         * */
        String val = SerializeUtils.serialize(value);
        /**插入值*/
        hash.put(id,key.toString(),val);
    }

    /**取出缓存*/
    @Override
    public Object getObject(Object key) {
        /**获取StringRedisTemplate*/
        StringRedisTemplate stringRedisTemplate = (StringRedisTemplate) ApplicationContextUtils.getBean(StringRedisTemplate.class);
        /**StringRedisTemplate操作hash*/
        HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
        /**
         *  在进行取值之前先进性判断
         *  查询缓存:
         *      有缓存:返回缓存中的值
         *      无缓存:进入添加缓存,调用添加缓存的方法,去数据库查数据,添加缓存   return null
         * */
        /**判断大键(id)和map集合中的小键(key.toString)是否存在*/
        if (hash.hasKey(id,key.toString())){
            /**根据大键和小键拿到值(拿到的值是String类型的 在添加时进行了序列化)*/
            String val = (String) hash.get(id, key.toString());
            /**将拿到的值进行反序列化,转化为原有的类型,进行返回*/
            Object o = SerializeUtils.serializeToObject(val);
            return o;
        }
        return null;
    }

    @Override
    public Object removeObject(Object key) {
        return null;
    }

    /**清空缓存*/
    @Override
    public void clear() {
        /**获取StringRedisTemplate*/
        StringRedisTemplate stringRedisTemplate = (StringRedisTemplate) ApplicationContextUtils.getBean(StringRedisTemplate.class);
        /**根据大键(id)进行删除,实现清空缓存*/
        stringRedisTemplate.delete(id);
    }

    @Override
    public int getSize() {
        return 0;
    }

    //Redis单线程不需要
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}
6.自定义实现类(使用RedisTemplate—缓存)
package com.xkdgx.controller;
import com.xkdgx.util.ApplicationContextUtils;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.concurrent.locks.ReadWriteLock;
/**
 *  RedisCache2 class
 *
 * @author L-JiaHuui
 * @date 2020/3/13
 * */
public class RedisCache2 implements Cache {
    /**
     *  注意:
     *      1.必须提供有参构造方法且有一个String id 的参数
     *      2. id:当前mapper.xml的namespace
     *      3.getId 返回的就是该id
     * */
    private String id;
    /**有参构造*/
    public RedisCache2(String id) {
        this.id = id;
    }
    /**返回当前的namespace*/
    @Override
    public String getId() {
        return id;
    }

    /**
     *  添加缓存:
     *      参数1:key     参数2:value
     *      添加缓存需要注入RedisTemplate,由于当前类是交由mybatis实例化,
     * */
    @Override
    public void putObject(Object key, Object value) {
        /**获取RedisTemplate*/
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        /**将RedisTemplate的序列化方式JdkSerializationRedisSerializer改为
         * StringRedisTemplate的Key-Value序列化,是展示更加友好
         * */
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);

        /**RedisTemplate操作hash*/
        HashOperations hash = redisTemplate.opsForHash();
        hash.put(id,key.toString(),value);
    }


    /**取出缓存*/
    @Override
    public Object getObject(Object key) {
        /**获取RedisTemplate*/
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        /**改变RedisTemplate的序列化方式*/
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);

        /**RedisTemplate操作hash*/
        HashOperations hash = redisTemplate.opsForHash();
        Object o = hash.get(id, key.toString());
        return o;
    }

    @Override
    public Object removeObject(Object key) {
        return null;
    }

    /**清空缓存*/
    @Override
    public void clear() {
        /**获取RedisTemplate*/
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        /**改变RedisTemplate的序列化方式*/
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);

        /**删除大键(id)*/
        redisTemplate.delete(id);
    }

    @Override
    public int getSize() {
        return 0;
    }

    //Redis单线程不需要
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}
19.缓存存在的问题
# 缓存击穿
	缓存穿透,是指查询一个数据库一定不存在的数据.正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存.如果数据库查询对象为空,则不放进缓存.
解决方案:
	a.查到的数据即使为空,也添加缓存(并设置较短的过期时间)
	b.使用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层数据库的查询压力.
# 布隆过滤器(Bloom Filter)
	本质上布隆过滤器是一种数据结构,比较巧妙的概率性数据结构
特点是:
	高效的插入和查询,可以用来告诉你"某样东西一定不存在或者可能存在"
# 缓存雪崩
	缓存雪崩,是指在某一个时间段,缓存集中过期失效.
解决方案:
	a.缓存永不过期
	b.设置不同的过期时间,让缓存失效的时间点尽量均匀
	c.缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量.比如对某个key只允许一个线程查询数据和写缓存,其他线程等待.
20.Redis集群
Redis在3.0后开始支持Cluster(模式)模式,目前redis的集群支持节点的自动发现,
支持slave-master选举和容错,支持在线分片(sharding shard)等特性
选举和容错: 主节点挂掉之后,选举一个从节点代替主节点
分片:      把数据拆分成n多份,放在每一个机器上
  1. Redis集群架构图
    在这里插入图片描述
  2. Redis的集群细节
1.所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
2.节点的fail是通过集群中超过半数的节点检测失效时才生效.  (一般搭建的集群是奇数个)
3.客户端与redis节点直连,不需要中间proxy(代理)层.客户端不需要连接集群所有节点,连接集群中任意一个可用节点即可
4.redis cluster把所有的物理节点映射到slot[0-16383]
 cluster 管理 node 管理 solt 管理 数据   (解决单点压力,分向不同的node)
solt: 槽
	Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽 (hash slot)的方式来分配的。redis cluster 默认分配了 16384 个slot,当我们set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈希槽区间的节点上,具体算法就是:CRC16(key) % 16384。

(为什么是16384,选取了16384是因为crc16会输出16bit的结果,可以看作是一个分布在0-2^16-1之间的数,redis的作者测试发现这个数对2^14求模的会将key在0-2^14-1之间分布得很均匀,因此选了这个值。)

在构建redis cluster集群时,master必须大于等于3,否则会创建失败。并且,当集群中存活的master节点数小于总节点数的一半的话,集群就无法提供服务了。

例:我们有三个master节点A、B、C,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:
	节点A:0 ~ 5460
	节点B:5461 ~ 10922
	节点C:10923 ~ 16383
节点在收到读写请求时,会根据CRC16(key) % 16384算出的槽号去查是否指向自己,如果是则进行处理,如果不是,则返回moved错误,moved错误携带正确的节点IP和端口号返回客户端并指引其转向执行,而后客户端每次关于该key都会去moved返回的节点执行。

当节点的key正在迁移的时候,收到关于该key的请求,那么节点会返回ask错误,并但会正确的节点ip和端口号给客户端去执行。但是这个转向只对本次请求有效,后面关于该key的请求还是会发送到目前正在处理key迁移的节点,直到key迁移完毕并发送广播通知。
当有新节点D加入时,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上。会变成下面这样:
	节点A:1364 ~ 5460
	节点B:6826 ~ 10922
	节点C:12287 ~ 16383
	节点D:0 ~ 1364, 5461 ~ 6826, 10923 ~ 12287
删除节点也是类似,数据会均匀的迁移到剩余节点上,迁移完成后就可以删除这个节点了。
  1. redis容错架构图
    在这里插入图片描述

  2. 容错的细节

    1.如果半数以上master节点与master节点通信超时(cluster-node-timeout),认为当前master节点挂掉
    2.收到((error) CLUSTERDOWN The cluster is down)错误,整个集群不可用(cluster_state:fail),所有对集群的操作做都不可用
    	#注意:
        	如果集群任意master挂掉,且当前master没有slave.集群进入fail状态
    		也可以理解成进群的slot映射[0-16383]不完成时进入fail状态.
    
  3. redis集群搭建

    #注意 : 
    	1.	判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数.
    	2.	搭建集群至少需要三个主节点,三个从节点,至少需要6个节点
    
   1.准备至少六台机器
   	port 7000 - 7005
   2.修改六台机器的配置文件
   	cluster-enabled  yes                    //开启集群模式       
   	cluster-config-file  nodes-.conf 		//集群节点配置文件    
   	cluster-node-timeout  15000      	   //集群节点超时时间(默认 15秒)
   	appendonly  yes   		               //开启AOF持久化
   3.构建集群
   	./redis-server /root/7000/redis.conf
   	./redis-server /root/7001/redis.conf
   	./redis-server /root/7002/redis.conf
   	./redis-server /root/7003/redis.conf
   	./redis-server /root/7004/redis.conf
   	./redis-server /root/7005/redis.conf
   
   	./redis-cli --cluster create 192.168.244.15:7000 192.168.244.15:7001 192.168.244.15:7002 192.168.244.15:7003 192.168.244.15:7004 192.168.244.15:7005 --cluster-replicas 1
   	注意:
   		修改成自己定义的端口号

在这里插入图片描述

4.	输入yes后集群确定搭建,输入其它命令不创建集群

在这里插入图片描述

5.客户端操作集群
	a)集群中的节点都是平等的,连接集群中的任意节点即可
		./redis-cli -p 任意port -c
		-c: 集群形式
6.查看接群节点的状态
	a)使用./redis-cli --cluster check ip:port 查看集群中节点的详细 
 		./redis-cli --cluster check 192.168.64.10:任意可用port
 

在这里插入图片描述

7.节点状态说明
	#主节点 :
    	√ 主节点存在hash slots,且主节点的hash slots 没有交叉
		√ 主节点不能删除
		√ 一个主节点可以有多个从节点
		√ 主节点宕机时多个副本之间自动选举主节点
	#从节点 :
    	√ 从节点没有hash slots
		√ 从节点可以删除
		√ 从节点不负责数据的写,只负责数据的同步
		√ 从节点对应的主节点宕机,该从节点会变为主节点
  1. 集群节点的操作

    1.向集群中添加一个主节点
    	./redis-cli --cluster add-node 192.168.132.16:7006 192.168.132.16:7005
    

在这里插入图片描述

  参数说明
	i.	第一个参数是新节点的地址,
	ii.	第二个参数是任意一个已经存在的节点的IP和端口
	#注意 : 
    	1.该节点必须以集群模式启动
        2.默认情况下该节点就是以master节点形式添加,但是该节点没有hashslots

2.向集群中添加副本节点
	./redis-cli --cluster add-node 192.168.132.16:7007 192.168.132.16:7006 --cluster-slave

在这里插入图片描述

  参数说明 : 
	add-node 添加从节点
	参数一:新的从节点
	参数二:集群中的任意节点
	#注意:
       当添加副本节点时没有指定主节点,redis会随机给副本节点较少的主节点添加当前副本节点
---

~~~txt
3.为某个指定主节点添加从节点
	./redis-cli --cluster add-node 192.168.244.15:7007 192.168.244.15:7000 --cluster-slave --cluster-master-id 747e1871ea74103e4a6dd0137dd5a222270462b0

在这里插入图片描述

  参数说明 :
    参数一: 新添加的从节点
    参数二: 集群中的任意节点
    --cluster-master-id 主节点id

4.从集群中删除副本节点
	./redis-cli --cluster del-node 192.168.244.15:7005 17a31e662ff7b1854da2b3c8e62088814b4a884c
   参数说明:
	参数一是任意一个节点的地址 
	参数二是想要删除的节点id
	#注意 :
		被删除的节点必须是从节点或没有被分配hash slots的节点


5.节点的从新分片
	a)集群节点(hash slots)的从新分配(reshard)
		使用 ./redis-cli --cluster reshard 192.168.132.16:7006
		#说明 : 
			需要指定集群中其中一个节点的地址就会自动找到集群中的其他主节点。

在这里插入图片描述

   	b)为哪个主节点分配hash slots

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

21.后台连接Redis集群
  1. Jedis操作Redis

在这里插入图片描述

​ 注意:

​ 避免redis某个节点宕机的问题,一般会配置多个连接

​ 集群模式下不能再切换库,只能用0号库

​ flushall, flushdb 都不能用

  1. SpringData操作Redis

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值