常见的东西:

  • rdb和aof
  • redis的主从和哨兵
  • redis的集群
  • redis的事务(弱事务:可满足,可不满足)
  • redis的过去策略和淘汰机制
  • redis脑裂
  • redis的底层IO多路复用模型
  • redis的缓存击穿和redis的缓存穿透和redis的雪崩
  • redis双写一致性问题
  • redis的跳跃表
  • redis的分布锁

为什么需要Redis?

因为mysql 操作数据会很慢,mysql的服务器一般是 16核32G ,32核64G的服务器
但是他们并不支持上万的高并发的情况 这个时候mysql的cpu io 就会瞬间打满
一般企业服务器大概能够支撑三千到四千的并发 32核64G大概是在七千的并发
普通的tomcat它的服务器级别可能是2核4G 4核8G 一般只能支撑 3核8G

mysql毕竟读取的是磁盘上的数据,它会根据存储引擎的不同
InnoDB ,MyIASM 这样的存储引擎 都是把表存储到磁盘上的,当我们
想读取数据的时候,这些存储引擎会从文件系统中把数据读取出来返回
给我们,当我们想写入数据的时候,这些存储引擎会把这些数据又写回去文件系统
mysql 存储的一些列日志也属于文件系统 最终都会进行落盘的行为
导致mysql不支持太高的并发的原因是啥子?
终上所述:mysql无论如何都会从磁盘上加载数据
同时它还有一套机制是确保事务的安全性
然后这个时候就出现了救世主Redis:
那么redis其实就是说是一款纯内存的数据库,它的数据其实都会优先加载到内存中去,无论你的什么操作都是基于内存来完成的,关于落盘问题,可以选择落也可以选择不落问题不大,由于它是存内存操作,
他不会像mysql一样去保持事务的完整性
所以就造成了redis能支持高并发,同时也确保redis的性能非常牛逼
你说是为啥子呢?
因为我的redis是不会从磁盘上去加载数据啊,同时也不用确保事务
的完整性啊,然后我也没有非常复杂的表关系啊,我的落盘机制比mysql
简单呀那这个又是为什么呢?因为mysql的落盘机制是要保证事务的完整性的吗。我的redis的事务是弱事务的吗所以比他的强事务机制上来说
就没有那么复杂的吗
mysql的多表关联是不允许存在三张的(阿里开发手册)
综上所诉:如果说我的redis跟mysql处于同一个配置的话redis可以支撑10W级别的并发

高并发的概念:

你比如说双十一,十二点开始抢购东西吗 然后这个时候就可能是成千上亿的人来购物所以说这个时候你的服务器肯定是有压力的,因为他会存在一个成千上万的请求
向你服务器发起
准确说在同一个时间段来访问但是这个时间段是极短的时间段大量的人发出大量的请求

高并发来的话:
核心思路其实就是错峰,什么是错峰?比如说 十万个请求要过来
但是呢你又承受不了这个压力那么怎么办?所以说就要进行把流量打闪
那么有那些方案?比如说前端的那个验证码,一个作用是他会验证你是不是机器人还有一个作用是每个人填写验证码的速度不一致,那么不一致就会在一定程度上进行一个流量的分闪,你说是不是这个道理
1,错峰
2,企业级的图形验证码:这些验证码可以做到防止刷单
专门的大数据检测你当前:对应的鼠标的轨迹是否属于人工学

Redis - 图2
3 ,后台进行限流
比如说我的服务器只能支撑一万个并发
就是说你的服务器个数有限但是呢,但是呢做活动当并发大于一万那么怎么办呢?
首先错峰
通过验证码去解决一些刷单的行为
去高防DDos解决一些攻击
还需利用一些核心技术去承载高并发

Redis - 图3
这个CDN服务器放着静态资源它其实上是帮助我们后台服务器集群一个流量的分闪作用读的请求就直接打到CDN服务器里面去

lvs 它可以支持高并发的性能很好轻松抗下百万并发情况收费也很贵
那么为什么这么快呢?因为它是基于osi模型,tcp协议来的也就是说
它更本不在乎你的应用层是什么情况 直接拿着tcp报文直接操作,
直接抗下并发情况 再将这个请求交给下一层

