Redis数据淘汰策略
Redis提供了8种不同的数据淘汰策略,默认是noeviction不删除任务数据,内存不足直接报错
LRU:最少最近使用,用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高
LFU:最少频率使用,会统计每个key的访问频率,值越小淘汰优先级就越高
Redis分布式锁,是如何实现的呢?
Redis实现分布式锁主要利用Redis的setnx,setnx是SET If not exists(如果不存在则SET)
or 通过Redisson实现分布式锁,底层是setnx和lura脚本(保证原子性)
获取锁
# 添加锁,NX是互斥,EX是设置超时时间
SET lock value NX EX 10
释放锁
# 释放锁,删除即可
DEL key
Redisson实现分布式锁如何合理的控制锁的有效t时长?
在Redisson分布式锁中,提供了WatchDog(看门狗),一个锁获取锁成功以后,WatchDog会给持有锁的线程续期(默认是每隔10秒续期一次)
Redisson这个锁可以重入吗?
可以重入,多个锁重入需要判断是否是当前线程,在Redis进行存储的时候使用Hash结构,来存储线程信息和重入次数
Redisson锁能解决主从数据一致的问题吗?
不能解决,但是可以使用Redisson提供红锁来解决,但是这样的话,性能就太低了,如果业主中非要保证业务的强一致性,可以采用Zookeerper实现分布式锁。
介绍一下Redis的住从同步
单点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离,一般都是一主多从,主节点负责写数据,从节点负责读数据
能说一下,主从同步数据的流程吗?
全量同步:
从主节点请求主节点同步数据(replication id、offset)
主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id 和 offset)
主节点执行bgsave,生成rdp文件后,发送给从节点去执行
在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
把生成之后的命令日志文件发送给从节点进行同步
增量同步:
从节点请求主节点同步数据,主节点判断是不是第一次请求,不是第一次就获取从节点的offset值
主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
怎么保证Redis高并发高可用?
哨兵模式:实现主从集群的自动故障修复(监控、自动故障修复、通知)
你们使用Redis单点还是集群?哪种集群?
主从(1主1从)+哨兵模式就可以了,单节点不超过10G内存,如果Redis内存不足则可以给不同的服务分配独立的Redis主从节点
Redis集群脑裂,该怎么解决呢?
集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master, 就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新的master同步数据,就会导致数据丢失。
解决:
我们可以修改Redis配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
Redis是单线程的,单是为什么还那么快?
Redis是纯内存操作,执行速度非常快
采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全的问题
使用I/O多路复用模型,非阻塞IO
能解释一下I/O多路复用模型吗?
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求
用户空间和内核空间
常见的IO模型
阻塞IO(Blocking IO)
非阻塞IO (Nonblocking IO)
IO多路复用(IO Multiplexing)
Redis网络模型
1、I/O多路复用
是指利用单个线程同时监听多个Socket,并在某个Socket可读,可写时得到通知,从而避免无效的等待,充分利用CPU资源,目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能
2、Redis网络模型
就是使用I/O多路复用的结合事件的处理器来应对多个Socket请求
连接应答处理器
命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
如何定位慢查询?
我们系统中当时采用了运维工具(Skywalking),可以监测出哪个接口,最终是因为是sql的问题
在mysql中开启了慢日志查询,我们设置的值就是2秒,一旦sql执行超过2秒就会记录到日志中(调试阶段)在我们调试阶段才会开启慢日志查询的功能
那这条SQL语句执行很慢,如何分析呢?
聚合查询(新增临时表解决)
多表查询(试着优化SQL结构)
表数据量过大查询(添加索引、分析SQL语句)
深度分页查询(覆盖索引.....TODO)
理解Explain的列参数
possible_keys 当前sql可能会使用到的索引
key 当前sql实际命中的索引
key_len 索引占用的大小
Extra 额外的优化建议
Using where; Using Index 查找使用了索引,需要的数据在索引列中能找到,不需要回表查询数据
Using index condition 查找使用了索引,但是需要回表查询数据
type 这条sql的连接类型,性能由好到差为Null、System、const、eq_ref、ref、range、index、all
system 查询系统中的表
const 根据主键查询
eq_ref 主键索引查询或者唯一索引查询
ref 索引查询
range 范围查询
index 索引树扫描
all 全盘扫描
这条SQL语句执行的很慢,如何分析优化呢?
可以采用MySQL自带的分析工具 EXPLAIN
通过key和key_len检查是否命中了索引(索引本身存在是否有失效的情况)
通过type字段查看sql是否进一步的优化空间,是否存在全索引扫描或全盘扫描
通过extra建议判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回的字段来修复
了解过索引吗?(什么是索引)
索引(index)是帮助Mysql高效的获取数据的数据结构(有序)
提高数据检索的效率,降低数据库的IO成本(不走全表扫描)
通过索引对数据进行排序,降低数据库的排序成本,降低了CPU的消耗
索引底层的数据机构了解过吗?
Mysql的innoDB引擎采用的B+树的数据结构来存储索引
阶数更多,路径更短
磁盘读写代价B+树更低,非叶子节点只存储指针,叶子阶段存储数据
B+树便于扫库和区间查询,叶子节点是一个双向链表
什么是聚簇索引什么是非聚簇索引?
聚簇索引(聚集索引):数据与索引放到一块,B+树的叶子节点保存了整行数据,有且只有一个
非聚簇索引(二级索引):数据与索引分开存储,B+树的叶子节点保存对应主键,可以有多个(单独给字段创建的索引,大部分都是二级索引)
知道什么是回表查询吗?
通过二级索引找到对应的主键值,到聚集索引中查找整行数据,这个过程就是回表
知道什么是覆盖索引吗?
覆盖索引是指查询使用了索引,返回的列,必须在索引中全部能够找到
使用ID查询,直接走到聚集索引查询,一次索引扫描,直接返回数据,性能高。
如果返回的列中没有创建索引,有可能会触发回表查询,尽量避免使用select *
Mysql超大分页怎么处理?
解决方案:覆盖索引+子查询
select * from tb_sku t,
(select id from tb_sku order by id limit 9000000,10) a
where t.id = a.id;
索引创建原则有哪些?
针对数据量较大,且查询比较频繁的表建立索引。(单表超过10万数据(增加用户体验))
针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度较高,使用索引效率高。
如果是字符串类型的字段,字段的长度比较长,可以针对字段的特点,建立前缀索引。
尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
要控制索引的数量,索引不是多多益善,索引越多,维护索引结构代价就越大,会影响增删改的效率。
什么情况下索引会失效?
违反最左前缀法则(在使用复合索引时,跳过某一列就会导致索引失效)
范围查询右边的列,不能使用索引
不要在索引上进行运算操作,不然会失效
字符串不加单引号,会导致索引失效
以%开头like模糊查询,都会导致索引失效
谈谈你对Sql的优化经验
表的设计优化,数据类型的选择
索引优化,索引创建原则
sql语句优化,避免索引失效,避免使用Select *
主从复制,读写分离,不让数据的写入,影响读的操作
分库分表
ACID是什么?可以详细说一下吗?(事务的特性是什么?可以说说吗?)
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
一致性(Consistency):事务完成时,必须使所有的数据保持一致的状态。
隔离性(Isolation):数据库系统提供的隔离机制,保证事务不在受外部并发操作影响的独立环境下运行。
持久性(Durability):事务的一旦提交或回滚,它对数据库中的数据的改变就是永久的。
并发事务带来哪些问题?怎么解决这些问题呢?Mysql的默认隔离级别是?
并发事务的问题:
脏读:一个事务读到另外一个事务还没提交的数据。
不可重复读:一个事务先读取同一条记录,但两次数据读取不同。
幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻觉”。
隔离级别:
READ UNCOMMITTED 未提交读
READ COMMITTED 读已提交
REPEATABLE READ 可重复读
SERIALIZABLE 串行化
undo log和redo log的区别
redo log:记录的是数据页的物理变化,服务宕机可用来同步数据
undo log:记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据
redo log保证了事务的持久性,undo log保证了事务的原子性和一致性
事务的隔离性是怎么实现的?(解释一下MVCC)
MVCC是Mysql中的多版本控制,指维护一个数据的多个版本,使得读写操作没有冲突
隐藏字段
trx_id(事务ID),记录每一次的操作的事务ID,是自增的
roll_pointer(回滚指针),指向上一个版本的版本记录地址
row_id(隐藏主键)如果表结构没有指定主键,会生成一个隐藏的主键
undo log
回滚日志,存储老版本的日志
版本链:多个事务并行操作某一记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表
readView解决的是一个事务查询选择版本的问题
根据readView的匹配规则和当前的一些事务ID判断访问哪个版本的数据
不同的隔离级别快照读是不一样的,最终的访问结果不一样
RC:每一次执行快照读生成ReadView
RR:仅在事务中第一次执行快照读时生成ReadView,后续复用
主从同步的原理
Mysql主从复制的核心是二进制日志binglog(DDL (数据定义语言)语句和DML(数据操纵语言)语句)
主库在事务提交时,会把数据变更记录在二进制日志文件binlog中。
从库读取主库的二进制文件Binlog,写入到从库的中继日志Relay Log。
从库重做中继日志中的事件,将改变反映它自己的数据。
你们项目中用过分库分表吗?
水平分库,将一个库的数据拆分到多个库中,解决海量数据存储和高并发的问题
水平分表,解决单表存储和性能的问题
垂直分库,根据业务进行拆分,高并发下提高磁盘IO和网络连接数
垂直分表,冷热数据分离,多表互不影响
Spring框架中得单例bean是线程安全的吗?
不是线程安全的
Spring框架中有一个@Scope注解,默认的值是Sinleton,单例的。
因为一般在Spring bean中都是注入的无状态对象,没有线程安全问题,如果在bean中定义了可修改的成员变量,是要考虑线程安全问题的,可以使用多例或者加锁来解决。
什么是AOP?
面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低耦合
你们项目中有没有用到AOP?
记录操作日志、缓存、spring实现的事务
核心是:使用aop的环绕通知+切点表达式(找到要记录日志的方法),通过环绕通知的参数获取请求方法的参数(类、方法、注解、请求方式等),获取到这些参数以后,保存到数据库
Spring事务是如何实现的?
基本是通过AOP的功能,对方法前后进行拦截,在方法执行之前开启事务,在执行目标方法之后根据执行情况提交或者回滚事务。
Spring中事务失效的场景有哪些?
异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出
抛出检查异常,配置RollbackFor属性为Exception
非public方法导致的事务失效,改为public
Spring的Beam的生命周期
通过BeanDefinition获取bean的定义信息
调用构造函数实例化bean
bean的依赖注入
处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
Bean的后置处理器BeanPostProcessor-前置
初始化方法(initializingBean、init-method)
Bean的后置处理器BeanPostProcessor-后置
销毁Bean
Spring中的循环依赖/引用
循环依赖:循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环,比如A依赖于B,B依赖于A
循环依赖在Spring中是允许存在的,Spring框架依据三级缓存解决了大部分循环依赖的问题
一级缓存(singletonObjects):单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
二级缓存(earlySingletonObjects):缓存早期的bean对象(生命周期还没走完)
三级缓存(singletonFactories):缓存的是ObjectFactory,表示对象工厂,用来创建某个对象
构造方法出现了循环依赖怎么解决呢?
A依赖于B,B依赖于A,注入的方式是构造函数
原因:由于Bean的生命周期中构造函数是第一个执行的,Spring框架并不能解决构造函数的依赖注入
解决方案:实用@Lazy进行懒加载,什么时候需要对象再进行bean对象的创建
Spring MVC的执行流程知道吗?
JSP视图版本:
用户发送请求到前端控制器DispatcherServelet
DispatcherServelet收到请求后调用HandlerMapping(处理器映射器)
HandlerMapping找到具体处理器,生成处理对象及处理拦截器(如果有),再一次返回给DispacherServelet
DispatcherServelet调用HandlerAdaper(处理器适配器)
HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
Controller执行完成返回ModelAndView对象
HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServelet
DispatcherServelet将ModelAndView传给ViewReslover(视图解析器)
ViewReslover解析后返回具体View(视图)
DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
DispatcherServlet响应用户
前后端开发:
用户发送请求到前端控制器DispatcherServelet
DispatcherServelet收到请求后调用HandlerMapping(处理器映射器)
HandlerMapping找到具体处理器,生成处理对象及处理拦截器(如果有),再一次返回给DispacherServelet
DispatcherServelet调用HandlerAdaper(处理器适配器)
HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
方法上添加了@ReponseBody
通过HttpMessageConverter来返回结果转换为JSON响应
Spring boot自动配置原理
1、在Spring boot项目中引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
2、其中@EnableAutoConfiguration是实现自动化配置的核心注解,该注解通过@Import注解导入对应的配置选择器。
内部就是读取了该项目和该项目引用的Jar包的classpath路径下的META-INF/spring.factories文件中的所配置的类的全类名,在这些配置类中定义的Bean会根据条件注解所指定的条件来判定是否需要将其引入到Spring容器中。
3、条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有Bean放入Spring容器中使用。
Spring框架常见注解(Spring、Spring Boot、Spring MVC)
Spring常见注解:
@Component、@Controller、@Service、@Repository 使用在类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合@Autowired一起使用用于根据名称依赖注入
@Scope 标注Bean的作用范围
@Cofiguration 指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定Spring容器时要扫描的包
@Bean 使用该方法上,标注将该方法的返回值存储到Spring容器中
@Import 使用@Import导入类会被Spring加载到IOC容器中
@Aspect、@Before、@After、@Around、@Pointcut 用于切面编程(AOP)
Spring MVC 常见注解:
@RequestMapping 用于映射请求路径,可以定义在类上和方法上,用于类上,则表示类中的所有的方法都是以该地址作为父路径
@RequestBody 注解实现接收http请求的json数据,将JSON转为Java对象
@RequestParam 指定请求参数的名称
@PathViriable 从请求路径下中获取请求参数/usr/{id},传递给方法的形式参数
@ResponseBody 注解实现将Controller方法返回对象转化为JSON对象响应给客户端
@RequestHeader 获取指定的请求头数据
@RestController @Controller + @ResponseBody
Spring Boot 常见注解:
@SpringBootConfiguration 组合了 - @Configuration;注解,实现配置文件功能
@EnableAutoConfiguration 打开自动配置的功能
@ComponentScan Spring组件扫描
Mybatis 执行流程
加载Mybatis配置文件:mybatis-config.xml加载运行环境和映射文件
构造会话工厂SqlSessionFactory
会话工厂创建SqlSession对象(包含执行SQL语句的所有方法)
操作数据库的接口,Executor执行器,同时负责查询缓存的维护
Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
输入参数映射(把Java类型转换为数据库支持的类型)
输出结果映射 (从数据库类型转换为Java的类型)
Mybatis是否支持延迟加载?
延迟加载的意思是:就是需要用到数据时才进行加载,不需要用数据时就不加载数据
Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载
在Mybatis配置中,可以配置是否启用延迟加载LazyLoadingEnabled=true/false,默认是关闭的
延迟加载的底层原理知道吗?
使用CGLIB创建目标对象的代理对象
当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,执行sql查询
获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了
Mybatis的一级、二级缓存用过吗?
一级缓存:基于PerpetualCache 的 HashMap 本地缓存,其存储作用域为Session,当Session进行flush或close之后,
二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于Sql session,默认也是采用PerpetualCache,HashMap存储。需要单独开启,一个是核心配置,一个是Mapper映射文件。
Mybatis的二级缓存什么时候会清理缓存中的数据
当某一个作用域(一级缓存 Session/二级缓存 namespace)的进行了新增、修改、删除操作后,默认该作用域下所有select中的缓存被clear。
Spring Cloud 5大组件有哪些?
说下Nacos和eureka的区别?
Nacos和eureka的共同点(注册中心)
都支持服务注册和服务拉取
都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别(注册中心)
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
Nacos集群默认采用AP的方式,当集群中存在非临时实例时,采用CP模式,Eureka采用AP方式
Nacos还支持了配置中心,eureka则只有注册中心,也是选择使用nacos的一个重要原因
Ribbon负载均衡策略有哪些?
RoundRobinRule:简单轮询服务列表来选择服务器
WeightedReponseTime:按照权重重新来选择服务器,响应时间越长,权重越小
RadomRule:随机选择一个可用的服务器
BestAvailableRule:忽哟那些短路的服务器,并选择并发数较低的服务器
RetryRule:重试机制的选择逻辑
AvailabilityFilteringRule:可用性敏感策略,先过滤非健康的,再选择连接数较小的实例
ZoneAvoidanceRule(Ribbon默认策略):以区域可用的服务器为基础进行服务器的选择,使用Zone对服务器进行分类,这个Zone可以理解为一个机房,一个机架等,而后再对Zone内的多个服务做轮询
如何自定义负载均衡策略?如何实现?
提供了两种方式:
创建类实现IRule接口,可以指定负载均衡策略(全局)
在客户端的配置文件中,可以配置某一个服务调用的负载均衡策略(局部)
什么是服务雪崩?怎么解决这个问题?
服务雪崩:一个服务失败,导致整条链路的服务都失败的情形
服务降级:服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃,一般实际开发中与Feign接口整合,编写降级逻辑。
服务熔断:默认关闭,需要手动打开,如果检测到10秒内请求的失败率超过百分之50,就触发熔断降级,之后每隔5秒重新尝试请求微服务,如果服务不能响应,继续走熔断机制,如果服务可达,则关闭熔断机制,恢复正常请求