暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

基础概念及问答(五)

萌小璐 2021-10-26
375
1: Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
  • @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

  • @EnableAutoConfiguration打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:@SpringBootApplication(exclude= { DataSourceAutoConfiguration.class })。

  • @ComponentScan:Spring组件扫描。


2: Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置
namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。
原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id 就可以重复,namespace不同,namespace+id自然也就不同。

3: Mybatis是如何进行分页的?分页插件的原理是什么
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student) t limit 0, 10

4: JDK8中链表转变为红黑树的条件
  • 链表中的元素的个数为8个或超过8个
  • 同时,还要满足当前数组的长度大于或等于64才会把链表转变为红黑树。


为什么?

因为链表转变为红黑树的目的是为了解决链表过长,导致查询和插入效率慢的问题,而如果要解决这个问题,也可以通过数组扩容,把链表缩短也可以解决这个问题。所以在数组长度还不太长的情况,可以先通过数组扩容来解决链表过长的问题。


5: 如何使用 Spring Boot 实现异常处理
Spring 提供了一种使用 ControllerAdvice 处理异常的非常有用的方法。我们通过实现一个 ControllerAdvice 类,来处理控制器类抛出的所有异常

6: 什么是 Spring Profiles
Spring Profiles允许用户根据配置文件(dev,test,prod 等)来注册 bean。因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在
PRODUCTION中,某些其他 bean 可以加载。假设我们的要求是 Swagger 文档仅适用于 QA 环境,并且禁用所有其他文档。这可以使用配置文件来完成。Spring Boot 使得使用配置文件非常简单。

7: Mybatis中如何指定使用哪一种Executor执行器
在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType) 。配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句 (prepared statements);BATCH 执行器将重用语句并执行批量更新。

8: Jvm的基本认识
(1) 基本概念:
JVM是可运行Java代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域。JVM 是运行在操作系统之上的,它与硬件没有直接的交互。
(2) 运行过程:
我们都知道 Java 源文件,通过编译器,能够生产相应的.Class文件,也就是字节码文件,而字节码文件又通过 Java 虚拟机中的解释器,编译成特定机器上的机器码 。
也就是如下:
① Java 源文件—->编译器—->字节码文件
② 字节码文件—->JVM—->机器码
每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是 Java 为什么能够跨平台的原因了 ,当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享。

9: 简述Mybatis的插件运行原理,以及如何编写一个插件
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、 Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体 就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

10: AVLtree
定义:先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有 平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为 是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或 从可能存储在节点中的子树高度计算出来。
一般我们所看见的都是排序平衡二叉树。
AVLtree使用场景:AVL树适合用于插入删除次数比较少,但查找多的情况。插 入删除导致很多的旋转,旋转是非常耗时的。AVL 在linux内核的vm area中使用。

11: JDK8中的ConcurrentHashMap为什么使用synchronized来进行加锁
JDK8中使用synchronized加锁时,是对链表头结点和红黑树根结点来加锁的,而ConcurrentHashMap会保证,数组中某个位置的元素一定是链表的头结点或红黑树的根结点,所以JDK8中的ConcurrentHashMap在对某个桶进行并发安全控制时,只需要使用synchronized对当前那个位置的数组上的元素进行加锁即可,对于每个桶,只有获取到了第一个元素上的锁,才能操作这个桶,不管这个桶是一个链表还是红黑树。
想比于JDK7中使用ReentrantLock来加锁,因为JDK7中使用了分段锁,所以对于一个ConcurrentHashMap对象而言,分了几段就得有几个ReentrantLock对象,表示得有对应的几把锁。
而JDK8中使用synchronized关键字来加锁就会更节省内存,并且jdk也已经对synchronized的底层工作机制进行了优化,效率更好。

12: Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式
第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME, 对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常 工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

13: 我们如何监视所有 Spring Boot 微服务
Spring Boot 提供监视器端点以监控各个微服务的度量。这些端点对于获取有关应用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运行很有帮助。但是,使用监视器的一个主要缺点或困难是,我们必须单独打开应用程序的知识点以了解其状态或健康状况。想象一下涉及 50 个应用程序的微服务,管理员将不得不击中所有 50 个应用程序的执行终端。为了帮助我们处理这种情况,我们将使用位于的开源项目。它建立在 Spring Boot Actuator 之上,它提供了一个 Web UI,使我们能够可视化多个应用程序的度量。

14: Java和C++的区别
  • 都是面向对象的语言,都支持封装、继承和多态

  • Java不提供指针来直接访问内存,程序内存更加安全

  • Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。

  • Java有自动内存管理机制,不需要程序员手动释放无用内存






