六、中间件篇
1.消息中间件如何保证消息的一致性和如何进行消息的重试机制?
2.Spring Cloud熔断机制介绍;
在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand,Hystrix会找有这个注解的方法,并将这类方法关联到和熔断器连在一起的代理上。当前,@HystrixCommand仅当类的注解为@Service或@Component时才会发挥作用。
3.Spring Cloud对比下Dubbo,什么场景下该使用Spring Cloud?
1.①dubbo由于是二进制的传输,占用带宽会更少 ②springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大 ③dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决 ④springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级 ⑤dubbo的注册中心可以选择zk,redis等多种,springcloud的注册中心只能用eureka或者自研 2.对于类似于电商等同步调用场景多并且能支撑搭建Dubbo 这套比较复杂环境的成本的产品而言,Dubbo 确实是一个可以考虑的选择。但如果产品业务中由于后台业务逻辑复杂、时间长而导致异步逻辑比较多的话,可能Dubbo 并不合适。同时,对于人手不足的初创产品而言,这么重的架构维护起来也不是很方便 参考链接:https://blog.csdn.net/u010664947/article/details/80007767
七、数据库篇
4.锁机制介绍:行锁、表锁、排他锁、共享锁;
①表锁和行锁锁的粒度不一样,表锁锁住的是一整张表,行锁锁住的是表中的一行数据。InnoDB使用的是行级锁,MyISAM使用的是表级锁。
②共享锁又称读锁(S锁),一个事务获取了共享锁,其他事务可以获取共享锁,不能获取排他锁,其他事务可以进行读操作,不能进行写操作。
③排他锁又称写锁(X锁),如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);
5.乐观锁的业务场景及实现方式;
1.①悲观锁(Pessimistic Lock): 每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。 ②乐观锁(Optimistic Lock):每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。 2.适用场景:
①悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。 ②乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。 总结:两种所各有优缺点,读取频繁使用乐观锁,写入频繁使用悲观锁。
6.事务介绍,分布式事物的理解,常见的解决方案有哪些,什么事两阶段提交、三阶段提交;
1.事务:事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。 2.①分布式事务:用需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。 3.解决方案: ①两阶段提交; ②补偿事务:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作; ③本地消息表(异步确保):消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送 4.两阶段提交:①TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。在发送了否定答复并回滚了已经的工作后,RM就可以丢弃这个事务分支信息。②TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。 三阶段提交:①CanCommit阶段:3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。
②PreCommit阶段: 协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。
③doCommit阶段, 该阶段进行真正的事务提交,也可以分为以下两种情况。 Case 1:执行提交 1).发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。 2).事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。 3).响应反馈 事务提交完之后,向协调者发送Ack响应。 4).完成事务 协调者接收到所有参与者的ack响应之后,完成事务。 Case 2:中断事务 协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。 1).发送中断请求 协调者向所有参与者发送abort请求 2).事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。 3).反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息 4).中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断
参考链接:http://www.tianshouzhi.com/api/tutorials/distributed_transaction
7.MySQL记录binlog的方式主要包括三种模式?每种模式的优缺点是什么?
①Row Level行模式:日志中会记录每一行数据被修改的形式,然后在slave端再对相同的数据进行修改 优点:在row level模式下,bin-log中可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录那一条被修改。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节。不会出现某些特定的情况下的存储过程或function,以及trigger的调用和触发无法被正确复制的问题 缺点:row level,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,会产生大量的日志内容。 ②Statement Level(默认):每一条会修改数据的sql都会记录到master的bin-log中。slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行 优点:statement level下的优点首先就是解决了row level下的缺点,不需要记录每一行数据的变化,减少bin-log日志量,节约IO,提高性能,因为它只需要在Master上锁执行的语句的细节,以及执行语句的上下文的信息。 缺点:由于只记录语句,所以,在statement level下 已经发现了有不少情况会造成MySQL的复制出现问题,主要是修改数据的时候使用了某些定的函数或者功能的时候会出现。 ③Mixed 自动模式:在Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志格式,也就是在Statement和Row之间选择一种。如果sql语句确实就是update或者delete等修改数据的语句,那么还是会记录所有行的变更。
8.同步\异步\阻塞\非阻塞;
1.同步与异步:获取完成标志的方式。如果是采用轮询的方式监测I/O操作是否完成称为同步,而以通过回调通知的方式获得完成标志则称为异步。 阻塞与非阻塞:在那段时间差的过程中,CPU有没有处理别的事情,如果处理过别的事情则是非阻塞,如果并没有处理过别的事情则是阻塞。 2.①同步阻塞:即是在B阶段CPU一直采用轮询的方式直到获得完成标志,所以此段时间CPU一直阻塞在此I/O操作上。 ②同步不阻塞:在B阶段依然采用轮询的方式直至获得完成标志,但是此轮询不同于上面的轮询过程,而是在相邻的轮询中完成了上下文切换去处理别的任务的,所以是同步不阻塞 ③异步阻塞:也就是所没有上面的①,②过程,当I/O操作完成后回调通知CPU已完成【即是③过程】,但是此阶段CPU处于休眠状态而不处理别的任务。= ④异步不阻塞:和上面一样没有①,②过程而是通过回调知道I/O操作已完成,但是并没有休眠,而是在此阶段处理其他任务。 综上所述:异步不阻塞是最高效的。
9.数据库事务隔离级别,MySQL默认的隔离级别;
MySQL数据库提供四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证
MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
10.Spring如何实现事务、JDBC如何实现事务、嵌套事务实现;
spring事务:①获取事务属性。 ②加载配置中配置的TransactionManager。 ③不同的事务处理使用不同的逻辑,第一点区别在事务属性上,编程式事务是不需要有事务属性的。第二点区别在TransactionManager上,CallbackPreferringPlatformTransactionManager实现了PlatformTransactionManager接口,暴露出一个方法用于执行事务处理中的回调。所以这两种方式都可以 用作事务处理方式的判断。 ④在目标方法执行前获取事务并收集事务信息。(事务信息和事务属性并不相同) ⑤执行目标方法。 ⑥一旦出现异常,尝试异常处理。并不是所有异常spring都会回滚,默认只对RuntimeException回滚。 ⑦提交事务前的事务信息清除。 ⑧提交事务。 jdbc事务:①获取连接 Connection con = DriverManager.getConnection() ②开启事务con.setAutoCommit(true/false); ③执行CRUD ④提交事务/回滚事务 con.commit() / con.rollback(); ⑤关闭连接 conn.close(); 嵌套事务:在spring中将事务级别设置为RROPAGATION_REQUIRES_NEW
11.SQL的整个解析、执行过程原理、SQL行转列;
1.①第一步:客户端把语句发给服务器端执行 ②第二步:语句解析 1)查询高速缓存(library cache) 2)语句合法性检查(data dict cache)
3)语言含义检查(data dict cache)
4)获得对象解析锁(control structer)
5)数据访问权限的核对(data dict cache)
6)确定最佳执行计划 ③第三步:绑定变量赋值④第四步:语句执行⑤第五步:提取数据
2.脚本:
Select st.stuid, st.stunm,
MAX(CASE c.coursenm WHEN '大学语文' THEN s.scores ELSE 0 END ) '大学语文',
MAX(CASE c.coursenm WHEN '新视野英语' THEN ifnull(s.scores,0) ELSE 0 END ) '新视野英语',
MAX(CASE c.coursenm WHEN '离散数学' THEN ifnull(s.scores,0) ELSE 0 END ) '离散数学',
MAX(CASE c.coursenm WHEN '概率论与数理统计' THEN ifnull(s.scores,0) ELSE 0 END ) '概率论与数理统计',
MAX(CASE c.coursenm WHEN '线性代数' THEN ifnull(s.scores,0) ELSE 0 END ) '线性代数',
MAX(CASE c.coursenm WHEN '高等数学(一)' THEN ifnull(s.scores,0) ELSE 0 END ) '高等数学(一)',
MAX(CASE c.coursenm WHEN '高等数学(二)' THEN ifnull(s.scores,0) ELSE 0 END ) '高等数学(二)'
From student st
Left Join score s On st.stuid = s.stuid
Left Join courses c On c.courseno = s.courseno
Group by st.stuid
参考链接:https://blog.csdn.net/wulantian/article/details/52687640
八、Redis
Redis为什么这么快?redis采用多线程会有哪些问题?
1.①完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1); ②数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的; ③采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗; ④使用多路I/O复用模型,非阻塞IO; ⑤使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求; 2.多线程处理可能涉及到锁
多线程处理会涉及到线程切换而消耗CPU
Redis支持哪几种数据结构;
Redis目前支持5种数据类型,分别是:
String(字符串)
List(列表)
Hash(字典)
Set(集合)
Sorted Set(有序集合)
Redis跳跃表的问题;
跳跃表:是一种支持平均O(log(n)),最坏O(N)复杂度的节点查找,可以通过顺序性操作来处理节点的有序数据结构。 跳跃表本身呢是有序链表的一种形式,但是在有序链表中选取了一些关键节点,
添加节点: 当要添加进一个节点的时候节点按照查找的顺序到最底层加入链表中,然后对于新加入的节点我们要判断是否将其提拔为关键节点(也就是是否让其上升),这个时候应用的策略是抛硬币,也就是每次节点都有百分之50的几率上升到上一层。 删除节点: 逐层查找到第一次出现节点的索引,然后逐层找到每一层的对应的节点 删除每一层查找到的节点,如果该层只剩下一个节点就删除整个一层。O(log(N)) 跳跃表的用途 1. 实现有序集合键 2. 集群节点用作内部的数据结构
Redis单进程单线程的Redis如何能够高并发?
单线程程序绝对可以通过使用 i/o 复用机制和事件循环 (Redis用的是事件循环), 在 i/o 级别上提供并发性。
并行性具有成本: 在现代硬件上可以找到多个套接字/多个内核, 线程之间的同步非常昂贵。另一方面, 像 Redis 这样的高效存储引擎的瓶颈往往是网络--CPU前面的陷阱。因此, 孤立的事件循环 (不需要同步) 被视为构建高效、可伸缩的服务器的良好设计。
Redis如何使用Redis实现分布式锁?
Redis分布式锁的代码实现
(1)为避免特殊原因导致锁无法释放,在加锁成功后,锁会被赋予一个生存时间(通过lock方法的参数设置或者使用默认值),超出生存时间锁会被自动释放锁的生存时间默认比较短(秒级),因此,若需要长时间加锁,可以通过expire方法延长锁的生存时间为适当时间,比如在循环内。
(2)系统级的锁当进程无论何种原因时出现crash时,操作系统会自己回收锁,所以不会出现资源丢失,但分布式锁不用,若一次性设置很长时间,一旦由于各种原因出现进程crash 或者其他异常导致unlock未被调用时,则该锁在剩下的时间就会变成垃圾锁,导致其他进程或者进程重启后无法进入加锁区域。
实现原理
-
互斥性
-
保证同一时间只有一个客户端可以拿到锁,也就是可以对共享资源进行操作
-
-
安全性
-
只有加锁的服务才能有解锁权限,也就是不能让a加的锁,bcd都可以解锁,如果都能解锁那分布式锁就没啥意义了
-
可能出现的情况就是a去查询发现持有锁,就在准备解锁,这时候忽然a持有的锁过期了,然后b去获得锁,因为a锁过期,b拿到锁,这时候a继续执行第二步进行解锁如果不加校验,就将b持有的锁就给删除了
-
-
避免死锁
-
出现死锁就会导致后续的任何服务都拿不到锁,不能再对共享资源进行任何操作了
-
-
保证加锁与解锁操作是原子性操作
- 这个其实属于是实现分布式锁的问题,假设a用redis实现分布式锁
-
假设加锁操作,操作步骤分为两步:
-
1,设置key set(key,value)2,给key设置过期时间
-
假设现在a刚实现set后,程序崩了就导致了没给key设置过期时间就导致key一直存在就发生了死锁
Redis分布式锁操作的原子性,Redis内部是如何实现的?