
JAVA 基础八股文问java中序列化是怎么实现的呢1.实现Serializable接口就会实现数据序列化的效果。2.调用json做序列化。就比如Jacksonfastjson等等3.实现Enternalizable接口就可以实现反序列化的效果。问:java的流有哪些呢从方向方面主要就是输入流和输出流。从单位方面主要就是分为字节流和字符流。字节流主要就是InputStreamOutputStream。字符流主要就是ReaderWriter。问抽象类和接口有什么区别呢从方法编写方面抽象类中可以抽象方法和普通方法而接口中只能编写抽象方法。从继承和实现方面抽象方法只能继承一个类并且可以实现多个接口而接口可以继承多个接口。在变量的定义方面接口只能定义静态变量抽象类可以定义普通变量和静态变量。问final关键值有了解过吗在修饰方法的时候说明该方法无法被重写。在修饰类的时候说明该类无法被继承。在修饰属性的时候说明该变量从创建到销毁过程中不会改变。在修饰形参的时候说明该形参的引用在方法执行前后都会不发生改变。问异常类有哪些异常主要就是ExceptionError。Exception主要就是运行时异常和检查时异常为了保证代码恶的正常运行我们需要主动使用try/catch进行捕获常见的就是IoEceptionSQLExceptinon。Error主要就是就是发生在运行时的异常就比如oomthreadDeath。问Filter和Interceptor有说明区别哪个先执行呢在作用域方面Filter作用域每个请求而interceptor主要就是对Controller方法的请求起作用。在执行时机方面filter会在请求加入dispatchServlet前和返回给客户端前执行而interceptor会在执行Controller方法的时候和进行视图渲染前执行。Filter会像执行而interceptor随后执行。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题需要全套面试笔记【点击此处即可】免费获取问java中共享变量有哪些1.静态变量。2.使用Voliate修饰java中得变量就会变成共享变量。问编写sigleton函数?单例模式饿汉式public class sigleton {//饿汉式,每次都会创建好,即使没有调用private final static Test test new Test();public Test getInstance() {return test;}}懒汉式public class sigleton {//懒汉式只有在被第一次调用时才会被创建private static Test test;public Test getInstance() {return test null ? new Test() : test;}}Redis八股文问Redis数据结构的底层实现由了解过吗String类型的底层使用动态字符串实现的。List类型的底层使用双向链表压缩数组实现的。set类型的底层使用哈希表数组整数数组实现的。hash类型的底层使用压缩数组哈希表实现的。zset类型的底层使用压缩数组跳表实现的。问zset的底层结构有了解过吗zset的底层结构主要就是跳表压缩列表。压缩列表本质就是一个数组在其中记录 列表的长度尾部得偏移量列表的个数。跳表就是基于二分查找的思想对链表创建多级索引就是基于每隔一个节点做索引以此类推创建索引的层数就是 logn 1。整个查找过程就是在多级索引间跳来跳去。问我看你在做项目的时候都使用到redis你在最近的项目中哪些场景下使用redis呢?缓存和分布式 锁都有使用到。问说说在缓存方面使用1.在我最写的物流项目中就使用redis作为缓存当然在业务中还是比较复杂的。2.在物流信息查询模块使用中使用二级缓存一级缓存使用的是Caffeine二级缓存就是使用redis。问对缓存击穿缓存雪崩缓存穿透有了解过吗说说者三个缓存问题的解决方案吧。1.缓存击穿某个热点key设置了过期时间在高并发查询的情况下该热点key过期了导致大量的请求去访问数据库最终压垮数据库。解决方案1不给热点key设置过期时间在redis中设置的过期时间都是逻辑过期时间通过逻辑字段来判断key是否过期。2使用分布式锁保证每次只有一个请求去访问数据库在每次访问数据库前再做一次查询缓存的操作然后获取锁并去数据库查询将查到的数据重新缓存起来下一次请求就会在缓存中查询到数据就不会查询数据库防止压垮数据库。2.缓存雪崩第一种情况:大量的key设置了相同的过期时间在同一大量的key失效导致大量的请求去访问数据库导致压垮数据库。第二种情况:redis宕机。解决方案:(1) 错开key的过期时间TTL在每个key的统一过期时间上在随机加上1~5分钟的过期时间。(2)设置服务降级服务熔断服务限流到达阈值的时候直接返回自定义的错误信息。作为系统的兜底策略(3)为redis搭建集群,就包括哨兵模式集群模式。(4)做二级缓存。可以重点介绍就引导面试官到运单信息查询模块给他介绍二级缓存的实现还有缓存同步的问题主要讲caffeine缓存同步的问题3.缓存穿透:查询的key在缓存和数据库中都不存在每次都会进入数据库查询当大量的这种key访问数据库就会导致缓存穿透。这种情况多半上是恶意攻击解决方案:(1)在缓存中储存空值当在数据库中没有查到数据时将key关联null的键值对存储到缓存中后续就会走缓存这种解决方案缺点很明显就是存储大量无用的存储浪费空间。(2)使用布隆过滤器在查询缓存的时候先去布隆过滤器中查询是否存在缓存再去查询缓存。问具体讲讲你对这个布隆过滤器理解布隆过滤器类似hash表叫做bitmap(位图)在没有位置存放的是二进制的数据1表示存在0表示不存在。通过哈希函数计算储存位置。在后续查询缓存前先去布隆过滤器中查询数据是否存在如果存在才去查缓存如果不存在就直接返回空。布隆过滤器的优点就是储存二进制的数据而非真实的数据查询速度快。缺点判断数据是否存在有误判率不能做删除操作。为了降低布隆过滤器的误判率因为两个数据的hashCode可能相同所以我们可以设置多哈希函数操作较低过滤器的误判率。我们主要是根据redisson设置布隆过滤器可以设置其误判率。在高并发的场景下一般误判率控制在5%之内就可以了。问说说分布式锁方面的使用在项目的支付模块中的扫码支付为了保证交易单的状态不会被除当前业务的其他业务修改计算运力我们都会使用分布式锁。问你能简单的讲述一下分布式锁的实现吗引导面试官到你的项目中去解释分布式锁可以跟面试官说说优惠劵超卖的问题。在redisson中的获取锁方法底层主要是通过Lua能够调用redis命令保证命令执行的原子性实现的如果获取锁方法没有设置过期时间则分布式锁会有watch dog来保证延长锁的有效时间。还可以通过setnx来实现分布式锁(因为redis是单线程的)。 通过 set If not exists并设置过期时间实现分布式锁但是存在的问题就是无法确定要给锁设置多长的有效时间所以在项目中比较少使用。公平锁 (我们项目中使用的分布式锁保证可重入性)公平锁主要还是基于redisson实现的。锁的结构采用的是hash结构以大小key的形式在我业务中使用订单id和交易单id进行拼接作为大key使用当前的线程id作为小key,vlaue存放的就是上锁的次数。在业务修改交易单状态是需要先判断当前交易单是否被上锁通过订单id交易单id进程id来获取对应的锁如果被上锁则当前线程的其他业务不能进行操作。保证订单状态的准确性。(判断幂等性) 保证可重入性根据当前线程id来获取分布式锁解决死锁的情况分布式锁的底层实现。问分布式锁可以实现重入吗重入的意思就是在同一个线程中获取锁后继续获取锁。因为我们分布式锁中小key使用的是线程idvalue就是上锁的次数我们每次进行重入时就是value值1在释放锁的时候就将对应的value-1在value为0的时候就删除对应的信息。其他微服务就可以获取此分布式锁了。通过线程id的不同来保证分布式锁的互斥。蓝字选答问redisson实现的分布式锁可以解决主从一致性的问题吗不能解决主从一致的问题单master 节点获取锁后就没释放锁就宕机了此时slave节点变成了master节点因为新的master没有上锁所以新的master会进行上锁破坏了锁的互斥性。为了解决这个问题我们可以使用红锁也就是给一般以上的节点添加分布式锁但是这样效率就变的很低为了提高效率建议采用zookeeper实现分布式锁。听说过zookeeper实现分布式锁问在集群的项目中为什么不能用关键字synchronized呢在两个微服务中如果使用synchronized是无法达到同步上锁效果因为两个微服务是两个单独的JVM。问了解过双写一致性吗双写一致性当数据库中的数据发生修改的时候我们需要修改缓存中的数据保证数据库和缓存中的信息相同。在读操作的时候会先到缓存中查询数据如果没有命中的话就到数据库中查询。在写操作的时候会采用延迟双删。问在延迟双删中为什么要延迟删除缓存数据库采用的是主从模式遵循读写分离需要一些时间来将主数据库中的数据同步到从数据库但是还是可能出现脏读的情况。问在延迟双删中为什么要删除两次缓存呢?如果只有一次删除缓存操作的话就会有两种情况。1.情况1先删除缓存再修改数据库。线程1删除缓存。在线程1要修改数据库前。线程2去做查询操作发现没有缓存就去数据库中查询数据并做缓存之后线程1修改了数据库中的信息最终出现缓存数据和数据库数据不相同的情况。当前线程在删除缓存并且在写数据库前其他线程查询了就的数据2.情况2先修改数据再删除缓存。当前缓存中方没有数据线程1做查询操作准备将旧数据缓存前线程2做了修改数据和删除缓存的操作(此时是没有缓存的)最终缓存中就存储了旧的数据出现脏读的情况。当前线程要将旧的数据进行缓存的过程中进行了写数据库和删除缓存的操作为了防脏读的缓存被使用所以在数据同步的需要两次删除缓存的操作。问延迟双删是没法保证强一致性的有什么强一致性的方法吗方法一使用分布式锁每次只有一个线程进行操作效率比较低下。方法二因为缓存中的数据大多是读多写少所以我们可以使用在读取数据的时候使用共享锁 多个线程同时可以读取缓存但是其他线程不可以写在修改数据的时候使用排他锁会阻塞其他线程的读写操作。排他锁和共享锁可以使用redisson实现通过redissonClient获取对应的读写锁。1.读锁也就是共享锁。2.写锁也就是排他锁。必须保证读写锁的名字相同。问那有了解过最终一致性的方法吗在我们支付模块中每次支付都需要获取支付模板也就是支付宝支付和微信支付模板这些数据我们基本上不会修改会将其缓存起来在修改mysql中模板的数据的时候通过rabbitmq发送消息异步修改缓存的数据达到最终一致的效果。为什么mq可以实现最终一致的效果呢mq中的消息都是按照顺序进行消费的消息的消费是和事务绑定的如果事务进行了回滚操作则被消费的消息也会被重新放回队列的原先位置中并且接收到消息的服务会异步进行处理。当然我们不仅仅可以使用rabbitmq实现最终一致的效果我们还可以使用canal实现最终一致的效果。canal主要是通过mysql的主从同步实现的。通过监听bin Log日志的方式来修改缓存中的信息达到最终一致的效果。binLog日志主要是储存DDL(数据定义语句)和DML(数据操纵语句)问redis做为缓存数据的持久化是怎么做的呢在持久化上用 RDB和AOF两种方式。问:说说你对RDB和AOF的理解吧。1.RDB通过对数据做快照的方式做持久化将快照存放到磁盘上后续需要恢复数据的时间就使用该快照进行恢复。RDB的执行原理在主进程会有一个页表文件(用于映射数据在物理内存上的数据)通过复制该页表到子进程中子进程通过页表找到数据并做快照。当是如果在修改数据的过程中做RDB就会出现脏读的情况RDB通过设置数据为只读在修改数据的时候复制一份相同的数据进行修改如何修改页表的映射最终解决脏读的问题。2.AOF在redis做操写的指令的时候会将这些指令都存储到对应的AOF文件中在后续数据需要恢复的时候就执行AOF文件中的所有指令。AOF执行的原理就是每次去记录操作写的指令到AOF文件中。问RDB和AOF有什么区别吗?1.RDB是对整个内存做快照而AOF是记录redis每一条执行的语句。2.RDB的在两次备份间可能会出现数据丢失的情况(redis宕机)完整性低 而AOF的完整性比较高其取决去刷盘的策略。3.RDB的文件比较小而AOF会记录每日一条指令所以AOF的文件比较大。4.RDB的数据恢复比较快因为AOF文件比较大需要一条条的执行指令恢复速度慢。5.RDB系统占用高需要大量的CPU和内存的消耗而AOF系统占用比较小只要是文件的IO操作但是在AOF文件重写的时候需要大量的CPU和内存的消耗。问RDB和AOF谁的恢复速度更快我们在平时要怎么选择呢?RDB的快照文件本质是二进制文件其体积比较小而AOF文件需要保存redis中的写操作指令在体积上比较大所以在恢复速度上RDb比较快但是RDB存在数据丢失的风险在我的项目中只要主要还是使用AOF像支付方式模板这种比较重要的数据我们应当降低其丢失的风险在刷盘策略上使用每秒进行一次刷盘也就是每秒批量写入一次。问redis的key过期后会立刻做删除操作吗?redis有两种数据过期策略分别是惰性删除和定期删除。惰性删除我们会为每个key设置一个过期时间在每次获取数据的时候会先去判断该key是否过期如果过期直接删除key如果没有过期则直接返回也就是说在没有使用数据时不会主动删除。优点对CPU友好只在查询时才做过期判断。缺点内存消耗大。定期删除定期去判断一定数量的key是否过期如果过期则直接进行删除操作定期删除又分为SLOW模式和FAST模式。优点内存消耗小。缺点定期查询key需要消耗大量时间。SLOW模式默认的频率为10hz一秒内进行十次每次不大于25ms我们可以通过配置文件中的hz修改频率。FAST模式执行频率是不固定的两次删除的间隔不小于2ms每次耗时小于1ms。redis默认采用的数据过期删除策略惰性删除定期删除配合使用。问假如缓存过多内存有限内存满了怎么办呢?在redis中提供了八种数据淘汰的策略那默认的处理就是noeviction就是直接报错。我们可以提供配置文件修改对应的淘汰策略。策略中有两种重要的思想分别是LRU和LFU。LRU:就是将当前使用间距最长的数据进行淘汰。对应两个LFU:就是将当前使用频率最少的数据进行淘汰。对应两个随机淘汰策略。八个策略的不同就在于key的类型(全部key和设置过期时间的key)和使用的思想不同(LRU和LFU)。在我的项目中主要使用的淘汰策略就是: allkey-lru,淘汰掉当前使用最少的数据。问数据库中有100w条数据redis只能存储20w数据如何保证redis中的数据是热点数据呢?使用 allkey-lru策略保证经常使用的数据不被淘汰。问redis缓存的空间用完了会怎么样如果是默认情况下的话会直接报错因为默认的淘汰策略就是noeviction如果设置了其他的策略则会对数据进行淘汰。问能介绍一下redis的主从复制和主从复制的流程吗?单个redis节点的并发能力是有限的所以为了提高并发能力我们需要搭建redis集群就比如主从复制。主从复制的流程主从复制主要分为全量同步和增量同步。全量同步在salve请求数据同步的时候会携带application Id和offset如果master判断出applid和自己的不一样就认为slave是第一次进行同步所以会进行全量同步。 master会执行bgsave生成RDB文件给slaveslave进行同步在此过程中master可能会进行新的指令master会将这些指令存储到日志文件在加载RDB完后再将日志文件传给slave进行最终的同步master同步applid和offset给slave。增量同步 在master判断出slave中的applid和自己一样就认为不是第一次同步直接进行增量同步从日志文件中获取到offset的位置将offset之后的数据发送给slave进行数据的同步。问那主节点宕机了又该怎么办呢为了提高redis集群的高可用我们可以使用哨兵模式解决主节点宕机的问题。问那说说你对哨兵模式的理解吧哨兵模式通过sentinel的心跳机制去监测master的状态当然为了保证高可用我们也需要对sentinel搭建集群。sentinel每隔1秒就会向集群的节点发送ping指令当master失效后会就选择出新的master。在sentinel中有两种概念主观下线和客观下线。主观下线当有一个sentinel发现redis节点没有返回响应就认为其为主观下线。客观下线当有一半以上的sentinel节点发现redis节点没有返回响应就认为其为客观下线。当master发生客观下线就会筛选slave作为新的master。筛洗新master的优先级为下1.判断master与slave断开的时长如果时长超过指指定值则直接排除。2.判断slave的权重如果权重越小优先级就越高。3.如果权重相同的话就比较slave的offst值也就是偏移量如果offset越大优先级就越高。4.判断slave运行id的大小如果运行id越小则优先级越高。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题需要全套面试笔记【点击此处即可】免费获取问哨兵模式可能会出现脑裂的情况有了解过吗由于网络不稳定的因素多个sentinel都没有ping到master此时master是没有宕机的而哨兵模式就会选择出新的master就出现两个master的情况而客户还是对旧的master进行数据的操作在网络稳定后旧的master就会变成新master的slave最终导致数据操作的丢失。从而形成脑裂的现象。解决脑裂的方案1.在redis的配置文件中设置最少的slave个数对应的配置项:min-replicas-to-write。当出现脑裂的情况时旧的master会监测到没有slave就不会做数据的操作直接返回错误信息防止数据操作的丢失。2.在resdis的配置文件中设置最大数据同步的延迟时间。对应的配置项:min-replicas-max-lag。当出现脑裂的情况时。旧的master在做数据的同步时一直找不到slave当时间超过最大的延迟时间就会直接返回错误信息防止数据操作的的丢失。问你们使用redis是单点还是集群我们的redis使用主从模式一主一从 哨兵模式。当然在容量不够的时候会为不同的服务配置独立的redis主从节点。问redis分片集群有什么作用1,存在多个master,这些master存储不同的数据多个master可以解决并发写的问题。2.每一个master都可以有多个slave解决并发读的问题。3.在分片集群中不再通过sentinel监测master的健康状态而是通过master之间ping状态判断各个master的健康状态最终达到哨兵模式的效果。4.客户端在访问对应的数据时会路由到对应的节点上。问:redis分集群中的数据是如何存储和读取的呢redis分片集群采用的是哈希槽的结构实现的哈希槽总共有16384个。master节点等量的获取哈希槽范围用于存储数据。在存储数据的时候通过有效部分取哈希槽总数的模计算出哈希值这里的有效部分指key前大括号中的有效值就比如: set {}key value如果没有大括号key就是有效部分找到master后做写操作。在读数据的时候通过计算出的哈希值确定存储数据的master位置从该master对应的slave中读取数据到达读写分离的效果。问redis是单线程的为什么会那么快呢?1.完全基于内存的是C语言编写的。2.采用单线程避免了不必要的上下文切换可竞争条件多线程还需要考虑线程问题。3.采用多路IO复用模型非阻塞IO模型。问说说对阻塞IO和非阻塞IO的理解吧。我们先需要知道内存的使用情况为用户空间和内核空间。用户空间只能执行受限制的指令不能直接调用系统资源需要通过内核提供的接口来调用系统资源。内核空间可以执行特权指令可以直接调用系统资源。阻塞IO:在用户线程要获取内核中获取数据而此时内核中没有数据用户线程就会等待从而导致用户线程阻塞当内核中有数据后数据需要重内核缓冲区复制到用户缓冲区在这个过程中用户线程也需要等待从而导致线程堵塞。在这两个阶段中用户线程都是堵塞的这就是堵塞IO。非堵塞IO: 用户线程要从内核中获取数据但内核中没有数据此时内核直接返回错误信息给用户线程反之线程堵塞用户线程会循环的调用内核的方法直到内核中有数据当内核中有数据后用户线程就会等待内核缓冲区中的数据复制到用户缓冲区中此时用户线程是阻塞的。在第一阶段是不阻塞的而第二阶段是堵塞的这就是非堵塞IO。比阻塞IO优化没多少而且忙等机制可能会导致CPU的空转CUP使用率暴增。问解释一下什么是多路IO复用模型单线程同时监听多个Socket操作客户端的状态某个Socket可读可写时得到通知防止出现忙等的情况提高CPU的利用率可能同时存在多个可用Socket通过循环做读取数据的操作多路IO复用主要通过epoll模式实现的将已就绪的socket存到用户空间中就不需要遍历判断socket是否就绪从而提高性能。问有了解过redis的网络模型吗通过多路IO复用 事件处理器实现的。事件处理器主要是连接应答处理器命令回复处理器命令请求处理器。在redis6.0之后使用多线程来处理命令的回复和命令的请求从而实现高效的网络请求在命令执行的时候依旧是单线程线程安全的。MySQL八股文问Mysql的存储引擎有理解过吗我比较了解就是 InnodbmyisamMemory。Innodb现在的mysql默认存储引擎就是innodb主要就是因为它是唯一一个支持事务的存储引擎支持表级锁和行级锁其索引的底层结构使用的是B树在数据索引表结构都存储到.idb中。Myisam其不支持事务仅支持表级锁其索引的底层结构为B树表结构存储到.sdi中索引存储到.myi数据存储到.myd中。Memory:基础内存进行存储的主要就是用sdi存储表结构。问如何定位慢查询在我们的项目中在上线时使用skywalking来定位慢的查询如果发现是某个SQL执行速度慢我们就可以使用skywalking的追踪功能来确定SQL语句。而在测试环境中我们使用MySQL提供的慢日志来确定慢查询的位置。mysql是默认没有开启慢日志的需要通过配置文件开启并设置快查询的最大时间超过这个时间就认为其为慢查询我们就可以在慢日志中找到慢查询的sql,在我们的项目中设置最大的时间为2秒。#开启慢日志slow_query_log1#设置快查询的最大时间long_query_time2问一个SQL语句执行很慢应该如何分析呢我们可以借助Mysql提供的关键值 explain来展示出某个SQL语句的状态。在该状态中包含属性 key和key_lenSQL中使用到的索引如果提供我们的索引出现失效的情况就可以修改和SQL和添加索引。属性type表示SQL的性能通常其值为const提供type判断是否存在全索引扫描或全盘扫描。属性extra表示建议属性提供该属性判断是否出现回表的情况。问有了解过索引吗1.索引是帮助Mysql高效查询数据的数据结构。2.索引提高检索效率大大降低IO成本。3.通过索引列对数据排序大大降低了排序的成本。问B树和B树的区别B树作将数据存放在叶子节点上非叶子节点就可以组织更宽的结构就会变的更矮更胖提高查询速度。B树中叶子节点使用双向链表进行存储的并且按顺序进行存储的不同于B树查询范围数据的时候进行多次的从根节点进行查询而B树在查询范围数据的时候只需要从根节点查询一次即可。且排序的性能更好。B树在做范围查询的时候只需要从根节点遍历一次二B树则需要遍历对应数量的个数问什么是聚簇索引和非聚簇索引聚簇索引索引结构和数据是存放在一起的也就是在B树的叶子节点中存放整行的数据。非聚簇索引也叫二级索引索引结构和数据不是存放在一起的也就是在B树的叶子节点上存放对应的主键且是不唯一的。我们为字段添加索引通常就是二级索引。问知道什么是回表操作吗通过二级索引查到的主键再去聚簇索引中查询数据行的过程就是回表。而直接查询聚簇索引则不会出现回表的情况。问有了过覆盖索引吗查询数据通过索引进行查询返回列都可以在索引的数据中找到包含在其中就是覆盖查询。使用id主键进行查询就是覆盖索引查询因为聚簇索引的数据中包含id主键性能高。在做查询的时候如果返回列吧全部存在于索引中就会回表查询所以尽量避免使用select *。问Mysql超大分页查询怎么进行优化Mysql做limit分页查询的时候需要做排序这个过程非常耗时。优化方案覆盖索引 子查询。先通过子查询出分页排序完后的id主键因为是主键所以会直接进行覆盖索引查询。通过子查询的id关联表中的id查询出分页后的数据。select * from table t(select id from table limit 0 10 order by id) swhere t.id s.id问索引的创建原则有哪些1.数据量大于十万且查询的频率表较高的表我们才会考虑创建索引。2.如果一个表需要添加索引我们应该选择作为查询字段排序字段分组字段的字段作为索引且字段的区分度要高。3.在添加索引的时候都使用复合索引来创建尽量使用覆盖查询降低回表的概率。4.如果需要对长字符串添加索引我们可以使用前缀索引。5.控制索引的数量并不是越多越快在增删改的时候我们也需要消耗时间来维护索引。问什么情况下索引会失效复合索引1.在使用复合索引的时候不遵循最左前缀法则。在做条件查询的时候跳跃某一列字段导致索引失效。2.在条件查询中的范围查询的右遍的列不能使用索引使用也会失效。如果三个都有效的话 key_len应该为六百多说明此时address字段失效3.不能在索引列进行运算操作这会导致索引失效。4.在条件查询的时候如果没有加单引号也会导致索引失效。就比如0和0会进行类型转换导致索引失效5. 在模糊查询的时候如果字符串中是以%开头的就会导致索引失效。就比如: %abc在我遇到的随影失效问题就是没有遵循最左前缀原则只要实在测试的时候通过explain查询SQL语句的执行状态来判断的。问谈谈你对SQL的优化经验在做SQL优化的时候主要从建表时使用索引时sql语句编写主从复制读写分离的方面进行考虑当数据量过大的时候考虑使用分库分表。问创建表的时候你是怎么优化的我们主要遵循阿里的开发手册就比如在使用整数类型的时候就考虑使用tinyIntIntbigInt如果是逻辑字段就使用tinyInt在使用字符串是考虑使用charvarchartext。问那在使用索引的时候如何进行优化讲出索引失效的五种情况再使用SQL的时候避免使用select *使用覆盖索引减少回表的操作。问你平时SQL语句是怎么优化的select 指明字段不要使用select * from防止回表的操作。在使用聚合查询的时候尽量使用union all而不是unionunion会多一次过滤在效率上比较低。使用inner join 而不使用 left join/right join如果必须使用的一定要以小表为驱动。问事务的特性是什么可以详细说一下这里你可以取钱的例子来引导模式官。原子性在事务中的语句要么都成功要么都失败。一致性在事务中数据的总量不会变。持久性 提交和会滚的数据都会持久化到数据库。隔离性事务中间是是相互隔离的是不会相互影响的。问并发事务带来了哪些问题并发事务可能会出现三种问题。1.脏读事务1读取到事务2未提交的数据。2.不可重复读事务2先后读取事务1中的某合个数据两次的结果不一样。3.幻读一个事务在按条件查询数据时没有查到数据吗但是插入操作时又发现该数据已经存在。因为其他事务在这个过程中插入数据选答怎么解决解决这些问题通过设置过隔离级别来解决。隔离级别包括1.读取未提交无法解决并发事务带来的问题。2.读取已提交可以解决脏读。3.可重复读可以解决脏读不可重复读。4.串行化事务只能一个一个执行可以解决脏读不可重复读幻读隔离级别最高效率最低。MySQL默认的隔离级别是什么Mysql默认使用的隔离级别是可重复读。问undo log和rado log有什么区别redo log用于记录数据页的物理变化当服务宕机的时候进行数据同步操作。保证了事务的持久性。undo log记录逻辑日志就比如当做插入操作时会在日志中记录逆向的操作也即是删除在事务回滚的时候会执行逻辑日志中的指令。保证了事务的持久性和原子性。问隔离级别是怎么实现的排他锁MVCC实现的。问说说你对MVCC的理解吧多版本并发控制。维护一个数据的多个版本使得读写操作没有冲突。mvcc主要有三个重点1.隐藏字段trx_id(事务id)记录当前事务的id其为自增的。 roll-pointer指向上一个版本的事务记录地址。2.undo log回滚日志存储老版本的数据版本链多个同时修改某条记录产生多版本的数据通过rool-pointer指针形成链表。3.readview解决一个事务查询选择版本的问题。根据readView的匹配规则和当前事务id找到对应的版本信息。问规则时答1.判断是事务id是否为当前事务的id。2.是否是活跃事务id。3.判断事务是否是在readview创建后开启的也就是事务id大于当前事务。4.判断事务中的数据是否已提交事务id小于最小的事务id。不同的隔离级别快照读是不一样的最终的访问结果也是不一样的。问时答当前读读取的是最新的数据并且会加锁。快照读读取的是记录数据的可见版本不会加锁。读已提交在每次快照读的时候都会生成readview。可重复读在有在第一次快照读的时候才会生成readview后续的快照读都是使用该readview的复制保证数据的一致性。问隔离级别中可重复读有什么缺点呢1.无法解决幻读的问题当我们事务去读取一定范围的数据且在该过程中其他的事务修改了该范围的数据此时两次读取就会出现查询结果不一致的情况最终导致幻读。2.无法读取到最新的数据因为可重复读在每次读取的时候都使用同一个readView且readView并非当前最新的数据最终导致无法读取到新的数据。问MySQL的主从同步有了解过吗Mysql主从同步的核心就是bin log(二进制日志)这个日志中主要记录 DDL(表的操作)DML(表中数据的操作)。1.master中事务提交数据后会将修改的数据保存到bin log中。2.slave有个iothread线程会监控的bin log的变化并将变化写入relay log中。3.slave有个SQLthread线程会监控relay log将改变的数据写入slave中。问你在项目中有使用过分库分表吗在物流项目中的订单服务的数据非常庞大请求数多且业务累计大。差不多单表的数据有100w条这时我们就使用分库分表。分库分表有四种策略1.水平分库通过将一个库中的数据拆分到多个库中解决海量数据存储和高并发的问题。主要通过sharing-sphere和mtycat实现。2.水平分表解决单表存储和性能的问题。3.垂直分库根据业务来拆分库中的表在高并发的情况下提高磁盘IO和网络连接数。每个微服务都有自己的表。4.垂直分表冷热数据分离多表不会相互影响。就比如表中字段为id,namedes将id,name和des分离id和name都是热数据而des为冷数据访问频率较低。框架八股文问Spring中的设计模式有哪些我们目前主要了解的就是工厂模式代理模式单例模式策略模式责任链模式。工厂模式常见的工厂模式主要就是BeanFactory延迟注入和ApplicationContext完全注入我们无需知道类如何创建直接从工厂中获取即可。单例模式ioc默认情况下就是一个单例模式每次获取相同恶的类的时候获取的对象是同一个。不会就行多次的创建。单例模式还分为饿汉式主动创建单例及懒汉式需要时再创建单例。代理模式 aop就是采用代理模式来实现的。当要代理的对象实现接口的时候我们就会使用JDK Proxy来生成代理对象。如果代理的对象没有实现接口的话我们就回使用CGLIB生成一个被代理对象的子类作为代理对象。策略模式在我们编写项目的时候就有使用到策略模式因为我要控制分布式锁的失败策略我们会在枚举类编写一个抽象方法编写多个内部方法去重新该方法不同的内部方法就是不同的策略。责任链模式stram流就是使用该模式的按照对应的顺序去执行方法并向下传递对象。起到解耦合的作用。问spring框架的单例bean是线程安全的吗在spring框架中有个注解叫Scope可以设置bean的状态默认就是singleton也就是单例。bean进行注入的时都是无状态的其不会被修改的。所以没有线程安全的问题。但是如果bean中有成员变量时就可能会有线程安全的问题因为该成员变量可能会被多个线程修改为了解决这个问题我们可以加锁或将bean设置为多例。(Scope设置为prototype)问什么是AOP面向切面编程将那些于业务无关的复用性比较高的代码快抽取出来较低代码的耦合度。问在你的项目中有使用过AOP吗在我的云盘项目中就使用到AOP在记录日志的时候我创建有个自定义注解aop的切面就是这个注解使用环绕通知在方法中我们通过传入的参数joinPoint获取对应的类和方法从而获取前端传来的参数和其他主要信息实现记录日志的效果。问Spring中的事务是怎么实现的本质就是通过AOP实现的通过环绕通知对应方法进行前后拦截在方法执行前开启事务在执行后提交事务会对此过程进行try/catch如果报错直接回滚。就是transactional问spring中事务失效场景有哪些1.在出现异常后方法中try/catch了该异常并且没有主动抛出异常这时候就会导致事务失效。解决方法在方法try/catch异常后手动的抛出异常。就会导致事务不知道出现异常了2.抛出检查异常时会导致事务失效,spring中的事务只会对runtime异常进行回滚。就比如:Not found Exception就是检查异常。解决方法在transactional中设置属性 rollbackFor Exception.class。使得事务会对所有的异常进行回滚。3.非public方法会导致事务失效。解决方法将方法的作用域该为public。4.在非事务方法中调用了事务的方法此时就会导致事务失效。就比如某给没有加事务注解的方法调用了加了事务注解的方法5.回滚异常类型不匹配。我们可能会设置需要进行回滚的异常就是rollback的值如果抛出的异常类型不匹配就会导致事务失效6.事务的传播行为错误。就比如在事务方法中调用了其他的事务方法初始化如果其他的时候方法设置开启新事务的话在其事务成功后就不会参与外部事务的回滚操作问事务的传播性有哪些主要就是三个Propagation_RequiredPropagation_Required_newPropagation_nested。Propagation_Required如果B为A的子方法并且都为该传播类型那么它们都会在同一个事务中。如果主方法中没有开启事务的话就会开启新事务。Propagation_Required_new如果B为A子方法B会创建一个独立的事务B不会受A事务的影响。Propagation_nested如果B为A子方法B会创建一个事务内嵌到A的事务中如果A中没有事务的话就单独开启一个事务。问Transactional的原理有了解过嘛事务注解是基于AOP来实现的也就是在执行方法前开启事务如果try/cath捕获到异常的话就会进行回滚在方法执行完成后就会进行提交。那AOP实现涉及到动态代理的流程。如果被代理目标对象实现了接口的话就会使用JDK Proxy生成代理对象。如果代理对象没有实现接口的话就会使用CGLIB Proxy生成被代理对象的子类作为代理对象。问说说CGLIB的执行流程问spring中bean的生命周期有了解过吗结构图生命周期的流程为下1.通过BeanDefinition获取bean的定义信息。2.通过构造函数创建bean可以将当前的bean理解为一个空壳。3.进行依赖注入对bean中的属性进行赋值。4.处理Aware接口也就是一些以Aware结尾的接口就比如beanNameAwarebeanFactoryAwareapplicationContextAware。如果实现了个接口的话我们需要重写一些方法。5加载BeanPostProcessor对象并执行处理器的前置初始化方法(PostProcessorAfterInitiazilation方法)。6.执行初始化方法包括 IntializingBean和自定义的初始化方法。7.加载BeanPostProcessor对象并执行处理器的后置初始化方法(PostProcessorBeforeInitiazilation方法)。在此后置处理器中我们可以通过aop对原始的bean做增强也就是进行代理代理就包括 JVM代理和CGLIB代理。8.将bean进行销毁。问说说CGLIB动态代理的执行流程吧aop就是采用代理模式来实现的。当要代理的对象实现接口的时候我们就会使用JDK Proxy来生成代理对象。如果代理的对象没有实现接口的话我们就回使用CGLIB生成一个被代理对象的子类作为代理对象。问有了解过bean的循环依赖吗循环依赖也就是循环引用两个或以上的bean同时相互依赖对方最终形成闭环。就比如A依赖BB依赖A。spring提供了解决方案:三级缓存。1.一级缓存:单例池存储已经初始化完成的单例bean。2.二级缓存缓存早期的bean单例对象就bean只完成到执行构造方法。3.三级缓存缓存创建bean的factory这些factory用于创建代理对象和普通对象。三级缓存的解决流程当前问题为A,B相互依赖实例化A并将生成的A的objectFacttory将其存入三级缓存因为A依赖BB也会去实例化创建对应的objectFactory存入三级缓存。此时B依赖A就会通过三级缓存中A的objectFactory生成早期A实例并将该A实例存储到二级缓存中将这个早期的A注入B中此时B就创建完成了将B实例存储到一级缓存中。将完整的B注入A中将A实例存储到一级缓存中并将二级缓存中A的实例删除。