MySQL实战学习总结

一、MySql架构

1.redo log和binlog

  • redo log 是 InnoDB 引擎特有的,而binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志

2.redo log和binlog协助处理流程

使用两阶段提交。先写redo log为prepare,后写binlog,然后更新redo log为commit状态。

3.InnoDB 的 redo log细节

简单说就是固定大小的一块地址,可以循环写,有两个指针,一个代表写入,一个代表擦除(持久化到DB了)

注:只要redolog有了,那么数据就不会丢了。

 

4.InnoDB 的索引模型

InnoDB 使用了 B+ 树索引模型,每一个索引在 InnoDB 里面对应一棵 B+ 树
左边为主键索引(聚簇索引),主键索引叶子节点存储的是整行数据;右边为非主键索引(普通索引、二级索引),叶子节点存储内容是主键的值。
使用主键索引查询可以直接获得数据,而使用普通索引需要先搜索得到主键的值,然后再根据主键查询主键索引得到数据(这个过程叫回表)。

 

Spring

一、Spring三大特性

IOC控制反转、AOP面向切面编程、DI依赖注入。

1.IOC控制反转

将创建对象的权利交给Spring来进行处理,可以减低计算机代码之间的耦合度。

  • 作用:解耦(减低程序间的耦合性)。
  • 优点:解耦,降低程序间的依赖关系;
  • 缺点:使用反射生成对象,损耗效率。

注:最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

2.AOP面向切面编程

将纵向重复的代码(公共行为和逻辑)横向抽取出来并封装为一个可重用的模块,这个模块就是“切面”(Aspect)。简单的说就是将程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在每个流程里面都能执行到,而不用重复开发代码。

  • 优点:减少重复代码;提高开发效率;维护方便。
  • 主要用于:权限认证、日志、事务处理、埋点等。

3.DI依赖注入

  • 创建对象实例时,为这个对象注入对应属性值。
  • Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。

 

二、Spring MVC 和Spring boot

都属于spring这个轻量级java开发框架。springMvc属于WEB开发的MVC框架,包含模型、前端视图、控制器(也就是对应逻辑)。springBoot框架相对于springMvc框架来说, 更专注于开发微服务后台接口,不开发前端视图,简化了配置流程,不需要配置xml等。

Java基础知识

一、JAVA面向对象的三大特征

封装、继承、多态

1.封装:

隐藏实现细节,提高代码的复用性,提高了安全性。

2.继承:

多个事物直接有共同的属性和行为放到父类中,特有的放在子类中,子类可以继承父类的属性和行为。通过extends关键字。

注:JAVA只支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。但可以多层继承。子类访问父类的成员变量通过super关键字。

  • 优点:可以继承父类的特征
  • 缺点:如果多层父类有相同名字的实例变量或者相同的方法,调用可能会产生歧义,不知道用的哪个父类的。

3.多态:

子类的对象放在父类的引用中,如 Animal a=new Cat(),子类的对象当父类对象来用。

优点:提高了程序的扩展性和复用性

缺点:通过父类引用操作子类对象时,只能用父类中有的方法,不能操作子类特有的方法。

注:多态的前提:1、必须有关系:继承、实现  2、通常都有重写的操作

3.1 向上转型:

当父类的引用指向子类的对象时,就发生了向上转型,即把子类类型转成了父类类型。

优点:隐藏了子类类型,提高了代码的扩展性

弊端:只能使用父类的内容,无法使用子类特有的功能

3.2 向下转型:

当要使用子类特有的功能时,就需要使用向下转型

优点:可以使用子类的 特有功能

弊端:需要面对具体的子类对象时,向下转型时容易发生ClassCastException类型转换异常。所以转型前必须要做判断。

3.3 对象的强制转换:

格式: 引用 instanceof 类型 判断当前对象是否是引用类

用法: Animal a1=new Dog();

if( !Cat instanceof a1){ //判断当前对象是不是Cat类型

}

Cat d=(Cat)a1;

二、java异常

1.异常

Java主要分为Error错误Exception异常两类。Error(错误)是程序无法处理的错误,表示运行应用程序中较严重问题。Exception(异常)是程序本身可以处理的异常。Exception(异常)又包含运行时异常非运行时异常(编译异常)

  • Error:OOM等
  • 运行时异常:ArrayIndexOutOfBoundException数组越界、NullPointerException空指针等。
  • 非运行时异常:如IOException IO异常。

2.可查的异常和不可查异常

Java的异常(Throwable)分为可查的异常(checked exceptions)不可查的异常(unchecked exceptions)

 

  • 可查异常必须要去处理,否则编译不通过,如IOException和ClassNotFoundException等。

处理异常方法:要么用try-catch语句捕获它,要么用throws子句声明抛出它。

  •  不可查异常(编译器不要求强制处置的异常)包括运行时异常和错误,不用捕获对应异常,应该找出错误程序进行修改。

