Java初级开发面试记
技术面试自我介绍:
姓名,年龄,毕业院校,毕业专业,毕业时间,工作时间。这些可以回避自己的劣势,比如自己的毕业专业不是计算机相关的可以暂时不说,等问到在说。最后提前准备一块熟练的理论部分和项目,尝试诱导面试官提问,比如:我成就感最多的项目是xxx,我熟悉的技术是xxx;
Java基础:
StringBuilder和StringBuffer的区别:
StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。所以, StringBuffer 对缓存区优化,不过 StringBuffer 的这个toString 方法仍然是同步的。
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,如果是单线程场合 StringBuilder 更适合。
hashmap底层:
- 数组+链表+红黑树:key哈希特征一致的元素存储在数组的相同下标下,多个元素在同一个下标下用链表存储,
- 链表长度大于8树化,链表长度小于6链化;
- 红黑树:根节点左侧值小于根节点、右侧值大于根节点;
- hash冲突:多个key的哈希特征一致;hash冲突过多表明算法不够优秀,将过多的元素存储在数组的同一下标下;
- 扩容:初始容量16,扩容因子0.75,扩容方式*2;
- 与hashtable区别:hashmap线程不安全、性能和hash算法更优秀、可以有一个键可以为null、
- hashmap可以有多个值为null所以不能用get()来判断是否有null键,应该用containsKey();
- hashtable线程安全但是有更多的集合已经可以取代它、初始容量为11、扩容方式是*2+1
treemap:
- 存储键值对,通过红黑树实现,默认通过key的自然顺序进行排序;
- 实现了cloneable接口,serializable接口,可克隆,可序列化;
抽象类和接口的区别:
- 抽象类需要被继承,接口需要被实现;
- 抽象类可以有抽象方法,也可以有实现方法,接口只能有抽象方法;
- 抽象类可以有自己的属性,接口的属性只能是静态常量;
- 抽象类抽象的是类别,接口抽象的是行为;
- 抽象类更像一个模板已经有了完整类的雏形,只需要子类进行继续完善或者修改;
- 接口是规范告诉实现类该做哪些事情;
- 接口的抽象级别更高
线程基础:
线程创建方式:
- 类继承Thread,重写run方法,创建类对象,调用start方法进入就绪状态
- 类实现Runnable接口,重写run方法,创建类对象,创建Thread的对象并将类对象作为参数传入Thread的对象作为target,Thread的对象调用start方法
- 类实现Callable接口,重写call方法,用FutureTask包装类对象,创建Thread对象,并将FutrueTask对象作为target,Thread对象调用start方法,并且可以通过FutureTask对象调用get获得返回值
线程状态:
- 新建、就绪、运行、阻塞、死亡
- 线程对象创建出来,线程处于新建
- 线程对象调用start方法,线程处于就绪
- 线程被cpu调度,run方法执行,线程处于运行
- 线程对象执行sleep、wait等方法线程进入阻塞
- 运行线程执行yield,线程进入就绪,重新等待cpu调度
- 线程体执行完成或抛出异常、错误,线程死亡
- 死亡的线程不能重新启动,一个线程也只能启动一次即只能调用一次start方法
wait sleep:
- wait和sleep都会使线程阻塞
- wait是Object类的方法,sleep是Thread类的方法
- wait只能在synchronized修饰的方法或代码块中使用,sleep可以在任意位置使用
- wait会释放锁,sleep不会释放锁
- wait一般和notify和notifyAll配合使用,实现线程通信,sleep一般是设置延时不涉及通信
线程池:
- 重复创建和销毁线程都会大大增加开销,线程池可以减少这种资源的消耗
- 线程池可以统一管理线程避免浪费资源,还可以统一优化
ssm相关:
springmvc请求流程:
- 首先由前端发起http请求,服务器接受到请求,如果请求匹配映射路径,服务器就会将该请求交给dispacherservlet处理;
- dispacherservlet将请求交给handlermapping,handlermapping根据请求信息找到对应的handler处理器,将其封装为处理器执行链,返回给dispatcherservlet;
- dispatcherservlet根据处理器执行链中的处理器找到对应的handlerAdapter处理器适配器;
- 处理器适配器调用具体的controller进行业务处理并将处理结果和要跳转的视图封装到modelandview,返回处理器适配器;
- 处理器适配器将modelandview返回给dispatcherservlet;
- dispacherservlet将modelandview交给视图解析器,将modelandview中的逻辑名封装为视图对象;
- 视图解析器将view返回给dispatcherservlet,dispatcherservlet根据view对modelandview进行数据填充,得到响应对象;
- dispatcherservlet将响应前端浏览器,浏览器得到的可能是html页面、json字符串、图片等不同的形式;
动态代理:
- 在程序运行期间,使用反射机制、字节码生成技术来创建代理类和实例;
- 相比于静态代理,我们不用为每个目标类去单独去写一个代理类;
- spring aop实现原理就是动态代理,当目标类实现了接口时采用jdk的动态代理,为目标类生成实现同一个接口的代理类;
- 当目标类没有实现接口,则采用cglib动态代理,为目标类生成一个子代理类和对象,完成增强;
spring事务底层原理ioc aop:
首先spring的事务是一定依赖于数据库对事务的支持;
想要用到事务就要先开启事务,在需要进行事务管理的类和者方法上标注@transactional,
spring在启动时会解析生成相关的bean,就会扫描该注解,为这些类和方法生成代理,并且根据注解完成参数注入。
在aop的支持下完成事务处理,具体是:
- 调用动态代理,增强异常捕捉,内部创建数据库连接Connection,禁止自动提交来开启事务。
- 执行当前方法的sql。
- 出现异常回滚事务。
- 没有出现异常提交事务。
真正的提交事务和回滚是binlog和redo log
自动配置:
springboot只需要引入starters,就可以在启动时自动加载依赖,配置参数,然后就直接能在方法中使用相关的对象;
首先springboot启动类上面的@SpringBootApplication注解包含了@EnableAutoConfiguration注解,
@EnableAutoConfiguration通过@Import引入了deferredImportSelector(延迟选择器,使自动配置类加载靠后,方便条件筛选),
选择器会加载所有的spring.factories文件,找到key为EnableAutoConfiguration下面的某某某AutoConfiguration类名,
这些类上标有的@Conditional注解写有各种前置条件,包括存在哪些bean、不存在哪些bean、类路径有哪些类等等,
当前置条件满足时,对应的AutoConfiguration类就会实例化类里定义的bean,并注入到spring容器中,这就完成了自动配置。
ioc注入方法:
实现ioc注入就需要告诉ioc service provider哪个对象是被注入对象,哪个对象是被依赖对象,告知的方法大致有三种:
- 第一种是构造方法,在被注入对象的构造方法中,被依赖对象作为参数列表,这样构造方法执行完,注入也完成了;
- 第二种是setter方法,被依赖对象作为被注入对象的属性,为被依赖对象添加setter方法,这样也可完成注入;
- 第三种是接口注入,被注入对象必须实现一个接口,该接口声明方法中的参数类型必须是被依赖对象的类型;
构造方法直接,对象构造完毕,即完成注入;
setter方法灵活,可以在注入前做其他事情;
接口方法笨重,为了注入还要实现接口,不怎么实用;
ioc实现原理:
在spring启动时会读取bean相关的配置文件和注解,在spring容器中生成一份bean配置注册表,
根据注册表利用java反射实例化对应的类得到bean实例并装配bean之间的依赖关系,
最后放入到spring容器中的bean缓存池中,该缓存池由ConcurrentHashMap实现
ioc和aop是什么:
ioc:控制反转是一种面向对象的编程思想。它可以实现对象间的松耦合,实现这一思想的手段是依赖注入。ioc容器可以实现自动注入,只需要在配置文件中配置好依赖关系,在需要对应的对象时直接向ioc容器获取就行了。
aop:面向切面编程,这种编程思想是对面向对象编程的一种补充。它可以解决一批组件的共性需求如:权限检查、日志记录、事务管理等功能;我们可以在配置文件中声明切点、切面,就可以在指定方法执行的指定时间段织入指定增强了,这样统一实现了共性需求又不用修改这一批组件的代码;
BeanFactory和ApplicationContext的区别:
BeanFactory:
是Spring里面最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
- 1) 国际化(MessageSource)
- 2) 访问资源,如URL和文件(ResourceLoader)
- 3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
- 4) 消息发送、响应机制(ApplicationEventPublisher)
- 5) AOP(拦截器)
两者装载bean的区别:
BeanFactory:BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;
ApplicationContext:ApplicationContext在启动的时候就把所有的Bean全部实例化了。
bean的配置方式:
1、xml配置,声明类路径,id或name属性
2、注解,component、controller、service、repository
3、Java类配置,@Configuration和@Bean
4、import
Spring的Bean是否是线程安全的:
当bean作为方法局部变量时是线程安全的;
若是类成员变量,要进行读写操作就是线程不安全的;
可以采用以下几种方式来解决:
1、设置bean的scope为prototype多例
2、将bean放入threadlocal中
3、synchronized修饰来同步方法
Spring中Bean的实例化方式:
1、构造器方式;(反射)
2、静态工厂方式;(factory-method)
3、实例工厂方法;(factory-bean+factory-method,@Bean的原理)
4、FactoryBean;
Spring自动装配的方式:
自动装配是指不使用<constructor-arg>和<property>标签的情况下,自动维护bean之间的关系,将一个bean注入到其他bean的属性中;
注入方式有:
- byName:通过bean名称进行自动装配,如果一个bean的属性名和另一个bean名称相同,就进行自动装配
- byType:通过参数的类型进行自动装配
- constructor:通过构造函数进行自动装配,并且构造函数的参数通过byType进行装配
Spring中bean的生命周期:
bean的生命周期是指bean从创建到销毁的整个过程:
实例化:
可以由spring通过反射推断构造函数进行实例化、实例工厂实例化、静态工厂实例化
属性填充:
解析自动装配(byName、byType、constructor)、解决循环依赖问题
初始化:
调用aware回调方法如:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
调用初始化生命周期函数
如果bean实现aop,创建动态代理
销毁:
spring容器关闭时调用
调用销毁生命周期函数
Spring有哪几种配置方式:
1、xml配置文件,spring.xml中的<bean>标签
2、注解配置,spring.xml中的<component-scan base-package=""/>标签 ,然后再对应包里面使用@Component、@Autowired
3、JavaConfig,@Configuration、@Bean
@import可以有几种用法:
1、直接指定类,是配置类正常解析成配置文件,是普通类就解析成bean
2、ImportSelector,可以注册多个,返回一个string[],每个值就是一个完整的类路径
3、ImportBeanDefinitionRegister,可以异常注册多个
4、DeferredImportSelector比ImportSelector靠后加载
aop实现方式:
1、接口配置
2、xml配置,命名空间<aop><aop/>
3、aspectj注解配置
事务特性:
原子性,事务里包含的操作要么都提交成功,要么都失败;
一致性,事务执行前后数据库的状态要保持一致;比如转账前后的总金额要保持一致;
隔离性,多个用户操作同一张表,为每个用户开启一个事务,事务之间不应该相互影响
持久性,事务一旦被提交,数据库中数据的改变就是永久性的,即使数据库出现问题也不会丢失事务;
事务管理类型:
编程式事务,声明式事务
声明式事务的实现方式:
接口、xml<tx><aop>命名空间、@Trasanctional
事务传播行为:
指当出现一个事务方法被另一个事务方法调用时,该方法该如何处理事务。
常见的事务传播行为类型有:
required,默认类型表示需要事务,外部有事务就融入,没有事务就开启事务,常用在增删改操作中;
supports,表示支持,外部有事务就融入,没有也不会开启,常用在查询操作中;
requires-new,需要新事务,不管外部有没有事务都会开启新的事务,常用在外部事务和内部事务没有业务关联的情况,如记录日志;
此外还有not-supported、never、mandatory等等这些不常用的类型。
spring用了哪些设计模式:
1、简单工厂,BeanFactory
2、单例模式,Bean实例
3、观察者模式,事件监听
4、模板方法,几乎所有外界拓展
5、代理模式,aop底层
6、责任链模式,aop方法调用
springmvc的拦截器和过滤器的区别:
- 拦截器不依赖servlet,过滤器依赖servlet;
- 拦截器拦截dispatcherservlet映射的请求,过滤器可以处理几乎所有的请求;
- 过滤器先于拦截器执行;
- tomcat>filter>servlet>interceptor>controller>interceptor>filter>tomcat
MyBatis中的$和#有什么区别:
使用#设置参数时,MyBatis会创建预编译的SQL语句,然后在执行SQL时MyBatis会为预编译SQL中的占位符(?)赋值。预编译的SQL语句执行效率高,并且可以防止注入攻击。
使用$设置参数时,MyBatis只是创建普通的SQL语句,然后在执行SQL语句时MyBatis将参数直接拼入到SQL里。这种方式在效率、安全性上均不如前者,但是可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递SQL的列名,根据某些列进行排序,或者传递列名给SQL都是比较常见的场景,这就无法使用预编译的方式了。
mybatis怎么实现一对一、一对多查询:
一对一、一对多查询都分为嵌套结果的方式和嵌套查询的方式。
嵌套结果的方式就是执行一个联表查询sql,将结果进行嵌套封装;
嵌套查询的方式就是先查询第一张表的sql,封装结果时封装和第二种表有关字段时,再执行第二张表的sql;
<!-- 一对一嵌套查询方式 begin-->
<select id="FindPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
SELECT * from tb_person where id=#{id}
</select>
<resultMap id="IdCardWithPersonResult" type="Pojo.Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<!--property:当前实体的属性名(person的card属性)-->
<!--javaType:当前实体的属性的属性类型(全类名)这里可以取别名-->
<association property="card" column="card_id" javaType="Pojo.IdCard"
select="Mapper.UserMapper.FindById"/>
</resultMap>
<select id="FindById" parameterType="Integer" resultType="Pojo.IdCard" >
SELECT * from tb_idcard where id=#{id}
</select>
<!-- 一对一嵌套查询方式 end-->
<!-- 一对一嵌套结果查询 begin-->
<select id="FindPersonById2" parameterType="Integer"
resultMap="IdCardWithPersonResult2">
SELECT p.*,idcard.code
from tb_person p,tb_idcard idcard
where p.card_id=idcard.id
and p.id=#{id}
</select>
<resultMap id="IdCardWithPersonResult2" type="Pojo.Person" >
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<!--这里association内部也可以写resultmap来代替手动映射-->
<association property="card" column="card_id" javaType="Pojo.IdCard">
<id property="id" column="card_id" />
<result property="code" column="code"/>
</association>
</resultMap>
<!-- 一对一嵌套结果查询 end-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Mapper.UserMapper">
<!--一对多嵌套查询 begin-->
<select id="FindEmpWithDept" parameterType="Integer" resultMap="EmpWithDeptResult">
SELECT * from dept where deptno=#{deptno}
</select>
<resultMap id="EmpWithDeptResult" type="Pojo.Dept">
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<collection property="empList" column="deptno" ofType="Pojo.Emp"
select="Mapper.UserMapper.FindEmpWithDeptByOne"/>
</resultMap>
<select id="FindEmpWithDeptByOne" parameterType="Integer" resultType="Pojo.Emp">
SELECT * from emp where deptno=#{deptno}
</select>
<!-- 一对多 嵌套查询 end-->
<!-- 一对多 嵌套结果查询 begin-->
<!-- SELECT d.*,e.* 查询两各表的所有需要的字段-->
<!-- from dept d,emp e 来自dept表和emp表,d,e是表名的缩写,用于区分两个表的deptno字段-->
<!-- SELECT d.*,e.deptno,e.empno,e.ename,e.job,e.mgr,e.hiredate,e.sal,e.comm-->
<select id="FindUserWithDept" parameterType="Integer"
resultMap="UserWithOrdersResult">
SELECT d.*,e.*
from dept d,emp e
where d.deptno=e.deptno
and d.deptno=#{deptno}//传入参数
</select>
<resultMap id="UserWithOrdersResult" type="Dept" >
<!-- 先查询dept表,然后再拼接empList-->
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<collection property="empList" ofType="Emp" >
<!-- property="empList" 引入Dept对象指定实体类对象中的emplist泛型数组 -->
<!-- ofType="Emp" 指定实体类对象中的集合类属性 -->
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="mgr" column="mgr"/>
<result property="hiredate" column="hiredate"/>
<result property="sal" column="sal"/>
<result property="comm" column="comm"/>
<result property="deptno" column="deptno"/>
</collection>
</resultMap>
<!-- 一对多 嵌套结果查询 end-->
</mapper>
springboot相关:
springboot启动流程:
springboot启动的流程是由springapplication.run()执行的;
其中包括实例化和执行run方法两大部分:
实例化,读取spring.factories中的监听器和初始化器包括:
- 确定应用程序的类型;
- 加载初始化器;
- 加载监听器;
- 设置应用程序主类;
run方法主要包括:
- 开启计时器;
- headless设置为true,在无鼠标键盘时照常工作;
- 获取并启用监听器;
- 设置应用程序参数;
- 准备程序环境变量;
- 忽略bean信息;
- 打印banner;
- 创建应用程序上下文;创建spring容器
- 实例化异常报告;
- 准备上下文环境变量;将启动类作为配置类来读取;
- 刷新上下文环境变量;解析import注解,加载所有的自动配置类,加载内置的servlet容器
- 刷新上下文环境变量后处理;
- 停止计时器;
- 发布上下文就绪事件;
- 执行自定义的run方法;
此时springboot启动完成;
springboot常用注解:
主类上的:@SpringbootApplication、@EnableAutoConfiguration、@ComponentScan、@Configuration
标注组件:@Component、@Controller、@RestController、@Service、@Repository
控制层:@Autowired、@Qualifier、@RequestMapping、@GetMapping、@PostMapping、@ResponseBody
变量有关:@RequestParam、@PathVariable
spring、springMVC、springboot有什么区别:
spring是一种分层的轻量级java开发框架,可以接管web层,业务层、持久层组件维护他们的关系,核心是控制反转和面向切面编程。
springmvc是spring的一种web层mvc框架,用来替代servlet处理请求、获取表单。
springboot延续了spring的ioc和aop核心,简化了应用的开发部署,使开发者更注重应用开发,而无需过多关注xml配置,能快速整合其他框架,是基于spring的一种扩展。
springboot怎么整合mybatis:
首先要添加mybatis的依赖,
在springboot的配置文件中整合mybatis的配置并且配置数据源,
将持久层标注上@mapper表示是mapper接口,
在业务层注入mapper就是完成整合了
springboot的理解,有哪些优点:
- SpringBoot是快速开发Spring应用的脚手架,其设计目的就是简化Spring的开发过程;
- SpringBoot提供了很多内置stater,结合自动配置,对主流框架无配置集成;
- 采用JavaConfig的方式可以代替xml;
- 内置Web容器,直接运行jar文件就可以启动web应用;
- 管理常用的第三方依赖,减少版本冲突问题;
springboot核心注解:
springbootapplication:标注在springboot主类上,表示是一个springboot应用,用来开启springboot的各项功能;
主要由springbootconfiguration、enableautoconfiguration、componentscan三个注解组成:
springbootconfiguration:根配置类,将当前类声明的@bean注解标注的方法返回的对象注册为bean;
enableautoconfiguration:自动配置,加载spring.factories文件,找到enableautoconfiguration键,看值里有没有满足自动配置条件的类,如果有就将该类实例化,注册到容器中。
componentscan:默认扫描本包及其子包里的component注解包括controller、service、repository,将标注了这些注解的类注册为bean。
springboot内嵌tomcat的启动原理:
在springboot启动过程中,创建应用上下文时,根据应用程序类型为servlet创建对应的spring容器,在刷新上下文时创建webserver启动tomcat,获取服务等待请求
为什么springboot的jar可以直接运行:
springboot提供了一个spring-boot-maven-plugin用来吧程序打包为一个可执行的jar包。
这个jar是一个fatjar,jar包中包含了应用依赖的jar包和springbootloader相关的类。
命令java -jar回去找jar中的mainfest文件,确定启动类mainclass;
fatjar的启动main函数是jarlauncher,它创建一个launcherurlclassloader来加载应用依赖的jar,启动应用启动类的main函数。
自定义stater:
1、新建maven工程,工程名后缀为spring-boot-stater,导入spingboot依赖
2、编写自定义的属性类
3、编写配置类
4、/MATEINF/srpring.factories内配置自动配置类
说说开发时怎么在springboot的基础上做扩展:
比如springboot的aop的代理技术默认时cglib,要修改,肯定要上百度,百度没有只有研究源码,着重看自动配置类里的conditional注解,里面多半有设置默认代理技术的选项;
又比如springmvc的扩展,也是关注自动配置类,里面有将所有实现了某接口的类全部自动注入。
数据库:
left join、right join、inner join(join)区别:
left join、right join都是外联接,inner join内联接;
left join取左表相关全部数据和左右表交集数据,没有数据的地方用null填充;
right join取右表相关全部数据和左右表交集数据,没有数据的地方用null填充;
join取左右表交集数据,范围最小。
sql优化:
避免索引失效:
模糊查询避免在开头使用%
尽量用between替代in
避免使用or,可用union代替
避免筛选条件是null,可添加一个默认值来代替null
where匹配时=左侧避免表达式
尽量避免where 1=1,而是在代码中判断是否有条件
联合索引一定要用到最左索引
字段类型和实际存储类型不匹配
where筛选的字段要和order by的字段相同
select优化:
避免select * 这不仅是降低查询效率,还会浪费网络资源、内存资源
多表联合查询时小表在前大表在后,先扫描小表可能在大表还未扫描结束就已经匹配出结果了
where多个条件时,将能筛掉数据多的条件放在前面
索引失效:
like关键字查询的字符串第一个字符是%时,索引失效;
使用联合索引时,匹配条件没有包含最左侧也就是第一个字段时,索引失效
使用or关键字匹配查询时,只有or关键字作为查询条件,且or两端都是索引列索引才有效,其余情况索引失效
排查慢sql:
可以同过配置文件或者命令开启慢查询日志,
首先要开启慢查询日志,设置阈值时间即查询时间超过多少才会记录,设置日志格式一般是表或者文件
可以用mysql自带的mysqldumpslow工具来分析日志
并发事务隔离级别:
事务并发问题:
脏读:一个事务读到了其他事务还没提交的数据;
不可重复读:一个事务先后两次读到的同一个数据不一致,原因是在这两次读之间其他事务提交了update数据
幻读:同一个事务先后两次读到的数据不一致,但是是整行数据的不一致,是因为其他事务在这期间提交了delete或insert数据
事务隔离级别:
读未提交:read uncommitted,有脏读问题
读已提交:read committed,解决了脏读,但是有不可重复读问题,是oracle的默认隔离级别
可重复读:repeatable read,解决了不可重复读,但是有幻读问题,是mysql的默认隔离级别
可串行化:serializable,解决了脏读,相当于锁住整张表
索引类型:
普通索引:没有限制
唯一索引:值要求唯一,允许null
主键索引:一种特殊的唯一索引,唯一且不能为空
组合索引:多个字段组合上创建的索引,使用时必须要包含创建索引时的第一个字段索引才会生效
全文索引:查找文本中的关键字
索引能提高查询速度,但是也降低表创建和更新的速度。
维护索引需要索引文件,所以创建索引和更新数据都需要保存索引文件。
b树和b+的区别:
b+树所有的查询都要到叶子节点,性能稳定;
b+树每一个节点存储的元素更多,更加扁平,磁盘io次数更少;
b+树在叶子节点形成链表,范围查询更快;
不影响生产的情况下转移数据:
通过双写机制在写数据到原数据库的同时写一份数据到新数据库中;
后台程序将原数据库通过中间件迁移到库中;
在迁移过程中的插入数据要检查数据更新情况,如果新表没有当前数据则新增,如果新表有数据但不是当前数据则更新。
迁移过一遍后校验新旧库数据是否完全一致。
多迁移几次就能保证完全一致。
redis 数据类型以及应用场景:
string:可以存储任何格式的数据,可以利用自增来做统计,存储地址、图片等
hash:可以存储一个对象的多个属性,这个对象就是key,多个属性可以map形式存储在value
list:对两端操作效率高,可以根据这个特性做时间轴相关的功能
set:唯一,并且可以做交集并集差集处理,适合做好友列表
zset:有序集合,结构和hash类似将value中key换为score,天然适合做排行
redis 淘汰策略:
出现原因:定期删除和惰性删除都不是精确删除,仍然可能出现添加数据时出现内存不足的问题;
redis的淘汰策略有
noeviction:不提供写服务,返回错误
allkeys-lru:在所有key中淘汰最近最不常用的
volatile-lru:在设置了时间的key中淘汰最近最不常用的
allkeys-random:所有key随机淘汰
volatile-random:所有设置了时间的key随机淘汰
volatile-ttl:所有设置了时间的key淘汰最早过期的
缓存穿透、缓存击穿、缓存雪崩:
缓存穿透:大量缓存和数据库都没有数据的请求,一般是攻击。
解决办法有:
设置权限,将发出大量不合理请求的用户拉入黑名单;
使用布隆过滤器快速判断key在数据库中存不存在;
缓存击穿:大量同时对同一个key请求,此时该key又过期了,都去访问数据库;
解决办法有:
不设置热点数据的过期时间;
热点数据快过期时重新设置;
从数据库拿数据时加上互斥锁避免所有请求都去访问数据库;
缓存雪崩:大量不同的key过期,或者缓存挂掉,都去访问数据库直至压崩;
解决办法有:
设置过期时间时添加随机值,以分散过期时间;
采用主从加哨兵模式,避免缓存挂掉;
限流,虽然体验不好但是总比挂了好
redis和消息队列:
消息队列:
应用间通信的方式,消息发送后立即返回,由消息系统来确保消息的传递;
消息发布者只管把消息发布到mq中,消息使用者只管从mq中取消息;
将数据放入mq的叫生产者,从mq取数据的叫消费者、mq消息队列叫消息处理中心;
使用mq可以提高系统性能,实现系统松耦合,流量削峰;
redis有部分功能和命令可以实现mq,比如list、订阅发布、stream
微服务了解:
微服务项目的优点,什么情况需要微服务架构:
分工协作:
单体项目需要每个人都对整体项目有所把握,加重了开发人员的负担;
拆分为微服务能分工合作,提高开发效率,充分利用开发人员的优势;
并发:
单体项目整体集群会浪费资源,而且无法准确的预估扩容;
拆分后可以对单个服务进行压测,准确地预估扩容。
业务:
单体项目规模庞大后难以继续升级,也难以维护。
拆分后项目功能明确,对单个服务的升级维护都很方便。
容错:
单体项目一个功能内存溢出导致整个应用都不可用;
拆分后弱依赖的服务异常,可以熔断隔离,不影响主业务使用;
缺点:
分布式项目远程调用速度慢,有调用失败的风险,保证最终一致性困难;
整体运维较单体应用复杂很多;
微服务常用组件:
- 注册中心:管理服务,necos
- 配置中心:管理服务配置,necosconfig
- 网关:为客户端提供统一服务,一些与业务本身无关的功能:路由转发、验证权限、跨域等,gateway
- 负载均衡:客户端的负载均衡器,LoadBalancer
- 远程调用:使远程调用服务像本地方法一样,更加优雅,OpenFeign
- 服务熔断:保证应用高可用,防止出现服务雪崩,sentinel
- 分布式事务:Seata
- 链路追踪:追踪服务健康状况,协助恢复,Skywalking
其他:
项目开发流程:
成立项目小组,讨论需求形成文档,详细设计,数据库设计,程序开发,测试上线
swagger:
管理测试接口的框架,能够生成接口文档
linux常用命令:
cd:切换目录
ls:查看文件查看目录
file:查看文件类型
grep:文本搜索工具
find:查找文件
mkdir:创建文件夹
touch:创建修改文件
cp:复制
mv:移动文件
rename:重命名
tar:压缩、解压
rm:删除
待续:
redis分布式锁
微服务 通信 定理
mabatis一二级缓存
延迟加载
一对一一对多
单点登录
vue的理解
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