后端日志-week06
day01
MySQL查询语句
查询结果去重
使用Distinct和group by都能够实现去重的效果。
distinct
只能放在查询字段的最前面,不能放在查询字段的中间或者后面。distinct 对后面所有的字段均起作用,即 去重是查询的所有字段完全重复的数据,而不是只对 distinct 后面连接的单个字段重复的数据。因此,Distinct查询多个字段只对一个字段去重是无法实现的。SELECT DISTINCT university from user_profile;
group by
一般与聚类函数使用(如count()/sum()等),也可单独使用。group by
可以使查询结果按一个或者多个字段进行分组(查询的字段可以不等于group by的字段),SELECT university from user_profile GROUP BY university
;
查询结果限制返回行数
- LIMIT n:从第0+1(m=0)条开始,取n条数据,是LIMIT 0,n的缩写
1 | SELECT device_id |
- LIMIT m,n:从第m+1条开始,取n条数据
1 | SELECT device_id |
- LIMIT n OFFSET m:跳过前m条数据,从第m+1条开始取n条数据
1 | SELECT device_id |
tips : between是闭区间,包含了两个端点。
不等于的几种写法
1 | select device_id,gender,age,university |
空值的过滤
过滤空值的三种方法:
(1) Where 列名 is not null(推荐使用)
(2) Where 列名 != ‘null’
(3) Where 列名 <> ‘null’
or的使用
当查询列含有索引,使用or将会使索引失效,进行全表扫描,此时推荐使用UNION进行联合查询,二者各自走索引。
1 | -- 走索引 |
sql中的字符匹配
- _代表任意一个字符
1 | SELECT * FROM 学生表 WHERE name LIKE '张__'//查询姓“张”且名字是3个字的学生姓名。 |
% 代表任意数量(包括0个)的字符
[]匹配[]中的任意一个字符(若要比较的字符是连续的,则可以用连字符“-”表 达),
[^ ] 不匹配[ ]中的任意一个字符
1 | SELECT * FROM 学生表 WHERE 姓名 LIKE '[张李刘]%' --查找姓张、李、刘 |
聚合函数
AVG()、COUNT()、SUM()、MAX()、MIN()、ROUND();
HAVING是在聚合函数之后,GROUP BY 之前的条件。主要用于过滤分组。
1 | --聚合函数应用经典案例 |
连接查询与子查询
join
Join和Inner Join是相同的,表示在表中存在至少一个匹配时返回行,相当于返回的是两个的交集。
LeftJoin从左表(table1)返回所有的行,即使右表(table2)中没有匹配。如果右表中没有匹配,则结果为 NULL。
总结
A Inner Join B
:类似于取A和B的交集,只取满足条件的相交部分。A Left Join B
:左连接,以左表A为基础,取A表的全部行,B满足On条件的取对应行,不满足的取NULL。A Right Join B
:有连接,以右表B为基础,取B表的全部行,A满足On条件的取对应行,不满足的取NULL。A Full Outer Join B
:类似于取A和B的并集。全都取,但彼此没有对应的值就取NULL。
在进行连接操作时where和on的区别
- ON条件是在生成临时表时使用的条件,它不管ON中的条件是否满足,都会返回左表中的数据。
- where 条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有 left join 的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。
tips : 子查询返回多个值只能用
in
,确保只返回一个值可以用=
。
1 |
|
Union与Union all的使用(or不走索引时,考虑Union)
- 结果集:
UNION
会在结果集中自动去除重复的行并会自动排序,会带来一定的性能开销。UNION ALL不会进行排序也不会去重,结果集顺序由各个子查询顺序决定,
IF、CASE WHEN、COALESCE等函数的使用
IF语句和CASE WHEN语句通常用于根据某些条件返回不同值的函数。
if(x=n,a,b)表示如果x=n,则返回a,否则返回b。
1 | -- IF下面返回两列,一列是25岁及以上,另一列是25岁以下。 |
day03
Spring-WebFlux
响应式编程
tips : 受欢迎的web库Vertx完美支持协程,但在生态上并不好,不利于快速开发,协程对应响应式、Java/Kotlin的响应式生态不完整,导致响应式无法被普遍使用,导致协程无法被广泛使用。(协程和异步响应式往往是对应的)
JDK1.8是基于Observer/Observable接口而实现的观察者模式,JDK9及以后,Observer/Observable接口就被弃用了,取而代之的是Flow类(juc包下面的Flow)。(采用发布者订阅者模式并在两者之间建立订阅关系)
响应式流规范可以总结为4个接⼝:Publisher、Subscriber、Subscription和Processor
。Publisher
负责⽣成数据,并将数据发送给Subscription
(每个Subscriber
对应⼀个Subscription
)。一旦Subscriber
订阅成功,就可以接收来⾃Publisher
的事件。Publisher调⽤ onSubscribe() ⽅法时,会将Subscription对象传递给Subscriber。通过Subscription,Subscriber可以管理其订阅情况。Subscriber开始请求数据后,数据就会开始流经响应流,
1 | //下面发布者订阅者相关类都是juc包下面的flow类 |
Reactor项目的Flux和Mono
Flux
和Mono
都是数据流的发布者,使用Flux
和Mono
都可以发出三种数据信号:元素值、错误信号、完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。
tips : Mono和Flux的很多操作都是相同的,二者处理的数据量不同。Flux返回N个元素,Mono返回0或1个元素。Flux适用于处理多个元素,Mono适用于处理只有一个或没有元素的情况。
1 | Flux<String> characterFlux = Flux |
异步机制
事件轮询
事件轮询机制在多个事件源或输入源之间检测和处理事件。轮询机制不断检查是否有新的事件发生,并根据检测到的事件作出相应处理。与事件驱动机制不同,事件轮询通过主动查询的方式来获取事件的状态,而不是等待事件自动触发。
工作流程:
- 创建一个轮询器,并为它指定一个或多个事件源(如文件、网络套接字、定时器等)。
- 在循环中不断查询事件源的状态,检查是否有事件发生。
- 一旦检测到事件,立即调用相应的处理程序来处理该事件。
例如:程序启动后,初始化一个轮询器,用来检测多个客户端的连接。轮询器不断轮询客户端的连接状态,检测是否有新的请求。如果某个客户端有请求数据到达,轮询器立即调用对应的函数处理该请求。处理完之后继续轮询。
NIO
1.NIO的非阻塞I/O模型
- 在传统的阻塞I/O中,程序通常需要为每一个客户端连接创建一个线程,每个线程阻塞等待I/O操作完成(例如读或写操作)。这种模型在高并发场景下效率较低,因为线程数量的增加会导致大量的上下文切换和资源消耗。
- NIO 提供了一种非阻塞模式,程序不必阻塞等待 I/O 操作完成。它允许在单个线程中管理多个通道(Channel),从而极大地提升了性能。
2.Selector与事件轮询
- NIO的核心组件之一是
Selector
,它就是事件轮询机制的一个实现。Selector
允许程序同时监控多个通道(如网络套接字)的状态。程序通过Selector
注册多个通道,然后可以在事件发生时轮询这些通道,处理其中的I/O事件(如可读、可写等)。 - Selector 内部使用类似于操作系统提供的select()、poll()或epoll()等系统调用实现事件轮询机制,监听通道的状态变化。
3.具体工作流程
- 事件注册:Selector可以注册多个
Channel
,并指定关注的事件类型(如READ、WRITE等)。 - 事件轮询:Selector进入轮询循环,通过操作系统底层的事件轮询机制不断检查通道上是否有事件发生。
- 事件处理:当检测到有通道上有事件发生时,
Selector
将返回相应的事件,程序会调用相应的事件处理逻辑来处理这些I/O事件。
4.总结
NIO的Selector
是Java中事件轮询机制的核心实现,依赖底层操作系统提供的 select()、poll() 或 epoll() 等机制来高效地管理大量 I/O 通道的状态。通过事件轮询机制,NIO 能够在单个线程中处理多个I/O事件,从而显著提升高并发场景下的性能和资源利用率。
select()、poll() 和 epoll()
1.select()
- select() 通过将需要监控的文件描述符集合传递给内核,内核遍历这些文件描述符,检查它们是否可读、可写或者有异常发生。
- 程序调用 select() 函数并传入一组文件描述符,内核会阻塞该调用,直到其中至少有一个文件描述符变为可用(如可读或可写),或超时发生。
- 文件描述符通过三个
fd_set
集合来表示,分别用于读、写和异常事件的检测。
底层实现:
select() 依赖于位图(bitmap)来表示每个文件描述符的状态。其实现方式是在每次调用时,遍历所有文件描述符,检查它们的状态变化。当事件发生时,内核更新位图并返回给用户空间程序。
不足:
- 文件描述符的数量有上限:通常为1024。
- 每次调用select() 时,用户空间和内核空间之间需要复制文件描述符集合,这会导致性能瓶颈。
- select()会遍历整个文件描述符集合,即使只有少数几个文件描述符有事件发生,这种线性扫描会导致效率低下。
2.poll()
- 不同于
select()
使用位图表示文件描述符,poll()
使用一个结构体数组**pollfd[]**来保存每个文件描述符及其感兴趣的事件。 - 和
select()
一样,poll()
的检查过程也是每次都会遍历整个文件描述符集合。 poll()
会在用户空间和内核空间之间复制一个pollfd
结构数组(所以每次调用poll时也会在用户态和内核态之间复制),然后内核通过线性扫描检查每个文件描述符是否有事件发生。poll()
是水平触发的,即当某个文件描述符的状态改变后,如果不处理它,则每次调用poll()
都会通知该文件描述符的状态变化。
3.epoll()
工作原理:
epoll()
是Linux专有的I/O多路复用机制,是 select() 和 poll() 的改进版本。它在处理大量并发连接时效率更高,特别适合高并发场景。epoll()
除水平触发机制外,还有边缘触发:只在文件描述符的状态从不可用变为可用时才会通知程序。
底层实现:
- 事件注册机制:
epoll()
首次调用时,用户通过epoll_create()
创建一个epoll
实例,然后通过epoll_ctl()
将要监控的文件描述符注册到这个实例中。与select()
和poll()
每次调用时都需要传递文件描述符不同,epoll()
将文件描述符注册一次,后续就可以直接通过事件通知机制来获取I/O事件。 - 内核事件队列:
epoll
通过一个内核级事件队列来监控文件描述符的状态。当某个文件描述符有事件发生时,内核将该事件加入队列中,然后用户空间通过epoll_wait()
来获取已发生的事件。 epoll
使用红黑树来管理文件描述符,以实现高效的增删操作,事件发生时,内核将事件加入双向链表中,避免了遍历整个文件描述符集合。
区别总结:
Spring WebFlux对响应式编程(异步)的支持
tips: WebFlux的默认嵌⼊式服务器是Netty⽽不是Tomcat。Netty是⼀个异步、事件驱动的服务器,⾮常适合Spring WebFlux这样的反应式Web框架。
传统的基于Servlet的Web框架,如Spring MVC,在本质上都是阻塞和多线程的,每个连接都会使用一个线程。在请求处理的时候,会在线程池中拉取一个工作者( worker )线程来对请求进行处理。同时,请求线程是阻塞的,直到工作者线程提示它已经完成为止。
在Spring5中,引入了一个新的异步、非阻塞的WEB模块,就是Spring-WebFlux。该框架在很大程度上是基于Reactor项目的,能够解决Web应用和API中对更好的可扩展性的需求。
异步的Web框架能够以更少的线程获得更⾼的可扩展性,通常它们只需要与CPU核⼼数量相同的线程。通过使⽤所谓的事件轮询(event looping)机制,这些框架能够⽤⼀个线程处理很多请求,这样每次连接的成本会更低。
1 |
|
SpirngWebFlux一般用于SpringCloud-Gateway网关,用于处理请求、路由转发等功能。
try-with-resources
在try-with-resources内的资源,需要释放的资源(比如 BufferedReader)实现了 AutoCloseable接口。
day06
使用枚举类的方法
直接枚举类名.加上枚举字段名加上get方法。