三、基础知识点汇总

1.java中extends和implements

extends是继承类,implements是实现接口。 类只能继承一个,接口可以实现多个。
extends继承父类的时候可以重写父类的方法,implements实现接口,必须实现接口的所有方法。

abstract class A {
    abstract m(): void;
}


class B extends A{
}

class C implements A {
    m(): void { } //必须要实现定义在A中的所有方法
}

 

2.基本类型和包装类型

2.1 区别

  • 包装类型对应变量default值是 null ,而基本类型有默认值且不是 null
  • 包装类型可用于泛型,而基本类型不可以。
  • 基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,几乎所有对象实例都存在于堆中。
  • 相比于对象类型, 基本数据类型占用的空间非常小。

2.2 装箱与拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

举例:

Integer i = 10;  //装箱
int n = i;   //拆箱

3.接口和抽象类

3.1 共同点 :

  • 都不能被实例化。
  • 都可以包含抽象方法。
  • 都可以有默认实现的方法(Java 8 可以用 default 关键字在接口中定义默认方法)。

3.2 区别 :

  • 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
  • 一个类只能继承一个类,但是可以实现多个接口。
  • 接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。

4.深拷贝、浅拷贝、引用拷贝

浅拷贝、深拷贝、引用拷贝示意图

  • 引用拷贝:两个不同的引用指向同一个对象
  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),对象内部的属性是引用类型的话,指向的是同一地址。
  • 深拷贝 :深拷贝会完全复制整个对象。

5.== 和 equals()

  • 对于基本数据类型来说,== 比较的是值。
  • 对于引用数据类型来说,== 比较的是对象的内存地址。

 ==:比较的是值是否相等(基本类型比较值,引用类型(对象)比较的是内存地址)

equals():只能用来判断对象,不能用于基本数据类型。不重写等同于==,重写可以自定义。

6.String、StringBuffer、StringBuilder 的区别

  • String :里面的对象是不可变的,线程安全。适用于操作少量数据
  • StringBuffer :线程安全。适用于多线程操作大量数据。
  • StringBuilder:非线程安全的。适用于单线程操作大量数据。

四、反射

赋予了我们在运行时分析类以及执行类中方法的能力。通过反射可以获取任意一个类的所有属性和方法。

优点:代码更加灵活、为各种框架提供开箱即用的功能提供了便利。

缺点:不安全(比如无视泛型参数的安全检查),性能会变差。

应用场景:业务场景使用较少,框架使用较多。像 Spring/Spring Boot、MyBatis这些框架, Spring 里面的注解,都用了反射机制。里面也用了动态代理,动态代理的实现也依赖反射。

五、集合

1.java集合框架

两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。

2. List, Set, Queue, Map 四者的区别

List: 有序、可重复。
Set: 无序、不可重复。
Queue: 排队,有序、可重复。一般用于排队功能的叫号机。
Map: 使用键值对(key-value)存储。

List

  • ArrayList: Object[] 数组
  • VectorObject[] 数组
  • LinkedList: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)

Set

  • HashSet(无序,唯一): 基于 HashMap 实现,底层采用 HashMap 来保存元素
  • LinkedHashSet: 通过 LinkedHashMap 实现。
  • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)

Queue

  • PriorityQueueObject[] 数组来实现二叉堆
  • ArrayQueueObject[] 数组 + 双指针

Map

  • HashMap: 数组+链表组成,链表长度大于阈值(默认为 8)会出现红黑树转换。
  • LinkedHashMap: 继承自 HashMap,增加了一条双向链表,可以实现顺序访问。
  • Hashtable: 数组+链表组成的,数组是 Hashtable 的主体,链表则是主要为了解决哈希冲突而存在的
  • TreeMap: 红黑树(自平衡的排序二叉树)

注:上图是LinkedHashMap的存储结构,在hashMap的基础上多了一个双向链表,可以实现顺序访问。

3.区别

3.1 ArrayList 和 Vector :

  • ArrayList 是 List 的主要实现类,底层使用 Object[ ]存储,线程不安全 ;
  • Vector 是 List 的古老实现类,底层使用Object[ ] 存储,线程安全的。

3.2 ArrayList 与 LinkedList:

  • 数据结构:一个数组一个链表数组。LinkedList 是 双向链表 (JDK1.6 之前为循环链表,JDK1.7 取消了循环)
  • 访问速度:数组可以快速随机访问,链表不行
  • 更新:链表对中间数据插入较友好好。
  • 线程安全:都不是线程安全的;
注:很少使用LinkedList

3.3 HashSet、LinkedHashSet 和 TreeSet

  • 都是 Set 接口的实现类,元素唯一,并且都不是线程安全的。
  • HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于 FIFO 的场景,TreeSet 用于元素需要自定义排序场景。

