知识杂谈-2021

杂谈-2021

Java基础

  • JVM
  • JMM
  • JMX

JMX,全称Java Management Extensions,用于管理和监控java应用程序。JMX有以下用途:

  1. 监控应用程序的运行状态和相关统计信息。
  2. 修改应用程序的配置(无需重启)。
  3. 状态变化或出错时通知处理。

JMX的基础架构

图片

这里简单介绍下这三层结构:

层次描述
Instrumentation主要包括了一系列的接口定义和描述如何开发MBean的规范。在JMX中MBean代表一个被管理的资源实例,通过MBean中暴露的方法和属性,外界可以获取被管理的资源的状态和操纵MBean的行为。
Agent用来管理相应的资源,并且为远端用户提供访问的接口。该层的核心是MBeanServer,所有的MBean都要向它注册,才能被管理。注册在MBeanServer上的MBean并不直接和远程应用程序进行通信,他们通过协议适配器(Adapter)和连接器(Connector)进行通信。
Distributed定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。注意,Adapter 和Connector的区别在于:Adapter是使用某种Internet协议来与 Agent获得联系,Agent端会有一个对象 (Adapter)来处理有关协议的细节。比如SNMP Adapter和HTTP Adapter。而Connector则是使用类似RPC的方式来访问Agent,在Agent端和客户端都必须有这样一个对象来处理相应的请求与应答。比如RMI Connector。
  • GC
    • CMS
    • G1
    • ZGC
  • Jar
    • jar xvf xxx.jar 解包
    • jar命令格式jar {c t x u f } v m e 0 M i 文件名…{ctxu},这四个参数必须选选其一。[v f m e 0 M i],这几个是可选参数,文件名也是必须的。参数 | 说明 —|— -c | 创建一个jar包 -t | 显示jar中的内容列表 -x | 解压jar包 -u | 添加文件到jar包中 -f | 指定jar包的文件名 -v | 输出详细报告 -m | 指定MANIFEST.MF文件 -0 | 生成jar包时不压缩内容 -M | 不生成清单文件MANIFEST.MF -i | 为指定的jar文件创建索引文件 -C | 可在相应的目录下执行命令关于MANIFEST.MF定义:https://baike.baidu.com/item/MANIFEST.MF
    • 演示往jar包添加文件jar uf xxx.jar BOOT-INF/classes/application.yml解压jar包jar -xvf xxx.jar打jar包,不生成清单文件,不压缩jar -cvfM0 xxx.jar BOOT-INF/ META-INF/ org/或者jar -cvfM0 xxx.jar *如果要往线上jar包添加、更新部分文件到jar包,这些命令也许对你有用。
  • 数据库连接池使用threadlocal的目的
    • 连接池是缓存并托管数据库连接,主要是为了提高性能。
    • 而ThreadLocal缓存连接,是为了把同一个数据库连接“分享”给同一个线程的不同调用方法。(不管调用哪个方法,都是使用的同一个连接,方便进行“跨方法”的事务控制)
    • Java存在多线程处理业务的场景,为了保证开的多个线程的事务,需确保不同时间多个线程可能拿到的是同一个连接,所以使用threadlocal,另外就算拿的是“同一个连接”,在引入了threadlocal后,每个线程之间都会创建独立的连接副本,将collection各自copy一份,互相不干扰。
  • 方法上加断点,可用于判断调用的接口使用的具体实现类,但需注意,要在项目启动后再加,且用后及时移除。
  • 针对于接口的多实现,调用的方法,是属性对应类型的实现类中的实现。这一点需理解。
  • Queue是简单的FIFO队列,Deque继承Queue实现双端队列,可以取头或尾的值,先进先出或先进后出,当普通队列用使用offer和poll,当栈使用push和pop。需要组合使用。
  • 范型中的通配符的约定: ?表示不确定的 java 类型 T (type) 表示具体的一个java类型 K V (key value) 分别代表java键值中的Key Value E (element) 代表Element
  • 两个List相等,当且仅当两着类型一致、大小一致、相同位置的元素一致。
  • Redis的底层是六种数据结构,其中String(字符串)类型的数据底层为简单动态字符串,List(列表)、Hash(哈希)、Set(集合)和 Sorted Set(有序集合)统称为集合类型,其底层分别为以下五种数据结构中的两种,五种数据结构按照查找时间的复杂度分类如下: 数据结构 时间复杂度 哈希表 O(1) 跳表 O(logN) 双向链表 O(N) 压缩链表 O(N) 整数数组 O(N) quickest 3.0版本开始,List对象底层数据结构 listpack 5.0版本开始,替换压缩链表
