面试问题归纳2022.2

JAVA基础

  • Java 8 新特性

集合

  • 简要说明集合(List,Set,Map)

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

  1. List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList。你可以将任何东西放到一个List容器中,并在需要时从中取出。ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快,而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。在具体应用时可以根据需要自由选择。前面说的Iterator只能对容器进行向前遍历,而ListIterator则继承了Iterator的思想,并提供了对List进行双向遍历的方法。
  2. Set接口也是Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSet和TreeSet类。HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用类:Collections和Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。
  3. Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。Map有两种比较常用的实现:HashMap和TreeMap。HashMap也用到了哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。
  • ArrayList 与 LinkedList 区别[https://www.cnblogs.com/lingshang/p/10897912.html]

    • 底层数据结构:

      ArrayList:它的底层是用数组实现的,所以查询速度相对LinkedList要快。

      LinkList:是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,这些对比都是指数据量很大或者操作很频繁。

    • 初始大小

      ArrayList:会默认帮我们初始化数组的容量大小为10。

      LinkedList:是基于双向链表实现的,不需要指定初始容量,链表中任何一个存储单元都可以通过向前或者向后的指针获取到前面或者后面的存储单元。

    • 线程安全问题

      ArrayList和LinkedList在单线程环境下是安全的,多线程环境下不是线程安全的,容易造成脏读的问题(如果这两个对象的内存空间被多线程共享,而会出现一个线程正在修改数值的时候,被另外一个线程读到原来的值)

      如果要使ArrayList是线程安全的,那么可以选择使用Collections.synchronizedList(new ArrayList())

      如果要使LinkedList是线程安全的,那么可以选择使用Collections.synchronizedList(new LinkedList())

    • 扩容机制

      ArrayList新增一个为原来1.5倍容量的新数组。

      LinkedList:是一个双向链表,没有初始化大小,也没有扩容的机制。

    • 时间复杂度与空间复杂度

      ArrayList:

      ​ 访问:随机访问时间固定,时间复杂度是O(1);

      ​ 添加/删除:使用system.arrayCopy(),为线性操作,最好情况下的时间复杂度O(1),平均时间复杂度为O(n)

      LinkedList:

      ​ 访问:随机访问时间与列表的大小成正比(除非访问第一个或者最后一个元素,时间固定),时间复杂度为O(n);

      ​ 添加/删除:需要遍历列表,时间复杂度为O(n)
      在这里插入图片描述

  • HashMap

    • 底层数据结构

      JDK1.7中HashMap的底层是由数组+单向链表(头插法)这两种数据结构组合而成的,先扩容后插入数据。

      JDK1.8中HashMap是由数组+单向链表(尾插法)+红黑树三种数据结构组合而成的,先插入数据后扩容。

    • put过程简述HashMap的put过程

      HashMap在put方法中,它使用hashCode()和equals()方法。当我们通过传递key-value对调用put方法的时候,HashMap使用Key的hashCode()和哈希算法来找出存储key-value对的索引。如果索引处为空,则直接插入到对应的数组中,否则,判断是否是红黑树,若是,则红黑树插入,否则遍历链表,若长度不小于8,则将链表转为红黑树,转成功之后 再插入。

    • 扩容机制

      在这里插入图片描述

      HashMap的本质就是一个复合结构,它其实就是有一个一个Node节点组成的一个Node数组。在初始化一个HashMap的时候,会规定这个数组容量为16(2的4次方),下标从0-15;数组的最大容量(扩容后)为2的30次方,而数组的扩容因素为0.75,即当每次数组实际用到的容量达到当前数组最大容量的0.75时,这个数组就会整体扩容一倍(乘以2);而每个Node算出数组下标是根据该Node的hashcode(哈希值)除以16取余后的数字再进行一系类的计算得出,但是会出现计算出来的数值相同(即数组下标相同的Node节点出现),这时候就出现了所谓的哈希冲突。为了解决这一问题,引入了单向链表的操作,当算出数组下标相同的Node节点出现,就会以单向链表的数据结构形式,纵向的向下排,当向下排的Node节点达到8个节点。为了解决性能问题,(接下来是JDK1.8的内容)这时候引入了红黑树这个数据结构,程序会将之前的单向链表通过算法将其变成红黑树,而当这个红黑树里面的叶子节点数小于6的时候,程序又会将其由红黑树转换成单向链表结构。

  • 线程安全问题、什么操作会产生线程安全问题

    HashMap并非线程安全的,在多个线程put时,会造成key之间的死循环。当另一个线程调用这个key时,get()方法会一直执行,导致线程积压,最终造成CPU满。

  • ConcurrentHashMap

    是HashMap的一个线程安全、支持高效并发的版本,理想状态下可以支持可以支持16个线程并发写的操作,以及任意线程的读操作。

    • 底层数据结构

      JDK8:使用数组+链表+红黑树来实现

    • 怎么实现的线程安全

      利用 CAS + synchronized 来保证并发更新的安全

    • 扩容机制

      当往hashMap中成功插入一个key/value节点时,有两种情况可能触发扩容动作:
      1、如果新增节点之后,所在链表的元素个数达到了阈值 8,则会调用treeifyBin方法把链表转 换成红黑树,不过在结构转换之前,会对数组长度进行判断,实现如下:如果数组长度n小于阈值MIN_TREEIFY_CAPACITY,默认是64,则会调用tryPresize方法把数组长度扩大到原来的两倍,并触发transfer方法,重新调整节点的位置。

      2、调用put方法新增节点时,在结尾会调用addCount方法记录元素个数,并检查是否需要进行扩容,当数组元素个数达到阈值时,会触发transfer方法,重新调整节点的位置。

      扩容状态下其他线程对集合进行插入、修改、删除、合并、compute 等操作时遇到 ForwardingNode 节点会触发扩容 。

      putAll 批量插入或者插入节点后发现存在链表长度达到 8 个或以上,但数组长度为 64 以下时会触发扩容 。

      注意:桶上链表长度达到 8 个或者以上,并且数组长度为 64 以下时只会触发扩容而不会将链表转为红黑树 。

    • put流程

      ①先传入一个k和v的键值对,不可为空(HashMap是可以为空的),如果为空就直接报错。
      ②接着去判断table是否为空,如果为空就进入初始化阶段。
      ③如果判断数组中某个指定的桶是空的,那就直接把键值对插入到这个桶中作为头节点,而且这个操作不用加锁。
      ④如果这个要插入的桶中的hash值为-1,也就是MOVED状态(也就是这个节点是forwordingNode),那就是说明有线程正在进行扩容操作,那么当前线程就进入协助扩容阶段。
      ⑤需要把数据插入到链表或者树中,如果这个节点是一个链表节点,那么就遍历这个链表,如果发现有相同的key值就更新value值,如果遍历完了都没有发现相同的key值,就需要在链表的尾部插入该数据。插入结束之后判断该链表节点个数是否大于8,如果大于就需要把链表转化为红黑树存储。
      ⑥如果这个节点是一个红黑树节点,那就需要按照树的插入规则进行插入。
      ⑦put结束之后,需要给map已存储的数量+1,在addCount方法中判断是否需要扩容

Spring

  • 常用注解介绍

  • spring aop

  • srping aop的使用场景

  • aop怎么实现的(代理)

  • 两种代理分别在什么时候使用

  • 怎么定义注解(对应元注解都有什么)

  • aop 的 after、around、before简单介绍

  • Spring Mvc 的执行原理

  • 拦截器与过滤器

    作用域不同:Filter是Severlet规范中的规定,只能作用于WEB,而拦截器的作用范围可以是web、Application、Swing等;

    规范差异:Filter是Severlet规范中的定义的,是依赖于Serverlet的容器,拦截器是Spring容器内的,是Spring框架支持的。

    资源差异:拦截器是Spring的一个组件,由Spring进行管理,可以使用Spring管理的任意资源和对象等,Filter不可以访问这些资源。

    深度差异:Filer只能在servlet前后作用,拦截器可以在方法前后、抛出异常前后等更深层的地方使用,所以Spring框架中优先考虑使用拦截器。

  • Spring bean的作用域

  • 怎么更改作用域

  • Autowired 与Resource注解的区别

  • Spirng boot的启动流程 核心注解

  • Configuration都实现了哪些注解

  • Spring Boot Aop 与Spring Aop 有什么区别

  • 事务问题会产生什么

    脏读:一个事务读取了另外一个事务未提交的数据。

    不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。

    虚读/幻读:是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。

  • 事务的基本要素

    1.A,原子性,事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。

    2.C,一致性,事务前后数据的完整性必须保持一致。

    3.I,隔离性,多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

    4.D,持久化,一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

  • 事务的隔离级别

    1.读未提交

    一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。

    2.读已提交

    一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。

    3.不可重复读

    一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。此隔离级别可有效防止不可重复读和脏读。

    4.可串行化

    提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。此隔离级别可有效防止脏读、不可重复读和幻读。但这个级别可能导致大量的超时现象和锁竞争,在实际应用中很少使用。

  • 事务的传播行为

    支持事务:

    1.Required,如果当前存在事务,则加入事务,不存在则新建事务。

    2.Support,支持当前事务,如果当前不存在事务,则以非事务方式执行。

    3.Mandated,使用当前事务,如果当前事务不存在,则抛出异常。

    不支持当前事务

    1.Required_new,新建事务,如果当前存在事务,则把当前事务挂起。

    2.Not_Soupport,以非事务方式运行,如果当前存在事务,则把当前事务挂起。

    3.Never,不使用当前事务,如果当前存在事务,则抛出异常。

    嵌套事务

    1.Nested,如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与Required类似的操作。

  • 事务失效的场景

    1.访问权限:

    ​ 如果Transactional注解应用在非public修饰的方法上,Transactional将会失效。

    ​ 之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute方法,获取Transactional 注解的事务配置信息。

    ​ 此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

    2.方法用final修饰,

    3.方法内部调用

    4.未被spring管理

    。。。

  • 分布式事务解决方案

    1.两段提交

    2.三段提交

Spring Boot自动装配过程

  1. 通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法(其实是其父类的方法)

  2. selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表

  3. loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName,大概120多个

  4. selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
    @ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean

  5. 最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IOC环境当中

Mybatis

  • #{} 与 ${}的区别
  • mybatis的缓存机制
  • 分页方法

多线程

Java并发/多线程-锁的区别与使用

  • 你们项目中哪用到了多线程(几乎问线程都问这个问题),是为了解决什么问题

  • 中断线程的方法

  • sleep()与wait()区别

    ​ sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是锁没有被释放,其他线程依然无法访问这个对象。

    ​ wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

  • synchronized与Lock的区别

    存在层次:**synchronized **是Java的关键字,是jvm层面的;Lock是一个类;

    锁的释放:synchronized 1.已经获取锁的线程,执行完同步代码,主动释放锁2.线程执行发生异常,JVM会让线程释放锁;Lock在finally中必须释放锁,不然容易造成线程死锁;

    锁的获取:synchronized 假设A获取锁,B一直等待,A线程阻塞,B线程会一直等待;Lock可以使用tryLock()尝试获取锁

    锁的状态:synchronized 无法判断;Lock可以判断

    锁的类型:synchronized 可重入 不可中断 非公平;Lock可重入 可判断 可公平(两者皆可)

    锁的性能:synchronized 少量同步;Lock大量同步

  • Runnable和Callable的区别

    1.runnable定义的方法是run,没有返回值,不可以抛出异常。

    2.callable定义的方法是call,可以有返回值,可以抛出异常。

    3.都是接口实现。

  • synchronized关键字

    同步块,通过 synchronized 关键字来实现,所有加上synchronized 和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法 或者 代码块。

  • synchronized锁升级过程

  • volatile的作用

    1.保证内存可见性

    通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的。

    当一个变量被 volatile 修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。
    2.禁止指令重排序

  • volatile怎么实现的可见性(?)

  • ReentrantLock的使用

    ReentrantLock是可重入的互斥锁,主要利用CAS+AQS(AbstractQueuedSynchronizer)队列来实现。它支持公平锁和非公平锁,两者的实现类似。

    ReentrantLock reentrantLock = new ReentrantLock();
    reentrantLock.lock();//拿锁
    
    boolean res = reentrantLock.tryLock();//拿到锁返回true,没拿到返回false
    
    boolean res = reentrantLock.tryLock(100,TimeUnit.SECONDS);//100秒内拿到锁返回true,否则没拿到返回false
    
    reentrantLock.unlock();//释放锁
    
  • 写一个线程不安全的场景

    计数器

  • 线程池创建方式 + 参数说明

  • 有没有自定义线程池

  • 你们用到的线程池线程数是多少

  • 最佳线程数是多少

  • 线程池的执行过程

  • 等待队列用的是什么

    ArrayBlockingQueue:

    ​ 基于数组的有界阻塞队列,按照FIFO排序,新的线程任务进来后会放到队列的队尾,有界的数组可以防止资源耗尽问题,当进来的线程数达到CorePoolSize后,有新的线程进来则会被放在队列的队尾,等待被调度,如果队列已经满了,则会创建一个新的线程,如果线程数已经达到最大的maximumPoolSize,则会执行拒绝策略。

Redis

  • 你们怎么用到的Redis,场景

  • Redis数据结构

  • 给你的场景,需要你设计Redis的类型

  • 分布式锁的实现原理(次数很多)

  • Redis的淘汰策略--------最近最少使用(less recently used ,LRU)

    noeviction:Redis 默认淘汰策略,对于写请求不再提供服务,直接返回错误( DEL 请求和部分特殊请求除外)

    allkeys - lru:从所有 key 中使用 LRU 算法进行淘汰

    volatile - lru:从设置了过期时间的 key 中使用 LRU 算法进行淘汰

    allkeys - random:从所有 key 中随机淘汰数据

    volatile - random:从设置了过期时间的 key 中随机淘汰

    volatile - ttl:从设置了过期时间的 key 中,根据 key 的过期时间进行淘汰,越早过期的越优先被淘汰

  • 怎么保证缓存与数据库的数据一致性

    串行化:我们利用MQ将所有“读数据库” + “写缓存”的步骤串行化。第一步先读缓存,如果缓存没读到,则去读DB,之后再异步将数据标识写入MQ(这里MQ 与写流程的MQ是同一个),接下来就消费MQ,解析MQ消息来读库获取相应的数据刷新缓存。

    双删加超时:在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。这样最差的情况是在超时时间内存在不一致,当然这种情况极其少见,可能的原因就是服务宕机。此种情况可以满足绝大多数需求。

    使用canal解析binlog:Mysql通过binlog同步redis https://blog.csdn.net/u010743397/article/details/97258672

消息队列

RabbitMQ如何防止消息丢失及重复消费

  • 消息丢失问题/保证消息的可靠性

    生产者到RabbitMQ:事务机制和Confirm机制,注意:事务机制和 Confirm 机制是互斥的,两者不能共存,会导致 RabbitMQ 报错。

    RabbitMQ自身:持久化、集群、普通模式、镜像模式。

    RabbitMQ到消费者:basicAck机制、死信队列、消息补偿机制。

  • 消息的重复消费问题
    幂等性去重、redis、MySQL

  • 怎么确保消息发出

  • Mq签收模式?

  • MQ消费时候异常怎么办

JVM

  • JVM原理

    JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。

    java编译器只要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。
    在这里插入图片描述

  • 内存模型

    堆、栈、本地方法栈、程序计数器、方法区五个部分

    1.Java 虚拟机栈与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。

    2.Java 堆对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

    3.方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    4.程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。

    5.本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。

在这里插入图片描述

  • 哪些个类加载器

  • 基本的参数

    -Xms/-Xmx:堆内存

    -Xmn:年轻代

    -XX:PermSize/-XX:MaxPermSize:持久代

  • String str = new String(“abc”)

分布式/微服务相关

  • Dubbo 出现服务找不到的问题怎么办
  • Dubbo的namespace和group的作用
  • Dubbo的timeout的优先级顺序
  • Dubbo的协议
  • Dubbo的负载策略有哪些
  • nacos 是怎么记录服务的 、数据结构是什么
  • nacos,zk的区别是什么
  • Spring cloud 的常用组件

数据库

  • 事务的特性

事务的定义很严格,它必须同时满足四个特性,即原子性、一致性、隔离性和持久性,也就是人们俗称的 ACID 特性,具体如下。

1)原子性(Atomic)