3.4 Queue 与 Deque 

  • Queue :单端队列,一端入,另一端出,一般遵循 先进先出(FIFO) 规则
  • Deque :双端队列,在队列的两端均可以插入或删除元素。

3.5HashMap 和 Hashtable

  • HashMap 非线程安全,Hashtable 是线程安全的(内部方法基本都经过synchronized 修饰)。
  • HashMap 要比 Hashtable 效率高一点。

注:基本不会用Hashtable,要线程安全就用ConcurrentHashMap

3.6ConcurrentHashMap在jdk1.7和1.8的区别

  • 数据结构:1.7是Segment 数组 + hashMap(HashEntry 数组 + 链表),1.8是 Node 数组 + 链表 / 红黑树。相当于原来是Segment和hashMap的数组是映射关系,现在合并了。
  • 锁1.7是Segment 分段锁,针对Segment(段/槽)加锁,1.8后锁的力度更细了,锁粒度更细,synchronized 只锁定当前链表或红黑二叉树的首节点。
  • 1.7的分段锁继承自 ReentrantLock。1.8 放弃了分段锁设计,采用 Node + CAS + synchronized 保证线程安全。
  • 并发度 :JDK 1.7 最大并发度是 Segment 的个数,默认是 16。JDK 1.8 最大并发度是 Node 数组的大小,并发度更大

六、并发编程

1.进程和线程

进程:是系统运行程序的基本单位。一个进程在其执行的过程中可以产生多个线程。

线程:是一个比进程更小的执行单位。同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。多线程之间切换负担要比进程小得多。

2.并发与并行的区别

:两个及两个以上的作业在同一 时间段 内执行。
:两个及两个以上的作业在同一 时刻 执行。

3.同步和异步的区别

同步 : 发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
异步 :调用在发出之后,不用等待返回结果,该调用直接返回。

4.线程的生命周期和状态

5.sleep() 方法和 wait() 方法

  • 都可以暂停线程的执行。
  • sleep() 方法没有释放锁,而 wait() 方法释放了锁 。
  • sleep() 是 Thread 类的静态本地方法,wait() 则是 Object 类的本地方法(本质是每个对象都有对象锁,要释放当前线程占有的对象锁,所以操作的是对象而不是线程)。
  • wait() 需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法唤醒。sleep()方法执行完成后,线程会自动苏醒。

6.可以直接调用 Thread 类的 run 方法吗

可以但不会以多线程的方式执行,用当前线程执行的。

new 一个 Thread后,需要调用 start()方法,start()方法作用是启动一个线程处于就绪状态,当分配到时间片后就可以开始运行,这时候该线程会自动执行 run() 方法进行执行,这样就是多线程了。省略了start,那么就没有额外的线程,会用当前线程执行。

7.volatile

作用:保证变量的可见性,禁止指令重排。

7.1 如何保证变量可见性

volatile修饰的,不会有在工作内存有变量副本,线程直接从主内存取。

7.2 如何禁止指令重排

单例模式(线程安全) :

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

 uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

JVM 的指令重排可能导致执行顺序不是1->2->3,而是 1->3->2。这样再多线程情况下,1->3的时候,另一个线程读到的就不是null了。

8.synchronized

8.1 作用域

修饰方法

修饰对象:根据修饰的对象不同可以分为全局锁(xxx.class)或者代码块.

8.2 底层原理

通过 JDK 自带的 javap 命令,可以查看java字节码.class信息,发现synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令;synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。不过两者的本质都是对对象监视器 monitor 的获取。

8.3 锁升级

偏向锁、轻量级锁、重量级锁

用synchronized修饰的会有锁升级过程,如果只有一个线程获取锁,那么这个锁会偏向这个线程,叫做偏向锁;如果这时候有其他线程也来获取这个锁,那么这个锁会变成轻量级锁,获取不到锁的线程会自旋,然后重新获取;如果还是获取不到,则会变为重量级锁,获取不到的线程会阻塞(锁池状态)。

8.4 synchronized 和 ReentrantLock 的区别

  • 两者都是可重入锁
  • synchronized非公平锁,ReentrantLock默认也是非公平锁,不过可以有参数可以配置变成公平锁
  • synchronized 依赖于 JVM , ReentrantLock 依赖于 API(JDK 层面实现),需要lock() 和 unlock() 方法配合 try/finally 语句块来完成
  • ReentrantLock功能更多,如可以变成公平锁

9.ThreadLocal

为了实现每个线程都有自己的专属本地变量。通过创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,从而避免了线程安全问题。

9.1 ThreadLocal 原理

// 源码:
public class Thread implements Runnable {
    //......
    //与此线程有关的ThreadLocal值。由ThreadLocal类维护
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    //......
}

ThreadLocal内部有一个ThreadLocalMap,每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。

10.线程池

10.1为什么使用线程池

  • 降低资源消耗:可以重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  • 提高响应速度:当任务到达时,用已有线程可以直接执行。
  • 方便管理:统一创建,分配,调优和监控。