图片

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。

当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性

  • 如果先在一个接口中将一个方法定义为默认方法, 然后又在超类或另一个接口中定义了 同样的方法, 会发生什么情况? 诸如 Scala 和 C++ 等语言对于解决这种二义性有一些复杂的 规则。 幸运的是, Java 的相应规则要简单得多。 规则如下: 1 ) 超类优先。 如果超类提供了一个具体方法, 同名而且有相同参数类型的默认方法会 被忽略。 2 ) 接口冲突。 如果一个超接口提供了一个默认方法, 另一个接口提供了一个同名而且 参数类型(不论是否是默认参数)相同的方法, 必须覆盖这个方法来解决冲突。
  • 声明在接口中的内部类自动成为 static 和 public 类。
  • 代理:调用处理器是实现了 InvocationHandler 接口的类对象。 在这个接口中只有一个方法: Object invoke(Object proxy, Method method, Object args)
  • 通过使用泛型类、 擦除和 @SuppressWamings 注解, 可以消除 Java 类型系统的部分基本限制(具体看Java核心技术 卷1 8.5、8.6部分)。
  • List下的ArrayList和LinkedList采用着完全不同的迭代方式,前者通过索引方法,后者通过iter迭代访问
  • Swing、JavaFX用于编写Java GUI程序,与👤当下的WebService有着不少差异。
  • 二、wait() 和 sleep() 的区别 同:
    • 都是线程同步时会用到的方法,使当前线程暂停运行,把机会交给其他线程
    • 如果任何线程在等待期间被中断都会抛出InterruptedException
    • 都是native() 方法
    异:
    • wait() 是Object超类中的方法;而sleep()是线程Thread类中的方法
    • 对锁的持有不同,wait()会释放锁,而sleep()并不释放锁
    • 唤醒方法不完全相同,wait() 依靠notify或者notifyAll 、中断、达到指定时间来唤醒;而sleep()到达指定时间被唤醒.
    • 使用位置不同,wait只能在同步代码块或同步控制块中使用,而sleep可以在任何位置使用
  • await() signal() 和 signalAll()

java.util.concurrent类库中提供的Condition类(条件对象)来实现线程之间的协调。

在Condition上调用 await() 方法使线程等待,其他线程调用signal() 或 signalAll() 方法唤醒等待的线程。

ReentrantLock里面默认有实现newCondition()方法,新建一个条件对象。

一个使用场景就是,在加锁后,若条件不满足,通过🔒创建的condition调用await(),等待并释放锁,其他线程在进行了可能使await()的线程满足条件时,调用condition的signal()或signalAll()进行唤醒。同时需考虑虚假唤醒的问题,所以倾向与这种结构

  lock.lock()
  try{
    while (!(ok to proceed)) 
      condition.await();
  // do Something

    condition.signalAll();
  }  finally{
    lock.unlock();
  }

​ 这里有一点需注意,虽然上面等待的代码在获取锁之后,但被唤醒后,需要重新获取锁才会继续向下执行。

  • volatile虽然标识同步变量,但不能提供原子性,对其操作可能线程不安全
  • 多线程原子操作AtomicXXX原子类、XXXAdder加法器、XXXAccumulator累加器。 取自:Java核心技术 卷1 14.5.10 原子性 截屏2021-12-08 下午6.18.28 截屏2021-12-08 下午6.19.57 截屏2021-12-08 下午6.25.24
  • 任何集合类都可以通过使用同步包装器(synchronization wrapper) 变成线程安全的:
  List<String> syncArrayList = Collections.synchronizedList(new ArrayList<>()); 
  Map<String, Long> syncHashMap = Collections.synchronizedMap(new HashMap<>());

结果集合的方法使用锁加以保护, 提供了线程安全访问。

对于映射,ConcurrentHashMap较同步的HashMap要好一些。