15: 如何提升系统业务数据访问性能?谈谈你对缓存的理解
缓存构建的基本思想是利用时间局限性原理,通过空间换时间来达到加速数据获取的目的,同时由于缓存空间的成本较高,在实际设计架构中还要考虑访问延迟和成本的权衡问题。

缓存的优势:

  • 提升访问性能

  • 降低网络拥堵

  • 减轻服务负载

  • 增强可扩展性

缓存的风险:
  • 增加了系统的复杂度
  • 缓存相比原始 DB 存储和运维的成本更高
  • 存在一致性问题
业务系统读写缓存有 3 种模式:
  • Cache Aside(旁路缓存)
  • Read/Write Through(读写穿透)
  • Write Behind Caching(异步缓存写入)

Cache Aside模式

1.Write: 更新 DB 后,直接将 key 从 cache 中删除,然后由 DB 驱动缓存数据的更新;
2.Read: 是先读 cache,如果 cache 没有,则读 DB,同时将从 DB 中读取的数据回写到 cache。
特点:
确保数据以DB 结果为准
适用场景:
数据一致性要求比较高的业务,或者是缓存数据更新比较复杂的业务,比如需要通过多个原始数据进行计算后设置的缓存数据

Read/Write Through模式

1. Write: 存储服务收到业务应用的写请求时,会首先查 cache,如果数据在 cache 中不存在,则只更新 DB,如果数据在 cache 中存在,则先更新 cache,然后更新 DB。
2. Read: 存储服务收到读请求时,如果命中 cache 直接返回,否则先从 DB 加载,回写到 cache 后返回响应。
特点:
  • 存储服务封装了所有的数据处理细节,业务应用端代码只用关注业务逻辑本身,系统的隔离性更佳。
  • 进行写操作时,如果 cache 中没有数据则不更新,有缓存数据才更新,内存效率更高。
适用场景:
用户最新Feed列表

Write Behind Caching模式

1.Write: 只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB
2.Read: 如果命中 cache 直接返回,否则先从 DB 加载,回写到 cache 后返回响应。
特点:
写性能最高,定期异步刷新,存在数据丢失概率
适用场景:
适合变更频率特别高,但对一致性要求不太高的业务,特别是可以合并写请求的业务,比如对一些计数业务

16: Spring Boot 的自动配置是如何实现的?
Spring Boot 项目的启动注解是:@SpringBootApplication,其实它就是由下面三个注解组成的:
@Configuration
@ComponentScan
@EnableAutoConfiguration
其中 @EnableAutoConfiguration 是实现自动配置的入口,该注解又通过 @Import 注解导入了AutoConfigurationImportSelector,在该类中加载 META-INF/spring.factories 的配置信息。然后筛选出以 EnableAutoConfiguration 为 key 的数据,加载到 IOC 容器中,实现自动配置功能!

17: Spring中有哪些依赖注入的方式
首先分两种:
  1. 手动注入,通常使用XML的方式定义Bean的时候就会通过手动方式给属性进行赋值
  2. 自动注入,利用@Autowired注解利用Spring自动给属性进行赋值
手动注入分为:
  1. 手动指定值
  2. 手动指定bean的名字
  3. 实例工厂方法得到bean
  4. 静态工厂方法得到bean
手动注入站在底层原理角度分为:
  1. 通过构造方法注入
  2. 通过set方法注入
自动注入分为:
  1. 通过属性自动注入
  2. 通过set方法自动注入
  3. 通过构造方法自动注入

18: Spring Boot 中如何解决跨域问题
跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
1 @Configuration
2 public class CorsConfig implements WebMvcConfigurer { 
3
4   @Override
5   public void addCorsMappings(CorsRegistry registry) {
6   registry.addMapping("/**")
7   .allowedOrigins("*")
8   .allowCredentials(true)
9   .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
10   .maxAge(3600);
11   }
12}
项目中前后端分离部署,所以需要解决跨域的问题。
我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。
当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。
我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。
1 @Configuration
2 public class CorsConfig { 
3
4   @Bean
5   public CorsFilter corsFilter() {
6   CorsConfiguration corsConfiguration = new CorsConfiguration();
7   corsConfiguration.addAllowedOrigin("*");
8   corsConfiguration.addAllowedHeader("*");
9   corsConfiguration.addAllowedMethod("*");
10   corsConfiguration.setAllowCredentials(true);
11   UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
12   urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsCo nfiguration);
13   return new CorsFilter(urlBasedCorsConfigurationSource);
14   }
15
16 }