10.2 实现 Runnable 接口和 Callable 接口的区别

  • Runnable自 Java 1.0 以来一直存在,Callable在 Java 1.5 中引入
  • Runnable 接口不会返回结果或抛出检查异常,但是 Callable 接口 可以。所以如果任务不需要返回结果或抛出异常就使用 Runnable 接口 ,看起来更简洁。

10.3  execute()方法和 submit()方法的区别

  • execute()方法:没有返回值,无法判断任务是否被线程池执行成功;
  • submit()方法:线程池会返回一个 Future 类型的对象,可以判断是否执行成功。此外可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成。

10.4 四类线程池

CachedThreadPool: 可根据实际情况动态调整线程数量的线程池。

FixedThreadPool : 固定线程数量的线程池。

SingleThreadExecutor: 只有一个线程的线程池。

ScheduledThreadPool:调度类的线程池。

 

10.4.1 几个核心参数:

  • corePoolSize : 核心线程数。
  • maximumPoolSize : 最大线程数。
  • workQueue: 工作队列。
  • keepAliveTime:非核心线程(临时线程)存活时间;
  • handler :饱和策略。主要有报错、丢弃当前任务、丢弃最早未处理的任务、用提交任务的线程执行这四种。

10.4.2 大致流程

进来任务时,用核心线程做任务;如果任务超过核心线程数,则放入队列;如果任务还在增加,则创建临时线程执行,直到最大线程数;如果还在增加,则按照对应的饱和策略处理。

11.JUC 包中的4大Atomic原子类

基本类型:使用原子的方式更新基本类型

  • AtomicInteger:整型原子类
  • AtomicLong:长整型原子类
  • AtomicBoolean:布尔型原子类

数组类型:使用原子的方式更新数组里的某个元素

  • AtomicIntegerArray:整型数组原子类
  • AtomicLongArray:长整型数组原子类
  • AtomicReferenceArray:引用类型数组原子类

引用类型:

  • AtomicReference:引用类型原子类
  • AtomicStampedReference:原子更新带有版本号的引用类型。
  • AtomicMarkableReference :原子更新带有标记位的引用类型

对象的属性修改类型

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器
  • AtomicReferenceFieldUpdater:原子更新引用类型字段的更新器

12.AQS

AQS 的全称为(AbstractQueuedSynchronizer),是一个用来构建锁和同步器的框架。如: ReentrantLock,Semaphore信号量等都是用的AQS框架。

12.1 AQS 原理

通过ReentrantLock加锁说明:简单的说就是,AQS对象内部有一个volatile的int变量,初始state的值是0,一个线程要来加锁时,通过CAS把0改为1,代表加锁成功,释放锁就是再减回去,减到0就代表锁释放了。

另外AQS内部还维护着一个先进先出的等待队列,如果其他线程也来加锁,但是因为锁已经被其他线程占用而加锁失败,就会存放这些加锁失败的线程。

 