对于经常被修改的数组列表, 同步的 ArrayList 可以胜过 CopyOnWriteArrayList();

  • 关于集合、并发这块,后续再回顾下Java核心技术 中的相关内容。
  • Java中可通过脚本引擎调用相关脚本,Invocable接口、详见Java核心技术相关章节
    • 脚本部分讲述了如何使用Java调用脚本
    • 编译部分讲述了Java -> class compiler的过程
    • 注解处理时期:运行期、源码级别、字节码工程
  • ClassLoader结构
截屏2021-12-16 上午9.31.22
  • Class 这些元数据在 JVM 当中是如何被表示的? 假设有一个 ClassLoader 正在 Loader 一些类,然后把它们 Load 进虚拟机当中。 JVM 当中有一个结构体叫做 SystemDictionary,它是一个 Meta,会把 Class 的类名 Meta 到 Class 的 Pointer 当中,然后 Pointer 指向的就是 Metaspace 当中真正的 Class 结构描述。 Class 当中有一些 Mirror 的字段,这些 Mirror 指向 java.lang.Class。Mirror 和上层 的.class 是一样的,是一个反射接口的作用。 可以看到,ClassLoader 会索引到 SystemDictionary,然后索引到 Metaspace Chunk,接着索引到 Heap,这几个可以互相引用。 图中 Metaspace Chunk 的 Klass 以及 Heap 里的 java.lang.Class 图形大小是不同 的。因为用户自己写的 Class 有可能会继承自不同的父类以及不同的接口,它有可能实 现了若干个父类和接口,实现接口和父类的数量有所不同, Class 里的东西也是不尽相 同,因此元数据的大小也是不一样的。
  • 类加载有双亲委派机制。但可以通过一些方式不使用该机制。
  • 类加载、链接、初始化
  • JDK8 和 JDK11 中 JDK library 中的 ClassLoader 有所不同
    • ExtClassLoader 更名为了 PlatformClassLoader;
    • PlatformClassLoader 和 AppClassLoader 不再继承自 URLClassLoader;
    • 如果指定了 -Djava.ext.dirs 这个变量,需要用-classpath 来加以替代;
    • 如果指定了-Djava.endorsed.dirs 来覆盖 JDK 内部的 API,需要删掉参数。
  • 一次编译到处执行是 Java 的一个很大的卖点,通过一段代码来了解在 Java 内是如 何被加载和执行的,如在应用代码里面去调用 new Gson(). from Json(..);然后有一个 new byte code 会触发 loadClass()机制,还要去找”com.google.gson.Gson”用 loadClass 方法,去找 Jar,因为应用的 class 下有很多 Jar 包,如 commons-io.jar、myaql-connector.jar、gson.jar,找到 gson.jar 里面有 com.google/gson/Gson.class 文 件,然后会把 Class 给读出来,读成一个 byte 的数组,调用一个 define class JVM 的 接口,define class 会进行 parse、verify、link,调出,最终达到一个可以让 Jvm 识别的 byte code,Jvm 解释器会去执行 byte code 到 2000 次以后,会运行一个 client compiler 让代码编译到 c1 级别,c1 级别其实已经在 native 执行了,同时会收集 一些 provide 信息,帮助编译到更高的优化级别 c2,然后到 15,000 次以后会进入到最 快的 c2 级别,interpret 和 c2 之间可能大概有 50 倍的差距,所以 Java1 开始是很慢的, 但只要跑稳后是非常快的。 通过上述,一段代码想要被执行,生命周期是非常长的,优点是这种跨平台性,可以收集的信息越跑越快,缺点就是 Java 代码装载的开销非常大。
  • Thread in Java(Interpreter、JIT compile)、Thread in native、Thread in VM

