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) -[![Stargazers over time](https://starchart.cc/zhonghuasheng/Tutorial.svg)](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在执行时使用内部的线程池完成操作。 + +![](img/executor-service.png) + +#### 创建线程池常用的类 +* 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注解。 + + ![](../image/hystrix.png) +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+树中查找,无论查找是否成功,每次都是一条从根节点到叶节点的路径。 -![](../img/btree-example.png)![](../img/b+tree-example.png) \ No newline at end of file +![](../img/btree-example.png)![](../img/b+tree-example.png) + +### 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失败了,数据就不一致。 + + ![MQ详细对比](img/mq-compare.png) \ 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个环境跑起来都没问题,上了线,数据总是同步多了几条,重新同步后还是多几条(重新同步后多出来的数据条数不固定) - 根本原因是线上配置的是主从数据库 + +![](database/img/mysql-sync.PNG) + +> 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() { }); ``` -- ERR 'EVAL' command keys must in same slot +> ERR 'EVAL' command keys must in same slot 在Redis集群版实例中,事务、脚本等命令要求所有的key必须在同一个slot中,如果不在同一个slot中将返回以下错误信息(command keys must in same slot) 在集群下,它会将数据自动分布到不同的节点(虚拟的16384个slot) 它数据的路由分发,是通过计算key,所以只要key一样,则一定会被分到同一个slot @@ -889,4 +899,77 @@ beta: hash: fnv1a_64 hash_tag: "{}" 解决方案,在这个Lua script中所有的key(key和hashKey)都是用同一个前缀,并且用花括号括起来,就能保证命中同一个slot,但是问题来了,这会导致该lua script中的所有key都集中到了一个slot。 -redis集群版的分布式是会根据KEY进行hash取模然后打到不同的slot,这种思想是典型的分而治之。分治,分流,降级。 \ No newline at end of file +redis集群版的分布式是会根据KEY进行hash取模然后打到不同的slot,这种思想是典型的分而治之。分治,分流,降级。 + +> Redis遍历key +1. 使用keys pattern来全量遍历key,如果库中key过多会造成阻塞 + * 哈希 hgetall + * 集合 smembers + * 有序集合 zrange +2. 使用scan cursor [match pattern] [count number]来渐进式遍历 + * cursor是必须参数,实际cursor是一个游标,第一次遍历从0开设,每次scan都会返回当前游标和数据集合,直到游标为0,表示遍历结束 + * match pattern 可选,在遍历中匹配想要的key + * count number 可选,表示每次要遍历的键个数,默认10; number不限制返回的条数,而是限制遍历的个数 + * eg: scan 0 match a* count 100 + * 哈希 hscan + * 集合sscan + * 有序集合zscan +* Java中具体伪代码可百度之,关注具体类 ScanResult result = redis.scan(key, cursor, pattern); result.getStringCursor()用于获取游标 +* `问题`: 如果在scan的过程中有键的变化,那么会产生新增的键可能没有遍历到,遍历出重复的键等情况,代码逻辑要过滤 + +> 记录慢查询 +Redis 原生提供慢查询统计功能,执行 slowlog get {n} 命令可以获取最近的 n 条慢查询命令,默认对于执行超过10毫秒(可配置)的命令都会记录到一个定长队列中,线上实例建议设置为1毫秒便于及时发现毫秒级以上的命令。 +```sh +# 超过 slowlog-log-slower-than 阈值的命令都会被记录到慢查询队列中 +# 队列最大长度为 slowlog-max-len +slowlog-log-slower-than 10000 +slowlog-max-len 128 +``` + +> Redis查找大对象 +Redis 本身提供发现大对象的工具,对应命令:redis-cli-h {ip} -p {port} bigkeys。这条命令会使用 scan 从指定的 Redis DB 中持续采样,实时输出当时得到的 value 占用空间最大的 key 值,并在最后给出各种数据结构的 biggest key 的总结报告。 +```sh +> redis-cli -h host -p 12345 --bigkeys +``` + +> Redis查看上一次fork耗时 +```sh +> redis-cli -c -p 7000 info | grep -w latest_fork_usec +latest_fork_usec:315 +``` + +> Redis SkidMap病毒 +利用Redis未授权访问漏洞,来执行恶意脚本,本质上是Redis安全没做好 + + + +https://www.imcsummit.org/2019/us/sites/2019.us/files/slides/10-Ways-to-Scale-with-Redis-IMCSummit-2019.pdf +https://blog.csdn.net/weixin_33885253/article/details/88034739 +https://www.pauladamsmith.com/blog/2011/03/redis_get_set.html +https://www.processon.com/view/60335eea6376896cd6ea50e3?fromnew=1 +当 Redis 发生高延迟时,到底发生了什么 +http://remcarpediem.net/article/ef4e619/ +Redis 命令执行过程(上) +https://www.cnblogs.com/remcarpediem/p/12024468.html +https://www.cnblogs.com/remcarpediem/p/12038377.html +https://blog.csdn.net/weixin_33885253/article/details/88034739 +Redis 6.0 多线程性能测试结果及分析 +https://www.cnblogs.com/wy123/p/14180499.html +Redis内部数据结构详解(1)——dict +https://blog.csdn.net/twt936457991/article/details/89816468 +https://github.com/huangz1990/redis-3.0-annotated +https://www.processon.com/view/610e2894e0b34d0b1a438ede?fromnew=1 +https://www.processon.com/view/614c87fe7d9c08065b403dc5?fromnew=1 +https://www.processon.com/view/60501f32f346fb348a92f954?fromnew=1 +https://www.processon.com/view/60f21d6ee0b34d0e1b69ad83?fromnew=1 +https://www.processon.com/view/60335eea6376896cd6ea50e3?fromnew=1 +https://www.processon.com/view/5ea7a58807912948b0e1aa2f?fromnew=1 +https://www.processon.com/view/5f7c98677d9c0806f2659737?fromnew=1 +https://blog.csdn.net/user2025/article/details/109178738 + +### Redis底层浅析 +* Redis整体结构 +* Redis服务启动都发生了什么 +* Redis get命令发生了什么 +* Redis set命令发生了什么 +* Redis-cli是如何和redis-server建立连接以及通信的 \ No newline at end of file diff --git "a/system/architecture/\345\211\215\347\253\257\346\200\247\350\203\275\346\217\220\345\215\207\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/plugins/redis/redis-rdb-aof\345\210\206\346\236\220.md" similarity index 100% rename from "system/architecture/\345\211\215\347\253\257\346\200\247\350\203\275\346\217\220\345\215\207\350\247\243\345\206\263\346\226\271\346\241\210.md" rename to "plugins/redis/redis-rdb-aof\345\210\206\346\236\220.md" diff --git "a/plugins/redis/redis\345\221\275\344\273\244\346\211\247\350\241\214\345\210\206\346\236\220.md" "b/plugins/redis/redis\345\221\275\344\273\244\346\211\247\350\241\214\345\210\206\346\236\220.md" new file mode 100644 index 00000000..e69de29b diff --git "a/plugins/redis/redis\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213\345\217\212\344\275\277\347\224\250\345\234\272\346\231\257.md" "b/plugins/redis/redis\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213\345\217\212\344\275\277\347\224\250\345\234\272\346\231\257.md" new file mode 100644 index 00000000..b3005b10 --- /dev/null +++ "b/plugins/redis/redis\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213\345\217\212\344\275\277\347\224\250\345\234\272\346\231\257.md" @@ -0,0 +1,38 @@ +## Redis基础数据类型 +Redis包含`String`、`Hash`、`List`、`Set`、`ZSet`、`Stream`、`Bitmap`、`HLL`、`GEO`、`Bitfield`这些数据类型 + +String +------ +* 简单动态字符串(simple dynamic string, SDS)Redis没有直接使用C语言的传统字符串表示,而是自己构建了一种名为简单动态字符串(Simple Dynamic String, SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。 +* String底层对应的数据结构是SDS,有好几个结构体,大致在往db插入值前,会判断value的长度,然后决定使用哪个结构体来存值 + ```shell + struct __attribute__ ((__packed__)) sdshdr8 { + uint8_t len; /* used */ hdr5已经不足以描述数据长度了,定义一个8bit的 len来描述 2^5 - 2^8 - 1的字符创长度 + uint8_t alloc; /* excluding the header and null terminator */ + /**已分配的总长度, 用于对已存在的字符串追加的场景,3.2之前叫free + * 比如 set a luke01, len=6, 那最开始分给我的时候是字符创长度的2倍,12,现在我要追加 xx,变为luke01xx,就不用重新分配空间,而是在 + * 现有的字符数组追加元素即可,通过空间预分配,极大程度的解决了内存分配产生的问题(碎片等) + * 作者为啥这么做,据说是因为大家使用redis极大部分存的值都是字符串,但是我觉得append命令我用的不多 + * **/ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; //SDS遵循C字符串以空字符结尾的惯例 + }; + struct __attribute__ ((__packed__)) sdshdr64 { + uint64_t len; /* used */ + uint64_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; + }; + ``` +* SDS相比C语言字符创具有以下优点: + 1. 获取字符串长度的复杂度为O(1)。C语言并不记录自身的长度信息,所以为了获取一个C字符串的长度,要遍历整个字符数组,但是SDS会记录字符长度,可以通过len直接在内存中拿取到整块的数据。 + 2. 杜绝缓存溢出。除了获取字符串长度的复杂度高之外,C字符串不记录自身长度带来的另外一个问题是容易造成缓冲区溢出(buffer overflow)。SDS在赋值前会判断剩余的长度,如果长度不够了,就会重新分配空间。 + 3. 减少修改字符串长度时所需的内存重分配的次数。C语言中每次值的修改都会触发内存重新分配,SDS通过空间预分配和记录剩余字节数量free来减少字符串append(追加)造成的内存重分配,同时通过惰性删除来减少字符串缩短的操作造成的内存重分配。 + * 通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略 + * 空间预分配: 空间预分配用于优化SDS的字符串增长操作:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所必须的空间,还会为SDS分配额外的未使用空间。通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数。 + * 惰性空间释放:惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是可以通过alloc-len来计算未使用的长度,并等待将来使用。3.2之前有个free属性记录剩余长度,3.2之后改为alloc记录总长度。 + 4. 二进制安全。SDS以二进制的方式处理buf数组中的数据,写的时候是什么样,读的时候也是什么样。即使写入\0也能正常读出来 + 5. 兼容部分C字符串函数。遵循C字符串以空字符结尾的惯例 +* `String`使用场景 + * Redis String可以包含任何数据,最大能存储512M + * 可以用在计数器/分布式锁/session diff --git "a/plugins/redis/redis\346\225\264\344\275\223\346\236\266\346\236\204\345\210\206\346\236\220.md" "b/plugins/redis/redis\346\225\264\344\275\223\346\236\266\346\236\204\345\210\206\346\236\220.md" new file mode 100644 index 00000000..8712065d --- /dev/null +++ "b/plugins/redis/redis\346\225\264\344\275\223\346\236\266\346\236\204\345\210\206\346\236\220.md" @@ -0,0 +1,26 @@ +## Redis整体架构分析 + +如下是我学习redis及阅读源码,总结的redis整体架构图 + +![](http://r3rutcmq2.hd-bkt.clouddn.com//github01-redis-architecture.PNG) + +我认为Redis架构包含四个模块 `网络模型`、`线程模型`、`持久化模型`、`数据结构`。其中`网络模型`使用的是IO多路复用;`线程模型`是对读写采用单线程,其他地方有使用多线程,Redis6.0提出的多线程是对网络的读写采用多线程的方式,核心的cmd的操作还是单线程;`持久化模型`指的是RDB和AOF;`数据结构`需要强调一下,Redis的数据结构是经过精心设计的。 + +## Redis源码目录文件分析 + +Redis的作者在readme中进行了较为详细的介绍,全文2W多字,系统的描述了Redis的设计理念和关键的源码文件。 +* `deps` 依赖的第三方库 +* `src` 源码文件 + * `db.c` 读取插入值逻辑 + * `server.c` redis服务器启动,初始化配置 + * `server.h` 结构体 redisServer,redisDb,client,redisObject 记录了redis服务器,数据库,客户端的信息 + * `dict.h` 字典结构体dict,还有其中的dictht, dictEntry + * `t_*.h` t_开头的文件是redis底层的数据结构 + * `networking.c` 是处理网络I/O的 + * `object.c` 封装了对redisObject的操作 + +## Redis数据类型及底层数据结构 + +![](http://r3rutcmq2.hd-bkt.clouddn.com//github02-redis-data-type.PNG) + +![](http://r3rutcmq2.hd-bkt.clouddn.com/01-redis-data-structure.PNG) \ No newline at end of file diff --git "a/plugins/redis/redis\346\234\215\345\212\241\345\231\250\345\220\257\345\212\250.md" "b/plugins/redis/redis\346\234\215\345\212\241\345\231\250\345\220\257\345\212\250.md" new file mode 100644 index 00000000..e69de29b diff --git "a/plugins/redis/redis\347\275\221\347\273\234\346\250\241\345\236\213\345\210\206\346\236\220.md" "b/plugins/redis/redis\347\275\221\347\273\234\346\250\241\345\236\213\345\210\206\346\236\220.md" new file mode 100644 index 00000000..e69de29b diff --git a/system/algorithm/algorithm.md b/system/algorithm/algorithm.md new file mode 100644 index 00000000..7474f308 --- /dev/null +++ b/system/algorithm/algorithm.md @@ -0,0 +1,4 @@ +### 学习笔记 +* [数据结构](数据结构.md) +* [设计模式](设计模式.md) +* [倒排索引](倒排索引.md) \ No newline at end of file diff --git "a/system/architecture/SQL\346\263\250\345\205\245.md" "b/system/architecture/SQL\346\263\250\345\205\245.md" deleted file mode 100644 index 1e6ae53a..00000000 --- "a/system/architecture/SQL\346\263\250\345\205\245.md" +++ /dev/null @@ -1,11 +0,0 @@ -### 学习思路 -* 什么是SQL注入 -* Mybatis是如何防止SQL注入的 -* 自定义SQL注入框架 or 方案 - -### 什么是SQL注入 -SQL注入,是一种常见的攻击方式。攻击者在界面的表单信息或URL上输入一些奇怪的SQL片段(例如“or ‘1’=’1’”这样的语句),有可能入侵参数检验不足的应用程序。所以,在我们的应用中需要做一些工作,来防备这样的攻击方式。在一些安全性要求很高的应用中(比如银行软件),经常使用将SQL语句全部替换为存储过程这样的方式,来防止SQL注入。这当然是一种很安全的方式,但我们平时开发中,可能不需要这种死板的方式。 - -SQL注入并不是一个在SQL内不可解决的问题,这种攻击方式的存在也不能完全归咎于SQL这种语言,因为注入的问题而放弃SQL这种方式也是因噎废食。首先先说一个我在其他回答中也曾提到过的观点:`没有(运行时)编译,就没有注入`。SQL注入产生的原因,和栈溢出、XSS等很多其他的攻击方法类似,就是未经检查或者未经充分检查的用户输入数据,意外变成了代码被执行。针对于SQL注入,则是用户提交的数据,被数据库系统编译而产生了开发者预期之外的动作。也就是,SQL注入是用户输入的数据,在拼接SQL语句的过程中,超越了数据本身,成为了SQL语句查询逻辑的一部分,然后这样被拼接出来的SQL语句被数据库执行,产生了开发者预期之外的动作。所以从根本上防止上述类型攻击的手段,还是避免数据变成代码被执行,时刻分清代码和数据的界限。而具体到SQL注入来说,被执行的恶意代码是通过数据库的SQL解释引擎编译得到的,所以只要避免用户输入的数据被数据库系统编译就可以了。现在的数据库系统都提供SQL语句的预编译(prepare)和查询参数绑定功能,在SQL语句中放置占位符'?',然后将带有占位符的SQL语句传给数据库编译,执行的时候才将用户输入的数据作为执行的参数传给用户。这样的操作不仅使得SQL语句在书写的时候不再需要拼接,看起来也更直接,而且用户输入的数据也没有机会被送到数据库的SQL解释器被编译执行,也不会越权变成代码。至于为什么这种参数化的查询方式没有作为默认的使用方式,我想除了兼容老系统以外,直接使用SQL确实方便并且也有确定的使用场合。 - -### Mybatis是如何防止SQL注入的 diff --git a/system/architecture/img/distribute_lock.jpg b/system/architecture/img/distribute_lock.jpg new file mode 100644 index 00000000..13f34e86 Binary files /dev/null and b/system/architecture/img/distribute_lock.jpg differ diff --git "a/system/architecture/redis\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/system/architecture/redis\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\351\224\201.md" deleted file mode 100644 index 94d66471..00000000 --- "a/system/architecture/redis\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\351\224\201.md" +++ /dev/null @@ -1,115 +0,0 @@ -### 需求 -在多个服务间保证同一时刻同一时间段内只有一个用户能获取到锁 - -### 面临的挑战 -1. 死锁问题:线程A获取到了锁,但是程序执行异常,导致锁未及时释放。可通过try catch finally来在finally中执行程序异常释放锁的问题;另外,通过`set key value px nx`仅当key不存在的时候设置key,并设置过期时间(建议毫秒)来保证程序执行超时,锁自动释放。 -2. 释放锁问题:线程A获取到了锁,线程A在某个操作上长时间执行,导致锁过期,自动释放;线程B获取到了这个锁;线程A执行完毕,准备释放锁,因为设置的`value`值一样,所以就释放了线程B的锁。这就要求释放锁的线程必须是加锁的线程,也就是说要给线程加标记,保证锁的唯一性,其实是区分线程,可以使用ThreadLocal+UUID来保证,使用lua脚本删除key,保证释放锁的原子操作。 -3. 集群下的故障转移问题:Redis在进行主从复制时是异步完成的,线程A在master获取到了锁,但是在复制数据到slave的过程中master挂了,导致这个锁没有复制到slave中;然后redis选举一个升级为master,那么这个新的master中没有线程A的那个锁,这时候其他线程是可以获取到锁的,导致互斥失效。思路:原master上有线程A的锁,现master上有线程B的锁,那怎么办呢?如果线程B是在线程A获取的锁过期后获取的,就不存在这个互斥问题,或者线程B在px毫秒之后再获取锁,也不存在互斥问题 -4. 多节点redis实现的分布式锁算法(RedLock): 有效防止单点故障。思路就是在线程A尝试去这N个节点拿锁,每次去一个节点拿锁的时间不能超过M毫秒,当拿到锁的个数超过总个数/2+1个,就认为拿锁成功。 - -### 最低保证分布式锁的有效性和安全性的要求 -1. 互斥:任何时刻只能有一个client获取锁 -2. 释放/死锁:释放锁的线程必须是加锁的线程;服务器宕机情况下,也能释放锁 -3. 容错:只要多数(一半以上)redis节点在使用,client就可以获取和释放锁 - -### 实现方式 -1. Redis中的Redlock -2. 参考代码 -```lua -// 释放锁 -if redis.call('get',KEYS[1]) == ARGV[1] then - return redis.call('del', KEYS[1]); -else - return 0; -end; -``` - -```lua -// 减库存 -if (redis.call('exists', KEYS[1]) == 1) then - local stock = tonumber(redis.call('get', KEYS[1])); - if (stock == -1) then - return -1; - end; - if (stock > 0) then - redis.call('incrby', KEYS[1], -1); - return stock; - end; - return 0; -end; -return -1; -``` - -```java - -@Slf4j -@Component -public class RedisRepository { - - @Autowired - private RedisTemplate redisTemplate; - - /** - * 设置RedisTemplate的序列化 - * @param redisTemplate - */ - public RedisRepository(RedisTemplate redisTemplate) { - RedisSerializer redisSerializer = new StringRedisSerializer(); - redisTemplate.setKeySerializer(redisSerializer); - redisTemplate.setValueSerializer(redisSerializer); - redisTemplate.setHashKeySerializer(redisSerializer); - redisTemplate.setHashValueSerializer(redisSerializer); - this.redisTemplate = redisTemplate; - } - - - /** - * 获取分布式锁 - * @param key 锁 - * @param requestId 锁标识 - * @param expireTime 过期时间(毫秒) - * @return 加锁是否成功 - */ - public boolean lock(String key, String requestId, int expireTime) { - if (null == key || null == requestId || expireTime < 0) { - return false; - } - - boolean locked = false; - int tryCount = 3; - while (!locked && tryCount > 0) { - locked = redisTemplate.opsForValue().setIfAbsent(key, requestId, expireTime, TimeUnit.MICROSECONDS); - tryCount--; - - try { - Thread.sleep(300); - } catch (InterruptedException e) { - log.error("获取分布式锁失败, {}", e); - } - } - - return locked; - } - - /** - * @param key 锁 - * @param requestId 锁标识 - * @return 释放锁是否成功 - */ - public boolean unlockLua(String key, String requestId) { - if (null == key || null == requestId) { - return false; - } - - DefaultRedisScript redisScript = new DefaultRedisScript(); - // 用于解锁的lua脚本位置 - redisScript.setScriptText("if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"); - redisScript.setResultType(Long.class); - // 没有指定序列化方式,默认使用上面配置的 - Object result = redisTemplate.execute(redisScript, Arrays.asList(key), requestId); - return result.equals(Long.valueOf(1)); - } -``` - -### 参考文章 -* https://cloud.tencent.com/developer/article/1431873 \ No newline at end of file diff --git "a/system/architecture/spring-cloud-\344\274\230\351\233\205\344\270\212\344\270\213\347\272\277.md" "b/system/architecture/spring-cloud-\344\274\230\351\233\205\344\270\212\344\270\213\347\272\277.md" new file mode 100644 index 00000000..22785098 --- /dev/null +++ "b/system/architecture/spring-cloud-\344\274\230\351\233\205\344\270\212\344\270\213\347\272\277.md" @@ -0,0 +1 @@ +PL \ No newline at end of file diff --git "a/system/architecture/\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" "b/system/architecture/\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" new file mode 100644 index 00000000..0eacbc38 --- /dev/null +++ "b/system/architecture/\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" @@ -0,0 +1,3 @@ +### 为什么要有分布式ID +### 号段模式 +https://time.geekbang.org/dailylesson/detail/100075738 \ No newline at end of file diff --git "a/system/architecture/\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/system/architecture/\345\210\206\345\270\203\345\274\217\351\224\201.md" new file mode 100644 index 00000000..f869e196 --- /dev/null +++ "b/system/architecture/\345\210\206\345\270\203\345\274\217\351\224\201.md" @@ -0,0 +1,102 @@ +## 目录 +* [分布式锁需求](#分布式锁需求) +* [分布式锁演进](#分布式锁演进) +* [分布式锁面临的挑战](#面临的挑战) +* [分布式锁的5个特性](#分布式锁的5个特性) +* [分布式锁实现方案](#实现方式) + * [SET EX|PX NX + 校验唯一值](#实现方式) + * [开源框架Redisson](#实现方式) + * [Redis集群实现分布式锁Redlock](#实现方式) + * [数据库悲观锁X锁](#数据库悲观锁X锁) +* [分布式锁常见问题](#分布式锁常见问题) + * [java gc STW (stop the word)导致的锁过期问题](#分布式锁常见问题) + +## 分布式锁需求 +在多个服务间保证同一时刻同一时间段内只有一个用户能获取到锁 + +## 分布式锁演进 +1. 基于数据库的悲观锁:X锁 +2. 基于数据库的乐观锁:基于版本号 +3. 基于Redis的分布式锁 +4. 基于Zookeeper的分布式锁 + +## 面临的挑战 +1. 死锁问题:线程A获取到了锁,但是程序执行异常,导致锁未及时释放。可通过try catch finally来在finally中执行程序异常释放锁的问题;另外,通过`set key value px nx`仅当key不存在的时候设置key,并设置过期时间(建议毫秒)来保证程序执行超时,锁自动释放。 +2. 释放锁问题:线程A获取到了锁,线程A在某个操作上长时间执行,导致锁过期,自动释放;线程B获取到了这个锁;线程A执行完毕,准备释放锁,因为设置的`value`值一样,所以就释放了线程B的锁。这就要求释放锁的线程必须是加锁的线程,也就是说要给线程加标记,保证锁的唯一性,其实是区分线程,可以使用ThreadLocal+UUID来保证,使用lua脚本删除key,保证释放锁的原子操作。 +3. 锁丢失 - 集群下的故障转移问题:Redis在进行主从复制时是异步完成的,线程A在master获取到了锁,但是在复制数据到slave的过程中master挂了,导致这个锁没有复制到slave中;然后redis选举一个升级为master,那么这个新的master中没有线程A的那个锁,这时候其他线程是可以获取到锁的,导致互斥失效。思路:原master上有线程A的锁,现master上有线程B的锁,那怎么办呢?如果线程B是在线程A获取的锁过期后获取的,就不存在这个互斥问题,或者线程B在px毫秒之后再获取锁,也不存在互斥问题。红锁RedLock +4. 多节点redis实现的分布式锁算法(RedLock): 有效防止单点故障。思路就是在线程A尝试去这N个节点拿锁,每次去一个节点拿锁的时间不能超过M毫秒,当拿到锁的个数超过总个数/2+1个,就认为拿锁成功。(简单来说是过半机制) +5. 锁续期 - 如何合理设置px过期时间,太短-逻辑还没走完,就过期了,太长-造成其他线程不必要的等待。太短-可以使用锁续期(redission的watchdog),太长-finally中主动设置过期 + +## 分布式锁的5个特性 +1. 互斥性:在任意时刻 ,只有一个客户端能持有锁。 +2. 不会发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁成功。 +3. 具有容错性:只要大部分的Redis节点运行正常,客户端就可以加锁和解锁。 +4. 解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。 +5. 锁不能自己失效-续期策略:正常执行程序的过程中,锁不能因为某些原因失效。(控制锁的时间) + +## 实现方式 +1. SET EX|PX NX + 校验唯一值,再释放锁 +优点:保证加锁的原子性,使用LUA脚本释放锁时,通过判断唯一值(如线程ID+时间戳),锁不会被其他线程释放 +缺点:锁没有自动续期机制,锁无法支持重入 +2. 开源框架Redisson +优点:锁支持自动续期。只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。 +缺点:主从模式可能造成锁丢失 +3. Redis集群实现分布式锁Redlock - 红锁 +优点:锁支持自动续期,同时能有效防止锁丢失 +缺点:需要多台redis机器,极端情况下会造成两个线程同时获取锁(如5个节点,线程A拿到了1,2,3的锁,过半,加锁成功,1,2宕机,线程B去拿到了4,5的锁,也过半了,此时线程A,B同时持有锁)。实际项目中很少使用Redlock,因为红锁会影响并发环境下的性能,且耗费服务器 +简化的实现步骤: +``` + 按顺序向5个master节点请求加锁 + 根据设置的超时时间来判断,是不是要跳过该master节点。 + 如果大于等于三个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。 + 如果获取锁失败,解锁! +``` +使用Redisson实现Redlock。 在Redisson框架中,实现了红锁的机制,Redisson的RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁, + +每个RLock对象实例可以来自于不同的Redisson实例。当红锁中超过半数的RLock加锁成功后,才会认为加锁是成功的,这就提高了分布式锁的高可用。 +```java +public void testRedLock(RedissonClient redisson1,RedissonClient redisson2, RedissonClient redisson3){ + RLock lock1 = redisson1.getLock("lock1"); + RLock lock2 = redisson2.getLock("lock2"); + RLock lock3 = redisson3.getLock("lock3"); + RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); + try { + // 同时加锁:lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。 + lock.lock(); + // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁 + boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } +} +``` + +参考代码 +------- +```lua +// 释放锁 +if redis.call('get',KEYS[1]) == ARGV[1] then + return redis.call('del', KEYS[1]); +else + return 0; +end; +``` +4. 数据库悲观锁X锁 +排它锁,又叫写锁,又叫X锁,如果事务T对A加了X锁,则其他事务不能对A加任何类型的锁 +用法 SELECT * FOR UPDATE +可利用主键唯一性来达到加锁的目的,此方法并发性能低,同时对锁的过期时间需要额外处理 +5. 基于数据库的乐观锁:基于版本号 + +## 分布式锁常见问题 +### java gc STW (stop the word)导致的锁过期问题 +1. 工作线程1,获取锁,并设置了超时淘汰时长 +2. jvm gc垃圾回收时,会暂停工作线程,即STW +3. 当工作线程1恢复工作的时候,由于STW的时长稍长,可能锁已经超时淘汰了,但是该线程还不知道,此时工作线程2去获取,也是能获取到的,导致出现多个线程获取同一个锁的异常问题 +这个问题的思路不能放在解决锁的互斥性上,要解决GC。watchdog也解决不了这类问题 + +### 参考文章 +* https://cloud.tencent.com/developer/article/1431873 +* https://www.cnblogs.com/woshare/p/15253014.html +* https://www.cnblogs.com/crazymakercircle/p/14731826.html \ No newline at end of file diff --git "a/system/architecture/\345\276\256\346\234\215\345\212\241.md" "b/system/architecture/\345\276\256\346\234\215\345\212\241.md" deleted file mode 100644 index 25ff3222..00000000 --- "a/system/architecture/\345\276\256\346\234\215\345\212\241.md" +++ /dev/null @@ -1,9 +0,0 @@ -### 学习计划 - -### 学习笔记 -* 微服务定义: 微服务架构就是将一个庞大的业务系统按照业务模块拆分成若干个独立的子系统,每个子系统都是一个独立的应用,它是一种将应用构建成一系列按业务领域划分模块的,小的自治服务的软件架构方式,倡导将复杂的单体应用拆分成若干个功能单一、松偶合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发,及持续集成与交付活动。 -* 服务治理的概念:所有服务间不允许直接调用,而是先到注册中心进行登记,再由注册中心统一协调和管理所有服务的状态并对外发布,调用者只需要记住服务名称,去找注册中心获取服务即可。 -* 目前国内用的最多的无外乎是两套框架:Dubbo,Spring Cloud。Dubbo大家都很熟悉,从开源到无人维护再到重新冲击Apache顶级项目。但是Dubbo更加准确来说是一个分布式服务框架,致力于提供高效的RPC远程服务调用方案以及SOA服务治理方案。说白了就是个分布式远程服务调用框架。 - -### RPC -RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。这句话至少有3个层面的含义。①它是协议,说明这是一种规范,就需要有遵循这一规范的实现。典型的RPC实现包括 Dubbo Thrift、 GRPC 等。②网络通信实现是透明的,调用方不需要关心网络之间的通信协议、网 I/0 模型、通信的信息格式等。③跨语言,调用方实际上并不清楚对端服务器使用的是什么程序语言。对于调用方来说,无论其使用的是何种程序语言,调用都应该成功,并且返回值也应按照调用方程序语言理解的形式来描述。 \ No newline at end of file diff --git "a/system/architecture/\346\225\260\346\215\256\344\270\255\345\217\260\346\236\266\346\236\204\351\232\217\346\203\263.md" "b/system/architecture/\346\225\260\346\215\256\344\270\255\345\217\260\346\236\266\346\236\204\351\232\217\346\203\263.md" deleted file mode 100644 index 601af7d2..00000000 --- "a/system/architecture/\346\225\260\346\215\256\344\270\255\345\217\260\346\236\266\346\236\204\351\232\217\346\203\263.md" +++ /dev/null @@ -1,42 +0,0 @@ -### What is the big data platform -> 大数据平台是一个集数据的获取、抽取、整理、计算与服务为一体的平台。 - -### What can be supported by big data platform - -### How to build up a big data platform -#### 数据的采集 -* 数据的采集分历史数据和实时数据,历史数据可以通过脚本来一次性导入数据中台,实时数据需要实时推送到数据中台。 -* 数据采集阶段采用双写来保证数据在传输的过程中会丢失的问题,双写是指写到数据中台和写到日志。 -* 数据采集阶段配置manager: - * 数据接入管理 - * 运营操作管理 - * 指标监控预警 - * 权限管理 - -#### 数据运营 -* 谁在生产数据,谁采集,谁负责,谁授权,谁消费 - -笔记: -2018年年底到2019年年初,一场组织变革的飓风席卷了国内各大互联网公司。阿里、腾讯、百度、京东、美团等先后拿出了几年来最大规模的组织调整计划。 - -在这些变化中,一个值得关注的现象是,各大公司都不约而同地提到了未来转型To B的计划,并在组织架构中增设“中台”,甚至将中台上升为调整的核心方向。 - -多数大公司开展新项目时,都由业务部门先提出需求,信息中心部门进行招标。在这个过程中,每个新系统的上线都意味着一座新的烟囱立起,项目完成后经常遗留下无法计算的重复建设、重复投资、高昂的交互和协作成本。 - -这种烟囱式的系统管理困境,是中国互联网企业发展史上的一个典型问题。 - -数据咨询公司Thoughtworks首席咨询师王健将多数公司的前台和后台形容为两个转速不相协调的齿轮,前台需要快速响应用户需求,后台却追求稳定至上。他对《中国企业家》谈到,中台的功能是将企业的核心能力、数据、用户信息以共享服务的形式加以沉淀,从而解决各业务部门重复造轮子的问题,降低创新成本。 - -落实到具体形态上,中台与平台的最大区别是什么?右军认为,平台的特征是高内聚、低耦合,职责边界清晰、易于集成;而中台在高内聚、低耦合的基础上,更追求数据完整性、业务可运营。 - -全域数据采集与引入:以需求为驱动,以数据多样性的全域思想为指导,采集与引入全业务、多终端、多形态的数据; - -标准规范数据架构与研发:统一基础层、公共中间层、百花齐放应用层的数据分层架构模式,通过数据指标结构化规范化的方式实现指标口径统一; - -连接与深度萃取数据价值:形成以业务核心对象为中心的连接和标签体系,深度萃取数据价值; - -统一数据资产管理:构建元数据中心,通过资产分析、应用、优化、运营四方面对看清数据资产、降低数据管理成本、追踪数据价值。 - -统一主题式服务:通过构建服务元数据中心和数据服务查询引擎,面向业务统一数据出口与数据查询逻辑,屏蔽多数据源与多物理表; - -极大的丰富和完善了阿里巴巴大数据中心,OneData、OneID、OneService渐趋成熟并成为上至CEO、下至一线员工共识的方法论体系。 \ No newline at end of file diff --git "a/system/architecture/\346\236\266\346\236\204\350\256\276\350\256\241\351\232\217\346\200\235\345\275\225.md" "b/system/architecture/\346\236\266\346\236\204\350\256\276\350\256\241\351\232\217\346\200\235\345\275\225.md" index 6f346baa..b5c60182 100644 --- "a/system/architecture/\346\236\266\346\236\204\350\256\276\350\256\241\351\232\217\346\200\235\345\275\225.md" +++ "b/system/architecture/\346\236\266\346\236\204\350\256\276\350\256\241\351\232\217\346\200\235\345\275\225.md" @@ -5,6 +5,17 @@ 2. 在技术选型上要对技术的细节有较强的掌控力。 3. 设计时要带上版本号,为后期升级和线上排查推演提供依据。 * 微服务是以业务拆分和分布式来应对业务复杂度高和重复开发的问题 - +* 假设在10点到11点这一个小时内,有200W个用户访问我们的系统,假设平均每个用户请求的耗时是3秒,那么计算的结果如下: + ``` + QPS=2000000/60*60 = 556 (表示每秒钟会有556个请求发送到服务端) + RT=3s(每个请求的平均响应时间是3秒) + 并发数=556*3=1668 + ``` ### 架构和程序开发的区别 -架构设计的关键思维是判断和取舍,程序设计的关键思维是逻辑和实现。 \ No newline at end of file +架构设计的关键思维是判断和取舍,程序设计的关键思维是逻辑和实现。 + +### 文章 +* 微服务与架构师 https://www.cnblogs.com/kiba/p/15497173.html + ``` + 讲了微服务与架构师之间的关系,以及对当前微服务之死的一种理解 + ``` \ No newline at end of file diff --git a/tool/2021-04-29.png b/tool/2021-04-29.png deleted file mode 100644 index d5324d8c..00000000 Binary files a/tool/2021-04-29.png and /dev/null differ diff --git a/tool/2021-05-01.png b/tool/2021-05-01.png deleted file mode 100644 index d5324d8c..00000000 Binary files a/tool/2021-05-01.png and /dev/null differ diff --git a/tool/2021-07-28.png b/tool/2021-07-28.png deleted file mode 100644 index d5324d8c..00000000 Binary files a/tool/2021-07-28.png and /dev/null differ diff --git a/tool/2023-03-24.png b/tool/2023-03-24.png new file mode 100644 index 00000000..0e1b6e1e Binary files /dev/null and b/tool/2023-03-24.png differ diff --git a/tool/books.md b/tool/books.md deleted file mode 100644 index f47be97c..00000000 --- a/tool/books.md +++ /dev/null @@ -1 +0,0 @@ -* Java系统性能优化实战 https://item.jd.com/12742086.html \ No newline at end of file diff --git a/tool/browser.md b/tool/browser.md deleted file mode 100644 index 65117a5c..00000000 --- a/tool/browser.md +++ /dev/null @@ -1,3 +0,0 @@ -* [Chrome好用的插件](http://note.youdao.com/noteshare?id=530c5fcc0dfba1feadf26748cce65666&sub=wcp1583414597450563) -* 浏览器URL中输入data:text/html, ,可以当作记事本来使用 -* 主页推荐 https://a.maorx.cn/ 可添加多个搜索引擎,同时添加标签,可在不同设备同步,火狐需要安装New Tab Homepage来实现打开“新标签页”时显示的是homepage \ No newline at end of file diff --git a/tool/btrace.md b/tool/btrace.md deleted file mode 100755 index 86aae45a..00000000 --- a/tool/btrace.md +++ /dev/null @@ -1,7 +0,0 @@ -### FAQ -1. btrace DEBUG: adding to boot classpath failed! - 常见是由于系统安装的btrace版本和代码中引入的jar的版本不匹配,建议加载jar(/btrace/../build/) -2. Port 2020 unavailable. - 2020端口不可用,那就换一个:btrace pid -p port xxx.java -3. 当参数为泛型的时候,使用Object替换 -4. 在Windows中,使用cmd执行btrace命令,git bash执行不了 \ No newline at end of file diff --git a/tool/chrome.md b/tool/chrome.md deleted file mode 100644 index 36707c64..00000000 --- a/tool/chrome.md +++ /dev/null @@ -1,2 +0,0 @@ -* [Chrome好用的插件](http://note.youdao.com/noteshare?id=530c5fcc0dfba1feadf26748cce65666&sub=wcp1583414597450563) -* 浏览器URL中输入`data:text/html, `,可以当作记事本来使用 diff --git a/tool/coding-life.md b/tool/coding-life.md index 7cd0896b..12c50912 100644 --- a/tool/coding-life.md +++ b/tool/coding-life.md @@ -2,22 +2,21 @@ * [记录自己的成长](#记录自己的成长) * [最好的建议](#最好的建议) * [正视自己的价值](#正视自己的价值) -* [新工程师要干的五件事情](#新工程师要干的五件事情) +* [后端避坑心得](#后端避坑心得) ### 记录自己的成长 -* 2014-10 收到群硕(上海)软件开发有限公司的Offer +* 2013-10 收到群硕(上海)软件开发有限公司的Offer * 2014-03 去群硕扬州实习 * 2014-07 河南中医药大学毕业 * 2014-07 - 2014-09 群硕(上海)软件开发有限公司 * 2017-09 - 2020-06 群硕(武汉)软件开发有限公司 -* 2020-06 - 至今 武汉氪细胞网络技术有限公司(产品Timing App) -
- 展开查看 -

-        System.out.println("Hello to see U!");
-      
-
+* 2020-06 - 2021-03 武汉氪细胞网络技术有限公司(产品Timing App) + * 千万级的用户量,百万DAU,3000左右的QPS,高并发场景较多。后来,国家对在线教育进行了整改。。。 +* 2021-04 - 2022-02 孚创武汉研发中心 + * 互联网+的产品,对手是途虎 +* 2022-03 - 至今 腾讯云科技 CSIG + * JOOX音乐APP,QQ音乐的海外版 ### 最好的建议 我毕业后就进入一家外企,在这家公司学的比较全,不论是技术,思想,管理都有了很大的提升。后期我整理了自己的思考,总结来说就是这几个词,人、事、共赢。管理上,我感觉我们都是做事情的人,那么一帮人,团队氛围很好,做事情很开心,这很重要。所谓的共赢,强调的是个人与企业,个人与其他人之间的关系。 @@ -28,7 +27,7 @@ ``` 2. 推荐自己 ```txt -将自己的建议和想法或者能促进集体效率的告诉其他人,从集体的角度出发,这也是一种自我推荐。 +将自己的建议和想法或者能促进集体效率的告诉其他人,从集体的角度出发,这也是一种自我推荐。做好自己的工作是本分,尽可能去发光发热。 ``` 3. 学会带领团队 ```txt @@ -40,11 +39,11 @@ ``` 5. 时刻抓住重点 ```txt -你出在某一个位置时,对应的就有什么样的责任和义务,抓住此刻的重点,把事情做的完美,同时也要适当抓住下一个提升的重点,为下一次能够胜任更高的位置打好基础。 +你处在某一个位置时,对应的就有什么样的责任和义务,抓住此刻的重点,把事情做的完美,同时也要适当抓住下一个提升的重点,为下一次能够胜任更高的位置打好基础。 ``` 6. 尽职尽责 ```txt -把属于自己负责的事情做的完美,和自己相关的事情做好,是做事情的底线。 +把属于自己负责的事情做的完美,和自己相关的事情做好,是做事情的底线。总的来说就是对自己的上下游负责。 ``` 7. 多交朋友 ```txt @@ -55,7 +54,7 @@ ```txt 企业最关心的(或者说唯一在乎的)事情,就是增加收入、降低成本。 企业实际上需要的不是程序员,而是能够帮助它们增加收入、降低成本的人。 -企业雇佣你,是为了让你帮它们完成某个可以增加收入、降低成本的项目,而不是为了让你追求个人的软件成就。 +企业雇佣你,是为了让你帮它们完成某个可以增加收入、降低成本的项目,而不只是为了让你追求个人的软件成就。 ``` 我想说的是,在实现公司价值的同时提升自己的能力,达到双赢的局面。 @@ -70,8 +69,18 @@ * 可以提高技术能力,因为只有理解了代码才能表达出来,文档越清晰,就代表思路越清晰 * 可以提高个人影响力,文档写得越好,看的人就越多 -## 引用 -* [The best career advice I’ve received](https://humanwhocodes.com/blog/2013/10/15/the-best-career-advice-ive-received/) -* [七个对我最好的职业建议](http://www.ruanyifeng.com/blog/2015/09/career-advice.html) -* [不要自称为程序员](http://www.ruanyifeng.com/blog/2011/10/dont_call_yourself_a_programmer.html) -* [新工程师要干的五件事情](https://evanliman.to/2019/10/06/so-youre-a-new-grad-software-engineer.html) \ No newline at end of file +### 箴言 +* 核心链路永远要100%,深知00% +* 隔行如隔山,保持对一个行业的敬畏心 +* 不了解行业,平等对话就是神话 +* 互联网+不是将互联网的一套塞给传统企业,而是在理解行业的基础之上再发挥互联网的价值 +* 烂尾楼产品,最小化可行产品MVP,现有滑板车,再有自行车,再有电动车,再有汽车,不要一下子就开始造汽车,迭代而行 +* 团队的战斗力来自一起熬夜,一起关过小黑屋...一起过事;团队的信任来自沟通和每次高质量的交付 +* 所以,我们不是要克服舒适区,盲目的挑战自我,而是要寻找合适的挑战区,去把它开拓成新的舒适区。 +* 重大问题的解决方案永远不可能在产生这个问题的维度上出现! + +### 后端避坑心得 +敬畏之心 - 敬畏之心 - 敬畏之心 +* 对得到的参数要质疑。前端传来的参数,要判断是否为空;数据库中的字段,要判断是不是一定有值,要看线上的数据 +* 写高质量的API。首先要保证结果的正确性,其次要保证健壮性,考虑了所有的异常情况,最后要保证高性能,应对多少量的数据查询不会造成慢API +* 日志一定要打好。关键的日志一定要打好,这对后期排查问题非常重要。 \ No newline at end of file diff --git a/tool/common-tools.md b/tool/common-tools.md deleted file mode 100755 index 82fc8340..00000000 --- a/tool/common-tools.md +++ /dev/null @@ -1,4 +0,0 @@ -### 流量统计,网站分析 -* CNZZ 提供免费的网站流量统计 - -![添加站点](./png/CNZZ网站流量统计分析.PNG) \ No newline at end of file diff --git a/tool/daily.md b/tool/daily.md deleted file mode 100755 index abd8a56a..00000000 --- a/tool/daily.md +++ /dev/null @@ -1 +0,0 @@ -* 如何设计一个本地缓存? https://my.oschina.net/OutOfMemory/blog/3133013 \ No newline at end of file diff --git a/tool/english.md b/tool/english.md index 643e3a9d..66018879 100644 --- a/tool/english.md +++ b/tool/english.md @@ -25,6 +25,9 @@ * DevOps:(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。 * QPS = req/sec = 请求数/秒 (单个进程每秒请求服务器的成功次数) * TPS = Transaction Per Second 每秒处理的事务量(如用户提交一个表单,数据落库就是一个事务) +* GA Google Analytics 通过安装谷歌的agent(如ga.js等)来收集用户互动数据,进行数据加工,得到访问的统计结果 + * PV(Page View)访问量, 即页面浏览量或点击量,衡量网站用户访问的网页数量;在一定统计周期内用户每打开或刷新一个页面就记录1次,多次打开或刷新同一页面则浏览量累计 + * UV(Unique Visitor)独立访客,统计1天内访问某站点的用户数(以cookie为依据);访问网站的一台电脑客户端为一个访客。可以理解成访问某网站的电脑的数量。如果更换了IP后但不清除cookies,再访问相同网站,该网站的统计中UV数是不变的。 * failover机制又称失效转移或故障切换,指系统中的某一项设备或服务失效而无法运行时,另一项设备或服务能自动接手原失效系统所执行的工作。 * failback机制又称自动回复,是指主系统或服务因升级而暂时不可用,现有服务需要转义到备用系统,待主系统升级后切换服务至主系统 * Oops:哎呀 @@ -124,4 +127,7 @@ * BRD Business Requirement Document 业务需求 * PRD Product Requirement Document 产品需求 * User Stroy 场景 -* LDAP Lightweight Directory Access Protocol 轻型目录访问协议。LDAP采用树形结构存储,查询高效,开源产品OpenLDAP。可用于SSO单点登录。LDAP主要的应用场景是查询多而修改极少,查询和修改的比率是10:1 甚至更高, 那就充分发挥LDAP的优势了,因为没有事务处理,那数据库的速度可是比不上。 还有LDAP能存储海量的数据,还可以轻松地在各个系统之间复制,可用性超高。 \ No newline at end of file +* LDAP Lightweight Directory Access Protocol 轻型目录访问协议。LDAP采用树形结构存储,查询高效,开源产品OpenLDAP。可用于SSO单点登录。LDAP主要的应用场景是查询多而修改极少,查询和修改的比率是10:1 甚至更高, 那就充分发挥LDAP的优势了,因为没有事务处理,那数据库的速度可是比不上。 还有LDAP能存储海量的数据,还可以轻松地在各个系统之间复制,可用性超高。 +* privileged 有特权的 +* syntactic sugar 语法糖 +* AIGC Artificial Intelligence Generated Content 人工智能生成内容 diff --git a/tool/git.md b/tool/git.md index 5f0c4f6d..b8d84a7a 100755 --- a/tool/git.md +++ b/tool/git.md @@ -20,6 +20,13 @@ ## 创建自己的代码仓库,并提交一个commit * 参考文章 https://jingyan.baidu.com/article/8cdccae9269b1f315413cde2.html +## Git Architecture + +![](http://r3rutcmq2.hd-bkt.clouddn.com//githubgit-architecture.jpg) +1. Workspace: 工作区 +2. Stage:暂存区 +3. Repository: 本地仓库 +4. Remote: 远程仓库 ### Create a new git branch from an old commit diff --git a/tool/hutool.md b/tool/hutool.md new file mode 100644 index 00000000..c8da26f1 --- /dev/null +++ b/tool/hutool.md @@ -0,0 +1,20 @@ +## 学习笔记 +### 简介 +* 官方网站 https://www.hutool.cn/docs/#/ + +### 具体类 +* Convert 基础数据类型相互转化 +* 时间和日期 + * DateUtil 常用时间工具类 + * DateTime DateTime时间的处理 + * DatePattren 标准日期格式 + * ChineseDate 农历日期 +* FileUtil 文件帮助类 +* IOUtil IO流处理 +* StrUtil 字符串工具类 +* ArrayUtil 数组工具类 +* RandomUtil 随机工具类 +* IdUtil 唯一ID工具 +* ReUtil 正则工具类 +* IdCardUtil 身份证工具类 +* DesensitizedUtil 信息脱敏工具类 \ No newline at end of file diff --git a/tool/install.md b/tool/install.md deleted file mode 100644 index 44ddb940..00000000 --- a/tool/install.md +++ /dev/null @@ -1,24 +0,0 @@ -### JDK安装 -* 解压 -``` - mkdir /usr/lib/jvm - tar -zxvf jdk-8u192-linux-x64.tar.gz -C /usr/lib/jvm -``` -* 文件末尾追加如下内容 -``` - vim ~/.bashrc - - #set oracle jdk environment - export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_181 ## 这里要注意目录要换成自己解压的jdk 目录 - export JRE_HOME=${JAVA_HOME}/jre - export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib - export PATH=${JAVA_HOME}/bin:$PATH -``` -* 使环境变量生效 -``` - source ~/.bashrc -``` -* 验证 -``` - java -version -``` \ No newline at end of file diff --git a/tool/intellij.md b/tool/intellij.md index af09adb4..f8c9ecaa 100755 --- a/tool/intellij.md +++ b/tool/intellij.md @@ -19,6 +19,9 @@ ### 配置 1. 开始没开启显示空格的选项(Setting->Editor->Appearance->show whitespaces) +一些代码规范会要求编程时使用4个空格缩进而不是tab,好处是在不同编辑器下4个空格的宽度看起来是一致的,而tab则长短不一 +设置好以后,可以按ctrl+alt+L整理格式,然后选中缩进的一部分,如果能选中缩进的一部分,证明是空格。 +https://jingyan.baidu.com/article/148a1921cbaaf04d71c3b1ee.html ![](png/idea-tab-whitespace.PNG) @@ -86,4 +89,14 @@ File -> Project Structure -> Project Settings -> Modules ### idea moudle没有蓝色的小方块 * https://blog.csdn.net/wt_better/article/details/86380826 1. 右边Maven -> Generate Source and Update Project -2. 更新.idea中的modules.xml \ No newline at end of file +2. 更新.idea中的modules.xml + +### java.nio.charset.MalformedInputException: Input length = 1 +1. 可能是编码问题 +2. 配置文件,ctrl+x, ctrl+v解决 + +### IDEA右下角显示内存指示器 +双shift,输入 Show Memory Indicator + +### DBeaver连接mysql时Public Key Retrieval is not allowed +编辑连接->驱动属性->allowPublicKeyRetrieval=TRUE \ No newline at end of file diff --git a/tool/interview.md b/tool/interview.md index bc7879c2..e1a1d75d 100644 --- a/tool/interview.md +++ b/tool/interview.md @@ -1,7 +1,30 @@ # 目录 +* [知识点纪要](#知识点纪要) * [面试题](#面试题) * [面试技巧](#面试技巧) +### 知识点纪要 +基础,集合,AQS,JVM,Spring,SpringBoot,SpringCloud,缓存,消息队列,数据库,搜索,架构 + +集合 +---- +Java的集合类位于java.util.*包下,大体分为2类,Collection和Map,另外就是2个工具类。 +Collection包含3个分支: List, Set, Queue +Map包含1个分支:Map +Concurrent主要有3个package组成: + * java.util.concurrent + ``` + 提供大部分关于并发的接口和类,如BlockingQueue, ConcurrentHashMap, ExecutorService等 + ``` + * java.util.concurrent.atomic + ``` + 提供所有的原子类操作,如AtomicInteger, AtomicLong等 + ``` + * java.util.concurrent.locks + ``` + 提供锁相关的类,如Lock, ReentrantLock, ReadWriteLock, Confition等 + ``` + ### 面试题 #### Java 基础 * [什么是面向对象](https://github.com/zhonghuasheng/Tutorial/issues/188) @@ -44,6 +67,7 @@ 37. synchronized 和 Lock 有什么区别? 38. synchronized 和 ReentrantLock 区别是什么? 39. 说一下 atomic 的原理? +* [如何定位JVM死锁、cpu飙高、内存溢出](#如何定位JVM死锁、cpu飙高、内存溢出) ### spring/spring MVC * [SpringMVC 说说过滤器、监听器、拦截器有啥区别](https://github.com/zhonghuasheng/Tutorial/issues/197) * [SpringBoot系列](https://github. com/zhonghuasheng/Tutorial/issues?q=label%3ASpringBoot+) @@ -109,7 +133,7 @@ 92. Jedis 和 redisson 有哪些区别? 93. 怎么保证缓存和数据库数据的一致性? 94. redis 持久化有几种方式? -95. redis 怎么实现分布式锁? +95. [redis 怎么实现分布式锁?](../system/architecture/redis实现分布式锁.md) 96. redis 分布式锁有什么缺陷? 97. redis 如何做内存优化? 98. redis 淘汰策略有哪些? @@ -205,6 +229,8 @@ epoll_wait() 多长时间去轮询一次,看有没有数据 4. 排队下单,类似12306入Queue的操作 参考答案:https://github.com/zhonghuasheng/JAVA/tree/master/seckill# +### 场景题 +* 如果你们公司现在的产品能够支持10W用户访问,你们老板突然和你说,融到钱了,会大量投放广告,预计在1个月后用户量会达到1000W,如果这个任务交给你,你应该怎么做? ### 面试技巧 1. 面试前出于礼貌和面试官确认自己讲话是否能被听懂,方言的问题 @@ -220,6 +246,11 @@ epoll_wait() 多长时间去轮询一次,看有没有数据 答:ArrayList的底层数据结构是数组,不指定ArrayList大小的时候初始化的大小是0,第一次add的时候size会变成10,扩容的话会是之前的1.5倍。ArrayList由于底层是数组,因此随机查找的速度很快,插入和删除效率比较低。因为它底层是数组,因此分配内存空间的时候要求是连续的内存单元,所以如果需要存储的数据量很大的情况下,不建议使用ArrayList。LinkedList的底层是一个带有头节点和尾节点的双向链表,提供了头插(LinkedFirst)和尾插(LinkedLast),插入和删除比较快,不支持随机查询,LinkedList数据的存储不要求内存空间是连续的。 问:如果在多线程的情况下,我既想用List,又想保证线程安全,那怎么办? 答:我知道的有三种方式:1. 使用Vector,它是一个线程安全的List集合,所有的方法都加了synchronized关键字来保证同步,但它性能很差。[读写都加锁,底层也是数组,扩容时是之前的2倍] 2. 使用Collections.SynchronizedList,它是Collections下的一个静态内部类,它把List下的所有方法都变成了线程安全的,于Vector不同的是,它把synchronized加到了方法内部的代码块上,提高了扩展性[锁的粒度变小] 3. 使用CopyOnWriteArrayList,add的时候加锁,读的时候不加锁,提高了读取性能 [锁粒度变小,同时锁范围变小] + +CopyOnWriteArrayList:CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,很常一段时间,这个容器数据、数量等没有发生变化的时候,大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个新的容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数 + Object[] newElements = Arrays.copyOf(elements, len + 1); + newElements[len] = e; + setArray(newElements); ``` 3. 常规的来聊一下HashMap @@ -248,4 +279,6 @@ epoll_wait() 多长时间去轮询一次,看有没有数据 5. volatile看过吗? ``` 答:volatile修饰的变量保证了多线程下的可见性,当CPU写数据时,发现此变量被volatile修饰时,发现其他CPU中也存在该变量的副本,会发出信号通知其他CPU该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中的变量行是无效的,就重新去内存读取。它是通过计算机的总线嗅探机制(MESI)来实现的,当然它也会照成一个问题,就是volitale会一直嗅探,导致一些无效的交互,引发总线风暴。 -``` \ No newline at end of file +``` + +## 如何定位JVM死锁、cpu飙高、内存溢出 \ No newline at end of file diff --git a/tool/todo.md b/tool/todo.md new file mode 100644 index 00000000..780ae5aa --- /dev/null +++ b/tool/todo.md @@ -0,0 +1,15 @@ +20220101 +-------- +* ~~RedisLock 红锁 与 Redission中的分布式锁区别~~ -> 数据库中的锁 +* ~~消息平台架构图绘制~~ +* ~~SpringCloud知识点整理~~ -> 可以继续完善 +* MQ常见问题整理 +* ZGC梳理(CMS的三次标记一次清除[浮动垃圾]流程)-> 待整理(整理到了processon),后期整理到github +* ~~Mybatis中${}和#{}区别~~ +* AQS整理 +20220111 +-------- +* RocketMQ实现事务消息机制 +* Kafka实现事务消息机制 +* Zookeeper实现分布式锁方案和代码 +* MySQL源码分析 \ No newline at end of file diff --git a/tool/vscode-settings.md b/tool/vscode-settings.md index 671354f8..60f97e6b 100755 --- a/tool/vscode-settings.md +++ b/tool/vscode-settings.md @@ -11,4 +11,5 @@ "spell.StopAsking": true } ``` -* vscode相同内容多选,同时修改: 先选中所要修改的内容,然后按 `ctrl + alt + l`,接着进行修改,修改完后保存,再次按`ctrl + alt + l`释放操作 \ No newline at end of file +* vscode相同内容多选,同时修改: 先选中所要修改的内容,然后按 `ctrl + alt + l`,接着进行修改,修改完后保存,再次按`ctrl + alt + l`释放操作 +* 多行成一行:选中全部-> ctrl+alt+j \ No newline at end of file