注:CLH(Craig,Landin and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

12.2 AQS 组件总结

  • ReentrantLock:一次只允许一个线程访问某个资源,可重入。
  • Semaphore(信号量)-允许多个线程同时访问问: synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。
  • CountDownLatch (倒计时器): 可以让某一个线程等待直到倒计时结束,再开始执行。
  • CyclicBarrier(循环栅栏): 让一组线程到达栅栏时被阻塞,直到最后一个线程到达时,栅栏被推倒,所有线程开始执行。

 

七、高cpu占用优化

1.背景:

saas服务启动时,cpu占用太高。

2.过程:

  1. 首先确定CPU占比高的进程,线程ID。通过windows提供的工具Process Explorer可查到saas服务的进程ID(PID), 以及内部线程占用cpu情况。实时显示选中的进程内部线程ID,占用CPU情况。 操作saas,记录下占比CPU比较高的线程ID。
  2. 通过Jstack工具查看进程内部的线程执行情况: jstack -l PID > XXX.stack .
  3. 通过线程ID在jstack文件中找到消耗cpu比较大的线程为:C2 CompilerThread0 .在server模式下启动jar包后默认是开启tiered compiler对javac产生的字节码进行优化,这个线程消耗资源比较多。
  4.  用 -client 模式启动jar包后, 初步看内存占用从400+MB降到200+MB,CPU占比高的持续时间明显降低。

3.分析:

因为我们都知道JIT( just in time ), 也就是即时编译编译器。使用即时编译器技术,能够加速 Java 程序的执行速度。主要有Server 模式和 client 模式两种启动模式,主要的差别在于:-server 模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。原因是:当虚拟机运行在-client 模式的时候,使用的是一个代号为 C1 的轻量级编译器,而-server 模式启动的虚拟机采用相对重量级代号为 C2 的编译器。C2 比 C1 编译器编译的相对彻底,服务起来之后,性能更高。

 

Hadoop

一、背景

一个任务:需要计算一个100M的文本文件中的单词的个数。写程序可以解决。

需要计算1T的文本文件汇总单词的个数,就需要有Hadoop了。

所以,简单说Hadoop就是存储海量数据和分析海量数据的工具。

二、Hadoop

Hadoop是Apache基金会开发的分布式系统基础架构。用java编写的,在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架,其核心部件是HDFS与MapReduce。

1.Hadoop框架的核心

HDFS:一个高度容错性的分布式文件系统,为海量的数据提供了存储。可以理解为一个分布式的,有冗余备份的,可以动态扩展的用来存储大规模数据的大硬盘。

MapReduce:为海量的数据提供了计算。可以理解成是一个计算引擎,按照MapReduce的规则编写Map计算/Reduce计算的程序,可以完成计算任务。

 

2.Hadoop作用

大数据存储:分布式存储

日志处理:擅长日志分析

数据挖掘:目前比较流行的广告推荐,个性化广告推荐

分布式事务框架

一、阿里开源框架Seata

1.正常提交

2.回滚

 

  • Seata实现2PC要点

1、全局事务开始使用 @GlobalTransactional标识 。

2、每个本地事务方案仍然使用@Transactional标识。

3、每个数据都需要创建undo_log表,此表是seata保证本地事务一致性的关键。

二、TCC模型

  • Try 阶段:对应 2PC 中一阶段的准备提交事务(Prepare);
  • Confirm 阶段:对应 2PC 中二阶段事务提交(Commit)。默认 Confirm 阶段是不会出错的,只要 Try 成功,Confirm 一定成功;
  • Cancel 阶段:对应 2PC 中二阶段事务回滚(Rollback)。

1.空提交

对于空提交,出现的情况为:第一阶段TM调用RM超时或者下游返回明确失败,此时TM仍旧调用下游执行提交的第二阶段,这对于RM来说,由于并没有Prepare阶段,然后收到了Commit请求,这就是一次空提交,对于TCC模型来说,空提交为系统的bug,需要业务进行处理。

2.空回滚

如图所示,TM在调用下游服务的一阶段Prepare操作时,因为超时而导致下游服务并没有收到请求,此时,TM会触发二阶段回滚操作,调用下游服务执行Rollback操作,因为下游服务在没有收到Prepare请求的情况下收到Rollback请求,这种场景被称为空回滚。 空回滚在实际使用中可能会出现,而且被视为正常情况,在处理业务逻辑时需要处理。

3.事务悬挂

如图所示,TM在调用下游服务的一阶段Prepare操作时,可能因为各种原因而TM没有收到下游响应,此时TM会执行Rollback操作。而对于下游来说,可能存在的一种情况是:下游服务先收到了Rollback请求,执行了空回滚操作,然后又收到了Prepare请求,然后执行了Try操作,此时,该下游服务的事务状态将永远存在于Prepare阶段,这种情况就叫做事务悬挂。 对于事务悬挂,目前处理的做法是设置事务悬挂检测程序,检测下游服务处于Prepare状态而上游服务处于Rollback的事务,调用下游服务执行二次Rollbacki操作。

三、基于消息中间件

1.A处理事务时,先向消息中间件发送一条消息,中间件进行消息持久化并返回应答; 2.A收到应答后开始处理并提交事务,提交成功后向消息中间件发送Commit请求(此时消息有可能丢失,如果发丢,由消息中间件的事务回查机制完成) 3.消息中间件收到Commit请求后便向B投递该消息; 4.B收到后开始处理事务,处理成功后向消息中间件返回应答 5.如果向B投递的消息未送达,则消息中间件重新投递该消息

通过以上流程实现了类2pc的流程,此时的消息中间件充当了TM的角色。

注:消息中间件有一个回查任务,定期扫描非最终态的消息,进行回查

四、其他

1.RocketMQ事务消息

执行流程:
1、Producer向Broker端发送Half Message;
2、Broker ACK,Half Message发送成功;
3、Producer执行本地事务(executeLocalTransaction);
4、本地事务完毕,根据事务的状态,Producer向Broker发送二次确认消息,确认该Half Message的Commit或者Rollback状态。Broker收到二次确认消息后,对于Commit状态,则直接发送到Consumer端执行消费逻辑,而对于Rollback则直接标记为失败,一段时间后清除,并不会发给Consumer。正常情况下,到此分布式事务已经完成,剩下要处理的就是超时问题,即一段时间后Broker仍没有收到Producer的二次确认消息;
5、针对超时状态,Broker主动向Producer发起消息回查(checkLocalTransaction);
6、Producer处理回查消息,返回对应的本地事务的执行结果;
7、Broker针对回查消息的结果,执行Commit或Rollback操作,同Step4。

 

 

RocketMQ

一、MQ对比

性能 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级 万级 十万级 十万级
Topic数量对吞吐量的影响 Topic可以达到几百、几千个的级别,吞吐量会有小幅下降 Topic从几十个到几百个的时候,吞吐量会大幅下降
时效性 ms µs级 ms级 ms级以内
可用性 高,主从架构 高,主从架构 非常高,分布式架构 非常高,分布式架构
消息可靠性 有较低的概率丢失数据 经过参数配置,可以做到零丢失 经过参数配置,可以做到零丢失
负载均衡 支持 不支持 支持 支持
优势 非常成熟,功能强大,文档丰富,支持多语言 erlang语言开发,延时很低,管理界面友好,支持多语言,社区活跃 接口简单易用,分布式扩展方便,社区活跃,支持大规模的Topic,支持多种消费方式 极高的可用性和可靠性,分布式扩展方便
劣势 偶尔有较低概率丢失消息,社区活跃度不高 erlang语言开发不容易进行定制开发,集群动态扩展麻烦 只支持Java,接口不是按照标准JMS规范走的,有的系统迁移要修改大量的代码 大量topic下吞吐量降低。

RocketMQ和kafka对比:

性能:差不多,都支持10万级别。但是kafka在topic达到几百的时候性能下降严重,RocketMQ略微会有下降。

稳定性:都是分布式架构,稳定性较高。

可靠性:都可以做到零丢失

其他:RocketMQ支持消息重试、死信队列,kafka不支持,kafka主要用于大数据。

 

为什么选RocketMQ:高吞吐单机可以达10万,分布式架构,服务稳定,消息可靠,可以做到0丢失。

二、RocketMQ介绍

1.整体架构

 

 

Name Server:为 Producer 和 Consumer 提供路由信息。路由到Broker的信息。可以理解成是Broker的发现和注册。保存Topic和Broker的关系,即哪个Topic哪个Queue在哪个Broker上。

Producer:生产消息。

Consumer:消费消息。

Broker:存储消息的地方。

Topic:消息的逻辑分类,是生产者在发送消息和消费者在拉取消息的类别。

2.四种消息类型

  • 普通消息:没有什么特殊的地方,就是普通消息
  • 延迟消息:延迟特定的时间间隔后消息才会被消费者消费。目前只支持特定级别的延时消息(1s到2h多个级别),每一个延迟级别单独有一个定时器,定时(每隔1秒)拉取对应延迟级别的消费队列。
  • 顺序消息:对于指定的一个 Topic,Producer保证消息顺序的发到一个队列中,消费的时候需要保证队列的数据只有一个线程消费。
  • 事务消息:通过两阶段提交、状态定时回查来保证消息一定发到broker。

3.三种消息发送方式

消息发送方式 介绍 优点 缺点
同步发送 消息发出后,在收到接收方发回响应之后才发下一个数据包 简单 耗时高
异步发送 发出数据后,不等接收方发回响应,接着发送下个数据包。 (需要用户实现异步发送回调接口(SendCallback),在回调接口中对发送结果进行处理) 耗时短 需要实现SendCallback接口
单向发送 发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。 耗时短,微秒级别 可靠性会降低

4.消息发送流程

5.消息存储

  • CommitLog:存消息的文件(采用文件系统存储),单个Broker下的所有队列共用一个CommitLog。
  • ConsumeQueue:是消息消费队列文件,消息达到commitlog文件后将被异步转发到消息消费队列,供消息消费者消费;
  • IndexFile:消息索引文件,记录key和offset的对应关系。提供了一种可以通过key和时间区间来查询消息的方法。

总结一下:所有消息都记录在CommitLog,生产者生产完一个消息后,会dispatch到对应的某个ConsumeQueue中,然后消费者来当前Queue消费消息时,根据对于的偏移量到CommitLog找到对应的消息进行消费。IndexFile只是提供了可以根据key和对应时间来找对应消息的一种方法。

注:只要消息写入了CommitLog,那么这个消息就不会丢失了。

6.Consumer消费方式

有拉取式消费(Pull Consumer)以及推动式消费(Push Consumer)两种方式。pull就是消费者定时轮询Broker,push就是Broker收到消息时主动推送给消费者。

7.消息刷盘

同步刷盘:只有消息真正持久化到磁盘,Broker才会给Producer一个成功的ack响应。消息可靠性有保障,但是性能会有影响。

异步刷盘:只要消息写入PageCache即给Producer一个成功的ack响应,消息刷盘采用后台异步线程提交的形式进行。

三、常见问题

1.RocketMq快的原因

RocketMq与Kafka在写消息与发送消息上,继续沿用了Kafka的这两个方面:顺序写和零拷贝

1)顺序写