两个细节:基于tcp协议来玩的我们普通的请求实际上是在TCP协议之上建立起来了HTTP协议然后通过前四层 网络模型处理 对应使用的是应用层协议 但是lvs不需要应用层直接在TCP协议报文哪里直接拿到了请求负载均很直接转发给到了下一层那么这个下一层是哪里其实就是nginx服务器
它可以去承载一些对应的高并发基于应用层来玩的正因为它是基于应用层来玩的所以我们可以在这个nginx上面去进行lua表达式开发 我可以让它去拿缓存通过lua表达式去做一些高级操作
1,相当于说这个请求根本就不会从后台服务器过
2,可以直接拿到你后台你对应的redis的数据
3,nginx限流(核心技术限流)限制流量 你一百万人发送一个请求
首先经过前端的错峰加上DDos高防 操作 把当前流量限制成我后台能够承载的最高流量
2.2,限流分成两类:
1,整体限流:
放过去服务里边不会被打死的请求
比如说我现在服务器只能承载一万个那么这个时候
我整体限流就给我限一万个
比如说我现在只有只有五千个商品
那么我没必要放一万个请求进去撒
砍成后台服务器里边剩余的这样一个商品数量
通常来说会多放后台商品的 110% 或者 120%的请求
你说为啥子会多放呢?
因为比如说现在我抢到了商品但是我不付款
所以就确保了不会有多余的流量
2,业务限流:
但是现在nginx现在做限流的时候他需要去统筹,当前这些商品还剩下
多少我需要去放多少?
通过lva去读取redis里边的数据
通过lua进行限流的操作:
限流算法:
令牌桶算法,SpringCloud ,sentinel sentinel组件他就可以基于令牌桶算法来进行限流(令牌桶算法是基于redis来的+所谓的业务限流)

nginx由于它是应用层协议 其实你就可以拿到完整的报文

一般来说我们不会直接让请求直接打到mysql里面去,而是把他提前加载到redis里面去然后,后台的请求直接去找redis承载这个数据就可以了
但是这个数据最终还是会交到我们后台服务器去的吗那么我的服务器还是会被打死那么我应该怎么办?
比如说我现在要做一个秒杀服务 我开了两台秒杀服务器
秒杀服务器接收到请求之后会走到下一层被包裹一层秒杀下单服务然后会交给一下一套下单服务的程序然后才会入库
现在这一套东西还不足够支持很高的并发情况 那么会出现一个mq削峰的东西(其实就是队列的排队)然后再从mq里面去拿东西高并发丑图.png
秒杀流程图 (1).png

负载均衡:
比如说我现在有两个服务器分别各处理一万的请求
但是呢我的请求发过来之后它是不长脑壳的撒不晓得到底打给那个撒
那么这个时候就会出现一个叫做负载均衡器的东西来给这么多的请求进行分配

redis缓存穿透

是什么意思?
Redis - 图6
因为mysql 他说基于磁盘加载再加上mysql它有一套完整的事务机制
所以说他的性能毕竟没有那么高我们就会选择 常用 而且不容易发生变化的数据把他加载到redis里面去
你说为什么不把所有的数据直接加载到redis里面去呢?
因为redis是基于内存来加载数据的吗,所以内存其实是有限的啊而且内存贵啊

service 会看看redis里面有没有我想要,

Redis:基础

什么是Rrdis:

Redis是一个基于内存的key-value结构的数据库
所以说他的读写性能就特别高吗

但是呢我们的内存是有限制的吗所以说我们redis一般都会存储热点信息
你就比如说那个双十一的秒杀活动,这个情况是不是就解决了高并发问题应用广泛;

  • Redis入门
  • 数据类型
  • 常用命令
  • 在java中存在Redis

    入门:

    简绍:
    Redis是一个开源的内存中的数据结构存储系统,它可以用来
    数据库,缓存和消息中间件
    是一个高性能键值对数据库 官方给的数据是可以达到每秒十万
    +的qps(每秒访问次数)
    下载安装
    NOsql数据库是关系型数据库的补充
    Redis服务启动与停止

redis的应用场景:
  1. 缓存
  2. 任务队列
  3. 消息队列
  4. 分布式锁

捕获.PNG

Redis服务启动与停止:

LInux中redis服务启动 使用 redis - server 默认端口号为6379

