985研一学习日记 - 2024.12.29

一个人内耗,说明他活在过去;一个人焦虑,说明他活在未来。只有当一个人平静时,他才活在现在。

日常

1、起床6:00

2、健身1.5h

今天练了背部和胸,然后爬坡半小时

3、LeetCode刷了1题

  1. 二叉树的最小深度:BFS
    1. 使用BFS广度优先遍历,用存放TreeNode的队列来实现,队列用ArrayList< TreeNode >数组以及头尾指针head和rear来实现,首先将root加入队列中,入队要让rear++,出队要让head++,当head!=rear时,说明当前队列中有未遍历的元素
    2. 求最小深度就是从节点开始层序遍历(BFS),并记录当前遍历的层数,当遇到第一个叶子节点时就是最小深度,故每次要遍历一层的所有元素,所以每一层中先记录当前层的范围(pos = rear),然后再遍历当前层的所有节点(从head->pos),当某个节点为叶子节点时,直接返回当前的层数就是最小深度,如果某个节点非叶结点,则分别判断左右孩子是否存在,存在则加入队列(rear++),此时必须要记录当前层的位置,不可以用rear,因为每次加入rear就会改变,不再是当前层,当前层遍历结束后,深度+1,继续遍历下一层
  2. 岛屿问题:BFS/DFS
    1. 岛屿问题就是使用BFS或者DFS遍历所有的节点,而且不进行回溯,遍历后就标记当前节点已经遍历,不需要进行回溯
    2. 如求岛屿个数时,先遍历所有的节点,对未遍历的节点使用DFS且不回溯,此时就是遍历一个岛屿,进行几次DFS就是几个岛屿
    3. 求岛屿最大面积时,在DFS的同时使用全局变量进行计数,可以先遍历再计数,也可以先计数再遍历,然后每遍历一个岛屿就判断面积是不是最大
    4. 求孤立岛屿时,对边界上的岛屿进行DFS并标记已经遍历过(直接修改为海洋即可,不需要重新设置一个新的数组),然后遍历所有位置就可以找打孤立岛屿

4、复盘

不复盘等于白学!!!


学习

1. SpringBoot集成Redis

  1. 中间件总体概述
    1. Java程序连接Redis必须使用中间件,类似于连接MySQL的JDBC,将对Redis的操作封装为API供Java程序进行使用
    2. 第一代是Jedis,然后优化为Lettuce,现在是redis对Lettuce进行整合为RedisTemplate
  2. 常见问题
    1. 因为是Java远端服务器连接Redis服务器,故要在配置文件中实现相应的配置,关闭redis服务端的绑定和保护模式等
  3. Jedis
    1. 是Redsi提供的面向Java客户端,实现了对各类API的封装调用
    2. 创建SpringBoot微服务:约定>配置>编码
    3. 建项目
    4. 改POM,引入Jedis的POM依赖
    5. 写YML,实现对redis服务端连接需要的一些配置,Java程序作为redis客户端
    6. 主启动
    7. 业务类
      1. Jedis就是将对redis客户端的各种操作封装为API
  4. Lettuce:保证只有一个redis连接,多个线程共享
    1. 当使用Jedis时,此时每个线程都要创建一个Jedis实例去连接Redis,会造成大量开销反复关闭Jedis,而且也是线程不安全的,多个线程并发访问redis
    2. 故引入了Lettuce,底层使用的是Netty,当有多个线程连接Redis时保证只有一个Lettuce连接多个线程共享,而且也是线程安全的
    3. 改POM引入Lettuce依赖
    4. 业务类:使用commands执行各种操作
    5. 就是Jedis的连接池
  5. RedisTemplate
    1. SpringBoot连接Redis单机
      1. 建项目
      2. 改POM
        1. 在RedisTemplate依赖中包含Lettuce的依赖
      3. 写YML
        1. 配置redis服务器的IP和端口号
      4. 主启动
      5. 业务类
        1. RedisTemplate默认配置会出现中文乱码,故要进行配置序列化,或者使用StringRedisTemplate,然后开启客户端时使用–raw
        2. 要创建RedisConfig配置类,如果不配置使用默认时,则会出现中文乱码,故要对RedisTemplate进行配置实现序列化
        3. 当使用StringRedisTemplate时key不会出现序列化问题,因为此时使用的是StringSerializer序列化,但value仍然会出现乱码,可以在开启redis客户端时加上 --raw 来解决中文乱码
        4. RedisTemplate配置类实现序列化
        5. ![[Pasted image 20241029075631.png]]
    2. SpringBoot连接Redis集群
      1. SpringBoot连接redis集群时,一定要在配置文件中开启Lettuce的动态刷新,否则无法动态感知拓扑结构
      2. 连接集群时要注意开启RedisTemplate集群节点拓扑的动态刷新,否则SpringBoot无法动态感知,连接集群时一定要开启动态刷新,
      3. 修改YML使其连接redis集群,修改YML配置文件来设置redis集群的配置
      4. 此时直接按照单机的业务类也可正常运行,但要开启RedisTemplate的动态刷新,复制无法动态感知
      5. 当某个主机master宕机时,集群Cluster会自动监控并完成主从切换redis端正常运行但此时Java客户端再访问时会出现故障显示连接不到指定的已经宕机的master
      6. 因为SpringBoot客户端没有动态感知到RedisCluster的最新集群消息,故不知道master已经宕机,无法正常访问
      7. Redis默认连接池使用的是Lettuce,当redis集群节点发生改变时,Lettuce默认是不会动态刷新节点信息,故SpringBoot没有动态感知到节点的改变
      8. 解决:开启自动刷新节点集群拓扑动态感应,SpringBoot3已经解决这个问题,默认开启动态刷新