表示将事务中所进行的操作捆绑成一个不可分割的单元,即对事务所进行的数据修改等操作,要么全部执行,要么全都不执行。

2)一致性(Consistency)

表示事务完成时,必须使所有的数据都保持一致状态。

3)隔离性(Isolation)

指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

4)持久性(Durability)

持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。提交后的其他操作或故障不会对其有任何影响。

  • 事务的隔离级别

在实际应用中,数据库中的数据是要被多个用户共同访问的,在多个用户同时操作相同的数据时,可能就会出现一些事务的并发问题,具体如下。

1)脏读

指一个事务读取到另一个事务未提交的数据。

2)不可重复读

指一个事务对同一行数据重复读取两次,但得到的结果不同。

3)虚读/幻读

指一个事务执行两次查询,但第二次查询的结果包含了第一次查询中未出现的数据。

4)丢失更新

指两个事务同时更新一行数据,后提交(或撤销)的事务将之前事务提交的数据覆盖了。

丢失更新可分为两类,分别是第一类丢失更新和第二类丢失更新。

  • 第一类丢失更新是指两个事务同时操作同一个数据时,当第一个事务撤销时,把已经提交的第二个事务的更新数据覆盖了,第二个事务就造成了数据丢失。
  • 第二类丢失更新是指当两个事务同时操作同一个数据时,第一个事务将修改结果成功提交后,对第二个事务已经提交的修改结果进行了覆盖,对第二个事务造成了数据丢失。

