diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 8fa92a8..63869db 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -8,7 +8,7 @@ import { gitPlugin } from '@vuepress/plugin-git' export default defineUserConfig({ lang: "zh-CN", - title: "Java学习&面试指南-程序员大彬", + title: "大彬", description: "Java学习、面试指南,涵盖大部分 Java 程序员所需要掌握的核心知识", base: "/", dest: './public', diff --git a/docs/README.md b/docs/README.md index 4b49ed1..9f8ba39 100644 --- a/docs/README.md +++ b/docs/README.md @@ -47,26 +47,10 @@ projects: [](https://www.zhihu.com/people/dai-shu-bin-13) [](https://github.com/Tyson0314/java-books) -## 秋招提前批信息汇总 - -[秋招提前批及正式批信息汇总(含内推)](https://docs.qq.com/sheet/DYW9ObnpobXNRTXpq?tab=BB08J2) - ## 面试手册电子版 本网站所有内容已经汇总成**PDF电子版**,**PDF电子版**在我的[**学习圈**](zsxq/introduce.md)可以获取~ -## 计算机经典书籍PDF - -给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取: - -![](http://img.topjavaer.cn/image/Image.png) - -![](http://img.topjavaer.cn/image/image-20221030094126118.png) - -**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq# - -备用链接:https://pan.quark.cn/s/3f1321952a16 - ## 学习路线 ![](http://img.topjavaer.cn/img/20220530232715.png) diff --git "a/docs/advance/excellent-article/MySQL\344\270\255N\344\270\252\345\206\231SQL\347\232\204\345\245\275\344\271\240\346\203\257.md" "b/docs/advance/excellent-article/MySQL\344\270\255N\344\270\252\345\206\231SQL\347\232\204\345\245\275\344\271\240\346\203\257.md" new file mode 100644 index 0000000..9a2241a --- /dev/null +++ "b/docs/advance/excellent-article/MySQL\344\270\255N\344\270\252\345\206\231SQL\347\232\204\345\245\275\344\271\240\346\203\257.md" @@ -0,0 +1,91 @@ +MySQL中编写SQL时,遵循良好的习惯能够提高查询性能、保障数据一致性、提升代码可读性和维护性。以下列举了多个编写SQL的好习惯 + +#### 1.使用EXPLAIN分析查询计划 + +在编写或优化复杂查询时,先使用EXPLAIN命令查看查询执行计划,理解MySQL如何执行查询、访问哪些表、使用哪种类型的联接以及索引的使用情况。 + +好处:有助于识别潜在的性能瓶颈,如全表扫描、错误的索引选择、过多的临时表或文件排序等,从而针对性地优化查询或调整索引结构。 + +#### 2.避免全表扫描 + +2. 习惯:尽可能利用索引来避免全表扫描,尤其是在处理大表时。确保在WHERE、JOIN条件和ORDER BY、GROUP BY子句中使用的列有适当的索引。 + +好处:极大地减少数据访问量,提高查询性能,减轻I/O压力。 + +#### 3. 为表和字段添加注释 + +3. 习惯:在创建表时,为表和每个字段添加有意义的注释,描述其用途、数据格式、业务规则等信息。 + +好处:提高代码可读性和可维护性,帮助其他开发人员快速理解表结构和字段含义,减少沟通成本和误解。 + +#### 4. 明确指定INSERT语句的列名 + +习惯:在INSERT语句中显式列出要插入数据的列名,即使插入所有列也应如此。 + +好处:避免因表结构变化导致的插入错误,增强代码的健壮性,同时也提高了语句的清晰度。 + +#### 5. 格式化SQL语句 + +习惯:保持SQL语句的格式整洁,使用一致的大小写(如关键词大写、表名和列名小写),合理缩进,避免过长的单行语句。 + +好处:提高代码可读性,便于审查、调试和团队协作。 + +#### 6. 使用LIMIT限制结果集大小 + +习惯:在执行SELECT、DELETE或UPDATE操作时,若不需要处理全部数据,务必使用LIMIT子句限制结果集大小,特别是在生产环境中。 + +好处:防止因误操作导致大量数据被修改或删除,降低风险,同时也能提高查询性能。 + +#### 7.使用JOIN语句代替子查询 + +习惯:在可能的情况下,优先使用JOIN操作代替嵌套的子查询,特别是在处理多表关联查询时。 + +好处:许多情况下JOIN的执行效率高于子查询,而且JOIN语句通常更易于理解和优化。 + +#### 8.避免在WHERE子句中对NULL进行比较 + +习惯:使用IS NULL和IS NOT NULL来检查字段是否为NULL,而不是直接与NULL进行等值或不等值比较。 + +好处:正确处理NULL值,避免逻辑错误和未预期的结果。 + +#### 9.避免在查询中使用SELECT + +习惯:明确列出需要的列名,而不是使用SELECT *从表中获取所有列。 + +好处:减少网络传输的数据量,降低I/O开销,提高查询性能,同时也有利于代码的清晰性和可维护性。 + +#### 10. 数据库对象命名规范 + +习惯:遵循一致且有意义的命名约定,如使用小写字母、下划线分隔单词,避免使用MySQL保留字,保持表名、列名、索引名等的简洁性和一致性。 + +好处:提高代码可读性,减少命名冲突,便于团队协作和维护。 + +#### 11. 事务管理 + +习惯:对一系列需要保持原子性的操作使用事务管理,确保数据的一致性。 + +好处:在发生异常时能够回滚未完成的操作,避免数据处于不一致状态。 + +#### 12.适时使用索引覆盖 + +习惯:对于只查询索引列且不需要访问数据行的查询(如计数、统计),创建覆盖索引以避免回表操作。 + +好处:极大提升查询性能,减少I/O开销。 + +#### 13.遵循第三范式或适当反范式 + +习惯:根据业务需求和查询模式,合理设计表结构,遵循第三范式以减少数据冗余和更新异常,或适当反范式以优化查询性能。 + +好处:保持数据一致性,减少数据维护成本,或提高查询效率。 + +#### 14.使用预编译语句(PreparedStatement) + +习惯:在应用程序中使用预编译语句(如Java中的PreparedStatement)执行SQL,特别是对于动态拼接SQL语句的情况。 + +好处:避免SQL注入攻击,提高查询性能,减少数据库服务器的解析开销。 + +#### 15.定期分析与优化表和索引 + +习惯:定期运行ANALYZE TABLE收集统计信息,以便MySQL优化器做出更准确的查询计划决策。根据查询性能监控结果,适时调整索引或重构表结构。 + +好处:确保数据库持续高效运行,适应不断变化的业务需求和数据分布。 \ No newline at end of file diff --git a/docs/advance/system-design/README.md b/docs/advance/system-design/README.md index 515b3d5..15ef840 100644 --- a/docs/advance/system-design/README.md +++ b/docs/advance/system-design/README.md @@ -26,8 +26,8 @@ 怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)? -**扫描以下二维码**领取50元的优惠券即可加入。星球定价**218**元,减去**50**元的优惠券,等于说只需要**168**元的价格就可以加入,服务期一年,**每天只要三毛钱**(0.46元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**218**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 -![](http://img.topjavaer.cn/img/202312280808000.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/campus-recruit/share/2-years-tech-upgrade.md b/docs/campus-recruit/share/2-years-tech-upgrade.md index b2aa471..5322d98 100644 --- a/docs/campus-recruit/share/2-years-tech-upgrade.md +++ b/docs/campus-recruit/share/2-years-tech-upgrade.md @@ -58,4 +58,4 @@ head: **加入方式**:**扫描二维码**领取优惠券即可加入~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/career-plan/3-years-reflect.md b/docs/career-plan/3-years-reflect.md index 8c4c12c..21e4cfc 100644 --- a/docs/career-plan/3-years-reflect.md +++ b/docs/career-plan/3-years-reflect.md @@ -95,5 +95,5 @@ head: **加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/career-plan/how-to-prepare-job-hopping.md b/docs/career-plan/how-to-prepare-job-hopping.md index 0dea290..59aee75 100644 --- a/docs/career-plan/how-to-prepare-job-hopping.md +++ b/docs/career-plan/how-to-prepare-job-hopping.md @@ -85,4 +85,4 @@ head: **加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/career-plan/java-or-bigdata.md b/docs/career-plan/java-or-bigdata.md index d3bbf32..cf98ec1 100644 --- a/docs/career-plan/java-or-bigdata.md +++ b/docs/career-plan/java-or-bigdata.md @@ -59,4 +59,4 @@ head: **加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/computer-basic/network.md b/docs/computer-basic/network.md index c407f6d..831488a 100644 --- a/docs/computer-basic/network.md +++ b/docs/computer-basic/network.md @@ -41,8 +41,8 @@ head: 怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)? -**扫描以下二维码**领取50元的优惠券即可加入。星球定价**218**元,减去**50**元的优惠券,等于说只需要**168**元的价格就可以加入,服务期一年,**每天只要三毛钱**(0.46元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**218**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 -![](http://img.topjavaer.cn/img/202304212233017.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/database/mysql.md b/docs/database/mysql.md index b7946d9..c8b0032 100644 --- a/docs/database/mysql.md +++ b/docs/database/mysql.md @@ -22,6 +22,8 @@ head: ## 更新记录 +- 2024.06.05,更新[MySQL查询 limit 1000,10 和limit 10 速度一样快吗?](###MySQL查询 limit 1000,10 和limit 10 速度一样快吗?) + - 2024.5.15,新增[B树和B+树的区别?](###B树和B+树的区别?) ## 什么是MySQL diff --git "a/docs/database/\344\270\200\346\235\241 SQL \346\237\245\350\257\242\350\257\255\345\217\245\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" "b/docs/database/\344\270\200\346\235\241 SQL \346\237\245\350\257\242\350\257\255\345\217\245\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" new file mode 100644 index 0000000..94d82b5 --- /dev/null +++ "b/docs/database/\344\270\200\346\235\241 SQL \346\237\245\350\257\242\350\257\255\345\217\245\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" @@ -0,0 +1,67 @@ +一条 SQL 查询语句如何执行的 + +在平常的开发中,可能很多人都是 CRUD,对 SQL 语句的语法很熟练,但是说起一条 SQL 语句在 MySQL 中是怎么执行的却浑然不知,今天大彬就由浅入深,带大家一点点剖析一条 SQL 语句在 MySQL 中是怎么执行的。 + +比如你执行下面这个 SQL 语句时,我们看到的只是输入一条语句,返回一个结果,却不知道 MySQL 内部的执行过程: + +```mysql +mysql> select * from T where ID=10; +``` + +在剖析这个语句怎么执行之前,我们先看一下 MySQL 的基本架构示意图,能更清楚的看到 SQL 语句在 MySQL 的各个功能模块中的执行过程。 + +![](http://img.topjavaer.cn/img/202406030841887.png) + +整体来说,MySQL 可以分为 Server 层和存储引擎两部分。 + +Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 + +### 连接器 + +如果要操作 MySQL 数据库,我们必须使用 MySQL 客户端来连接 MySQL 服务器,这时候就是服务器中的连接器来负责根客户端建立连接、获取权限、维持和管理连接。 + +在和服务端完成 TCP 连接后,连接器就要认证身份,需要用到用户名和密码,确保用户有足够的权限执行该SQL语句。 + +### 查询缓存 + +建立完连接后,就可以执行查询语句了,来到第二步:查询缓存。 + +MySQL 拿到第一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中,如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。 + +如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。 + +### 分析器 + +如果没有命中缓存,就要开始真正执行语句了,MySQL 首先会对 SQL 语句做解析。 + +分析器会先做 “词法分析”,MySQL 需要识别出 SQL 里面的字符串分别是什么,代表什么。 + +做完之后就要做“语法分析”,根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。若果语句不对,就会收到错误提醒。 + +### 优化器 + +经过了分析器,MySQL 就知道要做什么了,但是在开始执行之前,要先经过优化器的处理。 + +比如:优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。 + +MySQL 会帮我去使用他自己认为的最好的方式去优化这条 SQL 语句,并生成一条条的执行计划,比如你创建了多个索引,MySQL 会依据**成本最小原则**来选择使用对应的索引,这里的成本主要包括两个方面, IO 成本和 CPU 成本。 + +### 执行器 + +执行优化之后的执行计划,在开始执行之前,先判断一下用户对这个表有没有执行查询的权限,如果没有,就会返回没有权限的错误;如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。 + +### 存储引擎 + +执行器将查询请求发送给存储引擎组件。 + +存储引擎组件负责具体的数据存储、检索和修改操作。 + +存储引擎根据执行器的请求,从磁盘或内存中读取或写入相关数据。 + +### 返回结果 + +存储引擎将查询结果返回给执行器。 + +执行器将结果返回给连接器。 + +最后,连接器将结果发送回客户端,完成整个执行过程。 \ No newline at end of file diff --git a/docs/framework/springboot/springboot-cross-domain.md b/docs/framework/springboot/springboot-cross-domain.md index fa7dac3..b93a909 100644 --- a/docs/framework/springboot/springboot-cross-domain.md +++ b/docs/framework/springboot/springboot-cross-domain.md @@ -90,4 +90,4 @@ public class AccountController { 6、打卡学习,**大学自习室的氛围**,一起蜕变成长 -![](http://img.topjavaer.cn/img/星球优惠券-学习网站.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/framework/springmvc.md b/docs/framework/springmvc.md index f0bc2ae..6bf2369 100644 --- a/docs/framework/springmvc.md +++ b/docs/framework/springmvc.md @@ -43,8 +43,8 @@ head: 怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)? -**扫描以下二维码**领取50元的优惠券即可加入。星球定价**218**元,减去**50**元的优惠券,等于说只需要**168**元的价格就可以加入,服务期一年,**每天只要三毛钱**(0.46元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**218**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 -![](http://img.topjavaer.cn/img/202304212233017.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/java/java-concurrent.md b/docs/java/java-concurrent.md index e1c611a..09c408f 100644 --- a/docs/java/java-concurrent.md +++ b/docs/java/java-concurrent.md @@ -22,7 +22,9 @@ head: ## 线程池 -线程池:一个管理线程的池子。 +### 什么是线程池,如何使用?为什么要使用线程池? + +线程池就是事先将多个线程对象放到一个容器中,使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高了代码执行效率。 ### 为什么平时都是使用线程池创建线程,直接new一个线程不好吗? @@ -35,7 +37,7 @@ head: 系统资源有限,每个人针对不同业务都可以手动创建线程,并且创建线程没有统一标准,比如创建的线程有没有名字等。当系统运行起来,所有线程都在抢占资源,毫无规则,混乱场面可想而知,不好管控。 -**频繁手动创建线程为什么开销会大?跟new Object() 有什么差别?** +### 频繁手动创建线程为什么开销会大?跟new Object() 有什么差别? 虽然Java中万物皆对象,但是new Thread() 创建一个线程和 new Object()还是有区别的。 @@ -733,9 +735,23 @@ class SeasonThreadTask implements Runnable{ ## ThreadLocal +### ThreadLocal是什么 + 线程本地变量。当使用`ThreadLocal`维护变量时,`ThreadLocal`为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。 -### ThreadLocal原理 +### 为什么要使用ThreadLocal? + +并发场景下,会存在多个线程同时修改一个共享变量的场景。这就可能会出现**线性安全问题**。 + +为了解决线性安全问题,可以用加锁的方式,比如使用`synchronized` 或者`Lock`。但是加锁的方式,可能会导致系统变慢。 + +还有另外一种方案,就是使用空间换时间的方式,即使用`ThreadLocal`。使用`ThreadLocal`类访问共享变量时,会在每个线程的本地,都保存一份共享变量的拷贝副本。多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线性安全。 + +### Thread和ThreadLocal有什么联系呢? + +Thread和ThreadLocal是绑定的, ThreadLocal依赖于Thread去执行, Thread将需要隔离的数据存放到ThreadLocal(准确的讲是ThreadLocalMap)中,来实现多线程处理。 + +### 说说ThreadLocal的原理? 每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。 diff --git a/docs/java/jvm.md b/docs/java/jvm.md index 9120ef0..5400aac 100644 --- a/docs/java/jvm.md +++ b/docs/java/jvm.md @@ -26,8 +26,8 @@ 怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)? -**扫描以下二维码**领取50元的优惠券即可加入。星球定价**218**元,减去**50**元的优惠券,等于说只需要**168**元的价格就可以加入,服务期一年,**每天只要三毛钱**(0.46元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**218**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 -![](http://img.topjavaer.cn/img/202312280805419.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/other/site-diary.md b/docs/other/site-diary.md index 426ce57..477801a 100644 --- a/docs/other/site-diary.md +++ b/docs/other/site-diary.md @@ -8,6 +8,14 @@ sidebar: heading ## 更新记录 +- 2024.06.11,更新-Redis大key怎么处理? + +- 2024.06.11,新增-聊聊如何用Redis 实现分布式锁? + +- 2024.06.07,更新-为什么Redis单线程还这么快? + +- 2024.05.21,新增一条SQL是怎么执行的 + - 2023.12.28,[增加源码解析模块,包含Sprign/SpringMVC/MyBatis(更新中)](/source/mybatis/1-overview.html)。 - 2023.12.28,[遭受黑客攻击,植入木马程序](/zsxq/article/site-hack.html)。 diff --git a/docs/redis/redis.md b/docs/redis/redis.md index 0f2a11c..0890449 100644 --- a/docs/redis/redis.md +++ b/docs/redis/redis.md @@ -824,6 +824,95 @@ Redis 的哈希表使用链地址法(separate chaining)来解决键冲突: 原理跟 Java 的 HashMap 类似,都是数组+链表的结构。当发生 hash 碰撞时将会把元素追加到链表上。 +## Redis实现分布式锁有哪些方案? + +在这里分享六种Redis分布式锁的正确使用方式,由易到难。 + +方案一:SETNX+EXPIRE + +方案二:SETNX+value值(系统时间+过期时间) + +方案三:使用Lua脚本(包含SETNX+EXPIRE两条指令) + +方案四::ET的扩展命令(SETEXPXNX) + +方案五:开源框架~Redisson + +方案六:多机实现的分布式锁Redlock + +**首先什么是分布式锁**? + +分布式锁是一种机制,用于确保在分布式系统中,多个节点在同一时刻只能有一个节点对共享资源进行操作。它是解决分布式环境下并发控制和数据一致性问题的关键技术之一。 + +分布式锁的特征: + +1、「互斥性」:任意时刻,只有一个客户端能持有锁。 + +2、「锁超时释放」:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。 + +3、「可重入性」“一个线程如果获取了锁之后,可以再次对其请求加锁。 + +4、「安全性」:锁只能被持有的客户端删除,不能被其他客户端删除 + +5、「高性能和高可用」:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。 + + + +**Redis分布式锁方案一:SETNX+EXPIRE** + +提到Redis的分布式锁,很多朋友可能就会想到setnx+expire命令。即先用setnx来抢锁,如果抢到之后,再用expire给锁设置一个过期时间,防止锁忘记了释放。SETNX是SETIF NOT EXISTS的简写。日常命令格式是SETNXkey value,如果 key不存在,则SETNX成功返回1,如果这个key已经存在了,则返回0。假设某电商网站的某商品做秒杀活动,key可以设置为key_resource_id,value设置任意值,伪代码如下: + +![img](https://cdn.nlark.com/yuque/0/2024/png/28848830/1718076327854-c75a4b72-4a8a-4afb-87fe-378082b36046.png) + +缺陷:加锁与设置过期时间是非原子操作,如果加锁后未来得及设置过期时间系统异常等,会导致其他线程永远获取不到锁。 + +**Redis分布式锁方案二**:SETNX+value值(系统时间+过期时间) + +为了解决方案一,「发生异常锁得不到释放的场景」,有小伙伴认为,可以把过期时间放到setnx的value值里面。如果加锁失败,再拿出value值校验一下即可。 + +这个方案的优点是,避免了expire 单独设置过期时间的操作,把「过期时间放到setnx的value值」里面来。解决了方案一发生异常,锁得不到释放的问题。 + +但是这个方案有别的缺点:过期时间是客户端自己生成的(System.currentTimeMillis()是当前系统的时间),必须要求分布式环境下,每个客户端的时间必须同步。如果锁过期的时候,并发多个客户端同时请求过来,都执行jedis.get()和set(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。该锁没有保存持有者的唯一标识,可能坡别的客户端释放/解锁 + +**分布式锁方案三:使用Lua脚本(包含SETNX+EXPIRE两条指令)** + +实际上,我们还可以使用Lua脚本来保证原子性(包含setnx和expire两条指令),lua脚本如下: + +![img](https://cdn.nlark.com/yuque/0/2024/png/28848830/1718075869527-a3704805-53a6-4bd4-be07-2558cff533a2.png) + +加锁代码如下: + +![img](https://cdn.nlark.com/yuque/0/2024/png/28848830/1718075859795-a0cfcfe0-7c56-49ac-9182-6b203739a99e.png) + +**Redis分布式锁方案四:SET的扩展命令(SET EX PX NX)** + +除了使用,使用Lua脚本,保证SETNX+EXPIRE两条指令的原子性,我们还可以巧用Redis的SET指令扩展参数。(`SET key value[EX seconds]`PX milliseconds][NX|XX]`),它也是原子性的 + +`SET key value[EX seconds][PX milliseconds][NX|XX]` + +1. NX:表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁, 才能获取。 +2. EXseconds:设定key的过期时间,时间单位是秒。 +3. PX milliseconds:设定key的过期时间,单位为毫秒。 +4. XX:仅当key存在时设置值。 + +伪代码如下: + +![img](https://cdn.nlark.com/yuque/0/2024/png/28848830/1718075985907-86dd8066-001a-4957-a998-897cdc27c831.png) + +**Redis分布式锁方案五:Redisson框架** + +方案四还是可能存在「锁过期释放,业务没执行完」的问题。设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。当前开源框架Redisson解决了这个问题。一起来看下Redisson底层原理图: + +![img](https://cdn.nlark.com/yuque/0/2024/png/28848830/1718076061807-8b2419dd-13ff-441e-a238-30bf402b07fb.png) + +只要线程一加锁成功,就会启动一个watchdog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了「锁过期释放,业务没执行完」问题。 + +**分布式锁方案六:多机实现的分布式锁Redlock+Redisson** + +前面五种方案都是基于单机版的讨论,那么集群部署该怎么处理? + +答案是多机实现的分布式锁Redlock+Redisson + ![](http://img.topjavaer.cn/img/20220612101342.png) diff --git a/docs/system-design/README.md b/docs/system-design/README.md index 4ea61d7..dac1d6d 100644 --- a/docs/system-design/README.md +++ b/docs/system-design/README.md @@ -24,4 +24,4 @@ [知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**: -![](http://img.topjavaer.cn/img/202312280805419.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/inner-material.md b/docs/zsxq/inner-material.md index 536c880..613a61e 100644 --- a/docs/zsxq/inner-material.md +++ b/docs/zsxq/inner-material.md @@ -22,4 +22,4 @@ [知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**: -![](http://img.topjavaer.cn/img/202312280805419.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/introduce.md b/docs/zsxq/introduce.md index 7e2d967..a72a60d 100644 --- a/docs/zsxq/introduce.md +++ b/docs/zsxq/introduce.md @@ -73,7 +73,7 @@ APP端页面如下(建议大家**使用APP**,因为APP布局更加美观, 如果你加入了,希望你也能跟像球友们一样**每天坚持打卡学习,为未来奋斗**~ -![](http://img.topjavaer.cn/img/202312280800515.png) +![](http://img.topjavaer.cn/img/202412271108286.png) ## 学习圈能提供什么? @@ -205,10 +205,10 @@ APP端页面如下(建议大家**使用APP**,因为APP布局更加美观, ## 怎么进入星球? -如果你下定决心要加入的话,可以直接扫下面这个二维码。星球定价**218**元,减去**50**元的优惠券,等于说只需要**168**元(**拒绝割韭菜**)的价格就可以加入,服务期一年,**每天只要三毛钱**(0.46元),相比培训班几万块的学费,非常值了,星球提供的服务**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**218**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 -PS:如果加入学习圈之后觉得不合适,**支持3天内全额退款**~ +PS:如果加入学习圈之后觉得不合适,**支持3天内全额退款** -![](http://img.topjavaer.cn/img/202312280801529.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/mianshishouce.md b/docs/zsxq/mianshishouce.md index 287488c..24d1368 100644 --- a/docs/zsxq/mianshishouce.md +++ b/docs/zsxq/mianshishouce.md @@ -76,10 +76,10 @@ ## 怎么进入星球? -如果你下定决心要加入的话,可以直接扫下面这个二维码。星球定价**158**元,减去**50**元的优惠券,等于说只需要**108**元(**拒绝割韭菜**)的价格就可以加入,服务期一年,**每天不到三毛钱**(0.29元),相比培训班几万块的学费,非常值了,星球提供的服务**远超**门票价格了。 +**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。 -随着星球内容不断积累,星球定价也会不断**上涨**,所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 +随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。 PS:如果加入学习圈之后觉得不合适,**支持3天内全额退款**~ -![](http://img.topjavaer.cn/img/202304212233017.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/question/2-years-tech-no-upgrade.md b/docs/zsxq/question/2-years-tech-no-upgrade.md index 3bf789f..b4eccf3 100644 --- a/docs/zsxq/question/2-years-tech-no-upgrade.md +++ b/docs/zsxq/question/2-years-tech-no-upgrade.md @@ -58,4 +58,4 @@ head: **加入方式**:**扫描二维码**领取优惠券即可加入~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/question/3-years-confusion.md b/docs/zsxq/question/3-years-confusion.md index 7fde282..1888eda 100644 --- a/docs/zsxq/question/3-years-confusion.md +++ b/docs/zsxq/question/3-years-confusion.md @@ -89,5 +89,5 @@ head: 6、打卡学习,**大学自习室的氛围**,一起蜕变成长 -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/zsxq/question/frontend-or-backend.md b/docs/zsxq/question/frontend-or-backend.md index b33ccf4..35e6f0c 100644 --- a/docs/zsxq/question/frontend-or-backend.md +++ b/docs/zsxq/question/frontend-or-backend.md @@ -31,4 +31,4 @@ 最后,给大家送福利啦,限时发放10张[知识星球](https://mp.weixin.qq.com/s/6eAOmYiNhEjIvhhXoXm9QQ)60元的优惠券,先到先得!目前[知识星球](https://mp.weixin.qq.com/s/6eAOmYiNhEjIvhhXoXm9QQ)已经有**300**多位成员了,想加入的小伙伴不要错过这一波优惠活动,**扫描下方二维码**领取优惠券即可加入。 -![](http://img.topjavaer.cn/img/星球优惠券0317-减60.png) \ No newline at end of file +![](http://img.topjavaer.cn/img/202412271108286.png) \ No newline at end of file diff --git a/docs/zsxq/question/how-to-prepare-job-hopping.md b/docs/zsxq/question/how-to-prepare-job-hopping.md index 0dea290..59aee75 100644 --- a/docs/zsxq/question/how-to-prepare-job-hopping.md +++ b/docs/zsxq/question/how-to-prepare-job-hopping.md @@ -85,4 +85,4 @@ head: **加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git a/docs/zsxq/question/java-or-bigdata.md b/docs/zsxq/question/java-or-bigdata.md index d3bbf32..cf98ec1 100644 --- a/docs/zsxq/question/java-or-bigdata.md +++ b/docs/zsxq/question/java-or-bigdata.md @@ -59,4 +59,4 @@ head: **加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~ -![](http://img.topjavaer.cn/img/星球优惠券-b站.png) +![](http://img.topjavaer.cn/img/202412271108286.png) diff --git "a/docs/zsxq/\347\272\277\344\270\212CPU\351\243\231\345\215\207100%\351\227\256\351\242\230\346\216\222\346\237\245.md" "b/docs/zsxq/\347\272\277\344\270\212CPU\351\243\231\345\215\207100%\351\227\256\351\242\230\346\216\222\346\237\245.md" new file mode 100644 index 0000000..57a099a --- /dev/null +++ "b/docs/zsxq/\347\272\277\344\270\212CPU\351\243\231\345\215\207100%\351\227\256\351\242\230\346\216\222\346\237\245.md" @@ -0,0 +1,95 @@ +线上CPU飙升100%问题排查 + +对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),特此整理排查方法一篇,供大家参考讨论提高。 + +## 二、问题复现 + +线上系统突然运行缓慢,CPU飙升,甚至到100%,以及Full GC次数过多,接着就是各种报警:例如接口超时报警等。此时急需快速线上排查问题。 + +## 三、问题排查 + +不管什么问题,既然是CPU飙升,肯定是查一下耗CPU的线程,然后看看GC。 + +### 3.1 核心排查步骤 + +1.执行“top”命令``:查看所有进程占系统CPU的排序。极大可能排第一个的就是咱们的java进程(COMMAND列)。PID那一列就是进程号。`` + +2.执行“top -Hp 进程号”命令:查看java进程下的所有线程占CPU的情况。 + +3.执行“printf "%x\n 10"命令 :后续查看线程堆栈信息展示的都是十六进制,为了找到咱们的线程堆栈信息,咱们需要把线程号转成16进制。例如,printf "%x\n 10-》打印:a,那么在jstack中线程号就是0xa. + +4.执行 “jstack 进程号 | grep 线程ID” 查找某进程下-》线程ID(jstack堆栈信息中的nid)=0xa的线程状态。如果“"VM Thread" os_prio=0 tid=0x00007f871806e000 nid=0xa runnable”,第一个双引号圈起来的就是线程名,如果是“VM Thread”这就是虚拟机GC回收线程了 + +5.执行“jstat -gcutil 进程号 统计间隔毫秒 统计次数(缺省代表一致统计)”,查看某进程GC持续变化情况,如果发现返回中FGC很大且一直增大-》确认Full GC! 也可以使用“jmap -heap 进程ID”查看一下进程的堆内从是不是要溢出了,特别是老年代内从使用情况一般是达到阈值(具体看垃圾回收器和启动时配置的阈值)就会进程Full GC。 + +6.执行“jmap -dump:format=b,file=filename 进程ID”,导出某进程下内存heap输出到文件中。可以通过eclipse的mat工具查看内存中有哪些对象比较多 + +### 3.2 原因分析 + +#### 1.内存消耗过大,导致Full GC次数过多 + +执行步骤1-5: + +- 多个线程的CPU都超过了100%,通过jstack命令可以看到这些线程主要是垃圾回收线程-》上一节步骤2 +- 通过jstat命令监控GC情况,可以看到Full GC次数非常多,并且次数在不断增加。--》上一节步骤5 + +确定是Full GC,接下来找到**具体原因**: + +- 生成大量的对象,导致内存溢出-》执行步骤6,查看具体内存对象占用情况。 +- 内存占用不高,但是Full GC次数还是比较多,此时可能是代码中手动调用 System.gc()导致GC次数过多,这可以通过添加 -XX:+DisableExplicitGC来禁用JVM对显示GC的响应。 + +#### 2.代码中有大量消耗CPU的操作,导致CPU过高,系统运行缓慢; + +执行步骤1-4:在步骤4jstack,可直接定位到代码行。例如某些复杂算法,甚至算法BUG,无限循环递归等等。 + +#### 3.由于锁使用不当,导致死锁。 + +执行步骤1-4: 如果有死锁,会直接提示。关键字:deadlock.步骤四,会打印出业务死锁的位置。 + +造成死锁的原因:最典型的就是2个线程互相等待对方持有的锁。 + +#### 4.随机出现大量线程访问接口缓慢。 + +代码某个位置有阻塞性的操作,导致该功能调用整体比较耗时,但出现是比较随机的;平时消耗的CPU不多,而且占用的内存也不高。 + +思路: + +首先找到该接口,通过压测工具不断加大访问力度,大量线程将阻塞于该阻塞点。 + +执行步骤1-4: + +``` +"http-nio-8080-exec-4" #31 daemon prio=5 os_prio=31 tid=0x00007fd08d0fa000 nid=0x6403 waiting on condition [0x00007000033db000] + + java.lang.Thread.State: TIMED_WAITING (sleeping)-》期限等待 + + at java.lang.Thread.sleep(Native Method) + + at java.lang.Thread.sleep(Thread.java:340) + + at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) + + at com.*.user.controller.UserController.detail(UserController.java:18)-》业务代码阻塞点 +``` + +如上图,找到业务代码阻塞点,这里业务代码使用了TimeUnit.sleep()方法,使线程进入了TIMED_WAITING(期限等待)状态。 + +#### 5.某个线程由于某种原因而进入WAITING状态,此时该功能整体不可用,但是无法复现; + +执行步骤1-4:jstack多查询几次,每次间隔30秒,对比一直停留在parking 导致的WAITING状态的线程。例如CountDownLatch倒计时器,使得相关线程等待->AQS->LockSupport.park()。 + +``` +"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9de08c7000 nid=0x5603 waiting on condition [0x0000700001f89000] +java.lang.Thread.State: WAITING (parking) ->无期限等待 +at sun.misc.Unsafe.park(Native Method) +at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304) +at com.*.SyncTask.lambda$main$0(SyncTask.java:8)-》业务代码阻塞点 +at com.*.SyncTask$$Lambda$1/1791741888.run(Unknown Source) +at java.lang.Thread.run(Thread.java:748) +``` + + + +## 四、总结 + +按照3.1节的6个步骤走下来,基本都能找到问题所在。 \ No newline at end of file