ctrl+c停止Redis服务
redis-cli 文件 可以连接服务
Windows 系统中启动redis 直接双击 redis - server.exe 就可以直接启动服务

设置密码允许远程连接:

默认情况下redis是没有开启密码校验的

redis数据类型:

Redis存储的是key-value结构的数据其中key是字符串类型
value有五种常用的数据类型:

  • 字符串 String
  • 哈希 hash
  • 列表 list
  • 集合 set
  • 有序集合 sorted set

捕获.PNG
指的是 value的数据类型

redis结构应用场景:

String :一般用来存放验证码 存放一些普通的字符串 还可以存放对象
hash :可以用来购物车
队列 : 砸门去做一些排队的事情,直播平台连麦
set:可以做交友朋友圈的处理,它会提供一套API去获得两个集合的交并补集

zset:可以做排行榜 可以根据分值进行排序

redis常用命令_字符串类型操作命令:

捕获.PNG
SETEX key seconds value :设置指定key的值 并将key的过期时间设为seconds 秒:指定过期时间吗 比如说验证码有一个有效时间

127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> get age
“23”
127.0.0.1:6379> set age 30
OK
127.0.0.1:6379> get age
“30”
127.0.0.1:6379>

key 相同的情况下后面设置的值会覆盖掉前面所设置的值

127.0.0.1:6379> setex city 10 bejing
OK
127.0.0.1:6379> get city
“bejing”
127.0.0.1:6379> get city
(nil)
127.0.0.1:6379>
设定键值对的时间

127.0.0.1:6379> setnx key1 valu1
(integer) 1
127.0.0.1:6379> setnx key1 value2
(integer) 0
127.0.0.1:6379> get key1
“valu1”
127.0.0.1:6379>
只有这个key不存在的时候他才会去设置值
如果存在就不回去进行任何操作

—-哈希类型操作命令

捕获.PNG
对应关系:
捕获.PNG
Hset — haget
127.0.0.1:6379> hset 002 name xiaoming
(integer) 1
127.0.0.1:6379> hset 002 age 90
(integer) 0
127.0.0.1:6379> hget 002 name
“xiaoming”
127.0.0.1:6379> hget 002 age
“90”
127.0.0.1:6379>
HDEL:
127.0.0.1:6379> HDEL 001 age
(integer) 1
127.0.0.1:6379> hget 001 age
(nil)
127.0.0.1:6379>
HKEYS:获取哈希表中所有的字段
127.0.0.1:6379> hkeys 001
1) “name”
127.0.0.1:6379> hkeys 002
1) “age”
2) “name”
127.0.0.1:6379>
Hvals key:返回哈希表中所有的值
hvals 002
1) “90”
2) “xiaoming”
127.0.0.1:6379>
HGETALL Key 获取哈希表中指定key的所有字段和值
127.0.0.1:6379> hgetAll 002
1) “age”
2) “90”
3) “name”
4) “xiaoming”
127.0.0.1:6379>

列表LIST类型操作:

捕获.PNG
LPUSH Key value[value2]:先进来的在尾部后进来的在头部
127.0.0.1:6379> lpush mylist a b c
(integer) 3

LRANGE KEY start stop:获取列表指定范围的元素
127.0.0.1:6379> lrange mylist 0 -1
1) “c”
2) “b”
3) “a”
从最开始到最后的数据

RPOP key:移除列表中最后以为元素:
127.0.0.1:6379> rpop mylist
“a”
127.0.0.1:6379> lrange mylist 0 -1
1) “c”
2) “b”

LLEN KEY 获取列表的长度:
127.0.0.1:6379> llen mylist
(integer) 2
BRPOP key1【key2】timeout
移除并获取列表最后一个元素 如果列表没有元素会阻塞列表直到等待超时或发现弹出元素为止
timeout是指定超时,时间

127.0.0.1:6379> brpop mylist 10
1) “mylist”
2) “b”

集合set操作命令:

捕获.PNG
SADD:向集合添加一个或多个成员
127.0.0.1:6379> sadd myset 2 3 42 4 45 6
(integer) 6
SMEMBERS :返回集合中的所有成员
127.0.0.1:6379> smembers myset
1) “2”
2) “3”
3) “4”
4) “6”
5) “42”
6) “45”
SCARD KEY 获取集合的成员数
127.0.0.1:6379> scard myset
(integer) 6