为了避免上述事务并发问题的出现,在标准的 SQL 规范中定义了四种事务隔离级别,不同的隔离级别对事务的处理有所不同。这四种事务的隔离级别如下。

1)Read Uncommitted(读未提交)

一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。

2)Read Committed(读已提交)

一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。

3)Repeatable Read(可重复读取)

一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。此隔离级别可有效防止不可重复读和脏读。

4)Serializable(可串行化)

提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。此隔离级别可有效防止脏读、不可重复读和幻读。但这个级别可能导致大量的超时现象和锁竞争,在实际应用中很少使用。

  • 常用数据库

  • SQL调优(重要)

  • 执行计划的参数说明

  • 索引失效的场景
    字符行不使用引号等等

  • 索引机制

  • 索引类型

    Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。

    1. FULLTEXT
      即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。

      全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE "%word%"这类针对文本的模糊查询效率较低的问题。

    2. HASH
      由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。

      HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。

    3. BTREE
      BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。

    4. RTREE
      RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。

      相对于BTREE,RTREE的优势在于范围查找。

      ps. 此段详细内容见此片博文:Mysql几种索引类型的区别及适用情况

  • 索引种类

    普通索引:仅加速查询

    唯一索引:加速查询 + 列值唯一(可以有null)

    主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个

    组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并

    全文索引:对文本的内容进行分词,进行搜索

    ps.索引合并,使用多个单列索引组合搜索
    覆盖索引,select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖

  • 索引方法

  • B+Tree与B Tree和Hash的区别

  • 存储过程会不会

搜索引擎

  • es使用

设计模式

  • 你知道的设计模式
  • 单例模式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值