diff --git a/README.md b/README.md
index 9a41fa27..79984d15 100755
--- a/README.md
+++ b/README.md
@@ -1,12 +1,11 @@
**`声明`**
-此项目已维护快3年多了,接下来我有了新的目标,后期的更新不会太过频繁,我也在思考如何精简自己的知识脑图。
本项目为个人学习之总结,内容大多来源网上、书籍、视频,若引用不当,麻烦告知,我定删除。喜欢的话可以`Star`下,生活不易,希望对读者有帮助。祝好!!!
### 目录
-| 英语 | Java | Spring生态 | 中间件 | 数据库 | 服务器 | 网络/系统 | 架构设计 | 内功 | 程序人生 |
-|:----|:-----|:------|:------|:------|:------|:------|:------|:------|:-----|
-|英语|Basic
JVM
Web
|Spring
Spring Boot
Spring Cloud|Keepalived
ActiveMQ
RabbitMQ
RocketMQ
MQTT
Netty
Mybatis
Redis
Log|MySQL
Postgresql
Mongodb|Tomcat
Nginx|网络
Linux|原则
安全
高可用
扩展性
伸缩性
性能
|数据结构
算法
设计模式|软文
规范
工具
面试集锦
实战训练|
+| 英语 | Java生态 | Golang生态 | 中间件 | 持久化 | 服务器 | 网络/系统 | 内功 | 程序人生 |
+|:----|:-----|:------|:------|:------|:------|:------|:------|:------|
+|英语|Java Basic
JVM
Web
Spring
Spring Boot
Spring Cloud|Go Basic
Go Web|Keepalived
ActiveMQ
RabbitMQ
RocketMQ
MQTT
Netty
Mybatis
Redis
Log|MySQL
Postgresql
Mongodb|Tomcat
Nginx|网络
Linux|数据结构
算法
设计模式|软文
规范
工具
面试集锦
实战训练|
### 英语
* [计算机行业常用英语积累](tool/english.md)
@@ -16,7 +15,11 @@
[JDBC基础](https://github.com/zhonghuasheng/JAVA/blob/master/jdbc/src/main/java/com/zhonghuasheng/jdbc/learn01/BasicSteps.java) [Java集合](java/basic/java-collection.md) [Java多线程系列](java/basic/java-thread.md) [JUC系列](java/basic/java-thread-juc.md) [Java IO基础](java/basic/java-io-nio.md)
* `Java -VM`: [Java虚拟机系列](java/jvm/深入理解Java虚拟机.md) [JVM虚拟机监控及性能调优系列](java/jvm/JVM虚拟机监控及性能调优.md)
* `Java-Web`: [Servlet基础](java/javaweb/servlet.md) [JSP基础](java/javaweb/jsp.md)
-* `Spring X`: [Spring4基础知识系列](java/spring/spring.md#Spring) [SpringMVC基础知识系列](java/spring/spring.md#SpringMVC) [SpringBoot基础知识系列](java/spring/spring.md#SpringBoot)
+* `Spring X`: [Spring4系列](java/spring/spring.md#Spring) [SpringMVC系列](java/spring/spring.md#SpringMVC) [SpringBoot系列](java/spring/spring.md#SpringBoot) [SpringCloud系列](java/spring/spring-cloud/spring-cloud.md)
+
+### Golang
+* `Golang Basic`: [Go基础](golang/basic/go-basic.md)
+* `Golang Web`: [Go Web](golang/basic/go-basic.md#goweb)
### 中间件
* `负载均衡`: [Keepalived系列](plugins/keepalived.md)
@@ -24,7 +27,7 @@
* `数据访问`: [MyBatis](plugins/mybatis.md) [MyBatis-Plus](plugins/mybatis-plus.md)
* `数据缓存`: [Redis系列](plugins/redis.md)
* `搜索引擎`: [Elasticsearch](plugins/elasticsearch.md)
-* `日志模块`: [Log4j2](plugins/log.md)
+* `日志模块`: [Log4j2](plugins/log.md)
### 数据库
* `关系型数据库`: [数据库理论基础](plugins/database/database.md) [MySQL](plugins/mysql.md) [Postgresql](plugins/postgresql.md)
@@ -34,13 +37,11 @@
* [Tomcat服务器](plugins/tomcat.md) [Nginx反向代理服务器搭建](plugins/nginx.md) [Linux系统常用命令](tool/shell/linux.md)
### 架构设计
-* `设计原则`: [系统设计注意事项](system/architecture/系统设计注意事项.md)
-* `系统安全`: [系统架构安全设计](system/architecture/系统架构安全设计.md)
-* `高可用性`: [系统架构高可用设计](system/architecture/系统架构高可用设计.md)
-* `高扩展性`: [系统架构扩展性设计](system/architecture/系统架构扩展性设计.md)
-* `高伸缩性`: [系统架构伸缩性设计](system/architecture/系统架构伸缩性设计.md)
-* `系统性能`: [系统架构性能设计](system/architecture/系统架构性能设计.md)
-* `其他事项`: [系统架构设计其他注意事项](system/architecture/系统架构设计其他注意事项.md)
+* [分布式锁](system/architecture/分布式锁.md)
+* [分布式ID生成方案](system/architecture/分布式ID生成方案.md)
+* [如何设计一个对外安全的接口](system/architecture/如何设计一个对外安全的接口.md)
+* [CAP理论](system/architecture/CAP.md)
+* [日志](system/architecture/log-note.md)
### 内功
* `设计模式`: [23种设计模式](system/algorithm/设计模式.md)
@@ -59,6 +60,7 @@
* [新工程师要干的五件事情](tool/coding-life.md/#新工程师要干的五件事情)
* [为什么CTO,技术总监,架构师不写代码都这么牛逼](http://note.youdao.com/noteshare?id=f4eeda7da9b73adf4294f984a5e7cbe5&sub=945D4467238947AAB5C030B17D5AC01E)
* [技术/管理](https://www.cnblogs.com/yexiaochai/p/14805941.html#top)
+* [简单/不简单](https://www.cnblogs.com/xxcanghai/p/ping-shi-de-gong-zuo-ru-he-ti-xian-yi-ge-ren-de-ji.html)
> `规范`
* [雅虎前端34条军规](http://note.youdao.com/noteshare?id=b59d0da4f7bb2b7ba5f73129d85b1ba1)
@@ -68,6 +70,8 @@
* [Java命名规范参考](http://note.youdao.com/noteshare?id=c0ca7331624eb2f19b06f623a1b832ae&sub=2F7223EB9D9E4072B60A1FB578BF0AFA)
> `工具`
+* [尖刀]()
+ * [Hutool](tool/hutool.md)
* [Java诊断工具]
* [阿里JAVA诊断工具Arthas](tool/tools.md)
* [API测试工具](tool/api-testing-tool.md)
@@ -87,8 +91,6 @@
> `实战训练`
* [秒杀商城](https://github.com/zhonghuasheng/JAVA/tree/master/seckill)
-[](https://starchart.cc/zhonghuasheng/Tutorial)
-
技术栈
diff --git a/golang/basic/go-100-questions.md b/golang/basic/go-100-questions.md
new file mode 100644
index 00000000..fb57b30e
--- /dev/null
+++ b/golang/basic/go-100-questions.md
@@ -0,0 +1,5 @@
+# Go白问
+1. 使用go关键字开启一个协程都发生了什么?它与java中的并发有什么不一样的地方?
+```go
+go count(5) // 使用go关键字创建一个goroutine
+```
\ No newline at end of file
diff --git a/golang/basic/go-basic.md b/golang/basic/go-basic.md
new file mode 100644
index 00000000..cf6f2b04
--- /dev/null
+++ b/golang/basic/go-basic.md
@@ -0,0 +1,88 @@
+### 学习计划
+* B站8小时转go开发
+* go-kit实战小例子
+
+#### go basic
+
+#### go web
+* 示例地址 https://github.com/zhonghuasheng/JAVA_LOVE_GO/tree/master/gop/com/zhonghuasheng/web
+
+### 目录
+* RPC
+* Protobuf
+* go thinking
+* basic
+* 空白标识符
+* 注意
+
+### RPC
+整体过程就是:
+1. 客户端发送数据(以字节流的方式)
+2. 服务端接收并解析。根据约定知道执行什么,然后把结果返回客户端
+RPC就是把
+1. 上述流程封装下,使其操作更加优化,让使用者感觉像是调用本地方法一样
+2. 使用一些大家都认可的协议 使其规范化
+3. 做成一些框架,直接或间接产生利益
+
+### Protobuf
+Google Protocol Buffer
+轻便高效的序列化数据结构协议
+
+### go thinking
+众多语言学术流派,面向过程、面向对象、函数式编程、面向消息编程等。Go语言接受函数式编程、支持匿名函数与闭包,接受以Erlang语言为代表的面向消息编程思想,支持goroutine和通道,并推荐使用消息而不是共享内存来进行并发编程。Go语言最主要的特性
+* 自动垃圾回收
+* runtime系统调度机制
+* 更丰富的内置类型
+* 函数多返回值
+* 错误处理
+* 匿名函数和闭包
+* 类型和接口
+* 并发编程
+* 反射
+* 语言交互性
+
+### basic
+* Go程序由多个标记组成,可以是关键字、标识符、常量、字符串、符号。
+ * 行分隔符: 每一行代表一个语句结束。建议每一行加上分号;标识结尾,不加的话Go编译器自动完成
+ * 注释:注释不会被编译
+ * 标识符:标识符用来命名变量、类型等程序实体
+ * 字符串连接:Go语言的字符串可以通过+实现
+ * 关键字:Go语言包含25个关键字。break default func interface select case defer go map struct chan else goto package switch const flatthrough if range type continue for import return var
+ * Go语言还有36个预定义标识符
+* 空白标识符_是一个只写变量,你不能得到它的值,被用于抛弃值。这样做是因为Go语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
+* 环境搭建
+ * windows环境搭建
+ * 安装go二进制文件 到镜像网站下载 https://goproxy.io/zh/
+ * 下载Goland IDEA,设置GOPATH
+ * 配置GOPROXY代理,不然很慢
+ * go get xxx 下载xxx包
+ * go mod tidy 刷新 类似maven update
+* go三层经典模型
+ * Transport 主要负责Http, gRpc, thrift
+ * Services 业务函数
+ * Endpoint 定义request, response格式等
+* 四种变量声明方式
+* channel,go语言是如何实现不同协程之前通信的呢?其他语言是通过共享内存来实现通信,而go语言是通过通信来实现共享。一个重要的特性:不论是从channel中读一条消息,还是往channel中发送一条消息,都会阻塞当前代码运行。
+
+##### 指针
+取址运算符“&”用于获取对象地址。
+指针运算符“*”用于间接引用目标对象。
+```code
+x := 10
+var p *int = &x // 获取地址,保存到指针变量
+*p += 20 // 用指针间接引用,并更新对象
+```
+
+### 注意
+* go语言中{不能单独放在一行,否则运行时会产生错误
+
+### 其他
+* facebook grace平滑升级项目
+* 腾讯在15年的时候使用go做了docker万台规模,主要用于蓝鲸游戏平台
+
+### 常见错误
+* package command-line-arguments is not a main package
+要运行的go文件,package不是main。PS,也要有一个main函数
+
+* sql: Scan error on column index 3, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
+在conn上加parseTime=true,eg `/guestdb?charset=utf8&parseTime=true`
\ No newline at end of file
diff --git a/golang/basic/go-question.md b/golang/basic/go-question.md
new file mode 100644
index 00000000..67360975
--- /dev/null
+++ b/golang/basic/go-question.md
@@ -0,0 +1,111 @@
+# Go语言答疑解惑
+
+## channel
+* 从一个关闭的 channel 仍然能读出数据吗
+* channel 有哪些应用
+* 如何优雅地关闭 channel
+* channel 在什么情况下会引起资源泄漏
+* 什么是 CSP
+* channel 底层的数据结构是什么
+* channel 发送和接收元素的本质是什么
+* 关于 channel 的 happened-before 有哪些
+* 向 channel 发送数据的过程是怎样的
+* 从 channel 接收数据的过程是怎样的
+* 操作 channel 的情况总结
+* 关闭一个 channel 的过程是怎样的
+
+## map
+* map 的底层实现原理是什么
+* 可以边遍历边删除吗
+* map 的删除过程是怎样的
+* 可以对 map 的元素取地址吗
+* 如何比较两个 map 相等
+* 如何实现两种 get 操作
+* map 是线程安全的吗
+* map 的遍历过程是怎样的
+* map 中的 key 为什么是无序的
+* float 类型可以作为 map 的 key 吗
+* map 的赋值过程是怎样的
+* map 的扩容过程是怎样的
+
+## interface
+* iface 和 eface 的区别是什么
+* Go 接口与 C++ 接口有何异同
+* 接口转换的原理
+* 如何用 interface 实现多态
+* Go 语言与鸭子类型的关系
+* 值接收者和指针接收者的区别
+* 接口的构造过程是怎样的
+* 编译器自动检测类型是否实现接口
+* 类型转换和断言的区别
+* 接口的动态类型和动态值
+
+## 标准库
+* context 如何被取消
+* context 是什么
+* context 有什么作用
+* context.Value 的查找过程是怎样的
+* unsafe
+* Go指针和unsafe.Pointer有什么区别
+* 如何利用unsafe包修改私有成员
+* 如何利用unsafe获取slice&map的长度
+* 如何实现字符串和byte切片的零拷贝转换
+
+## goroutine 调度器
+* g0 栈何用户栈如何切换
+* goroutine 如何退出
+* goroutine 调度时机有哪些
+* goroutine和线程的区别
+* GPM 是什么
+* M 如何找工作
+* mian gorutine 如何创建
+* schedule 循环如何启动
+* schedule 循环如何运转
+* sysmon 后台监控线程做了什么
+* 一个调度相关的陷阱
+* 什么是 go shceduler
+* 什么是M:N模型
+* 什么是workstealing
+* 描述 scheduler 的初始化过程
+
+## 编译和链接
+* Go 程序启动过程是怎样的
+* Go 编译相关的命令详解
+* Go 编译链接过程概述
+* GoRoot 和 GoPath 有什么用
+* 逃逸分析是怎么进行的
+
+## 反射
+* Go 语言中反射有哪些应用
+* Go 语言如何实现反射
+* 什么情况下需要使用反射
+* 什么是反射
+* 如何比较两个对象完全相同
+
+## 数组与切片
+* 切片作为函数参数
+* 切片的容量是怎样增长的
+* 数组和切片有什么异同
+
+
+## GC 的认识
+* 什么是 GC,有什么作用?
+* 根对象到底是什么?
+* 常见的 GC 实现方式有哪些?Go 语言的 GC 使用的是
+* 三色标记法是什么?
+* STW 是什么意思?
+* 如何观察 Go GC?
+* 有了 GC,为什么还会发生内存泄露?
+* 并发标记清除法的难点是什么?
+* 什么是写屏障、混合写屏障,如何实现?
+* Go 语言中 GC 的流程是什么?
+* 触发 GC 的时机是什么?
+* 如果内存分配速度超过了标记清除的速度怎么办?
+* GC 关注的指标有哪些?
+* Go 的 GC 如何调优?
+* Go 的垃圾回收器有哪些相关的 API?其作用分
+* Go 历史各个版本在 GC 方面的改进?
+* Go GC 在演化过程中还存在哪些其他设计?为什么没有被采用?
+* 目前提供 GC 的语言以及不提供 GC 的语言有哪些?GC 和 No GC 各自的优缺点是什么?
+* Go 对比 Java、V8 中 JavaScript 的 GC 性能如何?
+* 目前 Go 语言的 GC 还存在哪些问题?
diff --git a/java/basic/hashmap.md b/java/basic/hashmap.md
index dd8e60ef..89b969f9 100644
--- a/java/basic/hashmap.md
+++ b/java/basic/hashmap.md
@@ -1,5 +1,14 @@
### 学习笔记
Hash几乎是面试的必考,我没事就找机会面试,几乎每次都被面到,面试的时候主要是考察你对Hash底层结构的理解,同时结合你项目实际中的使用来聊。我在这些年的开发和研发过程中,确实有很多场景的设计或者使用都或多或少用到了Hash,当然平时开发写代码,肯定少不了HashMap。面试官还喜欢问你对JDK7和8中HashMap的区别,也可以看看我的[面试宝典](../../tool/interview.md#盘涅)
+* HashMap 全理论 https://blog.csdn.net/weixin_34392227/article/details/112348756
+* HashMap面试题 https://www.cnblogs.com/aobing/p/12014271.html
+* HashMap JDK8以前头插法为什么会出现死循环 https://www.bilibili.com/video/BV1n541177Ea?spm_id_from=333.337.search-card.all.click
+
+
+### 为什么链表长度达到8后变为红黑树
+https://www.pianshen.com/article/2770135206/
+
+
### HashMap基础知识
* [一致性Hash](http://note.youdao.com/noteshare?id=2df0dfa390855dde55075260b8ddc27a&sub=A2CDBD1C8B614310ABFF0F03682FAB7A)
\ No newline at end of file
diff --git a/java/basic/img/executor-service.png b/java/basic/img/executor-service.png
new file mode 100644
index 00000000..c0470f80
Binary files /dev/null and b/java/basic/img/executor-service.png differ
diff --git a/java/basic/java-basic.md b/java/basic/java-basic.md
index 5be32739..c333d348 100755
--- a/java/basic/java-basic.md
+++ b/java/basic/java-basic.md
@@ -1189,7 +1189,8 @@ System.out.println(e2.get(2)); // 2
`transient`
将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。【对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序;从字节流创建对象的相反的过程称为反序列化】
-
+为什么要不序列化?
+ 一些敏感信息,比如密码和银行卡号等在java对象序列化时不希望被序列化,不被序列化的属性就不会被存储到磁盘上,或通过网络传输。为了实现属性不被序列化,只需要在属性前加上transient关键字就能达到目的。
`volatile`
volatile只能修饰变量,被修饰的变量对其他线程是“可见的”,同时volatile修饰的变量禁止指令重排(保证了有序性)
@@ -1595,4 +1596,10 @@ https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html
$ 在字符串的结尾结束匹配。
```
4. 获取系统临时目录 File tmpdir = new File(AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); AccessController.doPrivileged中断了栈检查过程,使得后续原本没有权限的代码也可以正常执行 https://blog.csdn.net/jiangtianjiao/article/details/87909065
-5. ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 用于解决fastjson漏洞,防止被远程执行
\ No newline at end of file
+5. ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 用于解决fastjson漏洞,防止被远程执行
+6. 动态监控JVM内存,并触发gc,可弄个定时任务
+```java
+long totalMemory = Runtime.getRunTime().totalMemory() / 1024 /1024;
+long freeMemory = Runtime.getRunTime().freeMemory() / 1024 /1024;
+System.gc(); // 可触发gc
+```
\ No newline at end of file
diff --git a/java/basic/java-collection.md b/java/basic/java-collection.md
index 832b7f8a..4fb2b264 100755
--- a/java/basic/java-collection.md
+++ b/java/basic/java-collection.md
@@ -42,6 +42,7 @@
* [Collections工具类-操作集合]()
* [Arrays工具类-操作数组]()
* [5. 圈重点](#5.圈重点)
+* [6. Stream](#6.Stream)
* [参考资料](#参考资料)
@@ -119,8 +120,30 @@ Map 映射顺序。有些实现类,可以明确保证其顺序,如 TreeMap
Map 的实现类应该提供2个“标准的”构造方法:第一个,void(无参数)构造方法,用于创建空映射;第二个,带有单个 Map 类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是 JDK 中所有通用的映射实现都遵从它。
```
-[Map的三种Collection视图例子 MapTest01.java](https://github.com/zhonghuasheng/JAVA/blob/master/basic/src/main/java/com/zhonghuasheng/basic/util/MapTest01.java)
+[Map的三种Collection视图例子 MapTest01.java]
+```java
+ // key视图
+ Set keys = hashMap.keySet();
+ Iterator iteratorKeys = keys.iterator();
+ while (iteratorKeys.hasNext()) {
+ System.out.println(iteratorKeys.next());
+ }
+
+ // value视图
+ Collection values = hashMap.values();
+ Iterator iteratorValues = values.iterator();
+ while (iteratorValues.hasNext()) {
+ System.out.println(iteratorValues.next());
+ }
+ // key-value视图
+ Set> entrySets = hashMap.entrySet();
+ Iterator> iteratorEntrySets = entrySets.iterator();
+ while (iteratorEntrySets.hasNext()) {
+ Entry entry = iteratorEntrySets.next();
+ System.out.println(String.format("key: %s, value: %s", entry.getKey(), entry.getValue()));
+ }
+```
## Concurrent包下的集合概述
1. 概述
@@ -491,7 +514,7 @@ AbstractSet没有对Set做多少的实现,其继承了AbstractCollection
`总结`
```
HashSet是一个没有重复元素的集合,它是由HashMap实现的(HashMap中key不能重复),不保证元素的顺序,而且HashSet允许使用null元素。
-HashSet是非同步的,因此如果多线程同时访问一个HashSet,而其中至少有一个线程修改了该HashSet夺得话,那么需要保持外部同步,通常可以对该Set的对象封装来完成同步操作,也可以使用Collections.synchronizedSet方法来完成。
+HashSet是非同步的,因此如果多线程同时访问一个HashSet,而其中至少有一个线程修改了该HashSet的话,那么需要保持外部同步,通常可以对该Set的对象封装来完成同步操作,也可以使用Collections.synchronizedSet方法来完成。
HashSet是通过Iterator迭代遍历的
```
@@ -1123,6 +1146,12 @@ List list = Arrays.asList(1, 2, 3);
* Collection集合用于存Object的,不支持存储基础数据类型,这是由Collection接口的定义决定的: Collection
* iterator.remove()方法必须要在调用了next()方法之后,否则会报IllegalStateException
+# 6.Stream
+* List转map,按某个字段分类
+```java
+ // group by City
+ Map> employeesByCity = employees.stream().collect( Collectors.groupingBy(Employee::getCity));
+```
# 参考资料
* 集合框架图 https://img-blog.csdn.net/20160124221843905
diff --git a/java/basic/java-thread-juc.md b/java/basic/java-thread-juc.md
index 02eea984..a88cb76f 100644
--- a/java/basic/java-thread-juc.md
+++ b/java/basic/java-thread-juc.md
@@ -1,4 +1,5 @@
# 目录
+* [为什么要AQS?](#为什么要AQS)
* [Java为什么会有并发问题](#Java为什么会有并发问题)
* [什么是CAS](#什么是CAS)
* [Unsafe类解读](#Unsafe类解读)
@@ -7,6 +8,10 @@
* [基础类型AtomicInteger](#基础类型AtomicInteger)
* [线程池](#线程池)
+
+## 为什么要AQS
+AQS(Abstracting Queue Sychronizer),望文生义,即一个抽象队列 + 一个需要同步的状态,所谓抽象队列即这个队列并不是真是存在的(通俗的讲,不是一个LinkedList对象),而是像HashMap中的链表一样,只存在Node之间的关系中,每个Node负责维护前置与后置节点,以及持有一个请求线程(可以理解为将一个请求线程封装成Node);
+http://chpf.rongsoft.com/article/2020/02/18101645580/
## Java为什么会有并发问题
> 出现的原因
因为Java是一种多线程的处理模型。所以当一个请求过来的时候,Java会将产生一个线程来处理这个请求。如果多个线程访问同一个共享变量的时候,就会出现并发问题。所以,并发问题产生的条件之一是“共享变量”。那么什么样的变量是共享变量呢?这就涉及到Java内存模型JMM了,Java内存模型中,一个Java线程,要想获取到一个变量,需要先将变量从主内存放入工作内存,然后再通过工作内存获取,经历一个lock->read->load->use的过程。每一个线程都有这样一个过程才能获取到变量,这样自然就有可能出现A线程获取到变量,还未赋值回主内存,就被B线程读取或更改的场景,这样自然就会出现不一致问题。
diff --git a/java/basic/java-thread.md b/java/basic/java-thread.md
index dd9312bc..19062975 100644
--- a/java/basic/java-thread.md
+++ b/java/basic/java-thread.md
@@ -18,6 +18,8 @@
* [生产者消费者问题](#生产者消费者问题)
* [钩子线程](#钩子线程)
* [线程中的异常](#线程中的异常)
+* [线程池](#线程池)
+* [建议](#建议)
### 基础概念
@@ -598,6 +600,15 @@ public final synchronized void join(long millis)
while(isAlive())是为了防止子线程伪唤醒(spurious wakeup),只要子线程没有TERMINATED的,父线程就需要继续等下去。
5. join() 和 sleep() 一样,可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会出让锁,而 sleep() 会一直保持锁。
+```java
+thread1.start();
+thread1.join();
+thread2.start();
+thread2.join();
+thread3.start();
+thread3.join();
+```
+调用线程的wait()方法时,会使主线程处于等待状态,等待子线程执行完成后再次向下执行。也就是说,在ThreadSort02类的main()方法中,调用子线程的join()方法,会阻塞main()方法的执行,当子线程执行完成后,main()方法会继续向下执行,启动第二个子线程,并执行子线程的业务逻辑,以此类推
### 线程的中断interrupted
interrupt()的作用是中断本线程。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
@@ -765,10 +776,66 @@ public class UncaughtExceptionExample {
非核心线程:当等待队列满了,如果当前线程数没有超过最大线程数,则会新建线程执行任务,那么核心线程和非核心线程到底有什么区别呢?说出来你可能不信,本质上它们没有什么区别,创建出来的线程也根本没有标识去区分它们是核心还是非核心的,线程池只会去判断已有的线程数(包括核心和非核心)去跟核心线程数和最大线程数比较,来决定下一步的策略。
-作者:三好码农
-链接:https://www.jianshu.com/p/9a8c81066201
-来源:简书
-著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
+### 线程池
+Executor框架是指java5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,
+CompletionService,Future,Callable等。并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe)。Executor在执行时使用内部的线程池完成操作。
+
+
+
+#### 创建线程池常用的类
+* Executors.newCachedThreadPool: 创建一个可缓存的线程池
+* Executors.newFixedThreadPool: 创建一个定长的线程池,可以控制线程的最大并发数,超出的线程会在队列中等待
+* Executors.newScheduledThreadPool: 创建一个定长的线程池,支持定时、周期性的任务执行
+* Executors.newSingleThreadExecutor: 创建一个单线程的线程池,使用一个唯一的工作线程执行任务,保证所有任务按照指定顺序执行
+* Executors.newSingleThreadScheduledExecutor: 创建一个单线程的线程池,支持定时、周期性的任务执行
+* Executors.newWorkStealingPool: 创建一个具有并行级别的work-stealing线程池
+> 参数
+* corePoolSize: 核心线程数量
+* maximumPoolSize: 最大线程数
+* workQueue: 阻塞队列,存储等待执行的任务
+线程池任务执行的主要工作流程:
+1. 如果运行的线程数小于corePoolSize,直接创建新线程处理任务,即使线程池中的其他线程是空闲的
+2. 如果运行的线程数大于等于cordPoolSize,并且小于maximumPoolSize,则将此任务放入队列workQueue,如果workQueue满并且正在运行的线程数量小于maximumPoolSize,才会创建新的非核心线程处理任务
+3. 如果运行的线程数量大于等于maximumPoolSize,同时,workQueue已经满了,会通过拒绝策略参数rejectHandler来指定处理策略
+4. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
+5. 当一个线程空闲时,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize 值,那么这个线程会被销毁。
+ > 根据上面三个参数配置,线程池会对任务进行如下处理方式
+ ```
+ 当提交一个新的任务到线程池,线程池会根据当前线程池中正在进行的线程数量来决定该任务的处理方式,处理方式共有三种:直接切换、使用无限队列、使用有界队列
+ 1. 直接切换常用的队列就是SynchronousQueue
+ 2. 使用无限队列就是使用基于链表的队列,比如:LinkedBlockingQueue,如果使用这种方式,线程池中创建的最大线程就是corePoolSize,此时maximumPoolSize不会起作用,因为队列是无限的,workQueue不会满。当线程池中的所有的核心线程都是运行状态时,提交新任务,就会放入等待队列中
+ 3. 使用有界队列比如ArrayBlockingQueue,使用这种方式可以将线程池的最大线程数限制为maximumPoolSize,可以降低资源的消耗。
+ ```
+> 参数
+* keepAliveTime: 线程没有任务执行时最多保持多久时间终止
+* unit: keepAliveTime的时间单位
+* threadFactory: 线程工厂,用来创建线程
+* rejectHandler: 拒绝处理任务时的策略
+ ```
+ 如果workQueue阻塞队列满了,并且没有空闲的线程池,此时,继续提交任务,需要采取一种策略来处理这个任务
+ 1. 默认直接抛出异常,实现类为AbortPolicy
+ 2. 用调用者所在的线程来执行任务,实现类为CallerRunsPolicy
+ 3. 丢弃队列中最靠前的任务,并执行当前任务,实现类DiscardOldestPolicy
+ 4. 直接丢弃当前任务,实现类为DiscardPolicy
+ ```
+> ThreadPoolExecutor提供的启动和停止任务的方法
+1. execute(); 提交任务,交给线程池执行
+2. submit(); 提交任务,能够返回执行结果execute + Future
+3. shutdown(); 关闭线程池,等待任务都执行完
+4. shutdownNow(); 立即关闭线程池,不等待任务执行完
+
+#### 线程池
+> 配置
+1. CPU密集型,设置为N + 1
+2. IO密集型,设置为2N
+> 创建
+* 使用Executors工具类创建线程池
+* 使用ThreadPoolExecutor类创建线程池
+* 使用ScheduledThreadPoolExecutor类创建线程池
+
+### 建议
+1. SimpleDateFormat类是线程不安全的,推荐使用JDK8中的DateTimeFormatter,高并发场景中推荐使用joda-time库来处理日格式化,效率高
+`protected Calendar calendar;`多个线程之间共享变量calendar,并修改calendar。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
# 引用
* https://www.cnblogs.com/skywang12345/
diff --git "a/java/jvm/JVM\350\231\232\346\213\237\346\234\272\347\233\221\346\216\247\345\217\212\346\200\247\350\203\275\350\260\203\344\274\230.md" "b/java/jvm/JVM\350\231\232\346\213\237\346\234\272\347\233\221\346\216\247\345\217\212\346\200\247\350\203\275\350\260\203\344\274\230.md"
index bb458435..99e11eb8 100644
--- "a/java/jvm/JVM\350\231\232\346\213\237\346\234\272\347\233\221\346\216\247\345\217\212\346\200\247\350\203\275\350\260\203\344\274\230.md"
+++ "b/java/jvm/JVM\350\231\232\346\213\237\346\234\272\347\233\221\346\216\247\345\217\212\346\200\247\350\203\275\350\260\203\344\274\230.md"
@@ -100,7 +100,28 @@
java.rmi.ConnectException: Connection refused to host: y.y.y.y; nested exception is:
```
> 注意:我在这里查看了远程机器的hosts,发现其配有一些其他的IP,将其删除后能够联通
-*
+* 快速定位导致cpu飙升的线程堆栈信息
+ ```log
+ top 首先通过top命令找到高负载的CPU,获取进程id
+ top -p <进程id> 精确定位到cpu高的进程,然后按H键,查看该进程所有线程
+ 或者 top -p 进程id -H 查看进程下的线程
+ printf "%x" 进程id 将进程号转化为16进制,注意把十六进制的大写字母转换为小写
+ jstack 进程id > xxx.txt 导出日志,然后在日志中查找nid=转换后进程id
+ 或者 jstack 进程ID|grep -A 10 55a0 10表示这个线程所在行后面10行,55a0是进程ID转换后的十六进制
+ ```
+* 查看堆内存使用情况
+ ```
+ jps查看各个应用进程id
+ jmap -heap java项目进程id
+ jmap -histo 进程id > log.txt 查看此应用中各实例生成情况
+ jmap -histo:live [pid] > log.txt 过滤存活的对象
+ ```
+* 查找代码死锁
+ ```
+ jstack 进程id > xxx.txt 导出日志
+ 搜索 deadlock 或者查 locked关键字找到发生死锁线程
+ ```
+
# 引用
* [jvisualvm简要说明](https://blog.csdn.net/weixin_38750084/article/details/83314046)
* [jvisualvm 连接 jstatd 远程监控 jvm 或 Visual GC提示"不受此JVM支持“](https://blog.csdn.net/liupeifeng3514/article/details/78998161)
diff --git a/java/spring/image/hystrix.png b/java/spring/image/hystrix.png
new file mode 100644
index 00000000..b61c63f3
Binary files /dev/null and b/java/spring/image/hystrix.png differ
diff --git a/java/spring/spring-annotation.md b/java/spring/spring-annotation.md
index 6b71e521..6c67f9a2 100644
--- a/java/spring/spring-annotation.md
+++ b/java/spring/spring-annotation.md
@@ -364,6 +364,11 @@ public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
* @NotEmpty 用在集合类上面 加了@NotEmpty的String类、Collection、Map、数组,是不能为null或者长度为0的(String Collection Map的isEmpty()方法)
* @NotBlank只用于String,不能为null且trim()之后size>0
* @NotNull:不能为null,但可以为empty,没有Size的约束
+ * @Size(min = 1,max = 10,message = "姓名长度必须为1到10")
+ private String name;
+ * @Past过去的时间
+ * @Future 将来的时间
+ * @ConditionalOnProperty(prefix="xxx", name={"yyy"}, havingValue="zzz") xxx为配置文件中的前缀,yyy为配置文件名字,havingVaue是与配置的值进行对比,返回true则配置类生效,反之失效
* SpringCloud
* @SpringBootApplication是Sprnig Boot项目的核心注解,目的是开启自动配置
* @EnableDiscoveryClient注解基于spring-cloud-common依赖,并且在classpath中实现
diff --git a/java/spring/spring-cloud/micro-service-register-discovery.md b/java/spring/spring-cloud/micro-service-register-discovery.md
deleted file mode 100644
index 6e877896..00000000
--- a/java/spring/spring-cloud/micro-service-register-discovery.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## 服务注册与发现
-
-> 服务注册
-* 服务注册是指向服务注册中心注册一个服务实例,服务提供者将自己的服务信息(如服务名,IP,端口等)告知服务注册中心。
-> 服务发现
-* 服务发现是指服务实例请求注册中心获取所依赖服务信息。
diff --git a/java/spring/spring-cloud/spring-cloud-gateway.md b/java/spring/spring-cloud/spring-cloud-gateway.md
new file mode 100644
index 00000000..79d3660e
--- /dev/null
+++ b/java/spring/spring-cloud/spring-cloud-gateway.md
@@ -0,0 +1,18 @@
+## 目录
+* [SpringCloud Gateway为什么会出现](#简介)
+* [能用来干什么](#功能特性)
+
+## 简介
+SpringCloud Gateway是基于spring-webflux,采用netty+reactor(Spring WebFlux 是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求,WebFlux 内部使用的是响应式编程(Reactive Programming),以 Reactor 库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。Spring MVC 构建于 Servlet API 之上,使用的是同步阻塞式 I/O 模型,什么是同步阻塞式 I/O 模型呢?就是说,每一个请求对应一个线程去处理。)
+
+## 功能特性
+1. 路由:能够在任意请求属性上匹配路由
+2. 过滤:
+3. 集成Hystrix熔断器
+4. 集成Spring Cloud DiscoveryClient,能够从注册中心获取服务注册列表,实现自动路由
+5. 集成了Ribbon,实现了负载均衡
+6. 能够限制请求速率
+
+Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由,目标URI会被访问。
+Predicate(断言):这是一个java 8的Predicate,可以使用它来匹配来自HTTP请求的任何内容,可以理解为当满足这种条件后才会被转发
+Filter(过滤器):
diff --git a/java/spring/spring-cloud/spring-cloud-hystrix.md b/java/spring/spring-cloud/spring-cloud-hystrix.md
index 271c5d7c..5ecdf2bf 100644
--- a/java/spring/spring-cloud/spring-cloud-hystrix.md
+++ b/java/spring/spring-cloud/spring-cloud-hystrix.md
@@ -1,10 +1,17 @@
-### 目录
-> 问题产生: 雪崩效应是一种因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程
+## 目录
+* [要解决什么问题](#问题产生)
+* [实现原理和基本特性](#实现原理和基本特性)
+* [SpringCloud Hystrix是如何实现隔离、熔断、降级的?]()
+* [熔断配置](#熔断配置)
-Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,服务间的调用不可避免会失败,比如超时、异常等。Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务的失败,避免级联故障。
+## 问题产生
+问题产生: 雪崩效应是一种因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程
+Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,服务间的调用不可避免会失败,比如超时、异常等。Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务的失败,避免级联故障。Hystrix 熔断机制是如何保证的?当失败的情况达到预定的阈值(5秒失败20次)或达到一定的失败率,会打开断路器,拒绝所有请求,直到服务恢复正常为止。
+
+## 实现原理和基本特性
Hystrix是Netflix开源的一个限流熔断的项目,主要有以下功能:
-1. 隔离(`线程池隔离`和`信号量隔离`): 限制调用分布式服务资源的使用,通过线程池隔离或者信号量隔离来保证当某一个服务请求响应非常慢的时候,不会影响其他服务。使用线程池隔离或信号量隔离的目的是将各依赖服务的访问交由独立的线程池来处理,会为每个依赖服务创建一个线程池,当自己的资源用完了,直接返回失败而不是占用别人的资源。
+1. 隔离(`线程池隔离`和`信号量隔离`): 限制调用分布式服务资源的使用,通过线程池隔离或者信号量隔离来保证当某一个服务请求响应异常的时候,不会影响其他服务。使用线程池隔离或信号量隔离的目的是将各依赖服务的访问交由独立的线程池来处理,会为每个依赖服务创建一个线程池,当自己的资源用完了,直接返回失败而不是占用别人的资源。
```
> 线程池方式:
优点:
@@ -14,9 +21,12 @@ Hystrix是Netflix开源的一个限流熔断的项目,主要有以下功能:
缺点:
线程池隔离增加了系统资源如CPU的开销,每隔命令涉及到系统调度和上下文切换
> 信号量方式:
- 使用一个原子计数器来记录当前有多少线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程池个数则拒绝该请求;若不超过则同行,计数器+1,请求返回成功后计数器-1。信号量模式下任务由当前线程直接处理,不涉及到线程切换,自然也就没有超时控制。TryableSemaphoreActual类中使用AtomicInteger(Unsafe.cas来保证)来计数
+ 使用一个原子计数器来记录当前有多少线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程池个数则拒绝该请求;若不超过则通行,计数器+1,请求返回成功后计数器-1。信号量模式下任务由当前线程直接处理,不涉及到线程切换,自然也就没有超时控制。TryableSemaphoreActual类中使用AtomicInteger(Unsafe.cas来保证)来计数
```
-2. 熔断:熔断机制是应对雪崩效应的一种微服务链路保护机制,当微服务链路的某个服务出错不可用或者响应时间太长,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到节点微服务调用响应正常后,恢复调用链路。在SpringCloud框架中,熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况,当失败的调用达到一定阈值,缺省值是5秒20次调用失败,就会开启熔断机制。熔断机制使用的是@HystrixCommand注解。
+
+ 
+2. 熔断:熔断机制是应对雪崩效应的一种微服务链路保护机制,当微服务链路的某个服务出错不可用或者响应时间太长,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阀值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据,保证服务链的完整。
+熔断有自动恢复机制,如:当熔断器启动后,每隔5秒,尝试将新的请求发送给服务提供者,如果服务可正常执行并返回结果,则熔断器半开,允许定量服务请求,如果调用都成功,则认为恢复了,关闭熔断器;如果仍旧调用失败,则继续返回托底数据,熔断器持续开启状态。熔断机制使用的是@HystrixCommand注解。
```
熔断器是位于线程池之前的组件,用户请求某一服务之后,Hystrix会先经过熔断器,此时如果熔断器是打开,则说明已经熔断,直接进行降级处理;如果熔断器是关闭的状态,则会继续将请求发到对应的线程池。
熔断器状态:
@@ -35,4 +45,50 @@ Hystrix是Netflix开源的一个限流熔断的项目,主要有以下功能:
4. 请求缓存:高并发场景下,通过请求缓存来减少服务器的压力。SpringClound Hystrix中有两种方式来实现,一种是重写HystrixCommand下的getCacheKey()方法,另外一种是使用注解。注解有三个
* @CacheResult: 注解在方法上,表示该方法开启缓存,默认情况下方法的所有参数都作为缓存key
* @CacheKey:注解在方法上,指定缓存的key
- * @CacheRemove:注解在方法上,该注解用来让请求的缓存失效,失效的缓存根据定义的key决定
\ No newline at end of file
+ * @CacheRemove:注解在方法上,该注解用来让请求的缓存失效,失效的缓存根据定义的key决定
+
+## 熔断配置
+* 全局配置
+```
+circuitBreaker.sleepWindowInMilliseconds:监控时间
+circuitBreaker.requestVolumeThreshold:失败次数
+circuitBreaker.errorThresholdPercentage:失败率
+```
+* 方法级别的配置
+```java
+
+@RestController
+public class HiController {
+
+ @GetMapping("/hystrix")
+ @HystrixCommand(fallbackMethod = "fallbackMethod", commandProperties = {
+ // 设置超时时间,默认1秒
+ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
+ // 监控时间,默认5秒
+ @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
+ // 失败次数,默认20次
+ @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
+ // 失败了,默认50%
+ @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
+ })
+ public String sayHi() {
+/* // 1. 容错,此处逻辑抛出异常,会走熔断方法
+ int i = 3 / 0;*/
+ // 2. 响应超时熔断
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+
+ }
+ return "hi";
+ }
+
+ /**
+ * 熔断方法
+ * @return String
+ */
+ private String fallbackMethod() {
+ return "熔断了";
+ }
+}
+```
diff --git a/java/spring/spring-cloud/spring-cloud-request-process.md b/java/spring/spring-cloud/spring-cloud-request-process.md
new file mode 100644
index 00000000..3fb3b284
--- /dev/null
+++ b/java/spring/spring-cloud/spring-cloud-request-process.md
@@ -0,0 +1,9 @@
+## 目录
+
+## SpringCloud调用接口过程
+Feign -> Hystrix -> Ribbon -> Http Client(apache http components OR Okhttp)
+1. `接口请求调用`:当调用被@FeignClient注解修饰的接口时,在框架内部,将请求转化成Feign的请求实例`feign.Request`,交由Feign框架处理
+2. `Feign`:转化请求,Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,封装了Http调用流程
+3. `Hystrix`:熔断处理机制,Feign的调用关系,会被Hystrix代理拦截,对每一个Feign调用请求,Hystrix都会将其包装成HystrixCommand,参与Hystrix的流控和熔断规则。如果请求判断需要熔断,则Hystrix直接熔断,抛出异常或者使用FallbackFactory返回熔断Fallback结果;如果通过,则将调用请求传递给Ribbon组件
+4. `Ribbon`:服务地址选择,当请求传递到Ribbon之后,Ribbon会根据自身维护的服务列表,根据服务的服务质量,如平均响应时间、Load等,结合特定的规则,从列表中挑选合适的服务实例,选择好机器之后,然后将机器实例的信息请求传递给HttpClient客户端,HttpClient客户端来执行真正的Http接口调用
+5. `Http Client`:Http客户端,真正执行Http调用根据上层Ribbon传递过来的请求,已经指定了服务地址,则HttpClient开始执行真正的Http请求
\ No newline at end of file
diff --git a/java/spring/spring-cloud/spring-cloud.md b/java/spring/spring-cloud/spring-cloud.md
index f6572297..29da22e6 100644
--- a/java/spring/spring-cloud/spring-cloud.md
+++ b/java/spring/spring-cloud/spring-cloud.md
@@ -1,3 +1,5 @@
+### 学习资源
+* 比较全和详细 https://blog.didispace.com/spring-cloud-learning/
### 微服务本身的复杂度带来的问题
* 分散在各个服务器上的日志如何处理?日志在format的时候一定要有规则,最好的方式是异常都要有对应的errorCode,方便后期过滤
* 如果业务出现了错误和异常,如何定位和处理?
@@ -8,8 +10,9 @@
```
文中讲了Doubbo, Spring, SpringBoot, SpringCloud的发展,及SpringBoot/Cloud产生的原因,SpringCloud各个组件
```
-* [Spring Cloud注册中心 - Eureka](spring-cloud-eureka.md)
-* [Spring Cloud熔断器 - Hystrix](spring-cloud-hystrix.md)
+* [SpringCloud请求处理流程](spring-cloud-request-process.md)
+* [SpringCloud注册中心 - Eureka](spring-cloud-eureka.md)
+* [SpringCloud熔断器 - Hystrix](spring-cloud-hystrix.md)
### 笔记
* SpringCloud是需要SpringBoot做基础的,因此在选择好SpringCloud的版本之后,要查看该版本依赖的是哪个SpringBoot版本,不能随便选择SpringBoot的版本,否则容易遇到意想不到的错误
diff --git a/plugins/database/img/mysql-sync.PNG b/plugins/database/img/mysql-sync.PNG
new file mode 100644
index 00000000..65d92b9d
Binary files /dev/null and b/plugins/database/img/mysql-sync.PNG differ
diff --git "a/plugins/database/mysql/mysql\347\264\242\345\274\225.md" "b/plugins/database/mysql/mysql\347\264\242\345\274\225.md"
index 671a0b76..2a309327 100644
--- "a/plugins/database/mysql/mysql\347\264\242\345\274\225.md"
+++ "b/plugins/database/mysql/mysql\347\264\242\345\274\225.md"
@@ -1,5 +1,6 @@
### 索引是什么
索引底层是`数据结构`,是一种`排好序`的数据结构,帮助MySQL高效获取数据。
+MYSQL用到了索引和执行时间没有必然关系。确定查询执行效率的是扫描行数和回表次数。
> 索引的优缺点
@@ -88,11 +89,16 @@ FULLTEXT(全文)索引,仅可用于MyISAM和InnoDB,针对较大的数据
#### 索引的创建
1. 尽量扩展索引,不要新建索引,因为索引过多会导致写入速度变慢。
2. 建有索引的列别参与计算,因为BTREE中存的都是表中的字段值,如果需要先计算再比较的话成本还是比较大的。
+3. CREATE INDEX t ON(columnA, columnB) 索引是以左侧列为基准,向右侧列查找,所以a的区分度最好够小
#### 索引的使用
1. 最左匹配原则
这个原则非常重要,我们建立索引的时候根据业务场景建立合适的索引。mysql索引匹配会从左向右一直匹配直到遇到范围查询才停止,比如id=1 and phone=15652635684 and time>1562565855,如果建立索引的顺序为(id,phone,time)则time用不到索引,但是如果顺序为(id,time,phone)则都可以用到。索引的最左前缀和和B+Tree中的“最左前缀原理”有关,举例来说就是如果设置了组合索引那么以下3中情况可以使用索引:col1,,,其它的列,比如,,col2,col3等等都是不能使用索引的。
2. =和in查询中可以乱序,不需要考虑最左匹配原则,mysql的查询优化器会进行自动优化
3. 使用短索引。索引字段尽量选择区分度高的字段,因为重复值越多,扫描的记录数就越多,比如性别类型为0和1,这种的重复就很高,不建议这种类型加索引,因为加了也没有多大提高。例如在一些政府项目中,对公民的身份证号码建立索引的时候,就可以采用这种hash索引,具体设计时可以将身份证倒序,然后使用前缀索引,匹配前9个字符效率就很高了。对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
+4. 优化方案
+* 增加多种不同规格索引提高索引选择性(5-6组)
+* 空间换时间,定时任务增加时报、日报等中间结果
+* 硬件调优:增大innodb_buffer_pool多利用内存,减少硬盘回表
### B树与B+树的区别
1. B树每个节点都存储数据,所有节点组成这棵树。B+树只有叶子节点存储数据(B+数中有两个头指针:一个指向根节点,另一个指向关键字最小的叶节点),叶子节点包含了这棵树的所有数据,所有的叶子结点使用链表相连,便于区间查找和遍历,所有非叶节点起到索引作用。
@@ -100,4 +106,6 @@ FULLTEXT(全文)索引,仅可用于MyISAM和InnoDB,针对较大的数据
3. B树中每个节点(非根节点)关键字个数的范围为[m/2(向上取整)-1,m-1](根节点为[1,m-1]),并且具有n个关键字的节点包含(n+1)棵子树。B+树中每个节点(非根节点)关键字个数的范围为[m/2(向上取整),m](根节点为[1,m]),具有n个关键字的节点包含(n)棵子树。
4. B+树中查找,无论查找是否成功,每次都是一条从根节点到叶节点的路径。
-
\ No newline at end of file
+
+
+### EXPLAIN
diff --git a/plugins/docker.md b/plugins/docker.md
index 40fd6933..d4bf68d5 100644
--- a/plugins/docker.md
+++ b/plugins/docker.md
@@ -1,9 +1,18 @@
-### 快速入门
-### 快速上手
+### 学习路线
+* Docker基础知识
+ * [CentOS下安装Docker](#CentOS下安装Docker)
+ * [基础概念](https://www.cnblogs.com/Can-daydayup/p/15559341.html)
+ * [常用命令](#常用命令)
+* Docker基础使用
+ * Docker发布SpringBoot项目 https://www.jianshu.com/p/397929dbc27d
+ * Docker+Jenkins+Git+SpringBoot构建自动化部署
### Docker是什么
Docker是一个开源的应用容器引擎,Docker其实就是可以打包程序和运行环境,把环境和程序一起发布的容器,当你需要发布程序时,你可以使用Docker将运行环境一起发布,其他人拿到你的程序后可以直接运行,避免出现一次编译,到处调试的尴尬局面~。Docker的出现主要是为了解决“在我的机器上是正常的,为什么到你的机器上就不正常了”的问题,但是随着Docker的进步,以及K8S等编排软件的流行,Docker的使用场景大大扩展,Docker已经成为高可用服务集群持续交付、继续集成以及云原生的关键技术。
+### Docker解决了什么问题
+Docker主要解决了在你的环境上运行没问题,但是在我的环境上运行有问题
+
#### CentOS下安装Docker
``` shell
# linux 3.10 内核,docker官方说至少3.8以上,建议3.10以上
@@ -21,4 +30,30 @@ Docker是一个开源的应用容器引擎,Docker其实就是可以打包程
[root@localhost ~]# docker version
```
-#### Docker中启动springboot项目
\ No newline at end of file
+#### 常用命令
+```shell
+启动 systemctl start docker
+停止 systemctl stop docker
+重启 systemctl restart docker
+查看状态 systemctl status docker
+开机启动 systemctl enable docker
+查docker信息 docker info
+列举在跑的容器 docker ps
+列举出所有容器 docker ps -a 包含历史
+查看镜像 docker image ls 或 docker images
+启动容器 docker start containerId
+停止容器 docker stop containerId
+重启容器 docker restart containerId 或者 docker restart 容器名字
+删除容器 docker rm containerId
+查看日志 docker logs [-f 跟踪日志输出 -t 显示时间戳 --since="2021-11-19"显示某个时间的所有日志 --tail=10列出最新的N条日志] containerId
+修改容器名字 docker rename 容器原来名 要改为的名字
+```
+
+#### Docker中启动springboot项目
+
+FROM java:8
+MAINTAINER luke.chen
+VOLUME /tmp
+ADD demo-0.0.1-SNAPSHOT.jar app.jar
+RUN bash -c 'touch /app.jar'
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
\ No newline at end of file
diff --git a/plugins/elasticsearch.md b/plugins/elasticsearch.md
index e257bd1e..10800b7c 100755
--- a/plugins/elasticsearch.md
+++ b/plugins/elasticsearch.md
@@ -1,38 +1,27 @@
### 学习笔记
* 快速上手 http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html
* ES基础部分
+ * ES可视化工具
+ * Kibana 学习网址 https://www.elastic.co/guide/en/kibana/current/index.html
+ * ES基础知识
+ * ES与MySQL对比
+ * Near Realtime (NRT)
+ * Node 与 Cluster
* ES的工作过程
## ES基础部分
-> 两个端口(9200/9300)
-* 节点客户端(Node Client)
- 节点客户端作为一个非数据节点加入到本地集群中。换句话说,它本身不保存任何数据,但是它知道数据在集群中的哪个节点中,并且可以把请求转发到正确的节点。
-* 传输客户端(Transport Client)
- 轻量级的传输客户端可以将请求发送到远程集群。它本身不加入集群,但是它可以将请求转发到集群中的一个节点上。
+* ES具有时序和冷热两个特征
+* ES数据的生命周期管理
-两个 Java 客户端都是通过 9300 端口并使用 Elasticsearch 的原生 传输 协议和集群交互。集群中的节点通过端口 9300 彼此通信。如果这个端口没有打开,节点将无法形成一个集群。
+### ES可视化工具
+Solr基于tomcat等服务器有自己的一套图形界面方便进行操作。同样的,ES也有图形化界面进行操作。
+* Kibana https://www.elastic.co/cn/downloads/kibana 注意Kibana的版本需要和ES保持相对一致
+* 编辑 config/kibana.yml,配置 `elasticsearch.hosts`
+* 启动 `bin\kibana.bat`
+* 首页 http://localhost:5601/,也可以进入Dev Tools来操作 http://localhost:5601/app/dev_tools#/console
-> Near Realtime (NRT)
-
-ES号称对外提供的是近实时的搜索服务,意思是数据从写入ES到可以被Searchable仅仅需要1秒钟,所以说基于ES执行的搜索和分析可以达到秒级。
-
-> Node 与 Cluster
-Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
-单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
-
-> Index
-
-index是一类拥有相似属性的document的集合,index名称必须是小写的字符。比如用户可以创建一个index,产品可以创建一个index,订单也可以创建一个index。
-
-> Type
-
-type作为index中的逻辑类别。比如用户这个index下,可以分用户的type,爱好的type
-
-> Document
-
-document就是ES中存储的一条数据,就像mysql中的一行记录一样。
-
-> ES与Mysql对比着看
+### ES基础知识
+#### ES与Mysql对比着看
| ES | MySQL |
| -- | -- |
@@ -40,10 +29,23 @@ document就是ES中存储的一条数据,就像mysql中的一行记录一样
| type | 表 |
| document | 行 |
+* index是一类拥有相似属性的document的集合,index名称必须是小写的字符。比如用户可以创建一个index,产品可以创建一个index,订单也可以创建一个index。
+* type作为index中的逻辑类别。比如用户这个index下,可以分用户的type,爱好的type
+* document就是ES中存储的一条数据,就像mysql中的一行记录一样
> Shards
-
-shard可以理解为ES中的最小工作单元,可以理解为一个lucene的实现,拥有完整的创建索引,处理请求的能力。shard分为primary shard和replicas shard,primary shard与其对应的replicas shard不能同时存在于一台server中,当primary shard宕机时,其对应的replicas shard可以继续响应用户的都请求。通过这种分片的机制,可以横向的成倍提升系统的吞吐量,比如一个shard可以处理200/s请求,此时再加一个服务器,就能支持400/s请求,天生分布式,高可用。此外,每个一document肯定存在一个primary shard和对应的replica shard中,绝对不会出现同一个document同时存在于多个primary shard中的情况。
+shard可以理解为ES中的最小工作单元,可以理解为一个lucene的实现,拥有完整的创建索引,处理请求的能力。shard分为primary shard和replicas shard,primary shard与其对应的replicas shard不能同时存在于一台server中,当primary shard宕机时,其对应的replicas shard可以继续响应用户的都请求。`通过这种分片的机制,可以横向的成倍提升系统的吞吐量,比如一个shard可以处理200/s请求,此时再加一个服务器,就能支持400/s请求,天生分布式,高可用。`此外,每个一document肯定存在一个primary shard和对应的replica shard中,绝对不会出现同一个document同时存在于多个primary shard中的情况。
问题的引入:如果让一个index自己存储1TB的数据,响应的速度就会下降,为了解决这个问题,ES提供了一种将用户的index进行subdivide(分割,再分割)的操作,就是将index分片,每一片都叫一个shards,进而实现了将整体庞大的数据分布在不同的服务器上存储。
+#### 两个端口(9200/9300)
+* 9200是ES节点与外部通讯使用的端口。它是http协议的RESTful接口(各种CRUD操作都是走的该端口,如查询:http://localhost:9200/user/_search
+* 9300是ES节点之间通讯使用的端口。它是tcp通讯端口,集群间和TCPclient都走的它。(java程序中使用ES时,在配置文件中要配置该端口)
+
+#### Near Realtime (NRT)
+
+ES号称对外提供的是近实时的搜索服务,意思是数据从写入ES到可以被Searchable仅仅需要1秒钟,所以说基于ES执行的搜索和分析可以达到秒级。
+
+#### Node 与 Cluster
+Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
+单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
### 启动
@@ -219,4 +221,11 @@ sudo sysctl -w vm.max_map_count=262144
```shell
[esuser@VM_0_12_centos root]$ curl -X PUT 'localhost:9200/accounts' -H 'content-Type:application/json' -d '{"mappings":{"person":{"properties":{"user":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"},"title":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"},"desc":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"}}}}}'
{"acknowledged":true,"shards_acknowledged":true,"index":"accounts"}
-```
\ No newline at end of file
+```
+
+### This version of Kibana (v7.14.0) is incompatible with the following Elasticsearch nodes in your cluster: v7.10.0
+Kibana版本与服务器的ES版本不匹配
+
+### 面试
+* ES为什么天生支持分布式
+
diff --git a/plugins/mq/mq.md b/plugins/mq/mq.md
index 6cb7ab35..63e034b6 100755
--- a/plugins/mq/mq.md
+++ b/plugins/mq/mq.md
@@ -1,19 +1,27 @@
### 学习笔记
- [什么时候使用MQ]()
-- []()
+- [常见问题]()
+ - [为什么要用消息队列?(消息队列的应用场景?)](#为什么要用消息队列-消息队列的应用场景)
+ - [各种消息队列产品的比较?](#各种消息队列产品的比较)
+ - [消息队列的优点和缺点?](#消息队列的优点和缺点)
* MQ总结
* 消息总线(Message Queue),是一种跨进程的通信机制,用于上下游传递消息。
- * 在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。
+ * 消息队列的本质是一种“新进先出”的数据结构
+ * 常用场景:解耦、异步、削峰
* 什么时候不使用MQ?
* 上游实时关注执行结果
* 什么时候使用MQ?
* 数据驱动的任务依赖。D依赖C,C依赖B,B依赖A,
* 上游不关心多下游执行结果
* 异步返回执行时间长
-* QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。目前在公司内部日常消息qps在60W左右,生产上承载将近4W+消息topic,消息的端到端延迟可以控制在10ms以内。
-* Apache RocketMQ is a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
-* ActivyMQ,RabbitMQ搭建集群需要自己配置HA
+* 常见产品
+ * ActivyMQ
+ * RabbitMQ
+ * QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。目前在公司内部日常消息qps在60W左右,生产上承载将近4W+消息topic,消息的端到端延迟可以控制在10ms以内。
+ * Apache RocketMQ is a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
+ * Kafka
+ * MQTT
### 消息队列的技术选型
消息队列及常见消息队列介绍 - 云+社区...
@@ -21,13 +29,52 @@ http://note.youdao.com/noteshare?id=7c550bb62a6597091e4533fbb6b920c1&sub=wcp1555
### 消息队列优势
消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构提升开发效率和系统稳定性。主要具有以下优势:
-* 解耦(解决不同重要程度、不同能力级别系统之间依赖导致一死全死)
-复杂的应用里会存在多个子系统, 比如在电商应用中有订单系统、库存系统、物流系统 支付系统等,这个时候如果各个子系统之间的耦合性太高,整体系统的可用性就会大幅降低,多个低错误率的子系统糅合在一起,得到的是一个高错误率的整体系统。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。当转变成基于消息队列的方式后,系统可用性就高多了,比如物流系统因为发生故障,需要几分钟的时间来修 ,在这几分钟的时间里,物流系统要处理的内容被缓存在消息队列里,用户的下单操作可以正常完成。当物流系统恢复后,补充处理存储在消息队列里的订单信息即可,终端用户感知不到物流系统发生过几分钟的故障。
-* 削峰(主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题)
-每年的双十一,淘宝的很多活动都在0点的时候开启,大部分应用系统流量会在瞬间猛增,这个时候如果没有缓冲机制,不可能承受住短时大流量的冲击。通过利用消息队列,把大量的请求暂存起来,分散到相对长的一段时间内处理,能大大提高系统的稳定性和用户体验。举个例子,如果订单系统每秒最多能处理1万次下单,这个处理能力应对正常时段的下单是绰绰有余的,正常时段我们下单后一秒内就能返回结果。双十一零点的时候,如果没有消息队列这种缓冲机制,为了保证系统稳定,只能在订单超过一万次后就不允许用户下单了;如果有消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这时有些用户可能在下单后十几秒才能收到下单成功的状态,但是也比不能下单的体验要好。使用消息队列进行流量消峰,很多时候不是因为能力不够,而是出于经济性的考量。比如有的业务系统,流量最高峰也不会超过一万QPS ,而平时只有一千左右的 QPS 这种情况下我们就可以用个普通性能的服务器(只支持一千左右的 QPS 就可以),然后加个消息队列作为高峰期的缓冲,无须花大笔资金部署能处理上万 QPS 的服务器。
-* 异步(当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统,应用间并发处理消息,相比串行处理,减少处理时间)
-比如用户注册的消息,需要被数据部门处理,也要被业务部门处理,如果串行调用,会导致用户注册的流程耗时,这时候采用消息异步处理,用户完成基本的注册后只需要写入消息,各个子系统订阅消费此消息。
-* 蓄流压测(线上有些链路不好压测,可以通过堆积一定量消息再放开来压测)
+*
+
+
+
除了上面列出的应用解棉、流量消峰、消息分发等功能外,消息队列还有保证最终一致性、方便动态扩容等功能。
+
+### 常见问题
+1. 为什么要用消息队列?(消息队列的应用场景?)
+2. 各种消息队列产品的比较?
+3. 消息队列的优点和缺点?
+4. 如何保证消息队列的高可用?
+5. 如何保证消息不丢失?
+6. 如何保证消息不被重复消费?
+7. 如何保证消息的顺序性?
+8. 基于MQ的分布式事务实现
+
+为什么要用消息队列-消息队列的应用场景
+----------------------------------
+
+消息队列的本质是一个先进先出的数据结构。它能在解耦、异步、削峰上提供很好的能力。
+1. 解耦(解决不同重要程度、不同能力级别系统之间依赖导致一死全死)
+复杂的应用里会存在多个子系统, 比如在电商应用中有订单系统、库存系统、物流系统 支付系统等,这个时候如果各个子系统之间的耦合性太高,整体系统的可用性就会大幅降低,多个低错误率的子系统糅合在一起,得到的是一个高错误率的整体系统。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。当转变成基于消息队列的方式后,系统可用性就高多了,比如物流系统因为发生故障,需要几分钟的时间来修 ,在这几分钟的时间里,物流系统要处理的内容被缓存在消息队列里,用户的下单操作可以正常完成。当物流系统恢复后,补充处理存储在消息队列里的订单信息即可,终端用户感知不到物流系统发生过几分钟的故障。
+2. 异步(当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统,应用间并发处理消息,相比串行处理,减少处理时间)
+比如用户注册的消息,需要被数据部门处理,也要被业务部门处理,如果串行调用,会导致用户注册的流程耗时,这时候采用消息异步处理,用户完成基本的注册后只需要写入消息,各个子系统订阅消费此消息。
+3. 削峰(主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题)
+在秒杀或团队抢购活动中,由于用户请求量较大,导致流量暴增,秒杀的应用在处理如此大量的访问流量后,下游的通知系统无法承载海量的调用量,甚至会导致下游系统崩溃等问题而发生漏通知的情况。为解决这些问题,可在应用和下游通知系统之间加入消息队列来做缓冲。
+4. 蓄流压测(线上有些链路不好压测,可以通过堆积一定量消息再放开来压测)
+
+各种消息队列产品的比较
+---------------------
+* ActiveMQ, 早期使用的比较多,没有经过大规模吞吐量场景的验证,社区也不是很活跃,现在确实用的不多,不推荐
+* RabbitMQ, 采用Erlang语言开发,导致很少有工程师去深入研究和掌控它,对公司而言,如果想进行二次开发,基本处于不可控的状态,但是RabbitMQ是开源的,且社区很活跃,相对来说有比较稳定的支持。如果对性能要求不是特别高,且追求稳定,推荐使用。另外RabbitMQ严格遵循了AMQP协议,功能十分丰富,且提供rabbitmqAdmin可视化管理后端
+* RocketMQ,采用Java开发,诞生于电商业务,天生支持分布式,且经过阿里双十一业务场景考验,稳定性和性能均不错,且可以考虑后期二次开发,推荐使用,也提供后端可是化界面。社区活跃度中等
+* Kafka, 采用Scala开发,诞生于Hadoop大数据业务,是大数据领域、日志采集的标配,社区活跃度高,推荐使用。
+
+https://blog.csdn.net/zollty/article/details/53958656
+
+
+消息队列的优点和缺点
+-------------------
+* 优点:解耦、异步、削峰
+* 缺点:
+ * 系统可用性降低: 系统引入的外部越多,系统的稳定性就越差。一旦MQ宕机,就会对业务产生影响。 -> `如何保证MQ的高可用?`。之前我们发完文章,直接RPC调用用户模块,获取好友列表,然后挨个增加他们的未读文章列表,为了解耦,我们引入MQ,如果MQ挂了,那他们的未读文章就没有我发的这篇文章。
+ * 系统复杂度提高: MQ的引入大大增加了系统的复杂度,以前系统是同步的远程调用,现在是通过MQ进行异步调用。也就是在内容服务和用户服务之间加了一层MQ,那就必须考虑`消息丢失了怎么办?`、`重复消息怎么处理?`、`如何保证消息顺序性消费?`这些情况怎么办?
+ * 一致性问题:A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B、C系统处理成功,D系统处理失败,那这个数据就不一致了。比如商城下的订单,要异步同步到门店工单,和OMS订单,那如果门店成功了,OMS失败了,数据就不一致。
+
+

\ No newline at end of file
diff --git a/plugins/mybatis.md b/plugins/mybatis.md
index 56d83fd7..0f7fe793 100755
--- a/plugins/mybatis.md
+++ b/plugins/mybatis.md
@@ -10,6 +10,7 @@
* mybatis源码的中文注释以及mybatis的使用和源码解析 https://github.com/homejim/mybatis-cn
* 【Java进阶】实现自己的ORM框架 https://blog.csdn.net/liyazhou0215/article/details/77431561
* MyBatis Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生
+* [MyBatis中${}和#{}有什么区别?MyBatis是如何防止SQL注入的?](#MyBatis防止SQL注入)
### 学习笔记
* 【关注点】针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。【SQL优化方面】Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。而Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。但Hibernate具有自己的日志统计。Mybatis本身不带日志统计,使用Log4j进行日志记录。【扩展性】Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。MyBatis项目中所有的SQL语句都是依赖所用的数据库的,所以不同数据库类型的支持不好。【优势对比】MyBatis可以进行更为细致的SQL优化,可以减少查询字段。MyBatis容易掌握,而Hibernate门槛较高。
@@ -114,3 +115,18 @@
```
* 处理and
当遇到sql中这样写WHERE ( au_state = '1' OR au_state = '2' ) ,mybatis中queryWrapper.and(Wrapper -> Wrapper.eq(PcmArticle::getAuState, "1").or().eq(PcmArticle::getAuState, "2"));
+* 注解
+ * @TableName:数据库表相关
+ * @TableId:表主键标识
+ * @TableField:表字段标识
+ * @TableField(value= "", exist = false):表示该属性不为数据库表字段,但又是必须使用的。
+ * @TableField(value= "", exist = true):表示该属性为数据库表字段。
+ * @TableLogic:表字段逻辑处理注解(逻辑删除)
+
+### MyBatis防止SQL注入
+SQL注入的根本原因就是SQL的动态编译。能产生SQL注入的肯定是字符串,否则会报类型错误。在MyBatis中${}会产生SQL注入,#{}不会产生SQL注入。
+* ${}匹配的是真实传递的值,传递过后,会与sql语句进行字符串拼接,不能预防SQL注入。比如我传递的值是 1' OR 1=1
+* #{}匹配的是一个占位符,相当于JDBC中的?,会对一些敏感的字符进行过滤。比如输入带了单引号,会在单引号前加\
+1. 那么#{}底层是如何防止SQL注入的?只看字符串
+#{}底层使用的是PreparedStatement#setString方法能够保证传参作为一个字符串而不会被拆分(所以不会产生字符串拼接),setString对每个字符都做了检查,比如单引号前会加个\来转义
+2. ${}的使用场景动态传入表名或列名
\ No newline at end of file
diff --git a/plugins/mysql.md b/plugins/mysql.md
index 52147acf..58e32a78 100644
--- a/plugins/mysql.md
+++ b/plugins/mysql.md
@@ -8,6 +8,7 @@
- [MySQL日志](#mysql日志)
- [索引](#索引)
- [表中有多个索引,优化器怎么决定使用哪个索引]()
+ - [MySQL in会不会走索引](#in会不会走索引)
- [SQL查询优化](#sql查询优化)
- [order by是怎么工作的](#order-by是怎么工作的)
- [数据库监控](#数据库监控)
@@ -22,12 +23,14 @@
- [性能查询](#性能查询)
- [数据页的空间利用率](#数据页的空间利用率)
- [事务的传播机制]()
-
+ - [Sharding-JDBC](#Sharding-JDBC)
- [百问](#百问)
- [实战](#实战)
- [设计](#设计)
- [使用](#使用)
- [count(*)的实现](#count)
+ - [MySQL给用户授权](#MySQL给用户授权)
+ - [给用户赋予某个数据库某种权限]
### 番外篇
* 数据库的扩展没有web服务器那样容易
@@ -190,6 +193,13 @@
这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关;
```
* 等值查询,范围查找
+
+#### in会不会走索引
+1. 当in()种的数据很大时,不走索引
+2. 当查询的列是char类型没有加引号,mysql优化器会自动给填充引号,同时也会导致索引失效
+3. 当in()中存在子查询、格式化函数等同样也会使索引失效!
+
+
#### SQL查询优化
* 如何获取
* 终端用户反馈存在性能的SQL
@@ -379,6 +389,10 @@ alter table A engine=InnoDB 命令来重建表
2. SQL语句没写好
3. MySQL选错了索引
+## Sharding-JDBC
+https://blog.csdn.net/taoerchun/article/details/104247399
+几个核心的步骤是一样的:SQL解析,重写,路由,执行,结果归并。
+
## 百问
1. MYSQL 索引长度的限制
```
@@ -410,16 +424,16 @@ from SUser;
当然,使用前缀索引很可能会损失区分度,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~L7 中,找出不小于 L * 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。 `使用前缀索引不能避免要回表一次来获取完整数据`,因为系统并不确定索引是不是被截断了的。
* 身份证。公民的身份证是18位,前6位是地址,精确到县,接着6位是年月,如果使用前缀索引,至少得去前12位,才能满足区分度,但是索引选的越长,占用的磁盘空间越大,相同的数据页能放下的索引值就越少,搜索的效率越低。可以使用倒序存储身份证,取身份证的后6位做前缀索引;或者新增一个字段,对身份证及进行hash(不支持范围查找)
### 使用
-#### INSERT
-#### DELETE
+> INSERT
+> DELETE
1. 根据业务来决定是物理删除还是软删除
-#### UPDATE
-#### SELECT
+> UPDATE
+> SELECT
`通用原则:在保证逻辑正确的前提下,尽量减少扫描的数据量,是数据库系统设计的通用原则之一。`
1. 避免全表扫描。随着数据量的递增,全表扫描带来的性能消耗越来越大。可以通过其他业务模块来辅助标记,比如我曾在Timing业务中写一个晚上9点给sp发群总结的job,当时就是要过滤出来所有sp创建的并且未解散的群,查询条件无法命中现有索引,只能全表扫描,随着业务的发展,数据量越来越大,单次查询的时间已经到了10几秒,系统层面已经无法接受,只能曲线救国。于是在sp创建群和解散群的时候,往redis中存放群的id,这样就把全表扫描变成了range获取了,命中primary key
2. 只查出来需要的字段,不要使用*。
-### count
+> count
你首先要明确的是,在不同的 MySQL 引擎中,count(*) 有不同的实现方式。
* MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高;
* 而 InnoDB 引擎就麻烦了,它执行 count(*) 的时候,会选择合适的索引,需要把数据一行一行地从引擎里面读出来,然后累积计数。【当然InnoDB也没那么笨,我们知道InnoDB是索引组织表,主键索引的叶子节点是数据,而普通索引的叶子节点是主键,因此对于count(*)这样的操作,查哪个索引结果都是一样的,MySQL会找到最小的那棵树来遍历。】
@@ -446,6 +460,37 @@ InnoDB 只给必要的值;
* MySQL SQL底层索引的实现原理
-* utf8和utf8mb4区别
+> utf8和utf8mb4区别
MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。
-那上面说了既然utf8能够存下大部分中文汉字,那为什么还要使用utf8mb4呢? 原来mysql支持的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等(utf8的缺点)。
\ No newline at end of file
+那上面说了既然utf8能够存下大部分中文汉字,那为什么还要使用utf8mb4呢? 原来mysql支持的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等(utf8的缺点)。
+
+* MySQL单表可以存储10亿级数据,只是这时候性能比较差,业界公认MySQL单表容量在1KW以下是最佳状态,因为这时它的BTREE索引树高在3~5之间。
+
+> MySQL中我使用了索引,为什么SQL还那么慢呢?
+1. MySQL使用了索引,但是有可能扫描了整个索引树,而整个索引树数据量又很大 - 全索引扫描
+2. MySQL使用了索引,但是条件语句中有条件需要回表,那么执行的过程时扫描的行数 + 回表的次数
+ * Mysql 5.6引入了index condition pushdown的优化,意思是对于where条件中有使用到联合索引的地方,会先将判断条件下移到索引中,判断是否需要回表扫描,如果需要,则再回表扫描。比如select * from user where name like '张%' and age =8,在扫描name时,同时判断age
+ * Mysql 5.7引入的虚拟列
+PS:
+1. 使用了索引是指使用了索引的快速搜索能力,并且有效的减少了扫描行数。索引的过滤性要足够好,区分度足够高。
+2. Mysql默认的long_query_time时10s,一个SQL不论有没有使用索引,如果执行时间超过了这个配置的long_query_time都会被记录到慢查询日志中
+
+> 写了一个数据同步,大概实现是这样的:首先从A表筛选出待同步的数据,每次取50条,然后一条条同步(中间数据组装逻辑过多,未做批量同步),不论这条数据是否同步完,都把A表的原数据打一个sync=1的标识(标记为已同步),线下N个环境跑起来都没问题,上了线,数据总是同步多了几条,重新同步后还是多几条(重新同步后多出来的数据条数不固定) - 根本原因是线上配置的是主从数据库
+
+
+
+> MySQL查询重复的数据
+```sql
+SELECT columnA FROM tableA
+WHERE condation
+GROUP BY columnA HAVING COUNT(columnA) > 1
+```
+
+https://mp.weixin.qq.com/s/SNRvdmyS57oWS_CyYKVvSA
+
+#### MySQL给用户授权
+1. 给某个用户赋予某个数据库的某种权限
+* 首先创建用户 `create user user1 identified by '123';`
+* 赋予`user1`某些权限 `grant select,insert,update on db1.* to user1@"%" identified by "123";`
+* 刷新 `flush privileges;`
+* 切换到user1查看数据库
\ No newline at end of file
diff --git a/plugins/redis.md b/plugins/redis.md
index fe532839..79d01f77 100644
--- a/plugins/redis.md
+++ b/plugins/redis.md
@@ -1,9 +1,19 @@
# 目录
* [学习计划](#学习计划)
+ * [Redis安装及基础知识]()
+ * [Redis底层数据结构]()
+ * [Redis数据类型]()
+ * [Redis高级特性]()
+ * [Redis场景问题]()
* [学习笔记](#学习笔记)
* [百问](#百问)
- * redis在什么情况下会变慢?
+ * Redis番外篇
+ * Redis在什么情况下会变慢?
* 单线程的redis,如何知道要运行定时任务?
+ * Redis Server启动源码分析
+ * Redis get/set源码分析
+ * 面试题 https://www.sohu.com/a/486868862_121124376
+* [实战]()
### 学习计划
* [Redis的介绍、优缺点、使用场景](#Redis的介绍、优缺点、使用场景)
@@ -131,7 +141,7 @@
auth "password" 验证密码
redis-cli --raw可以避免中文乱码
exit 退出
- select index 切换到指定的数据库
+ select index[0-15] 切换到指定的数据库
keys * 显示所有key,如果键值对多不建议使用,keys会遍历所有key,可以在从节点使用;时间复杂度O(N)
dbsize 算出所有的key的数量,只是数量;时间复杂度O(1)
exists key key是否存在,存在返回1,不存在返回0;时间复杂度O(1)
@@ -146,6 +156,7 @@
set key value插入值
sadd myset 1 2 3 4 插入set
get key获取值
+ object encoding key获取value对应的编码
del key删除key
cat redis.conf | grep -v "#" | grep -v "^$" 查看配置文件,去除所有的#,去除所有的空格
setnx key value #key不存在,才设置
@@ -327,7 +338,7 @@ for(0->100) {
| List | 双向链表 | 增删快,提供了操作某一元段元素的API | 1. 最新消息,按照时间线显示
2. 消息队列 |
| Set | 哈希表实现,元素不重复 | 添加/删除/修改的复杂度都是O(1),为集合提供求交集/并集/差集的操作 | 1. 打label/tag,如文章
2. 查找共同好友
3. 抽奖系统
|
| Zset | 将Set中的元素增加一个double类型的权重score,按照score排序 | 数据插入集合就好序了 | 排行榜 |
-| Hyperloglog | 本质是string | 极小空间完成独立数据量统计 | 统计基数,不完全正确 |
+| Hyperloglog | 本质是string | 极小空间完成独立数据量统计 | 统计基数,不完全正确
统计注册IP数,统计页面UV数,统计在线用户数,统计每日搜索词条数|
| GEO | 数据类型是zset | 存储地理位置信息,并提供计算距离等操作 | 微信摇一摇查看附近好友 |
| Bitmap | 位图 | 数据量很大的时候节省存储内存,数据量小了不节省 | 1. 设置用户的状态
2. BitMap解决海量数据寻找重复、判断个别元素是否在 |
@@ -417,7 +428,7 @@ struct sdshdr {
* 复制的配置:
* 使用slaeof命令,在从redis中执行slave masterip:port使其成为master的从服务器,就能从master拉取数据了;执行slaveof no one清除掉不成为从节点,但是数据不清楚;
* 修改配置, slaveof ip port / slave-read-only yes(从节点只做都操作);配置要更改的话,要重启,所以选择的时候谨慎
-* 全量复制
+* 全量复制f
* run_id(使用info server可以看到run_id),重启之后run_id就没有了,当从服务器去复制主服务器,主服务器run_id会在从服务器上做一个标识,当从服务器发现主服务器的run_id发生了变化,说明主服务器发生了变化(重启或者什么的),那么从服务器就要把主服务器的数据都同步过来
* 偏移量:部分复制中的一个依据,后面说
@@ -611,7 +622,7 @@ struct sdshdr {
* 另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。【当分区较多或发生变化的时候需要处理一些额外的情况】
### 其他
-* Redis设置port为6379的原因
+* Redis设置port为6379的原因。一个对外port,一个对内port,rabbitmq也有这种设置
### I/O多路复用技术(multiplexing)
关于I/O多路复用(又被称为“事件驱动”),首先要理解的是,操作系统为你提供了一个功能,当你的某个socket可读或者可写的时候,它可以给你一个通知。这样当配合非阻塞的socket使用时,只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。写操作类似。操作系统的这个功能通过select/poll/epoll/kqueue之类的系统调用函数来使用,这些函数都可以同时监视多个描述符的读写就绪状况,这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程。
@@ -661,7 +672,7 @@ redisTemplate.opsForValue().set(String.valueOf(goodsId), null, 60, TimeUnit.SECO
解决方案:
* 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
-* 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
+* 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
* 设置热点数据永远不过期。
* redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
@@ -692,13 +703,12 @@ Redis 最开始的设计可能就是想做一个缓存来用。但是分布式
> Redis单线程为什么这么快?
-快的原因有主要三点:
+快的原因有主要五点:
1. 纯内存操作:Redis是基于内存的,所有的命令都在内存中完成,内存的响应速度相比硬盘是非常快的,内存的响应时间大约是100纳秒,Redis官方给出的OPS是10W
-2. 编程语言:Redis采用C语言编写,不依赖第三方类库,执行速度快
-3. 线程模型:Redis使用单线程操作,避免了线程的切换和竞态消耗
+2. 编程语言:Redis采用C语言编写,不依赖第三方类库,执行速度快。其依赖的第三方libs在deps文件夹中
+3. 线程模型:Redis使用单线程操作,避免了线程的切换和竞态消耗。由于对命令的读写是单线程,所以就存在一个顺序读写的问题,顺序读写比随机读写的速度快。
4. 采用了非阻塞IO多路复用机制:多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了事件,不在I/O上浪费过多的时间
-5. 由于是单线程,所以就存在一个顺序读写的问题,顺序读写比随机读写的速度快。
-6. Redis的数据结构是经过专门的研究和设计的,所以操作起来简单且快。
+5. Redis的数据结构是经过专门的研究和设计的,操作起来简单且快。
最后,再说一点,Redis 是单进程和单线程的设计,并不是说它不能多进程多线程。比如备份时会 fork 一个新进程来操作;再比如基于 COW 原理的 RDB 操作就是多线程的。
> Redis如何处理过期数据?Slave不能处理数据,那数据过期了怎么办?
@@ -860,7 +870,7 @@ struct sdshdr {
```
### 实战
-- SpringBoot RedisTemplate执行lua
+> SpringBoot RedisTemplate执行lua
```java
String luaScript = "redis.call(\"sadd\", KEYS[1], ARGV[1]);\n" +
"local score = redis.call(\"scard\", KEYS[1]);\n" +
@@ -879,7 +889,7 @@ redisTemplate.executePipelined(new RedisCallback