考点整理

一  “集合”一章考点

       1.  HashMap的数据结构

            HashMap是一个链表的数组

       2.  HashMap是如何处理Hash冲突的

        (1)Hash冲突产生于put时,put时逻辑:

               (a) 对key的hashCode()做hash,然后再计算index;

               (b)如果没碰撞直接放到bucket里;

               (c)如果碰撞了,以链表的形式存在buckets前面(1.8是放在buckets后

                        面)

               (d)如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把

                        链表转换成红黑树;

               (e)如果节点已经存在就替换old value(保证key的唯一性)

               (f)如果bucket满了(超过load factor*current capacity),就要resize。 

        (2)get时源码

       if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
 
           return e.value;

        3.  HashMap什么地方线程不安全

            是由hash冲突和扩容

            hash冲突时:会形成链表,两个线程一起来,就只有一个保存成功

            扩容:扩容需要把原来的数组写到一个新数组里面,然后在把新数

                       组赋值给table变量,最后只有一个线程能赋值成功,其它的

                       都被覆盖。

       4.  JDK1.8对HashMap做的优化

            “由数组+链表”改为“数组+链表+红黑树”。当链表长度太长(默认超

            过8)时,链表就转换为红黑树

       5.  HashTable和HashMap的区别

            我太熟悉了

       3  TreeMap以及TreeMap和HashMap的比较

       (1)非线程安全的

       (2)基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。

       (3)TreeMap适用于按自然顺序或自定义顺序遍历键(key);HashMap适用于在

                Map中插入、删除和定位元素。

       4.ConcurrentHashMap和HashMap、ConcurrentHashMap和HashTable

          这个我也太熟悉了

       5.  Set 和 List区别

            Set:无序不可重复

            List:有序可重复

       6.  ArrayList为什么线程不安全

           概括的说就是扩容时下标越界和赋值(添加新元素)时元素覆盖

           详细解释(有空看,没空不看):

           线程不安全的源码:

public boolean add(E e) {
 
    /**
     * 添加一个元素时,做了如下两步操作
     * 1.判断列表的capacity容量是否足够,是否需要扩容
     * 2.真正将元素放在列表的元素数组里面
     */
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

            ArrayList的本质是一个Object数组,线程不安全发生在两个地方

            ArrayList的add操作执行逻辑

          (a)判断Object数组(elementData)容量是否足够

          (b)在elementData对应位置上设置值

          (1)扩容时下标越界(达到Object(elementData)数组最大下标时才扩容) 

                  (a)列表大小为9,即size=9

                  (b)线程A开始进入add方法,这时它获取到size的值为9,调用

                          ensureCapacityInternal方法进行容量判断。

                  (c)线程B此时也进入add方法,它获取到size的值也为9,也开始调

                           用ensureCapacityInternal方法。

                  (c)线程A发现需求大小为10,而elementData的大小就为10,可以

                           容纳。于是它不再扩容,返回。

                  (d)线程B也发现需求大小为10,也可以容纳,返回。

                  (e)线程A开始进行设置值操作, elementData[size++] = e 操作。

                           此时size变为10。

                  (f)线程B也开始进行设置值操作,它尝试设置elementData[10] = e,

                           而elementData没有进行过扩容,它的下标最大为9。于是此时

                           会报出一个数组越界的异常ArrayIndexOutOfBoundsException.

          (2)赋值时元素覆盖

                   elementData[size++] = e 设置值的操作同样会导致线程不安全。这不是

                   一个原子操作,它由以下两步组成:

                  第一步:elementData[size] = e;
                  第二步:size = size + 1;

                  在单线程执行这两条代码时没有任何问题,但是当多线程环境下执行时,

                  可能就会发生一个线程的值覆盖另一个线程添加的值,具体逻辑如下:

                  列表大小为0,即size=0

                (a)线程A开始添加一个元素,值为A。此时它执行第一条操作,将A放

                         在了elementData下标为0的位置上。
                (b)接着线程B刚好也要开始添加一个值为B的元素,且走到了第一步操

                         作。此时线程B获取到size的值依然为0,于是它将B也放在了

                         elementData下标为0的位置上。

               (c)线程A开始将size的值增加为1

               (d)线程B开始将size的值增加为2

            详细请看文章:

            https://blog.csdn.net/u012859681/article/details/78206494

       7.  Vector

           线程安全的List

       8.   CopyOnWriteArrayList和Collections.synchronizedList()

                CopyOnWriteArrayList和Collections.synchronizedList是实现线程安全的列表的

                两种方式。两种实现方式分别针对不同情况有不同的性能表现,其中

                CopyOnWriteArrayList的多线程写操作性能较差,而多线程的读操作性能较好。

                Collections.synchronizedList的写操作性能比CopyOnWriteArrayList在多线程操

                作的写操作好很多,而读操作因为是采用了synchronized关键字的方式,其读

                操作性能并不如CopyOnWriteArrayList。因此在不同的应用场景下,应该选择不

                同的多线程安全实现类。

                CopyOnWriteArrayList是java.util.concurrent包中的一个List的实现类。

                CopyOnWrite的意思是在写时拷贝,也就是如果需要对CopyOnWriteArrayList

                的内容进行改变,首先会拷贝一份新的List并且在新的List上进行修改,最后将原

                List的引用指向新的List。CopyOnWriteArrayList可以线程安全的遍历

二  “多线程”一章考点

        1.  线程和进程的区别与联系

             答:进程是资源分配的最小单位;线程是程序执行的最小单位;

                    一个进程可以有多个线程,如果一个进程只有一个线程,那

                    么该进程是单线程的进程。

        2.  Lock和Sychronized的区别

             答:等待可中断、可实现公平锁、可以绑定多个条件(面试时不要答)

        3.  ReenTrantLock 原理

             脑补一遍

        4.  JDK1.6对sychronized做的优化

             太长了不写了

        5.  volatile

             可保证可见性、有序性但是不能保证原子性

        6.  CAS AQS

        7.  公平锁和非公平锁

        8.  线程池

            keepAliveTime:空闲的线程保留的时间。

          TimeUnit:空闲线程的保留时间单位。

          上面两个粘这加深记忆

          直接丢弃(DiscardPolicy)

          丢弃队列中最老的任务(DiscardOldestPolicy)。

          默认,直接抛异常(AbortPolicy)

          将任务分给调用线程来执行(CallerRunsPolicy)。

        9.  ThreadLocal

 

三  “框架”一章考点

https://blog.csdn.net/jialanshun/article/details/81634686

       1.  说明一下什么是IOC/DI

            IOC是控制反转;DI是依赖注入

         (1)IOC,控制反转

                  --  正传和反转

                      正转,就是传统应用程序是由我们自己在对象中主动控

                                 制去直接获取依赖对象。

                      反转:就是由容器来帮忙创建及注入依赖对象。

                  --  谁控制谁?控制什么

                      由IOC容器控制所依赖的对象。

                             传统的应用中程序是通过new主动创建依赖对象,而IoC是有专门

                      一个容器来创建这些对象,所以是由IOC容器控制所依赖的对象。

         (2)DI,依赖注入

                  --  DI的由来

                             由于控制反转概念比较含糊(可能只是理解为容器控制对象这一

                      个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物

                      Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,

                      “依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

                  --  谁依赖谁谁?为什么要依赖?

                      应用程序依赖IOC容器,因为应用程序需要的IOC容器提供需要的外部

                      资源。

                  -- 谁注入谁?注入什么?

                     IOC容器注入应用古程序的某个对象,注入的是应用程序某个对象需要

                     的外部资源

       2.  依赖注入和控制反转是同一概念吗?

            答:不是同一概念,是同一事物的不同角度的描述,控制反转是从IOC/DI容

                   器角度来说;依赖注入是从应用程序角度来说。

                   面试的时候可以答就是一个东西。

       3.  IOC/DI的优点

            答:IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,

                   它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由

                   我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测

                   试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由

                   容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便

                   测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵

                   活。

       2.  Spring Aop的原理

            Spring Aop的原理使用的时代理模式

            代理模式分为:

          (1)静态代理

          (2)动态代理

                (a)JDK动态代理

                (b)cglib动态代理代理

           动态代理和静态代理的区别:

                  静态代理是在编译时就确认了代理类

                  动态代理是在运行时由反射确认代理类

           JDK动态代理和cglib代理的区别:

                  JDK动态代理只能对实现了接口的类生成代理,而不能针对类;CGLIB

                  是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

               (继承)当Bean实现接口时,Spring就会用JDK的动态代理;当Bean没

                 有实现接口时,Spring使用CGlib是实现

       3.  Aop的优点

          (1)关注点在需要增加或删除这些共性功能的时候不需对目标模块进行修改,

                   实现了快速插拔

          (2)使得系统架构更优良,分而治之,关注点只需要关注自身要实现的共性

                   的功能,服务模块只需要关注自身的核心功能,这样设计胜过共性功能

                   糅杂进服务模块

       4.  Aop术语

         (1)关注点:又名“横切关注点”,是我们关注的共性的功能。比如用户日志

                                 记录、用户的验证

         (2)切面:横切关注点被模块化为特殊的类,这些类称为切面。

         (3)通知:切面的工作被称为通知。其实就是指拦截到连接点之后要执行

                             的代码

                             Before——在方法调用之前调用通知。

                             After——在方法完成之后调用通知,无论方法执行成功与否。

                             After-returning——在方法执行成功之后调用通知。

                             After-throwing——在方法抛出异常后进行通知。

                             Around——通知包裹了被通知的方法,在被通知的方法调用之

                                                  前和调用之后执行自定义的行为。

         (4)连接点:一个类或一段程序代码拥有一些具有边界性质的特定点,这

                                些点中的特定点就称为“连接点”。如类开始初始化前、类初始

                                化后、类某个方法调用前、调用后、方法抛出异常后。

                                通俗的说就是“目标对象或者代理对象之中的方法”

         (5)切入点:对连接点进行拦截的定义。举个例子,连接点相当于数据库

                                中的记录,而切点相当于查询条件。切点和连接点不是一对

                                一的关系,一个切点可以匹配多个连接点。   

       5.  数据库事务

          (1)事务特性

            A:原子性

            C:一致性

            I:隔离性  

            D:持久性

           张三给李四转账100元,需要先重张三的账户上减去100元,在给李四

           的账户上增加100元,如果张三减去100失败那么整个事物就回滚,这

           就是原子性的体现;假设张三李四的账户在转账前都是1000元,那么

           转账后张三账户为900元,李四账户为1100元,这就是一致性的体现。

          (2)事务的隔离级别

                   事务的隔离级别由低到高分别是:

                          READ UNCOMMITTED 幻读、不可重复读和脏读都允许。

                          READ COMMITTED 允许幻读、不可重复读,不允许脏读。

                          REPEATABLE READ 允许幻读,不允许不可重复读和脏读。

                          SERIALIZABLE 幻读、不可重复读和脏读都不允许。

                          幻读

                                 数据发生新增。第一个事务对一个表中的所有数据进行

                                 了修改,同时,第二个事务向这个表中插入一行新数据。

                                 那么,以后就会发生操作第一个事务的用户发现表中还

                                 有没有修改的数据行,就好象发生了幻觉一样。

                          不可重复读:

                                 数据发生修改由于一个事务的修改导致另外一个事务两

                                 次读到的数据不一致。如在一个事务内,多次读同一数

                                 据。在这个事务还没有结束时,另外一个事务也访问该

                                 同一数据。那么,在第一个事务中的两次读数据之间,

                                 由于第二个事务的修改,那么第一个事务两次读到的的

                                 数据可能是不一样的。 

                          脏读

                                 读到了另外一个事务未提交的数据。如当一个事务正在

                                 访问数据,并且对数据进行了修改,而这种修改还没有

                                 提交到数据库中,这 时,另外一个事务也访问这个数

                                 据,然后使用了这个数据。 

       5.  Spring 事务的原理

            有空在看    

       6.  Spring事务的传播

            REQUIRED(默认)

                   业务方法需要在一个容器里运行。如果方法运行时,已经处在一

                   个事务中,那么加入到这个事务,否则自己新建一个新的事务(

                   默认,和SUPPORTS一起记)

            SUPPORTS

                    该方法在某个事务范围内被调用,则方法成为该事务的一部分。

                    如果方法在该事务范围外被调用,该方法就在没有事务的环境

                    下执行(和默认分一类吧,暂叫它默认2吧)。 

            REQUIRESNEW

                    总会发起一个新得事务。不管是否存在事务,该方法总会为自

                    己发起一个新的事务。如果方法已经运行在一个事务中,则原

                    有事务挂起,新的事务被创建(新事务,零碎没组)。

            NOT_SUPPORTED

                     总是没有事务。如果方法没有关联到一个事务,容器不

                     会为他开启事务,如果方法在一个事务中被调用,该事务会被

                     挂起,调用结束后,原先的事务会恢复执行(不需要事务,零

                     碎没组)。

            MANDATORY

                     必须要有事务

                     该方法只能在一个已经存在的事务中执行,业务方法不能发起

                     自己的事务。如果在没有事务的环境下被调用,容器抛出例外

                     (必须要在有事务的环境中执行,和NEVER一起记)。                                       

            NEVER

                     必须没有事务

                     该方法绝对不能在事务范围内执行。如果在就抛例外。只有该

                     方法没有关联到任何事务,才正常执行(必须要在没有有事务

                     的环境中执行)。

            NESTED

                     如果一个活动的事务存在,则运行在一个嵌套的事务中。如果

                     没有活动事务,则按REQUIRED属性执行。它使用了一个单独

                     的事务,这个事务拥有多个可以回滚的保存点。内部事务的回

                     滚不会对外部事务造成影响。它只对

                     DataSourceTransactionManager事务管理器起效。            

       7.  Spring常用标签

          (1)@Controller、@Service、@Respository,分别是Controller、

                   Service、Dao的注解。

         (2)@Component

         (3)@Resource和@Autowired

                  @Resource和@Autowired功能相同,区别是@Resource是按照name

                  注入,而@Autowired是按照type注入。

       8.  Spring bean的生命周期(创建、调用、销毁,这里有一个流程,我没有理解,只是记忆一下),

          (1)实例化bean对象(通过构造方法或者工厂方法)

          (2)设置对象属性(setter等)(依赖注入)

          (3)检查Aware接口,设置相关依赖。如果Bean实现了BeanNameAware接

                   口,spring将bean的id传给setBeanName()方法;如果Bean实现了

                   BeanFactoryAware接口,spring将调用setBeanFactory方法,将

                   BeanFactory实例传进来。

          (4)将Bean实例传递给Bean的前置处理器的

                    postProcessBeforeInitialization(Object bean, String beanname)方法

          (5)调用Bean的初始化方法。(初始化bean有两种方式,在配置文件中通过

                    指定init-method属性来完成和实现

                    org.springframwork.beans.factory.InitializingBean接口。图上有画)

          (6)将Bean实例传递给Bean的后置处理器的

                   postProcessAfterInitialization(Object bean, String beanname)方法使用

                   Bean。

          (7)使用。此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻

                   留在应用上下文中,直到该应用上下文被销毁。

          (8)容器关闭之前,调用Bean的销毁方法(bean的销毁方法有两种:使用

                   配置文件指定的destroy-method属性和实现

                   org.springframwork.bean.factory.DisposeableBean接口。图上有画)

       8.  Bean的作用域

            singleton、prototype、request、session、global session

       9.  Bean注入方式

         (1)setter注入

         (2)构造器注入

         (3)静态工厂方法注入

         (4)实例工厂方法注入

       10.  Spring MVC的处理请求的流程

            (1)客户端请求提交到DispatcherServlet

            (2)由DispatcherServlet控制器查询一个或多个HandlerMapping,找到

                     处理请求的Controller

            (3)DispatcherServlet将请求提交到Controller

            (4)Controller调用业务逻辑处理后,返回ModelAndView

            (5)DispatcherServlet查询一个或多个ViewResoler视图解析器,找到

                     ModelAndView指定的视图

            (6)视图负责将结果显示到客户端

       9.  BeanFactory 接口和 ApplicationContext 接口有什么区别

            有空在看

   (11)Spring Quartz和Spring Task

             有空看,不重要,重要的了解是分布式的,比如当当的

四  “数据库”一章考点

 

二  数据库

       1.  数据库事务,看“框架”一节中

       2.  数据库索引原理(Hash索引和B+Tree索引)

          (1)B+Tree索引

                   自己脑过一遍

          (2)Hash索引

                   答:哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检

                          索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次

                          哈希算法即可立刻定位到相应的位置,速度非常快。

       3.   InnoDB和MyISAM区别

           (1)事务支持

                    InnoDB支持事务;MyISAM不支持事务;

           (2)行锁支持

                    InnoDB支持行锁;MyISAM不支持行锁;

           (3)外键支持

                    InnoDB支持外键,MyISAM不支持

           (4)全文检索支持

                    InnoDB不支持全文索引,而MyISAM支持

       5.   B+tree索引和Hash索引的比较

           (1)Hash索引只支持“=” 、"<>"、“in”

           (2)如果是等值查询,Hash索引快

           (3)B+Tree索引关键字检索比较平均,而Hash索引如果Hash碰撞比较多,

                    Hash索引效率就比较差

           (4)Hash索引不支持范围查询

           (5)Hash索引无法进行索引排序

           (6)Hash索引不能复合索引的最左匹配原则

           (7)Hash索引不支持形如 “like 'xxx%' ”

       6.  如何避免索引失效

           (1)慎用 null 值判断

           (2)慎用 in

           (3)慎用<>

           (4)索引字段上不使用函数、运算

           (5)索引字段上不进行隐式转换

       7.  索引为什么能增加查询速度

            答:对表的扫描变成对索引的扫描,索引因其特殊的数据结构(B+Tree和

                   Hash)决定其扫描速度非常的快。索引存放了真实数据的行地址

       8.  SQL执行顺序

            from->join->on->where->group by->with->having->select->distinct->order by

       9.  MySql的执行计划

          (1)type列

                   效率由高到低排序:

                          system > const > eq_ref > ref > fulltext > ref_or_null >

                          index_merge >unique_subquery > index_subquery >

                          range > index > ALL

                   含义:

                            eq_ref :表和表关联时候返回一行

                            ref:索引和扫描的混合体

          (2)extra列

                   出现如下两列表示需要优化

                          Using filesort:常见于Order By字段上没走索引

                          Using temporary:常见于Group By字段上没走索引

       9.  索引覆盖

              脑过

              注意:索引覆盖不仅包括select 列表中的字段,而且还包括where、on和

                         order by、group by(没找到例子,个人认为是可以覆盖的,使用

                        的时候留意一下)列表中的。索引覆盖的前提是索引要生效,如果

                        没有生效,比如复合索引因有断点使得部分没有生效,这就导致没

                        有生效的索引无法使用索引覆盖。

       10.  mysql大数据量翻页

          (1)使用索引自增的特性

                   把SQL写成形如下格式

                   SELECT * FROM 表名称 WHERE   id_pk > (pageNum*10)

                    ORDER BY id_pk ASC LIMIT M

                    例子:

                    select * from table where id>100*10 limit 10;

          (2)使用延迟加载+索引覆盖

                    select * from table limit 3000000,10

                    改为:

                    select table.* from table

                    inner join

                   ( select id from table limit 3000000,10 ) as tmp on tmp.id=table.id

          (3)摆渡76页不让翻

          (4)聚簇索引(主键索引)、非聚簇索引(二级索引)的区别

                   脑过

          (6)常见的分库分表的方法

                   脑过,重点是明白分布式的分库分表

       11.微服务跨库翻页

            不会

 

       12. 分布式中跨库join的几种方案

 

          (1)全局表

 

                   所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。

 

                   比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以

 

                   将这类表在其他每个数据库中均保存一份。同时,这类数据通常也

 

                   很少发生修改(甚至几乎不会),所以也不用太担心“一致性”问题。

 

          (2)字段冗余

 

                   这是一种典型的反范式设计,在互联网行业中比较常见,通常是为

 

                   了性能来避免join查询。

 

                          举个电商业务中很简单的场景:

 

                                 “订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗

 

                          余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。

 

                          字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场

 

                         景也比较有限,比较适合依赖字段较少的情况。最复杂的还是数

 

                         据一致性问题,这点很难保证,可以借助数据库中的触发器或者

 

                         在业务代码层面去保证。当然,也需要结合实际业务场景来看一

 

                         致性的要求。

 

                                就像上面例子,如果卖家修改了Name之后,是否需要在订单

 

                         信息中同步更新呢?         

 

          (3)系统层组装

 

select * from order_1 where user_id=257 and auction_id = 100;

 

                 其中,order_1是根据257%256计算得出,表示分表之后的第一张order表。

       13.共享锁、排它锁、悲观锁、乐观锁

          (1)共享锁和排它锁(原文:http://www.cnblogs.com/boblogsbo/p/5602122.html)       

                   共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于 同一数

                  据可以共享一把锁,都能访问到数据,但是只能读不能修改;排他锁又称为

                  写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取

                  了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁

                  和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

                         我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读

                  取和修改该行数据,其实不是这样的。排他锁指的是一个事务在一行数据加上

                  排他锁后,其他事务不能再在其上加其他的锁,mysql InnoDB引擎默认的修改

                  数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,

                  select语句默认不会加任何锁类型(网上说默认会加S锁,取决于隔离级别),

                  如果加排他锁可以使用select ...for update语句,加共享锁可以使用

                  select ... lock in share mode语句

          (2)悲观锁和乐观锁

                   悲观锁

                           在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,

                           Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。

                           它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务

                           执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务

                           才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环

                           境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的

                           环境中。悲观锁的实现,往往依靠数据库提供的锁机制。悲观锁的执行流

                           程如下:

                           --  在对任意记录进行修改前,先尝试为该记录加上排他锁。

                           --  如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者

                               抛出异常。 具体响应方式由开发者根据实际需要决定。

                           --  如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了

                           --  其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁

                               或直接抛出异常。

                           优点与不足:

                                    悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处

                                    理的安全提供了保证。但是在效率方面,处理加锁的机制会让

                                    数据库产生额外的开销,还有增加产生死锁的机会;另外,在

                                    只读型事务处理中由于不会产生冲突,也没必要使用锁,这样

                                    做只能增加系统负载;还有会降低了并行性,一个事务如果锁

                                    定了某行数据,其他事务就必须等待该事务处理完才可以处理

                                    那行数据。                       

                           MySQL InnoDB中使用悲观锁:

                                    要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为

                                    MySQL默认使用autocommit模式,也就是说,当你执行一个更新

                                    操作后,MySQL会立刻将结果进行提交。

                                    set autocommit=0;

//0.开始事务
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;

                                     上面的查询语句中,我们使用了select…for update的方式,这样就通过

                             开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就

                             被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可

                             以保证当前的数据不会被其它事务修改。

                                    上面我们提到,使用select…for update会把数据给锁住,不过我们

                             需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于

                             索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表

                             级锁把整张表锁住,这点需要注意

   

                   乐观锁

                          在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,

                          Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它

                          假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不 产生

                          锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会

                          先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事

                          务有更新的话,正在提交的事务会进行回滚。乐观锁( Optimistic Locking )

                          是相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在

                          数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现

                          冲突了,则让返回用户错误的信息,让用户决定如何去做。相对于悲观锁,

                          在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般

                          的实现乐观锁的方式就是记录数据版本。

                                   数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识

                            的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交

                            更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版

                            本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值

                            相等,则予以更新,否则认为是过期数据。

                                   实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间

                            戳。

                            使用版本号实现乐观锁:

1.查询出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};

                            使用时间戳实现乐观锁:有时间在查。

                            优点与不足

       14.死锁

            行锁死锁

                 (1)在学习行锁死锁之前,需要补充几个知识点

                         (a)在MySQL中,行级锁并不是直接锁记录,而是锁索引。

                                          索引分为主键索引和非主键索引两种,如果一条sql语句操作了主

                                  键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键

                                  索引,MySQL会先锁该非主键索引,然后在定再锁定相关的主键索引。

                         (b)行锁是逐行获取的。

                                          比如表A有50行记录,语句一逐行获取锁,获取了1到50条的锁,

                                   语句2逐条获取锁,获取了100到51条的锁,当语句1要获取第51条记录

                                   的锁,语句2要获取第50条记录 的锁,这个时候就出现了互相等待的状

                                   况。

                         (c)除了单个SQL组成的事务外,锁是逐步获取的

                 (2)四种死锁的情况

                        (a)不同表的相同记录行锁冲突

                                 案例:两个表、两行记录,交叉获得和申请互斥锁

                                 条件:

                                 -  两事务分别操作两个表、相同表的同一行记录

                                 -  申请的锁互斥

                                 -  申请的顺序不一致

                        (b)主键索引锁冲突

                                 案例:本文案例,产生冲突在主键索引锁上

                                 条件:

                                 -  两sql语句即两事务操作同一个表、使用不同索引

                                 -  申请的锁互斥

                                 -  操作多行记录

                                 -  查找到记录的顺序不一致

                                 

                                 案例:

                                        tab_test 结构如下:

                                        id:主键;

                                        state:状态;

                                        time:时间;

                                        索引:idx_1(state,time)

                                        出现死锁的2条sql语句:

update tab_test set state=1064,time=now() where state=1061 
           and time <  date_sub(now(), INTERVAL 30 minute);
update tab_test set state=1067,time=now () where id in (9921180)

                                 原因分析:

                                        当“update tab_test set state=1064,time=now()

                                        where  state=1061 and  time < date_sub(now(), INTERVAL 30 minute)”

                                        执行时,MySQL会使用idx_1索引,因此首先锁定相关的索引记录,因

                                        为idx_1是非主键索引,为执行该语句,MySQL还会锁定主键索引。假

                                        设“update tab_test set state=1067,time=now ()  where id in (9921180)”

                                        几乎同时执行时,本语句首先锁定主键索引,由于需要更新state的值,

                                        所以还需要锁定idx_1的某些索引记录。这样第一条语句锁定了idx_1的

                                        记录,等待主键索引,而第二条语句则锁定了主键索引记录,而等待

                                        idx_1的记录,这样死锁就产生了。在第一条语句给主键加锁前,第二条

                                        语句已经给主键加了锁,所以在高并发的数据操作时,死锁的情况就容

                                        易产生InnoDB 会自动检测一个事务的死锁并回滚一个或多个事务来防止

                                        死锁。Innodb 会选择代价比较小的事务回滚,此次事务。

                                 解决方案:

                                               拆分第一条sql,先查出符合条件的主键值,再按照主键更新记录:

                                               select id from tab_test where state=1061 and time <

                                               date_sub(now(), INTERVAL 30 minute);

                                               update tab_test state=1064,time=now() where id in(......);

                        (c)主键索引锁与非聚簇索引锁冲突

                                 直接看案例:

                                      teamUser表的表结构如下:

PRIMARY KEY (`uid`,`Id`),
KEY `k_id_titleWeight_score` (`Id`,`titleWeight`,`score`),
ENGINE=InnoDB

                                      出现死锁的两条SQL语句如下:

insert into teamUser_20110121 select * from teamUser
DELETE FROM teamUser WHERE teamId=$teamId AND titleWeight<32768 AND joinTime<'$daysago_1week'

                                 原因分析:

                                            在innodb默认的事务隔离级别下,普通的SELECT是不

                                      需要加行锁的,只有LOCK IN SHARE MODE、

                                      FOR UPDATE及高串行化级别中的SELECT都要加锁。但

                                      是此案例中的,第一条语句这种情况 

                                      insert into teamUser_20110121 select * from teamUser会

                                      对表teamUser_20110121(ENGINE= MyISAM)加表锁,

                                      并对teamUser表所有行的主键索引(即聚簇索引)加共享

                                      锁。默认对其使用主键索引。而第二条语句

                                      DELETE FROM teamUser  WHERE teamId=$teamId

                                      AND titleWeight<32768 AND joinTime<'$daysago_1week'

                                      为删除操作,会对选中行的主键索引加排他锁。由于此语句

                                      还使用了非聚簇索引

                                      KEY `k_teamid_titleWeight_score` (`teamId`,`titleWeight`,`score`)

                                      的前缀索引,于是,还会对相关行的此非聚簇索引加排他锁。

                                             由于共享锁与排他锁是互斥的,当一方拥有了某行记录的

                                      排他锁后,另一方就不能其拥有共享锁,同样,一方拥有了某

                                      行共享锁后,另一方也无法得到其排他锁(可见,元凶是锁是

                                      逐行获取的,比如teamUser表有50行记录,语句一逐行获取锁,

                                      获取了1到50条的锁,语句2逐条获取锁,获取了100到51条的

                                      锁,当语句1要获取第51条记录的锁,语句2要获取第50条记录

                                      的锁,这个时候就出现了互相等待的状况)。所以两条语句同

                                      时行时,相当于两个事务会同时申请某相同记录行的锁资源,

                                      于是会产生锁冲突。由于两个事务都会申请主键索引,锁冲突

                                      只会发生在主键索引上。

                                 解决方案:

                                             InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力

                                      的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且

                                      也在SELECT语句提供非锁定读。这些特色增加了多用户部署

                                      和性能。但其行锁的机制也带来了产生死锁的风险,这就需

                                      要在应用程序设计时避免死锁的发生。以单个SQL语句组成的

                                      隐式事务来说,建议的避免死锁的方法如下:

                                      --  如果使用insert…select语句备份表格且数据量较大,在单

                                          独的时间点操作,避免与其他sql语句争夺资源,或使用

                                          select into outfile加上load data infile代替 insert…select,

                                          这样不仅快,而且不会要求锁定

                                      --  一个锁定记录集的事务,其操作结果集应尽量简短,以免

                                          一次占用太多资源,与其他事务处理的记录冲突。

                                      --  更新或者删除表格数据,sql语句的where条件都是主键或

                                          都是索引,避免两种情况交叉,造成死锁。对于where子

                                          句较复杂的情况,将其单独通过sql得到后,再在更新语句

                                          中使用。

                                      --  sql语句的嵌套表格不要太多,能拆分就拆分,避免占有资

                                          源同时等待资源,导致与其他事务冲突。

                                      --  对定点运行脚本的情况,避免在同一时间点运行多个对同

                                          一表进行读写的脚本,特别注意加锁且操作数据量比较大的

                                          语句。

                                      --  应用程序中增加对死锁的判断,如果事务意外结束,重新运

                                          行该事务,减少对功能的影响。

                        (d)锁升级造成锁队列阻塞

                                 提交:

                                 -  两事务操作同一行记录

                                 -  一事务对某一记录先申请共享锁,再升级为排他锁

                                 -  另一事务在过程中申请这一记录的排他锁

                                       例如,用户A查询一条纪录,然后修改该条纪录;这时用户B修

                                改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升

                                到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释

                                放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释

                                放共享锁,于是出现了死锁。这种死锁由于比较隐蔽,但在稍大点

                                的项目中经常发生。

                                       一般更新模式由一个事务组成,此事务读取记录,获取资源

                              (页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X)

                                锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新

                                数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁

                                的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共

                                享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以

                                进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都

                                等待另一个事务释放共享模式锁,因此发生死锁。

                                解决方法:

                                --  使用乐观锁进行控制

                                --  使用悲观锁进行控制

            表锁死锁

                   出现原因: 

                      一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B访

                      问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经

                      锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A

                      释放表A才能继续,这就死锁就产生了。

                      解决方法: 

                            这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的

                     逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,

                     尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A

                     和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,

                     要保证在任何时刻都应该按照相同的顺序来锁定资源。

五  "缓存" 一章中

       1.  Redis的持久化方式

            Rdb和Aof,可同时开启,同时开启时候,默认加载Aof

        2. 缓存穿透、缓存击穿、雪崩效应

          (1)缓存穿透

                   是指访问不存在的key,解决方案是使用布隆过滤器

                 布隆过滤器介绍

                                   布隆过滤器是一种多哈希函数映射的快速查找算法。它可以判断出某个元素肯定不在集合

                            里或者可能在集合里,即它不会漏报,但可能会误报。通常应用在一些需要快速判断某个元素

                            是否属于集合,但不严格要求100%正确的场合。

                                         一个空的布隆过滤器是一个m位的位数组,所有位的值都

                                  为0。定义了k个不同的符合均匀随机分布的哈希函数,每个函

                                  数把集合元素映射到位数组的m位中的某一位。

                 布隆过滤器原理

                                   布隆过滤器有点像HashMap,一个空的布隆过滤器是一个m位的位数组,所有位的值都为

                             0。定义了k个不同的符合均匀随机分布的哈希函数,每个函数把集合元素映射到位数组的m位

                             中的某一位。布隆过滤器是一个 bit 向量或者说bit 数组,长这样:

​                       ​​

                            何为增加一个元素?

                                              先把这个元素作为k个哈希函数的输入,拿到k个数组位置,然后把所有的这些位置置

                                      为1。

                            何为查询一个元素

 

                                              把这个元素作为k个哈希函数的输入,得到k个数组位置。这些位置中只要有任意一个

                                      是0,元素肯定不在这个集合里。如果元素在集合里,那么这些位置在插入这个元素时都

                                      被置为1了。如果这些位置都是1,那么要么元素在集合里,要么所有这些位置是在其他元

 

                                      素插入过程中被偶然置为1了,导致了一次“误报”。

                            增加一个元素和查询一个元素详细说明

                                              如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多

                                      个哈希值,并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的

                                      哈希函数分别生成了哈希值 1、4、7,则上图转变为:

 

                                   ​​​

 

                                      Ok,我们现在再存一个值 “tencent”,如果哈希函数返回3、4、8 的话,图继续变为:

                                  ​​​

 

                                             值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被

                                      覆盖了。现在我们如果想查询“dianping” 这个值是否存在,哈希函数返回了 1、5、8三

                                      个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,

                                      因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu”这个值是

                                      否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值

                                      均为 1,那么我们可以说 “baidu”存在了么?答案是不可以,只能是 “baidu”这个值可

                                      存在。这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会

                                      越来越多,这样某个值“taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位

                                      都被其他值置位了 1 ,那么程序还是会判断“taobao” 这个值存在。

                            误判率

                                           布隆过滤器只会误判不会误判。误判率就是在插入n个元素后,某元素被判断为“可能

                                    在集合里”,但实际不在集合里的概率,此时这个元素哈希之后的k个比特位置都被置为1。

                                           这里对误判率只做了简单的概述,如果工作中用到了误判率,请看博客:

                                           https://www.cnblogs.com/Jack47/p/bloom_filter_intro.html中的误判率一节。

                            删除元素

                                   布隆过滤器不支持删除元素

                           布隆过滤器的长度对误判率的影响

                                           很显然,过小的布隆过滤器很快所有的 bit 位均为 1,那么查询任何值都会返回“可能

                                    存在”,起不到过滤的目的了。布隆过滤器的长度会直接影响误报率,布隆过滤器越长其

                                    误报率越小。另外,哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1的

                                    速度越快,且布隆过滤器的效率越低;但是如果太少的话,那我们的误报率会变高。

           (2)缓存击穿

                         (a)什么是缓存击穿

                                         对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,

                                  是一种非常“热点”的数据。而恰好这些热点key缓存过期,这个时候大量的请求会访问数据

                                  库,这就是缓存击穿。

                         (b)解决方案

                                      (i)使用互斥锁

                                                     在根据key获得的value值为空时,先锁上,再从数据库加载,加载完毕,释放

                                              锁。若其他线程发现获取锁失败,则睡眠50ms后重试。至于锁的类型,单机环境

                                              用并发包的Lock类型就行,集群环境则使用分布式锁

                                     (ii)提前使用过期key

                                                     在value内部设置1个超时值(timeout1), 该超时值比实际的超时值(timeout2)

                                              小。当从缓存读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到

                                              缓存。然后再从数据库加载数据并设置到缓存中。

                                     (iii)设置永不过期

                                                      设置Redis的key永不过期,这就保证了热点key不会过期。把过期时间存在key

                                               对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建(例

                                               如,该方案redis自己维护一个timeout,当timeout小于System.currentTimeMillis())。             

                                               从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(

                                               非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是

                                               可以忍受。

                                     (iv)采用netflix的hystrix

           (3)缓存雪崩

                           缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全

                    部转发到DB,DB瞬时压力过重雪崩。

                    解决方案:

                           过期时间+随机值

        3.  Redis集群

             一致性hash,脑过

             Redis Cluster 有空看

             过期策略

        4.  Redis过期策略

             使用“定期删除+过期删除”

             注:过期的key对Rdb和Aof没有任何影响,压根不进入这两个文件

       5.  内存淘汰

                   “定期删除+过期删除”还是有可能漏掉很多key(这些key没有被查询,所以没有惰性删除),这些key

            会占用大量的内存空间,于是就有了内存淘汰机制。

                   redis内存淘汰指的是用户存储的一些键被可以被Redis主动地从实例中删除,从而产生读miss的情况,

           redis为了更好地使用内存,用一定的缓存miss来换取内存的使用效率。 我们可以通过配置redis.conf中的

           maxmemory这个值来开启内存淘汰功能。maxmemory0的时候表示我们对Redis的内存使用没有限制

           Redis的内存淘汰策略

                           noeviction:当内存不足以容纳新写入数据时,新写入操作会报

                                                错,这个一般没人用吧。

                            allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除

                                                最近最少使用的key(这个是最常用的)

                            allkeys-random:当内存不足以容纳新写入数据时,在键空间中,

                                                        随机移除某个key,这个一般没人用吧

                            volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间

                                                的键空间中,移除最近最少使用的key(这个一般不

                                                太合适)

                            volatile-random:当内存不足以容纳新写入数据时,在设置了过

                                                        期时间的键空间中,随机移除某个key

                            volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间

                                               的键空间中,优先移除更早过期时间的key。

           Redis执行内存淘汰的过程

                          --  客户端发起了需要申请更多内存的命令(如set)。

                          --  Redis检查内存使用情况,如果已使用

                          --  的内存大于maxmemory则开始根据用户配置的不同淘汰策略来淘汰内存(key),从而换取一

                              定的内存。

                          --  如果上面都没问题,则这个命令执行成功。

       6.   Redis常用命令

            自己去博客https://blog.csdn.net/jialanshun/article/details/81567621最下方翻 

 六  “JVM” 一章考点

       这个博客记得移动,没有移动完,因为两次标记找了更好的答案

       1.  JVM的内存结构

             栈帧包括:局部变量表、操作数栈、常量池、动态链接、方法出口信息等

            脑过

       2. 如何判断对象已死

           两次标记

                   一个对象从被判定为死亡对象到被垃圾收集器回收掉还要经历两次标记的过程,该过程可以认为是

           该对象在死刑的缓刑阶段。第一次标记:当可达性分析确认该对象没有引用链与GC Roots相连,则对其

           进行第一次标记和筛选,筛选的条件是重写了finalize()方法并没有执行过,对于重写了且并没有执行

           finalize()方法的对象这将其放置在一个F-Queue队列中,并在稍后由一个由虚拟机自动建立的低优先级

           的Finalizer线程去执行它。此处执行只保证执行该方法,但是不保证等待该方法执行结束,之所以这样

           子设计是为了系统的稳定性和健壮性考虑,以免该方法执行时间较长或者死循环导致系统崩溃。在此之

           后,系统会对对象进行第二次标记,如果在第一次标记之后的对象在执行finalize()方法时没有被引用到

           一个新的变量,这该对象将被回收掉。finalize方法只能被执行一次,并且一般不推荐也不建议重写

           Object的该方法,如果需要关闭外部资源,比如数据库,I/O等完全可在finally块中完成。以下是jdk中

           Object对象中finalize()方法的注释

       3.  常见的垃圾回收算法,垃圾回收器

            脑过

       4.  CMS回收器的

            关注的是系统的停顿时间

            缺点

            脑过

       3.  G1收集器的结构

            建立可预测的停顿时间模型属于分代垃圾回收器区分新生代和老年代,依然有eden和from、to区,

            它不要求新生代、老年代、eden、from、

            to区空间都连续,使用了分区算法

       4.  减少GC开销的编程技巧

          (1)计划好List的容量

          (2)能用基本类型如Int,long,就不用Integer,Long对象

          (3)尽量少用静态对象变量

          (4)对象不用时最好显式置为Null

          (5)尽量使用StringBuffer,而不用String来累加字符串

       5.  JVM优化实践,需要要记住几个时间,JVM调优命令

            如下时间是不需要优化的:

            Minor GC执行时间不到50ms;

            Minor GC执行不频繁,约10秒一次;

            Full GC执行时间不到1s;

            Full GC执行频率不算频繁,不低于10分钟1次;

            如下时间是需要优化的

            minor由之前的10ms延长到50ms左右

            FullGC的时间也由之前的0.5s延长到4、5s

            FullGC的次数越来越多,最频繁时隔不到1分钟

            就进行一次FullGC年老代的内存越来越大并且每次

            FullGC后年老代没有内存被释放。

            50、1、10、10

       6.“Java 应用性能调优实践”中掌握top vmstat free –m iostat

七  “分布式” 一章

          1.  分布式的两阶段提交协议和三阶段提交协议(这个面试一般不会问)

             (1)两阶段提交

                    (a)表决阶段(或称请求阶段)

                                    事务协调者通知每个参与者准备提交或取消事务,然后进入表决过程,参与者要么在本地执

                             行事务,写本地的redo和undo日志,但不提交,到达一种"万事俱备,只欠东风"的状态。请求阶

                             段,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执

                             行故障)。

                     (b)提交阶段(commit phase)

                                    在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参

                              与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事

                              务。参与者在接收到协调者发来的消息后将执行响应的操作。

                     两阶段提交协议的缺点:

                     --  同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他

                             第三方节点访问公共资源不得不处于阻塞状态。

                        --  单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,

                            协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

                          (如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于

                            阻塞状态的问题)

                        --  数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网

                            络异常或者在发送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了

                            commit请求。

             (2)三阶段提交协议

                             三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase commit protocol),是二

                      阶段提交(2PC)的改进版本。与两阶段提交不同的是,三阶段提交有两个改动点:

                      --  引入超时机制。同时在协调者和参与者中都引入超时机制。

                      --  两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。说白了这样减少了

                          阻塞。

                     三阶段提交协议详细介绍

                             --  CanCommit阶段

                                         3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与

                                  者如果可以提交就返回Yes响应,否则返回No响应。

                                  *  事务询问

                                             协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参

                                             与者的响应。

                                  *  响应反馈

                                            参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返

                                            回Yes响应,并进入预备状态。否则反馈No。

                             --  PreCommit阶段

                                        协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有

                                 以下两种可能。

                                 可能一:

                                        假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。

                                        *  发送预提交请求协调者向参与者发送PreCommit请求,并进入Prepared阶段。

                                        *  事务预提交参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到

                                           事务日志中(未提交事务)。

                                        *  响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

                                 可能二:

                                       假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的

                                       响应,那么就执行事务的中断:

                                        *  发送中断请求 协调者向所有参与者发送abort请求。

                                        *  中断事务参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),

                                           执行事务的中断。

                             --  doCommit阶段

                                               该阶段进行真正的事务提交,也可以分为以下两种情况:

                                        执行提交

                                        *  发送提交请求

                                                   协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所

                                                   有参与者发送doCommit请求。

                                        *  事务提交

                                                   参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放

                                                   所有事务资源。

                                        *  响应反馈

                                                   事务提交完之后,向协调者发送Ack响应。

                                        中断事务

                                               协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响

                                        应超时),那么就会执行中断事务。

                                        *  发送中断请求 协调者向所有参与者发送abort请求

                                        *  事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚

                                           操作,并在完成回滚之后释放所有的事务资源。

                                        *  反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息

                                        *  中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。

                                        *  完成事务 协调者接收到所有参与者的ack响应之后,完成事务。在doCommit阶段,如果参

                                           与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续

                                           进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在

                                           第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第

                                           二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了

                                           PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段

                                           时,由于网络超时等原因,虽然参与者没有收到commit或者abort响 应,但是他有理由相信:

                                           成功提交的几率很大。 )

          2.  基于MQ的分布式事务

               脑过,最终一致性

          3.  Zookeeper实现分布式锁的原理

             (1)客户端连接zookeeper,并在/lock下创建临时的且临时有序的子节点,第一个客户端对应的子节点为

                      /lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。

             (2)客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如

                      果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁。

             (3)执行业务代码;

             (4)完成业务流程后,删除对应的子节点释放锁

          4.  Zookeeper为什么使用临时节点而

          5.  Zookeeper的节点类型

               持久节点、持久有序、临时节点、临时有序节点

          6.  Zab协议

                     Zookeeper是可以集群复制的,集群间通过Zab协议来保证数据的一致性,该协议包括两个阶段,leader选

               举阶段和原子广播阶段。

                      集群中会选出一个leader,其它节点,大多数follower时,需要重新选取一个新的leader,让所有服务恢复到、

               一个正确的状态。当leader选举出来且大,多数服务器完成了和leader的状态同步后,leader选举过程就结束了,

               将进入原子广播的过程。原子广播同步leader和follower之间的信息,保证leader和follower具有相同的系统状态。

               

          7.  Zookeeper的四种事件

               -- 节点创建;

               -- 节点删除;

               -- 节点数据修改;

               -- 子节点变更

          8.  Zookeeper是如何知道服务上线和下线的

                       Zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连

               接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除,比如100.19.20.02这台机器

               如果宕机了,那么zookeeper上的路径就会只剩/HelloWorldService/1.0.0/100.19.20.01:16888。服务消费者会去

               监听相应路径(/HelloWorldService/1.0.0),一旦路径上的数据有任务变化(增加或减少),zookeeper都会通

               知服务消费方服务提供者地址列表已经发生改变,从而进行更新。更为重要的是zookeeper与生俱来的容错容

               能力(比如leader选举),可以确保服务注册表的高可用性。使用 zookeeper 作为注册中心时,客户端订阅服

               务时会向 zookeeper 注册自身;主要是方便对调用方进行统计、管理。但订阅时是否注册 client 不是必要行为,

               和不同的注册中心实现有关,例如使用 consul 时便没有注册。

          8.  Zookeeper作为dubbo注册中心的原理

                      简单的说是(心跳+临时节点+通知)

                      在zookeeper中,进行服务注册,实际上就是在zookeeper中创建了一个znode节点,该节点存储了该服

               务的IP、端口、调用方式(协议、序列化方式)等。该节点承担着最重要的职责,它由服务提供者(发布服务时

               )创建,以供服务消费者获取节点中的信息,从而定位到服务提供者真正网络拓扑位置以及得知如何调用。

               RPC服务注册、发现过程简述如下:

              --  服务提供者启动时,会将其服务名称,ip地址注册到配置中心。

              --  服务消费者在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地,以供后

                  续使用。当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服

                  务提供者的服务器调用服务。

                  注意:这里的客户端缓存根据Zookeeper的节点的更新而自动更新是应该是dubbo实现的。

              --  当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者IP列表中移除。同时,注册中心会将

                  新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。

              --  当某个服务的所有服务器都下线了,那么这个服务也就下线了。同样,当服务提供者的某台服务器上线

                  时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。

              --  服务提供方可以根据服务消费者的数量来作为服务下线的依据感知服务的。

          9.  为什么要使用服务注册中西你

             (1)集群A的服务调用者如何发现集群B的服务提供者

             (2)集群A的服务调用者如何选择集群B中的某一台服务提供者机器发起调用

             (3)集群B的服务提供者机器上/下线之后,集群A中调用者如何感知到这台机器的上/下线,不在对已下线

                      的机器发起调用。

            (4)集群B提供的某个服务如何获知集群A中那些机器正在消费该服务。

              概括的说就是:服务发现、服务路由、服务健康检查

          10.  Zookeeper和Eureka的区别             

               Eureka实现的是AP,Zookeeper实现的是CP;Zookeeper漫长的选举会导致整个集群不可用,还会丢失

               一些请求;Zookeeper本身是作为分布式协调服务而产生的,而Eureka是做为注册中心而产生的。 

          11.  dubbo原理

                 脑过     

          12.  dubbo支持协议

                 dubbo协议、hessian 协议、rmi、http 协议、webservice 协议、thrift 协议、memcached协议、redis 协

                 议(不知道还有没了,知道这些就行了)

          13.  dubbo和springcloud

               (1)Rpc VS Rest

              (2)框架完整度(网关、hystrix)

          14.  Rpc调用过程

                 --  一次Rpc调用大约分10次

                 --  服务消费方(client)调用以本地调用方式调用服务;

                 --  client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

                 --  client stub找到服务地址,并将消息发送到服务端;

                 --  server stub收到消息后进行解码;

                 --  server stub根据解码结果调用本地的服务;

                 --  本地服务执行并将结果返回给server stub;

                 --  server stub将返回结果打包成消息并发送至消费方;

                 --  client stub接收到消息,并进行解码;

                 --  服务消费方得到最终结果

          15.  微服务和soa

                 soa是面向服务的体系架构,微服务是在soa上做的升华,微服务强调一个重点“业务需要彻底的组件化

                 和服务化”。原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间

                 通过服务完成交互和集成。

                 微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想 + 80%的领域建模思想

          16.  dubbo的集群容错模式

                 记忆版:

                 --  失败自动切换其它服务器(可指定重试次数)

                 --  失败抛异常

                 --  失败不抛异常

                 --  定时重试

                 --  并行调用多个服务器,有一个成功就返回

                详细版:

              (1)Failover Cluster

                       失败自动切换,当出现失败,重试其它服务器(默认)。

                       * 通常用来读操作,但重试会带来更长延迟。

                       * 用于写操作的时候应该注意因为网络延时造成重复写的问题。如下图,当消费端访问服务A后,

                         服务A开始执行,但是因为网络故障消费端无法接到A的响应,消费端就认为是失败,所以消费

                         端又尝试请求服务B,这就造成重复写的问题。设置服务端的幂等性并设置reetries="0"。

                         reetries参数用来设置重试次数(不含第一次)。

                         示例:

                <dubbo:service retries="2" />                <dubbo:referenceretries="2" />                <dubbo:reference><dubbo:methodname="findFoo" retries="2" /></dubbo:reference>

 

              (2)Failfast Cluster

                       * 快速失败(只发起一次调用),抛出异常 

                       * 通常用于非幂等性的写操作,比如新增记录。

                         示例:

           <dubbo:service cluster="failfast" />           <dubbo:referencecluster="failfast" />

              (3)Failsafe Cluster

                       * 快速失败(只发起一次调用),不抛出异常 。

                       * 通常用于写入审计日志等操作

                       示例:

          <dubbo:service cluster="failsafe" />          <dubbo:referencecluster="failsafe" />

              (4)Failback Cluster

                       * 失败后定时重试 

                       * 通常用于新消息通知操作。

          <dubbo:service cluster="failback" />          <dubbo:referencecluster="failback" />

              (5)Broadcast Cluster (2.10开始支持)

                       广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所

                      有提供者更新缓存或日志等本地资源信息。                 

          17.  dubbo的负载均衡

                 随机(可按权重设置)

                 轮询

                 最小活跃数

                 一致性hash

          18.  dubbo的服务降级

                              

                                        *  

转载于:https://www.cnblogs.com/jialanshun/p/10574228.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值