Spring

  • Springboot的YML配置文件是一种新式的格式,以空格的缩进程度来控制层级关系。空格的个数并不重要,只要左边空格对齐则视为同一个层级。注意不能用tab代替空格。且大小写敏感。支持字面值,对象,数组三种数据结构,也支持复合结构 字面值:字符串,布尔类型,数值,日期。字符串默认不加引号,单引号会转义特殊字符。日期格式支持yyyy/MM/dd HH:mm:ss 对象:由键值对组成,形如 key:(空格)value 的数据组成。冒号后面的空格是必须要有的,每组键值对占用一行,且缩进的程度要一致,也可以使用行内写法:{k1: v1, ....kn: vn} 数组:由形如 -(空格)value 的数据组成。短横线后面的空格是必须要有的,每组数据占用一行,且缩进的程度要一致,也可以使用行内写法:[1,2,...n] 复合结构:上面三种数据结构任意组合 注意如下:
  1. 字符串可以不加引号,若加双引号则输出特殊字符,若不加或加单引号则转义特殊字符
  2. 数组类型,短横线后面要有空格;对象类型,冒号后面要有空格
  3. YAML是以空格缩进的程度来控制层级关系,但不能用tab键代替空格,大小写敏感
  userinfo:
      age: 25
      name: abc
      active: true
      created-date: 2018/03/31 16:54:30
      map: {k1: v1,k2: v2}
      hobbies:
        - one
        - two
        - three
  #对应的实体
  @Data
  @ToString
  public class UserInfo {
      private String name;
      private Integer age;
      private Boolean active;
      private Map<String,Object> map;
      private Date createdDate;
      private List<String> hobbies;
  }
  • Springboot从配置文件中取值的三种方式
    • @ConfigurationProperties这个注解用于从配置文件中取值,支持复杂的数据类型,但是不支持SPEL表达式。@ConfigurationProperties注解能够很轻松的从配置文件中取值,优点如下:
      1. 支持批量的注入属性,只需要指定一个前缀prefix
      2. 支持复杂的数据类型,比如ListMap
      3. 对属性名匹配的要求较低,比如user-nameuser_nameuserNameUSER_NAME都可以取值
      4. 支持JAVA的JSR303数据校验注意:@ConfigurationProperties这个注解仅仅是支持从Spring Boot的默认配置文件中取值,比如application.propertiesapplication.yml
    • @Value@Value 这个注解估计很熟悉了,Spring中从属性取值的注解,支持SPEL表达式,不支持复杂的数据类型,比如List
    • @PropertySource Spring Boot在启动的时候会自动加载application.xxxbootsrap.xxx,但是为了区分,有时候需要自定义一个配置文件,@PropertySource用于从指定的配置文件中取值@PropertySource指定加载自定义的配置文件,默认只能加载properties格式,但是可以指定factory属性来加载YML格式的配置文件。
  • 日志框架都是基于日志门面SLF4j即简单日志门面(Simple Logging Facade for Java),SLF4j并不是一个真正的日志实现,而是一个抽象层,它允许你在后台使用任意一个日志实现。 SLF4j只是一个门面,共有两大特性。一是静态绑定、二是桥接。
  • AOP的相关概念(面试常客) 「切面(Aspect)」:一个关注点的模块化。以注解@Aspect的形式放在类上方,声明一个切面。 「连接点(Joinpoint)」:在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候都可以是连接点。 「通知(Advice)」:通知增强,需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用。增强包括如下五个方面:
  1. @Before:在切点之前执行
  2. @After:在切点方法之后执行
  3. @AfterReturning:切点方法返回后执行
  4. @AfterThrowing:切点方法抛异常执行
  5. @Around:属于环绕增强,能控制切点执行前,执行后,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解。 「切点(Pointcut)」:其实就是筛选出的连接点,匹配连接点的断言,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点做为切点。 「引入(Introduction)」:在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去。 「目标对象(Target Object)」:被一个或者多个切面所通知的对象。也被称做被通知(adviced)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。 「AOP代理(AOP Proxy)」AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 「织入(Weaving)」:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
  • 方法内调用导致AOP失效的原因(以事务为例):AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成。内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效。 解决方法通常是获取或注入Bean,然后进行调用
  • Spring boot的启动过程
    • 创建SpringApplication对象 SpringApplication的构建都是为了run()方法启动做铺垫,构造方法中总共就有几行代码,最重要的部分就是设置应用类型、设置初始化器、设置监听器。 图片
    • 执行run()方法 主体是以下8个环节 图片
  • ArgumentResolver

参数解析器是 Spring 提供的用于解析自定义参数的工具,我们常用的 @RequestParam 注解就有它的影子,使用它,我们可以将参数在进入Controller Action之前就组合成我们想要的样子。Spring 会维护一个 ResolverList, 在请求到达时,Spring 发现有自定义类型参数(非基本类型), 会依次尝试这些 Resolver,直到有一个 Resolver 能解析需要的参数。要实现一个参数解析器,需要实现 HandlerMethodArgumentResolver 接口

  • Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的 图片
  • JWT
  1. JWT长什么样? JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  1. JWT的构成 第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
  • header jwt的头部承载两部分信息:
    • 声明类型,这里是jwt
    • 声明加密的算法 通常直接使用 HMAC SHA256
    完整的头部就像下面这样的JSON:
  {
    'typ': 'JWT',
    'alg': 'HS256'
  }

