基础
认证和授权区别是什么?
Authentication(认证) 是验证您的身份的凭据(例如用户名/用户ID和密码) , 通过这个凭据, 系统得以知道你就是你, 也就是说系统存在你这个用户。所以, Authentication被称为身份/用户验证。
Authorization(授权) 发生在Authentication(认证) 之后。授权, 它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如admin, 有些对系统资源操作比如删除、添加、更新只能特定人才具有。
这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。
Cookie和Session有什么区别?如何使用Session进行身份认证?
Session的主要作用就是通过服务端记录用户的状态。
Cookie数据保存在客户端(浏览器端) , Session数据保存在服务器端。相对来说Session安全性更高。如果使用Cookie的话, 一些敏感信息不要写入Cookie中, 最好能将Cookie信息加密然后使用到的时候再去服务器端解密。
那么, 如何使用Session进行身份验证?
很多时候我们都是通过SessionID来指定特定的用户, SessionID一般会选择存放在服务端。举个例子:用户成功登陆系统, 然后返回给客户端具有SessionID的Cookie, 当用户向后端发起请求的时候会把SessionID带上, 这样后端就知道你的身份状态了。关于这种认证方式更详细的过程如下:
用户向服务器发送用户名和密码用于登陆系统。
服务器验证通过后, 服务器为用户创建一个Session, 并将Session信息存储起来。
服务器向用户返回一个SessionID, 写入用户的Cookie。
当用户保持登录状态时, Cookie将与每个后续请求一起被发送出去。
服务器可以将存储在Cookie上的SessionID与存储在内存中或者数据库中的Session信息进行比较, 以验证用户的身份,返回给用户客户端响应信息的时候会附带用户当前的状态。
使用Session的时候需要注意下面几个点:
依赖Session的关键业务一定要确保客户端开启了Cookie。
注意Session的过期时间
什么是Token?什么是JWT?如何基于Token进行身份验证?
JWT:JSON Web Token
JWT本质上就一段签名的JSON格式的数据。由于它是带有签名的, 因此接收者便可以验证它的真实性。
Token需要查库验证token 是否有效,而JWT不用查库,直接在服务端进行校验,因为用户的信息及加密信息,和过期时间,都在JWT里,只要在服务端进行校验就行,并且校验也是JWT自己实现的。
为什么Cookie无法防止CSR F攻击, 而token可以?
为什么Cookie无法防止CSR F攻击, 而token可以?
CSR F(Cross Site Request Forgery) 一般被翻译为跨站请求伪造。那么什么是跨站请求伪造呢?说简单一点用你的身份去发送一些对你不友好的请求。举个简单的例子:
小壮登录了某网上银行,他来到了网上银行的帖子区,看到一个帖子下面有一个链接写着"科学理财,年收益率70%",小壮好奇的点开了这个链接,结果发现自己的账户少了10000元。这是这么回事呢?原来黑客在链接中藏了一个请求, 这个请求直接利用小壮的身份给银行发送了一个转账请求, 也就是通过你的Cookie向银行发出请求。
<a src=http://www.mybank.com/Transfer?bankld=11&money=10000>科学理财, 年收益率70%</>, 原因是进行Session认证的时候, 我们一般使用Cookie来存储Session ld, 当我们登陆后后端生成一个Session ld放在Cookie中返回给客户端, 服务端通过Red is或者其他存储工具记录保存着这个Sessionid, 客户端登录以后每次请求都会带上这个Session ld, 服务端通过这个Session ld来标示你这个人。如果别人通过cookie拿到了Session ld后就可以代替你的身份访问系统了。
Session认证中Cookie中的Session ld是由浏览器发送到服务端的, 借助这个特性, 攻击者就可以通过让用户误点攻击链接,达到攻击效果。
但是, 我们便用token的话就不会存在这个问题, 在我们登录成功获得token之后, 一般会选择存放在localstorage中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个token, 这样就不会出现CSR F漏洞的问题。因为, 即使有个你点击了非法链接发送了请求到服务端, 这个非法请求是不会携带token的, 所以这个请求将是非法的。
框架
介绍下Spring、SpringMVC、SpringBoot
spring和spring Mvc:
1.spring是一个一站式的轻量级的java开发框架, 核心是控制反转(IOC) 和面向切面(AOP) , 针对于开发的WEB层(spring Mvc) 、业务层(loc) 、持久层(jdbc Template) 等都提供了多种配置解决方案;
2.spring Mvc是spring基础之上的一个MVC框架, 主要处理web开发的路径映射和视图渲染, 属于spring框架中
WEB层开发的一部分;
spring Mvc和spring Boot:
1、spring Mvc属于一个企业WEB开发的MVC框架, 涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等, XML、config等配置相对比较繁琐复杂;
2、spring Boot框架相对于spring Mvc框架来说, 更专注于开发微服务后台接口, 不开发前端视图, 同时遵循默认优于配置, 简化了插件配置流程, 不需要配置xml, 相对spring mvc, 大大简化了配置流程;
总结:
1、Spring框架就像一个家族, 有众多衍生产品例如boot、security、jpa等等。但他们的基础都是Spring的ioc、aop等.ioc提供了依赖注入的容器,aop解决了面向横切面编程, 然后在此两者的基础上实现了其他延伸产品的高级功能;
2、spring Mvc主要解决WEB开发的问题, 是基于Servlet的一个MVC框架, 通过XML配置, 统一开发前端视图和后端逻辑;3、由于Spring的配置非常复杂, 各种XML、Java Config、servlet处理起来比较繁琐, 为了简化开发者的使用,从而创造性地推出了spring Boot框架, 默认优于配置, 简化了spring Mvc的配置流程; 但区别于spring Mvc的是,spring Boot专注于单体微服务接口开发, 和前端解耦, 虽然spring Boot也可以做成spring Mvc前后台一起开发,但是这就有点不符合spring Boot框架的初衷了;
SpringBoot自动配置原理是什么?
# 移动到后面
SpringMvc工作流程是什么?
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。
1、Dispatcher Servlet表示前置控制器, 是整个Spring MVC的控制中心。用户发出请求, Dispatcher Servlet接收请求并拦截请求。
2、Handler Mapping为处理器映射。Dispatcher Servlet调用Handler Mapping, Handler Mapping根据请求url查找Handler。
3、返回处理器执行链, 根据url查找控制器, 并且将解析后的信息传递给Dispatcher Servlet
4、Handler Adapter表示处理器适配器, 其按照特定的规则去执行Handler。
5、执行handler找到具体的处理器
6、Controller将具体的执行信息返回给Handler Adapter, 如Model And View。
7、Handler Adapter将视图逻辑名或模型传递给Dispatcher Servlet。
8、Dispatcher Servlet调用视图解析器(View Resolver) 来解析Handler Adapter传递的逻辑视图名。
9、视图解析器将解析的逻辑视图名传给Dispatcher Servlet。
10、Dispatcher Servlet根据视图解析器解析的视图结果, 调用具体的视图, 进行试图渲染
11、将响应数据返回给客户端
SpringMVC 的九大组件有哪些?
1.Handler Mapping
根据request找到相应的处理器。因为Handler(Controller) 有两种形式, 一种是基于类的Handler, 另一种是基于Method的Handler(也就是我们常用的)
2.Handler Adapter
调用Handler的适配器。如果把Handler(Controller) 当做工具的话, 那么Handler Adapter就相当于干活的工人
3.Handler Exception Resolver
对异常的处理
4.View Resolver
用来将String类型的视图名和Locale解析为View类型的视图
5.Request To View Name Translator
有的Handler(Controller) 处理完后没有设置返回类型, 比如是void方法, 就需要从request中获取view Name
6.Locale Resolver
从request中解析出Locale。Locale表示一个区域, 比如zh-cn, 对不同的区域的用户, 显示不同的结果, 这就是i18n(Spring MVC中有具体的拦截器Locale Change Interceptor)
7.Theme Resolver
主题解析, 这种类似于我们手机更换主题, 不同的UI, css等
8.Multipart Resolver
处理上传请求, 将普通的request封装成Multipart HttpServletRequest
9.Flash Map Manager
用于管理Flash Map, Flash Map用于在redirect重定向中传递参数
Spring的核心是什么?
spring是一个开源框架,为了简化企业开发而生的, 使得开发变得更加优雅和简洁。
spring是一个IOC和AOP的容器框架。
IOC:控制反转(DI是一种实现)
AOP:面向切面编程
容器:包含并管理应用对象的生命周期, 就好比用桶装水一样, spring就是桶, 而对象就是水
Spring是如何简化开发的?使用Spring的优势?
基于POJO的轻量级和最小侵入性编程
通过依赖注入和面向接口实现松耦合
基于切面和惯例进行声明式编程
通过切面和模板减少样板式代码1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发
2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈, 它将Spring扩展到不同的领域, 如Web服务、REST、移动开发以及No SQL
3、低侵入式设计,代码的污染极低
4、独立于各种应用服务器, 基于Spring框架的应用, 可以真正实现Write Once, Run Anywhere的承诺
5、Spring的loC容器降低了业务对象替换的复杂性, 提高了组件之间的解耦
6、Spring的AOP支持允许将一-些通用任务如安全、事务、日志等进行集中式处理, 从而提供了更好的复用
7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合, 并简化了底层的数据库访问
8、Spring的高度开放性, 并不强制应用完全依赖于Spring, 开发者可自由选用Spring框架的部分或全部
Spring的事务传播机制是什么?
多个事务方法相互调用时, 事务如何在这些方法之间进行传播, spring中提供了7中不同的传播特性, 来保证事务的正常执行:
REQUIRED:默认的传播特性, 如果当前没有事务, 则新建一个事务, 如果当前存在事务, 则加入这个事务
SUPPORTS:当前存在事务, 则加入当前事务, 如果当前没有事务, 则以非事务的方式执行
MANDATORY:当前存在事务, 则加入当前事务, 如果当前事务不存在, 则抛出异常
REQUIRED_NEW:创建一个新事务, 如果存在当前事务, 则挂起该事务
NOT_SUPPORTED:以非事务方式执行, 如果存在当前事务, 则挂起当前事务
NEVER:不使用事务, 如果当前事务存在, 则抛出异常
NESTED:如果当前事务存在, 则在嵌套事务中执行, 否则REQUIRED的操作一样
NESTED和REQUIRED_NEW的区别:
REQUIRED_NEW是新建一个事务并且新开始的这个事务与原有事务无关, 而NESTED则是当前存在事务时会开启一个嵌套事务, 在NESTED情况下, 父事务回滚时, 子事务也会回滚, 而REQUIRED_NEW情况下, 原有事务回滚,不会影响新开启的事务
NESTED和REQUIRED的区别:
REQUIRED情况下, 调用方存在事务时, 则被调用方和调用方使用同一个事务, 那么被调用方出现异常时, 由于共用一个事务, 所以无论是否catch异常, 事务都会回滚, 而在NESTED情况下, 被调用方发生异常时, 调用方可以catch其异常,这样只有子事务回滚,父事务不会回滚。
Spring事务的实现方式原理是什么?
在使用Spring框架的时候, 可以有两种事务的实现方式, 一种是编程式事务, 有用户自己通过代码来控制事务的处理逻辑, 还有一种是声明式事务, 通过@Transactional注解来实现。
其实事务的操作本来应该是由数据库来进行控制, 但是为了方便用户进行业务逻辑的操作, spring对事务功能进行了扩展实现, 一般我们很少会用编程式事务, 更多的是通过添加@Transactional注解来进行实现, 当添加此注解之后事务的自动功能就会关闭, 有spring框架来帮助进行控制。
其实事务操作是AOP的一个核心体现, 当一个方法添加@Transactional注解之后, spring会基于这个类生成一个代理对象, 会将这个代理对象作为bean, 当使用这个代理对象的方法的时候, 如果有事务处理, 那么会先把事务的自动提交给关系,然后去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会直接提交,如果出现任何异常情况,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚操作。
TransactionInterceptor
Spring事务什么时候会失效?
1、bean对象没有被spring容器管理
2、方法的访问修饰符不是public
3、自身调用问题
4、数据源没有配置事务管理器
5、数据库不支持事务
6、异常被捕获(识别不到,会正常提交。因此要想明白异常在哪里处理、怎么处理)
7、异常类型错误或者配置错误
Spring支持的Bean作用域有哪些?
①singleton
使用该属性定义Bean时, IOC容器仅创建一个Bean实例, IOC容器每次返回的是同一个Bean实例。
②prototype
使用该属性定义Bean时, IOC容器可以创建多个Bean实例, 每次返回的都是一个新的实例。
③request
该属性仅对HTTP请求产生作用, 使用该属性定义Bean时, 每次HTTP请求都会创建一个新的Bean, 适用于Web ApplicationContext环境。
④session
该属性仅用于HTTPSession, 同一个Session共享一个Bean实例。不同Session使用不同的实例。
⑤global-session
该属性仅用于HTTPSession, 同session作用域不同的是, 所有的Session共享一个Bean实例。
# @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
什么是Bean的自动装配?有哪些方式?
bean的自动装配指的是bean的属性值在进行注入的时候通过某种特定的规则和方式去容器中查找, 并设置到具体的对象属性中,主要有五种方式:
no-缺省情况下, 自动配置是通过"ref"属性手动设定, 在项目中最常用
by Name-根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的, 将会自装配它。
by Type-按数据类型自动装配, 如果bean的数据类型是用其它bean属性的数据类型, 兼容并自动装配它。
constructor-在构造函数参数的by Type方式。
autodetect-如果找到默认的构造函数, 使用“自动装配用构造"; 否则, 使用“按类型自动装配"。
Spring Bean 的生命周期
1、实例化bean对象
通过反射的方式进行对象的创建,此时的创建只是在堆空间中申请空间,属性都是默认值
2、设置对象属性
给对象中的属性进行值的设置工作
3、检查Aware相关接口并设置相关依赖
如果对象中需要引用容器内部的对象, 那么需要调用aware接口的子类方法来进行统一的设置
4、Bean PostProcessor的前置处理
对生成的bean对象进行前置的处理工作
5、检查是否是Initializing Bean的子类来决定是否调用after Properties Set方法
判断当前bean对象是否设置了Initializing Bean接口, 然后进行属性的设置等基本工作
6、检查是否配置有自定义的in it-method方法
如果当前bean对象定义了初始化方法, 那么在此处调用初始化方法
7、Bean PostProcessor后置处理
对生成的bean对象进行后置的处理工作
8、注册必要的Destruction相关回调接口
为了方便对象的销毁,在此处调用注销的回调接口,方便对象进行销毁操作
Spring框架中的单例Bean 是线程安全的么?
Spring中的Bean对象默认是单例的, 框架并没有对bean进行多线程的封装处理
如果Bean是有状态的, 那么就需要开发人员自己来保证线程安全的保证, 最简单的办法就是改变bean的作用域把singleton改成prototype, 这样每次请求bean对象就相当于是创建新的对象来保证线程的安全有状态就是由数据存储的功能
无状态就是不会存储数据, 你想一下, 我们的controller, service和dao本身并不是线程安全的, 只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制遍历,这是自己线程的工作内存,是最安全的。
因此在进行使用的时候, 不要在bean中声明任何有状态的实例变量或者类变量, 如果必须如此, 也推荐大家使用Thread Local把变量变成线程私有, 如果bean的实例变量或者类变量需要在多个线程之间共享, 那么就只能使用synchronized, lock, cas等这些实现线程同步的方法了。
Spring框架中使用了哪些设计模式及其应用场景?
1.工厂模式, 在各种BeanFactory以及ApplicationContext创建中都用到了
2.模版模式, 在各种BeanFactory以及ApplicationContext实现中也都用到了(子类继承,扩展实现)
3.代理模式, Spring AOP利用了AspectJ AOP实现的!AspectJ AOP的底层用了动态代理
4.策略模式, 加载资源文件的方式, 使用了不同的方法, 比如:ClassPathResourece,
FileSystemResource, ServletContextResource, UrlResource但他们都有共同的接口Resource; 在Aop的实现中, 采用了两种不同的方式, JDK动态代理和CGLIB代理
5.单例模式, 比如在创建bean的时候。
6.观察者模式, spring中的ApplicationEvent, ApplicationListener, ApplicationEvent Publisher
7.适配器模式, MethodBeforeAdviceAdapter, ThrowsAdviceAdapter, AfterReturning Adapter
8.装饰者模式, 源码中类型带Wrapper或者Decorator的都是
如何理解SpringBoot 中的Starter?
使用spring+spring mvc框架进行开发的时候, 如果需要引入mybatis框架, 那么需要在xml中定义需要的bean对象, 这个过程很明显是很麻烦的, 如果需要引入额外的其他组件, 那么也需要进行复杂的配置, 因此在spring boot中引入了starter
starter就是一个jar包, 写一个@Configuration的配置类, 将这些bean定义在其中, 然后再starter包的META-INF/spring.factories中写入配置类, 那么spring boot程序在启动的时候就会按照约定来加载该配置类
开发人员只需要将相应的starter包依赖进应用中, 进行相关的属性配置, 就可以进行代码开发, 而不需要单独进行bean对象的配置
如何实现一个IOC容器
IOC(Inversion of Control) , 意思是控制反转, 不是什么技术, 而是一种设计思想, IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
在传统的程序设计中, 我们直接在对象内部通过new进行对象创建, 是程序主动去创建依赖对象, 而IOC是有专门的容器来进行对象的创建, 即IOC容器来控制对象的创建。
在传统的应用程序中,我们是在对象中主动控制去直接获取依赖对象,这个是正转,反转是由容器来帮忙创建及注入依赖对象,在这个过程过程中,由容器帮我们查找级注入依赖对象,对象只是被动的接受依赖对象。
1、先准备一个基本的容器对象, 包含一些map结构的集合, 用来方便后续过程中存储具体的对象
2、进行配置文件的读取工作或者注解的解析工作, 将需要创建的bean对象都封装成Bean Definition对象存储在容器中
3、容器将封装好的Bean Definition对象通过反射的方式进行实例化, 完成对象的实例化工作
4、进行对象的初始化操作,也就是给类中的对应属性值就行设置,也就是进行依赖注入,完成整个对象的创建, 变成一个完整的bean对象, 存储在容器的某个map结构中
5、通过容器对象来获取对象,进行对象的获取和逻辑处理工作
6、提供销毁操作,当对象不用或者容器关闭的时候,将无用的对象进行销毁
什么是嵌入式服务器?为什么使用嵌入式服务器?
在spring boot框架中, 大家应该发现了有一个内嵌的tomcat, 在之前的开发流程中, 每次写好代码之后必须要将项目部署到一个额外的web服务器中, 只有这样才可以运行, 这个明显要麻烦很多, 而使用spring boot的时候, 你会发现在启动项目的时候可以直接按照java应用程序的方式来启动项目, 不需要额外的环境支持, 也不需要tomcat服务器, 这是因为在spring boot框架中内置了tomcat.jar, 来通过main方法启动容器, 达到一键开发部署的方式,不需要额外的任何其他操作。
AOP的理解?
AOP全称叫做Aspect Oriented Programming面向切面编程。它是为解耦而生的, 解耦是程序员编码开发过程中一直追求的境界, AOP在业务类的隔离上, 绝对是做到了解耦, 在这里面有几个核心的概念:
切面(Aspect) :指关注点模块化, 这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。在Spring AOP中, 切面可以使用通用类基于模式的方式(schema-based approach) 或者在普通类中以@Aspect注解(@Aspectj注解方式) 来实现。
连接点(Join point) :在程序执行过程中某个特定的点, 例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中, 一个连接点总是代表一个方法的执行。
通知(Advice) :在切面的某个特定的连接点上执行的动作。通知有多种类型, 包括"around", “before"and“after"等等。通知的类型将在后面的章节进行讨论。许多AOP框架, 包括Spring在内, 都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
切点(Pointcut) :匹配连接点的断言。通知和切点表达式相关联, 并在满足这个切点的连接点上运行(例如, 当执行某个特定名称的方法时) 。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
引入(Introduction) :声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现) 到任何被通知的对象上。例如, 可以使用引入来使bean实现IS Modified接口, 以便简化缓存机制(在Aspectj社区, 引入也被称为内部类型声明(inter) ) 。
目标对象(Target object) :被一个或者多个切面所通知的对象。也被称作被通知(advised) 对象。既然Spring AOP是通过运行时代理实现的, 那么这个对象永远是一个被代理(proxied) 的对象。
AOP代理(AOP proxy) :AOP框架创建的对象, 用来实现切面契约(aspect contract)(包括通知方法执行等功能) , 在Spring中, AOP代理可以是JDK动态代理或CGLIB代理织入(Weaving) :把切面连接到其它的应用程序类型或者对象上, 并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器) 、类加载时或运行时中完成。Spring和其他纯Java AOP框架一样,是在运行时完成织入的。
这些概念都太学术了,如果更简单的解释呢,其实非常简单:
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加途样的代码,很明显代码冗余太多,因此我们需要将这些公共的代码逻辑抽象出来变成一个切面, 然后注入到目标对象(具体业务) 中去, AOP正是基于这样的一个思路实现的, 通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。
中间件
数据库
事务隔离级别?
事务并发带来的问题:
更新丢失
对于同一行数据,A事务对该行数据的更新操作覆盖了B事务对该行数据的更新操作
脏读
A事务读取了B事务未提交的数据
不可重复读
A事务使用相同的查询语句在不同时刻读取到的结果不一致(主要针对更新删除)
幻读
A事务两次读取一个范围的数据记录,两次结果不一致(主要针对插入)
使用如下命令可以查询全局级别和会话级别的事务隔离级别:
select @@GLOBAL.tx_isolation;
select @@session.tx_isolation;
select @@tx_isolation;
READ-UNCOMMITTED读未提交
READ-COMMITTED读已提交
REPEATABLE-READ 可重复读(默认隔离级别)
SERIALIZABLE 串行化
# 使用SET TRANSACTION 改变单个或者所有新连接的事务隔离级别
设置事务隔离级别:
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
ACID是靠什么来保证的?
事务的四个特点ACID(原子性、一致性、隔离性、持久性)
- 原子性由undolog日志来保证, 它记录了需要回滚的日志信息, 事务回滚时撤销已经执行成功的sql
- 一致性是由其他三大特性保证,程序代码要保证业务上的一致性
- 隔离性是由锁和MVCC来保证
- 持久性由redolog来保证,mysql修改数据的时候会在redolog中记录一份日志数据, 就算数据没有保存成功, 只要日志保存成功了,数据仍然不会丢失
MVCC解决的问题是什么?
Multi-Version Concurrency Control 多版本并发控制
数据库并发场景有三种,分别为:
1、读读:不存在任何问题,也不需要并发控制
2、读写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读、幻读、不可重复读
3、写写:有线程安全问题,可能存在更新丢失问题
MVCC是一种用来解决读写冲突的无锁并发控制, 也就是为事务分配单项增长的时间戳, 为每个修改保存一个版本, 版本与事务时间戳关联, 读操作只读该事务开始前的数据库的快照, 所以MVCC可以为数据库解决一下问题:
1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题
MVCC实现原理是什么?
mvcc的实现原理主要依赖于记录中的三个隐藏字段、 undolog、read view来实现的。
隐藏字段
每行记录除了我们自定义的字段外, 还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR, DB_ROW_ID等字段
DB_TRX_ID
6字节,最近修改事务id,记录创建这条记录或者最后一次修改该记录的事务id
DB_ROLL_PTR
7字节, 回滚指针, 指向这条记录的上一个版本, 用于配合undo log, 指向上一个旧版本
DB_ROW JD
6字节, 隐藏的主键, 如果数据表没有主键, 那么innodb会自动生成一个6字节的row_id
记录如图所示:
undolog被称之为回滚日志, 表示在进行insert, delete, update操作的时候产生的方便回滚的日志
当进行insert操作的时候, 产生的undo log只在事务回滚的时候需要, 并且在事务提交之后可以被立刻丢弃
当进行update和delete操作的时候, 产生的undo log不仅仅在事务回滚的时候需要, 在快照读的时候也需要, 所以不能随便删除, 只有在快照读或事务回滚不涉及该日志时, 对应的日志才会被purge线程统一清除(当数据发生更新和删除操作的时候都只是设置一下老记录的deleted_bit, 并不是真正的将过时的记录删除, 因为为了节省磁盘空间, innodb有专门的purge线程来清除deleted_bit为true的记录, 如果某个记录的deleted_id为true,并且DB_TRX_ID相对于purge线程的read view可见, 那么这条记录一定时可以被清除的)
Read View
Read View是事务进行快照读操作的时候生产的读视图, 在该事务执行快照读的那一刻, 会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。
其实Read View的最大作用是用来做可见性判断的, 也就是说当某个事务在执行快照读的时候, 对该记录创建一个Read View的视图, 把它当作条件去判断当前事务能够看到哪个版本的数据, 有可能读取到的是最新的数据,也有可能读取的是当前行记录的undo log中某个版本的数据。
RC、RR级别下的InnoDB快照读有什么不同
因为Read View生成时机的不同, 从而造成RC、RR级别下快照读的结果的不同
1、在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照即Read View, 将当前系统活跃的其他事务记录起来, 此后在调用快照读的时候, 还是使用的是同一个Read View, 所以只要当前事务在其他事务提交更新之前使用过快照读, 那么之后的快照读使用的都是同一个Read View, 所以对之后的修改不可见
2、在RR级别下, 快照读生成Read View时, Read View会记录此时所有其他活动和事务的快照, 这些事务的修改对于当前事务都是不可见的, 而早于Read V rew创建的事务所做的修改均是可见
3、在RC级别下, 事务中, 每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因。
总结:在RC隔离级别下, 是每个快照读都会生成并获取最新的Read View, 而在RR隔离级别下, 则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View.
索引的设计原则有哪些?
在进行索引设计的时候,应该保证索引字段占用的空间越小越好,这只是一个大的方向,还有一些细节点需要注意下:
1、适合索引的列是出现在where字句中的列, 或者连接子句中指定的列,查询条件中没有的字段不需要创建索引,用不到
2、基数较小的表,索引效果差,没必要创建索引(如:性别只有男女这样的字段)
3、在选择索引列的时候,越短越好,可以指定某些列的一部分,没必要用全部字段的值
4、不要给表中的每一个字段都创建索引,并不是索引越多越好
5、定义有外键的数据列一定要创建索引,可以直接关联查找到数据,不用挨个匹配
6、更新频繁的字段不要有索引
7、创建索引的列不要过多,可以创建组合索引,但是组合索引的列的个数不建议太多。组合索引需要适应最左匹配原则,列越多被匹配到的概率越小,两到三个就可以了
8、大文本、大对象不要创建索引。索引过大会导致B+树变高,增加IO检索次数,效率降低
Myisam和Innodb的区别?
InnoDB存储引擎:主要面向OLTP ( Online Transaction Processing, 在线事务处理) 方面的应用, 是第一个完整支持ACID事务的存储引擎(BDB第一个支持事务的存储引擎, 已经停止开发) 。
特点:
1支持行锁
2支持外键
3支持自动增加列AUTO_INCREMENT属性
4支持事务
5支持MVCC模式的读写
6读的效率低于MYISAM
7.写的效率高优于MYISAM
8.适合频繁修改以及设计到安全性较高的应用
9.清空整个表的时候, Innodb是一行一行的删除,
MyISAM存储引擎:是MySQL官方提供的存储引擎, 主要面向OLAP(Online Analytical Processing, 在线分析处理) 方面的应用。
特点:
1独立于操作系统, 当建立一个MyISAM存储引擎的表时, 就会在本地磁盘建立三个文件, 例如我建立tb_demo表, 那么会生成以下三个文件tb_demo.frm, tb_demo.MYD,tb_demo.MYI
2不支持事务
3支持表锁和全文索引
4MyISAM存储引擎表由MYD和MYI组成, MYD用来存放数据文件, MYI用来存放索引文件。MySQL数据库只缓存其索引文件,数据文件的缓存交给操作系统本身来完成;
5 MySQL 5.0版本开始, MylSAM默认支持256T的单表数据;
6.选择密集型的表:MYISAM存储引擎在筛选大量数据时非常迅速, 这是他最突出的优点
7.读的效率优于InnoDB
8.写的效率低于InnoDB
9.适合查询以及插入为主的应用
10.清空整个表的时候, MYISAM则会新建表
MySQL中索引类型有哪些,以及对数据库的性能的影响?
普通索引:允许被索引的数据列包含重复的值
唯一索引:可以保证数据记录的唯一性
主键索引:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字primarykey来创建
联合索引:索引可以覆盖多个数据列
全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术
索引可以极大地提高数据的查询速度
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能
但是会降低插入、删除、更新表的速度,因为在执行这些写操作的时候,还要操作索引文件
索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大,如果非聚簇索引很多,一旦聚簇索引改变,那么所有非聚簇索引都会跟着变数据跟索引存储在一起的叫聚簇索引,没有存储在一起的叫做非聚簇索引
Innodb 存储引擎在进行数据插入的时候,数据必须要跟某一个索引列存储在一起,这个索引列可以是主键,如果没有主键,选择唯一键,如果没有唯一键,选择6字节的rowid来进行存储
其他索引的叶子节点中存储的数据不再是整行的记录,而是聚簇索引的ID值。
InnoDB既有聚簇索引也有非聚簇索引
MyISAM中数据和索引分开存放,只有非聚簇索引
怎么处理MySQL的慢查询?
1、开启慢查询日志, 准确定位到哪个sql语句出现了问题
2、分析sql语句, 看看是否load了额外的数据, 可能是查询了多余的行并且抛弃掉了, 可能是加载了许多结果中并不需要的列,对语句进行分析以及重写
3、分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引
4、如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。
mysql锁的类型有哪些?
基于锁的属性分类:共享锁(读锁)、排他锁(写锁)。
基于锁的粒度分类:行级锁(innodb) 、表级锁(innodb、myisam) 、页级锁(innodb引擎) 、记录锁、间隙锁、临键锁。
基于锁的状态分类:意向共享锁、意向排它锁。(能提高加锁的效率)
共享锁(share lock) :共享锁又称读锁, 简称S锁; 当一个事务为数据加上读锁之后, 其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
排他锁(exclusive lock) :排他锁又称写锁, 简称X锁; 当一个事务为数据加上写锁时, 其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取,避免了出现脏数据和脏读的问题。
表锁(table lock) :表锁是指上锁的时候锁住的是整个表, 当下一个事务访问该表的时候, 必须等前一个事务释放了锁才能进行对表进行访问;特点:粒度大,加锁简单,容易冲突;
行锁:行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问,特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高
记录锁(Record lock) :记录锁也属于行锁中的一种, 只不过记录锁的范围只是表中的某一条记录, 记录锁是说事务在加锁后锁住的只是表的某一条记录,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题
页锁:页级锁是MysQL中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快, 但冲突多, 行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。特点:开销和加锁时间界于表锁和行锁之间,会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
间隙锁:是属于行锁的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读) 的事务级别中。
临键锁(Next-Keylock) :也属于行锁的一种, 并且它是INNODB的行锁默认算法, 总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。
MySQL为什么需要主从同步?
1、在业务复杂的系统中, 有这么一个情景, 有一句sql语句需要锁表, 导致暂时不能使用读的服务, 那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。
2、做数据的热备
3、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。
分布式微服务
微服务的架构原理是什么?
主要是SOA(面向服务架构)理念,更细小粒度服务的拆分,将功能分解到各个服务当中,从而降低系统的耦合性,并提供更加灵活的服务支持。
ZAB协议是什么?
ZAB协议是paxos协议的简化版本
实现了主要以下几个功能:
1.Zab协议确保那些已经在Leader服务器上提交(Commit) 的事务最终被所有的服务器提交。
2.Zab协议确保再leader重启之后继续同步之前没有完成的数据
3.Zab协议实现了Leader选举
4.zab协议实现了在最快速度同步到半数节点, 并会尽快的把数据同步到所有节点
SpringCloud 和Dubbo有哪些区别?
SpringCloudAlibaba核心组件有哪些?分别什么作用?
监控中心-SpringBoot Admin
链路追踪-skywalking/pinpoint
配置中心-springCloud config /apollo
注册中心的原理是什么?
服务启动后向Eureka注册, Eureka Server会将注册信息向其他Eureka Server进行同步, 当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中获取服务列表来完成服务调用
配置中心的原理是什么?
在服务运行之前,将所需的配置信息从配置仓库拉取到本地服务,达到统一化配置管理的目的
如何实现自动刷新的?
1、配置中心Server端承担起配置刷新的职责
2、提交配置触发post请求给server端的bus/refresh接口
3、server端接收到请求并发送给Spring Cloud Bus总线
4、Spring Cloud bus接到消息并通知给其它连接到总线的客户端
5、其它客户端接收到通知, 请求Server端获取最新配置
6、全部客户端均获取到最新的配置
如何保证数据安全?1.保证容器文件访问的安全性,即保证所有的网络资源请求都需要登录
2.将配置中心里所有配置文件中的密码进行加密,保证其密文性
3.开发环境禁止拉取生产环境的配置文件
Zookeeper和Eureka做注册中心有什么区别?Zookeeper保证的是CP(一致性, 容错性) , 而Eureka则是AP(可用性, 容错性)
还有一个nacos
什么是分布式系统中的幂等?
幕等(idempotent、idem pot ence) 是一个数学与计算机学概念, 常见于抽象代数中。
在编程中,一个幕等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幕等函数,或幕等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
例如, “get Username) 和set True() "函数就是一个幕等函数.更复杂的操作幂等保证是利用唯一交易号(流水号) 实现.我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。
有哪些技术解决方案?
1.查询操作
查询一次和查询多次, 在数据不变的情况下, 查询结果是一样的。select是天然的幕等操作;
2.删除操作
删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个。
3.唯一索引
防止新增脏数据。比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建多个资金账户,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可。
4.token机制
防止页面重复提交。
业务要求:页面的数据只能被点击提交一次;
发生原因:由于重复点击或者网络重发, 或者ng in x重发等情况会导致数据被重复提交;
解决办法:集群环境采用token加red is(red is单线程的, 处理需要排队) ; 单JVM环境:采用token加red is或token加jvm锁。
处理流程:
1.数据提交前要向服务的申请token, token放到red is或jvm内存, token有效时间;
2.提交后后台校验token, 同时删除token, 生成新的token返回。
token特点:要申请, 一次有效性, 可以限流。
注意:red is要用删除操作来判断token, 删除成功代表token校验通过。
对外提供的API接口如何保证幂等?
举例说明:银联提供的付款接口:需要接入商户提交付款请求时附带:source来源, seq序列号。
source+seq在数据库里面做唯一索引, 防止多次付款(并发时, 只能处理一个请求) 。
重点:对外提供接口为了支持幂等调用, 接口有两个字段必须传, 一个是来源source, 一个是来源方序列号seq, 这个两个字段在提供方系统里面做联合唯一索引, 这样当第三方调用时, 先在本方系统里面查询一下, 是否已经处理过,返回相应处理结果;没有处理过,进行相应处理,返回结果。
注意,为了幕等友好,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理