19: 什么是 Swagger?你用 Spring Boot 实现了它吗
Swagger 广泛用于可视化 API,使用 Swagger UI 为前端开发人员提供在线沙箱。Swagger 是用于生成 RESTful Web 服务的可视化表示的工具,规范和完整框架实现。它使文档能够以与服务器相同的速度更新。当通过 Swagger 正确定义时,消费者可以使用 少量的实现逻辑来理解远程服务并与其进行交互。因此,Swagger消除了调用服务时的猜测。
前后端分离,如何维护接口文档 ?
前后端分离开发日益流行,大部分情况下,我们都是通过 Spring Boot 做前后端分离开发,前后端分离一定会有接口文档,不然前后端会深深陷入到扯皮中。一个比较笨的方法就是使用 word 或者 md 来维护接口文档,但是效率太低,接口一变,所有人手上的文档都得变。在 Spring Boot 中,这个问题常见
的解决方案是 Swagger ,使用 Swagger 我们可以快速生成一个接口文档网
站,接口一旦发生变化,文档就会自动更新,所有开发工程师访问这一个在线网站就可以获取到新的接口文档,非常方便。

20: Mybatis如何执行批量操作
使用foreach标签
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的 属性主要有item,index,collection,open,separator,close。
item  表示集合中每一个元素进行迭代时的别名,随便起的变量名;
index   指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open    表示该语句以什么开始,常用“(”;
separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
close    表示以什么结束,常用“)”。
在使用foreach的时候最关键的也是最容易出错的就是collection 属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection 属性值为list
如果传入的是单参数且参数类型是一个array数组的时候,collection 的属性值为array 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以 封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个 Map的,
map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己 封装的map里面的key
具体用法如下:
1 <!‐‐ 批量保存(foreach插入多条数据两种方法)
2 int addEmpsBatch(@Param("emps") List<Employee> emps); ‐‐>
3 <!‐‐ MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 ‐‐> //推荐使用
4 <insert id="addEmpsBatch">
5 INSERT INTO emp(ename,gender,email,did)
6 VALUES
7 <foreach collection="emps" item="emp" separator=",">
8 (#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
9 </foreach>
10 </insert>
1 <!‐‐ 这种方式需要数据库连接属性allowMutiQueries=true的支持
2 如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true ‐‐>
3 <insert id="addEmpsBatch">
4 <foreach collection="emps" item="emp" separator=";">
5 INSERT INTO emp(ename,gender,email,did)
6 VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
7 </foreach>
8 </insert>
使用ExecutorType.BATCH
Mybatis内置的ExecutorType有3种,默认为simple,该模式下它为每个语句的执行创建一 个新的预处理语句,单条提交sql;而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优;但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求 的
具体用法如下
1 //批量保存方法测试
2 @Test
3 public void testBatch() throws IOException{
4 SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
5 //可以执行批量操作的sqlSession
6 SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATC
H);
7
8 //批量保存执行前时间
9 long start = System.currentTimeMillis();
10 try {
11 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
12 for (int i = 0; i < 1000; i++) {
13 mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0,5), "b", "1"));
14 }
15
16 openSession.commit();
17 long end = System.currentTimeMillis();
18 //批量保存执行后的时间
19 System.out.println("执行时长" + (end ‐ start));
20 //批量 预编译sql一次==》设置参数==》10000次==》执行1次 677
21 //非批量 (预编译=设置参数=执行 )==》10000次 1121
22
23 } finally {
24 openSession.close();
25 }
26 }
mapper和mapper.xml如下
1 public interface EmployeeMapper {
2 //批量保存员工
3 Long addEmp(Employee employee);
4 }
1 <mapper namespace="com.jourwon.mapper.EmployeeMapper"
2 <!‐‐批量保存员工 ‐‐>
3 <insert id="addEmp">
4 insert into employee(lastName,email,gender)
5 values(#{lastName},#{email},#{gender})
6 </insert>
7 </mapper>
如何获取生成的主键
  • 对于支持主键自增的数据库(MySQL)


1 <insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
2 insert into user(
3 user_name, user_password, create_time)
4 values(#{userName}, #{userPassword} , #{createTime, jdbcType=
TIMESTAMP})
5 </insert>
parameterType 可以不写,Mybatis可以推断出传入的数据类型。如果想要访问主键,那 么应当parameterType 应当是java实体或者Map。这样数据在插入之后 可以通过ava实体 或者Map 来获取主键值。通过 getUserId获取主键
  • 不支持主键自增的数据库(Oracle)


