目录
17、Redis 雪崩、缓存穿透、缓存击穿是什么?如何解决?
1、Cookie和Session的区别?
Session是基于Cookie实现的。
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以从本地的cookie获取你的信息,
考虑到安全性应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,
会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4、个站点单个cookie保存的数据不能超过4K,
很多浏览器都限制一最多保存20个cookie。
5、可以考虑将登陆信息等重要信息存放为session,
其他信息如果需要保留,可以放在cookie中。
6、存取方式的不同
Cookie中只能保管ASCII字符串,假如需求存取Unicode字符或者二进制数据,需求先进行编码。Cookie中也不能直接存取Java对象。若要存储略微复杂的信息,运用Cookie是比拟艰难的。
而Session中能够存取任何类型的数据,包括而不限于String、Integer、List、Map等。Session中也能够直接保管Java Bean乃至任何Java类,对象等,运用起来十分便当。能够把Session看做是一个Java容器类。
7、安全性(隐私策略)的不同
Cookie存储在浏览器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。 假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。最好是像Google、Baidu那样将Cookie信息加密,提交到服务器后再进行解密,保证Cookie中的信息只要本人能读得懂。而假如选择Session就省事多了,反正是放在服务器上,Session里任何隐私都能够有效的保护。
8、有效期上的不同
只需要设置Cookie的过期时间属性为一个很大很大的数字,Cookie就可以在浏览器保存很长时间。 由于Session依赖于名为JSESSIONID的Cookie,而Cookie JSESSIONID的过期时间默许为–1,只需关闭了浏览器(一次会话结束),该Session就会失效。
9、对服务器造成的压力不同
Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。而Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择。
10、 跨域支持上的不同
Cookie支持跨域名访问,例如将domain属性设置为“.baidu.com”,则以“.baidu.com”为后缀的一切域名均能够访问该Cookie。跨域名Cookie如今被普遍用在网络中。而Session则不会支持跨域名访问。Session仅在他所在的域名内有效。
11、Session作用范围
本次会话的所有页面都关闭后再重新访问某个Jsp或者Servlet将会创建新的会话。注意事项:注意原有会话还存在,只是这个旧的SessionID任然存在服务端,只不过再也没有客户端会携带它然后交予服务端校验。
销毁:
Session的销毁只有三种方式:
1.调用了session.invalidate()方法
2.session过期(超时)
3.服务器重新启动
2、常用的集合有哪些,有什么应用场景?
集合是用来存储对象的容器
先说一说集合和数组的区别:
1.数组的长度不可变,而集合的长度是可变的
2.数组可以存基本数据类型和引用数据类型,而集合只能存储引用数据类型List集合:ArrayList和LinkedList,他们的区别是
ArrayList是基于动态数组,查询快,增删改的速度较慢
LinkedList是以链表的形式存储它的数据,查询慢增删快,
LinkedList比ArrayList更占用内存因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。List 有序,可重复,可存在多个空值
Set集合:HashSet、TreeSet、LinkedHashSet
一般来说,如果我们需要保证集合元素是唯一的,就用set集合
Set 有且只能有一个空元素,无序,不可重复TreeSet 相对于Set多了一个有序。
Map集合:HashMap、LinkedHashMap、TreeMap、HashTable
HashMap是最常用的Map,他根据键的HashCode值存储数据,根据键可以直接获取他的值,具有很快的访问速度。重写equals时必须要重写hashcode以保证相同对象有相同的hashcode。
HashMap存储过程:
1.根据key调用hashcode计算出hash值。
2.在put的时候判断严肃,如果不存在调用resize方法创建默认容量为16的数组。
3.确定node在数组中的位置,根据hash值和数组的最大索引值进行与运算得到索引的位置。(将hashCode高16位和低16位异或(^)操作,然后与当前数组长度-1结果进行与(&)操作,最终结果就是数组的下标值。)
4.获取该位置是否有元素,如果没有元素,直接创建一个node放在该位置。
5.如果有元素,判断key是否完全相等,如果相同,把原来的node赋值给一个变量。
6.再去判断此位置是红黑树还是链表。
7.如果是红黑树,则以红黑树的方式将node放在红黑树上。
8.如果是链表,就遍历链表,把node放在最后一位,放完之后需要去判断链表的长度是否超过8,如果超过就需要判断是否将链表转换为红黑树,当数组容量小于64的时候,只会对数组进行扩容,如果数组容量大于64,才会进行链表转红黑树。
9.返回被覆盖的值。
10.判断整个数组是否需要扩容。
遍历时,取到的数据顺序是随机的
HashMap最多只允许一条记录的键为Null,而可以允许多个记录的值为null
HashMap不支持线程同步,是非线程安全的
HashTable与HashMap类似,他不允许记录的键或者值为空,支持线程同步,所以在写入时较慢。
如果考虑线程安全的问题时用ConcurrentHashMap,我们很多时候把ConcurrentHashMap用于本地缓存。
3、浏览器地址栏输入请求到页面展示,描述一下具体流程。
1) SpringMVC 前端控制器 DispatcherServlet捕获浏览器请求。
2) DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404
错误b) 存在则执行下面的流程
3) 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及
Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。4) DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
5) 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
6) 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。
在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定
的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中7) Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
8) 此时将开始执行拦截器的postHandle(…)方法【逆向】。
9) 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model
和View,来渲染视图。10) 渲染视图完毕执行拦截器的afterCompletion(…)方法。
11) 将渲染结果返回给客户端
————————————————
版权声明:本文为CSDN博主「Njupt_dc」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43467956/article/details/124381984
4、现有一个student表sql 查出1班学生男女生人数
name | sex | class |
a | 男 | 1 |
b | 女 | 1 |
... | ... | ... |
select count(name) from stu where class=1 group by sex ;select count(name) from stu where class=1 and sex='男' union select count(name) from stu where class=1 and sex='女';
5、MySQL数据是如何存储的?什么是B+树?
MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层。
一、网络连接层
客户端连接器(Client Connectors):提供与MySQL服务器建立的支持。目前几乎支持所有主流的服务端编程技术,例如常见的 Java、C、Python、.NET等,它们通过各自API技术与MySQL建立连接。
二、服务层(MySQL Server)服务层是MySQL Server的核心,主要包含系统管理和控制工具、连接池、SQL接口、解析器、查询优化器和缓存六个部分。
连接池(Connection Pool):负责存储和管理客户端与数据库的连接,一个线程负责管理一个连接。
系统管理和控制工具(Management Services & Utilities):例如备份恢复、安全管理、集群管理等
SQL接口(SQL Interface):用于接受客户端发送的各种SQL命令,并且返回用户需要查询的结果。比如DML(增删改)、DDL(数据定义语言DDL用来创建数据库中的各种对象-----表、视图、索引、同义词、聚簇等如:CREATE TABLE/VIEW/INDEX/SYN/CLUSTER)、存储过程、视图、触发器等。
解析器(Parser):负责将请求的SQL解析生成一个"解析树"。然后根据一些MySQL规则进一步检查解析树是否合法。
查询优化器(Optimizer):当“解析树”通过解析器语法检查后,将交由优化器将其转化成执行计划,然后与存储引擎交互。缓存(Cache&Buffer): 缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,权限缓存,引擎缓存等。如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。
三、存储引擎层(Pluggable Storage Engines)存储引擎负责MySQL中数据的存储与提取,与底层系统文件进行交互。MySQL存储引擎是插件式的,服务器中的查询执行引擎通过接口与存储引擎进行通信,接口屏蔽了不同存储引擎之间的差异 。现在有很多种存储引擎,各有各的特点,最常见的是MyISAM和InnoDB。
四、系统文件层(File System)
该层负责将数据库的数据和日志存储在文件系统之上,并完成与存储引擎的交互,是文件的物理存储层。主要包含日志文件,数据文件,配置文件,pid 文件,socket 文件等。
日志文件
错误日志(Error log)
默认开启,show variables like ‘%log_error%’
通用查询日志(General query log)
记录一般查询语句,show variables like ‘%general%’;
二进制日志(binary log)
记录了对MySQL数据库执行的更改操作,并且记录了语句的发生时间、执行时长;但是它不记录select、show等不修改数据库的SQL。主要用于数据库恢复和主从复制。
show variables like ‘%log_bin%’; //是否开启
show variables like ‘%binlog%’; //参数查看
show binary logs;//查看日志文件
慢查询日志(Slow query log)
记录所有执行时间超时的查询SQL,默认是10秒。
show variables like ‘%slow_query%’; //是否开启
show variables like ‘%long_query_time%’; //时长
配置文件
用于存放MySQL所有的配置信息文件,比如my.cnf、my.ini等。
数据文件
db.opt 文件:记录这个库的默认使用的字符集和校验规则。
frm 文件:存储与表相关的元数据(meta)信息,包括表结构的定义信息等,每一张表都会
有一个frm 文件。
MYD 文件:MyISAM 存储引擎专用,存放 MyISAM 表的数据(data),每一张表都会有一个
.MYD 文件。
MYI 文件:MyISAM 存储引擎专用,存放 MyISAM 表的索引相关信息,每一张 MyISAM 表对
应一个 .MYI 文件。
ibd文件和 IBDATA 文件:存放 InnoDB 的数据文件(包括索引)。InnoDB 存储引擎有两种
表空间方式:独享表空间和共享表空间。独享表空间使用 .ibd 文件来存放数据,且每一张
InnoDB 表对应一个 .ibd 文件。共享表空间使用 .ibdata 文件,所有表共同使用一个(或多
个,自行配置).ibdata 文件。
ibdata1 文件:系统表空间数据文件,存储表元数据、Undo日志等 。
ib_logfile0、ib_logfile1 文件:Redo log 日志文件。
pid 文件
pid 文件是 mysqld 应用程序在 Unix/Linux 环境下的一个进程文件,和许多其他 Unix/Linux 服务端程序一样,它存放着自己的进程 id。
socket 文件
socket 文件也是在 Unix/Linux 环境下才有的,用户在 Unix/Linux 环境下客户端连接可以不通过TCP/IP 网络而直接使用 Unix Socket 来连接 MySQL。
————————————————
原文链接:https://blog.csdn.net/cyd_0619/article/details/113627509
6、Java导致内存泄漏的原因。
1、单例造成的内存泄漏
2、非静态内部类创建静态实例造成的内存泄漏
3、Handler造成的内存泄漏(创建匿名内部类的静态对象)
4、线程造成的内存泄漏
5、资源未关闭造成的内存泄漏
6、集合容器中的内存泄露
7、nginx的作用是什么?是如何配置的?
1、正/反向代理
2、负载均衡
3、动静分离
为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来进行解析,加快解析速度,降低原来单个服务器的压力。
4、web服务器
########### 每个指令必须有分号结束。################# #user administrator administrators; #配置用户或者组,默认为nobody nobody。 #worker_processes 2; #允许生成的进程数,默认为1 #pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址 error_log log/error.log debug; #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg events { accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off #use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport worker_connections 1024; #最大连接数,默认为512 } http { include mime.types; #文件扩展名与文件类型映射表 default_type application/octet-stream; #默认文件类型,默认为text/plain #access_log off; #取消服务日志 log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式 access_log log/access.log myFormat; #combined为日志格式的默认值 sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。 sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。 keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。 upstream mysvr { server 127.0.0.1:7878; server 192.168.10.121:3333 backup; #热备 } error_page 404 https://www.baidu.com; #错误页 server { keepalive_requests 120; #单连接请求上限次数。 listen 4545; #监听端口 server_name 127.0.0.1; #监听地址 location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。 #root path; #根目录 #index vv.txt; #设置默认页 proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表 deny 127.0.0.1; #拒绝的ip allow 172.18.5.54; #允许的ip } } } 注意事项:
1、几个常见配置项:
- 1.$remote_addr 与 $http_x_forwarded_for 用以记录客户端的ip地址;
- 2.$remote_user :用来记录客户端用户名称;
- 3.$time_local : 用来记录访问时间与时区;
- 4.$request : 用来记录请求的url与http协议;
- 5.$status : 用来记录请求状态;成功是200;
- 6.$body_bytes_s ent :记录发送给客户端文件主体内容大小;
- 7.$http_referer :用来记录从那个页面链接访问过来的;
- 8.$http_user_agent :记录客户端浏览器的相关信息;
2、惊群现象:一个网路连接到来,多个睡眠的进程被同时叫醒,但只有一个进程能获得链接,这样会影响系统性能。
3、每个指令必须有分号结束。
8、常用的linux命令有哪些?
shutdown -h now/min 或者 poweroff //立即/定时关机
shutdown -r now/min 或者 reboot //立即/定时重启
su 用户名 //用户切换
cd 目录 //切换目录
ls //查看当前目录下的所有目录和文件
pwd //显示当前位置路径
mkdir 文件夹名 //在当前位置新建文件夹
ps -ef //查看所有正在运行的进程
mkdir -p /a/b/文件夹名 //在指定目录位置,创建文件夹,并创建父文件夹
mv 当前目录名 新目录名 //修改目录名,同样适用与文件操作
cp /usr/tmp/tool /opt //将/usr/tmp目录下的tool目录复制到 /opt目录下面
find /bin -name 'a*' //查找/bin目录下的所有以a开头的文件或者目录
touch a.txt //在当前目录下创建名为a的txt文件(文件不存在),如果文件存在,将文件时间属性修改为当前系统时间
rm 文件名 //删除当前目录下的文件
cat a.txt //查看文件最后一屏内容
less a.txt //PgUp向上翻页,PgDn向下翻页,"q"退出查看
more a.txt //显示百分比,回车查看下一行,空格查看下一页,"q"退出查看tail -100 a.txt //查看文件的后100行,"Ctrl+C"退出查看
top //显示当前系统中占用资源最多的一些进程, shift+m 按照内存大小查看
yum install xxx //使用yum安装xxxxkill pid //杀死该pid的进程
kill -9 pid //强制杀死该进程
ifconfig //查看网络
service iptables status //查看iptables服务的状态
service iptables start //开启iptables服务
service iptables stop //停止iptables服务
service iptables restart //重启iptables服务
chkconfig iptables off //关闭iptables服务的开机自启动
chkconfig iptables on //开启iptables服务的开机自启动
##centos7 防火墙操作
systemctl status firewalld.service //查看防火墙状态
systemctl stop firewalld.service //关闭运行的防火墙
systemctl disable firewalld.service //永久禁止防火墙服务
tar -zcvf 打包压缩后的文件名 要打包的文件
参数说明:z:调用gzip压缩命令进行压缩; c:打包文件; v:显示运行过程; f:指定文件名;
示例:
tar -zcvf a.tar file1 file2,... //多个文件压缩打包
文件权限简介:'r' 代表可读(4),'w' 代表可写(2),'x' 代表执行权限(1),括号内代表"8421法"
##文件权限信息示例:-rwxrw-r--
-第一位:'-'就代表是文件,'d'代表是文件夹
-第一组三位:拥有者的权限
-第二组三位:拥有者所在的组,组员的权限
-第三组三位:代表的是其他用户的权限
普通授权 chmod +x a.txt
8421法 chmod 777 a.txt //1+2+4=7,"7"说明授予所有权限
vi 文件名 //打开需要编辑的文件
--进入后,操作界面有三种模式:命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode)
命令模式
-刚进入文件就是命令模式,通过方向键控制光标位置,
-使用命令"dd"删除当前整行
-使用命令"/字段"进行查找
-按"i"在光标所在字符前开始插入
-按"a"在光标所在字符后开始插入
-按"o"在光标所在行的下面另起一新行插入
-按":"进入底行模式
公众号:网络技术联盟站
插入模式
-此时可以对文件内容进行编辑,左下角会显示 "-- 插入 --""
-按"ESC"进入底行模式
底行模式
-退出编辑: :q
-强制退出: :q!
-保存并退出: :wq
## 操作步骤示例 ##
1.保存文件:按"ESC" -> 输入":" -> 输入"wq",回车 //保存并退出编辑
2.取消操作:按"ESC" -> 输入":" -> 输入"q!",回车 //撤销本次修改并退出编辑
## 补充 ##
vim +10 filename.txt //打开文件并跳到第10行
vim -R /etc/passwd //以只读模式打开文件
9、SpringBoot执行流程是什么?
(1)创建 Spring Application 实例,调用 run 方法,同时将启动入口类作 为参数传递进去,由此开始了 Spring Boot 内部相关核心组件以及配置的 启动和加载;
(2)通过 Spring Factories Loader 加载 META-INF/spring.factories 文 件,获取并创建 SpringApplicationRunListener 对象;
(3)然后由 SpringApplicationRunListener 来发出 starting 消息;
(4)创建参数,并配置当前 SpringBoot 应用需要使用的 Environment 实 例;
(5)完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息;
(6)创建 Spring 的应用上下文实例:ApplicationContext,初始化该实例 并设置应用环境配置实例:Environment,同时加载相关的配置项;
(7)由 SpringApplicationRunListener 发出 contextPrepared 消息,告 知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;
(8)将各种 Bean 组件装载入 Spring 的 IO 容器/应用上下文: ApplicationContext 中,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用当前使用的 ApplicationContext 已准备完毕;
(9)重新刷新 Refresh Spring 的应用上下文实例:ApplicationContext, 完成 IOC 容器可用的最后一步;
(10)由 SpringApplicationRunListener 发出 started 消息,完成最终的 程序的启动;
(11)由 SpringApplicationRunListener 发出 running 消息,告知程序已 成功运行起来了。
10、socket编程三次握手和四次挥手
三次握手:
第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认。
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手异常
重传存在最大重传次数
第一次握手丢失:客户端会重传。
由于第一次握手后,客户端会进入SYN_SEND状态,客户端长时间未收到服务端SYN+ACK包,一定时间后,会触发客户端重传机制。
第二次握手丢失:客户端和服务端都会重传。
第二次握手后,客户端在SYN_SEND状态中,一直接收不到服务端的SYN+ACK包,因此客户端会认为第一次握手失败,会触发重传机制,同时,服务端在SYN_RECV状态长时间未收到客户端的ACK包,因此服务端也会触发重传机制。
第三次握手丢失:服务端会重新进行第二次握手,即重传SYN+ACK包
因为,客户端发送ACK包不需要回应,因此客户端不回重传,服务端会重传SYN+ACK。
四次挥手:
● 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
● 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
● 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
● 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
● 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
● 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
● 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。
● 这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
挥手异常
第一次挥手:客户端触发重传
第一次挥手会进入FIN_WAIT_1状态,长时间会触发重传,重传次数达到
tcp_orphan_retries
会直接进入close第二次挥手:客户端会触发重传
因为第二次挥手是服务端发送ACK报文,而ACK报文不会重传,所以,客户端长时间未收到服务端ACK报文会触发重传机制。
第三次挥手:服务端触发重传
服务端进入LAST_ACK状态后,长时间未收到客户端的ACK报文,触发重传机制。
第四次挥手:服务端触发重传
服务端进入LAST_ACK状态后,长时间未收到客户端的ACK报文,触发重传机制。
11、SpringCloud五大基本组件?
1、Eureka实现服务治理;
2、Ribbon主要提供客户侧的软件负载均衡算法;
3、Hystrix断路器,保护系统,控制故障范围;
4、Zuul,api网关,路由,负载均衡等多种作用;
5、Config配置管理。
12、RabbitMQ解决消息丢失和重复消费?
MQ消息丢失
1、生产者未能成功将消息发给MQ
原因:因为网络传输的不稳定性,当生产者在向MQ发送消息的过程中,MQ没有成功接收到消息,但是生产者却以为MQ成功接收到了消息,不会再次重复发送该消息,从而导致消息的丢失。
解决:事务机制和confirm机制
2、MQ收到消息后丢失消息
原因:RabbitMQ接收到生产者发送过来的消息,是存在内存中的,如果没有被消费完,此时RabbitMQ宕机了,那么再次启动的时候,原来内存中的那些消息都丢失了。
解决:
消息持久化:1、队列持久化 2、消息持久化
注意:
1、持久化要起作用必须同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。
2、如果开启了消息的持久化,只有当消息成功持久化磁盘之后,才会回调生产者的接口返回ack消息,否则都算失败。
3、消费者弄丢了消息
原因:MQ确认消息发送成功后,消费者宕机丢失消息。
解决:关闭 RabbitMQ 的自动
ack
,采用手动ack
(消息应答机制:分为自动应答和手动应答)。消息在接收到消息并成功消费后,消费者进行应答。
MQ消息重复消费
原因:网络故障等问题导致,消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。
解决:保证消息的唯一性;保证消息的幂等性;(幂等性:用户对于同一操作发起的一次请求或者多次请求的结果是一致的)
- 在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;
- 在消息消费时,要求消息体中必须要有一个Id(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。
举例:
- 数据库insert相同id,会产生冲突;
- redis的set操作,具有幂等性;
- 通过第三方来进行消费记录,例如redis记录消费,消费前进行查询;
MQ应答机制
/**
* 消费者消费消息
* 1.消费哪个队列
* 2.消费成功之后是否要自动应答。 true代表自动应答 ,false手动应答
* 3.消费者,成功消费的回调
* 4.消费者 取消消费的回调
*/
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
MQ事务
// 开启事务
channel.txSelect
try {
// 这里发送消息
} catch (Exception e) {
channel.txRollback
// 这里再次重发这条消息
}// 提交事务
channel.txCommit
MQ Confirm
RabbitMQ可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,生产者每次写的消息都会分配一个唯一的 id,如果消息成功写入 RabbitMQ 中,RabbitMQ 会给生产者回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,生产者可以重新发送。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么可以重发。
三种确认方式:
同步-单独:
一个消息之后只有它被确认发布,后续的消息才能继续发布。
同步-批量发布:
先发布一批消息然后一起确认可以极大地提高吞吐量。
缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布这些消息。
异步处理:
- 第一步:发消息时记录下所有发送的消息
- 第二步:在消息确认成功的回调函数中删除已经确认的消息,剩下的就是未确认的消息
- 第三步:在消息确认失败的回调函数中对剩余的消息做处理。
三种确认速度对比:
- 同步-单独发布消息:同步等待确认,简单,但吞吐量非常有限。
- 同步-批量发布消息:批量同步等待确认,简单,合理的吞吐量,一旦出现问题但很难推断出是那条消息出现了问题。
- 异步处理:最佳性能和资源使用,在出现错误的情况下可以很好地控制,但是实现起来稍微难些。
13、RabbitMQ宕机是如何处理的?
消息的持久化,将消息存储到数据库里,宕机后重新读取数据消息。
14、Redis有哪些基本类型?都有什么常用命令?
基本类型:String,Set,Zset,Hash,List
服务命令:
- redis-cli :连接本地的 redis 服务
- redis-cli -h host -p port -a password :连接远程redis服务
- ping : 检测连接是否存活
- echo: 在命令行打印一些内容
- quit、exit: 退出客户端
- shutdown: 退出服务器端
- info: 返回redis相关信息
- config get dir/* 实时传递接收的请求
- showlog: 显示慢查询
- select n: 切换到数据库n,redis默认有16个数据库(DB 0~DB 15),默认使用的第0个
- dbsize: 查看当前数据库大小
- move key n: 不同数据库之间数据是不能互通的,move移动键到指定数据库
- flushdb: 清空当前数据库中的键值对。
- flushall: 清空所有数据库的键值对。
key相关命令:
- keys * :查看当前数据库中所有的key
- dbsize: 键总数
- exists key: 检查键是否存在
- del key [key …]: 删除键
- expire key seconds: 键过期
- ttl key: 获取键的有效时长
- persist key: 移除键的过期时间
- type key: 键的数据结构类型
- randomkey: 随机返回数据库中一个键
- rename key1 key2 : 重命名
- renamex key1 key2 : 当key2不存在时,key1重命名
————————————————
原文链接:https://blog.csdn.net/Lzy410992/article/details/116094703
15、Redis是如何实现持久化的?
1、RDB
把当前进程数据生成快照保存到磁盘上
- 触发方式
- 手动:
- save命令:阻塞当前Redis服务器,直到RDB过程完成为止
- bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
- 自动:
redis.conf中配置
save m n
,即在m秒内有n次修改时,自动触发bgsave生成rdb文件;主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点;
执行debug reload命令重新加载redis时也会触发bgsave操作;
默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作;
优点
- RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
- Redis加载RDB文件恢复数据要远远快于AOF方式;
缺点
- RDB方式实时性不够,无法做到秒级的持久化;
- 每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;
- RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全;
- 版本兼容RDB文件问题
2、AOF
Redis先执行命令,把数据写入内存,然后将命令记录日志。
- 实现:
命令追加:
当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。
文件写入和同步
关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:
Always
,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;Everysec
,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;No
,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。注:Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。
16、Java如何实现多个线程按规定顺序执行?
1、子线程join()
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("打开冰箱!");
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("拿出一瓶牛奶!");
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("关上冰箱!");
}
});
//下面三行代码顺序可随意调整,程序运行结果不受影响。
thread3.start();
thread2.start();
thread1.start();
2、主线程join()
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("打开冰箱!");
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("拿出一瓶牛奶!");
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("关上冰箱!");
}
});
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
3、 通过倒数计时器CountDownLatch实现
CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。
public class ThreadCountDownLatchDemo {
private static CountDownLatch countDownLatch1 = new CountDownLatch(1);
private static CountDownLatch countDownLatch2 = new CountDownLatch(1);
public static void main(String[] args) {
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("打开冰箱!");
countDownLatch1.countDown();
}
});
final Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch1.await();
System.out.println("拿出一瓶牛奶!");
countDownLatch2.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch2.await();
System.out.println("关上冰箱!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//下面三行代码顺序可随意调整,程序运行结果不受影响
thread3.start();
thread1.start();
thread2.start();
}
}
4、通过创建单一化线程池newSingleThreadExecutor()实现
public class ThreadPoolDemo {
static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
final Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("打开冰箱!");
}
});
final Thread thread2 =new Thread(new Runnable() {
@Override
public void run() {
System.out.println("拿出一瓶牛奶!");
}
});
final Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("关上冰箱!");
}
});
executorService.submit(thread1);
executorService.submit(thread2);
executorService.submit(thread3);
executorService.shutdown(); //使用完毕记得关闭线程池
}
}
参考:https://www.jb51.net/article/246666.htm
17、Redis 雪崩、缓存穿透、缓存击穿是什么?如何解决?
缓存穿透:访问缓存和数据库都没有的数据,导致数据库压力大。
解决:
- 将空key添加到缓存中。
从缓存取不到的数据,在数据库中也没有取到,新增key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
- 使用布隆过滤器过滤空key。
bloomfilter类似于一个hash set,用于快速判某个元素是否存在于集合中,布隆过滤器的关键就在于hash算法和容器大小。
- 一般对于这种访问可能由于遭到攻击引起,可以对请求进行身份鉴权、数据合法行校验等。
缓存击穿:缓存中没有但数据库中有的数据
解决:
- 设置热点数据永远不过期。定时更新缓存,但如果更新出问题会导致缓存中的数据一直为旧数据。
- 接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。
- 加互斥锁或者分布锁。
雪崩:缓存服务宕机或大量的key值同时过期,导致所有请求都直接访问数据库导致数据库压力增大。
解决:避免同时过期
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
设置热点数据永远不过期。
缓存污染:不用的旧数据依然留存在缓存中,消耗缓存空间。
解决:根据淘汰策略去淘汰数据。
八种淘汰策略:
- 不淘汰
- noeviction (v4.0后默认的)
- 对设置了过期时间的数据中进行淘汰
- 随机:volatile-random
- ttl:volatile-ttl
- lru:volatile-lru
- lfu:volatile-lfu
- 全部数据进行淘汰
- 随机:allkeys-random
- lru:allkeys-lru
- lfu:allkeys-lfu
18、URL和URI区别?
URL(统一资源定位器)用于标识资源;
URI(统一资源标识符)提供了更简单和可扩展的标识资源的方法。URL是URI的子集,下面我们就来看看一下URL和URI的主要区别是什么。
URN(统一资源名称)
1、作用的区别
URL(统一资源定位符)主要用于链接网页,网页组件或网页上的程序,借助访问方法(http,ftp,mailto等协议)来检索位置资源。
URI(统一资源标识符)用于定义项目的标识,此处单词标识符表示无论使用的方法是什么(URL或URN),都要将一个资源与其他资源区分开来。
2、可以说URL是URI(URL是URI的子集),但URI永远不能是URL。
3、协议区别
URL指定要使用的协议类型,而URI不涉及协议规范。
19、有哪些常见的设计模式?
创建型模式
- 单例模式:某个类只能有一个实例,提供一个全局的访问点。
- 工厂模式:一个工厂类根据传入的参数决定创建出那一种产品类的实例。
- 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
- 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
结构型模式
- 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
- 组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
- 装饰模式:动态的给对象添加新的功能。
- 代理模式:为其他对象提供一个代理以便控制这个对象的访问。
- 亨元模式:通过共享技术来有效的支持大量细粒度的对象。
- 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
- 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
行为型模式
- 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
- 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
- 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
- 状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
- 观察者模式:对象间的一对多的依赖关系。
- 备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
- 中介者模式:用一个中介对象来封装一系列的对象交互。
- 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
- 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
- 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
- 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
————————————————
参考链接:https://blog.csdn.net/weixin_57002282/article/details/124263340
20、Spring中用到了哪些设计模式?
工厂模式、单例模式、代理模式、模板模式、观察者模式、适配器模式。
21、索引失效
1、like 以%开头,索引无效;当like前缀没有%,后缀有%时,索引有效。
2、or语句前后没有同时使用索引。
3、组合索引,不是使用第一列索引,索引失效。
4、如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。
例如 select * from student where name=1;name字段为varchar字段,传入参数1为数值
5、在索引列上使用 IS NULL 或 IS NOT NULL操作。(不一定)
6、在索引字段上使用not,<>,!=。
7、对索引字段进行计算操作、字段上使用函数。
例如 select * from student where concat('张',name)='张三';
8、当全表扫描速度比索引速度快时,mysql会使用全表扫描,此时索引失效。
9、违背最左匹配原则且不满足索引覆盖
(索引覆盖:使用索引时,索引树查询到的叶子节点上的数据可以覆盖到你查询的所有字段)