​ 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
  • playload 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
    • 标准中注册的声明
    • 公共的声明
    • 私有的声明
    标准中注册的声明 (建议但不强制使用) :
    • iss: jwt签发者
    • sub: jwt所面向的用户
    • aud: 接收jwt的一方
    • exp: jwt的过期时间,这个过期时间必须要大于签发时间
    • nbf: 定义在什么时间之前,该jwt都是不可用的.
    • iat: jwt的签发时间
    • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
    公共的声明
    公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. 私有的声明
    私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。 定义一个payload:
  {
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
  }

​ 然后将其进行base64加密,得到Jwt的第二部分。

  eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
  • signature jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
    • header (base64后的)
    • payload (base64后的)
    • secret
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
  // javascript
  var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

  var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

​ 将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  1. JWT的特点及问题
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展。但因此无法做过期、续期等动作,因为过期需要服务器维护某个状态,续期则需要重新颁发token。
  • secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

多线程

  • wait()方法和notify()/notifyAll()方法在放弃🔒的时候的区别在于:wait()方法立即释放对象的🔒,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象的🔒
  • 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用。同时可以做到解耦。
  • 指令重排不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。

Mysql

  • SQL:结构化查询语句;DDL:数据定义语言
  • 索引: 在大数据场景下,表与表之间通过一个冗余字段来关联,要比直接使用 JOIN有更好的性能。如果确实需要使用关联查询的情况下,需要特别注意的是:
    • 确保 ON和 USING字句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。当表A和表B用列c关联的时候,如果优化器关联的顺序是A、B,那么就不需要在A表的对应列上创建索引。没有用到的索引会带来额外的负担,一般来说,除非有其他理由,只需要在关联顺序中的第二张表的相应列上创建索引(具体原因下文分析)。
    • 确保任何的 GROUP BY和 ORDER BY中的表达式只涉及到一个表中的列,这样MySQL才有可能使用索引来优化。
  • 表建索引,也会影响由表创建的视图的查询。
  • Text和Blob:两者都可存储大对象。但Blob是大对象(二进制字符串),Text是大文本(非二进制字符串)。
    • 就需求角度,Blob可以存储文本及其他的(例如图片),Text只能存储文本。
    • 但在操作角度,Blob的操作没有Text简单。可视情况选取。
      • Text可通过String类型直接操作
      • Blob建议使用Byte类型,若使用String类型,在5.7版本会乱码,在8.0版本没有问题
  • 图片
  • 采用多版本并发控制(MVCC,MultiVersion Concurrency Control)来支持高并发。并且实现了四个标准的隔离级别,通过间隙锁next-key locking策略防止幻读的出现。
  • InnoDB 引擎的四大特性是什么? 插入缓冲(Insert buffer) Insert Buffer 用于非聚集索引的插入和更新操作。先判断插入的非聚集索引是否在缓存池中,如果在则直接插入,否则插入到 Insert Buffer 对象里。再以一定的频率进行 Insert Buffer 和辅助索引叶子节点的 merge 操作,将多次插入合并到一个操作中,提高对非聚集索引的插入性能。 二次写 (Double write) Double Write由两部分组成,一部分是内存中的double write buffer,大小为2MB,另一部分是物理磁盘上共享表空间连续的128个页,大小也为 2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是通过 memcpy 函数将脏页先复制到内存中的该区域,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免操作系统缓冲写带来的问题。 自适应哈希索引 (Adaptive Hash Index) InnoDB会根据访问的频率和模式,为热点页建立哈希索引,来提高查询效率。索引通过缓存池的 B+ 树页构造而来,因此建立速度很快,InnoDB存储引擎会监控对表上各个索引页的查询,如果观察到建立哈希索引可以带来速度上的提升,则建立哈希索引,所以叫做自适应哈希索引。 缓存池 为了提高数据库的性能,引入缓存池的概念,通过参数 innodb_buffer_pool_size 可以设置缓存池的大小,参数 innodb_buffer_pool_instances 可以设置缓存池的实例个数。缓存池主要用于存储以下内容: 缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲 (insert buffer)、自适应哈希索引(adaptive hash index)、InnoDB存储的锁信息 (lock info)和数据字典信息 (data dictionary)。
  • SELECT语句 – 执行顺序: FROM
    <表名> # 选取表,将多个表数据通过笛卡尔积变成一个表。
    ON
    <筛选条件> # 对笛卡尔积的虚表进行筛选
    JOIN
    # 指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中
    WHERE
    # 对上述虚表进行筛选
    GROUP BY
    <分组条件> # 分组
    # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的
    HAVING
    <分组筛选> # 对分组后的结果进行聚合筛选
    SELECT
    <返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外
    DISTINCT
    # 数据除重
    ORDER BY
    <排序条件> # 排序
    LIMIT
    <行数限制>
  • 正则替换时,可在表达式中用()将表达式分区,然后通过$x引用分区匹配的内容(x为1、2、3。。)。从而实现正则替换时,部分内容保持不变 比如下面的这个就实现了,把上面这个注解替换为下面的注解,但其中的部分内容不变 [\u4E00-\u9FA5A-Za-z0-9_] 表示中英文和下划线数字
  (@ApiModelProperty\(value = )(\"[\u4E00-\u9FA5A-Za-z0-9_]+\")(, hidden = true\))
  @Schema\(description = $2 , accessMode = Schema.AccessMode.READ_ONLY\)
  • insert ignore insert ignore会忽略数据库中已经存在的数据(根据主键或者唯一索引判断),如果数据库没有数据,就插入新的数据,如果有数据的话就跳过这条数据.
  • replace into replace into 首先尝试插入数据到表中。如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据,否则,直接插入新数据。
  • insert on duplicate key update 如果在insert into 语句末尾指定了on duplicate key update,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则在出现重复值的行执行UPDATE;如果不会导致重复的问题,则插入新行,跟普通的insert into一样
  • 死锁 insert … on duplicate key 在执行时,innodb引擎会先判断插入的行是否产生重复key错误,如果存在,在对该现有的行加上S(共享锁)锁,然后返回该行数据给mysql,然后mysql执行完duplicate后的update操作, 然后对该记录加上X(排他锁),最后进行update写入。 如果有两个事务并发的执行同样的语句,那么就会产生death lock 解决办法:
  • 1、尽量对存在多个唯一键的table使用该语句
  • 2、在有可能有并发事务执行的insert 的内容一样情况下不使用该语句

Linux

  • netstat -tunlp|grep pid/port 根据pid或端口号查进程
  • 部分以 .gz 结尾的文件无法用tar解压,是因为压缩时没有加 -z参数,是做成tar包之后,用gzip命令单独压缩的。解压方法为先解压 gunzip xxx.gz,再展开成tar包,也有可能不用展开

Git

  • 撤销commit git reset –soft HEAD^ –soft表示保留add,使用–hard可同步撤销此次commit相关的更改 HEAD^表示上一个版本,HEAD~n 可撤销n次commit

Jpa

  • @DynamicInsert属性:默认为true,表示insert对象的时候,生成动态的insert语句,如果这个字段的值是null就不会加入到insert语句当中。如果不加该注解,数据库默认值可能不会生效。
    @DynamicUpdate属性:默认为true,表示update对象的时候,生成动态的update语句,如果这个字段的值是null就不会被加入到update语句中。 但默认值是在事务提交后才生效的,如果加了事务注解,那在事务内,默认值不会生效。
  • 在实体中,一个表字段可映射成多个属性,但其中只能有一个属性可以进行insert和update
  • @JoinColumn中,name 指定本实体中关联用字段,referencedColumnName 指定另一实体进行关联的字段,默认为主键

Mybatis

  • Mybatis Plugin 在mybatis官方文档中,对于Mybatis plugin的的介绍是这样的: MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
  //语句执行拦截
  Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

  // 参数获取、设置时进行拦截
  ParameterHandler (getParameterObject, setParameters)

  // 对返回结果进行拦截
  ResultSetHandler (handleResultSets, handleOutputParameters)

  //sql语句拦截
  StatementHandler (prepare, parameterize, batch, update, query)

简而言之,即在执行sql的整个周期中,可以任意切入到某一点对sql的参数、sql执行结果集、sql语句本身等进行切面处理。(分页插件 pageHelper 就是这样实现数据库分页查询的)

设计模式

  • 观察者模式:被观察对象状态改变,所有观察它的对象得到通知。也称订阅模式,英文Observer。
    被观察者不依赖观察者,通过依赖注入达到控制反转。
  • 事件通知:事件发生后,通知所有关心这个事件的对象。
    与观察者模式对比,可理解成所有对象都只依赖事件系统。一半对象观察事件系统,等待特定通知;一半对象状态变化就通过事件系统发出事件。
    观察者也不依赖被观察对象,他只关心事件,不需要到被观察对象那儿注册自己。
    被观察者也只是普通对象,状态改变,通过事件系统发出事件就行了。
  • 消息队列:将消息排成队列,逐步分发通知。
    与事件通知对比,可理解成事件不是立即通知,而是保存到队列里,稍后通知。
    这个可以达到时间解耦的效果。Windows的消息循环就是一个应用。多线程情况下,消息队列优先于事件系统。

计算机基础

  • 原码、反码、补码
    • 原码是数的二进制表示,最高位为符号位,1表示负数 0表示正数
    • 正数的反码与其原码相同,负数的反码为原码按位取反 0-1 1-0。
    • 正数的补码是其本身,负数的补码是其反码 +1,随即丢掉最高位
    • 反码解决了正负数的运算问题,但有正负两个0,所以引入了补码。
    • 计算机存储补码,并使用补码运算。
  • 运算优先级 四则运算 移位 逻辑运算(包括位运算) 赋值运算。然后 &优先级高于|、&&优先级高于||
  • 位运算符号
符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
>>>右移在Java中,表示逻辑右移或无符号右移
  • 数值运算,注意不要溢出时,使用long、double等
  • 异或运算不支持指针指向同一地址的情况。
    另数组中是对象,即便值相同,也是不同的地址。
  • 如果 n = 2^x ,则
          var a = (n & (n - 1)) == 0;
          var b = (n & (-n)) == n;
          var c = (n ^ (n - 1)) == (n - 1 + n);
  • 掩码:BitMask 基本操作
  • 状态设定 其他位不管,把第二位变为1即可。
       xxxx xxxx
   OR  0000 0010
    =  xxxx xx1x

代码:

  Status |= mask;
  • 状态清除 其他位不管,把第二位变为0。
      xxxx xxxx
  AND 1111 1101
   =  xxxx xx0x 

实际上这是对于Status和Mask的反码进行的“与“运算。

代码:

  status &= ~mask;
  • 查询状态 通过查询确定第二位是0还是1,和mask进行“与”运算:
       xxxx xxxx 
  AND  0000 0010
    =  0000 00x0 

如果为1,返回一个大于零的值,否则返回0。

代码:

  Boolean isOn = (status & mask) > 0;
  • x&(-x):保留二进制下最低位的1,其余位置置0(即一个数中最大的2的n次幂的因数) x&(x-1):将二进制下最低位的1变为0,其余保持不变(Brian Kernighan 算法)
  • 二进制 x 的 最高有效位:最高有效位( most significant bit,MSB)指的是一个n位二进制数字中的n-1位,具有最高的权值2^(n-1) 最低有效位:最低有效位( least significant bit,LSB)指的是一个二进制数字中的第0位(即最低位) 最低设置位:正整数 x 的「最低设置位」为 x 的二进制表示中的最低的 1 所在的位
  • 当x是偶数时 x&1= 0, 当x是奇数时 x&1=1
  • 大写变小写、小写变大写 : 字符 ^= 32; 大写变小写、小写变小写 : 字符 |= 32; 小写变大写、大写变大写 : 字符 &= -33;
  • TCP 拥塞控制的经典算法: 慢启动拥塞避免快速重传和快速恢复
  • IO 多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄; 一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作; 没有文件句柄就绪就会阻塞应用程序,交出CPU。 没有IO多路复用机制时,有BIO(同步阻塞)、NIO(同步非阻塞)两种实现方式,但它们都有一些问题 三种实现:select、poll、epoll

算法

  • 组合类回溯的排重,可以通用性的,先排序,然后若当前与前一个数一样,且前一个数未选,就跳过
  • 有限数的枚举组合,可以通过二进制 1/0 的组合来枚举。比如可定义1为选0为不选

Leave a Reply

Your email address will not be published. Required fields are marked *

lWoHvYe 无悔,专一