Redis!!!
内存型数据库
特点
- Redis是一个高性能key/value内存型数据库
- Redis支持丰富的数据类型
- Redis支持持久化
- Redis单线程,单进程
安装
准备环境
centos7
下载redis安装包
redis-6.0.9.tar.gz
解压部署
- 解压文件
tar -xzvf redis-5.0.10.tar.gz
- 安装gcc
yum install -y gcc
- 进入到解压文件夹
make MALLOC=libc
- 编译完成后
make install PREFIX=/usr/redis
- 进入/usr/redis/bin目录启动redis服务
./redis-server
- 新建一个会话窗口
./redis.cli -h localhost -p 6379 --raw(加上这个就可以显示中文 一般不用)
redis细节
-
redis启动服务的细节
直接使用. / redis-server方式启动使用的是redis-server这个shell脚本中默认配置 -
如何在启动redis时指定配置文件启动
默认在redis安装完成之后再安装目录没有任何配置文件,需要在源码目录中复制配置文件到安装目录在原先的解压包下将redis.confⅠ
cp redis.conf /usr/redis/
-
使用自己配置的配置文件启动
./redis-server ../redis.conf
-
库的概念
库:database用来存放数据一个基本单元一个库可以存放key-value键值对 redis中每一个库都有一个唯一名称 编号从0开始默认库的个数:16个库 库的编号:0-15 默认使用是0号库;
切换库命令: select dbid (库编号)
-
如何清除库
flushdb 清空当前库 flushall 清空所有
守护进程 后端开启
查看进程
ps aux|grep redis
基本命令
数据库操作命令
#选中库select 库的编号#清库flushdb #清空当前库flushall #清空所有
key的操作命令
#del语法: del key [key...]作用:删除一个或多个key 不存在的key被忽略返回:删除的个数#exists语法:exists key作用:检查是否存在返回:存在返回1 否则0#expire key seconds#expire key 毫秒语法:expire key seconds作用:给指定的key设置生存时间 到期自动删除返回:设置成功返回1#keysKEYS * 匹配数据库中所有key .KEYS h?1lo 匹配hello , hallo和hxllo等。KEYS h*llo 匹配hllo和heeeeello 等。KEYS h[ae]llo 匹配hello和hallo ,但不匹配hillo。特殊符号用“\”隔开#move key名 库名移动#TTL ttl 查看还差多少秒过期pttl 查看还差多少毫秒过期返回-1:永久存储返回-2:没查到#renamerename key keynewname修改名字 要是keynewname存在则覆盖#TYPE语法:TYPE key作用:返回key所储存的值的类型。返回值:none (key不存在)string(字符串)list(列表)set(集合)zset(有序集)hash(哈希表)
String类型的操作指令
内存存储模型
常用命令
命令 | 说明 |
---|---|
set | 设置一个key/value |
get | 根据key获取对应的value |
mset | 一次设置多个key/value |
mget | 一次获得多个key的value |
getset | 获得原始key的值并重新赋值 |
strlen | 获得对应key存储value的长度 |
append | 为对应的key追加内容 |
getrange | 截取value的内容 索引0开始 |
setex | 设置一个key存活的有效期(秒) |
psetex | 设置一个key存活的有效期(毫秒) |
setnx | 存在不做任何操作,不存在则添加 |
msetnx | 可以同时设置多个key,只要有一个存在就不做任何操作 |
decr | 进行数值类型的-1操作 |
decrby | 根据提供的数据进行减法操作 |
lncr | 进行数值类型的+1操作 |
incrby | 根据提供的数据进行加法操作 |
incrbyfloat | 根据提供的数据加入浮点数 |
List类型
有序,可以重复
内部存储类型
常用命令
命令 | 说明 |
---|---|
lpush | 将某个值加入到一个key列表头部 |
lpushx | 将某个值加入到一个key列表头部,key必须存在 |
rpush | 将某个值加入到一个key列表末尾 |
rpushx | 同rpush,但是必须要保证这个key存在 |
lpop | 返回和移除列表的左边第一个元素 |
rpop | 返回和移除列表的右边第一个元素 |
lrange 0 -1 | 获取某一个下标区间内的元素 |
llen | 获取列表元素个数 |
lset | 设置某一个指定索引的值(索引必须存在) |
lindex | 获取某一个指定索引位置的元素 |
lrem | 删除重复元素 |
ltrim | 保留列表中特定区间内的元素 |
linsert | 在某一个元素之前,之后插入新元素 |
Set类型
元素无序,不能重复
内部存储模型
常用命令
命令 | 说明 |
---|---|
sadd | 为集合添加元素 |
smembers | 显示集合中所有元素无序 |
scard | 返回集合中元素的个数 |
spop | 随机返回一个元素并将元素在集合中删除 |
smove | 从一个集合中刺另一个集合移动元素 |
srem | 从集合中删除一个元素 |
sismember | 判断一个集合中是否含有这个元素 |
srandmember | 随机返回元素 |
sdiff | 去掉第一个集合中其它集合含有的相同元素 |
sinter | 求交集 |
sunion | 求和集 |
ZSet类型
可排序的set集合 排序,不能重复
存储模型
常用命令
命令 | 说明 |
---|---|
zadd | 添加一个有序集合元素 |
zcard | 返回集合的元素个数 |
zrange升序zrevrange降序 | 返回一个范围内的元素 |
zrangebyscore | 按照分数查找一个范围内的元素 |
zrank | 返回排名 |
zrevrank | 倒序排名 |
zscore | 显示某一个元素的分数 |
zrem | 移除某一个元素 |
zincrby | 给某个特定元素加分 |
hash类型
key(String) value(key(无序的) value)
内存模型
常用命令
命令 | 说明 |
---|---|
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加入浮点值 |
开启远程连接
默认是不开启的,需要去修改配置文件,修改为0.0.0.0 允许一切远程客户端连接
数据持久化
有两种方式 快照持久化 与 AOF持久化
快照持久化
就是每隔一段时间自动保存在磁盘上,可以自己设置时间,默认使用的就是快照持久化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XOiCkAZ1-1619789926314)(redis.assets/image-20201117153006877.png)]
AOF持久化
就是将写入操作打印出日志,之后执行一遍取回数据,但是容易造成多余的重复字段,出现了AOF重写
持久化总结
- 两种持久化方案既可以同时使用(aof),又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定。
- 无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方).
Java操作redis
-
环境准备
引入依赖
<!--引入redis依赖--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
-
创建对象
package com.zhangan.test;import redis.clients.jedis.Jedis;import java.util.Set;//测试redis的连接public class TestRedis { public static void main(String[] args) { //创建jedis客户端对象 Jedis jedis = new Jedis("47.96.234.197",6379); //选择使用一个库 默认0号库 jedis.select(0); //获取redis所有key信息 Set<String> keys = jedis.keys("*"); keys.forEach(key -> System.out.println("key = "+key));// //操作库相关的// jedis.flushDB();//清除当前库// jedis.flushAll();//全部清除 //释放资源 jedis.close(); }}
-
相关api测试
package com.zhangan.test;import org.junit.After;import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;public class TestKey { private Jedis jedis; @Before public void before(){ this.jedis = new Jedis("47.96.234.197",6379); } @After public void after(){ jedis.close(); } //测试key相关 @Test public void testKeys(){ //删除多个key// jedis.del("name","sex"); //判断一个key是否存在// Boolean flag = jedis.exists("height");// System.out.println(flag); //设置key超时时间 Long age = jedis.expire("age", 100); System.out.println(age); //查看一个key的超时时间 Long age1 = jedis.ttl("age"); System.out.println(age1); }}
//测试String相关 @Test public void testKeys(){ //set jedis.set("xingming","小陈"); String xingming = jedis.get("xingming"); System.out.println(xingming); jedis.mset("name1","好人","name2","坏人"); List<String> mget = jedis.mget("name1", "name2"); System.out.println(mget); }
Spring boot整合
Spring Boot pata(数据) Redis中提供了RedisTemplate
和StringRedisTemplate
,其中stringRedisTemplate
是RedisTemplate
的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,RedisTemplate
中的两个泛型都是object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate
的两个泛型都是String,意味着stringRedisTemplate的key和value都只能是字符串。
引入依赖
<!--引入spring data redis依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
配置application.yml
#redisspring: redis: host: 47.96.234.197 port: 6379 database: 0
使用StringRedisTemplate
public class TestStringRedisTemplate { //注入StringRedisTemplate @Autowired private StringRedisTemplate stringRedisTemplate; //操作redis中的key public void testKey(){ stringRedisTemplate.delete("ceshi"); stringRedisTemplate.hasKey("测试"); stringRedisTemplate.type("cehsi"); stringRedisTemplate.keys("*"); stringRedisTemplate.getExpire("ceshi"); stringRedisTemplate.renameIfAbsent("ceshi","new"); stringRedisTemplate.move("ceshi",1); } //操作redis字符串 //opsForValue()操作的就是String @Test public void testString(){ stringRedisTemplate.opsForValue().set("ceshi","小吃"); //设置超时时间 stringRedisTemplate.opsForValue().set("code","2357",120, TimeUnit.SECONDS); //追加 stringRedisTemplate.opsForValue().append("ceshi","lalalala"); } //操作redis List @Test public void testList(){ stringRedisTemplate.opsForList().leftPushAll("listlist222","lalala","lalala2"); stringRedisTemplate.opsForList().leftPush("listlist","lalala","lalala2"); stringRedisTemplate.opsForList().range("listlist",0,-1); } //操作redis set @Test public void testSet(){ stringRedisTemplate.opsForSet().add("setset","1","2"); stringRedisTemplate.opsForSet().members("setset"); stringRedisTemplate.opsForSet().size("setset"); } //操作redis Zset @Test public void testZset(){ stringRedisTemplate.opsForZSet().add("zetzet","张安",100); stringRedisTemplate.opsForZSet().range("zetzet",0,-1); //获取指定元素和分数 Set<ZSetOperations.TypedTuple<String>> zetzet = stringRedisTemplate.opsForZSet().rangeByScoreWithScores("zetzet", 0, 100); zetzet.forEach(typed -> { System.out.println(typed.getValue()); System.out.println(typed.getScore()); });} //操作redis hash @Test public void testHash(){ //创建一个hash类型 放入key value stringRedisTemplate.opsForHash().put("maps","name","张安"); //获取hash中每个key的值 stringRedisTemplate.opsForHash().get("maps","name"); //获取所有的key/value的值 stringRedisTemplate.opsForHash().keys("maps"); stringRedisTemplate.opsForHash().values("maps"); //存放多个key Map<String,String> map = new HashMap<>(); map.put("name","张三"); map.put("age","19"); stringRedisTemplate.opsForHash().putAll("poeple",map); //取出 stringRedisTemplate.opsForHash().multiGet("people", Arrays.asList("name","age")); }
使用RedisTemplate
@SpringBootTest(classes = RedisApplication.class)public class TestRedisTemplate { //注入 @Autowired private RedisTemplate redisTemplate; //opsFor Value String List Set Zset Hash //注入RedisTemplate key Object Value 0bject===> 对象序列化 name new User ()====> name序列化 对象序列化结果 @Test public void testRedisTemplate(){ /** redisTemplate对象中 key和 value 的序列化都是JdkSerializationRedisSerializer key : string value: object 修改默认key序列化方案:key StringRedisserializer */ redisTemplate.setKeySerializer(new StringRedisSerializer()); User user = new User(); user.setId(1); user.setName("张安"); user.setAge("18"); redisTemplate.opsForValue().set("user",user); }}
简化操作,多次操作一个key时可以使用绑定
@SpringBootTest(classes = RedisApplication.class)public class bound { //注入 @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void testBound(){ BoundValueOperations<String, String> name = stringRedisTemplate.boundValueOps("name"); name.set("张安"); name.append("加油");// stringRedisTemplate.boundGeoOps();// stringRedisTemplate.boundHashOps();// stringRedisTemplate.boundListOps();// stringRedisTemplate.boundSetOps();// stringRedisTemplate.boundZSetOps();// stringRedisTemplate.boundStreamOps(); // redisTemplate.boundValueOps();// redisTemplate.boundGeoOps();// redisTemplate.boundHashOps();// redisTemplate.boundListOps();// redisTemplate.boundSetOps();// redisTemplate.boundStreamOps(); }
总结
1.针对于日后处理key value都是String使用StringRedisTemplate
2.针对于日后处理的key value存在对象使用RedisTemplate
3.针对于同一个key多次操作可以使用boundXXxOps () value List set Zset. Hash的api简化书写
redis在项目中的运用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F5lYN0fp-1619789926316)(redis.assets/image-20201118143422854.png)]
分布式缓存实现
-
什么是缓存(Cache)
定义:计算机内存中的一段数据
-
缓存(内存)中的数据特点
读写快 断电即失
-
如何使用缓存,他解决了什么问题
提高网站吞吐量,运行效率,为了减轻数据库的访问压力
-
注意:
使用缓存时,一定是数据库中的数据极少发生修改,更多用于查询
-
本地缓存与分布式缓存
本地缓存
:存在应用服务器内存中数据称之为本地缓存(local cache)
分布式缓存
:存储在当前应用服务器内存之外数据称之为分布式缓存(distribute cache)
集群
:将同一种服务的多个节点放在一起共同对系统提供服务过程称之为集群
分布式
:有多个不同服务集群共同对系统提供服务这个系统称之为分布式系统(distribute system) -
利用mybatis自身本地缓存结合redis实现分布式缓存
a.mybatis中应用级缓存(二级缓存)sqlSessionFactory级别缓存﹑所有会话共享b.如何开启(二级缓存)
mapper.xml 本地缓存
c.查看cache标签缓存实现
结论:mybatis底层默认使用的是 org.apache.ibatis.cache.impl.PerpetualCache实现
d.自定义Rediscache实现
1.通过mybatis默认cache源码得知可以使用自定义cache类implements Cache接口并对里面方法进行实现 2.使用Rediscache实现
<cache type=”xxzx.RedisCache" / > 3.解决关联关系(共享缓存)
<cache-ref namespace="com.zhangan.dao.xxdao"></cache-ref>
缓存优化策略
将放入缓存的key长度减短一些
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-odn7hIbK-1619789926316)(redis.assets/image-20201119110728778.png)]
package com.zhangan.cache;import com.zhangan.util.ApplicationContextUtils;import org.apache.ibatis.cache.Cache;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.util.DigestUtils;import java.util.concurrent.locks.ReadWriteLock;//自定义redis缓存实现public class RedisCache implements Cache { //当前放入缓存的mapper的namespace private final String id; //必须存在构造方法 public RedisCache(String id) { this.id = id; } //封装redisTemplate private RedisTemplate getRedisTemplate() { //使用application工具类获取redisTemplate RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } //封装一个对key进行md5处理方法 private String getKeyToMD5(String key){ return DigestUtils.md5DigestAsHex(key.getBytes()); } @Override public ReadWriteLock getReadWriteLock() { return null; } //返回cache唯一标识 @Override public String getId() { return this.id; } //放入缓存值 @Override public void putObject(Object key, Object value) { //使用redisHash类型作为缓存存储模型 key hashKey value getRedisTemplate().opsForHash().put(id.toString(), getKeyToMD5(key.toString()), value); } //缓存中获取数据 @Override public Object getObject(Object key) { //使用redisHash类型作为缓存存储模型 key hashKey value return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString())); } //注意:这个方法为mybatis保留方法默认没有实现后续版本可能会实现 @Override public Object removeObject(Object key) { return null; } //增删改都走这个 清空缓存 之后查询重新建立新的缓存 @Override public void clear() { //清空namespace getRedisTemplate().delete(id.toString());//清空缓存 } //用来计算缓存的数量 @Override public int getSize() { //获取hash中的key value数量 return getRedisTemplate().opsForHash().size(id.toString()).intValue(); }}
相关概念
缓存穿透(击穿)
定义:客户端查询数据库没有的数据记录,导致缓存在这种情况下无法利用
mybatis中cache解决了缓存穿透:将数据库中没有查询到结果也进行缓存
缓存雪崩
定义:在系统运行的某一时刻,突然系统中缓存全部失效,恰好在这一时刻涌来大量客户端请求,导致所有模块缓存无法利用,大量请求涌向数据库导致极端情况,数据库阻塞或挂起
缓存存储时:业务系统非常大模块多业务数据不同―不同模块在放入缓存时都会设置一个缓存超时时间
解决方案:
1.缓存永久存储[不推荐]
2.针对于不同业务数据一定要设置不同超时时间
// 解决雪崩// if(id.equals("com.zhangan.dao.UserDao")){// getRedisTemplate().expire(id.toString(),30, TimeUnit.MINUTES);// }
Redis主从复制
无法解决:1.master节点出现故障的自动故障转移|
-
主从复制
主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据
-
架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWQ0n8i0-1619789926317)(redis.assets/image-20201119123944291.png)]
-
搭建主从复制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dC2F3I6-1619789926317)(redis.assets/image-20201119124438368.png)]
分别启动即可主从复制
Redis哨兵机制
Sentinel(哨兵)是Redis的高可用性解决方案︰由一个或多个Sentinel 实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说哨兵就是带有自动故障转移功能的主从架构。
无法解决:1.单节点并发压力问题 2.单节点内存和磁盘物理上限
- 架构原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PcQme1ET-1619789926318)(redis.assets/image-20201119125553058.png)]
-
搭建过程
#1.在主节点上创建哨兵配置在Master对应redis .conf同目录下新建sentinel.conf文件,名字绝对不能错;#2配置哨兵,在sentinel.conf文件中填入内容:sentinel monitor 被监控数主从架构的名字(自己起名字) ip(主机IP地址) port(主机端口) 1(启动哨兵的数量)#3.启动哨兵模式进行测试./redis-sentinel(在源目录里的脚本 复制过来) /myredis/sentinel.conf说明:这个后面的数字2,是指当有两个及以上的sentinel服务检测到master宕机,才会去执行主从切换的功能。
-
-
配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSIK8vry-1619789926319)(redis.assets/image-20201119160851769.png)]
-
-
spring boot如何整合
#redisspring: redis: sentinel: master:(之前起的名字) nodes:192.xx.xx.x:6379,192.xx.xx.x:6380(多个哨兵的结点)
Redis集群
Redis在3.0后开始支持Cluster(模式)模式,目前redis的集群支持节点的自动发现,支持slave-master选举和容错,支持在线分片(sharding shard )(分槽的意思)等特性。|
-
架构图
ping pong协议 内部的二进制协议 互相查看是否存活
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WEOCR6UV-1619789926320)(redis.assets/image-20201119161402384.png)]
-
集群细节
集群维护主结点 槽维护value
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8fYXZW7-1619789926320)(redis.assets/image-20201119161940665.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFXcLVxS-1619789926320)(redis.assets/image-20201119200213026.png)]
-
搭建redis集群
判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数,搭建集群至少需要三个主节点,三个从节点,至少需要6个节点。
Redis实现分布式Session管理
管理机制
redis的session管理是利用spring提供的session管理解决方案,将一个应用session交给Redis存储,整个应用中所有session的请求都会去redis中获取对应的session数据。
开发Session管理
-
引入依赖
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId></dependency>
-
开发Session管理配置类(自己写个类 加上注解 不需要写内容)
@Configuration@EnableRedisHttpSessionpublic class RedisSessionManager {}
-
打包测试