2. Redis单线程 VS 多线程

  1. Redis4之前为什么选择单线程
    1. 单线程是指所有的操作都是原子操作,而且都是顺序执行,当前一个指令未完成时会阻塞后面的指令的执行
    2. redis4.0之前都是单线程,之后慢慢引入多线程,6/7之后才稳定为多线程
    3. redis的工作线程是单线程的,但整个redis是多线程的,只有一个主线程执行命令,有多个IO线程处理请求的socket解析的数据传输![[Pasted image 20241029085501.png]]
    4. 为什么redis4之前要使用单线程,因为单线程安全,避免上下文切换,且性能瓶颈不是CPU,
  2. 为什么又逐渐加入了多线程
    1. 单线程虽然提供了多路复用IO和非阻塞IO,但如果某个客户端的指令非常耗时(del bigkey),则会影响其他客户端指令的运行,造成卡顿
    2. 引入多线程异步处理指令
  3. Redis多线程特性和IO多路复用
    1. 使用多IO线程处理网络请求,使用主线程串行执行命令
    2. Java程序连接Redis也是socket连接,此时也会分配一个FD,Redis服务端会使用多个IO线程来解析多个FD对应的客户端的请求,但只有主线程会执行命令读写数据
    3. 对于Redis的主要性能瓶颈是网络带宽(io)或者内存,而不是CPU
    4. redis6/7引入真正的多线程使用多IO线程来并行处理网络请求使用单线程执行读写操作命令
    5. redis多线程处理网络IO请求,一个主线程执行读写操作(单线程原子操作),多个IO线程建立连接并解析请求
    6. 多个IO线程对请求进行解析,由主线程进行执行操作,然后把数据放入缓冲区,再由IO线程将数据返回
    7. Unix网络编程中的五种IO模型
      1. 阻塞IO:一直等待结果返回
      2. 非阻塞IO:先返回回去,不用一直等待
      3. IO多路复用:只用一个服务端进程通过epoll可以同时处理多个socket连接
        1. 只有一个redis-server服务端,可以连接多个redis-cli客户端,Java程序访问redis也是客户端,一个服务端只有一个主线程来执行命令,但是有多个IO线程处理网络请求
        2. 不会为每一个socket连接分配一个线程或进程,而是分配一个FD,并注册进入epoll中由epoll监控每个socket,有请求就用异步IO线程处理从而实现一个服务端进程同时处理多个socket请求
        3. 一个服务端redis-server进程可以同时处理多个套接字描述符每个描述符对应一个访问该服务端的客户端redis-cli
        4. 实现IO多路复用的模型由select、poll、epoll
        5. 文件描述符FD就是一个索引值,指向每个进程文件的打开记录表,就是进程每打开一个文件就会返回一个文件描述符FD指向这个文件,这样进程就可以通过FD找到打开的这个文件
        6. IO多路复用就是一个或者一组线程处理多个TCP的连接
        7. 每次TCP连接发送到线程时为其分配一个FD,并将FD放到等待队列中,当某个FD准备就绪时,就会解析并处理该连接的请求
        8. IO多路复用
      4. epoll实现IO多路复用
        1. 每个socket连接到服务端后都会分配一个FD描述符,并把FD放到等待队列中,每次由socket发出请求,为其分配一个IO线程处理,实现一个主线程同时处理多个socket套接字
        2. 如何实现处理多个socket:轮询、一对一、一对多(IO多路复用
        3. epoll模型实现多路IO复用:为每个连接该服务端的请求分配一个FD,并注册进epoll中,由epoll对每个socket进行监听,当有消息到达时才会对相应的socket进行处理,事件驱动
        4. 此时单个线程来记录追踪每一个socket的状态来同时管理多个IO流,一个服务端进程可以同时管理多个套接字FD
      5. redis之所以快就是因为IO多路复用+epoll函数使用,不仅仅是内存、数据结构和单线程,redis之所以快就是用了IO多路复用和epoll
    8. 小总结
      1. 主线程只执行命令(单线程安全),由多个IO线程解析发送socket网络数据,解决网络问题
      2. redis6/7后引入了多线程,此时工作线程仍是单线程,但整体是多线程主线程只进行执行socket的命令,与socket有关的解析和读写由专门的IO线程进行处理;即与redis数据库有关的读写操作由主线程单独处理,从而实现原子性,但与客户端socket连接的数据IO由专门的IO线程进行异步处理,从而提高并行性
      3. socket连接就是客户端向服务端发送命令读写数据并向客户端返回结果,redis主线程读写数据是阻塞的,此时主线程只进行读写操作,但读到用户态后由IO线程将数据写回客户端,此时redis主线程会继续处理别的socket请求
      4. redis的工作线程是单线程的,但整个redis是多线程的,有多个IO线程来处理多个socket的网络请求
    9. redis之所以快是因为主要性能瓶颈不在CPU和内存,而是网络,而通过引入多线程,使得主线程只执行命令,多个IO线程解析发送网络数据,从而提高性能
  4. Redis7默认不开启多线程(io-thread)
    1. 通过在配置文件中设置配置项来开启多线程,修改配置文件后要重启redis-server才会生效
    2. redis-server服务端是通过配置文件来启动的服务端只有一个,但可以有多个客户端redis-cli、Java来连接该服务端,当开启多线程时,为每个请求socket连接分配一个FD放入epoll中,当有请求时会分配一个IO线程对请求进行解析,然后把指令发送给唯一的主线程进行处理并将结果发送给IO线程,由IO线程返回给客户端,从而实现多线程
    3. 如果CPU开销不大但吞吐量上不去,则可能没有开启redis7的多线程机制,多线程是默认关闭的,通过在服务端redis-server的配置文件中开启多线程
  5. Redis引入多线程只是为了让Redis越来越快(只是让IO读写变为了多线程命令执行仍然是主线程串行执行,是线程安全的)

3. BigKey

  1. 面试题
  2. MoreKey案例(简历加分)
    1. 向Redis中插入大量数据
      1. 先使用linux操作创建一个文件,里面存放了一百万条set指令,使用管道指令将所有的指令在Redis中运行
      2. 使用DBSIZE可以查看Redis数据库中的key的个数
    2. 禁用keys * 等指令
      1. 数据量很大时,一定要避免使用keys * ,可以直接在配置文件中禁止该指令
      2. 当数据量很大时,不要使用keys * 和flushdb因为Redis只有主线程串行执行命令,且keys * 是遍历实现的,故会非常耗费时间,造成Redis主线程卡顿,则此时后面的所有请求的命令均会卡顿!
      3. 通过在redis.conf配置文件中设置SECURITY配置项禁止这些命令,或者重命名,更改配置文件后一定要先重启服务端
    3. 使用SCAN遍历 key
      1. 使用scan命令查找指定的key(不使用keys * )
      2. Scan命令用于迭代数据库中的数据库键,是基于游标的迭代器,当前遍历要基于上一次遍历的游标
      3. SCAN命令基于游标来遍历数据库中的所有key,游标从0开始,每次返回下一次遍历的游标和当前遍历的key(不保证数量),不是顺序遍历,而是高位进位加法遍历
      4. SCAN命令的意思就是对所有满足条件的key进行非顺序遍历,而是基于游标遍历,每次返回模糊数量的key,以及下一次遍历游标,直到所有的遍历结束返回游标0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值