有序集合类型操作命令:捕获.PNG

通用命令:

捕获.PNG

在java中操作:

捕获.PNG

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.8.0</version>
  5. </dependency>
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>
  1. public class JedisTest {
  2. /**
  3. * 使用Jedis操作Redis
  4. */
  5. @Test
  6. public void TestRedis(){
  7. //获取连接 指定连接那个Redis的服务 指定端口号
  8. Jedis jedis = new Jedis("localhost",6379);
  9. //执行具体操作
  10. jedis.set("username","小明");
  11. //查询一下
  12. String username = jedis.get("username");
  13. //删除
  14. Long username1 = jedis.del("username");
  15. //Hash
  16. jedis.hset("myhash","addr","shanghaio");
  17. final String hget = jedis.hget("myhash", "addr");
  18. System.out.println(hget);
  19. Set<String> keys = jedis.keys("*");
  20. for (String key : keys) {
  21. System.out.println(key);
  22. }
  23. //关闭连接
  24. jedis.close();
  25. }

捕获.PNG

  1. spring:
  2. application:
  3. name: springdataredis_demo
  4. #Redis相关配置
  5. redis:
  6. host: localhost
  7. port: 6379
  8. #password: 123456
  9. database: 0 #操作的是0号数据库
  10. jedis:
  11. #Redis连接池配置
  12. pool:
  13. max-active: 8 #最大连接数
  14. max-wait: 1ms #连接池最大阻塞等待时间
  15. max-idle: 4 #连接池中的最大空闲连接
  16. min-idle: 0 #连接池中的最小空闲连接
  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class RedisConfig {
  4. @Autowired
  5. private RedisTemplate redisTemplate;
  6. 能够直接注入是因为我写了配置文件
  7. }

设置序列化器

当前配置类不是必须的,因为 Spring Boot 框架会自动装配 RedisTemplate 对象,但是默认的key序列化器为JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别

  1. /**
  2. * Redis配置类
  3. */
  4. @Configuration
  5. public class RedisConfig extends CachingConfigurerSupport {
  6. @Bean
  7. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
  8. RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
  9. //默认的Key序列化器为:JdkSerializationRedisSerializer
  10. redisTemplate.setKeySerializer(new StringRedisSerializer());
  11. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  12. redisTemplate.setConnectionFactory(connectionFactory);
  13. return redisTemplate;
  14. }
  15. }
  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. class JdesDemoApplicationTests {
  4. @Autowired
  5. private RedisTemplate redisTemplate;
  6. @Test
  7. public void testString(){
  8. ValueOperations valueOperations = redisTemplate.opsForValue();
  9. valueOperations.set("city","chengdu");
  10. //redisTemplate 在执行操作的对我们的键执行了一个序列化 序列化之后是不能直接拿到的
  11. //想拿到就要改变redis的序列化方式 所以我配置类又重新整了一个序列化器
  12. }
  13. }

操作字符串类型

  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. class JdesDemoApplicationTests {
  4. @Autowired
  5. private RedisTemplate redisTemplate;
  6. @Test
  7. public void testString(){
  8. ValueOperations valueOperations = redisTemplate.opsForValue();
  9. String city = (String) valueOperations.get("city");
  10. //设置值的时候可以指定超时时间
  11. valueOperations.set("key1","valu1",10l, TimeUnit.MICROSECONDS);
  12. //当我的key不存在的时候它就会帮我设置
  13. Boolean aBoolean = valueOperations.setIfAbsent("cxcx", "xxx");
  14. }
  15. }

操作哈希数据类型:

  1. @Test
  2. public void TestHash(){
  3. HashOperations hashOperations = redisTemplate.opsForHash();
  4. //第二个参数是针对哈希表里面的那个字段
  5. hashOperations.put("0001","name","xiaoming");
  6. hashOperations.put("0001","age","20");
  7. String name = (String) hashOperations.get("0001", "name");
  8. //获取hash中所有的字段
  9. final Set keys = hashOperations.keys("0001");
  10. for (Object key : keys) {
  11. System.out.println(key);
  12. }
  13. //获取所有值
  14. final List values = hashOperations.values("0001");
  15. for (Object value : values) {
  16. System.out.println(value);
  17. }
  18. }