从磁盘读数据时,需要找到数据在磁盘上的地址(寻址),再进行读写,寻址需要的时间会比较长。但Kafka 的数据存储在磁盘上面,依然性能很好,这是因为,Kafka采用的是顺序写,直接追加数据到末尾。实际上,磁盘顺序写的性能极高,基本和内存速度一致,磁盘的顺序写这一机制,极大地保证了Kafka本身的性能
2)零拷贝
读取文件,再用socket发送出去这一过程,传统方式需要先读取再发送,会经过以下四次复制:
1、将磁盘文件,读取到操作系统内核缓冲区Read Buffer
2、将内核缓冲区的数据,复制到应用程序缓冲区Application Buffer
3、将应用程序缓冲区Application Buffer中的数据,复制到socket网络发送缓冲区
4、将Socket buffer的数据,复制到网卡,由网卡进行网络传输


第2次和第3次数据的复制过程,不仅没有任何帮助,反而带来了巨大的开销。使用零拷贝,就是说,直接由内核缓冲区Read Buffer将数据复制到网卡,省去第二步和第三步的复制。

注:RocketMq的存储消息的文件CommitLog的大小规定为1G。是因为零拷贝技术有限制,传输的文件不能超过2G。

2.消息有序是怎么实现的

全局有序:将Topic配置成只有一个MessageQueue队列。

