关系数据库MySQL的IO操作慢!noSQL为内存操作 快、高并发。
存储形式:K-V键值对
优点:
对数据高并发读写(直接在内存中操作)
单线程操作(所谓的多线程只是多个命令队伍排队 CPU处理时仍然是单线程)
Redis -----提供缓存服务!!!!
Redis定位是缓存,提高数据读写速度,减轻对数据库读写与访问的压力。
mac安装redis教程:mac安装redis(一条龙服务) - 掘金
安装到/usr/local目录下 访达内使用command+shift+g 可以进入此隐藏文件夹!
终端使用redis-server 启动服务端
终端再打开一个窗口 使用redis-cli 启动客户端 将自动本地连接
--->打开服务端 redis-server
--->打开客户端 redis-cli
也可以下载图形界面客户端连接Redis服务就好了
进入/usr/redis/bin 下 ./redis-server ./redis-cli 就可以打开服务端、客户端
应用--共享session--Redis的String类型键值对缓存
键一个---但是值里面又套了一个键值对
应用--共享session设计--Redis的key-value
Redis中List本质是双端链表 可以看做队列。 --有序
应用--收藏列表--多键值
String与Hash键值对都是一对一,而List键值对是一对多。
收藏列表:
key:favorite_list
value:id1,id2,id3...
4、Set类型--命令--应用
重要命令:
差集 -- sdiff key1 key2 ---返回key1特有元素
交集 -- sinter key1 key2 ---返回key1与key2共有元素
并集 -- sunion key1 key2 ---返回key1与key2 集合的元素(不含重复元素)
Sorted set = set + 排序效果
为了实现排序效果,要求每个元素关联一个double类型的分数,根据分数排序。
应用-排行榜
对所有的Key都有效
思考:啥时候项目中使用Redis? 怎么在项目中使用Redis?
1、是否需要缓存 --首选Redis
2、是否使用Redis
3、怎么设计KEY-VALUE?
Value设计核心----Value类型选择: String、Hash、List、Set、Sorted set
· 是否需要排序?要 使用Sorted set
· 缓存的数据是多个值还是单个值?
· 单个值:简单类型String 对象值 Hash
· 多个值:不允许重复set;允许重复List
公司常用:
排序用Sorted set;其他都用String 方便转化为JSON数据存进内存
Key设计核心----可读性、唯一性、灵活性、时效性
常用模板:
· 表名:主键名:主键值:列名
· 业务模块名:业务逻辑含义:其他:Value类型
MySQL事务 要么全部执行成功,要么回滚。
但Redis事务不会回滚。
Redis事务过程:
开始事务(MULTI) --> 命令入队 --> 执行事务(EXEC)
Redis事务可一次执行多个命令,具有以下特征:
· 在multi命令后,在exec命令触发事务执行前,所有命令都被放入队列缓存;
· exec命令后开始执行事务,某条命令执行失败不影响其他命令;
· 其他客户端的命令不会插入到事务命令队列中。
为啥要持久化?
Redis将数据全部存储在内存中,一旦断电或者崩溃,数据就将全部丢失。重启后想要回到原来的状态,只能重来。所以为了实现重启后任然可以快速恢复到原来的状态,就将 全部数据 备份成二进制文件xx.rdb 存储在硬盘中,这属于IO操作,为了提升效率,固定时间创建子进程快照存储一次。
Redis持久化机制:
快照方式RDB(Redis DataBase)
文件追加方式AOF(Append Only File)
混合持久化方式
将内存中所有数据以快照方式写入到二进制文件中,默认为dump.rdb
触发机制
优点:恢复数据快于AOF、快照文件是紧压缩文件,适用于全景备份,容灾备份。
缺点:无法实时/秒级持久化;快照文件不同版本格式不一致不兼容。
RDB机制,倘若在俩次快照之间宕机,必然造成部分数据丢失,所以仍然不足;
将所有Redis写命令记录到日志,可以保证所有数据的绝对安全
只追加日志文件(命令)
将Redis所有写命令记录到日志文件中,重启后再次执行命令达到恢复数据目的。
打开redis.conf配置文件修改。打开后就将生成appendonly.aof
rewrite目的就是将指令重写合并,实现减小AOF文件大小。
本质还是文件替换,不是文件内部修改!!!
手动:客户端执行bgrewriteaof命令
自动:redis.conf配置
优点:数据安全性高最多损失1s数据量;AOF文件过大后台自动重写
缺点:文件体积大(未压缩);持久化慢
(Redis4.0后默认配置)
RDB是定时将内存中数据存入硬盘;AOF是将全部写命令存入硬盘
RDB-AOF混合方式就是 把当前数据以RDB方式写入文件开头,再将后续操作命令以AOF方式存入文件。即以RDB作为全量备份,AOF作为增量备份。RDB可保证重启时的速度,AOF可保证数据避免丢失。
允许数据丢失 选RDB。
Redis配置: maxmemory
----Key内卷机制
惰性删除:当访问Key时才判断是否过期,过期则删除。对CPU友好,但是浪费内存;
定时删除:设置键过期时间的同时设置定时器,对内存友好,对CPU不好,需要CPU维护定时器;
定期删除:定期检查一遍Key 过期则删除。
Redis服务器采用惰性删除与定时删除结合,在对CPU减负与避免内存浪费之间平衡。
总结:为了防止内存吃满,使用多策略综合
为了防止 内存穿透(内存中热点数据过期被删,大量请求到达数据库) 多策略综合
为了防止 缓存雪崩()
过期时间均匀分布 + 热点数据永不过期
实现高可用俩大策略: 主从复制 + 哨兵
主节点主要负责写数据与数据同步,从节点主要负责读数据,读写分离提高性能;
主节点崩溃从节点顶替,实现高可用。
也就是冗余备份-- 针对重要数据
从节点不可以写,只可以读。
基本数据同步方式:主节点向从节点传输RDB文件,同时传输命令执行。(RDB与AOF混合)
从节点数据恢复:
主节点设置缓冲区,存储最近同步的数据,若某个从节点丢失数据,主节点将缓冲区文件传输,从节点根据复制偏移量得知缺失哪些数据从而弥补。
复制偏移量(游标):从0开始,随着数据复制和同步,主从节点同时更新,比较各自偏移量即可知道缺失哪些数据。
作用:监视所有节点;自动故障转移
负责自动将从节点升级为主节点;监控主从节点工作;
工作:
主节点挂掉做故障转移:
选择优先级高(设备配置高),复制偏移量大(数据更全)的从节点作为主节点。
哨兵机制解决了主节点宕机后的自动故障转移,但是无法解决单节点并发压力以及单节点内存上限问题。接下来就出现厄redis集群。
集群一下子解决掉自动故障转移、单节点并发压力、单节点内存上限三大问题。
将多个节点的容量合并,实现容量扩增。
三次握手加入集群
集群内任一成员发MEET信息发起握手;对方回复PONG信息同意加入;成员再发PING成功加入。
Redis-Cluster模式使用哈希槽(Hash slot)实现数据分片。
槽位分配:
手动为每个节点分配来负责一定的槽位。
槽位计算:
数据读写时,对键值进行哈希运算,映射到哪个槽位就由槽位负责节点负责读写。
实现信息一致
启动时每个节点将自己负责的槽位的信息通知其他节点,将自己负责的每个槽位用1表示,其他用0表示,那么一次出书的信息量就是16384bit,即2KB。
为了确定某个槽位由谁负责,直接建数组存储每个槽位对应的节点。
集群工作 + 主从复制
总:
1、添加jedis依赖
2、写业务类:
public static void main(String[] args) {
//创建jedis客户端对象
Jedis jedis = new Jedis("127.0.0.1",6379);
//选择使用一个库 默认使用0号库
jedis.select(0);
//开始对redis进行操作
System.out.println(jedis.get("mylove"));
Set keys = jedis.keys("*");
keys.forEach(key -> System.out.println("key : " + key));
System.out.println(jedis.dbSize());
System.out.println(jedis.exists("name"));
// jedis.expire("name",100);
// System.out.println(jedis.ttl("name"));
// System.out.println(jedis.randomKey());
jedis.rename("s1","s111");
//释放资源
jedis.close();
}
spring data提供了RedisTemplate、StringRedisTemplate俩父子接口。redisTemplate为父接口,俩个参数都必须是object对象;StringRedisTemplate为子接口俩参数都是String类型。
spring-boot-starter-data-redis
开启远程权限:/usr/local/etc/redis.conf 进入后直接 / 表示搜索 bind 将bind后面改为0.0.0.0
检查防火墙是否打开6379端口
中添加配置: IP、端口、库
server.port=8989
#redis
spring.redis.host=172.16.170.137
spring.redis.port=6379
spring.redis.database=0
//启动SpringBoot应用
@SpringBootTest(classes=RedisTestApplication.class)
@RunWith(SpringRunner.class)
class RedisTestApplicationTests {
//1、注入StringRedisTemplate
@Resource
private StringRedisTemplate stringRedisTemplate; //字符串友好型 -- key、vaue都是String类型
//2、操作redis中字符串opsForValue 实际操作就是redis中String类型
@Test
public void testString(){
stringRedisTemplate.opsForValue().set("name","woshinidaye");
System.out.println(stringRedisTemplate.opsForValue().get("name"));
}
@Test
public void testKey(){
stringRedisTemplate.opsForValue().set("name","woshinizuzong");
System.out.println(stringRedisTemplate.hasKey("name"));
System.out.println(stringRedisTemplate.type("name"));
Setkeys = stringRedisTemplate.keys("*");
keys.forEach(key -> System.out.println("key : " + key));
stringRedisTemplate.delete("name");
System.out.println(stringRedisTemplate.hasKey("name"));
}
}
实际应用:手机号验证码超时设置
// 设置一个Key超时时间 用于手机号验证码超时
stringRedisTemplate.opsForValue().set("phonenumber","3698",120, TimeUnit.SECONDS);
-- 有序可重复
public void testList(){
stringRedisTemplate.opsForList().leftPush("names","小王八羔子");//创建一个列表并放入一个元素
stringRedisTemplate.opsForList().leftPushAll("names","小明","小张","任爸爸");
List names = new ArrayList();
names.add("xiaosan");
names.add("xiaodongzi");
stringRedisTemplate.opsForList().leftPushAll("names","xiaosansiwu"); //创建一个列表放入多个元素
List strlist = stringRedisTemplate.opsForList().range("names",0,-1);
strlist.forEach(value-> System.out.println("value : " + value));
}
public void testSet(){
// stringRedisTemplate.opsForSet().add("sets","小明","小张","小人","郑仁");//创建sets集合添加元素
Set sets = stringRedisTemplate.opsForSet().members("sets");
sets.forEach(value-> System.out.println("value : " + value));
System.out.println(stringRedisTemplate.opsForSet().size("sets"));
}
public void testZset(){
stringRedisTemplate.opsForZSet().add("Score","小明",100);
stringRedisTemplate.opsForZSet().add("Score","小红",77);
stringRedisTemplate.opsForZSet().add("Score","小薛",89);
stringRedisTemplate.opsForZSet().add("Score","小卡",39);
stringRedisTemplate.opsForZSet().add("Score","小热",80);
Set zsets = stringRedisTemplate.opsForZSet().range("Score",0,-1);
zsets.forEach(value-> System.out.println("valus : " + value));
}
stringRedisTemplate接口:key-value都是字符串类型直接存储在redis内存中
RedisTemplate接口:key,value要通过序列化成一个序列化结果,最终存储在Redis内存中的是 序列化对象。(内存中序列化key -- 序列化value)
但是这种的话无法直接在终端直接通过String类型的key获取对应的value。一般实际开发中是String类型key -- Object类型value
对于String、Set、Zset、List都只需要调整一个键的类型为StringRedisSerializer()
但是Hash要调整key与hashkey二者
public void testRedisTemplate(){
/**
redisTemplate对象中key-value序列化都是JdkSerializationRedisSerializer
为了实现 key:string value:object
修改默认的key方案 :key StringRedisSerializer
*/
//修改key序列化方案 String类型序列
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 修改Hash key序列化方案
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setName("xiaosdsdsd");
user.setAge(19);
user.setBir(new Date());
redisTemplate.opsForValue().set("user",user);
//如果未修改key的序列化类型 只可以通过java通过序列化处理来获取键值 无法通过终端快速获取
User user1 = (User) redisTemplate.opsForValue().get("user");
System.out.println(user1);
redisTemplate.opsForList().leftPush("listUser",user);
redisTemplate.opsForSet().add("setUser",user);
redisTemplate.opsForZSet().add("zsetUser",user,89);
redisTemplate.opsForHash().put("hashUser","hashkey",user);
}
public class boundRedisTemplate {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RedisTemplate redisTemplate;
// spring data为了对redis操作更简便,提供了bound API
@Test
public void boundTest(){
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 将一个对key进行连续操作进行绑定,本质上对key绑定
// stringRedisTemplate.opsForValue().set("name","zhangsan");
// stringRedisTemplate.opsForValue().append("name","是我的好大儿");
// String name = stringRedisTemplate.opsForValue().get("name");
// System.out.println(name);
// 对字符串类型key进行绑定 后续所有操作都是基于这个key 的操作
BoundValueOperations nameValue = stringRedisTemplate.boundValueOps("name");
nameValue.set("zhangsan");
nameValue.append("是我的好大儿");
String s = nameValue.get();
System.out.println(s);
// set
// BoundSetOperations boundSetOperations = redisTemplate.boundSetOps();
// BoundSetOperations stringStringBoundSetOperations = stringRedisTemplate.boundSetOps();
// ZSet
// BoundZSetOperations stringStringBoundZSetOperations = stringRedisTemplate.boundZSetOps();
// BoundZSetOperations boundZSetOperations = redisTemplate.boundZSetOps();
// hash
// BoundHashOperations stringObjectObjectBoundHashOperations = stringRedisTemplate.boundHashOps();
// BoundHashOperations boundHashOperations = redisTemplate.boundHashOps();
//
}
}
1、针对处理key value 都是 String 使用 stringRedisTemplate 2、针对处理 key value存在对象 使用 redisTemplate 并且将key & hashkey 使用 StringRedisSerializer 3、针对同一个key多次操作 可以使用boundxxxops() value List Set Zset Hash 的API简化
应用很重要:4.Redis中主从复制架构_哔哩哔哩_bilibili
memcache本质上还是Tomcat服务器存储应用Session,并且同步到memcache上,首先访问的还是Tomcat;
redis是所有Session直接存储到redis,有请求,Tomcat直接到redis取Session。
up主----编程不良人