对于像Oracle这样的数据,没有提供主键自增的功能,而是使用序列的方式获取自增主 键。可以使用<selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的 数据库,也适用于提供主键自增功能的数据库
<selectKey>一般的用法
1 <selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
2 </selectKey>
属性 描述
keyProperty 
selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列, 也可以是逗号分隔的属性名称列表。
keyColmn
匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以
是逗号分隔的属性名称列表。
resultType
结果的类型,MyBatis 通常可以推算出来。MyBatis 允许任何简单类型用
作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性 的 Object 或一个 Map。
order
值可为BEFORE 或 AFTER。如果是 BEFORE,那么它会先执行selectKey设置
keyProperty 然后执行插入语句。如果为AFTER则相反。
statementType
使用何种语句类型,默认PREPARED。有STATEMENT,PREPARED
和 CALLABLE 语句的映射类型。
1 <insert id="insertUser" >
2 <selectKey keyColumn="id" resultType="long" keyProperty="userId" order="BEFORE">
3 SELECT USER_ID.nextval as id from dual
4 </selectKey>
5 insert into user(
6 user_id,user_name, user_password, create_time)
7 values(#{userId},#{userName}, #{userPassword} , #{createTime, jdbcType=TIMESTAMP})
8 </insert>
此时会将Oracle生成的主键值赋予userId变量。这个userId 就是USER对象的属性,这样就 可以将生成的主键值返回了。如果仅仅是在insert语句中使用但是不返回,此时 keyProperty=“任意自定义变量名”,resultType 可以不写。
Oracle 数据库中的值要设置为 BEFORE ,这是因为 Oracle中需要先从序列获取值,然后 将值作为主键插入到数据库中。
扩展
如果Mysql 使用selectKey的方式获取主键,需要注意下面两点:
order :AFTER
获取递增主键值 :SELECT LAST_INSERT_ID()
  • 当实体类中的属性名和表中的字段名不一样 ,怎么办


第1种:通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一 致。
1 <select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
2 select order_id id, order_no orderno ,order_price price 
form orders where order_id=#{id};
3 </select>
第2种:通过来映射字段名和实体类属性名的一一对应的关系。
1 <select id="getOrder" parameterType="int" resultMap="orderResultMap">
2 select * from orders where order_id=#{id}
3 </select>
4
5 <resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
6 <!–用id属性来映射主键字段–>
7 <id property="id" column="order_id">
8
9 <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
10 <result property ="orderno" column ="order_no"/>
11 <result property="price" column="order_price" />
12 </reslutMap>

21:如果叫你自己设计一个中间件,你会如何设计?

  我会从以下几点方面考虑开发:

1) 远程过程调用

2) 面向消息

利用搞笑的消息传递机制进行平台无关的数据交流,并给予数据通信来进行分布式系统的集成,有一下三个特点:

  1. i)  通讯程序可以在不同的时间运行

  2. ii) 通讯晨旭之家可以一对一、一对多、多对一甚至是上述多种方式的混合

  3. iii)程序将消息放入消息队列会从小吸毒列中取出消息来进行通讯

3) 对象请求代理

提供不同形式的通讯服务包括同步、排队、订阅发布、广播等。可构筑各种框架如:事物处理监控器、分布数据访问、对象事务管理器 OTM 等。

4) 事物处理监控有一些功能
a) 进程管理:包括启动 server 进程、分配任务、监控其执行并对负载进行平衡 

b) 事务管理:保证在其监控下的事务处理的原子性、一致性、独立性和持久性 

c) 通讯管理,为 client 和 server 之间提供多种通讯机制,包括请求响应、会话、排队、订阅发布和广播等


22:什么是中间件?

中间件是处于操作系统和应用程序之间软件,使用时往往是一组中间件集成在一起,构成一个平台(开发平台+运行平台),在这组中间件中必须要有一个通信中间件,即中间件= 平台+通信。该定义也限定了只有用于分布式系统中才能称为中间件

主要分类:远程过程调用、面向消息的中间件、对象请求代理、事物处理监控。


23:ThreadLock 用过没有,说说它的作用?

ThreadLock 为本地线程,为每一个线程提供一个局部变量,也就是说只有当前线程可以访问,是线程安全的。

原理:为每一个线程分配一个对象来工作,并不是由 ThreadLock 来完成的,而是需要在应用层面保证的,ThreadLock 只是起到了一个容器的作用。原理为ThreadLock 的 set()跟 get()方法。

实现原理:

public void set(T value) {
Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t); 

if (map != null)

map.set(this, value); 

else

createMap(t, value); 

}

public T get() {
Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t); 

if (map != null) {

ThreadLocalMap.Entry e = map.getEntry(this); 

if (e != null)

return (T)e.value; 

}

return setInitialValue(); 

}


24:Hashcode()和 equals()和==区别?