局部有序:将有序的一组消息都存入同一个MessageQueue。(发送消息时可以指定MessageSelector对象,可以发到对应的MessageQueue)

注:因为有MessageQueue存在,消费者会从多个MessageQueue消费数据,所以不管全局有序还是局部有序,都是利用的MessageQueue的FIFO设计。

 

 

编程语言分类

一、语言类型

  • 机器语言:0和1的二进制代码

如:0000,0001,000000000001

  • 汇编语言:指令采用了英文缩写的标识符,更容易识别和记忆

如:mov edx, len

  • 高级语言:去掉了与具体操作有关但与完成工作无关的细节

如:printlnf(“hello world”)

二、高级语言分类

1.编译型:在程序执行之前,将程序源代码“翻译”成目标代码(机器语言),之后可以脱离其语言环境独立执行(编译后生成的可执行文件)。

优缺点:编译后程序运行时不需要重新翻译,直接运行,程序执行效率高,依赖编译器,跨平台性差些。如Go、C、C++、Delphi等

 

2.解释型:应用程序源代码一边由相应语言的解释器“翻译”成目标代码(机器语言),一边执行,效率比较低。

如Python、PHP、Ruby等语言。

注:Java既不属于传统的编译型语言,也不属于解释型语言,Java是先编译成“.class”字节码文件,然后再利用JVM虚拟机进行解释执行的,所以Java即可以说成编译型,也可以说成解释型。

雅思每日任务

一、个人

1.口语

  • 每天背三句
  • [背词+听+影子跟读]P1P2P3素材
  • P1、P2写答案并录音练习
  • [背+复述+模仿]P3的100道题目及重点词句
  • 复习课件
  • 8句话扩展每天练一遍

2.阅读

  • 背诵538同义替换 30分钟
  • 背诵阅读14天同替 20分钟
  • 做一套题
  • 复习课件,背诵对应积累的词

3.听力

  • 语料库50分钟(看三遍):95%正确率  (p0)
  • 点听:用c8或者c14  (p1)
  • 复听:用c8或者c14 (p2)
  • 做一套题:一步一步练,先练c11 12 13 15 的s1和s4,后练习s2、s3,分析题干和点复听。 (p2)
  • 考前一周集训:地图训练  (p1)
  • 背诵179同义替换 20分钟 (p2)
  • 复习课件

4.写作

  • 写一篇作文
  • 复习课件

二、官方

1.听力

  • 复习课件——最为重要——方法论一定要超级熟练背诵(中文+英文);8句话扩展每天练一遍
  • 每天背三句
  • P1P2P3素材背词、反复听、努力且不纠结地进行影子跟读
  • 评分标准解析视频课+做笔记视频课
  • 阅读文章练习
  • 准备并练习答题
    • P1不要背答案,自己练习录音回答,听自己的回答、改错、提升
    • P2不要背答案,可以直接录音回答,也可以把自己的答案写下来之后,再练习回答
    • P3练习初期可以背/复述/模仿我的回答,充分模仿我的回答(词汇、句型、思路)

 

三、资料

1.口语每天推8句话

  • what I love the most about my neighborhood is all the good amenities there
  • for me, the best way to relax is just hanging out with my parents
  • my primary school was so close to our house that I would just walk to school every day
  • I should definitely do more to protect the environment
  • we see advertisements everywhere
  • the house that I grew up in was kind of big
  • sitting in the back of the car appeals to me more cuz I can do my own thing back there
  • I haven’t planted any flowers, but I’ve kept some

从这八句话开始推,每天推一遍,每一天推出来的最好不一样

雅思口语八

一、part3无观点怎么办

