MySQL笔记_6
语句耗时
如果select长时间不返回,使用show processlist查看当前语句处于什么状态
比如MDL写锁阻塞了别的session的读
可以查询是什么session阻塞,但是在MySQL启动时需要设置performance_schema = ON,这样会有10%的性能损失
sys.schema_table_lock_waits,查询这张表可以找到导致阻塞的processid,kill掉即可
还有可能被flush阻塞了,一般flush的两个语句是flush table t with read lock锁表t,flush tables with read lock锁所有的表,本身刷脏页的速度很快,但是可能flush命令被别的session阻塞了
或者别的线程占用写锁,那么申请lock in share mode的读锁就会被阻塞。通过查找sys.innodb_lock_waits表即可
如果在RR场景下,或者在一个事务中,使用select,如果此时别的session有一个update语句,update了一百万次,那么自己的select语句就会特别慢,因为是 ...
MySQL笔记_7
主备
主备切换流程
状态1,客户端读写都只访问节点A,B是从库,同步A的更新,保证AB数据相同
需要切换的时候,就变成状态2,客户端只访问节点B
状态1不会操作B,但是建议将B设置为read only
防止他人误操作B
防止切换逻辑出现双写的问题
判断访问节点的角色
read only对超级管理员无效,所以可以同步更新的线程有超级权限
binlog statement
statement就是记录SQL语言,甚至会把注释也都记录进去
statement的格式下,delete带有limit,就是unsafe的,会导致主从不一致
删除条件的两个都有索引,如果主从版本不一,可能选择的索引不一致,所以顺序不同,导致delete的东西不同,本质上就是主从库可能delete的时候用到的索引不同,因为有limit存在,比如只删一条数据,就会导致数据不一致。
binlog row
row里面多了Table_map和Delete_rows
Table_map用于说明接下来操作哪个库哪个表
Delete_rows用于定义删除行为
实际上,row虽然会占用空间,但是有助于恢复数据 ...
Go笔记_0
GOROOT VS GOPATH VS GOBIN VS GOPROXY
GOROOT: Go语言安装路径
GOPATH: 若干工作区目录的路径。是我们自己定义的工作空间
GO1.8版本之后,开发包安装完成后会自动设置一个GOPATH目录,
GO1.14版本之后,推荐使用Go Module模式,不一定非要将代码写在GOPATH目录下,也不需要自己配置GOPATH
GOBIN: GO程序生成的可执行文件的路径
GOPROXY: 默认为GOPROXY=https://proxy.golang.org,direct,修改为GOPROXY=https://goproxy.cn,direct
跨平台编译
Windows 编译 LINUX或者OSX
1234SET CGO_ENABLED=0 // 禁用CGOSET GOOS=linux // 目标平台, [windows, linux, darwin]SET GOARCH=amd64 // 目标处理器架构go build
LINUX 或者 OSX 编译其他环境
1CGO_ENABLED=0 GOOS=linux| ...
Go笔记_1
方法
Go语言中只有不同的package中可以有相同函数名的函数,尽管参数不同,但是如果函数名相同就不能出现在相同的包中(函数重载Go语言没有)
方法需要通过一个特定的实例调用,比如t.Errorf(),这里的Errorf就是一个方法,通过实例t调用
函数可以随便调用,没有限制
接口
接口让函数接受不同类型的参数并创造类型安全并且高解耦的代码
Go语言中 interface resolution 是隐式的。如果传入的类型匹配接口需要的,则编译正确。
函数实现因此不需要关心参数是什么类型的,只需要声明一个接口,辅助函数就可以从具体类型解耦而只关心本身需要做的工作
表格驱动测试
如果需要测试一个接口的不同实现,或者传入的数据有很多不同的测试需求,则可以使用表格驱动测试
123456789101112131415161718func TestArea(t *testing.T){ areaTests := []struct{ name string shape Shape want float64 }{ {name: & ...
Maven笔记_1
build设置
自定义打包文件
1234<build> <!-- 自定义打包文件, 需要指定后缀 --> <fileName>xxx.war</fileName></build>
包含特定的文件
如果一些配置文件没有放在resources/下
有时候Mybatis中会将编写SQL语句的映射文件和Mapper接口都写在src/main/java的某个包中
此时映射文件不会被打包, 就需要单独设置
123456789101112<build> <resources> <resource> <!-- 设置资源所在目录 --> <directory>src/main/java</directory> <includes> <!-- 设置包含的类型资源 --> <include ...
Elasticsearch笔记_3
自动补全
与IK分词器一样, 拼音分词器作为插件安装在ES中即可
pinyin
解压, 移动到plugin/, 重启elasticsearch
自定义分词器
ES中分词器包含三个部分
character filters: 在tokenizer之前对文本进行处理, 比如删除字符, 替换字符
tokenizer: 将文本按照一定规则切割成词条term, 比如keyword就是不分词, 还有ik_smart
tokenizer filter: 将tokenizer输出的词条进一步进行处理, 比如大小写转换, 同义词处理, 拼音处理
创建索引库的时候, 使用settings配置自定义的analyzer分词器
123456789101112131415161718192021222324PUT /test{ "settings": { "analysis": { "analyzer": { // 自定义分词器 ...
Maven笔记_0
Maven软件原理
pom.xml是Maven的核心配置文件, pom指Project Object Model, 项目对象模型, 将当前项目抽象为文档对象, 再操作整个项目
Dependency, 依赖管理模型, 主要负责Maven的依赖管理功能
在pom.xml中配置了一个坐标, Dependency就会在本地仓库(local)中找到对应的jar包
如果local没有, 就会通过其他方式(b2b, central中央仓库)下载
Maven本地仓库中一共存在三种jar包, 自己工程安装到本地仓库的jar包, 从网上下载的第三方jar包, 支持Maven工作的过程中需要用到的jar包
Maven结构
Maven安装需要有JAVA_HOME环境变量
bin: 包含有Maven的运行脚本, 比如mvn.cmd
boot: 包含有plexus-classworlds类加载器框架
conf: 包含核心配置文件, 主要是settings.xml
lib: 包含运行时需要的Java类库
Maven配置
修改本地仓库位置
1<localRepository>D: ...
Elasticsearch笔记_2
搜索结果处理
排序
默认根据相关度分数排序, 可以排序的类型分为keyword, 数值类型, 地理坐标, 日期
123456789101112131415161718GET /idxName/_search{ "query": { "match_all": {}, "sort": [ { "FIELD": "desc" }, { "_geo_distance": { "FIELD": "<lat, lon>", "order": "asc", ...
Elasticsearch笔记_1
ES支持的两种地理坐标存储形式
geo_point: 纬度和经度确定的一个点
geo_shape: 多个geo_point组成的一个复杂图形, 比如直线, 一般是一个区域
如果ES中同时需要根据名称, 位置, 商圈, 城市等信息进行多个字段的搜索, 又希望性能高一些, 可以使用copy_to
123456789"all": { "type": "text", "anaylzer": "ik_max_word"},"brand": { "type": "keyword", "copy_to": "all"}// 这样就可以在一个字段`all`中查询到所有字段中
JavaRestClient
初始化
引入ES的HighLevelRestClient
1234<dependency> <gro ...
Elasticsearch笔记_0
使用Docker安装ES
设置Docker网络
1docker network create es-net
拉取ES镜像
12docker pull elasticsearch:7.12.1docker pull kibana:7.12.1
如果有打包好的tar包, 可以使用docker load -i xxxx.tar完成加载
运行ES镜像
1234567891011121314151617docker run -d --name es \ -e "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" \ -e "discovery.type=single-node" \ -v es-data:/usr/share/elasticsearch/data \ -v es-plugins:/usr/share/elasticsearch/plugins \ --privileged \ --network=es-net \ -p 9200:9200 \ -p 9300:9 ...
Linux笔记_1
用户
可以查看/etc/passwd文件, 得到系统用户信息
这个目录最早用来存储用户密码的哈希, 但是所有用户都可以访问
现在用户密码的哈希存储在/etc/shadow中, 只有root可以访问
如果执行apt update发现权限不足, 可以使用sudo !!进行补救, 因为!!表示上一条命令
用户组
一组用户的集合, 使用groups命令查看当前用户组
一般用户在创建的时候, 都会创建一个与用户名相同的用户组
添加用户
Debian系列使用adduser可以实现添加用户, 添加组, 将用户添加到组
添加用户: sudo adduser 用户名
添加组: sudo adduser --group 组名
将用户添加指定用户组: sudo adduser 用户名 组名
一般创建的用户没有sudo权限, 可以将用户添加到sudo用户组中
文件
文件权限
ls -l可以查看文件的详细信息
第一位是文件类型: -表示普通文件, d表示目录
接下来三位是所属用户的权限
再三位是所属用户组的权限
最后三位是其他人的权限
文件层次
Linux下所有的文件从 ...
Linux笔记_0
软件安装
apt软件源列表在/etc/apt/sources.list下
一般情况下, Ubuntu的官方地址为https://archive.ubuntu.com/, 可以替换源
1sudo sed -i 's|//.*archive.ubuntu.com|//mirrors.ustc.edu.cn|g' /etc/apt/sources.list
如果需要一个命令可以在任意地方执行, 可以将这个目录中的内容添加到/usr/local目录下, 即sudo cp -R * /usr/local/
因为usr/local/bin/是在PATH的环境变量下
基本命令
查看文件内容
cat是concatenate的缩写, 连接多个文件并输出文件内容, 如果只有一个文件, 则直接输出这个文件的内容
less一次只显示一页,支持前后滚动搜索的功能
按键
功能
d/u
向下/向上半页
f/b
向下/向上一页
g/G
文件开头/结尾
j/k
向下/向上一行
/PATTERN
搜索PATTERN
n/N
跳转上一个/下一 ...
安卓逆向笔记_0
环境配置
雷电模拟器9(debug版)
开启Root权限
安装面具(app-debug.apk)
进入主页后安装Magisk, 会发现只勾选了安装到Recovery, 不要点击其他内容
直接控制台杀死进程
重新打开面具, 再次点击安装, 发现勾选了两个选项, 点击下一步
方式选择安装至系统分区, 点击开始
如果出现了Installation failed, 需要在雷电模拟器的设置中选择磁盘 -> 系统磁盘, 勾选可写入
出现All done表示安装完成
重启模拟器
再次启动面具, 如果有弹窗异常状态: 检测到不属于Magisk的su文件直接确定不需要管
在面具的设置中打开Zygisk
关于异常状态的提示, 可以进入模拟器的文件管理器
System -> xbin删除其中的su文件, 第一次删除失败就再删一次, 就可以消除这个提示
LSPosed模块
模拟器右侧更多 -> 共享文件夹 -> 打开电脑文件夹 -> 将LSPosed.zip拖到文件夹中
面具下面的模块 -> 从本地安装 -> 右边选择文件管理器 -&g ...
Java笔记_13
并发
创建线程
将线程任务放在一个类的run()中, 这个类需要实现Runnable接口
123public interface Runnable { void run();}
Runnable是个函数式接口, 所以可以使用lambda表达式创建实例
123Runnable r = () -> { task;}
从这个Runnable构造一个Thread对象 Thread t = new Thread(r);
启动线程 t.start();
还可以通过建立Thread类的子类定义线程
12345class MyThread extends Thread { public void run () { task; }}
然后构造这个子类的对象并调用start(), 但是现在一般不用这个方法, 如果有多个任务, 每个任务都要创建一个线程的开销的太大, 一般使用线程池
不要调用Thread类或者Runnable对象的run(), 直接调用run()会在同 ...
Java笔记_12
集合
Collection接口
集合类的基本接口是Collection接口, 有两个基本方法 12345678910111213141516171819public interface Collection<E> { boolean add(E element); Iterator<E> iterator();}public interface Iterator<E> { E next(); boolean hasNext(); void remove(); default void forEachRemaining(Consumer<? super E> action);}// 如果到达末尾依然调用next(), 会抛出NoSuchElementException// remove()会删除上一次调用next()返回的元素// remove()和next()有依赖性, 如果不先调用next(), 会抛出IllegalStateException// 如果需要删除两个元素it.next();it ...
Java笔记_10
异常
所有的异常都是由Throwable继承而来, 下一层有两个分支, Error和Exception
Error类描述了Java运行时系统的内部错误和资源耗尽问题, 一般不要抛出这个类型
Exception层次分为两个分支, RuntimeException和其他异常
编程错误导致的是RuntimeException
由于IO错误导致的是其他异常
继承自RuntimeException异常包括: 错误的强制类型转换; 数组越界; 访问null指针
不继承自RuntimeException异常包括: 打开不存在的文件; 越过文件末尾读取数据; 根据给定字符串查找Class, 但是这个类并不存在
所有派生于Error和RuntimeException的异常是非检查型异常, 其他异常都是检查型异常
抛出异常的情况
调用了某个会抛出异常的方法, 比如FileInputStream构造器
检测到一个错误, 使用throw语句抛出异常
程序出现错误, 比如a[-1]抛出一个非检查型异常
JVM内部错误
一个方法必须声明所有可能抛出的检查型异常, 也可以捕获一个异常, 这样也 ...
Java笔记_11
泛型
没有泛型类的时候, 泛型程序用继承实现, ArrayList类只维护一个Object引用数组, 这样就会每次都需要进行强制类型转换
现在是使用尖括号<String>
Java类库使用E表示集合类型, K,V表示键值对, T,U等表示任意类型
泛型类
1234567891011121314151617181920212223242526public class Pair<T> { T first; T second; public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T ...
Java笔记_9
内部类
使用内部类的原因
对同一个包中的其他类隐藏
访问定义这些方法的作用域的数据, 包括原本的私有数据
内部类对象会有一个隐式引用, 指向实例化这个对象的外部类对象, 可以访问外部对象的全部状态
但是Java中静态内部类没有这个指针, 所以Java静态内部类等于CPP中的嵌套类
可以使用OuterClass.this表示外部类的引用, 比如 T.this.b
可以使用outerObject.new InnerClass()编写内部类的构造器, 比如A listen = this.new B();
在外部类的作用域之外, 可以使用OutClass.InnerClass引用
内部类声明的所有静态字段都必须是final, 初始化为一个编译时常量
内部类不能有static方法
使用$和javap -private ClassName可以将内部类文件转换为常规类文件
局部类
可以在一个方法中声明局部类, 这个类不能用public或者private访问修饰符, 对外部完全隐藏, 除了这个方法外都不知道这个局部类的存在
局部类不仅可以访问外部类的字段, 还可以访问局部变量, 不过这 ...
Java笔记_7
继承
基本思想: 基于已有的类创建新的类, 复用已有类的方法, 同时可以增加一些新的方法和字段
反射是程序在运行期间更多地了解类以及属性的能力
CPP中使用:表示继承, 除了公共继承以外, 还存在私有继承和保护继承
Java中extends关键字表示继承, 所有继承都是公共继承
extends表示正在构造的类(子类, 派生类)派生于一个已经存在的类(超类, 基类, 父类)
子类比超类拥有更多的功能
“声明为私有的类成员不会被这个类的子类继承”
这里其实是子类不能直接访问这些私有成员
但是子类的每个实例对象中依然会包含超类中的私有字段
记录不能被扩展, 记录也不能继承别的类
如果希望使用超类中的方法, 就使用super关键字, super只是用于指示编译器调用超类方法的特殊关键字
同时可以使用super()来调用超类中对应的构造器
不管是this还是super, 在调用其他构造器的时候, 都必须放在第一行, 否则会报错
一个对象可以指示多种实际类型
比如一个类的超类以及他的子类都在一个数组中, 使用foreach循环的时候, 循环变量可以 ...
Java笔记_8
接口
接口用来描述类应该做什么, 不指定具体如何做, 一个类可以实现多个接口
接口可以定义常量, 但是绝不能有实例字段
Java 8之前, 接口中的方法都是抽象方法
定义接口的方法不必指定为public, 因为接口方法自动为public
接口中的字段都是public static final的, 也不需要手动指定
但是实现接口时, 必须明确写public, 否则编译器会默认认为这个方法的访问属性是包可访问, 就会报错
12345678class E implements Comparable<E> { public int compareTo(E oth) { return Double.compare(salary, oth.salary); // 如果x < y 返回 负数 // 如果x = y 返回 0 // 如果x > y 返回正数 }}
Comparable接口文档建议compareTo方法与equals方法兼容, 即x.equals(y)时, x.compareTo( ...
Docker笔记_0
Docker 下载安装
众所周知, 来自中国的程序员往往具有更好的网络相关基础 :(
因为多次尝试在Ubuntu上安装Docker以及Docker Compose, 中间踩了无数坑, 为此在这里做一个记录总结
如果有条件, 直接clash, 按照官网的流程, 大抵是可行的 (虽然我尝试了一下还是存在不少问题…)
哪怕直接sudo apt install docker 也是完全不行的, 软件源中的docker是究极老版本, 几乎用不了
卸载 Docker (有可能叫docker-engine docker.io)
Ubuntu大概率会自带老版本的Docker, 很多语法还有命令都不一样了, 所以需要先卸载了
1sudo apt remove docker docker-engine docker.io containerd runc
更新软件包
12sudo apt updatesudo apt upgrade
如果失败需要换源, 可以使用中科大源
sudo sed -i 's|//.*archive.ubuntu.com|//mirrors.ustc.edu.cn| ...
Java笔记_6
面向对象编程(OOP)
Java中对象变量只是包含了一个引用, 没有实际包含一个变量
123Date startTime = new Date();// 这里的startTime只是一个指向Date实例的引用// 不能看作是CPP中的引用, 应该看作CPP中的对象指针, 也就是Date* startTime
所有的Java对象都存储在堆中, 当一个对象包含了另一个对象的时候, 实际上只是包含了另一个对象在堆中的指针
所以如果需要得到一个对象的副本, 不能简单的用=, 而是应该用clone()方法
更改器方法和访问器方法
更改器方法: 调用方法以后, 对应实例的状态会改变
访问器方法: 调用方法以后, 只访问对象, 不会修改它. 比如get()
访问器方法不要返回可变对象的引用 1234567891011121314class E { private Date hireDay; public Date getHireDay() { return hireDay; }}// 这里的hireDay就是一 ...
Java笔记_5
概述和环境
源码文件
确保安装了JDK, 并且配置了环境变量
jdk/lib目录中可以找到src.zip, 这个压缩包包含了所有的公共类库的源码
使用jar xvf jdk/lib/src.zip可以将src.zip解压缩到当前目录下
如果是别的源码, 比如编译器, 原生方法, 虚拟机, 私有辅助类, 需要从openjdk那边拿到
控制台运行Java
一个包含Main函数的Java类, 使用javac xxx.java可以将xxx.java文件编译为.class文件
然后再使用java xxx, 不需要加.class后缀即可直接运行这个编译后的文件
JShell
JDK 9引入了另一种使用Java的方法,就是"读取-评估-打印循环" (Read-Evaluate-Print Loop, REPL)
输入一个表达式, JShell会评估输入, 打印结果, 并等待下一个输入. 直接在控制台输入jshell即可开始使用
输入"java".length()就会返回字符个数$1 ==> 4
然后再输入4 * $1 + 1, 会返回$2 = ...
设计模式笔记_1
简单工厂模式
需要实例化谁,以后可能会添加实例化的对象,就要用一个单独的类来做这个创造实例的过程,就是工厂
123456789101112131415161718192021222324// Operation.javapublic class Operation { public static double getResult(double numberA, double numberB, String operation){ double ans = 0; switch (operation) { case "+": ans = numberA + numberB; break; case "-": ans = numberA - numberB; break; case "*&qu ...
设计模式笔记_0
单一职责原则 SRP
一个类应该只有一个更改它的原因,也就是这个类只有一个职责
每个类的职责都有清晰明确的定义
一个类的修改只对自身有影响,对其他类没有影响
开闭原则 OCP
软件实体(类 模块 函数)应该可以扩展,但是不能修改.对于扩展是开放的,对于修改是封闭的.
对于要怎讲爱新功能或者需要调整的改动,应该扩展新的代码而不是修改原有的代码.
对程序中频繁变化的部分抽象
不要刻意的对每一个部分都进行抽象,拒绝不成熟的抽象,这和抽象本身一样重要.
依赖倒置原则 DIP
程序不应该依赖细节,细节应该依赖于抽象. 针对接口编程 而不是针对实现编程
使用接口或者抽象类的目的是制定好规范,不涉及任何具体的操作,把细节任务交给实现类去完成
让程序中所有的依赖关系都终止于抽象类或者接口
高层模块不应该依赖底层模块,二者都应该依赖抽象
抽象不应该依赖细节,细节应该依赖抽象
里氏替换原则 LSP
一个软件实体如果适用于父类,必定适用于子类,并且察觉不出子类和父类的区别,也就是子类必须能够替换父类
父类一般使用抽象类或者接口
抽象类定义公共对象和状态;接口定义公共行为
子类通 ...
Java笔记_4
垃圾回收算法(四种)
垃圾回收需要找到内存中存活的对象
还需要释放不再存活的对象, 使程序可以再次利用这部分空间
标记-清除算法
复制算法
标记-整理算法
分代GC
垃圾回收会有单独的GC线程来完成, 但是不管哪一种GC算法, 都会有部分阶段需要停止所有用户线程, 称之为Stop The World, 简称STW, 如果STW的时间过长, 则会影响用户使用
为什么一定需要STW?
分析工作必须在能够确保一致性的快照中进行
一致性指整个分析期间系统被冻结在某个时间点上
如果分析过程中对象的引用关系还在不断地变化, 那么分析的准确性就没有办法保证
如果不暂停业务线程的话, 在垃圾回收期间新创建的对象会被错误的回收, 这是因为业务线程和垃圾回收线程都是并行执行的
评价标准:
吞吐量: CPU用于执行用户代码的时间与CPU总执行时间的比值, 吞吐量=执行用户代码的时间/(执行用户代码的时间+GC时间), 吞吐量越高表示垃圾回收效率越高
最大暂停时间: 垃圾回收过程中STW时间的最大值, 越小越好
堆使用效率: 不同垃圾回收算法, 堆内存使用效率不同. 比如标记清除算法, 可 ...
Java笔记_3
垃圾回收器
主要负责在堆上进行内存回收
自动垃圾回收可以降低实现难度, 降低回收bug的可能性
但是程序员无法控制内存回收的及时性, 也无法完全避免内存溢出
应用场景
解决系统僵死(因为频繁的垃圾回收)
性能优化
常见垃圾回收, 四种引用等
方法区回收
线程不共享的程序计数器和Java虚拟机栈以及本地方法栈, 都只需要等待线程销毁自己就销毁了, 不需要垃圾回收
方法区回收的条件(三个)
方法区中的类不再使用, 即可被回收
类的所有实例都已经被回收了, 在堆中不存在任何该类的实例对象以及子类对象 123Class<?> clazz = loader.loadClass("类的全限定名");Object o = clazz.newInstance();o = null; // 此时对象o不再使用, 就可以让gc自动回收clazz类
加载该类的类加载器已经被回收了 12URLClassLoader loader = new URLClassLoader(new URL[]{new URL(spec:"路径" ...
Java笔记_2
运行时数据区
分类
线程不共享: 程序计数器、Java虚拟机栈、本地方法栈
线程共享: 方法区、堆区
程序计数器
Program Counter Register, 也叫做PC寄存器, 每个线程会通过程序计数器记录当前要执行的字节码指令的地址
作用
控制程序指令的执行, 比如跳转, 分支, 异常
多线程情况下JVM通过程序计数器记录CPU切换前执行到哪一句, 切换回来后执行并继续解释运行
程序计数器会产生内存溢出的问题吗
内存溢出指在使用某一块内存区域时, 存放的数据需要占用的内存大小超过了虚拟机能够提供的内存上限
每个线程只存储一个固定长度的内存地址, 因此程序计数器不会产生内存溢出
PC不用程序员修改
Java虚拟机栈
先进后出(FILO), 每个方法调用一个栈帧来保存
Java虚拟机栈随着线程的创建而创建, 线程销毁则栈回收
由于方法可能会在不同的线程中执行, 所以每个线程都有自己的虚拟机栈
栈帧组成
局部变量表, 操作数栈, 帧数据
局部变量表: 方法执行过程中存放所有的局部变量, 与字节码局部变量表不太一样
栈帧中的局部变量表是一个数组, 每 ...
Java笔记_1
类加载器
类加载器(ClassLoader)是JVM给应用程序实现类和接口字节码数据的技术
本地接口JNI允许Java调用其他语言编写的方法, 在hotspot类加载器中, 主要用于调用JVM中使用CPP编写的方法
应用
SPI机制
类的热部署
Tomcat类的隔离
类的双亲委派机制(怎么打破双亲委派机制)
自定义类加载器
使用Arthas不停机解决线上故障
分类
Java代码中实现的 or JDK默认提供 or 自定义的, 所有实现的类加载器都需要继承抽象类ClassLoader
JVM底层源码实现的, 跟虚拟机实现语言一致, 比如Hotspot使用cpp实现
作用是加载运行时的基础类, 保证运行中基础类可以被正确的加载, 比如java.lang.String, 确保可靠性
JDK8之前
JVM底层实现的类加载器, 启动类加载器Bootstrap, 加载Java中最核心的类, 在jre/lib目录下的所有类文件, 比如rt.jar, tools.jar, resources.jar
如果打印看到类加载器为null, 则就是启动类加载器. 因为为了安全性考虑, ...
Java笔记_0
面向对象的特性
封装
将数据和行为组合在一个包中, 并对使用者隐藏具体的实现细节
关键在于不能让别的类调用到当前类的实例字段
继承
实现了IS-A的关系, 子类可以获得父类非private的属性和方法
应该要遵循里氏替换原则
里氏替换原则: 子类必须能够替换掉所有的父类对象, 父类引用指向子类对象称为向上转型
举例
Cat和Animal就是一种IS-A关系, 所以Cat可以继承Animal, 并且获得Animal的所有非private的属性和方法
Cat可以当做Animal使用, 所以Animal可以引用Cat对象: Animal a = new Cat(), 这就是向上转型
类常见的关系
依赖 USES-A: 只要一个类需要使用或操作另一个类, 就说明前者依赖后者. 需要尽可能减少相互依赖的类, (减少耦合)
聚合 HAS-A: 类A的对象包含类B的对象
继承 IS-A
多态
分为编译时多态和运行时多态
编译时多态: 指方法的重载
运行时多态: 指对象引用的具体类型在运行期间才能确定
运行时多态具有三个条件: 继承、覆盖(重写)、向上转型
重载指的 ...
Kubernetes笔记_0
架构
一个Kubernetes集群至少包含一个控制平面,以及一个或多个工作节点
控制平面
负责管理工作节点和维护集群状态,所有任务的分配都来自控制平面
为集群做出去全局决策,比如资源的调度、检测和响应集群事件
kube-apiserver
如果与Kubernetes集群进行交互,需要通过API
apiserver是Kubernetes控制平面的前端,用于处理内部和外部请求
kube-scheduler
集群状态是否良好,如果需要创建新的容器,需要将他们放在哪里,由调度程序关注
scheduler调度程序考虑容器集的资源需求,比如CPU或者内存,以及集群的运行状态,然后将容器集安排到适当的计算节点
kube-controller-manager
控制器负责实际运行集群,controller-manager控制器管理器将多个控制器功能合并,降低了程序的复杂性
controller-manager包含以下控制器:
节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应
任务控制器(Job Controller): 检测代表一次性任务的Job对象 ...
Kafka笔记_1
消费
watermark水位线以下的数据是消费者可以消费的数据
消费者组中的消费者和分区之间的分配关系
同一个消费者组中的消费者都订阅同一个主题,所以消费者组中的多个消费者可以共同消费同一个主题中的所有数据
为了避免数据被重复消费,所以主题一个分区的数据只能被组中的一个消费者消费,所以两个消费者不能同时消费一个分区的数据。但是一个消费者可以同时消费多个分区的数据。
消费者分区分配策略(4种)
具体的分配策略实际上是由消费者组中的Leader决定的,Leader就是群主,是第一个加入消费者组的消费者
消费者加入群组时,发送一个JoinGroup,群主负责给每一个消费者分配一个分区
轮询分配策略(RoundRobinAssignor)
每个消费者组中的消费者都含有一个自动生成的UUID作为memberid
轮询策略会将每个消费者按照memberid进行排序,所有member消费的主题分区根据主题名称进行排序
将主题分区轮询分配给对应的订阅用户,未订阅当前轮询主题的消费者会跳过
范围分配策略(RangeAssignor)
每个Topic的partition数计算出 ...
Kafka笔记_0
消费模式
点对点模式(P2P)
队列中只有一个消费者可以消费数据,用后即销毁,因此数据有且只有一次消费。
适合用于短信业务,发送一次,消费一次。 特点:
- 每个消息只有一个接收者
- 发送和接收之间没有依赖,发送者成功发送消息后,不管接收有没有运行,都可以再次发送消息
- 接收者在成功接收后需要向队列应答成功,以便消息队列删除当前消息
发布订阅模式(PS)
数据会在队列中存储7天,每个订阅都可以消费到相应的数据,可以重复进行消费数据。
大部分都是发布订阅模式。 特点:
- 每个消息有多个订阅者
- 发布者和订阅者之间有时间上的依赖性。针对某个Topic的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息
- 为了消费消息,订阅者需要提前订阅该角色主题,并保持在线运行
基础架构
Kafka不是主从集群,因此每一个节点都可以是主节点,如果一个节点宕机了,那么其他的节点可以被选举为主节点
Kafka中每一个节点都称为Broker,每个节点都存在一个Kafka_controller组件
但是只有一台节点的controller组件是活跃状态,其他都是standby状态 ...
Redis笔记_5
QuickList
ZipList虽然节省内存,但是申请的是连续的空间,如果内存占用较多,内存申请效率很低;
这时候需要限制ZipList的长度或者entry大小;
也可以创建多个ZipList分片存储数据
但是拆分后比较分散,不方便管理和查找,所以引入了QuickList
QuickList本质上是一个双端链表,其中的每一个节点都是ZipList
为了避免QuickList中每个ZipList的entry过多,可以配置list-max-ziplist-size来进行限制
如果为正,表示ZipList的允许的entry最大个数
如果为负,表示ZipList的最大内存大小,共五种情况
情况
内存占用
-1
<= 4KB
-2(默认)
<= 8KB
-3
<= 16KB
-4
<= 32KB
-5
<= 64KB
QuickList同时可以对节点ZipList进行压缩,通过配置list-compress-depth控制。因为链表一般都是首尾访问较多,所以首尾不压缩。
情况
含义
0
不压 ...
Redis笔记_4
简单动态字符串SDS
传统的C语言字符串存在问题:
1. 获取字符串长度需要通过运算
2. 非二进制安全,如果中间有一个\0则字符串直接结束了
3. 不可修改
set name sangs: 这条命令会在底层创建两个SDS,分别是name 和 sangs的SDS
123456struct __attribute__ ((__packed__)) sdshdr8{ uint8_t len; // buf已保存的字符串字节数,不包含结束标识 uint8_t alloc; // buf申请的总字节数,不包含结束标识,第一次触发时,申请内存一般和len一样 unsigned char flags; // 不同的SDS头类型,用来控制SDS的头大小 char buf[];};
len: 4alloc: 4flags: 1name\0
由于通过len判断需要读取多少位,所以是二进制安全的,不会读取到一半终止
之所以叫动态字符串,是因为具备动态扩容的 ...
Redis笔记_3
传统缓存的问题
传统缓存策略是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库:
请求经过Tomcat处理,性能成为整个系统的瓶颈
Redis缓存失效时,对数据库产生冲击
多级缓存就是充分利用请求处理每个缓环节,分别添加缓存,减轻Tomcat压力,提升服务性能。
缓存分类
分布式缓存比如Redis:
优点: 存储容量更大,可靠性更好,可以在集群间共享
缺点: 访问缓存有网络开销
场景: 缓存数据量较大,可靠性要求较高,需要在集群间共享
进程本地缓存,比如HashMap、GuavaCache:
优点: 读取本地内存,没有网络开销,速度更快
缺点: 存储容量有限,可靠性较低,无法共享
场景: 性能要求较高,缓存数据量较小
Caffeine
123456789Cache<String, String> cache = Caffeine.newBuilder().build();cache.put("key", "value")// 取数据,如果没有则返回NULL,使用较少String key = cac ...
Redis笔记_2
数据持久化
RDB
Redis Database Backup file(Redis数据备份文件),也叫做Redis数据快照。把内存中的所有数据都记录到磁盘中,故障重启后,从磁盘读取快照文件,恢复数据。
RDB文件称为快照文件,默认保存当前运行目录
1234redis-clisave # 使用save命令来进行快照保存,由Redis主进程执行RDB,阻塞所有命令# 所以一般不使用save,Redis在停机退出的时候会自动执行save,再次启动会读取RDB,实现默认的持久化bgsave # 开启子进程执行RDB,避免主进程受到影响
redis.conf配置RDB机制
12345678save 900 1 # 表示如果900s内至少有1个key被修改,则执行bgsavesave 300 10save 60 10000 # 如果是save "" 表示禁用RDBrdbcompression yes # 是否压缩,建议不开启,因为压缩也会消耗CPUdbfilename dump.rdb # RDB文件名称dir ./ # 文件保存路径目录
RDB的bgsave开始 ...
Redis笔记_1
全局唯一ID: 经常会需要全局唯一ID,比如订单表的生成。因为
如果ID自增,则规律性太明显
并且表中可能存在大量的数据,会受到表单数据量的限制
全局唯一ID生成器特性:
唯一性
高可用
高性能
递增性
安全性
超卖问题
原本库存为1,线程1查询后发现库存满足要求。要去扣除库存,但在此之前,线程2,线程3同时查询,发现库存充足。同样满足扣除库存的要求,此时产生了超卖问题
解决方法: 加锁
乐观锁: 认为线程安全问题不一定发生,因此不加锁,只是在更新数据时判断有没有其他线程对数据进行了修改
如果没有修改则认为自己安全,才能更新数据
如果已经被其他线程修改说明发生了安全问题,此时重试或返回异常
悲观锁: 认为线程安全问题一定会发生,因此操作数据之前先获取数据,确保线程串行执行。(性能差)
比如Synchronized、Lock都是悲观锁
乐观锁
版本号法
用版本标识数据更新,如果版本version更新了,则表示数据已经被更新了。
CAS法
用之前查询到的数据判断是否存在相同的数据,如果不存在,则表示数据已经更新。实际上就是简化了版本字段。
...
Redis笔记_0
SQL VS NoSQL
SQL
NoSQL
数据结构
结构化
非结构化
数据关联
关联的
无关联
查询方式
SQL查询
非SQL
事务特性
ACID
BASE
存储方式
磁盘
内存
扩展性
垂直
水平
使用场景
数据结构固定且对安全性,一致性要求较高
数据结构不固定,对一致性安全性要求不高,对性能有一定要求
特征:
键值型,值支持多种不同的数据结构,功能丰富
单线程,每个命令具有原子性。现在多线程仅仅在网络连接请求方面,内部核心命令依然是单线程的
低延迟,速度快
支持数据持久化
支持主从集群,分片集群
支持多语言
为什么Redis单线程,但是速度快?
基于内存(最重要的原因)
IO多路复用
使用C语言,良好的编码
Redis安装
使用Docker安装:
在docker-data/redis/中执行命令wget http://download.redis.io/redis-stable/redis.conf下载config
修改权限
修改配置信息 1234567891011bind 127.0.0.1 # 注 ...
MySQL笔记_5
日志
MySQL中日志分为四类: 错误日志、二进制日志、查询日志、慢查询日志
错误日志
记录了当mysqld启动和停止时,以及服务器在运行过程中发生的任何严重错误时的相关信息。服务器出现故障无法使用就看这个日志
默认开启,存放在/var/log/中,日志文件名为mysqld.log
查看错误日志位置: show variables like '%log_error%';
二进制日志
binlog是server层日志
binlog记录了所有的DDL, DML语句,不包含查询语句
binlog默认开启,查看二进制日志位置: show variables like '%log_bin%';
最开始MySQL只有MyISAM引擎,只有binlog用于归档,没有crash-safe的能力,后面InnoDB设计了另一套日志系统,利用redo log来实现crash-safe的能力
作用
灾难时数据恢复
MySQL主从复制
日志格式
查看二进制的日志格式: show variables like '%binlog_format%';
日志格式
含义
STATE ...
MySQL笔记_4
锁
全局锁
锁整个数据库的,加锁后数据库只读。后续的DML、DDL包括已经更新操作的事务提交都被阻塞
应用场景:数据库逻辑备份,获得一致性视图,保证完整性
set global readonly=true也可以实现全库只读
但是readonly的值可能用来处理其他逻辑,比如主从库判断;
并且FTWRL可以再数据库异常退出的时候自动释放,但是readonly不行
加全局锁: flush tables with read lock;(FTWRL)
数据库备份: musqldump -u用户名 -p密码 数据库名 > 路径/数据库名.sql
解锁: unlock tables;
数据库备份的mysqldump命令只是一个工具,不是mysql内部的命令,因此不能在mysql界面中使用。在控制台使用即可,如果备份远程数据库,则需要使用-h 设置ip
缺点:
主库上备份,则备份期间业务停摆,无法更新
从库上备份,则备份期间主库可以更新,但是从库无法执行主库同步的二进制日志(binlog),导致主从延迟
InnoDB引擎中为了解决这个问题,加上 ...
MySQL笔记_3
SQL优化
insert优化
使用批量插入,一次性插入500~1000条数据,而不是一条一条插入,也不能一次性插入过多
使用手动事务提交
主键顺序插入
如果需要插入大批量的数据,可以使用MySQL提供的load指令,而不使用insert 12345678-- 使用load需要修改-- 客户端链接服务端 加上--local-infile参数mysql \-\-local-infile -u root -p-- 设置全局参数local-infile为1,开启本地加载文件到数据库的开关set global local-infile = 1;-- 执行load指令 将准备好的数据加载到表结构中load data local infile '/root/sql1.log' into table 'user' fields terminated by ',' lines terminated by '\n';-- 每个字段之间使用,分割,每行之间使用'\n'分割
对于load命令而言,同样顺序插 ...
MySQL笔记_2
索引
高效获取数据的有序数据结构
提高检索效率,降低IO成本。
索引列对数据进行排序,降低排序成本,减少CPU的消耗
但是需要占用一些空间,提高查询效率有时候会降低更新表的速度,比如增删改。但是实际上增删改比例较少。
索引结构
描述
InnoDB
MyISAM
Memory
B+树索引
最常见的索引类型,大部分引擎都支持
支持
支持
支持
Hash索引
底层使用哈希表实现,只能精确匹配索引列的查询才有效,不支持范围查询
-
-
支持
R-tree(空间索引)
是MyISAM引擎的一个特殊索引类型,用于地理空间数据类型,使用较少
-
支持
-
Full-text(全文索引)
通过建立倒排索引,快速匹配文档的方式,类似于Lucene, Solr, ES
5.6版本后支持
支持
-
常见索引模型
哈希表
比如拉链法解决哈希冲突,追加新数据的速度很快,但是如果需要区间查找,就很慢。所以哈希表只适合等值查找
有序数组
可以解决范围查找问题,等值和范围查找都可以达到log(n)log(n)log(n),但是插入删除元素需要移动数据,只适合静态搜索
...
MySQL笔记_1
函数
字符串函数
函数
功能
CONCAT(S1, S2, …, Sn)
字符串拼接,将S1, S2, …, Sn拼接成一个字符串
LOWER(str)
将字符串转为小写
UPPER(str)
将字符串转为大写
LPAD(str, n, pad)
左填充,用字符串pad对str的左边进行填充,达到长度n
RPAD(str, n, pad)
右填充,用字符串pad对str的右边进行填充,达到长度n
TRIM(str)
去掉字符串头部和尾部的空格
SUBSTRING(str, start, len)
返回从字符串str的start位置起的len个长度的字符串
trim不能去除中间的空格
substring的start从1开始,不是0
substring()可以只输入两个参数,表示从start的位置截取到字符串末尾
start参数可以为负,如果是负数,则自动忽略len参数,表示截取最后几个字符
lpad和rpad如果输入的字符串过长,超过了第二个参数n,则自动变为从左往右的n个字符
数值函数
函数
功能
CEIL(x)
向上取整 ...
MySQL笔记_0
创建数据库
字符集选择: utf8mb4
Unicode编码,也称作统一码,万国码。如:utf8, utf16, utf32
utf8mb4兼容utf8,并且可以表示更多的字符。具体来说: Unicode中的第1-126行是utf8的区域,utf8mb4同样兼容这一片区域,剩下126行以下的部分是utf8mb4扩充区域。
一般不选择utf8,因为其占用3B;而实际上很多内容,比如表情,就需要4B空间,所以一般使用utf8mb4
排序规则选择:
对于utf8mb4而言,常用的排序规则有utf8mb4_unicode_ci,utf8mb4_general_ci,utf8mb4_bin
utf8mb4_unicode_ci
utf8mb4_general_ci
utf8mb4_bin
基于标准的Unicode规则来排序和比较
没有实现Unicode排序规则
将字符串的每个字符用二进制数据编译存储
各语言精确排序
在某些特殊语言字符集,排序结果可能不一致
不区分大小写
不区分大小写
区分大小写
为了能够处理特殊字符,实现了略微复杂的排序算法
比较和排序 ...