(1) hashcode()方法跟 equals()在 java 中都是判断两个对象是否相等
(2) 两个对象相同,则 hashcode 值一定要相同;

即对象相同 ---->成员变量相同---->hashcode 值一定相同

(3) 两个对象的hashcode值相同,对象不一定相等。

总结:equals相等则hashcode一定相等,

         hashcode 相等,equals 不一定相等。

(4) ==比较的是两个引用在内存中指向的是不是同一对象(即同一内存空间)


25:mysql 数据库中,什么情况下设置了索引但无法使用?

(1)  索引的作用:在数据库表中对字段建立索引可以大大提高查询速度。

(2)  Mysql 索引类型:

  1. a)  普通索引

  2. b) 唯一索引

    唯一索引列的值必须唯一允许有空值,如果是组合索

    引,则列值的组合必须唯一:

    CREATE UNIQUE INDEX indexName ON 

    mytable(username(length)) -- 修改表结构
    ALTER mytable ADD UNIQUE [indexName] ON (username(length)) -- 创建表的时候直接指定

    CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16)

    NOT NULL, UNIQUE [indexName] (username(length)) );

    c) 主键索引

    一种特殊的唯一索引,不允许有空值,一般在创建表的时候创建主键索引:

CREATE TABLE mytable( ID INT NOT NULL,

VARCHAR(16) NOT NULL, PRIMARY KEY(ID) );
    d) 组合索引

CREATE TABLE mytable( ID INT NOT NULL,

VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL,age INT
NOT NULL );
为了进一步榨取 MySQL 的效率,就要考虑建立组合索引。就是 将 name, city, age 建到一个索引里:代码如下:

ALTER TABLE mytable ADD INDEX name_city_age

(name(10),city,age);

(3)  什么情况下有索引,但用不上?

    1. 如果条件中有 OR,即使其中有部分条件带索引也不会使用。注 意:要想使用 or,又想让索引生效,只能将 or 条件中的每个列都加上索引。

    2. 对于多了索引,不是使用的第一部分,则不会使用索引。

    3. Like 查询以%开头,不使用索引

    4. 存在索引列的数据类型隐形转换,则用不上索引,比如列类型是

      字符串,那一定要在条件中将数据使用引号引用起来,否则不使

      用索引

    5. Where 子句里对索引列上有数学运算,用不上索引

    6. Where 子句中对索引列使用函数,用不上索引

    7. Mysql 估计使用全表扫描要比用索引快,不使用索引

    8. 数据唯一性差的字段不要使用索引

    9. 频繁更新的字段不要使用索引

    10. 字段不在 where 语句中出现时不要添加索引,如果 where 后含

      IS NULL/IS NOT NULL/LIKE ‘%输入符%’等条件,不要使用索引

    11. Where 子句里对索引使用不等于(<>),不建议使用索引,效果一般


26:mysql 优化会不会,mycat 分库,垂直分库,水平分库?

  1. (1)  为查询缓存优化你的查询

  2. (2)  EXPLAIN select 查询:explain 的查询结果会告诉你索引主键是如何被利用的

  3. (3)  只需要一行数据时使用limit1

  4. (4)  为搜索字段添加索引

  5. (5)  在关联表的时候使用相当类型的列,并将其索引

  6. (6)  千万不要ORDERBY RAND()

  7. (7)  避免select*

  8. (8)  永远为每张表设置一个ID

    (9)  使用ENUM而不是VARCHAR

    (10)  从 PROCEDURE ANALYS()提取建议

    (11)  尽可能的使用 NOT NULL

    (12)  Java 中使用 Prepared Statements

    (13)  无缓冲的查询

    (14)  把 IP 地址存成 UNSIGNED INT

    (15)  固定表的长度

    (16)  垂直分库:“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以

    降低表的复杂度和字段的数目,从而达到优化的目的。

    (17)  水平分库:“水平分割”是一种把数据库中的表按行变成几张表的方法,这样可以

    降低表的复杂度和字段的数目,从而达到优化的目的。
  9. (18)  越小的列会越快

  10. (19)  选择正确的存储引擎

  11. (20)  使用一个对象关系映射器

  12. (21)  小心永久链接

  13. (22)  拆分大的 DELETE 或 INSERT 语句


27:分布式事务解决方案?