注:p3有观点就照着观点进行扩展阐述,没观点千万不要硬想观点,会造成流利度下降,就说第一反应+why,然后进行扩展。

1. Ask the examiner to repeat or clarify the question

2.First reaction+why

3.Focus on what you know

1. Ask the examiner to repeat or clarify the question

  • Sorry, could you say it again?
  • I’m sorry, would you please repeat the question?
  • What do you mean by …?
  • I’m not sure what … means. Could you explain it please?
  • I’m trying to get it right. Are you asking me …?

 

2.First reaction+why

  • That’s a tough/tricky/difficult question.
  • That’s an interesting question.
  • I’m not sure about this.
  • I don’t know much about this.
  • I’ve never thought about it before.
  • Oh, I have thought about it before.
  • I don’t think it’s a big problem at all.
  • I’m not sure if I agree with you.
  • I don’t suppose they should take a gap year after high school.

例子:

  • Why do most people smile in photographs?
  • How can you tell whether a website is reliable or not?
  • What are the consequences if children don’t like to share?
  • Are films a waste of money?

回答示例:

Q: Why do most people smile in photographs?

​Why do most people smile in photographs? ​Um, why do they smile? ​Well…​ Really, do most people smile in photos? Cuz I know some people, including myself, who don’t like to smile while posing for a picture. ​But perhaps…​ ​um…​ ​Yeah, most people do smile, and honestly I’ve never thought about why, because it seems like this is just normal, common and it’s customary for people to do it. ​When someone sees other smile, they will just do the same. not me, but most people are like that.

 

Q:​ How can you tell if a website is reliable or not?

​Oh my god, this is such a hard question, because/cuz I don’t study in this area, and actually, most people don’t know much about this. When we use the internet, we just go to a website, click on some links, hoping that it’s safe. But if it’s not, if it’s dangerous, we wouldn’t know beforehand. So in my opinion, it’s not us, the individuals, that should learn to tell whether a website is reliable or not, It should be the government that regulates the internet.

 

Q: Are films a waste of money?
um.. What kind of question is that? Is anything a waste of money? Are sports useless.. Sports, books, music, buildings, and films are what We’ve had for a long time, and definitely they exist​ for a reason. So, films are undoubtedly not a waste of money. They serve a purpose.

3.Focus on what you know

知道里面的什么说什么

4.重点句型

  • as for …/ in terms of …/ when it comes to …  :关于
  • what I can think of right now is/are …
  • one … that comes to mind is …
  • one … that pops into my mind is …
  • a few things I can think of now are …

5.P3部分

  • P3很重要!
  • 方法论超熟练背诵
  • P3素材大量练习
    • 背词
    • 反复听我的回答
    • 努力进行影子跟读
    • 练习回答(初期以模仿我的回答为主)

6.每天练习

  • 复习课件——最为重要——方法论一定要超级熟练背诵(中文+英文);8句话扩展每天练一遍
  • 每天背三句
  • P1P2P3素材背词、反复听、努力且不纠结地进行影子跟读
  • 评分标准解析视频课+做笔记视频课
  • 阅读文章练习
  • 准备并练习答题
    • P1不要背答案,自己练习录音回答,听自己的回答、改错、提升
    • P2不要背答案,可以直接录音回答,也可以把自己的答案写下来之后,再练习回答
    • P3练习初期可以背/复述/模仿我的回答,充分模仿我的回答(词汇、句型、思路)

 

雅思口语七

一、p2整体

1.p2整体逻辑非常重要:从上到下,逐点回答,单点扩展,不要串点,每个段落第一句是这个段落主题句,能扩展的在段落内部进行充分扩展。实在没得说回顾过去、展望未来,再不行说another one

这些都做到后p2会简单很多。

 

2.你不需要完美,你只需要进步。

 

3.p2练习方法

  • 把这道题的200+个字全部写下来,然后朗读,回答
  • 或者:写关键字,录音,答题 重复n次

二、part3

1.介绍

考试:4-5mins,2 topics

涉及方面:abstract、social、national、international、universal等大众、社会、抽象内容

2.回答方法

有观点:进行充分的扩展(推),不要罗列观点。扩展方法同前面所学,由宽泛到具体(时间、地点、降级、列举等)。

无观点:只要在路上,一定有方向

注:不要直接上来就用个人例子,因为part3是大众化的问题,推的过程中可以用个人例子(偶尔用,别太多)。

三、part3好的句子积累

I do believe there are many ways we can improve our

memory,  and one of them, from what I’ve heard, is learning a foreign

language.

 

四、作业

1.复习课件、背诵方法论部分
2.上课看到的p3题目,写出自己回答,练习张嘴说出回答
3.8句话,每天推一遍
4.每日背三句
5.评分标准解析每三天看一遍,p2做笔记视频课多看多练
6.音频素材,每天大量听和跟读
7.自己根据文章创造part3的问题,看考官可能会出哪些题目,写尽量的多