(1) 什么是分布式事务?

  1. a)  例如:当本地事务数据库断电的这种情况,如何保证数据一致性?

  2. 数据库由两个文件组成的,一个数据库文件和一个日志文件,数据库任何写入操作都要先写日志,在操作前会把日志文件写入磁盘,那么断电的时候即使才做没有完成,在重启数据库的时候,数据库会根据当前数据情况进行 undo 回滚或 redo 前滚, 保证了数据的强一致性。

  3. b)  分布式理论:当单个数据库性能产生瓶颈的时候,可能会对数据库进行分区(物理分区),分区之后不同的数据库不同的服务器上 ,此时单个数据库的ACID不适应这种情况啊,在此集群环境下很难达到集群的 ACID,甚至效率性能大幅度下降,重要的是再很难扩展新的分区了。此时就需要引用一个新的理论来适应这种集群情况:CAP 定理

  4. c)  CAP 定理:由加州肚饿伯克利分销 Eric Brewer 教授提出,指出 WEB服务无法同时满足 3 个属性:

  5. 具体的将在分布式系统中,在任何数据库设计中,一个 WEB 应至多只能同时支持上面两个属性。设计人员必须在一致性和可用性之间做出选择。

  • 一致性:客户端知道一系列的操作都会同时发生(生效)

  • 可用性:每个操作都必须以可预期的响应结束

  • 分区容错性:及时出现单组件无法可用,操作依然可以完成。

  • d) BASE 理论:分布式系统中追求的是可用性,比一致性更加重要, BASE 理论来实现高可用性。核心思想是:我们无法做到全一致,单每个应用都可以根据自身的业务特点,采用适当的方式使系统达到最终一致性。

  •     e) 数据库事务特性:ACID 

            i. 原子性

            ii. 一致性
            iii. 独立性或隔离性 

            iv. 持久性

    (2) 分布式系统中,实现分布式事务的解决方案:

    1. 两阶段提交 2PC

    2. 补偿事务 TCC

    3. 本地消息表(异步确保)

    4. MQ 事务消息

    5. Sagas 事务模型


    28:sql 语句优化会不会,说出你知道的?

    1. (1)  避免在列上做运算,可能会导致索引失败

    2. (2)  使用join时应该小结果集驱动大结果集,同时把复杂的join查询拆分成多个query,不然 join 越多表,会导致越多的锁定和堵塞。

    3. (3)  注意like模糊查询的使用,避免使用%%

    4. (4)  不要使用select*节省内存

    5. (5)  使用批量插入语句,节省交互

    6. (6)  Limit 基数比较大时,使用 between and

    7. (7)  不要使用rand函数随机获取记录

    8. (8)  避免使用null,建表时,尽量设置notnul,提高查询性能

    9. (9)  不要使用count(id),应该使用count(*)

    10. (10) 不要做无谓的排序,尽可能在索引中完成排序

        (11) From语句中一定不要使用子查询
        (12) 使用更多的 where 加以限制,缩小查找范围

        (13) 合理运用索引
        (14) 使用 explain 查看 sql 性能


    29:mysql 的存储引擎了解过没有? 

    (1) MySQL 存储引擎种类:

    (2) 事务处理:

    在整个流程中出现任何问题,都能让数据回滚到最开始的状态,这种处

    理方式称之为事务处理。也就是说事务处理要么都成功,要么的失败。


    30:红黑树原理?

    红黑树是一个二叉搜索树。在每个节点增加了一个存储位记录节点的颜色,可以是RED,也可以是BLACK,通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的两倍,加以平衡。性质如下:

    1. 每个节点颜色不是黑色就是红色

    2. 根节点的颜色是黑色的

    3. 如果一个节点是红色,那么他的两个子节点就是黑色的,没有持续的红节点

    4. 对于每个节点,从该节点到其后代叶节点的简单路径上, 均包含相同数目的黑色节点。


    31:说说springcloud的⼯作原理

    springcloud由以下⼏个核心组件构成:

    Eureka:

    各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从⽽而知道其他服务在哪⾥ 

    Ribbon:

    服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择⼀台 Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求 

    Hystrix:

    发起请求是通过Hystrix的线程池来⾛的,不同的服务⾛不同的线程池,实现了了不同服务调⽤的隔离,避免了服务雪崩的问题
    Zuul:

    如果前端、移动端要调⽤后端系统,统⼀从Zuul⽹关进入,由Zuul网关转发请求给对应的服务


    32:⽤什么组件发请求
    在Spring Cloud中使⽤Feign, 我们可以做到使用HTTP请求远程服务时能与调⽤本地方法一样的编码体验,开发者完全感知不到
    这是远程⽅法,更感知不到这是个HTTP请求。 


    33:注册中⼼心跳是几秒

    1、Eureka的客户端默认每隔30s会向eureka服务端更新实例,注册中心也会定时进行检查,发现某个实例默认90s内没有再收到心跳,会注销此实例,这些时间间隔是可配置的。

    2、不过注册中⼼还有一个保护模式(服务端在短时间内丢失过多客户端的时候,就会进入保护模式),在这个保护模式下,他会认为是网络问题,不会注销任何过期的实例。


    34:消费者是如何发现服务提供者的

    a. 当⼀个服务实例启动,会将它的ip地址等信息注册到eureka;
    b. 当a服务调用b服务,a服务会通过Ribbon检查本地是否有b服务实例信息的缓存; 

    c. Ribbon会定期从eureka刷新本地缓存。



    35:多个消费者调用同一接口,eruka默认的分配⽅式是什什么
    a. RoundRobinRule:轮询策略

    Ribbon以轮询的⽅式选择服务器,这个是默认值。所以示例中所启动的两个服务会被循环访问; 

    b. RandomRule:随机选择

    也就是说Ribbon会随机从服务器列表中选择一个进行访问;
    c. BestAvailableRule:最⼤可用策略

    即先过滤出故障服务器后,选择⼀个当前并发请求数最小的;
    d. WeightedResponseTimeRule:带有加权的轮询策略

    对各个服务器响应时间进行加权处理,然后在采用轮询的⽅式来获取相应的服务器器;
    e. AvailabilityFilteringRule:可用过滤策略

    先过滤出故障的或并发请求⼤于阈值一部分服务实例,然后再以线性轮询的⽅方式从过滤后的实例清单中选出⼀一个;
    f. ZoneAvoidanceRule:区域感知策略

    先使⽤主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进⾏过滤,判断最⼩过滤数(默认1)和最小过滤百分比(默认0),最后对满⾜条件的服务器则使用RoundRobinRule(轮询⽅方式)选择一个服务器实例。


    36:说说常⽤的springboot注解,及其实现? 

    a. @Bean:注册Bean

        i. 默认使⽤⽅法名作为id,可以在后面定义id如@Bean("xx"); 

        ii. 默认为单例。
        iii. 可以指定init方法和destroy方法:

           1.对象创建和赋值完成,调⽤初始化方法;
           2.单实例bean在容器销毁的时候执行destroy方法; 

           3.多实例bean,容器关闭是不会调用destroy方法。

    b. @Scope:Bean作用域

        i. 默认为singleton; 

        ii. 类型:

            1. singleton单实例(默认值):ioc容器启动时会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器中拿实例;
            2. prototype多实例:ioc容器启动不会创建对象,每次获取时才会调⽤⽅法创建实例;
            3. request同一次请求创建一个实例;

            4. session同一个session创建一个实例。 

    c. @Value:给变量赋值

         i. 代码:

    方式:
    1. 基本数字

    2. 可以写SpEL(Spring EL表达式):#{}

    3. 可以写${},取出配置文件中的值(在运⾏环境变量里⾯的值) 

    d. @Autowired:⾃动装配

    i. 默认优先按照类型去容器中找对应的组件:

    BookService bookService 

    = applicationContext.getBean(BookService.class); 

    ii. 默认⼀定要找到,如果没有找到则报错。可以使用@Autowired(required = false)标记bean为⾮必须的。
    iii. 如果找到多个相同类型的组件,再根据属性名称去容器中查找。
    iv. @Qualifier("bookDao2")明确的指定要装配的bean。

    v. @Primary:让spring默认装配首选的bean,也可以使用@Qualifier()指定要装配的bean。 

    e. @Profile:环境标识

    i. Spring为我们提供的可以根据当前环境动态的激活和切换⼀系列组件的功能;
    ii. @Profile指定组件在哪个环境才能被注册到容器中,默认为"default",如@Profile("default")。 

    iii. 激活⽅式:

    1. 运行时添加虚拟机参数:-Dspring.profiles.active=test

    2. 代码⽅式


    37:spring的事务注解是什么?什么情况下事物才会回滚

    a. spring事务实现机制:

    b. 事务注解@transactional;
    c. 默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务。
    d. @Transactional 只能应⽤到 public ⽅法才有效:只有@Transactional 注解应用到 public ⽅法,才能进行事务管理。这是因为 在使⽤ Spring AOP 代理时,Spring 在调用在图 1 中的 TransactionInterceptor 在⽬标⽅法执行前后进⾏拦截之前, DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept ⽅法或 JdkDynamicAopProxy 的 invoke ⽅法会间接调⽤ AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取表 1. @Transactional 注解的事务属性配置属性信息) 的 computeTransactionAttribute ⽅法。


    38:说说spring事物的传播性和隔离级别 

    a. 七个事务传播属性:

    1. PROPAGATION_REQUIRED -- ⽀持当前事务,如果当前没有事务,就新建⼀个事务。这是最常⻅的选择。 

    2. PROPAGATION_SUPPORTS -- ⽀持当前事务,如果当前没有事务,就以⾮事务⽅式执行。
    3. PROPAGATION_MANDATORY -- ⽀持当前事务,如果当前没有事务,就抛出异常。
    4. PROPAGATION_REQUIRESNEW -- 新建事务,如果当前存在事务,把当前事务挂起。

    5. PROPAGATION_NOTSUPPORTED -- 以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。 

    6. PROPAGATION_NEVER -- 以⾮事务⽅式执行,如果当前存在事务,则抛出异常。
    7. PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执⾏行行。如果当前没有事务,则进行与 PROPAGATION_REQUIRED类似的操作。

    b. 五个隔离级别:
    1. ISOLATION_DEFAULT 这是⼀个PlatfromTransactionManager默认的隔离级别,使⽤数据库默认的事务隔离级别.
    2. 另外四个与JDBC的隔离级别相对应:
    3. ISOLATION_READUNCOMMITTED 这是事务最低的隔离级别,它充许别外⼀一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。
    4. ISOLATION_READCOMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。
    5. ISOLATION_REPEATABLEREAD 这种事务隔离级别可以防⽌脏读,不可重复读。但是可能出现幻读。它除了保证一个事务 不能读取另一个事务未提交的数据外,还保证了避免不可重复读。
    6. ISOLATION_SERIALIZABLE 这是花费最⾼代价但是最可靠的事务隔离级别。事务被处理为顺序执⾏。除了防⽌脏读,不可重复读外,还避免了幻读。

    c. 关键词:

    1、脏读(新增或删除):

    脏读就是指当⼀个事务正在访问数据,并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据;

    2、不不可重复读(修改):是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样 的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(解决:只有在修改事务完全提交之后才可以读取数据,则可以避免该问题);

    3、幻读(新增或删除):是指当事务不是独⽴执行时发生的一种现象,例如第⼀一个事务对一个表中的数据进⾏了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插⼊一行新数据。那么,以后就会发生操作第一个事务的⽤户发现表中还有没有修改的数据行,就好象发生了了幻觉一样(解决:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题)。


    39:mysql的引擎有什么?他们的区别 

    a. InnoDB:

    1. ⽀持事务处理
    2. ⽀持外键
    3. ⽀持⾏锁
    4. 不⽀持FULLTEXT类型的索引(在Mysql5.6已引入) 

    5. 不保存表的具体行数,扫描表来计算有多少行

    6. 对于AUTO_INCREMENT类型的字段,必须包含只有该字段的索引 

    7. DELETE 表时,是⼀⾏⼀行的删除

    8. InnoDB 把数据和索引存放在表空间⾥面

    9. 跨平台可直接拷⻉使⽤

    10. 表格很难被压缩 

    b. MyISAM:

    1. 不⽀持事务,回滚将造成不完全回滚,不具有原⼦性 

    2. 不支持外键
    3. ⽀持全文搜索
    4. 保存表的具体行数,不带where时,直接返回保存的⾏数 

    5. DELETE 表时,先drop表,然后重建表

    6. MyISAM 表被存放在三个文件 。frm 文件存放表格定义。数据⽂文件是MYD (MYData) 。索引⽂件是MYI (MYIndex)

    7. 跨平台很难直接拷⻉
    8. AUTO_INCREMENT类型字段可以和其他字段一起建立联合索引
    9. 表格可以被压缩

    c. 选择:

    因为MyISAM相对简单所以在效率上要优于InnoDB.如果系统读多,写少。对原⼦子性要求低。那么MyISAM最好的选择。且MyISAM恢复速度快。可直接⽤备份覆盖恢复。如果系统读少,写多的时候,尤其是并发写⼊⾼的时候。InnoDB就是⾸选了。两种类型都有⾃己优缺点。


    40:innodb如何实现mysql的事务

    事务进⾏过程中,每次sql语句执行,都会记录undo log和redo log,然后更新数据形成脏页,然后redo log按照时间或者空间等条件进⾏落盘,undo log和脏⻚按照checkpoint进⾏落盘,落盘后相应的redo log就可以删除了。此时,事务还未 COMMIT,如果发⽣崩溃,则首先检查checkpoint记录,使⽤相应的redo log进⾏行数据和undo log的恢复,然后查看undo log的状态发现事务尚未提交,然后就使用undo log进行事务回滚。事务执行COMMIT操作时,会将本事务相关的所有redo log都进⾏落盘,只有所有redo log落盘成功,才算COMMIT成功。然后内存中的数据脏页继续按照checkpoint进⾏落盘。如果此时发⽣了崩溃,则只使用redo log恢复数据。

    文章转载自萌小璐,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论