diff --git a/.gitignore b/.gitignore
index 0182294..0e9b3e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
node_modules/
*.bat
*.sh
-web/
.idea/
*.py
public/
diff --git a/README.md b/README.md
index 6a2e1e4..77dfbb0 100644
--- a/README.md
+++ b/README.md
@@ -63,11 +63,14 @@
# 精选资源
-- [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)
-- [谷歌师兄刷题笔记](https://t.1yb.co/A6id)(推荐 :+1:)
-- [BAT大佬总结的刷题手册](https://t.1yb.co/yMbo)(推荐 :+1:)
-- [Java优质项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
-- [优质视频教程推荐](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd)(推荐 :+1:)
+- [200多本经典的计算机书籍,收藏吧](https://github.com/Tyson0314/java-books)
+- [谷歌师兄刷题笔记,支持Java、C++、Go三种语言!](https://t.1yb.co/A6id)(推荐 :+1:)
+- [刷题必备!BAT大佬总结的刷题手册!](https://t.1yb.co/yMbo)(推荐 :+1:)
+- [Github 上爆火的各种硬核技术学习路线思维导图 :star:](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494513&idx=1&sn=de1a7cf0b5580840cb8ad4a96e618866&chksm=ce9b1637f9ec9f212d054018598b96b5277f7733fac8f985d8dae0074c8446a2cad8e43ba739#rd)
+- [图解操作系统、网络、计算机组成PDF下载!那些让你起飞的计算机基础知识 :star:](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494510&idx=1&sn=b19d9e07321b8fca9129fe0d8403a426&chksm=ce9b1628f9ec9f3e7d45a6db8389ee2813864a9ca692238d29b139c35ccb01b08155bc2da358#rd)
+- [白嫖真的香!15个Java优质项目](https://www.zhihu.com/question/325011850/answer/2257046656)
+- [免费分享!字节大佬推荐的优质视频教程](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd)(推荐 :+1:)
+- [玩转ChatGPT手册限时免费分享:star:](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494344&idx=1&sn=d16f51e8bd3424f63e4fb6a5aa5ca4db&chksm=ce9b178ef9ec9e9841c7a049e4da0843c291b96f463e87190a6bf344c7022194ee393b695751#rd)
# 经验分享
@@ -76,6 +79,13 @@
- [对于java开发和大数据开发,24年秋招的话选择哪个方向会比较合适呢?](https://topjavaer.cn/career-plan/java-or-bigdata.html)
- [四年程序员生涯的反思](https://topjavaer.cn/career-plan/4-years-reflect.html)
- [在国企做开发,是什么样的体验](https://topjavaer.cn/career-plan/guoqi-programmer.html)
+- [工作两年多,技术水平没有很大提升,该怎么办](https://topjavaer.cn/zsxq/question/2-years-tech-no-upgrade.html)
+- [24届校招,Java开发和大数据开发怎么选](https://topjavaer.cn/zsxq/question/java-or-bigdata.html)
+- [新人如何快速的熟悉新项目](https://topjavaer.cn/zsxq/question/familiarize-new-project-qucikly.html)
+
+# 副业指南
+
+- [一些接单平台](https://topjavaer.cn/zsxq/article/sideline-guide.html)
# 面试前准备
@@ -93,6 +103,7 @@
- [Java集合高频面试题](https://topjavaer.cn/java/java-collection.html)(推荐 :+1:)
- [Java并发高频面试题](https://topjavaer.cn/java/java-concurrent.html) (推荐 :+1:)
- [JVM高频面试题](https://topjavaer.cn/java/jvm.html)(推荐 :+1:)
+- [Tomcat基础知识点总结](https://topjavaer.cn/web/tomcat.html)
**Java重要知识点**
@@ -102,6 +113,8 @@
- [泛型中的T、E、K、V,是什么含义?](https://topjavaer.cn/advance/excellent-article/24-generic.html)
- [面试官:反射是如何影响性能的?](https://topjavaer.cn/java/basic/reflect-affect-permance.html)
- [面试官:详细说说你对序列化的理解?](https://topjavaer.cn/java/basic/serialization.html)
+- [感受 lambda 之美](https://mp.weixin.qq.com/s/xwvdtWdFbvmUYaRAAkIhvA)
+- [try-catch 捕获异常会影响性能吗?](https://mp.weixin.qq.com/s/iZAB3XzBCoKaJMW6X2jmzA)
**JVM重要知识点**
@@ -109,12 +122,21 @@
- [一次简单的JVM调优,拿去写到简历里](https://topjavaer.cn/advance/excellent-article/5-jvm-optimize.html)
- [阿里排错神器--Arthas](https://topjavaer.cn/advance/excellent-article/23-arthas-intro.html)
- [Java堆内存是线程共享的?](https://topjavaer.cn/java/jvm/jvm-heap-memory-share.html)
+- [面试官:你工作中做过 JVM 调优吗?怎么做的?](https://mp.weixin.qq.com/s/mwZ5qiBt-xlxy8N3Ya2SvQ)
+- [JVM调优几款好用的内存分析工具](https://mp.weixin.qq.com/s/bSgNk6roybRp2buCvwfomw)
+
+**Java并发重要知识点**
+
+- [说一说多线程常见锁的策略](https://mp.weixin.qq.com/s/t0SK4fMF7D_zY_1zlTz6jA)
+- [8 种异步实现方式](https://mp.weixin.qq.com/s/2lgGj878MQoD-siZvG473g)
+- [CompletableFuture 异步多线程](https://mp.weixin.qq.com/s/gRnXInZznCyZE0ZQ9ByfIg)
# 数据库
## MySQL
- [MySQL高频面试题50道](https://topjavaer.cn/database/mysql.html)(**知乎1k+收藏,推荐** :+1:)
+- [MySQL锁高频面试题](https://topjavaer.cn/database/mysql-lock.html)
**重要知识点**:
@@ -125,6 +147,14 @@
- [8种最坑SQL语法](https://topjavaer.cn/advance/excellent-article/7-sql-optimize.html)
- [为什么说数据库连接很消耗资源](https://topjavaer.cn/advance/excellent-article/18-db-connect-resource.html)
- [SELECT COUNT(*) 会造成全表扫描?](https://topjavaer.cn/advance/excellent-article/25-select-count-slow-query.html)
+- [MySQL中的 distinct 和 group by 哪个效率更高?](https://mp.weixin.qq.com/s/jPUjKl81Es3bbtGoqdVDxg)
+- [MySQL慢查询之慢 SQL 定位、日志分析与优化方案](https://mp.weixin.qq.com/s/XpEfv0M_ArMa69fnXugWig)
+- [MySQL 上亿大表如何优化?](https://mp.weixin.qq.com/s/YSlhVJYp9AhR_UZEJKH1Vg)
+- [字节一面:select......for update会锁表还是锁行?](https://mp.weixin.qq.com/s/FW6y8UXVDODG2ViiiWKfYQ)
+- [面试官:从 MySQL 读取 100w 数据进行处理,应该怎么做?](https://mp.weixin.qq.com/s/a8vgtTvdgAU6E9xOfm18nw)
+- [面试官:int(1) 和 int(10) 有什么区别?](https://mp.weixin.qq.com/s/0P1R2JqTWuPvmqEA2ttj_w)
+- [1000万的数据,怎么查询?](https://mp.weixin.qq.com/s/WJmwxDGg6fOfV6hJ300Diw)
+- [新同事竟然不懂 where 1=1 是什么意思?](https://mp.weixin.qq.com/s/DjocMG-lE4Swsq2gTvl_7g)
## Redis
@@ -137,11 +167,17 @@
- [为什么Redis 6.0 引入多线程](https://topjavaer.cn/redis/article/redis-multi-thread.html)
- [缓存和数据库一致性问题,看这篇就够了](https://topjavaer.cn/redis/article/cache-db-consistency.html)
- [Redis 集群模式的工作原理](https://topjavaer.cn/redis/article/redis-cluster-work.html)
+- [面试官问:你们项目中用Redis来干什么?](https://mp.weixin.qq.com/s/eAKajEByV1P4eOF9J1hiNA)
+- [MySQL和Redis如何保持数据一致性?](https://mp.weixin.qq.com/s/0t3ZZpwwczFfrgbbvrTJRw)
## ElasticSearch
- [ElasticSearch高频面试题](https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg)
+## MongoDB
+
+- [MongoDB高频面试题](https://topjavaer.cn/database/mongodb.html)
+
# 框架
## Spring
@@ -153,6 +189,7 @@
- [Spring为何需要三级缓存解决循环依赖,而不是二级缓存?](https://topjavaer.cn/advance/excellent-article/6-spring-three-cache.html)
- [@Transactional事务注解详解](https://topjavaer.cn/advance/excellent-article/2-spring-transaction.html)
- [一文彻底搞懂Spring事务传播行为](https://topjavaer.cn/framework/spring/transaction-propagation.html)
+- [15个Spring扩展点](https://mp.weixin.qq.com/s/q2ZLXxAM0AC7sDlq6kDA4Q)
## Spring Boot
@@ -164,6 +201,7 @@
- [SpringBoot自动装配原理](https://topjavaer.cn/advance/excellent-article/3-springboot-auto-assembly.html)
- [SpringBoot如何解决跨域问题](https://topjavaer.cn/framework/springboot/springboot-cross-domain.html)
+- [SpringBoot项目启动优化实践](https://mp.weixin.qq.com/s/-WtrN3jD8pVXTHQ-kpwqQA)
- [SpringBoot实现电子文件签字+合同系统](https://topjavaer.cn/framework/springboot/springboot-contract.html)
## Spring MVC
@@ -182,13 +220,19 @@
[SpringCloud总结](https://topjavaer.cn/framework/springcloud-overview.html)
+## Zookeeper
+
+- [Zookeeper面试题](https://topjavaer.cn/zookeeper/zk.html)
+- [Zookeeper有哪些使用场景?](https://topjavaer.cn/zookeeper/zk-usage.html)
+
## Netty
[Netty实战笔记](https://topjavaer.cn/framework/netty-overview.html)
# 计算机网络
-[计算机网络常见面试题总结](https://topjavaer.cn/computer-basic/network.html) (**知乎1k+收藏!推荐 :+1:**)
+- [计算机网络常见面试题总结](https://topjavaer.cn/computer-basic/network.html) (**知乎1k+收藏!推荐 :+1:**)
+- [TCP常见面试题总结](https://topjavaer.cn/computer-basic/tcp.html)
**重要知识点**:
@@ -220,14 +264,22 @@
- [设计模式之代理模式](https://topjavaer.cn/advance/design-pattern/11-proxy.html)
- [设计模式之建造者模式](https://topjavaer.cn/advance/design-pattern/12-builder.html)
+**设计模式优质文章**
+
+- [代码越写越乱?那是因为你没用责任链](https://mp.weixin.qq.com/s/TpB5bmNwlcJj-XZo5iXXtg)
+
# 分布式
- [微服务面试题](https://topjavaer.cn/advance/distributed/4-micro-service.html)
- [RPC面试题](https://topjavaer.cn/advance/distributed/3-rpc.html)
-- [全局唯一ID](https://topjavaer.cn/advance/distributed/1-global-unique-id.html)
- [分布式事务总结](https://topjavaer.cn/advance/distributed/6-distributed-transaction.html)
+
+**优质文章**:
+
+- [全局唯一ID生成方案](https://topjavaer.cn/advance/distributed/1-global-unique-id.html)
- [分布式架构演进](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
- [新一代分布式任务调度框架](https://topjavaer.cn/advance/excellent-article/22-distributed-scheduled-task.html)
+- [分布式锁怎么实现?](https://topjavaer.cn/distributed/article/distributed-lock.html)
# 高并发
@@ -279,6 +331,7 @@
- [如何设计一个高并发系统?](https://topjavaer.cn/advance/system-design/19-high-concurrent-system-design.html)
- [分库分表平滑迁移](https://topjavaer.cn/advance/system-design/20-sharding-smooth-migration.html)
- [10w级别数据Excel导入优化](https://topjavaer.cn/advance/system-design/21-excel-import.html)
+- [从3s到25ms!看看人家的接口优化技巧](https://mp.weixin.qq.com/s/vDD_FT6re249HlPvgR9TRw)
# 安全
@@ -306,29 +359,31 @@
- [8种架构模式](https://topjavaer.cn/advance/excellent-article/11-8-architect-pattern.html)
- [几种常见的架构模式](https://topjavaer.cn/advance/excellent-article/20-architect-pattern.html)
- [线上接口很慢怎么办?](https://topjavaer.cn/practice/service-performance-optimization.html)
+- [不要再封装各种 Util 工具类了,这个神级框架值得拥有!](https://mp.weixin.qq.com/s/7VuxBrBcXsAoykcJyNRsvQ)
+- [怎样写出优雅的代码?](https://mp.weixin.qq.com/s/ph2pH4O1G_6YScGITaiJwg)
+- [BitMap牛逼在哪里?](https://mp.weixin.qq.com/s/jfRCHHh2D6wMAeyD7XLKxg)
+- [什么是雪花算法?啥原理?附 Java 实现!](https://mp.weixin.qq.com/s/1Kx55x3fYUs9afpeAzIUOg)
# 工具
-[Git 超详细总结!](https://topjavaer.cn/tools/git-overview.html)(推荐 :+1:)
-
-[Linux 常用命令总结!](https://topjavaer.cn/tools/linux-overview.html)
-
-[Docker 基础总结!](https://topjavaer.cn/tools/docker-overview.html)
-
-[Maven 基础总结!](https://topjavaer.cn/tools/maven-overview.html)
-
-[Nginx 高频面试题](https://topjavaer.cn/tools/nginx.html)
+- [Git 高频面试题总结](https://topjavaer.cn/tools/git.html)
+- [Git 超详细总结!](https://topjavaer.cn/tools/git-overview.html)(推荐 :+1:)
+- [Linux 常用命令总结!](https://topjavaer.cn/tools/linux-overview.html)
+- [Docker 基础总结!](https://topjavaer.cn/tools/docker-overview.html)
+- [Maven 基础总结!](https://topjavaer.cn/tools/maven-overview.html)
+- [Nginx 高频面试题](https://topjavaer.cn/tools/nginx.html)
# 交流
如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
-

+
+
# 赞赏
如果觉得**本仓库**对您有帮助的话,可以请大彬**喝一杯咖啡**(小伙伴们赞赏的时候可以备注下哦~)
diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts
index be5e7ac..63869db 100644
--- a/docs/.vuepress/config.ts
+++ b/docs/.vuepress/config.ts
@@ -8,8 +8,8 @@ import { gitPlugin } from '@vuepress/plugin-git'
export default defineUserConfig({
lang: "zh-CN",
- title: "程序员大彬",
- description: "自学转码之路",
+ title: "大彬",
+ description: "Java学习、面试指南,涵盖大部分 Java 程序员所需要掌握的核心知识",
base: "/",
dest: './public',
theme,
@@ -31,7 +31,7 @@ export default defineUserConfig({
["meta", { "http-equiv": "Expires", content: "0" }],
['meta', {name: 'baidu-site-verification', content: 'code-mtJaPDeFwy'}],
// ['meta', { name: 'google-site-verification', content: 'eGgkbT6uJR-WQeSkhhcB6RbnZ2RtF5poPf1ai-Fgmy8' }],
- ['meta', {name: 'keywords', content: 'Java, Spring, Mybatis, SpringMVC, Springboot, 编程, 程序员, MySQL, Redis, 系统设计, 分布式, RPC, 高可用, 高并发'}],
+ ['meta', {name: 'keywords', content: 'Java,Spring,Mybatis,SpringMVC,Springboot,编程,程序员,MySQL,Redis,系统设计,分布式,RPC,高可用,高并发,场景设计,Java面试'}],
[
'script', {}, `
var _hmt = _hmt || [];
diff --git a/docs/.vuepress/navbar.ts b/docs/.vuepress/navbar.ts
index 608e4dc..d2fcdc3 100644
--- a/docs/.vuepress/navbar.ts
+++ b/docs/.vuepress/navbar.ts
@@ -24,132 +24,243 @@ export default navbar([
{
text: "学习圈",
icon: "zsxq",
- link: "/zsxq/introduce.md",
+ link: "/zsxq/introduce.md",
},
{
- text: "Java",
+ text: "面试指南",
icon: "java",
children: [
{
text: "Java",
children: [
- {text: "基础", link: "/java/java-basic.md"},
- {text: "集合", link: "/java/java-collection.md"},
- {text: "并发", link: "/java/java-concurrent.md"},
- {text: "JVM", link: "/java/jvm.md"},
- {text: "Java8", link: "/java/java8"},
+ {text: "基础", link: "/java/java-basic.md", icon: "jihe"},
+ {text: "集合", link: "/java/java-collection.md", icon: "fuwuqi"},
+ {text: "并发", link: "/java/java-concurrent.md", icon: "bingfa"},
+ {text: "JVM", link: "/java/jvm.md", icon: "xuniji"},
+ {text: "Java8", link: "/java/java8", icon: "java"},
+ {text: "Tomcat", link: "/web/tomcat.md", icon: "TOMCAT"},
]
},
{
text: "框架",
children: [
- {text: "Spring面试题", link: "/framework/spring.md"},
- {text: "SpringMVC面试题", link: "/framework/springmvc.md"},
- {text: "Mybatis面试题", link: "/framework/mybatis.md"},
- {text: "SpringBoot面试题", link: "/framework/springboot.md"},
- {text: "SpringCloud详解", link: "/framework/springcloud/"},
- {text: "SpringCloud面试题", link: "/framework/springcloud-interview.md"},
- {text: "Netty详解", link: "/framework/netty/"},
+ {text: "Spring面试题", link: "/framework/spring.md", icon: "bxl-spring-boot"},
+ {text: "SpringMVC面试题", link: "/framework/springmvc.md", icon: "pingtai"},
+ {text: "Mybatis面试题", link: "/framework/mybatis.md", icon: "wendang"},
+ {text: "SpringBoot面试题", link: "/framework/springboot.md", icon: "bxl-spring-boot"},
+ {text: "SpringCloud详解", link: "/framework/springcloud/", icon: "jihe"},
+ {text: "SpringCloud面试题", link: "/framework/springcloud-interview.md", icon: "yun"},
+ {text: "ZooKeeper面试题", link: "/zookeeper/zk.md", icon: "Zookeeper"},
+ {text: "Netty详解", link: "/framework/netty/", icon: "fuwuqi"},
]
},
{
text: "消息队列",
children: [
- {text: "消息队列面试题", link: "/message-queue/mq.md"},
- {text: "RabbitMQ面试题", link: "/message-queue/rabbitmq.md"},
- {text: "Kafka面试题", link: "/message-queue/kafka.md"},
+ {text: "消息队列面试题", link: "/message-queue/mq.md", icon: "xiaoxiduilie"},
+ {text: "RabbitMQ面试题", link: "/message-queue/rabbitmq.md", icon: "amqpxiaoxiduilie"},
+ {text: "Kafka面试题", link: "/message-queue/kafka.md", icon: "Kafka"},
]
- }
- ]
- },
- {
- text: "计算机基础",
- icon: "computer",
- children: [
- {text: "网络", link: "/computer-basic/network.md"},
- {text: "操作系统", link: "/computer-basic/operate-system.md"},
- {text: "算法", link: "/computer-basic/algorithm.md"},
- {text: "LeetCode题解", link: "/leetcode/hot120"},
- {text: "数据结构", link: "/computer-basic/data-structure.md"},
+ },
{
text: "关系型数据库",
children: [
//{text: "MySQL基础", children: ["/database/mysql-basic/"],},
- {text: "MySQL基础", link: "/database/mysql-basic/"},
- {text: "MySQL面试题", link: "/database/mysql.md"},
- {text: "MySQL执行计划详解", link: "/database/mysql-execution-plan.md"},
+ {text: "MySQL基础", link: "/database/mysql-basic/", icon: "jihe"},
+ {text: "MySQL面试题", link: "/database/mysql.md", icon: "mysql"},
+ {text: "MySQL执行计划详解", link: "/database/mysql-execution-plan.md", icon: "chayan"},
]
},
{
text: "非关系型数据库",
children: [
- {text: "Redis基础", link: "/redis/redis-basic/"},
- {text: "Redis面试题", link: "/redis/redis.md"},
- {text: "ElasticSearch面试题", link: "https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg"},
+ {text: "Redis基础", link: "/redis/redis-basic/", icon: "jihe"},
+ {text: "Redis面试题", link: "/redis/redis.md", icon: "Redis"},
+ {text: "MongoDB面试题", link: "/database/mongodb.md", icon: "MongoDB"},
+ {text: "ElasticSearch面试题", link: "https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg", icon: "elastic"},
]
},
- ]
- },
+ {
+ text: "计算机基础",
+ icon: "computer",
+ children: [
+ {text: "网络", link: "/computer-basic/network.md", icon: "wangluo3"},
+ {text: "TCP专题", link: "/computer-basic/tcp.md", icon: "wangluo1"},
+ {text: "操作系统", link: "/computer-basic/operate-system.md", icon: "os"},
+ {text: "算法", link: "/computer-basic/algorithm.md", icon: "suanfa"},
+ {text: "LeetCode题解", link: "/leetcode/hot120", icon: "leetcode"},
+ {text: "数据结构", link: "/computer-basic/data-structure.md", icon: "datastruct"},
+ //{
+ // text: "关系型数据库",
+ // children: [
+ // //{text: "MySQL基础", children: ["/database/mysql-basic/"],},
+ // {text: "MySQL基础", link: "/database/mysql-basic/"},
+ // {text: "MySQL面试题", link: "/database/mysql.md"},
+ // {text: "MySQL执行计划详解", link: "/database/mysql-execution-plan.md"},
+ // ]
+ //},
+ //{
+ // text: "非关系型数据库",
+ // children: [
+ // {text: "Redis基础", link: "/redis/redis-basic/"},
+ // {text: "Redis面试题", link: "/redis/redis.md"},
+ // {text: "ElasticSearch面试题", link: "https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg"},
+ // ]
+ //},
+ ]
+ },
+ ]
+ },
+ //{
+ // text: "计算机基础",
+ // icon: "computer",
+ // children: [
+ // {text: "网络", link: "/computer-basic/network.md"},
+ // {text: "操作系统", link: "/computer-basic/operate-system.md"},
+ // {text: "算法", link: "/computer-basic/algorithm.md"},
+ // {text: "LeetCode题解", link: "/leetcode/hot120"},
+ // {text: "数据结构", link: "/computer-basic/data-structure.md"},
+ // //{
+ // // text: "关系型数据库",
+ // // children: [
+ // // //{text: "MySQL基础", children: ["/database/mysql-basic/"],},
+ // // {text: "MySQL基础", link: "/database/mysql-basic/"},
+ // // {text: "MySQL面试题", link: "/database/mysql.md"},
+ // // {text: "MySQL执行计划详解", link: "/database/mysql-execution-plan.md"},
+ // // ]
+ // //},
+ // //{
+ // // text: "非关系型数据库",
+ // // children: [
+ // // {text: "Redis基础", link: "/redis/redis-basic/"},
+ // // {text: "Redis面试题", link: "/redis/redis.md"},
+ // // {text: "ElasticSearch面试题", link: "https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg"},
+ // // ]
+ // //},
+ // ]
+ //},
{
text: "进阶之路",
icon: "win",
children: [
+ {
+ text: "海量数据",
+ children: [
+ {text: "统计不同号码的个数", link: "/mass-data/1-count-phone-num.md", icon: "phoneno"},
+ {text: "出现频率最高的100个词", link: "/mass-data/2-find-hign-frequency-word.md", icon: "datastruct"},
+ {text: "查找两个大文件共同的URL", link: "/mass-data/3-find-same-url.md", icon: "wenben"},
+ {text: "如何在100亿数据中找到中位数?", link: "/mass-data/4-find-mid-num.md", icon: "bingfa"},
+ {text: "如何查询最热门的查询串?", link: "/mass-data/5-find-hot-string.md", icon: "query"},
+ {text: "如何找出排名前 500 的数?", link: "/mass-data/6-top-500-num.md", icon: "rank"},
+ {text: "如何按照 query 的频度排序?", link: "/mass-data/7-query-frequency-sort.md", icon: "frequency"},
+ {text: "大数据中 TopK 问题的常用套路", link: "/mass-data/8-topk-template.md", icon: "bigdata"},
+ ]
+ },
+ {
+ text: "系统设计",
+ //link: "/advance/system-design/README.md",
+ //children: [
+ // {text: "扫码登录设计", link: "/advance/system-design/1-scan-code-login.md"},
+ // {text: "超时订单自动取消", link: "/advance/system-design/2-order-timeout-auto-cancel.md"},
+ // {text: "短链系统设计", link: "/advance/system-design/3-short-url.md"},
+ // {text: "微信红包系统如何设计?", link: "/advance/system-design/6-wechat-redpacket-design.md"},
+ // {text: "单点登录设计与实现", link: "/advance/system-design/8-sso-design.md"},
+ //]
+ children: [
+ {text: "扫码登录设计", link: "/advance/system-design/1-scan-code-login.md", icon: "scan"},
+ {text: "超时订单自动取消", link: "/advance/system-design/2-order-timeout-auto-cancel.md", icon: "timeout"},
+ {text: "短链系统设计", link: "/advance/system-design/README.md", icon: "lianjie"},
+ {text: "微信红包系统如何设计?", link: "/advance/system-design/README.md", icon: "hongbao"},
+ {text: "单点登录设计与实现", link: "/advance/system-design/README.md", icon: "login"},
+ {text: "如何用 Redis 统计用户访问量?", link: "/advance/system-design/README.md", icon: "visit"},
+ {text: "实时订阅推送设计与实现", link: "/advance/system-design/README.md", icon: "tongzhi"},
+ {text: "如何设计一个抢红包系统", link: "/advance/system-design/README.md", icon: "hongbao1"},
+ {text: "购物车系统怎么设计?", link: "/advance/system-design/README.md", icon: "shopcar"},
+ {text: "如何设计一个注册中心?", link: "/advance/system-design/README.md", icon: "zhuce"},
+ {text: "如何设计一个高并发系统?", link: "/advance/system-design/README.md", icon: "xitong"},
+ {text: "10w级别数据Excel导入怎么优化?", link: "/advance/system-design/README.md", icon: "excel"},
+ ]
+ },
{
text: "分布式",
icon: "distribute",
children: [
- {text: "全局唯一ID", link: "/advance/distributed/1-global-unique-id.md"},
- {text: "分布式锁", link: "/advance/distributed/2-distributed-lock.md"},
- {text: "RPC", link: "/advance/distributed/3-rpc.md"},
- {text: "微服务", link: "/advance/distributed/4-micro-service.md"},
- {text: "分布式架构", link: "/advance/distributed/5-distibuted-arch.md"},
- {text: "分布式事务", link: "/advance/distributed/6-distributed-transaction.md"},
+ {text: "全局唯一ID", link: "/advance/distributed/1-global-unique-id.md", icon: "quanju"},
+ {text: "分布式锁", link: "/advance/distributed/2-distributed-lock.md", icon: "lock"},
+ {text: "RPC", link: "/advance/distributed/3-rpc.md", icon: "call"},
+ {text: "微服务", link: "/advance/distributed/4-micro-service.md", icon: "weifuwu"},
+ {text: "分布式架构", link: "/advance/distributed/5-distibuted-arch.md", icon: "jiagou"},
+ {text: "分布式事务", link: "/advance/distributed/6-distributed-transaction.md", icon: "transaction"},
]
},
{
text: "高并发",
children: [
- {text: "限流", link: "/advance/concurrent/1-current-limiting.md"},
- {text: "负载均衡", link: "/advance/concurrent/2-load-balance.md"},
+ {text: "限流", link: "/advance/concurrent/1-current-limiting.md", icon: "bingfa"},
+ {text: "负载均衡", link: "/advance/concurrent/2-load-balance.md", icon: "balance"},
],
},
{
text: "设计模式",
icon: "win",
children: [
- {text: "设计模式详解", link: "/advance/design-pattern/"},
+ {text: "设计模式详解", link: "/advance/design-pattern/", icon: "design"},
],
},
+ {
+ text: "优质文章",
+ children: [
+ {text: "优质文章汇总", link: "/advance/excellent-article", icon: "wenzhang"},
+ ]
+ },
+ ]
+ },
+
+ {
+ text: "源码解读",
+ icon: "source",
+ children: [
{
- text: "系统设计",
- link: "/advance/system-design/README.md",
- //children: [
- // {text: "扫码登录设计", link: "/advance/system-design/1-scan-code-login.md"},
- // {text: "超时订单自动取消", link: "/advance/system-design/2-order-timeout-auto-cancel.md"},
- // {text: "短链系统设计", link: "/advance/system-design/3-short-url.md"},
- // {text: "微信红包系统如何设计?", link: "/advance/system-design/6-wechat-redpacket-design.md"},
- // {text: "单点登录设计与实现", link: "/advance/system-design/8-sso-design.md"},
- //]
+ text: "Spring",
+ children: [
+ {text: "整体架构", link: "/source/spring/1-architect.md", icon: "book"},
+ {text: "IOC 容器基本实现", link: "/source/spring/2-ioc-overview", icon: "book"},
+ {text: "IOC默认标签解析(上)", link: "/source/spring/3-ioc-tag-parse-1", icon: "book"},
+ {text: "IOC默认标签解析(下)", link: "/source/spring/4-ioc-tag-parse-2", icon: "book"},
+ {text: "IOC之自定义标签解析", link: "/source/spring/5-ioc-tag-custom.md", icon: "book"},
+ {text: "IOC-开启 bean 的加载", link: "/source/spring/6-bean-load", icon: "book"},
+ {text: "IOC之bean创建", link: "/source/spring/7-bean-build", icon: "book"},
+ {text: "IOC属性填充", link: "/source/spring/8-ioc-attribute-fill", icon: "book"},
+ {text: "IOC之循环依赖处理", link: "/source/spring/9-ioc-circular-dependency", icon: "book"},
+ {text: "IOC之bean 的初始化", link: "/source/spring/10-bean-initial", icon: "book"},
+ {text: "ApplicationContext容器refresh过程", link: "/source/spring/11-application-refresh", icon: "book"},
+ {text: "AOP的使用及AOP自定义标签", link: "/source/spring/12-aop-custom-tag", icon: "book"},
+ {text: "创建AOP代理之获取增强器", link: "/source/spring/13-aop-proxy-advisor", icon: "book"},
+ {text: "AOP代理的生成", link: "/source/spring/14-aop-proxy-create", icon: "book"},
+ {text: "AOP目标方法和增强方法的执行", link: "/source/spring/15-aop-advice-create", icon: "book"},
+ {text: "@Transactional注解的声明式事物介绍", link: "/source/spring/16-transactional", icon: "book"},
+ {text: "Spring事务是怎么通过AOP实现的?", link: "/source/spring/17-spring-transaction-aop", icon: "book"},
+ {text: "事务增强器", link: "/source/spring/18-transaction-advice", icon: "book"},
+ {text: "事务的回滚和提交", link: "/source/spring/19-transaction-rollback-commit", icon: "book"},
+ ]
},
- {
- text: "海量数据",
+ {
+ text: "SpringMVC",
children: [
- {text: "统计不同号码的个数", link: "/mass-data/1-count-phone-num.md"},
- {text: "出现频率最高的100个词", link: "/mass-data/2-find-hign-frequency-word.md"},
- {text: "查找两个大文件共同的URL", link: "/mass-data/3-find-same-url.md"},
- {text: "如何在100亿数据中找到中位数?", link: "/mass-data/4-find-mid-num.md"},
- {text: "如何查询最热门的查询串?", link: "/mass-data/5-find-hot-string.md"},
- {text: "如何找出排名前 500 的数?", link: "/mass-data/6-top-500-num.md"},
- {text: "如何按照 query 的频度排序?", link: "/mass-data/7-query-frequency-sort.md"},
- {text: "大数据中 TopK 问题的常用套路", link: "/mass-data/8-topk-template.md"},
+ {text: "文件上传和拦截器", link: "/source/spring-mvc/1-overview", icon: "book"},
+ {text: "导读篇", link: "/source/spring-mvc/2-guide", icon: "book"},
+ {text: "场景分析", link: "/source/spring-mvc/3-scene", icon: "book"},
+ {text: "事务的回滚和提交", link: "/source/spring-mvc/4-fileupload-interceptor", icon: "book"},
]
},
- {
- text: "优质文章",
+ {
+ text: "MyBatis(更新中)",
children: [
- {text: "优质文章汇总", link: "/advance/excellent-article"},
+ {text: "整体架构", link: "/source/mybatis/1-overview", icon: "book"},
+ {text: "反射模块", link: "/source/mybatis/2-reflect", icon: "book"},
]
},
+
]
},
//{
@@ -178,64 +289,82 @@ export default navbar([
{
text: "开发工具",
children: [
- {text: "Git详解", link: "/tools/git/"},
- {text: "Maven详解", link: "/tools/maven/"},
- {text: "Docker详解", link: "/tools/docker/"},
- {text: "Linux常用命令", link: "/tools/linux"},
- {text: "Nginx面试题", link: "https://mp.weixin.qq.com/s/SKKEeYxif0wWJo6n57rd6A"},
+ {text: "Git详解", link: "/tools/git/", icon: "git1"},
+ {text: "Maven详解", link: "/tools/maven/", icon: "jihe"},
+ {text: "Docker详解", link: "/tools/docker/", icon: "docker1"},
+ {text: "Linux常用命令", link: "/tools/linux", icon: "linux"},
+ {text: "Nginx面试题", link: "https://mp.weixin.qq.com/s/SKKEeYxif0wWJo6n57rd6A", icon: "nginx"},
+ ]
+ },
+ {
+ text: "在线工具",
+ children: [
+ {text: "json", link: "https://www.json.cn/"},
+ {text: "base64编解码", link: "https://c.runoob.com/front-end/693/"},
+ {text: "时间戳转换", link: "https://www.beijing-time.org/shijianchuo/"},
+ {text: "unicode转换", link: "https://www.fulimama.com/unicode/"},
+ {text: "正则表达式", link: "https://www.sojson.com/regex/"},
+ {text: "md5加密", link: "https://www.toolkk.com/tools/md5-encrypt"},
+ {text: "流程图工具", link: "https://app.diagrams.net/"},
+ {text: "二维码", link: "https://cli.im/"},
+ {text: "文本比对", link: "https://c.runoob.com/front-end/8006/"},
]
},
{
text: "编程利器",
children: [
- {text: "markdown编辑器", link: "/tools/typora-overview.md"},
+ {text: "markdown编辑器", link: "/tools/typora-overview.md", icon: "markdown"},
]
},
]
},
- // {
- // text: "珍藏资源",
- // icon: "collection",
- // children: [
- // {
- // text: "学习资源",
- // children: [
- // {text: "计算机经典电子书PDF", link: "https://github.com/Tyson0314/java-books"},
- // {text: "Leetcode刷题笔记", link: "/learning-resources/leetcode-note.md"},
- // ]
- // },
- // {
- // text: "学习路线",
- // children: [
- // {text: "Java学习路线", link: "/learning-resources/java-learn-guide.md"},
- // {text: "CS学习路线", link: "/learning-resources/cs-learn-guide.md"},
- // ]
- // },
- //
- // ]
- // },
{
- text: "关于",
- icon: "about",
+ text: "珍藏资源",
+ icon: "collection",
children: [
- {text: "关于我", link: "/about/introduce.md"},
- {text: "网站日记", link: "/other/site-diary.md"},
- {text: "联系我", link: "/about/contact.md"},
- {text: "留言区", link: "/other/leave-a-message.md"},
{
text: "学习资源",
children: [
- {text: "计算机经典电子书PDF", link: "https://github.com/Tyson0314/java-books"},
- {text: "Leetcode刷题笔记", link: "/learning-resources/leetcode-note.md"},
+ {text: "计算机经典电子书PDF", link: "https://github.com/Tyson0314/java-books", icon: "book"},
+ {text: "Leetcode刷题笔记", link: "/learning-resources/leetcode-note.md", icon: "leetcode"},
+ {text: "技术学习路线思维导图", link: "https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494513&idx=1&sn=de1a7cf0b5580840cb8ad4a96e618866&chksm=ce9b1637f9ec9f212d054018598b96b5277f7733fac8f985d8dae0074c8446a2cad8e43ba739#rd", icon: "route"},
+ {text: "图解操作系统、网络、计算机系列", link: "https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494510&idx=1&sn=b19d9e07321b8fca9129fe0d8403a426&chksm=ce9b1628f9ec9f3e7d45a6db8389ee2813864a9ca692238d29b139c35ccb01b08155bc2da358#rd", icon: "computer"},
+ {text: "优质视频教程", link: "https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd", icon: "video"},
+ {text: "ChatGPT手册", link: "https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247494344&idx=1&sn=d16f51e8bd3424f63e4fb6a5aa5ca4db&chksm=ce9b178ef9ec9e9841c7a049e4da0843c291b96f463e87190a6bf344c7022194ee393b695751#rd", icon: "ai"},
]
},
{
text: "学习路线",
children: [
- {text: "Java学习路线", link: "/learning-resources/java-learn-guide.md"},
- {text: "CS学习路线", link: "/learning-resources/cs-learn-guide.md"},
+ {text: "Java学习路线", link: "/learning-resources/java-learn-guide.md", icon: "java"},
+ {text: "CS学习路线", link: "/learning-resources/cs-learn-guide.md", icon: "jisuanji"},
]
},
+
+ ]
+ },
+ {
+ text: "关于",
+ icon: "about",
+ children: [
+ {text: "关于我", link: "/about/introduce.md", icon: "wode"},
+ {text: "网站日记", link: "/other/site-diary.md", icon: "riji"},
+ {text: "联系我", link: "/about/contact.md", icon: "lianxi"},
+ {text: "留言区", link: "/other/leave-a-message.md", icon: "liuyan"},
+ //{
+ // text: "学习资源",
+ // children: [
+ // {text: "计算机经典电子书PDF", link: "https://github.com/Tyson0314/java-books"},
+ // {text: "Leetcode刷题笔记", link: "/learning-resources/leetcode-note.md"},
+ // ]
+ //},
+ //{
+ // text: "学习路线",
+ // children: [
+ // {text: "Java学习路线", link: "/learning-resources/java-learn-guide.md"},
+ // {text: "CS学习路线", link: "/learning-resources/cs-learn-guide.md"},
+ // ]
+ //},
]
},
diff --git a/docs/.vuepress/sidebar.ts b/docs/.vuepress/sidebar.ts
index 9eb2d4c..283d1b5 100644
--- a/docs/.vuepress/sidebar.ts
+++ b/docs/.vuepress/sidebar.ts
@@ -122,13 +122,16 @@ export default sidebar({
children: getChildren('./docs/campus-recruit', 'share'),
},
],
- "/mass-data": [
- {
- text: "海量数据",
- collapsable: false,
- children: getChildren('./docs', '/mass-data'),
- },
- ],
+ // "/mass-data": [
+ // {
+ // text: "海量数据",
+ // collapsable: false,
+ // // children: getChildren('./docs', '/mass-data'),
+ // children: [
+ // {text: "统计不同号码的个数", link: "/mass-data/1-count-phone-num.md"}
+ // ]
+ // },
+ // ],
//'/': "auto", //不能放在数组第一个,否则会导致右侧栏无法使用
//"/",
diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts
index 6bcc6bf..f62dc4a 100644
--- a/docs/.vuepress/theme.ts
+++ b/docs/.vuepress/theme.ts
@@ -10,7 +10,7 @@ export default hopeTheme({
url: "https://www.topjavaer.cn",
},
- iconAssets: "//at.alicdn.com/t/c/font_3573089_nctmwoh7jtn.css",
+ iconAssets: "//at.alicdn.com/t/c/font_3573089_ladvogz6xzq.css",
iconPrefix: "iconfont icon-",
//iconAssets: "iconfont",
@@ -33,6 +33,8 @@ export default hopeTheme({
collapsable: true,
displayFooter: true,
+ prevLink: true,
+ nextLink: true,
// footer: '
粤ICP备2022005190号-2 |' +
// '
关于网站',
@@ -97,16 +99,16 @@ export default hopeTheme({
//myplugin
copyright: {
- disableCopy: true,
+ disableCopy: false,
global: true,
author: "大彬",
- license: "MIT",
+ //license: "MIT",
hostname: "https://www.topjavaer.cn",
},
baiduAutoPush: {},
sitemapPlugin: {
// 配置选项
- hostname: "https:www.topjavaer.cn"
+ hostname: "https://www.topjavaer.cn"
},
photoSwipePlugin: {
// 你的选项
diff --git a/docs/README.md b/docs/README.md
index 98599b3..9f8ba39 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -7,10 +7,10 @@ heroText: 程序员大彬
tagline: 优质的编程学习网站
actions:
- text: 开始阅读
- link: /java/java-basic.md
+ link: /java/java-basic.html
type: primary
- text: 学习圈子💡
- link: /zsxq/introduce.md
+ link: /zsxq/introduce.html
type: primary
features:
- title: 经典计算机书籍
@@ -47,10 +47,6 @@ 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)可以获取~
diff --git a/docs/about/contact.md b/docs/about/contact.md
index d88855f..680a8ad 100644
--- a/docs/about/contact.md
+++ b/docs/about/contact.md
@@ -6,10 +6,11 @@ sidebar: heading
如果有什么疑问或者建议,欢迎添加大彬微信进行交流~
-

+
+
## 交流群
学习路上,难免遇到很多坑,为方便大家交流求职和技术问题,我建了**求职&技术交流群**,在群里可以讨论技术、面试相关问题,也可以获得阿里、字节等大厂的内推机会!
@@ -24,4 +25,4 @@ sidebar: heading
感兴趣的小伙伴可以扫描下方的二维码**加我微信**,**备注加群**,我拉你进群,一起学习成长!
-
+
diff --git a/docs/advance/concurrent/1-current-limiting.md b/docs/advance/concurrent/1-current-limiting.md
index 303ca57..95d9136 100644
--- a/docs/advance/concurrent/1-current-limiting.md
+++ b/docs/advance/concurrent/1-current-limiting.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 限流算法介绍
+category: 实践经验
+tag:
+ - 并发
+head:
+ - - meta
+ - name: keywords
+ content: 限流算法,令牌桶算法,漏桶算法,时间窗口算法,队列法
+ - - meta
+ - name: description
+ content: Java常见面试题总结,让天下没有难背的八股文!
+---
+
# 限流算法
大多数情况下,我们不需要自己实现一个限流系统,但限流在实际应用中是一个非常微妙、有很多细节的系统保护手段,尤其是在高流量时,了解你所使用的限流系统的限流算法,将能很好地帮助你充分利用该限流系统达到自己的商业需求和目的,并规避一些使用限流系统可能带来的大大小小的问题。
@@ -136,4 +151,4 @@
-> 本文摘录自《深入浅出大型网站架构设计》
\ No newline at end of file
+> 本文摘录自《深入浅出大型网站架构设计》
diff --git a/docs/advance/concurrent/2-load-balance.md b/docs/advance/concurrent/2-load-balance.md
index a3e6e3d..6e94c80 100644
--- a/docs/advance/concurrent/2-load-balance.md
+++ b/docs/advance/concurrent/2-load-balance.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 高可用——负载均衡
+category: 实践经验
+tag:
+ - 并发
+head:
+ - - meta
+ - name: keywords
+ content: 高可用,负载均衡
+ - - meta
+ - name: description
+ content: Java常见面试题总结,让天下没有难背的八股文!
+---
+
# 高可用——负载均衡
## **一、 什么是负载均衡?**
@@ -161,4 +176,4 @@ Dubbo内置了4种负载均衡策略:
- 第 1 层:客户端层 -> 反向代理层 的负载均衡。通过 DNS 轮询
- 第 2 层:反向代理层 -> Web 层 的负载均衡。通过 Nginx 的负载均衡模块
- 第 3 层:Web 层 -> 业务服务层 的负载均衡。通过服务治理框架的负载均衡模块
-- 第 4 层:业务服务层 -> 数据存储层 的负载均衡。通过数据的水平分布,数据均匀了,理论上请求也会均匀。比如通过买家ID分片类似
\ No newline at end of file
+- 第 4 层:业务服务层 -> 数据存储层 的负载均衡。通过数据的水平分布,数据均匀了,理论上请求也会均匀。比如通过买家ID分片类似
diff --git a/docs/advance/design-pattern/1-principle.md b/docs/advance/design-pattern/1-principle.md
index cec9d65..873ad1f 100644
--- a/docs/advance/design-pattern/1-principle.md
+++ b/docs/advance/design-pattern/1-principle.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式的六大原则
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 设计模式的六大原则,设计模式,设计模式面试题
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 设计模式的六大原则
- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
diff --git a/docs/advance/design-pattern/10-observer.md b/docs/advance/design-pattern/10-observer.md
index 3e56211..138cd8a 100644
--- a/docs/advance/design-pattern/10-observer.md
+++ b/docs/advance/design-pattern/10-observer.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之观察者模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 观察者模式,设计模式,观察者
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 观察者模式
**观察者模式(Observer)**,又叫**发布-订阅模式(Publish/Subscribe)**,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:
@@ -120,4 +135,4 @@ public class Client {
- 关联行为场景
- 事件多级触发场景
-- 跨系统的消息变换场景,如消息队列的处理机制
\ No newline at end of file
+- 跨系统的消息变换场景,如消息队列的处理机制
diff --git a/docs/advance/design-pattern/11-proxy.md b/docs/advance/design-pattern/11-proxy.md
index 3e9c02b..8342c6c 100644
--- a/docs/advance/design-pattern/11-proxy.md
+++ b/docs/advance/design-pattern/11-proxy.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之代理模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 代理模式,设计模式,静态代理,动态代理
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 代理模式
代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。
@@ -192,4 +207,4 @@ InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定
-> 参考链接:https://zhuanlan.zhihu.com/p/72644638
\ No newline at end of file
+> 参考链接:https://zhuanlan.zhihu.com/p/72644638
diff --git a/docs/advance/design-pattern/12-builder.md b/docs/advance/design-pattern/12-builder.md
index af99eb6..1f894fe 100644
--- a/docs/advance/design-pattern/12-builder.md
+++ b/docs/advance/design-pattern/12-builder.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之建造者模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 建造者模式,设计模式,建造者,生成器模式
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 建造者模式
Builder 模式中文叫作建造者模式,又叫生成器模式,它属于对象创建型模式,是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
@@ -317,4 +332,4 @@ public static void student(){
### 缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
-- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
\ No newline at end of file
+- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
diff --git a/docs/advance/design-pattern/2-singleton.md b/docs/advance/design-pattern/2-singleton.md
index eec5004..3d536d5 100644
--- a/docs/advance/design-pattern/2-singleton.md
+++ b/docs/advance/design-pattern/2-singleton.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之单例模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 单例模式,设计模式,单例
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 单例模式
单例模式(Singleton),目的是为了保证在一个进程中,某个类有且仅有一个实例。
diff --git a/docs/advance/design-pattern/3-factory.md b/docs/advance/design-pattern/3-factory.md
index b8615f3..df2b20c 100644
--- a/docs/advance/design-pattern/3-factory.md
+++ b/docs/advance/design-pattern/3-factory.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之工厂模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 工厂模式,设计模式
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 工厂模式
工厂模式是用来封装对象的创建。工厂模式有三种,它们分别是简单工厂模式,工厂方法模式以及抽象工厂模式,通常我们所说的工厂模式指的是工厂方法模式。
@@ -292,4 +307,4 @@ operationController.control();
(2)需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。
-(3)系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
\ No newline at end of file
+(3)系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
diff --git a/docs/advance/design-pattern/4-template.md b/docs/advance/design-pattern/4-template.md
index a4a1195..f678096 100644
--- a/docs/advance/design-pattern/4-template.md
+++ b/docs/advance/design-pattern/4-template.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: 设计模式之模板模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 模板模式,设计模式
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
+
# 模板模式
模板模式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
diff --git a/docs/advance/design-pattern/5-strategy.md b/docs/advance/design-pattern/5-strategy.md
index f55dae5..263a42b 100644
--- a/docs/advance/design-pattern/5-strategy.md
+++ b/docs/advance/design-pattern/5-strategy.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之策略模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 策略模式,设计模式
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 策略模式
策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
diff --git a/docs/advance/design-pattern/6-chain.md b/docs/advance/design-pattern/6-chain.md
index 9de370e..dbe5599 100644
--- a/docs/advance/design-pattern/6-chain.md
+++ b/docs/advance/design-pattern/6-chain.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之责任链模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 责任链模式,设计模式
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 责任链模式
## 定义
diff --git a/docs/advance/design-pattern/7-iterator.md b/docs/advance/design-pattern/7-iterator.md
index a54bdf7..e2fabbd 100644
--- a/docs/advance/design-pattern/7-iterator.md
+++ b/docs/advance/design-pattern/7-iterator.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之迭代器模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 迭代器模式,设计模式,迭代器
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示。
diff --git a/docs/advance/design-pattern/8-decorator.md b/docs/advance/design-pattern/8-decorator.md
index 05c8108..f010b2b 100644
--- a/docs/advance/design-pattern/8-decorator.md
+++ b/docs/advance/design-pattern/8-decorator.md
@@ -1,4 +1,19 @@
-# 装饰模式
+---
+sidebar: heading
+title: 设计模式之装饰模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 装饰模式,设计模式,装饰者
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
+# 装饰者模式
装饰者模式(decorator pattern):动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。
diff --git a/docs/advance/design-pattern/9-adapter.md b/docs/advance/design-pattern/9-adapter.md
index 090bf73..919c173 100644
--- a/docs/advance/design-pattern/9-adapter.md
+++ b/docs/advance/design-pattern/9-adapter.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 设计模式之适配器模式
+category: 设计模式
+tag:
+ - 设计模式
+head:
+ - - meta
+ - name: keywords
+ content: 适配器模式,设计模式,适配器
+ - - meta
+ - name: description
+ content: 设计模式常见面试题总结,让天下没有难背的八股文!
+---
+
# 适配器模式
适配器模式将现成的对象通过适配变成我们需要的接口。 适配器让原本接口不兼容的类可以合作。
diff --git a/docs/advance/design-pattern/README.md b/docs/advance/design-pattern/README.md
index 650a778..e96d9f8 100644
--- a/docs/advance/design-pattern/README.md
+++ b/docs/advance/design-pattern/README.md
@@ -6,9 +6,15 @@ category: 设计模式
star: true
---
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
**本专栏是大彬学习设计模式基础知识的学习笔记,如有错误,可以在评论区指出**~
-
## 设计模式详解
- [设计模式的六大原则](./1-principle.md)
diff --git a/docs/advance/distributed/1-global-unique-id.md b/docs/advance/distributed/1-global-unique-id.md
index 53cac98..7812e4a 100644
--- a/docs/advance/distributed/1-global-unique-id.md
+++ b/docs/advance/distributed/1-global-unique-id.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 全局唯一ID生成方案
+category: 分布式
+tag:
+ - 分布式ID
+head:
+ - - meta
+ - name: keywords
+ content: 分布式ID,分布式,唯一ID生成方案
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# 全局唯一ID生成方案
传统的单体架构的时候,我们基本是单库然后业务单表的结构。每个业务表的ID一般我们都是从1增,通过`AUTO_INCREMENT=1`设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。
diff --git a/docs/advance/distributed/2-distributed-lock.md b/docs/advance/distributed/2-distributed-lock.md
index 07c5445..1016022 100644
--- a/docs/advance/distributed/2-distributed-lock.md
+++ b/docs/advance/distributed/2-distributed-lock.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 分布式锁
+category: 分布式
+tag:
+ - 分布式锁
+head:
+ - - meta
+ - name: keywords
+ content: 分布式锁,分布式
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# 分布式锁
## 为什么要使用分布式锁
@@ -166,6 +181,8 @@ public class RedisTest {
前面的方案是基于**Redis单机版**的分布式锁讨论,还不是很完美。因为Redis一般都是集群部署的。
+
+
如果线程一在`Redis`的`master`节点上拿到了锁,但是加锁的`key`还没同步到`slave`节点。恰好这时,`master`节点发生故障,一个`slave`节点就会升级为`master`节点。线程二就可以顺理成章获取同个`key`的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。
为了解决这个问题,Redis作者antirez提出一种高级的分布式锁算法:**Redlock**。它的核心思想是这样的:
@@ -174,6 +191,8 @@ public class RedisTest {
我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。
+
+
RedLock的实现步骤:
1. 获取当前时间,以毫秒为单位。
@@ -189,6 +208,8 @@ RedLock的实现步骤:
- 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
- 如果获取锁失败,解锁!
+Redisson 实现了 redLock 版本的锁,有兴趣的小伙伴,可以去了解一下。
+
### 基于ZooKeeper的实现方式
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:
diff --git a/docs/advance/distributed/3-rpc.md b/docs/advance/distributed/3-rpc.md
index c118664..a9d5008 100644
--- a/docs/advance/distributed/3-rpc.md
+++ b/docs/advance/distributed/3-rpc.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: RPC
+category: 分布式
+tag:
+ - RPC
+head:
+ - - meta
+ - name: keywords
+ content: RPC,分布式,RPC框架,RPC和HTTP,序列化技术
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# RPC
## RPC简介
diff --git a/docs/advance/distributed/4-micro-service.md b/docs/advance/distributed/4-micro-service.md
index cd6070e..aec218d 100644
--- a/docs/advance/distributed/4-micro-service.md
+++ b/docs/advance/distributed/4-micro-service.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 微服务
+category: 分布式
+tag:
+ - 微服务
+head:
+ - - meta
+ - name: keywords
+ content: 微服务,分布式,微服务设计原则,服务网关,微服务通讯方式,微服务框架,微服务链路追踪
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# 微服务
## 什么是微服务?
@@ -32,9 +47,15 @@
## 分布式和微服务的区别
-从概念理解,分布式服务架构强调的是服务化以及服务的**分散化**,微服务则更强调服务的**专业化和精细分工**;
+微服务解决的是系统复杂度问题,一般来说是业务问题,即在一个系统中承担职责太多了,需要打散,便于理解和维护,进而提升系统的开发效率和运行效率,微服务一般来说是针对应用层面的。
+
+分布式解决的是系统性能问题,即解决系统部署上单点的问题,尽量让组成系统的子系统分散在不同的机器上进而提高系统的吞吐能力。
+
+两者概念层面也是不一样的,微服务是设计层面的东西,一般考虑如何将系统从逻辑上进行拆分,也就是垂直拆分;
-从实践的角度来看,**微服务架构通常是分布式服务架构**,反之则未必成立。
+而分布式是部署层面的东西,即强调物理层面的组成,即系统的各子系统部署在不同计算机上。
+
+微服务可以是分布式的,即可以将不同服务部署在不同计算机上,当然如果量小也可以部署在单机上。
一句话概括:分布式:分散部署;微服务:分散能力。
@@ -73,6 +94,8 @@
**1、RPC**
+远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。
+
优点:简单,常见。因为没有中间件代理,系统更简单
缺点:
diff --git a/docs/advance/distributed/5-distibuted-arch.md b/docs/advance/distributed/5-distibuted-arch.md
index 30b0b32..372bd50 100644
--- a/docs/advance/distributed/5-distibuted-arch.md
+++ b/docs/advance/distributed/5-distibuted-arch.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 分布式架构
+category: 分布式
+tag:
+ - 分布式架构
+head:
+ - - meta
+ - name: keywords
+ content: 分布式架构,限流,熔断
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# 分布式架构,微服务、限流、熔断....
-[分布式架构,微服务、限流、熔断....](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
\ No newline at end of file
+[分布式架构,微服务、限流、熔断....](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
diff --git a/docs/advance/distributed/6-distributed-transaction.md b/docs/advance/distributed/6-distributed-transaction.md
index ed10a30..a952e8a 100644
--- a/docs/advance/distributed/6-distributed-transaction.md
+++ b/docs/advance/distributed/6-distributed-transaction.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 分布式事务
+category: 分布式
+tag:
+ - 分布式事务
+head:
+ - - meta
+ - name: keywords
+ content: 分布式事务,强一致性,弱一致性,最终一致性,CAP理论,BASE理论,2PC方案,TCC,本地消息表,saga事务,最大努力通知方案
+ - - meta
+ - name: description
+ content: 分布式常见面试题总结,让天下没有难背的八股文!
+---
+
# 分布式事务
## 简介
diff --git a/docs/advance/distributed/article/distributed-lock.md b/docs/advance/distributed/article/distributed-lock.md
new file mode 100644
index 0000000..2e1c0fd
--- /dev/null
+++ b/docs/advance/distributed/article/distributed-lock.md
@@ -0,0 +1,351 @@
+---
+sidebar: heading
+title: 分布式锁
+category: 分布式
+tag:
+ - 分布式ID
+head:
+ - - meta
+ - name: keywords
+ content: 分布式锁,Redis分布式锁,zookeeper分布式锁,redlock
+ - - meta
+ - name: description
+ content: 分布式锁常见面试题总结,让天下没有难背的八股文!
+---
+
+## 分布式锁怎么实现?
+
+一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
+
+## Redis 分布式锁
+
+Redis 分布式锁有 3 个重要的考量点:
+
+- 互斥(只能有一个客户端获取锁)
+- 不能死锁
+- 容错(只要大部分 Redis 节点创建了这把锁就可以)
+
+### Redis 最普通的分布式锁
+
+第一个最普通的实现方式,就是在 Redis 里使用 `SET key value [EX seconds] [PX milliseconds] NX` 创建一个 key,这样就算加锁。其中:
+
+- `NX`:表示只有 `key` 不存在的时候才会设置成功,如果此时 redis 中存在这个 `key`,那么设置失败,返回 `nil`。
+- `EX seconds`:设置 `key` 的过期时间,精确到秒级。意思是 `seconds` 秒后锁自动释放,别人创建的时候如果发现已经有了就不能加锁了。
+- `PX milliseconds`:同样是设置 `key` 的过期时间,精确到毫秒级。
+
+比如执行以下命令:
+
+```bash
+SET resource_name my_random_value PX 30000 NX
+```
+
+释放锁就是删除 key ,但是一般可以用 `lua` 脚本删除,判断 value 一样才删除:
+
+```lua
+-- 删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除。
+if redis.call("get",KEYS[1]) == ARGV[1] then
+ return redis.call("del",KEYS[1])
+else
+ return 0
+end
+```
+
+为啥要用 `random_value` 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 `lua` 脚本来释放锁。
+
+但是这样是肯定不行的。因为如果是普通的 Redis 单实例,那就是单点故障。或者是 Redis 普通主从,那 Redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。
+
+### RedLock 算法
+
+这个场景是假设有一个 Redis cluster,有 5 个 Redis master 实例。然后执行如下步骤获取一把锁:
+
+1. 获取当前时间戳,单位是毫秒;
+2. 跟上面类似,轮流尝试在每个 master 节点上创建锁,超时时间较短,一般就几十毫秒(客户端为了获取锁而使用的超时时间比自动释放锁的总时间要小。例如,如果自动释放时间是 10 秒,那么超时时间可能在 `5~50` 毫秒范围内);
+3. 尝试在**大多数节点**上建立一个锁,比如 5 个节点就要求是 3 个节点 `n / 2 + 1` ;
+4. 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
+5. 要是锁建立失败了,那么就依次之前建立过的锁删除;
+6. 只要别人建立了一把分布式锁,你就得**不断轮询去尝试获取锁**。
+
+
+
+[Redis 官方](https://redis.io/)给出了以上两种基于 Redis 实现分布式锁的方法,详细说明可以查看:https://redis.io/topics/distlock 。
+
+## zk 分布式锁
+
+zk 分布式锁,其实可以做的比较简单,就是某个节点尝试创建临时 znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能**注册个监听器**监听这个锁。释放锁就是删除这个 znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。
+
+```java
+/**
+ * ZooKeeperSession
+ */
+public class ZooKeeperSession {
+
+ private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
+
+ private ZooKeeper zookeeper;
+ private CountDownLatch latch;
+
+ public ZooKeeperSession() {
+ try {
+ this.zookeeper = new ZooKeeper("192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181", 50000, new ZooKeeperWatcher());
+ try {
+ connectedSemaphore.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ System.out.println("ZooKeeper session established......");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 获取分布式锁
+ *
+ * @param productId
+ */
+ public Boolean acquireDistributedLock(Long productId) {
+ String path = "/product-lock-" + productId;
+
+ try {
+ zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ return true;
+ } catch (Exception e) {
+ while (true) {
+ try {
+ // 相当于是给node注册一个监听器,去看看这个监听器是否存在
+ Stat stat = zk.exists(path, true);
+
+ if (stat != null) {
+ this.latch = new CountDownLatch(1);
+ this.latch.await(waitTime, TimeUnit.MILLISECONDS);
+ this.latch = null;
+ }
+ zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ return true;
+ } catch (Exception ee) {
+ continue;
+ }
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * 释放掉一个分布式锁
+ *
+ * @param productId
+ */
+ public void releaseDistributedLock(Long productId) {
+ String path = "/product-lock-" + productId;
+ try {
+ zookeeper.delete(path, -1);
+ System.out.println("release the lock for product[id=" + productId + "]......");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 建立 zk session 的 watcher
+ */
+ private class ZooKeeperWatcher implements Watcher {
+
+ public void process(WatchedEvent event) {
+ System.out.println("Receive watched event: " + event.getState());
+
+ if (KeeperState.SyncConnected == event.getState()) {
+ connectedSemaphore.countDown();
+ }
+
+ if (this.latch != null) {
+ this.latch.countDown();
+ }
+ }
+
+ }
+
+ /**
+ * 封装单例的静态内部类
+ */
+ private static class Singleton {
+
+ private static ZooKeeperSession instance;
+
+ static {
+ instance = new ZooKeeperSession();
+ }
+
+ public static ZooKeeperSession getInstance() {
+ return instance;
+ }
+
+ }
+
+ /**
+ * 获取单例
+ *
+ * @return
+ */
+ public static ZooKeeperSession getInstance() {
+ return Singleton.getInstance();
+ }
+
+ /**
+ * 初始化单例的便捷方法
+ */
+ public static void init() {
+ getInstance();
+ }
+
+}
+```
+
+也可以采用另一种方式,创建临时顺序节点:
+
+如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听**排在自己前面**的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知,一旦被通知了之后,自己就可以获取到了锁,就可以执行代码了。
+
+```java
+public class ZooKeeperDistributedLock implements Watcher {
+
+ private ZooKeeper zk;
+ private String locksRoot = "/locks";
+ private String productId;
+ private String waitNode;
+ private String lockNode;
+ private CountDownLatch latch;
+ private CountDownLatch connectedLatch = new CountDownLatch(1);
+ private int sessionTimeout = 30000;
+
+ public ZooKeeperDistributedLock(String productId) {
+ this.productId = productId;
+ try {
+ String address = "192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181";
+ zk = new ZooKeeper(address, sessionTimeout, this);
+ connectedLatch.await();
+ } catch (IOException e) {
+ throw new LockException(e);
+ } catch (KeeperException e) {
+ throw new LockException(e);
+ } catch (InterruptedException e) {
+ throw new LockException(e);
+ }
+ }
+
+ public void process(WatchedEvent event) {
+ if (event.getState() == KeeperState.SyncConnected) {
+ connectedLatch.countDown();
+ return;
+ }
+
+ if (this.latch != null) {
+ this.latch.countDown();
+ }
+ }
+
+ public void acquireDistributedLock() {
+ try {
+ if (this.tryLock()) {
+ return;
+ } else {
+ waitForLock(waitNode, sessionTimeout);
+ }
+ } catch (KeeperException e) {
+ throw new LockException(e);
+ } catch (InterruptedException e) {
+ throw new LockException(e);
+ }
+ }
+
+ public boolean tryLock() {
+ try {
+ // 传入进去的locksRoot + “/” + productId
+ // 假设productId代表了一个商品id,比如说1
+ // locksRoot = locks
+ // /locks/10000000000,/locks/10000000001,/locks/10000000002
+ lockNode = zk.create(locksRoot + "/" + productId, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
+
+ // 看看刚创建的节点是不是最小的节点
+ // locks:10000000000,10000000001,10000000002
+ List
locks = zk.getChildren(locksRoot, false);
+ Collections.sort(locks);
+
+ if (lockNode.equals(locksRoot + "/" + locks.get(0))) {
+ // 如果是最小的节点,则表示取得锁
+ return true;
+ }
+
+ // 如果不是最小的节点,找到比自己小1的节点
+ int previousLockIndex = -1;
+ for (int i = 0; i < locks.size(); i++) {
+ if (lockNode.equals(locksRoot + "/" +locks.get(i))){
+ previousLockIndex = i - 1;
+ break;
+ }
+ }
+
+ this.waitNode = locks.get(previousLockIndex);
+ } catch (KeeperException e) {
+ throw new LockException(e);
+ } catch (InterruptedException e) {
+ throw new LockException(e);
+ }
+ return false;
+ }
+
+ private boolean waitForLock(String waitNode, long waitTime) throws InterruptedException, KeeperException {
+ Stat stat = zk.exists(locksRoot + "/" + waitNode, true);
+ if (stat != null) {
+ this.latch = new CountDownLatch(1);
+ this.latch.await(waitTime, TimeUnit.MILLISECONDS);
+ this.latch = null;
+ }
+ return true;
+ }
+
+ public void unlock() {
+ try {
+ // 删除/locks/10000000000节点
+ // 删除/locks/10000000001节点
+ System.out.println("unlock " + lockNode);
+ zk.delete(lockNode, -1);
+ lockNode = null;
+ zk.close();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (KeeperException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public class LockException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public LockException(String e) {
+ super(e);
+ }
+
+ public LockException(Exception e) {
+ super(e);
+ }
+ }
+}
+```
+
+但是,使用 zk 临时节点会存在另一个问题:由于 zk 依靠 session 定期的心跳来维持客户端,如果客户端进入长时间的 GC,可能会导致 zk 认为客户端宕机而释放锁,让其他的客户端获取锁,但是客户端在 GC 恢复后,会认为自己还持有锁,从而可能出现多个客户端同时获取到锁的情形。
+
+针对这种情况,可以通过 JVM 调优,尽量避免长时间 GC 的情况发生。
+
+## redis 分布式锁和 zk 分布式锁的对比
+
+- redis 分布式锁,其实**需要自己不断去尝试获取锁**,比较消耗性能。
+- zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
+
+另外一点就是,如果是 Redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
+
+总体上来说,zk 的分布式锁比 Redis 的分布式锁牢靠、而且模型简单易用。
+
+
+
+参考链接:https://doocs.github.io/advanced-java
diff --git a/docs/advance/excellent-article/1-redis-stock-minus.md b/docs/advance/excellent-article/1-redis-stock-minus.md
index f7382f5..63215c8 100644
--- a/docs/advance/excellent-article/1-redis-stock-minus.md
+++ b/docs/advance/excellent-article/1-redis-stock-minus.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis 如何实现库存扣减操作和防止被超卖?
+category: 优质文章
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis,库存扣减,超卖问题
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Redis 如何实现库存扣减操作和防止被超卖?
电商当项目经验已经非常普遍了,不管你是包装的还是真实的,起码要能讲清楚电商中常见的问题,比如库存的操作怎么防止商品被超卖
diff --git a/docs/advance/excellent-article/10-file-upload.md b/docs/advance/excellent-article/10-file-upload.md
index 35655eb..0064765 100644
--- a/docs/advance/excellent-article/10-file-upload.md
+++ b/docs/advance/excellent-article/10-file-upload.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 大文件上传时如何做到秒传?
+category: 优质文章
+tag:
+ - 实践经验
+head:
+ - - meta
+ - name: keywords
+ content: 大文件上传优化,文件上传
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 大文件上传时如何做到秒传?
大家好,我是大彬~
@@ -330,12 +345,10 @@ public abstract class SliceUploadTemplate implements SliceUploadStrategy {
本示例代码在电脑配置为4核内存8G情况下,上传24G大小的文件,上传时间需要30多分钟,主要时间耗费在前端的**md5**值计算,后端写入的速度还是比较快。
-如果项目组觉得自建文件服务器太花费时间,且项目的需求仅仅只是上传下载,那么推荐使用阿里的oss服务器,其介绍可以查看官网:
-
-> https://help.aliyun.com/product/31815.html
+如果项目组觉得自建文件服务器太花费时间,且项目的需求仅仅只是上传下载,那么推荐使用阿里的oss服务器。
阿里的oss它本质是一个对象存储服务器,而非文件服务器,因此如果有涉及到大量删除或者修改文件的需求,oss可能就不是一个好的选择。
文末提供一个oss表单上传的链接demo,通过oss表单上传,可以直接从前端把文件上传到oss服务器,把上传的压力都推给oss服务器:
-> https://www.cnblogs.com/ossteam/p/4942227.html
\ No newline at end of file
+> https://www.cnblogs.com/ossteam/p/4942227.html
diff --git a/docs/advance/excellent-article/11-8-architect-pattern.md b/docs/advance/excellent-article/11-8-architect-pattern.md
index 7ec3181..51d03d8 100644
--- a/docs/advance/excellent-article/11-8-architect-pattern.md
+++ b/docs/advance/excellent-article/11-8-architect-pattern.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 8种架构模式
+category: 优质文章
+tag:
+ - 架构
+head:
+ - - meta
+ - name: keywords
+ content: 架构模式
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 8种架构模式
-[8种架构模式](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490779&idx=2&sn=eff9e8cf9b15c29630514a137f102701&chksm=ce98e19df9ef688bd9c7b775658c704a51b7961347a7aabf70e6c555cb57560aa5e8b1e497a1&token=1170645384&lang=zh_CN#rd)
\ No newline at end of file
+[8种架构模式](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490779&idx=2&sn=eff9e8cf9b15c29630514a137f102701&chksm=ce98e19df9ef688bd9c7b775658c704a51b7961347a7aabf70e6c555cb57560aa5e8b1e497a1&token=1170645384&lang=zh_CN#rd)
diff --git a/docs/advance/excellent-article/12-mysql-table-max-rows.md b/docs/advance/excellent-article/12-mysql-table-max-rows.md
index 71e6793..0fa64c0 100644
--- a/docs/advance/excellent-article/12-mysql-table-max-rows.md
+++ b/docs/advance/excellent-article/12-mysql-table-max-rows.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: MySQL最大建议行数 2000w,靠谱吗?
+category: 优质文章
+tag:
+ - 数据库
+head:
+ - - meta
+ - name: keywords
+ content: MySQL最大行数
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# MySQL最大建议行数 2000w,靠谱吗?
## **1 背景**
@@ -209,4 +224,4 @@ Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连
- *https://www.modb.pro/db/139052*
- *《MYSQL 内核:INNODB 存储引擎 卷 1》*
-*来源:my.oschina.net/u/4090830/blog/5559454*
\ No newline at end of file
+*来源:my.oschina.net/u/4090830/blog/5559454*
diff --git a/docs/advance/excellent-article/13-order-by-work.md b/docs/advance/excellent-article/13-order-by-work.md
index 6439f8e..7b73773 100644
--- a/docs/advance/excellent-article/13-order-by-work.md
+++ b/docs/advance/excellent-article/13-order-by-work.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: order by是怎么工作的?
+category: 优质文章
+tag:
+ - 数据库
+head:
+ - - meta
+ - name: keywords
+ content: order by,MySQL,数据库排序
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# order by是怎么工作的?
在你开发应用的时候,一定会经常碰到需要根据指定的字段排序来显示结果的需求。还是以我们前面举例用过的市民表为例,假设你要查询城市是“杭州”的所有人名字,并且按照姓名排序返回前 1000 个人的姓名、年龄。
@@ -260,4 +275,4 @@ alter table t add index city_user_age(city, name, age);
在开发系统的时候,你总是不可避免地会使用到 order by 语句。你心里要清楚每个语句的排序逻辑是怎么实现的,还要能够分析出在最坏情况下,每个语句的执行对系统资源的消耗,这样才能做到下笔如有神,不犯低级错误。
-> 内容摘录自丁奇的《MySQL45讲》
\ No newline at end of file
+> 内容摘录自丁奇的《MySQL45讲》
diff --git a/docs/advance/excellent-article/14-architect-forward.md b/docs/advance/excellent-article/14-architect-forward.md
index a561ce5..be04cd4 100644
--- a/docs/advance/excellent-article/14-architect-forward.md
+++ b/docs/advance/excellent-article/14-architect-forward.md
@@ -1,10 +1,25 @@
+---
+sidebar: heading
+title: 架构的演进
+category: 优质文章
+tag:
+ - 架构
+head:
+ - - meta
+ - name: keywords
+ content: 架构,架构演进
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 架构的演进
### 传统单体应用架构
十多年前主流的应用架构都是单体应用,部署形式就是一台服务器加一个数据库,在这种架构下,运维人员会小心翼翼地维护这台服务器,以保证服务的可用性。
-
+
#### 单体应用架构面临的问题
@@ -12,7 +27,7 @@
解决这两个问题最直接的方法就是在流量入口加一个负载均衡器,使单体应用同时部署到多台服务器上,这样服务器的单点问题就解决了,与此同时,这个单体应用也具备了水平伸缩的能力。
-
+
### 微服务架构
@@ -28,7 +43,7 @@
除分布式环境带来的挑战之外,微服务架构给运维也带来新挑战。研发人员原来只需要运维一个应用,现在可能需要运维十个甚至更多的应用,这意味着安全 patch 升级、容量评估、故障诊断等事务的工作量呈现成倍增长,这时,应用分发标准、生命周期标准、观测标准、自动化弹性等能力的重要性也更加凸显。
-
+
### 云原生
@@ -48,4 +63,4 @@
在架构的演进过程中,研发运维人员逐渐把关注点从机器上移走,希望更多地由平台系统管理机器,而不是由人去管理,这就是一个对 Serverless 的朴素理解。
-> 本文部分内容摘抄自网络
\ No newline at end of file
+> 本文部分内容摘抄自网络
diff --git a/docs/advance/excellent-article/15-http-vs-rpc.md b/docs/advance/excellent-article/15-http-vs-rpc.md
index fd082e0..a1e3f92 100644
--- a/docs/advance/excellent-article/15-http-vs-rpc.md
+++ b/docs/advance/excellent-article/15-http-vs-rpc.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 有了HTTP,为啥还要用RPC
+category: 优质文章
+tag:
+ - 网络
+head:
+ - - meta
+ - name: keywords
+ content: HTTP和RPC,HTTP,RPC
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 有了HTTP,为啥还要用RPC
> 原文链接:https://www.jianshu.com/p/9d42b926d40d
@@ -264,4 +279,4 @@ RPC 服务和 HTTP 服务还是存在很多的不同点的,一般来说,RPC
总之,选用什么样的框架不是按照市场上流行什么而决定的,而是要对整个项目进行完整地评估,从而在仔细比较两种开发框架对于整个项目的影响,最后再决定什么才是最适合这个项目的。
-一定不要为了使用 RPC 而每个项目都用 RPC,而是要因地制宜,具体情况具体分析。
\ No newline at end of file
+一定不要为了使用 RPC 而每个项目都用 RPC,而是要因地制宜,具体情况具体分析。
diff --git a/docs/advance/excellent-article/16-what-is-jwt.md b/docs/advance/excellent-article/16-what-is-jwt.md
index 0ca03ab..2fc9f62 100644
--- a/docs/advance/excellent-article/16-what-is-jwt.md
+++ b/docs/advance/excellent-article/16-what-is-jwt.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 什么是JWT
+category: 优质文章
+tag:
+ - web
+head:
+ - - meta
+ - name: keywords
+ content: jwt,web开发
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 什么是JWT
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
@@ -166,4 +181,4 @@ fetch('api/user/1', {
- [什么是 JWT](https://www.jianshu.com/p/576dbf44b2ae)
-- [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
\ No newline at end of file
+- [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
diff --git a/docs/advance/excellent-article/17-limit-scheme.md b/docs/advance/excellent-article/17-limit-scheme.md
index 4cc3051..d49d6e0 100644
--- a/docs/advance/excellent-article/17-limit-scheme.md
+++ b/docs/advance/excellent-article/17-limit-scheme.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 限流的几种方案
+category: 优质文章
+tag:
+ - 实践经验
+head:
+ - - meta
+ - name: keywords
+ content: 限流方案,令牌桶算法,漏桶算法,滑动窗口,Guava限流,网关层限流
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 限流的几种方案
### 文章目录
@@ -21,7 +36,7 @@
- 中间件限流
- 限流组件
- 合法性验证限流
- - Guawa限流
+ - Guava限流
- 网关层限流
- 从架构维度考虑限流设计
@@ -209,4 +224,4 @@ Tomcat 8.5 版本的最大线程数在 `conf/server.xml` 配置中,maxThreads
-> 参考链接:blog.csdn.net/liuerchong/article/details/118882053
\ No newline at end of file
+> 参考链接:blog.csdn.net/liuerchong/article/details/118882053
diff --git a/docs/advance/excellent-article/18-db-connect-resource.md b/docs/advance/excellent-article/18-db-connect-resource.md
index 5f73d17..d016413 100644
--- a/docs/advance/excellent-article/18-db-connect-resource.md
+++ b/docs/advance/excellent-article/18-db-connect-resource.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 为什么说数据库连接很消耗资源
+category: 优质文章
+tag:
+ - 实践经验
+head:
+ - - meta
+ - name: keywords
+ content: 数据库连接
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 为什么说数据库连接很消耗资源
相信有过工作经验的同学都知道数据库连接是一个比较耗资源的操作。那么资源到底是耗费在哪里呢?
@@ -92,3 +107,5 @@ conn.close();
总之,数据库连接真的很耗时,所以不要频繁的**建立连接**。
+
+
diff --git a/docs/advance/excellent-article/19-java19.md b/docs/advance/excellent-article/19-java19.md
index 1461ce1..fdf8a95 100644
--- a/docs/advance/excellent-article/19-java19.md
+++ b/docs/advance/excellent-article/19-java19.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Java19新特性
+category: 优质文章
+tag:
+ - java
+head:
+ - - meta
+ - name: keywords
+ content: Java19新特性
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Java19新特性
JDK 19 / Java 19 已正式发布。
@@ -53,4 +68,4 @@ JDK 19 引入了结构化并发,这是一种多线程编程方法,目的是
下载地址:https://jdk.java.net/19/
-Release Note:https://jdk.java.net/19/release-notes
\ No newline at end of file
+Release Note:https://jdk.java.net/19/release-notes
diff --git a/docs/advance/excellent-article/2-spring-transaction.md b/docs/advance/excellent-article/2-spring-transaction.md
index b13edcd..6567d64 100644
--- a/docs/advance/excellent-article/2-spring-transaction.md
+++ b/docs/advance/excellent-article/2-spring-transaction.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Transactional事务注解详解
+category: 优质文章
+tag:
+ - Spring
+head:
+ - - meta
+ - name: keywords
+ content: Spring事务,事务传播行为,事务注解
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# @Transactional 事务注解详解
## Spring事务的传播行为
@@ -108,4 +123,4 @@ Service中实现对事务的控制:实现类(各种情况的说明都写在
-> 原文:blog.csdn.net/fanxb92/article/details/81296005
\ No newline at end of file
+> 原文:blog.csdn.net/fanxb92/article/details/81296005
diff --git a/docs/advance/excellent-article/20-architect-pattern.md b/docs/advance/excellent-article/20-architect-pattern.md
index fd5b5c8..82d68ab 100644
--- a/docs/advance/excellent-article/20-architect-pattern.md
+++ b/docs/advance/excellent-article/20-architect-pattern.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 几种常见的架构模式
+category: 优质文章
+tag:
+ - 架构
+head:
+ - - meta
+ - name: keywords
+ content: 架构模式
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 几种常见的架构模式
分享一些工作中会用到的一些架构方面的设计模式。总体而言,共有八种,分别是:
@@ -231,4 +246,4 @@
-> 参考链接:https://juejin.cn/post/6844904007438172167
\ No newline at end of file
+> 参考链接:https://juejin.cn/post/6844904007438172167
diff --git a/docs/advance/excellent-article/22-distributed-scheduled-task.md b/docs/advance/excellent-article/22-distributed-scheduled-task.md
index e3ad034..2d643a5 100644
--- a/docs/advance/excellent-article/22-distributed-scheduled-task.md
+++ b/docs/advance/excellent-article/22-distributed-scheduled-task.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 新一代分布式任务调度框架
+category: 优质文章
+tag:
+ - 分布式
+head:
+ - - meta
+ - name: keywords
+ content: 分布式任务调度框架,任务调度
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 新一代分布式任务调度框架
我们先思考下面几个业务场景的解决方案:
@@ -132,4 +147,4 @@ E-Job和X-job都有广泛的用户基础和完整的技术文档,都能满足
-> 摘录自网络
\ No newline at end of file
+> 摘录自网络
diff --git a/docs/advance/excellent-article/23-arthas-intro.md b/docs/advance/excellent-article/23-arthas-intro.md
index 513cbcd..24a19bc 100644
--- a/docs/advance/excellent-article/23-arthas-intro.md
+++ b/docs/advance/excellent-article/23-arthas-intro.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Arthas 常用命令
+category: 优质文章
+tag:
+ - JVM
+head:
+ - - meta
+ - name: keywords
+ content: Arthas常用命令,Arthas
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Arthas 常用命令
### 简介
@@ -136,7 +151,7 @@ sc -d cn.test.mobile.controller.order.OrderController
classLoaderHash 18b4aac2
```
-###### 与之相应的还有sm( “Search-Method” ),查看已加载类的方法信息
+与之相应的还有sm( “Search-Method” ),查看已加载类的方法信息
查看String里的方法
@@ -478,4 +493,4 @@ java -jar arthas-boot.jar
[ERROR] 2. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
```
-注意提示[ERROR] 1,只需要进入11544这个应用,然后执行shutdown关闭这个应用就可以启动了
\ No newline at end of file
+注意提示[ERROR] 1,只需要进入11544这个应用,然后执行shutdown关闭这个应用就可以启动了
diff --git a/docs/advance/excellent-article/24-generic.md b/docs/advance/excellent-article/24-generic.md
index 94e97d0..46aa386 100644
--- a/docs/advance/excellent-article/24-generic.md
+++ b/docs/advance/excellent-article/24-generic.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 泛型详解
+category: 优质文章
+tag:
+ - java
+head:
+ - - meta
+ - name: keywords
+ content: 泛型
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 泛型详解
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。
@@ -274,4 +289,4 @@ public class Test3 {
本文零碎整理了下 JAVA 泛型中的一些点,不是很全,仅供参考。如果文中有不当的地方,欢迎指正。
-> 转自:juejin.im/post/5d5789d26fb9a06ad0056bd9
\ No newline at end of file
+> 转自:juejin.im/post/5d5789d26fb9a06ad0056bd9
diff --git a/docs/advance/excellent-article/25-select-count-slow-query.md b/docs/advance/excellent-article/25-select-count-slow-query.md
index a53f6e9..0300ea2 100644
--- a/docs/advance/excellent-article/25-select-count-slow-query.md
+++ b/docs/advance/excellent-article/25-select-count-slow-query.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: SELECT COUNT(*) 会造成全表扫描?回去等通知吧
+category: 优质文章
+tag:
+ - 数据库
+head:
+ - - meta
+ - name: keywords
+ content: count(*),全表扫描,执行计划
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
+
# SELECT COUNT(*) 会造成全表扫描?回去等通知吧
## 前言
@@ -203,4 +219,4 @@ SET optimizer_trace="enabled=off";
## 总结
-本文通过一个例子深入剖析了 MySQL 的执行计划是如何选择的,以及为什么它的选择未必是我们认为的最优的,这也提醒我们,在生产中如果有多个索引的情况,使用 WHERE 进行过滤未必会选中你认为的索引,我们可以提前使用 EXPLAIN, optimizer trace 来优化我们的查询语句。
\ No newline at end of file
+本文通过一个例子深入剖析了 MySQL 的执行计划是如何选择的,以及为什么它的选择未必是我们认为的最优的,这也提醒我们,在生产中如果有多个索引的情况,使用 WHERE 进行过滤未必会选中你认为的索引,我们可以提前使用 EXPLAIN, optimizer trace 来优化我们的查询语句。
diff --git a/docs/advance/excellent-article/26-java-stream.md b/docs/advance/excellent-article/26-java-stream.md
index ffb4229..7330950 100644
--- a/docs/advance/excellent-article/26-java-stream.md
+++ b/docs/advance/excellent-article/26-java-stream.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Java Stream常见用法汇总,开发效率大幅提升
+category: 优质文章
+tag:
+ - java
+head:
+ - - meta
+ - name: keywords
+ content: stream流操作,java stream,java8
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Java Stream常见用法汇总,开发效率大幅提升
Java8 新增的 Stream 流大大减轻了我们代码的工作量,但是 Stream 流的用法较多,实际使用的时候容易遗忘,整理一下供大家参考。
@@ -181,4 +196,4 @@ String names = users.stream().map(User::getName).collect(Collectors.joining(",")
---end--
\ No newline at end of file
+--end--
diff --git a/docs/advance/excellent-article/27-mq-usage.md b/docs/advance/excellent-article/27-mq-usage.md
index 76e36f3..5ccf9b9 100644
--- a/docs/advance/excellent-article/27-mq-usage.md
+++ b/docs/advance/excellent-article/27-mq-usage.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 消息队列常见的使用场景
+category: 优质文章
+tag:
+ - 消息队列
+head:
+ - - meta
+ - name: keywords
+ content: 消息队列使用场景,异步
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 消息队列常见的使用场景
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题
@@ -107,4 +122,16 @@
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
-*本文转载自:https://www.cnblogs.com/ruiati/p/6649868.html*
\ No newline at end of file
+
+
+
+
+
+
+最后给大家分享一个Github仓库,上面有大彬整理的**300多本经典的计算机书籍PDF**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
+
+
+
+
+
+[**Github地址**](https://github.com/Tyson0314/java-books)
\ No newline at end of file
diff --git a/docs/advance/excellent-article/28-springboot-forbid-tomcat.md b/docs/advance/excellent-article/28-springboot-forbid-tomcat.md
index a008861..3d55a1a 100644
--- a/docs/advance/excellent-article/28-springboot-forbid-tomcat.md
+++ b/docs/advance/excellent-article/28-springboot-forbid-tomcat.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 大公司为什么禁止SpringBoot项目使用Tomcat?
+category: 优质文章
+tag:
+ - Spring Boot
+head:
+ - - meta
+ - name: keywords
+ content: Spring Boot,Tomcat
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 大公司为什么禁止SpringBoot项目使用Tomcat?
## 前言
@@ -6,7 +21,7 @@
## SpringBoot中的Tomcat容器
-SpringBoot可以说是目前最火的Java Web框架了。它将开发者从繁重的xml解救了出来,让开发者在几分钟内就可以创建一个完整的Web服务,极大的提高了开发者的工作效率。Web容器技术是Web项目必不可少的组成部分,因为任Web项目都要借助容器技术来运行起来。在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。推荐:[几乎涵盖你需要的SpringBoot所有操作](https://link.juejin.cn?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2FCPtdGgzcvAv6JglKTioScQ)。
+SpringBoot可以说是目前最火的Java Web框架了。它将开发者从繁重的xml解救了出来,让开发者在几分钟内就可以创建一个完整的Web服务,极大的提高了开发者的工作效率。Web容器技术是Web项目必不可少的组成部分,因为任Web项目都要借助容器技术来运行起来。在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。推荐:[最全面的Java面试网站](https://topjavaer.cn)
## SpringBoot设置Undertow
@@ -54,4 +69,4 @@ SpingBoot中我们既可以使用Tomcat作为Http服务,也可以用Undertow
-> 参考链接:原文地址:toutiao.com/a677547665941699021
\ No newline at end of file
+> 参考链接:原文地址:toutiao.com/a677547665941699021
diff --git a/docs/advance/excellent-article/29-idempotent-design.md b/docs/advance/excellent-article/29-idempotent-design.md
new file mode 100644
index 0000000..6cf7d5b
--- /dev/null
+++ b/docs/advance/excellent-article/29-idempotent-design.md
@@ -0,0 +1,34 @@
+---
+sidebar: heading
+title: 接口的幂等性如何设计?
+category: 优质文章
+tag:
+ - Spring Boot
+head:
+ - - meta
+ - name: keywords
+ content: 接口幂等,幂等性
+ - - meta
+ - name: description
+ content: 努力打造最优质的Java学习网站
+---
+
+## 接口的幂等性如何设计?
+
+分布式系统中的某个接口,该如何保证幂等性?
+
+假如有个服务提供一个付款接口供外部调用,这个服务部署在了 5 台机器上。然后用户在前端上操作的时候,不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,结果一个订单扣款扣两次。
+
+这就是典型的接口幂等性问题。
+
+所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。
+
+其实保证幂等性主要是三点:
+
+对于每个请求必须有一个唯一的标识,举个例子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次。每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在数据库中记录一个状态,比如支付之前记录一条这个订单的支付流水。
+
+每次接收请求需要进行判断,判断之前是否处理过。如果订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
+
+实际运作过程中,你要结合自己的业务来,比如说利用 Redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
+
+要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 unique key 。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 Redis 里面去, set order_id payed ,下一次重复请求过来了,先查 Redis 的 order_id 对应的 value,如果是 payed 就说明已经支付过了,就别重复支付了。
diff --git a/docs/advance/excellent-article/3-springboot-auto-assembly.md b/docs/advance/excellent-article/3-springboot-auto-assembly.md
index 4383d1b..2e1dcfa 100644
--- a/docs/advance/excellent-article/3-springboot-auto-assembly.md
+++ b/docs/advance/excellent-article/3-springboot-auto-assembly.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Spring Boot 自动装配原理
+category: 优质文章
+tag:
+ - Spring Boot
+head:
+ - - meta
+ - name: keywords
+ content: SpringBoot自动装配原理,自动装配
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Spring Boot 自动装配原理
首先,先看SpringBoot的主配置类:
@@ -541,4 +556,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConf
然后这样一个简单的stater就完成了,然后可以进行maven的打包,在其他项目引入就可以使用。
-> 来源:cnblogs.com/jing99/p/11504113.html
\ No newline at end of file
+> 来源:cnblogs.com/jing99/p/11504113.html
diff --git a/docs/advance/excellent-article/30-yi-di-duo-huo.md b/docs/advance/excellent-article/30-yi-di-duo-huo.md
new file mode 100644
index 0000000..75657f1
--- /dev/null
+++ b/docs/advance/excellent-article/30-yi-di-duo-huo.md
@@ -0,0 +1,563 @@
+---
+sidebar: heading
+title: 异地多活
+category: 优质文章
+tag:
+ - 架构
+head:
+ - - meta
+ - name: keywords
+ content: 异地多活,同城灾备,同城双活,两地三中心,异地双活,异地多活
+ - - meta
+ - name: description
+ content: 努力打造最优质的Java学习网站
+---
+
+在软件开发领域,「异地多活」是分布式系统架构设计的一座高峰,很多人经常听过它,但很少人理解其中的原理。
+
+**异地多活到底是什么?为什么需要异地多活?它到底解决了什么问题?究竟是怎么解决的?**
+
+这些疑问,想必是每个程序看到异地多活这个名词时,都想要搞明白的问题。
+
+有幸,我曾经深度参与过一个中等互联网公司,建设异地多活系统的设计与实施过程。所以今天,我就来和你聊一聊异地多活背后的的实现原理。
+
+认真读完这篇文章,我相信你会对异地多活架构,有更加深刻的理解。
+
+**这篇文章干货很多,希望你可以耐心读完。**
+
+
+
+
+
+# 01 系统可用性
+
+要想理解异地多活,我们需要从架构设计的原则说起。
+
+现如今,我们开发一个软件系统,对其要求越来越高,如果你了解一些「架构设计」的要求,就知道一个好的软件架构应该遵循以下 3 个原则:
+
+1. 高性能
+2. 高可用
+3. 易扩展
+
+其中,高性能意味着系统拥有更大流量的处理能力,更低的响应延迟。例如 1 秒可处理 10W 并发请求,接口响应时间 5 ms 等等。
+
+易扩展表示系统在迭代新功能时,能以最小的代价去扩展,系统遇到流量压力时,可以在不改动代码的前提下,去扩容系统。
+
+而「高可用」这个概念,看起来很抽象,怎么理解它呢?通常用 2 个指标来衡量:
+
+- **平均故障间隔 MTBF**(Mean Time Between Failure):表示两次故障的间隔时间,也就是系统「正常运行」的平均时间,这个时间越长,说明系统稳定性越高
+- **故障恢复时间 MTTR**(Mean Time To Repair):表示系统发生故障后「恢复的时间」,这个值越小,故障对用户的影响越小
+
+可用性与这两者的关系:
+
+> 可用性(Availability)= MTBF / (MTBF + MTTR) * 100%
+
+这个公式得出的结果是一个「比例」,通常我们会用「N 个 9」来描述一个系统的可用性。
+
+
+
+从这张图你可以看到,要想达到 4 个 9 以上的可用性,平均每天故障时间必须控制在 10 秒以内。
+
+也就是说,只有故障的时间「越短」,整个系统的可用性才会越高,每提升 1 个 9,都会对系统提出更高的要求。
+
+我们都知道,系统发生故障其实是不可避免的,尤其是规模越大的系统,发生问题的概率也越大。这些故障一般体现在 3 个方面:
+
+1. **硬件故障**:CPU、内存、磁盘、网卡、交换机、路由器
+2. **软件问题**:代码 Bug、版本迭代
+3. **不可抗力**:地震、水灾、火灾、战争
+
+这些风险随时都有可能发生。所以,在面对故障时,我们的系统能否以「最快」的速度恢复,就成为了可用性的关键。
+
+可如何做到快速恢复呢?
+
+这篇文章要讲的「异地多活」架构,就是为了解决这个问题,而提出的高效解决方案。
+
+下面,我会从一个最简单的系统出发,带你一步步演化出一个支持「异地多活」的系统架构。
+
+在这个过程中,你会看到一个系统会遇到哪些可用性问题,以及为什么架构要这样演进,从而理解异地多活架构的意义。
+
+# 02 单机架构
+
+我们从最简单的开始讲起。
+
+假设你的业务处于起步阶段,体量非常小,那你的架构是这样的:
+
+
+
+这个架构模型非常简单,客户端请求进来,业务应用读写数据库,返回结果,非常好理解。
+
+但需要注意的是,这里的数据库是「单机」部署的,所以它有一个致命的缺点:一旦遭遇意外,例如磁盘损坏、操作系统异常、误删数据,那这意味着所有数据就全部「丢失」了,这个损失是巨大的。
+
+如何避免这个问题呢?我们很容易想到一个方案:**备份**。
+
+
+
+你可以对数据做备份,把数据库文件「定期」cp 到另一台机器上,这样,即使原机器丢失数据,你依旧可以通过备份把数据「恢复」回来,以此保证数据安全。
+
+这个方案实施起来虽然比较简单,但存在 2 个问题:
+
+1. **恢复需要时间**:业务需先停机,再恢复数据,停机时间取决于恢复的速度,恢复期间服务「不可用」
+2. **数据不完整**:因为是定期备份,数据肯定不是「最新」的,数据完整程度取决于备份的周期
+
+很明显,你的数据库越大,意味故障恢复时间越久。那按照前面我们提到的「高可用」标准,这个方案可能连 1 个 9 都达不到,远远无法满足我们对可用性的要求。
+
+那有什么更好的方案,既可以快速恢复业务?还能尽可能保证数据完整性呢?
+
+这时你可以采用这个方案:**主从副本**。
+
+# 03 主从副本
+
+你可以在另一台机器上,再部署一个数据库实例,让这个新实例成为原实例的「副本」,让两者保持「实时同步」,就像这样:
+
+
+
+我们一般把原实例叫作主库(master),新实例叫作从库(slave)。这个方案的优点在于:
+
+- **数据完整性高**:主从副本实时同步,数据「差异」很小
+- **抗故障能力提升**:主库有任何异常,从库可随时「切换」为主库,继续提供服务
+- **读性能提升**:业务应用可直接读从库,分担主库「压力」读压力
+
+这个方案不错,不仅大大提高了数据库的可用性,还提升了系统的读性能。
+
+同样的思路,你的「业务应用」也可以在其它机器部署一份,避免单点。因为业务应用通常是「无状态」的(不像数据库那样存储数据),所以直接部署即可,非常简单。
+
+
+
+因为业务应用部署了多个,所以你现在还需要部署一个「接入层」,来做请求的「负载均衡」(一般会使用 nginx 或 LVS),这样当一台机器宕机后,另一台机器也可以「接管」所有流量,持续提供服务。
+
+
+
+从这个方案你可以看出,提升可用性的关键思路就是:**冗余**。
+
+没错,担心一个实例故障,那就部署多个实例,担心一个机器宕机,那就部署多台机器。
+
+到这里,你的架构基本已演变成主流方案了,之后开发新的业务应用,都可以按照这种模式去部署。
+
+
+
+但这种方案还有什么风险吗?
+
+# 04 风险不可控
+
+现在让我们把视角下放,把焦点放到具体的「部署细节」上来。
+
+按照前面的分析,为了避免单点故障,你的应用虽然部署了多台机器,但这些机器的分布情况,我们并没有去深究。
+
+而一个机房有很多服务器,这些服务器通常会分布在一个个「机柜」上,如果你使用的这些机器,刚好在一个机柜,还是存在风险。
+
+如果恰好连接这个机柜的交换机 / 路由器发生故障,那么你的应用依旧有「不可用」的风险。
+
+> 虽然交换机 / 路由器也做了路线冗余,但不能保证一定不出问题。
+
+部署在一个机柜有风险,那把这些机器打散,分散到不同机柜上,是不是就没问题了?
+
+这样确实会大大降低出问题的概率。但我们依旧不能掉以轻心,因为无论怎么分散,它们总归还是在一个相同的环境下:**机房**。
+
+那继续追问,机房会不会发生故障呢?
+
+一般来讲,建设一个机房的要求其实是很高的,地理位置、温湿度控制、备用电源等等,机房厂商会在各方面做好防护。但即使这样,我们每隔一段时间还会看到这样的新闻:
+
+- 2015 年 5 月 27 日,杭州市某地光纤被挖断,近 3 亿用户长达 5 小时无法访问支付宝
+- 2021 年 7 月 13 日,B 站部分服务器机房发生故障,造成整站持续 3 个小时无法访问
+- 2021 年 10 月 9 日,富途证券服务器机房发生电力闪断故障,造成用户 2 个小时无法登陆、交易
+- …
+
+可见,即使机房级别的防护已经做得足够好,但只要有「概率」出问题,那现实情况就有可能发生。虽然概率很小,但一旦真的发生,影响之大可见一斑。
+
+看到这里你可能会想,机房出现问题的概率也太小了吧,工作了这么多年,也没让我碰上一次,有必要考虑得这么复杂吗?
+
+但你有没有思考这样一个问题:**不同体量的系统,它们各自关注的重点是什么?**
+
+体量很小的系统,它会重点关注「用户」规模、增长,这个阶段获取用户是一切。等用户体量上来了,这个阶段会重点关注「性能」,优化接口响应时间、页面打开速度等等,这个阶段更多是关注用户体验。
+
+等体量再大到一定规模后你会发现,「可用性」就变得尤为重要。像微信、支付宝这种全民级的应用,如果机房发生一次故障,那整个影响范围可以说是非常巨大的。
+
+所以,再小概率的风险,我们在提高系统可用性时,也不能忽视。
+
+分析了风险,再说回我们的架构。那到底该怎么应对机房级别的故障呢?
+
+没错,还是**冗余**。
+
+# 05 同城灾备
+
+想要抵御「机房」级别的风险,那应对方案就不能局限在一个机房内了。
+
+现在,你需要做机房级别的冗余方案,也就是说,你需要再搭建一个机房,来部署你的服务。
+
+简单起见,你可以在「同一个城市」再搭建一个机房,原机房我们叫作 A 机房,新机房叫 B 机房,这两个机房的网络用一条「专线」连通。
+
+
+
+有了新机房,怎么把它用起来呢?这里还是要优先考虑「数据」风险。
+
+为了避免 A 机房故障导致数据丢失,所以我们需要把数据在 B 机房也存一份。最简单的方案还是和前面提到的一样:**备份**。
+
+A 机房的数据,定时在 B 机房做备份(拷贝数据文件),这样即使整个 A 机房遭到严重的损坏,B 机房的数据不会丢,通过备份可以把数据「恢复」回来,重启服务。
+
+
+
+这种方案,我们称之为「**冷备**」。为什么叫冷备呢?因为 B 机房只做备份,不提供实时服务,它是冷的,只会在 A 机房故障时才会启用。
+
+但备份的问题依旧和之前描述的一样:数据不完整、恢复数据期间业务不可用,整个系统的可用性还是无法得到保证。
+
+所以,我们还是需要用「主从副本」的方式,在 B 机房部署 A 机房的数据副本,架构就变成了这样:
+
+
+
+这样,就算整个 A 机房挂掉,我们在 B 机房也有比较「完整」的数据。
+
+数据是保住了,但这时你需要考虑另外一个问题:**如果 A 机房真挂掉了,要想保证服务不中断,你还需要在 B 机房「紧急」做这些事情**:
+
+1. B 机房所有从库提升为主库
+2. 在 B 机房部署应用,启动服务
+3. 部署接入层,配置转发规则
+4. DNS 指向 B 机房接入层,接入流量,业务恢复
+
+看到了么?A 机房故障后,B 机房需要做这么多工作,你的业务才能完全「恢复」过来。
+
+你看,整个过程需要人为介入,且需花费大量时间来操作,恢复之前整个服务还是不可用的,这个方案还是不太爽,如果能做到故障后立即「切换」,那就好了。
+
+因此,要想缩短业务恢复的时间,你必须把这些工作在 B 机房「提前」做好,也就是说,你需要在 B 机房提前部署好接入层、业务应用,等待随时切换。架构就变成了这样:
+
+
+
+这样的话,A 机房整个挂掉,我们只需要做 2 件事即可:
+
+1. B 机房所有从库提升为主库
+2. DNS 指向 B 机房接入层,接入流量,业务恢复
+
+这样一来,恢复速度快了很多。
+
+到这里你会发现,B 机房从最开始的「空空如也」,演变到现在,几乎是「镜像」了一份 A 机房的所有东西,从最上层的接入层,到中间的业务应用,到最下层的存储。两个机房唯一的区别是,**A 机房的存储都是主库,而 B 机房都是从库**。
+
+这种方案,我们把它叫做「**热备**」。
+
+热的意思是指,B 机房处于「待命」状态,A 故障后 B 可以随时「接管」流量,继续提供服务。热备相比于冷备最大的优点是:**随时可切换**。
+
+无论是冷备还是热备,因为它们都处于「备用」状态,所以我们把这两个方案统称为:**同城灾备**。
+
+同城灾备的最大优势在于,我们再也不用担心「机房」级别的故障了,一个机房发生风险,我们只需把流量切换到另一个机房即可,可用性再次提高,是不是很爽?(后面还有更爽的)
+
+# 06 同城双活
+
+我们继续来看这个架构。
+
+虽然我们有了应对机房故障的解决方案,但这里有个问题是我们不能忽视的:**A 机房挂掉,全部流量切到 B 机房,B 机房能否真的如我们所愿,正常提供服务?**
+
+这是个值得思考的问题。
+
+这就好比有两支军队 A 和 B,A 军队历经沙场,作战经验丰富,而 B 军队只是后备军,除了有军人的基本素养之外,并没有实战经验,战斗经验基本为 0。
+
+如果 A 军队丧失战斗能力,需要 B 军队立即顶上时,作为指挥官的你,肯定也会担心 B 军队能否真的担此重任吧?
+
+我们的架构也是如此,此时的 B 机房虽然是随时「待命」状态,但 A 机房真的发生故障,我们要把全部流量切到 B 机房,其实是不敢百分百保证它可以「如期」工作的。
+
+你想,我们在一个机房内部署服务,还总是发生各种各样的问题,例如:发布应用的版本不一致、系统资源不足、操作系统参数不一样等等。现在多部署一个机房,这些问题只会增多,不会减少。
+
+另外,从「成本」的角度来看,我们新部署一个机房,需要购买服务器、内存、硬盘、带宽资源,花费成本也是非常高昂的,只让它当一个后备军,未免也太「大材小用」了!
+
+因此,我们需要让 B 机房也接入流量,实时提供服务,这样做的好处,**一是可以实时训练这支后备军,让它达到与 A 机房相同的作战水平,随时可切换,二是 B 机房接入流量后,可以分担 A 机房的流量压力**。这才是把 B 机房资源优势,发挥最大化的最好方案!
+
+那怎么让 B 机房也接入流量呢?很简单,就是把 B 机房的接入层 IP 地址,加入到 DNS 中,这样,B 机房从上层就可以有流量进来了。
+
+
+
+但这里有一个问题:别忘了,B 机房的存储,现在可都是 A 机房的「从库」,从库默认可都是「不可写」的,B 机房的写请求打到本机房存储上,肯定会报错,这还是不符合我们预期。怎么办?
+
+这时,你就需要在「业务应用」层做改造了。
+
+你的业务应用在操作数据库时,需要区分「读写分离」(一般用中间件实现),即两个机房的「读」流量,可以读任意机房的存储,但「写」流量,只允许写 A 机房,因为主库在 A 机房。
+
+
+
+这会涉及到你用的所有存储,例如项目中用到了 MySQL、Redis、MongoDB 等等,操作这些数据库,都需要区分读写请求,所以这块需要一定的业务「改造」成本。
+
+因为 A 机房的存储都是主库,所以我们把 A 机房叫做「主机房」,B 机房叫「从机房」。
+
+两个机房部署在「同城」,物理距离比较近,而且两个机房用「专线」网络连接,虽然跨机房访问的延迟,比单个机房内要大一些,但整体的延迟还是可以接受的。
+
+业务改造完成后,B 机房可以慢慢接入流量,从 10%、30%、50% 逐渐覆盖到 100%,你可以持续观察 B 机房的业务是否存在问题,有问题及时修复,逐渐让 B 机房的工作能力,达到和 A 机房相同水平。
+
+现在,因为 B 机房实时接入了流量,此时如果 A 机房挂了,那我们就可以「大胆」地把 A 的流量,全部切换到 B 机房,完成快速切换!
+
+到这里你可以看到,我们部署的 B 机房,在物理上虽然与 A 有一定距离,但整个系统从「逻辑」上来看,我们是把这两个机房看做一个「整体」来规划的,也就是说,相当于把 2 个机房当作 1 个机房来用。
+
+这种架构方案,比前面的同城灾备更「进了一步」,B 机房实时接入了流量,还能应对随时的故障切换,这种方案我们把它叫做「**同城双活**」。
+
+因为两个机房都能处理业务请求,这对我们系统的内部维护、改造、升级提供了更多的可实施空间(流量随时切换),现在,整个系统的弹性也变大了,是不是更爽了?
+
+那这种架构有什么问题呢?
+
+# 07 两地三中心
+
+还是回到风险上来说。
+
+虽然我们把 2 个机房当做一个整体来规划,但这 2 个机房在物理层面上,还是处于「一个城市」内,如果是整个城市发生自然灾害,例如地震、水灾(河南水灾刚过去不久),那 2 个机房依旧存在「全局覆没」的风险。
+
+真是防不胜防啊?怎么办?没办法,继续冗余。
+
+但这次冗余机房,就不能部署在同一个城市了,你需要把它放到距离更远的地方,部署在「异地」。
+
+> 通常建议两个机房的距离要在 1000 公里以上,这样才能应对城市级别的灾难。
+
+假设之前的 A、B 机房在北京,那这次新部署的 C 机房可以放在上海。
+
+按照前面的思路,把 C 机房用起来,最简单粗暴的方案还就是做「冷备」,即定时把 A、B 机房的数据,在 C 机房做备份,防止数据丢失。
+
+
+
+这种方案,就是我们经常听到的「**两地三中心**」。
+
+**两地是指 2 个城市,三中心是指有 3 个机房,其中 2 个机房在同一个城市,并且同时提供服务,第 3 个机房部署在异地,只做数据灾备。**
+
+这种架构方案,通常用在银行、金融、政企相关的项目中。它的问题还是前面所说的,启用灾备机房需要时间,而且启用后的服务,不确定能否如期工作。
+
+所以,要想真正的抵御城市级别的故障,越来越多的互联网公司,开始实施「**异地双活**」。
+
+# 08 伪异地双活
+
+这里,我们还是分析 2 个机房的架构情况。我们不再把 A、B 机房部署在同一个城市,而是分开部署,例如 A 机房放在北京,B 机房放在上海。
+
+前面我们讲了同城双活,那异地双活是不是直接「照搬」同城双活的模式去部署就可以了呢?
+
+事情没你想的那么简单。
+
+如果还是按照同城双活的架构来部署,那异地双活的架构就是这样的:
+
+
+
+注意看,两个机房的网络是通过「跨城专线」连通的。
+
+此时两个机房都接入流量,那上海机房的请求,可能要去读写北京机房的存储,这里存在一个很大的问题:**网络延迟**。
+
+因为两个机房距离较远,受到物理距离的限制,现在,两地之间的网络延迟就变成了「**不可忽视**」的因素了。
+
+北京到上海的距离大约 1300 公里,即使架设一条高速的「网络专线」,光纤以光速传输,一个来回也需要近 10ms 的延迟。
+
+况且,网络线路之间还会经历各种路由器、交换机等网络设备,实际延迟可能会达到 30ms ~ 100ms,如果网络发生抖动,延迟甚至会达到 1 秒。
+
+> 不止是延迟,远距离的网络专线质量,是远远达不到机房内网络质量的,专线网络经常会发生延迟、丢包、甚至中断的情况。总之,不能过度信任和依赖「跨城专线」。
+
+你可能会问,这点延迟对业务影响很大吗?影响非常大!
+
+试想,一个客户端请求打到上海机房,上海机房要去读写北京机房的存储,一次跨机房访问延迟就达到了 30ms,这大致是机房内网网络(0.5 ms)访问速度的 60 倍(30ms / 0.5ms),一次请求慢 60 倍,来回往返就要慢 100 倍以上。
+
+而我们在 App 打开一个页面,可能会访问后端几十个 API,每次都跨机房访问,整个页面的响应延迟有可能就达到了**秒级**,这个性能简直惨不忍睹,难以接受。
+
+看到了么,虽然我们只是简单的把机房部署在了「异地」,但「同城双活」的架构模型,在这里就不适用了,还是按照这种方式部署,这是「伪异地双活」!
+
+那如何做到真正的异地双活呢?
+
+# 09 真正的异地双活
+
+既然「跨机房」调用延迟是不容忽视的因素,那我们只能尽量避免跨机房「调用」,规避这个延迟问题。
+
+也就是说,上海机房的应用,不能再「跨机房」去读写北京机房的存储,只允许读写上海本地的存储,实现「就近访问」,这样才能避免延迟问题。
+
+还是之前提到的问题:上海机房存储都是从库,不允许写入啊,除非我们只允许上海机房接入「读流量」,不接收「写流量」,否则无法满足不再跨机房的要求。
+
+很显然,只让上海机房接收读流量的方案不现实,因为很少有项目是只有读流量,没有写流量的。所以这种方案还是不行,这怎么办?
+
+此时,你就必须在「**存储层**」做改造了。
+
+要想上海机房读写本机房的存储,那上海机房的存储不能再是北京机房的从库,而是也要变为「主库」。
+
+你没看错,两个机房的存储必须都是「**主库**」,而且两个机房的数据还要「**互相同步**」数据,即客户端无论写哪一个机房,都能把这条数据同步到另一个机房。
+
+因为只有两个机房都拥有「全量数据」,才能支持任意切换机房,持续提供服务。
+
+怎么实现这种「双主」架构呢?它们之间如何互相同步数据?
+
+如果你对 MySQL 有所了解,MySQL 本身就提供了双主架构,它支持双向复制数据,但平时用的并不多。而且 Redis、MongoDB 等数据库并没有提供这个功能,所以,你必须开发对应的「数据同步中间件」来实现双向同步的功能。
+
+此外,除了数据库这种有状态的软件之外,你的项目通常还会使用到消息队列,例如 RabbitMQ、Kafka,这些也是有状态的服务,所以它们也需要开发双向同步的中间件,支持任意机房写入数据,同步至另一个机房。
+
+看到了么,这一下子复杂度就上来了,单单针对每个数据库、队列开发同步中间件,就需要投入很大精力了。
+
+> 业界也开源出了很多数据同步中间件,例如阿里的 Canal、RedisShake、MongoShake,可分别在两个机房同步 MySQL、Redis、MongoDB 数据。
+>
+> 很多有能力的公司,也会采用自研同步中间件的方式来做,例如饿了么、携程、美团都开发了自己的同步中间件。
+>
+> 我也有幸参与设计开发了 MySQL、Redis/Codis、MongoDB 的同步中间件,有时间写一篇文章详细聊聊实现细节,欢迎持续关注。:)
+
+现在,整个架构就变成了这样:
+
+
+
+注意看,两个机房的存储层都互相同步数据的。有了数据同步中间件,就可以达到这样的效果:
+
+- 北京机房写入 X = 1
+- 上海机房写入 Y = 2
+- 数据通过中间件双向同步
+- 北京、上海机房都有 X = 1、Y = 2 的数据
+
+这里我们用中间件双向同步数据,就不用再担心专线问题,专线出问题,我们的中间件可以自动重试,直到成功,达到数据最终一致。
+
+但这里还会遇到一个问题,两个机房都可以写,操作的不是同一条数据那还好,如果修改的是同一条的数据,发生冲突怎么办?
+
+- 用户短时间内发了 2 个修改请求,都是修改同一条数据
+- 一个请求落在北京机房,修改 X = 1(还未同步到上海机房)
+- 另一个请求落在上海机房,修改 X = 2(还未同步到北京机房)
+- 两个机房以哪个为准?
+
+也就是说,在很短的时间内,同一个用户修改同一条数据,两个机房无法确认谁先谁后,数据发生「冲突」。
+
+这是一个很严重的问题,系统发生故障并不可怕,可怕的是数据发生「错误」,因为修正数据的成本太高了。我们一定要避免这种情况的发生。解决这个问题,有 2 个方案。
+
+**第一个方案**,数据同步中间件要有自动「合并」数据、解决「冲突」的能力。
+
+这个方案实现起来比较复杂,要想合并数据,就必须要区分出「先后」顺序。我们很容易想到的方案,就是以「时间」为标尺,以「后到达」的请求为准。
+
+但这种方案需要两个机房的「时钟」严格保持一致才行,否则很容易出现问题。例如:
+
+- 第 1 个请求落到北京机房,北京机房时钟是 10:01,修改 X = 1
+- 第 2 个请求落到上海机房,上海机房时钟是 10:00,修改 X = 2
+
+因为北京机房的时间「更晚」,那最终结果就会是 X = 1。但这里其实应该以第 2 个请求为准,X = 2 才对。
+
+可见,完全「依赖」时钟的冲突解决方案,不太严谨。
+
+所以,通常会采用第二种方案,从「源头」就避免数据冲突的发生。
+
+# 10 如何实施异地双活
+
+既然自动合并数据的方案实现成本高,那我们就要想,能否从源头就「避免」数据冲突呢?
+
+这个思路非常棒!
+
+从源头避免数据冲突的思路是:**在最上层接入流量时,就不要让冲突的情况发生**。
+
+具体来讲就是,要在最上层就把用户「区分」开,部分用户请求固定打到北京机房,其它用户请求固定打到上海 机房,进入某个机房的用户请求,之后的所有业务操作,都在这一个机房内完成,从根源上避免「跨机房」。
+
+所以这时,你需要在接入层之上,再部署一个「路由层」(通常部署在云服务器上),自己可以配置路由规则,把用户「分流」到不同的机房内。
+
+
+
+但这个路由规则,具体怎么定呢?有很多种实现方式,最常见的我总结了 3 类:
+
+1. 按业务类型分片
+2. 直接哈希分片
+3. 按地理位置分片
+
+**1、按业务类型分片**
+
+这种方案是指,按应用的「业务类型」来划分。
+
+举例:假设我们一共有 4 个应用,北京和上海机房都部署这些应用。但应用 1、2 只在北京机房接入流量,在上海机房只是热备。应用 3、4 只在上海机房接入流量,在北京机房是热备。
+
+这样一来,应用 1、2 的所有业务请求,只读写北京机房存储,应用 3、4 的所有请求,只会读写上海机房存储。
+
+
+
+这样按业务类型分片,也可以避免同一个用户修改同一条数据。
+
+> 这里按业务类型在不同机房接入流量,还需要考虑多个应用之间的依赖关系,要尽可能的把完成「相关」业务的应用部署在同一个机房,避免跨机房调用。
+>
+> 例如,订单、支付服务有依赖关系,会产生互相调用,那这 2 个服务在 A 机房接入流量。社区、发帖服务有依赖关系,那这 2 个服务在 B 机房接入流量。
+
+**2、直接哈希分片**
+
+这种方案就是,最上层的路由层,会根据用户 ID 计算「哈希」取模,然后从路由表中找到对应的机房,之后把请求转发到指定机房内。
+
+举例:一共 200 个用户,根据用户 ID 计算哈希值,然后根据路由规则,把用户 1 - 100 路由到北京机房,101 - 200 用户路由到上海机房,这样,就避免了同一个用户修改同一条数据的情况发生。
+
+
+
+**3、按地理位置分片**
+
+这种方案,非常适合与地理位置密切相关的业务,例如打车、外卖服务就非常适合这种方案。
+
+拿外卖服务举例,你要点外卖肯定是「就近」点餐,整个业务范围相关的有商家、用户、骑手,它们都是在相同的地理位置内的。
+
+针对这种特征,就可以在最上层,按用户的「地理位置」来做分片,分散到不同的机房。
+
+举例:北京、河北地区的用户点餐,请求只会打到北京机房,而上海、浙江地区的用户,请求则只会打到上海机房。这样的分片规则,也能避免数据冲突。
+
+
+
+> 提醒:这 3 种常见的分片规则,第一次看不太好理解,建议配合图多理解几遍。搞懂这 3 个分片规则,你才能真正明白怎么做异地多活。
+
+总之,分片的核心思路在于,**让同一个用户的相关请求,只在一个机房内完成所有业务「闭环」,不再出现「跨机房」访问**。
+
+阿里在实施这种方案时,给它起了个名字,叫做「**单元化**」。
+
+> 当然,最上层的路由层把用户分片后,理论来说同一个用户只会落在同一个机房内,但不排除程序 Bug 导致用户会在两个机房「漂移」。
+>
+> 安全起见,每个机房在写存储时,还需要有一套机制,能够检测「数据归属」,应用层操作存储时,需要通过中间件来做「兜底」,避免不该写本机房的情况发生。(篇幅限制,这里不展开讲,理解思路即可)
+
+现在,两个机房就可以都接收「读写」流量(做好分片的请求),底层存储保持「双向」同步,两个机房都拥有全量数据,当任意机房故障时,另一个机房就可以「接管」全部流量,实现快速切换,简直不要太爽。
+
+不仅如此,因为机房部署在异地,我们还可以更细化地「优化」路由规则,让用户访问就近的机房,这样整个系统的性能也会大大提升。
+
+> 这里还有一种情况,是无法做数据分片的:**全局数据**。例如系统配置、商品库存这类需要强一致的数据,这类服务依旧只能采用写主机房,读从机房的方案,不做双活。
+>
+> 双活的重点,是要优先保证「核心」业务先实现双活,并不是「全部」业务实现双活。
+
+至此,我们才算实现了真正的「**异地双活**」!
+
+> 到这里你可以看出,完成这样一套架构,需要投入的成本是巨大的。
+>
+> 路由规则、路由转发、数据同步中间件、数据校验兜底策略,不仅需要开发强大的中间件,同时还要业务配合改造(业务边界划分、依赖拆分)等一些列工作,没有足够的人力物力,这套架构很难实施。
+
+# 11 异地多活
+
+理解了异地双活,那「异地多活」顾名思义,就是在异地双活的基础上,部署多个机房即可。架构变成了这样:
+
+
+
+这些服务按照「单元化」的部署方式,可以让每个机房部署在任意地区,随时扩展新机房,你只需要在最上层定义好分片规则就好了。
+
+但这里还有一个小问题,随着扩展的机房越来越多,当一个机房写入数据后,需要同步的机房也越来越多,这个实现复杂度会比较高。
+
+所以业界又把这一架构又做了进一步优化,把「网状」架构升级为「星状」:
+
+
+
+这种方案必须设立一个「中心机房」,任意机房写入数据后,都只同步到中心机房,再由中心机房同步至其它机房。
+
+这样做的好处是,一个机房写入数据,只需要同步数据到中心机房即可,不需要再关心一共部署了多少个机房,实现复杂度大大「简化」。
+
+但与此同时,这个中心机房的「稳定性」要求会比较高。不过也还好,即使中心机房发生故障,我们也可以把任意一个机房,提升为中心机房,继续按照之前的架构提供服务。
+
+至此,我们的系统彻底实现了「**异地多活**」!
+
+多活的优势在于,**可以任意扩展机房「就近」部署。任意机房发生故障,可以完成快速「切换」**,大大提高了系统的可用性。
+
+同时,我们也再也不用担心系统规模的增长,因为这套架构具有极强的「**扩展能力**」。
+
+怎么样?我们从一个最简单的应用,一路优化下来,到最终的架构方案,有没有帮你彻底理解异地多活呢?
+
+# 总结
+
+好了,总结一下这篇文章的重点。
+
+1、一个好的软件架构,应该遵循高性能、高可用、易扩展 3 大原则,其中「高可用」在系统规模变得越来越大时,变得尤为重要
+
+2、系统发生故障并不可怕,能以「最快」的速度恢复,才是高可用追求的目标,异地多活是实现高可用的有效手段
+
+3、提升高可用的核心是「冗余」,备份、主从副本、同城灾备、同城双活、两地三中心、异地双活,异地多活都是在做冗余
+
+4、同城灾备分为「冷备」和「热备」,冷备只备份数据,不提供服务,热备实时同步数据,并做好随时切换的准备
+
+5、同城双活比灾备的优势在于,两个机房都可以接入「读写」流量,提高可用性的同时,还提升了系统性能。虽然物理上是两个机房,但「逻辑」上还是当做一个机房来用
+
+6、两地三中心是在同城双活的基础上,额外部署一个异地机房做「灾备」,用来抵御「城市」级别的灾害,但启用灾备机房需要时间
+
+7、异地双活才是抵御「城市」级别灾害的更好方案,两个机房同时提供服务,故障随时可切换,可用性高。但实现也最复杂,理解了异地双活,才能彻底理解异地多活
+
+8、异地多活是在异地双活的基础上,任意扩展多个机房,不仅又提高了可用性,还能应对更大规模的流量的压力,扩展性最强,是实现高可用的最终方案
+
+# 后记
+
+这篇文章我从「宏观」层面,向你介绍了异地多活架构的「核心」思路,整篇文章的信息量还是很大的,如果不太好理解,我建议你多读几遍。
+
+因为篇幅限制,很多细节我并没有展开来讲。这篇文章更像是讲异地多活的架构之「道」,而真正实施的「术」,要考虑的点其实也非常繁多,因为它需要开发强大的「基础设施」才可以完成实施。
+
+不仅如此,要想真正实现异地多活,还需要遵循一些原则,例如业务梳理、业务分级、数据分类、数据最终一致性保障、机房切换一致性保障、异常处理等等。同时,相关的运维设施、监控体系也要能跟得上才行。
+
+宏观上需要考虑业务(微服务部署、依赖、拆分、SDK、Web 框架)、基础设施(服务发现、流量调度、持续集成、同步中间件、自研存储),微观上要开发各种中间件,还要关注中间件的高性能、高可用、容错能力,其复杂度之高,只有亲身参与过之后才知道。
+
+我曾经有幸参与过,存储层同步中间件的设计与开发,实现过「跨机房」同步 MySQL、Redis、MongoDB 的中间件,踩过的坑也非常多。当然,这些中间件的设计思路也非常有意思,有时间单独分享一下这些中间件的设计思路。
+
+值得提醒你的是,只有真正理解了「异地双活」,才能彻底理解「异地多活」。在我看来,从同城双活演变为异地双活的过程,是最为复杂的,最核心的东西包括,**业务单元化划分、存储层数据双向同步、最上层的分片逻辑**,这些是实现异地多活的重中之重。
+
diff --git a/docs/advance/excellent-article/31-mysql-data-sync-es.md b/docs/advance/excellent-article/31-mysql-data-sync-es.md
new file mode 100644
index 0000000..8d43d94
--- /dev/null
+++ b/docs/advance/excellent-article/31-mysql-data-sync-es.md
@@ -0,0 +1,503 @@
+---
+sidebar: heading
+title: MySQL数据如何实时同步到ES
+category: 优质文章
+tag:
+ - MySQL
+head:
+ - - meta
+ - name: keywords
+ content: MySQL,ES,elasticsearch,数据同步
+ - - meta
+ - name: description
+ content: 努力打造最优质的Java学习网站
+---
+
+
+## 前言
+
+我们一般会使用MySQL用来存储数据,用Es来做全文检索和特殊查询,那么如何将数据优雅的从MySQL同步到Es呢?我们一般有以下几种方式:
+
+1.**双写**。在代码中先向MySQL中写入数据,然后紧接着向Es中写入数据。这个方法的缺点是代码严重耦合,需要手动维护MySQL和Es数据关系,非常不便于维护。
+
+2.**发MQ,异步执行**。在执行完向Mysql中写入数据的逻辑后,发送MQ,告诉消费端这个数据需要写入Es,消费端收到消息后执行向Es写入数据的逻辑。这个方式的优点是Mysql和Es数据维护分离,开发Mysql和Es的人员只需要关心各自的业务。缺点是依然需要维护发送、接收MQ的逻辑,并且引入了MQ组件,增加了系统的复杂度。
+
+3.**使用Datax进行全量数据同步**。这个方式优点是可以完全不用写维护数据关系的代码,各自只需要关心自己的业务,对代码侵入性几乎为零。缺点是Datax是一种全量同步数据的方式,不使用实时同步。如果系统对数据时效性不强,可以考虑此方式。
+
+4.**使用Canal进行实时数据同步**。这个方式具有跟Datax一样的优点,可以完全不用写维护数据关系的代码,各自只需要关心自己的业务,对代码侵入性几乎为零。与Datax不同的是Canal是一种实时同步数据的方式,对数据时效性较强的系统,我们会采用Canal来进行实时数据同步。
+
+那么就让我们来看看Canal是如何使用的。
+
+## 官网
+
+https://github.com/alibaba/canal
+
+## 1.Canal简介
+
+
+
+**canal [kə'næl]** ,译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费
+
+早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。
+
+基于日志增量订阅和消费的业务包括
+
+- 数据库镜像
+- 数据库实时备份
+- 索引构建和实时维护(拆分异构索引、倒排索引等)
+- 业务 cache 刷新
+- 带业务逻辑的增量数据处理
+
+当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x
+
+### **MySQL主备复制原理**
+
+
+
+- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
+- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
+- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
+
+### **canal工作原理**
+
+- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
+- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
+- canal 解析 binary log 对象(原始为 byte 流)
+
+## 2.开启MySQL Binlog
+
+- 对于自建 MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下
+
+```ini
+[mysqld]
+log-bin=mysql-bin # 开启 binlog
+binlog-format=ROW # 选择 ROW 模式
+server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
+lua
+## 复制代码注意:针对阿里云 RDS for MySQL , 默认打开了 binlog , 并且账号默认具有 binlog dump 权限 , 不需要任何权限或者 binlog 设置,可以直接跳过这一步
+```
+
+- 授权 canal 链接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant
+
+```sql
+CREATE USER canal IDENTIFIED BY 'canal';
+GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
+-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
+FLUSH PRIVILEGES;
+```
+
+注意:Mysql版本为8.x时启动canal可能会出现“caching_sha2_password Auth failed”错误,这是因为8.x创建用户时默认的密码加密方式为**caching_sha2_password**,与canal的方式不一致,所以需要将canal用户的密码加密方式修改为**mysql_native_password**
+
+```sql
+ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal'; #更新一下用户密码
+FLUSH PRIVILEGES; #刷新权限
+```
+
+## 3.安装Canal
+
+### 3.1 下载Canal
+
+**点击下载地址,选择版本后点击canal.deployer文件下载**
+
+
+
+### 3.2 修改配置文件
+
+打开目录下conf/example/instance.properties文件,主要修改以下内容
+
+```ini
+## mysql serverId,不要和 mysql 的 server_id 重复
+canal.instance.mysql.slaveId = 10
+#position info,需要改成自己的数据库信息
+canal.instance.master.address = 127.0.0.1:3306
+#username/password,需要改成自己的数据库信息,与刚才添加的用户保持一致
+canal.instance.dbUsername = canal
+canal.instance.dbPassword = canal
+```
+
+### 3.3 启动和关闭
+
+```bash
+#进入文件目录下的bin文件夹
+#启动
+sh startup.sh
+#关闭
+sh stop.sh
+```
+
+## 4.Springboot集成Canal
+
+### 4.1 Canal数据结构
+
+
+
+### 4.2 引入依赖
+
+```xml
+
+
+ com.alibaba.otter
+ canal.client
+ 1.1.6
+
+
+
+
+
+ com.alibaba.otter
+ canal.protocol
+ 1.1.6
+
+
+
+
+ co.elastic.clients
+ elasticsearch-java
+ 8.4.3
+
+
+
+
+ jakarta.json
+ jakarta.json-api
+ 2.0.1
+
+```
+
+### 4.3 application.yaml
+
+```yaml
+custom:
+ elasticsearch:
+ host: localhost #主机
+ port: 9200 #端口
+ username: elastic #用户名
+ password: 3bf24a76 #密码
+```
+
+### 4.4 EsClient
+
+```java
+@Setter
+@ConfigurationProperties(prefix = "custom.elasticsearch")
+@Configuration
+public class EsClient {
+
+ /**
+ * 主机
+ */
+ private String host;
+
+ /**
+ * 端口
+ */
+ private Integer port;
+
+ /**
+ * 用户名
+ */
+ private String username;
+
+ /**
+ * 密码
+ */
+ private String password;
+
+
+ @Bean
+ public ElasticsearchClient elasticsearchClient() {
+ CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ credentialsProvider.setCredentials(
+ AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+
+ // Create the low-level client
+ RestClient restClient = RestClient.builder(new HttpHost(host, port))
+ .setHttpClientConfigCallback(httpAsyncClientBuilder ->
+ httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
+ .build();
+ // Create the transport with a Jackson mapper
+ RestClientTransport transport = new RestClientTransport(
+ restClient, new JacksonJsonpMapper());
+ // Create the transport with a Jackson mapper
+ return new ElasticsearchClient(transport);
+ }
+}
+```
+
+### 4.5 Music实体类
+
+```java
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Music {
+
+ /**
+ * id
+ */
+ private String id;
+
+ /**
+ * 歌名
+ */
+ private String name;
+
+ /**
+ * 歌手名
+ */
+ private String singer;
+
+ /**
+ * 封面图地址
+ */
+ private String imageUrl;
+
+ /**
+ * 歌曲地址
+ */
+ private String musicUrl;
+
+ /**
+ * 歌词地址
+ */
+ private String lrcUrl;
+
+ /**
+ * 歌曲类型id
+ */
+ private String typeId;
+
+ /**
+ * 是否被逻辑删除,1 是,0 否
+ */
+ private Integer isDeleted;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date updateTime;
+
+}
+```
+
+### 4.6 CanalClient
+
+```java
+@Slf4j
+@Component
+public class CanalClient {
+
+ @Resource
+ private ElasticsearchClient client;
+
+
+ /**
+ * 实时数据同步程序
+ *
+ * @throws InterruptedException
+ * @throws InvalidProtocolBufferException
+ */
+ public void run() throws InterruptedException, IOException {
+ CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(
+ "localhost", 11111), "example", "", "");
+
+ while (true) {
+ //连接
+ connector.connect();
+ //订阅数据库
+ connector.subscribe("cloudmusic_music.music");
+ //获取数据
+ Message message = connector.get(100);
+
+ List entryList = message.getEntries();
+ if (CollectionUtils.isEmpty(entryList)) {
+ //没有数据,休息一会
+ TimeUnit.SECONDS.sleep(2);
+ } else {
+ for (CanalEntry.Entry entry : entryList) {
+ //获取类型
+ CanalEntry.EntryType entryType = entry.getEntryType();
+
+ //判断类型是否为ROWDATA
+ if (CanalEntry.EntryType.ROWDATA.equals(entryType)) {
+ //获取序列化后的数据
+ ByteString storeValue = entry.getStoreValue();
+ //反序列化数据
+ CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(storeValue);
+ //获取当前事件操作类型
+ CanalEntry.EventType eventType = rowChange.getEventType();
+ //获取数据集
+ List rowDataList = rowChange.getRowDatasList();
+
+ if (eventType == CanalEntry.EventType.INSERT) {
+ log.info("------新增操作------");
+
+ List musicList = new ArrayList<>();
+ for (CanalEntry.RowData rowData : rowDataList) {
+ musicList.add(createMusic(rowData.getAfterColumnsList()));
+ }
+ //es批量新增文档
+ index(musicList);
+ //打印新增集合
+ log.info(Arrays.toString(musicList.toArray()));
+ } else if (eventType == CanalEntry.EventType.UPDATE) {
+ log.info("------更新操作------");
+
+ List beforeMusicList = new ArrayList<>();
+ List afterMusicList = new ArrayList<>();
+ for (CanalEntry.RowData rowData : rowDataList) {
+ //更新前
+ beforeMusicList.add(createMusic(rowData.getBeforeColumnsList()));
+ //更新后
+ afterMusicList.add(createMusic(rowData.getAfterColumnsList()));
+ }
+ //es批量更新文档
+ index(afterMusicList);
+ //打印更新前集合
+ log.info("更新前:{}", Arrays.toString(beforeMusicList.toArray()));
+ //打印更新后集合
+ log.info("更新后:{}", Arrays.toString(afterMusicList.toArray()));
+ } else if (eventType == CanalEntry.EventType.DELETE) {
+ //删除操作
+ log.info("------删除操作------");
+
+ List idList = new ArrayList<>();
+ for (CanalEntry.RowData rowData : rowDataList) {
+ for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
+ if("id".equals(column.getName())) {
+ idList.add(column.getValue());
+ break;
+ }
+ }
+ }
+ //es批量删除文档
+ delete(idList);
+ //打印删除id集合
+ log.info(Arrays.toString(idList.toArray()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 根据canal获取的数据创建Music对象
+ *
+ * @param columnList
+ * @return
+ */
+ private Music createMusic(List columnList) {
+ Music music = new Music();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ for (CanalEntry.Column column : columnList) {
+ switch (column.getName()) {
+ case "id" -> music.setId(column.getValue());
+ case "name" -> music.setName(column.getValue());
+ case "singer" -> music.setSinger(column.getValue());
+ case "image_url" -> music.setImageUrl(column.getValue());
+ case "music_url" -> music.setMusicUrl(column.getValue());
+ case "lrc_url" -> music.setLrcUrl(column.getValue());
+ case "type_id" -> music.setTypeId(column.getValue());
+ case "is_deleted" -> music.setIsDeleted(Integer.valueOf(column.getValue()));
+ case "create_time" ->
+ music.setCreateTime(Date.from(LocalDateTime.parse(column.getValue(), formatter).atZone(ZoneId.systemDefault()).toInstant()));
+ case "update_time" ->
+ music.setUpdateTime(Date.from(LocalDateTime.parse(column.getValue(), formatter).atZone(ZoneId.systemDefault()).toInstant()));
+ default -> {
+ }
+ }
+ }
+
+ return music;
+ }
+
+ /**
+ * es批量新增、更新文档(不存在:新增, 存在:更新)
+ *
+ * @param musicList 音乐集合
+ * @throws IOException
+ */
+ private void index(List musicList) throws IOException {
+ BulkRequest.Builder br = new BulkRequest.Builder();
+
+ musicList.forEach(music -> br
+ .operations(op -> op
+ .index(idx -> idx
+ .index("music")
+ .id(music.getId())
+ .document(music))));
+
+ client.bulk(br.build());
+ }
+
+ /**
+ * es批量删除文档
+ *
+ * @param idList 音乐id集合
+ * @throws IOException
+ */
+ private void delete(List idList) throws IOException {
+ BulkRequest.Builder br = new BulkRequest.Builder();
+
+ idList.forEach(id -> br
+ .operations(op -> op
+ .delete(idx -> idx
+ .index("music")
+ .id(id))));
+
+ client.bulk(br.build());
+ }
+
+}
+```
+
+### 4.7 ApplicationContextAware
+
+```java
+@Component
+public class ApplicationContextUtil implements ApplicationContextAware {
+
+ private static ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ ApplicationContextUtil.applicationContext = applicationContext;
+ }
+
+ public static T getBean (Class classType) {
+ return applicationContext.getBean(classType);
+ }
+
+}
+```
+
+### 4.8 main
+
+```java
+@Slf4j
+@SpringBootApplication
+public class CanalApplication {
+ public static void main(String[] args) throws InterruptedException, IOException {
+ SpringApplication.run(CanalApplication.class, args);
+ log.info("数据同步程序启动");
+
+ CanalClient client = ApplicationContextUtil.getBean(CanalClient.class);
+ client.run();
+ }
+}
+```
+
+## 5.总结
+
+那么以上就是Canal组件的介绍啦,希望大家都能有所收获~
+
diff --git a/docs/advance/excellent-article/4-remove-duplicate-code.md b/docs/advance/excellent-article/4-remove-duplicate-code.md
index dff48c7..0532fdc 100644
--- a/docs/advance/excellent-article/4-remove-duplicate-code.md
+++ b/docs/advance/excellent-article/4-remove-duplicate-code.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 干掉 “重复代码” 的技巧有哪些
+category: 优质文章
+tag:
+ - 开发技巧
+head:
+ - - meta
+ - name: keywords
+ content: 重复代码,开发技巧
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 干掉 “重复代码” 的技巧有哪些
软件工程师和码农最大的区别就是平时写代码时习惯问题,码农很喜欢写重复代码而软件工程师会利用各种技巧去干掉重复的冗余代码。
@@ -592,4 +607,4 @@ return orderDO;
第二种代码重复是,使用硬编码的方式重复实现相同的数据处理算法。我们可以考虑把规则转换为自定义注解,作为元数据对类或对字段、方法进行描述,然后通过反射动态读取这些元数据、字段或调用方法,实现规则参数和规则定义的分离。也就是说,把变化的部分也就是规则的参数放入注解,规则的定义统一处理。
-第三种代码重复是,业务代码中常见的 DO、DTO、VO 转换时大量字段的手动赋值,遇到有上百个属性的复杂类型,非常非常容易出错。我的建议是,不要手动进行赋值,考虑使用 Bean 映射工具进行。此外,还可以考虑采用单元测试对所有字段进行赋值正确性校验。
\ No newline at end of file
+第三种代码重复是,业务代码中常见的 DO、DTO、VO 转换时大量字段的手动赋值,遇到有上百个属性的复杂类型,非常非常容易出错。我的建议是,不要手动进行赋值,考虑使用 Bean 映射工具进行。此外,还可以考虑采用单元测试对所有字段进行赋值正确性校验。
diff --git a/docs/advance/excellent-article/5-jvm-optimize.md b/docs/advance/excellent-article/5-jvm-optimize.md
index e9f0a2c..dc7f399 100644
--- a/docs/advance/excellent-article/5-jvm-optimize.md
+++ b/docs/advance/excellent-article/5-jvm-optimize.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 一次简单的 JVM 调优,拿去写到简历里
+category: 优质文章
+tag:
+ - JVM
+head:
+ - - meta
+ - name: keywords
+ content: JVM调优
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 一次简单的 JVM 调优,拿去写到简历里
大家好,我是大彬。
@@ -91,4 +106,4 @@ JVM调优一直是面试官很喜欢问的问题。周末在网上看到一篇JV
总之,这是一次挺成功的 GC 调整,让我对 GC 有了更深的理解,但由于没有深入到 old 区,之前学习到的 CMS 相关的知识还没有复习到。
-不过性能优化并不是一朝一夕的事,需要时刻关注问题,及时做出调整。
\ No newline at end of file
+不过性能优化并不是一朝一夕的事,需要时刻关注问题,及时做出调整。
diff --git a/docs/advance/excellent-article/6-spring-three-cache.md b/docs/advance/excellent-article/6-spring-three-cache.md
index c13b30e..1b3ff9f 100644
--- a/docs/advance/excellent-article/6-spring-three-cache.md
+++ b/docs/advance/excellent-article/6-spring-three-cache.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?
+category: 优质文章
+tag:
+ - Spring
+head:
+ - - meta
+ - name: keywords
+ content: Spring,循环依赖问题,三级缓存
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?
## **前言**
@@ -116,4 +131,4 @@ singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObjec
在工作中,一直认为编程代码不是最重要的,重要的是在工作中所养成的编程思维。
-原文:cnblogs.com/semi-sub/p/13548479.html
\ No newline at end of file
+原文:cnblogs.com/semi-sub/p/13548479.html
diff --git a/docs/advance/excellent-article/7-sql-optimize.md b/docs/advance/excellent-article/7-sql-optimize.md
index c4c2210..a77731f 100644
--- a/docs/advance/excellent-article/7-sql-optimize.md
+++ b/docs/advance/excellent-article/7-sql-optimize.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 这几个SQL语法的坑,你踩过吗
+category: 优质文章
+tag:
+ - 数据库
+head:
+ - - meta
+ - name: keywords
+ content: SQL语法
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
# 这几个SQL语法的坑,你踩过吗
> 本文已经收录到Github仓库,该仓库包含**计算机基础、Java基础、多线程、JVM、常见框架、分布式、微服务、设计模式、架构**等核心知识点,欢迎star~
@@ -418,4 +433,4 @@ ON a.resourceid = c.resourcesid
编写复杂SQL语句要养成使用 WITH 语句的习惯。简洁且思路清晰的SQL语句也能减小数据库的负担 。
-> 来源:yq.aliyun.com/articles/72501
\ No newline at end of file
+> 来源:yq.aliyun.com/articles/72501
diff --git a/docs/advance/excellent-article/8-interface-idempotent.md b/docs/advance/excellent-article/8-interface-idempotent.md
index e01fe65..26d6f46 100644
--- a/docs/advance/excellent-article/8-interface-idempotent.md
+++ b/docs/advance/excellent-article/8-interface-idempotent.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: 如何保证接口幂等性?
+category: 优质文章
+tag:
+ - 实践经验
+head:
+ - - meta
+ - name: keywords
+ content: 接口幂等性
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
+
# 面试官:如何保证接口幂等性?一口气说了9种方法!
大家好,我是大彬~
diff --git a/docs/advance/excellent-article/9-jvm-optimize-param.md b/docs/advance/excellent-article/9-jvm-optimize-param.md
index 2526431..6feb1f6 100644
--- a/docs/advance/excellent-article/9-jvm-optimize-param.md
+++ b/docs/advance/excellent-article/9-jvm-optimize-param.md
@@ -1,10 +1,26 @@
+---
+sidebar: heading
+title: 美团面试:熟悉哪些JVM调优参数?
+category: 优质文章
+tag:
+ - JVM
+head:
+ - - meta
+ - name: keywords
+ content: JVM调优参数,JVM调优
+ - - meta
+ - name: description
+ content: 优质文章汇总
+---
+
+
# 美团面试:熟悉哪些JVM调优参数?
今天来熟悉一下,关于`JVM`调优常用的一些参数。
X或者XX开头的都是非标准化参数
-
+
意思就是说标准化参数不会变,非标准化参数可能在每个`JDK`版本中有所变化,但是就目前来看X开头的非标准化的参数改变的也是非常少。
@@ -15,7 +31,7 @@ X或者XX开头的都是非标准化参数
`-XX:+PrintCommandLineFlags`查看当前`JVM`设置过的相关参数:
-
+
## JVM参数分类
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/excellent-article/\345\256\236\347\216\260\345\274\202\346\255\245\347\274\226\347\250\213\357\274\214\346\210\221\346\234\211\345\205\253\347\247\215\346\226\271\345\274\217\357\274\201.md" "b/docs/advance/excellent-article/\345\256\236\347\216\260\345\274\202\346\255\245\347\274\226\347\250\213\357\274\214\346\210\221\346\234\211\345\205\253\347\247\215\346\226\271\345\274\217\357\274\201.md"
new file mode 100644
index 0000000..92ca41f
--- /dev/null
+++ "b/docs/advance/excellent-article/\345\256\236\347\216\260\345\274\202\346\255\245\347\274\226\347\250\213\357\274\214\346\210\221\346\234\211\345\205\253\347\247\215\346\226\271\345\274\217\357\274\201.md"
@@ -0,0 +1,430 @@
+# 实现异步编程,我有八种方式!
+
+## **一、前言**
+
+> 异步执行对于开发者来说并不陌生,在实际的开发过程中,很多场景多会使用到异步,相比同步执行,异步可以大大缩短请求链路耗时时间,比如:**发送短信、邮件、异步更新等**,这些都是典型的可以通过异步实现的场景。
+
+## **二、异步的八种实现方式**
+
+1. 线程Thread
+2. Future
+3. 异步框架`CompletableFuture`
+4. Spring注解@Async
+5. Spring `ApplicationEvent`事件
+6. 消息队列
+7. 第三方异步框架,比如Hutool的`ThreadUtil`
+8. Guava异步
+
+## **三、什么是异步?**
+
+首先我们先看一个常见的用户下单的场景:
+
+
+
+在同步操作中,我们执行到 **发送短信** 的时候,我们必须等待这个方法彻底执行完才能执行 **赠送积分** 这个操作,如果 **赠送积分** 这个动作执行时间较长,发送短信需要等待,这就是典型的同步场景。
+
+实际上,发送短信和赠送积分没有任何的依赖关系,通过异步,我们可以实现`赠送积分`和`发送短信`这两个操作能够同时进行,比如:
+
+
+
+这就是所谓的异步,是不是非常简单,下面就说说异步的几种实现方式吧。
+
+## **四、异步编程**
+
+#### **4.1 线程异步**
+
+```java
+public class AsyncThread extends Thread {
+
+ @Override
+ public void run() {
+ System.out.println("Current thread name:" + Thread.currentThread().getName() + " Send email success!");
+ }
+
+ public static void main(String[] args) {
+ AsyncThread asyncThread = new AsyncThread();
+ asyncThread.run();
+ }
+}
+```
+
+当然如果每次都创建一个`Thread`线程,频繁的创建、销毁,浪费系统资源,我们可以采用线程池:
+
+```java
+private ExecutorService executorService = Executors.newCachedThreadPool();
+
+public void fun() {
+ executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ log.info("执行业务逻辑...");
+ }
+ });
+}
+```
+
+可以将业务逻辑封装到`Runnable`或`Callable`中,交由线程池来执行。
+
+#### **4.2 Future异步**
+
+```java
+@Slf4j
+public class FutureManager {
+
+ public String execute() throws Exception {
+
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ Future future = executor.submit(new Callable() {
+ @Override
+ public String call() throws Exception {
+
+ System.out.println(" --- task start --- ");
+ Thread.sleep(3000);
+ System.out.println(" --- task finish ---");
+ return "this is future execute final result!!!";
+ }
+ });
+
+ //这里需要返回值时会阻塞主线程
+ String result = future.get();
+ log.info("Future get result: {}", result);
+ return result;
+ }
+
+ @SneakyThrows
+ public static void main(String[] args) {
+ FutureManager manager = new FutureManager();
+ manager.execute();
+ }
+}
+```
+
+输出结果:
+
+```
+ --- task start ---
+ --- task finish ---
+ Future get result: this is future execute final result!!!
+```
+
+##### **4.2.1 Future的不足之处**
+
+Future的不足之处的包括以下几点:
+
+1️⃣ 无法被动接收异步任务的计算结果:虽然我们可以主动将异步任务提交给线程池中的线程来执行,但是待异步任务执行结束之后,主线程无法得到任务完成与否的通知,它需要通过get方法主动获取任务执行的结果。
+
+2️⃣ Future件彼此孤立:有时某一个耗时很长的异步任务执行结束之后,你想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future都是彼此之间都是孤立的,所以才有了后面的`CompletableFuture`,`CompletableFuture`就可以将多个Future串联起来形成任务流。
+
+3️⃣ Futrue没有很好的错误处理机制:截止目前,如果某个异步任务在执行发的过程中发生了异常,调用者无法被动感知,必须通过捕获get方法的异常才知晓异步任务执行是否出现了错误,从而在做进一步的判断处理。
+
+#### **4.3 CompletableFuture实现异步**
+
+```java
+public class CompletableFutureCompose {
+
+ /**
+ * thenAccept子任务和父任务公用同一个线程
+ */
+ @SneakyThrows
+ public static void thenRunAsync() {
+ CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
+ System.out.println(Thread.currentThread() + " cf1 do something....");
+ return 1;
+ });
+ CompletableFuture cf2 = cf1.thenRunAsync(() -> {
+ System.out.println(Thread.currentThread() + " cf2 do something...");
+ });
+ //等待任务1执行完成
+ System.out.println("cf1结果->" + cf1.get());
+ //等待任务2执行完成
+ System.out.println("cf2结果->" + cf2.get());
+ }
+
+ public static void main(String[] args) {
+ thenRunAsync();
+ }
+}
+```
+
+我们不需要显式使用`ExecutorService,CompletableFuture `内部使用了`ForkJoinPool`来处理异步任务,如果在某些业务场景我们想自定义自己的异步线程池也是可以的。
+
+#### **4.4 Spring的@Async异步**
+
+##### **4.4.1 自定义异步线程池**
+
+```
+/**
+ * 线程池参数配置,多个线程池实现线程池隔离,@Async注解,默认使用系统自定义线程池,可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称,比如:@Async("taskName")
+ *
+ * @author: jacklin
+ * @since: 2021/5/18 11:44
+ **/
+@EnableAsync
+@Configuration
+public class TaskPoolConfig {
+
+ /**
+ * 自定义线程池
+ *
+ * @author: jacklin
+ * @since: 2021/11/16 17:41
+ **/
+ @Bean("taskExecutor")
+ public Executor taskExecutor() {
+ //返回可用处理器的Java虚拟机的数量 12
+ int i = Runtime.getRuntime().availableProcessors();
+ System.out.println("系统最大线程数 : " + i);
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ //核心线程池大小
+ executor.setCorePoolSize(16);
+ //最大线程数
+ executor.setMaxPoolSize(20);
+ //配置队列容量,默认值为Integer.MAX_VALUE
+ executor.setQueueCapacity(99999);
+ //活跃时间
+ executor.setKeepAliveSeconds(60);
+ //线程名字前缀
+ executor.setThreadNamePrefix("asyncServiceExecutor -");
+ //设置此执行程序应该在关闭时阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余的任务完成他们的执行
+ executor.setAwaitTerminationSeconds(60);
+ //等待所有的任务结束后再关闭线程池
+ executor.setWaitForTasksToCompleteOnShutdown(true);
+ return executor;
+ }
+}
+```
+
+##### **4.4.2 AsyncService**
+
+```java
+public interface AsyncService {
+
+ MessageResult sendSms(String callPrefix, String mobile, String actionType, String content);
+
+ MessageResult sendEmail(String email, String subject, String content);
+}
+
+@Slf4j
+@Service
+public class AsyncServiceImpl implements AsyncService {
+
+ @Autowired
+ private IMessageHandler mesageHandler;
+
+ @Override
+ @Async("taskExecutor")
+ public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {
+ try {
+
+ Thread.sleep(1000);
+ mesageHandler.sendSms(callPrefix, mobile, actionType, content);
+
+ } catch (Exception e) {
+ log.error("发送短信异常 -> ", e)
+ }
+ }
+
+
+ @Override
+ @Async("taskExecutor")
+ public sendEmail(String email, String subject, String content) {
+ try {
+
+ Thread.sleep(1000);
+ mesageHandler.sendsendEmail(email, subject, content);
+
+ } catch (Exception e) {
+ log.error("发送email异常 -> ", e)
+ }
+ }
+}
+```
+
+在实际项目中, 使用`@Async`调用线程池,推荐等方式是是使用自定义线程池的模式,不推荐直接使用@Async直接实现异步。
+
+#### **4.5 Spring ApplicationEvent事件实现异步**
+
+##### **4.5.1 定义事件**
+
+```java
+public class AsyncSendEmailEvent extends ApplicationEvent {
+
+ /**
+ * 邮箱
+ **/
+ private String email;
+
+ /**
+ * 主题
+ **/
+ private String subject;
+
+ /**
+ * 内容
+ **/
+ private String content;
+
+ /**
+ * 接收者
+ **/
+ private String targetUserId;
+
+}
+```
+
+##### **4.5.2 定义事件处理器**
+
+```java
+@Slf4j
+@Component
+public class AsyncSendEmailEventHandler implements ApplicationListener {
+
+ @Autowired
+ private IMessageHandler mesageHandler;
+
+ @Async("taskExecutor")
+ @Override
+ public void onApplicationEvent(AsyncSendEmailEvent event) {
+ if (event == null) {
+ return;
+ }
+
+ String email = event.getEmail();
+ String subject = event.getSubject();
+ String content = event.getContent();
+ String targetUserId = event.getTargetUserId();
+ mesageHandler.sendsendEmailSms(email, subject, content, targerUserId);
+ }
+}
+```
+
+另外,可能有些时候采用ApplicationEvent实现异步的使用,当程序出现异常错误的时候,需要考虑补偿机制,那么这时候可以结合Spring Retry重试来帮助我们避免这种异常造成数据不一致问题。
+
+#### **4.6 消息队列**
+
+##### **4.6.1 回调事件消息生产者**
+
+```
+@Slf4j
+@Component
+public class CallbackProducer {
+
+ @Autowired
+ AmqpTemplate amqpTemplate;
+
+ public void sendCallbackMessage(CallbackDTO allbackDTO, final long delayTimes) {
+
+ log.info("生产者发送消息,callbackDTO,{}", callbackDTO);
+
+ amqpTemplate.convertAndSend(CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getExchange(), CallbackQueueEnum.QUEUE_GENSEE_CALLBACK.getRoutingKey(), JsonMapper.getInstance().toJson(genseeCallbackDTO), new MessagePostProcessor() {
+ @Override
+ public Message postProcessMessage(Message message) throws AmqpException {
+ //给消息设置延迟毫秒值,通过给消息设置x-delay头来设置消息从交换机发送到队列的延迟时间
+ message.getMessageProperties().setHeader("x-delay", delayTimes);
+ message.getMessageProperties().setCorrelationId(callbackDTO.getSdkId());
+ return message;
+ }
+ });
+ }
+}
+```
+
+##### **4.6.2 回调事件消息消费者**
+
+```
+@Slf4j
+@Component
+@RabbitListener(queues = "message.callback", containerFactory = "rabbitListenerContainerFactory")
+public class CallbackConsumer {
+
+ @Autowired
+ private IGlobalUserService globalUserService;
+
+ @RabbitHandler
+ public void handle(String json, Channel channel, @Headers Map map) throws Exception {
+
+ if (map.get("error") != null) {
+ //否认消息
+ channel.basicNack((Long) map.get(AmqpHeaders.DELIVERY_TAG), false, true);
+ return;
+ }
+
+ try {
+
+ CallbackDTO callbackDTO = JsonMapper.getInstance().fromJson(json, CallbackDTO.class);
+ //执行业务逻辑
+ globalUserService.execute(callbackDTO);
+ //消息消息成功手动确认,对应消息确认模式acknowledge-mode: manual
+ channel.basicAck((Long) map.get(AmqpHeaders.DELIVERY_TAG), false);
+
+ } catch (Exception e) {
+ log.error("回调失败 -> {}", e);
+ }
+ }
+}
+```
+
+#### **4.7 ThreadUtil异步工具类**
+
+```java
+@Slf4j
+public class ThreadUtils {
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 3; i++) {
+ ThreadUtil.execAsync(() -> {
+ ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
+ int number = threadLocalRandom.nextInt(20) + 1;
+ System.out.println(number);
+ });
+ log.info("当前第:" + i + "个线程");
+ }
+
+ log.info("task finish!");
+ }
+}
+```
+
+#### **4.8 Guava异步**
+
+`Guava`的`ListenableFuture`顾名思义就是可以监听的`Future`,是对java原生Future的扩展增强。我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。
+
+如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,代码复杂,而且效率低下。
+
+使用**Guava ListenableFuture**可以帮我们检测Future是否完成了,不需要再通过get()方法苦苦等待异步的计算结果,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。
+
+`ListenableFuture`是一个接口,它从`jdk`的`Future`接口继承,添加了`void addListener(Runnable listener, Executor executor)`方法。
+
+我们看下如何使用`ListenableFuture`。首先需要定义`ListenableFuture`的实例:
+
+```java
+ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+ final ListenableFuture listenableFuture = executorService.submit(new Callable() {
+ @Override
+ public Integer call() throws Exception {
+ log.info("callable execute...")
+ TimeUnit.SECONDS.sleep(1);
+ return 1;
+ }
+ });
+```
+
+首先通过`MoreExecutors`类的静态方法`listeningDecorator`方法初始化一个`ListeningExecutorService`的方法,然后使用此实例的`submit`方法即可初始化`ListenableFuture`对象。
+
+`ListenableFuture`要做的工作,在Callable接口的实现类中定义,这里只是休眠了1秒钟然后返回一个数字1,有了`ListenableFuture`实例,可以执行此Future并执行Future完成之后的回调函数。
+
+```java
+ Futures.addCallback(listenableFuture, new FutureCallback() {
+ @Override
+ public void onSuccess(Integer result) {
+ //成功执行...
+ System.out.println("Get listenable future's result with callback " + result);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ //异常情况处理...
+ t.printStackTrace();
+ }
+});
+```
\ No newline at end of file
diff --git a/docs/advance/system-design/1-scan-code-login.md b/docs/advance/system-design/1-scan-code-login.md
index 2c587c1..4c78f64 100644
--- a/docs/advance/system-design/1-scan-code-login.md
+++ b/docs/advance/system-design/1-scan-code-login.md
@@ -1,8 +1,24 @@
---
sidebar: heading
+title: 扫码登录原理
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,扫码登录原理,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
-> 回复【**手册**】获取大彬精心整理的**大厂面试手册**。
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## 扫码登录原理
diff --git a/docs/advance/system-design/10-pdd-visit-statistics.md b/docs/advance/system-design/10-pdd-visit-statistics.md
index 38e992a..04ae5d3 100644
--- a/docs/advance/system-design/10-pdd-visit-statistics.md
+++ b/docs/advance/system-design/10-pdd-visit-statistics.md
@@ -1,5 +1,18 @@
-如何用 Redis 统计用户访问量?
---------------------------------------------------
+---
+sidebar: heading
+title: 如何用 Redis 统计用户访问量?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,用户访问量统计,Redis,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
拼多多有数亿的用户,那么对于某个网页,怎么使用Redis来统计一个网站的用户访问数呢?
1、**Hash**
@@ -54,4 +67,4 @@ Redis已经为我们提供了SETBIT的方法,使用起来非常的方便,我
**缺点**: 查询指定用户的时候,可能会出错,毕竟存的不是具体的数据。总数也存在一定的误差。
-上面就是常见的3种适用Redis统计网站用户访问数的方法了。
\ No newline at end of file
+上面就是常见的3种适用Redis统计网站用户访问数的方法了。
diff --git a/docs/advance/system-design/11-realtime-subscribe-push.md b/docs/advance/system-design/11-realtime-subscribe-push.md
index 03cc72c..a38120e 100644
--- a/docs/advance/system-design/11-realtime-subscribe-push.md
+++ b/docs/advance/system-design/11-realtime-subscribe-push.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 实时订阅推送设计与实现
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,实时订阅推送设计,消息推送,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 实时订阅推送设计与实现
什么是订阅推送?就是用户订阅了优惠劵的推送,在可领取前的一分钟就要把提醒信息推送到用户的app中。具体方案就是到具体的推送时间点了,coupon系统调用消息中心的推送接口,把信息推送出去。
@@ -109,4 +124,4 @@
-> 参考链接:https://www.cnblogs.com/linlinismine/p/9214299.html
\ No newline at end of file
+> 参考链接:https://www.cnblogs.com/linlinismine/p/9214299.html
diff --git a/docs/advance/system-design/12-second-kill-5-point.md b/docs/advance/system-design/12-second-kill-5-point.md
index 4406339..42f1ddf 100644
--- a/docs/advance/system-design/12-second-kill-5-point.md
+++ b/docs/advance/system-design/12-second-kill-5-point.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 秒杀系统设计的5个要点
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,秒杀系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 秒杀系统设计的5个要点
## 秒杀系统涉及到的知识点
diff --git a/docs/advance/system-design/13-permission-system.md b/docs/advance/system-design/13-permission-system.md
index 85904bc..e953920 100644
--- a/docs/advance/system-design/13-permission-system.md
+++ b/docs/advance/system-design/13-permission-system.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 全网最全的权限系统设计方案
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,权限系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 全网最全的权限系统设计方案
今天和大家聊聊权限系统设计常见的方案。
@@ -170,4 +185,4 @@ RBAC模型根据不同业务场景的需要会有很多种演变,实际工作
-> 原文:blog.csdn.net/u010482601/article/details/104989532
\ No newline at end of file
+> 原文:blog.csdn.net/u010482601/article/details/104989532
diff --git a/docs/advance/system-design/15-red-packet.md b/docs/advance/system-design/15-red-packet.md
index 29edcd9..040506d 100644
--- a/docs/advance/system-design/15-red-packet.md
+++ b/docs/advance/system-design/15-red-packet.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 如何设计一个抢红包系统
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,抢红包系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 如何设计一个抢红包系统
## 前言
@@ -121,4 +136,4 @@ OK,分析完表结构其实方案已经七七八八差不多了。请接着看
-> 参考链接:https://juejin.cn/post/6925947709517987848
\ No newline at end of file
+> 参考链接:https://juejin.cn/post/6925947709517987848
diff --git a/docs/advance/system-design/16-mq-design.md b/docs/advance/system-design/16-mq-design.md
index 4046f82..64bd8a7 100644
--- a/docs/advance/system-design/16-mq-design.md
+++ b/docs/advance/system-design/16-mq-design.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 如何设计一个消息队列?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,消息队列设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 如何设计一个消息队列?
**如果让你来设计一个 MQ,该如何下手?需要考虑哪些问题?又有哪些技术挑战?**
@@ -201,4 +216,4 @@ Broker 服务的高可用,只需要保证 Broker 可水平扩展进行集群
-> 参考:https://toutiao.io/posts/ix9hfyh/preview
\ No newline at end of file
+> 参考:https://toutiao.io/posts/ix9hfyh/preview
diff --git a/docs/advance/system-design/17-shopping-car.md b/docs/advance/system-design/17-shopping-car.md
index 445f3cc..ff6763b 100644
--- a/docs/advance/system-design/17-shopping-car.md
+++ b/docs/advance/system-design/17-shopping-car.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 购物车系统怎么设计?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,购物车系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 购物车系统怎么设计?
## 1 主要功能
@@ -212,4 +227,4 @@
-> 参考:https://mp.weixin.qq.com/s/3P_f_Vua8rwsGOHrxWubSg
\ No newline at end of file
+> 参考:https://mp.weixin.qq.com/s/3P_f_Vua8rwsGOHrxWubSg
diff --git a/docs/advance/system-design/18-register-center.md b/docs/advance/system-design/18-register-center.md
index 3c0eff7..ba9721d 100644
--- a/docs/advance/system-design/18-register-center.md
+++ b/docs/advance/system-design/18-register-center.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 如何设计一个注册中心?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,注册中心,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
## 如何设计一个注册中心?
今天,给大家分享如何设计一个**注册中心**。
@@ -190,4 +205,4 @@ push有个不好点,那就是服务注册中心需要维护大量的会话,
- 服务是如何注册
- 消费端如何获取服务
- 如何保证注册中心的高可用
-- 动态感知服务的上下线
\ No newline at end of file
+- 动态感知服务的上下线
diff --git a/docs/advance/system-design/19-high-concurrent-system-design.md b/docs/advance/system-design/19-high-concurrent-system-design.md
index 98533a5..12499df 100644
--- a/docs/advance/system-design/19-high-concurrent-system-design.md
+++ b/docs/advance/system-design/19-high-concurrent-system-design.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 如何设计一个高并发系统?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,高并发系统,高并发系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 如何设计一个高并发系统?
## 总览
@@ -45,4 +60,4 @@ ES 是分布式的,可以随便扩容,分布式天然就可以支撑高并
-> 参考链接:https://hadyang.com/interview/docs/architecture/concurrent/design/
\ No newline at end of file
+> 参考链接:https://hadyang.com/interview/docs/architecture/concurrent/design/
diff --git a/docs/advance/system-design/2-order-timeout-auto-cancel.md b/docs/advance/system-design/2-order-timeout-auto-cancel.md
index bc15dfb..b100f91 100644
--- a/docs/advance/system-design/2-order-timeout-auto-cancel.md
+++ b/docs/advance/system-design/2-order-timeout-auto-cancel.md
@@ -1,30 +1,26 @@
---
sidebar: heading
+title: 订单30分钟未支付自动取消怎么实现?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,订单取消设计,订单自动取消,订单设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
# 订单30分钟未支付自动取消怎么实现?
-推荐大家加入我的[**学习圈**](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247491988&idx=1&sn=a7d50c0994cbfdede312715b393daa8a&scene=21#wechat_redirect),目前已经有100多位小伙伴加入了,下面有50元的**优惠券**,**扫描二维码**领取优惠券加入(**即将恢复原价**)。
+::: tip 这是一则或许对你有帮助的信息
-学习圈提供以下这些**服务**:
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
-1、学习圈内部**知识图谱**,汇总了**优质资源、面试高频问题、大厂面经、踩坑分享**,让你少走一些弯路
-
-2、四个**优质专栏**、Java**面试手册完整版**(包含**场景设计、系统设计、分布式、微服务**等),持续更新
-
-
-
-3、**一对一答疑**,我会尽自己最大努力为你答疑解惑
-
-4、**免费的简历修改、面试指导服务**,绝对赚回门票
-
-5、各个阶段的优质**学习资源**(新手到架构师),超值
-
-6、打卡学习,**大学自习室的氛围**,一起蜕变成长
-
-
-
---分割线--
+:::
**目录**
@@ -46,11 +42,11 @@ sidebar: heading
对上述的任务,我们给一个专业的名字来形容,那就是延时任务。那么这里就会产生一个问题,这个延时任务和定时任务的区别究竟在哪里呢?一共有如下几点区别
-定时任务有明确的触发时间,延时任务没有
+1、定时任务有明确的触发时间,延时任务没有
-定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
+2、定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
-定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务
+3、定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务
下面,我们以判断订单是否超时为例,进行方案分析
@@ -344,13 +340,13 @@ public class HashedWheelTimerTest {
- 集群扩展相当麻烦
- 因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现 OOM 异常
-## 方案 4:redis 缓存
+## 方案 4:Redis 缓存
### 思路一
利用 redis 的 zset,zset 是一个有序集合,每一个元素(member)都关联了一个 score,通过 score 排序来取集合中的值
-添加元素:ZADD key score member [[score member][score member] …]
+添加元素:ZADD key score member [score member …]
按顺序查询元素:ZRANGE key start stop [WITHSCORES]
@@ -623,11 +619,11 @@ ps:redis 的 pub/sub 机制存在一个硬伤,官网内容如下
### 思路
-我们可以采用 rabbitMQ 的延时队列。RabbitMQ 具有以下两个特性,可以实现延迟队列
+我们可以采用 RabbitMQ 的延时队列。RabbitMQ 具有以下两个特性,可以实现延迟队列
RabbitMQ 可以针对 Queue 和 Message 设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为 dead letter
-lRabbitMQ 的 Queue 可以配置 x-dead-letter-exchange 和 x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了 deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能,具体的,我改天再写一篇文章,这里再讲下去,篇幅太长。
+lRabbitMQ 的 Queue 可以配置 x-dead-letter-exchange 和 x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了 deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能。
### 优点
@@ -641,8 +637,8 @@ lRabbitMQ 的 Queue 可以配置 x-dead-letter-exchange 和 x-dead-letter-routin
最后给大家分享一个Github仓库,上面有大彬整理的**300多本经典的计算机书籍PDF**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
-
+
-
+
-**Github地址**:https://github.com/Tyson0314/java-books
\ No newline at end of file
+**Github地址**:https://github.com/Tyson0314/java-books
diff --git a/docs/advance/system-design/20-sharding-smooth-migration.md b/docs/advance/system-design/20-sharding-smooth-migration.md
index 5908863..781bfed 100644
--- a/docs/advance/system-design/20-sharding-smooth-migration.md
+++ b/docs/advance/system-design/20-sharding-smooth-migration.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 分库分表如何平滑过渡?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,分库分表,分库分表平滑过渡,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
## 分库分表概述
在业务量不大时,单库单表即可支撑。
@@ -54,4 +69,4 @@
当数据完全一致了之后,所有机器使用分库分表的最新代码重新部署一次,此时就完成迁移了。
-
\ No newline at end of file
+
diff --git a/docs/advance/system-design/21-excel-import.md b/docs/advance/system-design/21-excel-import.md
index 288410b..c2d6b32 100644
--- a/docs/advance/system-design/21-excel-import.md
+++ b/docs/advance/system-design/21-excel-import.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 10w级别数据Excel导入优化
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,Excel导入优化,海量数据处理,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 10w级别数据Excel导入优化
# Part1需求说明
@@ -254,4 +269,4 @@ InsertConsumer.insertData(feeList, arrearageMapper::insertList);
- 对于需要与数据库交互的校验、按照业务逻辑适当的使用缓存。用空间换时间
- 使用 values(),(),() 拼接长 SQL 一次插入多行数据
- 使用多线程插入数据,利用掉网络IO等待时间(推荐使用并行流,简单易用)
-- 避免在循环中打印无用的日志
\ No newline at end of file
+- 避免在循环中打印无用的日志
diff --git a/docs/advance/system-design/3-file-send.md b/docs/advance/system-design/3-file-send.md
index 181e862..a0e7ac0 100644
--- a/docs/advance/system-design/3-file-send.md
+++ b/docs/advance/system-design/3-file-send.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 如何把一个文件较快的发送到100w个服务器?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,文件发送,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
## 如何把一个文件较快的发送到100w个服务器?
diff --git a/docs/advance/system-design/3-short-url.md b/docs/advance/system-design/3-short-url.md
index cd0e596..99d69d7 100644
--- a/docs/advance/system-design/3-short-url.md
+++ b/docs/advance/system-design/3-short-url.md
@@ -1,9 +1,18 @@
---
sidebar: heading
+title: 怎么设计一个短链系统?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,短链系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
-
-
# 短链系统
短链服务的鼻祖是TinyURL,是最早提供短链服务的网站,目前国内也有很多短链服务:新浪(t.cn)、百度(dwz.cn)、腾讯(url.cn)等等。
diff --git a/docs/advance/system-design/4-oversold.md b/docs/advance/system-design/4-oversold.md
index 1a74058..cda11ee 100644
--- a/docs/advance/system-design/4-oversold.md
+++ b/docs/advance/system-design/4-oversold.md
@@ -1,9 +1,18 @@
---
sidebar: heading
+title: 超卖问题
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,超卖问题,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
-
-
# 超卖问题
先到数据库查询库存,在减库存。不是原子操作,会有超卖问题。
diff --git a/docs/advance/system-design/5-second-kill.md b/docs/advance/system-design/5-second-kill.md
index da8eb16..1123c69 100644
--- a/docs/advance/system-design/5-second-kill.md
+++ b/docs/advance/system-design/5-second-kill.md
@@ -1,9 +1,19 @@
---
sidebar: heading
+title: 秒杀系统设计
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,秒杀系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
-
# 秒杀系统
## 系统的特点
@@ -42,4 +52,4 @@ sidebar: heading
[如何设计一个秒杀系统](https://gongfukangee.github.io/2019/06/09/SecondsKill/#%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%89%B9%E7%82%B9)
- [秒杀系统如何设计](https://www.teqng.com/2021/09/07/%E9%9D%A2%E9%9C%B8%EF%BC%9A%E7%A7%92%E6%9D%80%E7%B3%BB%E7%BB%9F%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%EF%BC%9F/)
\ No newline at end of file
+ [秒杀系统如何设计](https://www.teqng.com/2021/09/07/%E9%9D%A2%E9%9C%B8%EF%BC%9A%E7%A7%92%E6%9D%80%E7%B3%BB%E7%BB%9F%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%EF%BC%9F/)
diff --git a/docs/advance/system-design/6-wechat-redpacket-design.md b/docs/advance/system-design/6-wechat-redpacket-design.md
index 65f7dd4..50241ea 100644
--- a/docs/advance/system-design/6-wechat-redpacket-design.md
+++ b/docs/advance/system-design/6-wechat-redpacket-design.md
@@ -1,8 +1,19 @@
---
sidebar: heading
-
+title: 微信红包后台系统设计
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,微信红包设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
---
+
## 微信红包后台系统设计
### **背景**
@@ -131,4 +142,4 @@ sidebar: heading
-> 来源:https://cloud.tencent.com/developer/article/1637408
\ No newline at end of file
+> 来源:https://cloud.tencent.com/developer/article/1637408
diff --git a/docs/advance/system-design/8-sso-design.md b/docs/advance/system-design/8-sso-design.md
index e2d15ec..0049c02 100644
--- a/docs/advance/system-design/8-sso-design.md
+++ b/docs/advance/system-design/8-sso-design.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: 单点登录(SSO)的设计与实现
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,单点登录设计与实现,SSO,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
+
# 单点登录(SSO)的设计与实现
## 一、前言
@@ -110,4 +126,4 @@ SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,
这次设计方案更多是提供实现思路。如果涉及到APP用户登录等情况,在访问SSO服务时,增加对APP的签名验证就好了。当然,如果有无线网关,验证签名不是问题。
-> 本文授权转载自Ken,原文链接:https://ken.io/note/sso-design-implement
\ No newline at end of file
+> 本文授权转载自Ken,原文链接:https://ken.io/note/sso-design-implement
diff --git a/docs/advance/system-design/9-coupon-design.md b/docs/advance/system-design/9-coupon-design.md
index 65a42aa..a585e02 100644
--- a/docs/advance/system-design/9-coupon-design.md
+++ b/docs/advance/system-design/9-coupon-design.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 如何设计一个优惠券系统?
+category: 场景设计
+tag:
+ - 场景设计
+head:
+ - - meta
+ - name: keywords
+ content: 场景设计面试题,优惠券系统设计,场景设计
+ - - meta
+ - name: description
+ content: 场景设计常见面试题总结,让天下没有难背的八股文!
+---
+
# 如何设计一个优惠券系统?
## 背景
@@ -284,4 +299,4 @@ C端用户领券是一个比较重要的地方,核心要求就是绝对不能
对于热点的db库存更新则采用了db事务消息表,通过事务保证领取记录插入成功的同时一定会落入更新库存任务,从而异步串行的进行库存更新。
-> 来源:https://juejin.cn/post/7160643319612047367
\ No newline at end of file
+> 来源:https://juejin.cn/post/7160643319612047367
diff --git a/docs/advance/system-design/README.md b/docs/advance/system-design/README.md
index aef9e83..15ef840 100644
--- a/docs/advance/system-design/README.md
+++ b/docs/advance/system-design/README.md
@@ -1,27 +1,33 @@
**系统设计高频面试题**是我的[知识星球](https://topjavaer.cn/zsxq/introduce.html)**内部专属资料**,已经整理到Java面试手册**完整版**。
-
+
-除了Java面试手册完整版之外,星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
+另外星球提供**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
-
-
-
-
-
+
-**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
+

-
-
-另外星球还提供**简历指导、修改服务**,大彬已经帮**90**+个小伙伴修改了简历,相对还是比较有经验的。
+如果你正在打算准备跳槽、面试,星球还提供**简历指导、修改服务**,大彬已经帮**120**+个小伙伴修改了简历,相对还是比较有经验的。


-[知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
+星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
+
+
+
+
+
+
+
+怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)?
+
+**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。
+
+随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/campus-recruit/biggest-difficulty.md b/docs/campus-recruit/biggest-difficulty.md
index 47b6acd..8bf35a8 100644
--- a/docs/campus-recruit/biggest-difficulty.md
+++ b/docs/campus-recruit/biggest-difficulty.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 你在项目里遇到的最大困难是什么,如何解决的?
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 项目里遇到的最大困难是什么
+ - - meta
+ - name: description
+ content: 你在项目里遇到的最大困难是什么,如何解决的?
+---
+
## 你在项目里遇到的最大困难是什么,如何解决的?
这是一道面试高频题,但是很多人都没能回答好,或者说没有准备好怎么去回答。
@@ -21,4 +36,4 @@
-最后总结一下,最重要是平时要多复盘总结,积累面试素材。不管是多小的问题,只要你认真对待,总能学到一些知识。大部分面试官也不会期待你有处理过多大的问题,毕竟大部分人都是普通人。只要能从你的回答中看出你的思考,解决问题的方式,那么面试官的问这个问题的目的也就达到了。
\ No newline at end of file
+最后总结一下,最重要是平时要多复盘总结,积累面试素材。不管是多小的问题,只要你认真对待,总能学到一些知识。大部分面试官也不会期待你有处理过多大的问题,毕竟大部分人都是普通人。只要能从你的回答中看出你的思考,解决问题的方式,那么面试官的问这个问题的目的也就达到了。
diff --git a/docs/campus-recruit/career-plan.md b/docs/campus-recruit/career-plan.md
index 223048a..c0b3cf0 100644
--- a/docs/campus-recruit/career-plan.md
+++ b/docs/campus-recruit/career-plan.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 程序员职业规划分享
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 职业规划
+ - - meta
+ - name: description
+ content: 面试高频题,职业规划
---
分享一下我的看法。
@@ -34,4 +45,4 @@ sidebar: heading
-> 参考:https://segmentfault.com/a/1190000040241465
\ No newline at end of file
+> 参考:https://segmentfault.com/a/1190000040241465
diff --git a/docs/campus-recruit/company/1-shanghai-it-company.md b/docs/campus-recruit/company/1-shanghai-it-company.md
index 354dee9..9fac37c 100644
--- a/docs/campus-recruit/company/1-shanghai-it-company.md
+++ b/docs/campus-recruit/company/1-shanghai-it-company.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: 上海互联网公司汇总
+category: 分享
+tag:
+ - 资讯
+head:
+ - - meta
+ - name: keywords
+ content: 上海互联网公司
+ - - meta
+ - name: description
+ content: 上海互联网公司汇总
+---
+
+
今年互联网整体行情不容乐观,不好找工作,因此我整理了一些比较靠谱的互联网公司,希望大家少踩坑 。
本期先整理**上海**的互联网公司,后续会整理其他地区的,敬请期待!
@@ -159,4 +175,4 @@
**物流运输**
-- **满帮**(上海分公司,长宁天山路,运满满,互联网物流)
\ No newline at end of file
+- **满帮**(上海分公司,长宁天山路,运满满,互联网物流)
diff --git a/docs/campus-recruit/hr-ask-offers.md b/docs/campus-recruit/hr-ask-offers.md
index 1c5b641..6784d1c 100644
--- a/docs/campus-recruit/hr-ask-offers.md
+++ b/docs/campus-recruit/hr-ask-offers.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: HR问目前拿到哪几个offer了,怎么回答好?
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 面试题
+ - - meta
+ - name: description
+ content: HR问目前拿到哪几个offer了,怎么回答好?
+---
+
## HR问目前拿到哪几个offer了,怎么回答好?
这是比较常见的面试问题。
@@ -40,4 +55,4 @@ HR也会根据你拿到的Offer的情况,评估你在市场上对标的位置
-> 参考链接:https://www.zhihu.com/question/23751641
\ No newline at end of file
+> 参考链接:https://www.zhihu.com/question/23751641
diff --git a/docs/campus-recruit/interview-question-career-plan.md b/docs/campus-recruit/interview-question-career-plan.md
index a4499cb..33f73de 100644
--- a/docs/campus-recruit/interview-question-career-plan.md
+++ b/docs/campus-recruit/interview-question-career-plan.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 面试时问你的职业规划,该怎么回答?
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 面试题,职业规划
+ - - meta
+ - name: description
+ content: 面试时问你的职业规划,该怎么回答?
+---
+
## 面试时问你的职业规划,该怎么回答?
建议紧扣工作和学习两个维度回答
@@ -30,4 +45,4 @@
-参考链接:https://www.zhihu.com/question/20054953
\ No newline at end of file
+参考链接:https://www.zhihu.com/question/20054953
diff --git a/docs/campus-recruit/interview/3-baidu.md b/docs/campus-recruit/interview/3-baidu.md
index 18caa36..3cd2f26 100644
--- a/docs/campus-recruit/interview/3-baidu.md
+++ b/docs/campus-recruit/interview/3-baidu.md
@@ -2,106 +2,126 @@
## 面经1
-shiro的组件
-分布式一致性算法
-zookeeper那些能参与投票,leader能投票吗?
-netty零拷贝实现
-volatile,如何感知到变量变化的
-redis高可用
-http如何跨域?
-tcp如何长链接。
-http如何操作浏览器缓存。
-用过消息队列吗?
-怎么自己扩展validator(参数校验)
-jwt组成 header payload 签名加密算法那些。
-rsa如何运用到jwt中
-synchronized和volatile的区别
-什么是上下文切换,URL解析过程
-http有那些方法,get那些
-进程和线程的区别。
-和别人协作出现冲突怎么办
-如何学一个新语言
-怎么自学的
+- shiro的组件
+- 分布式一致性算法
+- zookeeper那些能参与投票,leader能投票吗?
+- netty零拷贝实现
+- volatile,如何感知到变量变化的
+- redis高可用
+- http如何跨域?
+- tcp如何长链接。
+- http如何操作浏览器缓存。
+- 用过消息队列吗?
+- 怎么自己扩展validator(参数校验)
+- jwt组成 header payload 签名加密算法那些。
+- rsa如何运用到jwt中
+- synchronized和volatile的区别
+- 什么是上下文切换,URL解析过程
+- http有那些方法,get那些
+- 进程和线程的区别。
+- 和别人协作出现冲突怎么办
+- 如何学一个新语言
+- 怎么自学的
## 面经2
-说说IO多路复用
-你刚刚说的多路复用针对的是各个请求(比如set,get),那返回值Redis是怎么处理的(愣住)
-MySQL B+树一般几层,怎么算的
-数据库隔离级别
-脏读、不可重复读、幻读(结合具体场景来讲)
-MySQL隔离级别分别怎么实现的
-MVCC
-redo log、undo log
-刷脏页的流程
-算法题:平方根
+- 说说IO多路复用
+- 你刚刚说的多路复用针对的是各个请求(比如set,get),那返回值Redis是怎么处理的(愣住)
+- MySQL B+树一般几层,怎么算的
+- 数据库隔离级别
+- 脏读、不可重复读、幻读(结合具体场景来讲)
+- MySQL隔离级别分别怎么实现的
+- MVCC
+- redo log、undo log
+- 刷脏页的流程
+- 算法题:平方根
+
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
## 面经3
-自我介绍
-项目是自己练手的项目吗,怎么找的
-项目是从0开始搭建的,还是有用开源的脚手架
-秒杀大概用到哪些东西,怎么实现的
-MQ幂等性和消息积压问题
-缓存与数据库数据一致性
-唯一ID
-Java里怎么保证多个线程的互斥性
-一个线程有哪些状态
-AQS怎么理解的
-Spring IOC容器创建Bean的流程
-创建的Bean是单例还是多例的
-SpringCloud config是怎么在Bean创建后更新Bean的值的
-SpringBoot自动配置原理
-SpringMVC执行流程
-使用Spring和直接使用Java语言面向对象开发,有哪些好处
-怎么理解面向对象
-了解哪些设计模式
-策略模式描述一下
-JVM由哪些模块组成
-框架里打破双亲委派机制的SPI大概怎么实现的
-那说说双亲委派
-垃圾回收主要回收哪些区域
-怎么识别哪些是垃圾
-哪些是根节点
-什么时候会出现Full GC
-不同垃圾收集器的区别
-TCP为什么要握三次手,为什么要挥四次手,大概什么流程
-实现环形队列(数组,增加和删除功能)
-反转链表(迭代)
+- 自我介绍
+- 项目是自己练手的项目吗,怎么找的
+- 项目是从0开始搭建的,还是有用开源的脚手架
+- 秒杀大概用到哪些东西,怎么实现的
+- MQ幂等性和消息积压问题
+- 缓存与数据库数据一致性
+- 唯一ID
+- Java里怎么保证多个线程的互斥性
+- 一个线程有哪些状态
+- AQS怎么理解的
+- Spring IOC容器创建Bean的流程
+- 创建的Bean是单例还是多例的
+- SpringCloud config是怎么在Bean创建后更新Bean的值的
+- SpringBoot自动配置原理
+- SpringMVC执行流程
+- 使用Spring和直接使用Java语言面向对象开发,有哪些好处
+- 怎么理解面向对象
+- 了解哪些设计模式
+- 策略模式描述一下
+- JVM由哪些模块组成
+- 框架里打破双亲委派机制的SPI大概怎么实现的
+- 那说说双亲委派
+- 垃圾回收主要回收哪些区域
+- 怎么识别哪些是垃圾
+- 哪些是根节点
+- 什么时候会出现Full GC
+- 不同垃圾收集器的区别
+- TCP为什么要握三次手,为什么要挥四次手,大概什么流程
+- 实现环形队列(数组,增加和删除功能)
+- 反转链表(迭代)
## 面经4
-专业是偏向硬件吗
-对百度了解多少
-有什么兴趣爱好
-经常打球吗
-喜欢听什么音乐
-经常听音乐吗,什么时候开始喜欢听音乐的
-你说两个具体的歌名我听听
-平时是怎样的一个人,有什么特点
-有做过什么有成就感的事吗
-后面选择百度的概率有多少
-想过自己5年后、10年后是怎样的吗
+- 专业是偏向硬件吗
+- 对百度了解多少
+- 有什么兴趣爱好
+- 经常打球吗
+- 喜欢听什么音乐
+- 经常听音乐吗,什么时候开始喜欢听音乐的
+- 你说两个具体的歌名我听听
+- 平时是怎样的一个人,有什么特点
+- 有做过什么有成就感的事吗
+- 后面选择百度的概率有多少
+- 想过自己5年后、10年后是怎样的吗
## 面经5
-1.面试官介绍自己,然后自我介绍
-2.java中的线程池有哪些?为什么使用线程池?你在哪里使用过或是见过?
-3.Mysql底层是怎么实现的?从内存布局,磁盘布局说起?
-4.Mysql有哪些索引?B树和B+树的区别,分别解决了什么问题?
-5.try catch finally机制讲解一下?
-6.为什么要使用SpringBoot做开发?与传统的开发有什么不一样的?
-7.什么是微服务?微服务是如何实现服务的注册与发现的?
-8.java中的集合分类有哪些?知道Queue吗?她下面有哪些实现类?重点说说HashMap?
-9.在集合中哪些集合类是线程安全的?
-10.什么是数字签名,作用是什么?使用的是什么算法?
-11.常见的网络攻击有哪些?
-12.在表单提交的时候,容易发起什么样的攻击?
-13.在进行服务调用的时候如何进行身份验证,如何防止网络攻击?
-14.你见过哪些安全框架?具体怎么使用的?(shiro)
-15.两道算法题:1)普通的二分查找,问了其中的一些细节,二分查找存在的问题? 2)判断S1中是不是有S2的排列,找到返回true,否则返回false
-16.Cookie和session 的使用场景,他们之间的关系?
-17.String,StringBuilder,StringBuffer的区别,String的两种初始化的区别?
-
-**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
-
-[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
+1. 面试官介绍自己,然后自我介绍
+2. java中的线程池有哪些?为什么使用线程池?你在哪里使用过或是见过?
+3. Mysql底层是怎么实现的?从内存布局,磁盘布局说起?
+4. Mysql有哪些索引?B树和B+树的区别,分别解决了什么问题?
+5. try catch finally机制讲解一下?
+6. 为什么要使用SpringBoot做开发?与传统的开发有什么不一样的?
+7. 什么是微服务?微服务是如何实现服务的注册与发现的?
+8. java中的集合分类有哪些?知道Queue吗?她下面有哪些实现类?重点说说HashMap?
+9. 在集合中哪些集合类是线程安全的?
+10. 什么是数字签名,作用是什么?使用的是什么算法?
+11. 常见的网络攻击有哪些?
+12. 在表单提交的时候,容易发起什么样的攻击?
+13. 在进行服务调用的时候如何进行身份验证,如何防止网络攻击?
+14. 你见过哪些安全框架?具体怎么使用的?(shiro)
+15. 两道算法题:1)普通的二分查找,问了其中的一些细节,二分查找存在的问题? 2)判断S1中是不是有S2的排列,找到返回true,否则返回false
+16. Cookie和session 的使用场景,他们之间的关系?
+17. String,StringBuilder,StringBuffer的区别,String的两种初始化的区别?
+
+
+
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
\ No newline at end of file
diff --git a/docs/campus-recruit/interview/4-ali.md b/docs/campus-recruit/interview/4-ali.md
index 2c99b2e..00b2a7a 100644
--- a/docs/campus-recruit/interview/4-ali.md
+++ b/docs/campus-recruit/interview/4-ali.md
@@ -48,6 +48,18 @@
24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
25. B+树的特点与优势
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
+
## 面经3
- 自我介绍,说简历里没有的东西
@@ -74,6 +86,14 @@
- 服务注册的时候发现没有注册成功会是什么原因。
- 讲讲你认为的rpc和service mesh之间的关系。
-**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
-[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
+
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
\ No newline at end of file
diff --git a/docs/campus-recruit/interview/5-kuaishou.md b/docs/campus-recruit/interview/5-kuaishou.md
index a06a8c2..539a84a 100644
--- a/docs/campus-recruit/interview/5-kuaishou.md
+++ b/docs/campus-recruit/interview/5-kuaishou.md
@@ -1,79 +1,332 @@
# 快手
-## 一面
-
-1. 简单介绍项目
-2. 知道哪些数据结构以及他们的特点
-3. 链表增删快,那如何提高其查询效率,有没有什么想法?
-4. B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么?
-5. 跳表了解吗?
-6. 大顶堆、小顶堆了解吗?
-7. 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射?
-8. 那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
-9. 分布式是什么?为什么要分布式?分布式又会有哪些问题?分布式系统是如何实现事物的?
-10. Redis集群了解吗?如何处理宕机的情况?Redis的同步策略?
-11. LRU算法了解吗?你会如何实现它?这个算法可以应用在哪些场景下?
-12. TCP为什么是三次握手?两次行不行?多次行不行?
-13. TCP的安全性是如何实现的?两台服务器之间可以同时建立多条TCP链接吗?怎么实现的?
-14. 客服端输入一个网址后,是如何拿到客服想要的数据的,是怎样在网络中传输的?
-15. cookie和session
-16. java有哪些锁?共享锁是什么?CAS?乐观锁和悲观锁?synchronied的底层原理?锁升级?死锁怎么形成的?如何破解死锁?
-
-## 二面
-
-
-1. Java容器:List,Set,Map
-2. Map的遍历方式
-3. HashMap扩容为什么是扩为两倍?
-4. Java线程同步机制(信号量,闭锁,栅栏)
-5. 对volatile的理解:常用于状态标记
-6. 八种基本数据类型的大小以及他们的封装类(顺带了解自动拆箱与装箱)
-7. 线程阻塞几种情况?如何自己实现阻塞队列?
-8. Java垃圾回收。可达性分析->引用级别->二次标记(finalize方法)->垃圾收集 算法(4个)->回收策略(3个)->垃圾收集器(GMS、G1)。
-9. java内存模型
-10. TCP/IP的理解
-11. 进程和线程的区别
-12. http状态码含义
-13. ThreadLocal(线程本地变量),如何实现一个本地缓存
-14. JVM内存区哪里会出现溢出?
-15. 双亲委派模型的理解,怎样将两个全路径相同的类加载到内存中?
-16. CMS收集器和G1收集器
-17. TCP流量控制和拥塞控制
-18. 服务器处理一个http请求的过程
-19. 例举几个Mysql优化手段
-20. 数据库死锁定义,怎样避免死锁
-21. spring的aop是什么?如何实现的
-22. 面向对象的设计原则
-23. 策略模式的实现
-24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
-25. B+树的特点与优势
-
-## 三面
-
-- 自我介绍,说简历里没有的东西
-- 说几个你最近在看的技术(MySQL,多线程)
-- 口述了一个统计数据的场景题
-- 如果这个统计数据场景不用MySQL,而是用Java来实现,怎么做
-- 如果数据量过大,内存放不下呢
-- 用面向对象的思想解决上面提出的问题,创建出父类,子类,方法,说一下思路
-- 下一个场景,口述了一个登录场景,同学用线程池做登录校验,会有什么问题
-- 如何解决这些问题
-- 你给出的方案弊端在哪里,还有哪些方案
-
-## 四面
-
-- 谈谈类加载机制。
-- hashmap和concurenthashmap
-- 16g机器,让你分配jvm内存怎么分配。
-- 机器慢了怎么排查。
-- 谈谈consul和zookeeper,还有服务发现机制。
-- 详细说明raft协议。
-- 谈谈consul和zookeeper区别。
-- 服务注册的时候发现没有注册成功会是什么原因。
-- 讲讲你认为的rpc和service mesh之间的关系。
-
-
-
-**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
-
-[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
+## 面经1-一面
+
+> **开始先是手撕算法两道**
+
+1. 自我介绍
+2. 两道手撕
+ 1. 将字符串转化为整数 (这里当时出现溢出值问题,进行了思考解决,写了两种方式)
+ 2. synchronize , 可以使用的几种形式,代码写出
+
+> **操作系统 和 数据结构**
+
+1. hash解决冲突 ( 开放定址法、链地址法、再哈希法、建立公共溢出区 )
+2. 上述四种方式详细的过程、思路
+3. 链地址法和再哈希法之间的关联和区别
+4. 两者分别适用场景
+5. 两者底层的数据结构,关联和区别
+6. 链表和数组的底层结构设计、关联、区别、应用场景
+
+> **常用算法**
+
+1. 常用的排序算法 ( 冒泡、堆、快速、桶、选择、插入 )
+2. 堆排序和选择排序使用场景上有什么区别
+3. 选择排序和堆排序对于资源的利用 ( 选择排序适合数据量少的情况、堆排序适合数据量多的情况,资源利用率、设计思路 )
+4. 常用的查找结构都有什么? ( 二分查找法、插值法、hash查找、分块查找、树表查找 )
+
+> **数据结构**
+
+1. b树和b+树和红黑树的设计思路、结构区别、使用区别
+2. 队列和栈有什么区别
+3. 他们的使用场景 ( 栈:数据匹配、数据反转;队列:任务队列、共享打印机 )
+
+> **Jvm**
+
+1. jvm内存模型
+2. jvm垃圾回收算法
+3. jvm垃圾回收器
+4. cms、g1的设计思路、关联和区别、垃圾回收阶段的不同
+5. 让你设计系统中进行选择其中一个回收器,你的想法是什么
+
+> **使用框架、底层原理**
+
+1. 在你的开发中最常使用的框架
+2. SpringBoot常用注解
+3. RestController和Controller有什么区别
+4. 你在完成项目的过程中是怎么处理异常的 (全局异常梳理)
+5. 全局拦截器的设计、项目中实现 (注解、类)
+6. Aop的了解、怎么使用
+7. Aop底层实现( JDK、CGLib、动态代理实现 )
+8. asm是什么 (字节码增强器)
+
+> **MySql**
+
+1. Mysql事务隔离级别
+2. 什么情况下使用读已提交
+3. 对于脏读的理解
+
+> **redis**
+
+1. 对于redis的理解
+2. redis在项目中进行怎么样的使用
+3. redis 为什么读取速度那么块 (io、单线程、内存)
+4. 为什么redis单线程会快 (完全基于内存、单线程避免不必要的上下文切换、cpu消耗、加锁问题。。。)
+5. 对于很多文件和数据,怎么进行数据的查找、排序,使用什么样的数据结构 (类似于TopK、这个主要是让你进行优化、类似于位图、hash、过滤器之类的)
+6. 反问:
+ 1. 对于部门的业务、技术栈
+ 2. 对我的建议、和整个面试的感觉
+
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
+
+## 面经1-二面
+
+> **Java基础**
+
+1. 自我介绍
+2. 抽象类和接口有什么区别
+3. 在使用过程中,接口和抽象类的选择以及使用场景
+
+> **计网、Linux**
+
+1. http 和j https 的区别
+2. https 过程中都使用哪些加密的算法 ( 对称加密、非对称加密 )
+3. 都怎么使用的,这些j加密算法的理解
+4. Linux都是用过哪些常用命令 (cat、less、tail、grep、wc....)
+5. 查看系统内存 ( top )
+6. 查看系统内存,返回多个指标,怎么查看内存的占用率
+7. 怎么将系统内存显示的数据进行排序
+
+> **Java基础加深、线程、锁、数据机构等等**
+
+1. java里面的类加载器的设计
+2. 类加载器的类之间的可见性 (委托机制、单一性、可见性)
+3. 如果父级对子级进行调用,会出现什么异常
+4. 线程都有哪些状态
+5. blocking和waiting有什么区别吗
+6. 如果是sleep(1000) 会让线程进入什么状态
+7. synchronize的使用流程
+8. java中的原子类实现原理
+9. 对CAS的了解
+10. 对CAS底层了解
+11. HashMap的底层实现原理
+12. HashMap的put流程
+13. ConcurrentHashMap的实现原理
+
+> **框架Spring,代理**
+
+1. Spring的Aop的底层实现
+2. 动态代理的了解 ( 见上面文章 )
+3. 静态代理和动态代理的区别
+4. 对动态代理性能的了解
+5. 浅拷贝和深拷贝的区别
+6. 手撕 : topK问题 ( 堆、优先队列、快排、冒泡 )
+7. 大顶堆小顶堆的设计思路
+
+> **收尾的小问题**
+
+1. 在实习中最有成就感的项目
+2. 对抖音和快手的看法
+3. 反问
+ 1. 业务的具体方向
+ 2. 对我的整体感觉和建议
+
+## 面经1-三面
+
+1. 自我介绍
+2. 介绍一个你最得意的项目
+3. 介绍一下你的实习经历
+4. 实习项目中介绍一个你印象最深的需求
+5. 这个需求的设计、使用的框架详细介绍
+6. 这个项目的上线效果怎么样的
+7. 上线需要的什么问题
+8. 你在实习公司的转正情况
+9. 还有其他的offer吗
+10. 你对快手怎么看的
+11. 面试官主动介绍部门
+12. 反问
+ 1. 部门的业务、地点 ( 因为之前面试的组hc没了,转到隔壁组,重新问的业务方面 )
+ 2. 对我整体面试看法 ( 说的是看我之前面试,聊的挺详细的,面评也不错,等hr )
+
+## 面经1-HR面试
+
+1. 面试官先自我介绍了
+2. 最近2-3年,挑一个最有代表性的一件事
+3. 你为什么觉得这件事最有代表性呢
+4. 在你的整体实习的话,给自己打分你会打几分 、10分制 ( 我打的8分 )
+5. 你都做了那些事情,让你打的8分
+6. 那你觉得从那些手段方法提升剩下的2分呢
+7. 你完成实习之后,有哪些收获呢
+8. 考虑提前实习吗
+9. 毕业之后的未来规划
+10. 之后的定居城市怎么想的
+11. 还有什么进行的面试流程吗
+12. 你心中对这些公司的排序 ( 地点、技术、前景 )
+13. 反问
+ 1. 什么时候出结果
+ 2. 对我的整体感觉
+
+## 面经2-一面
+
+1、聊项目
+
+2、线程的几种状态
+
+3、线程池的状态
+
+4、线程池的运行过程
+
+5、如何合理地配置线程池
+
+6、怎么实现阻塞队列
+
+7、怎么监控线程池的运行状态,答的用一些线程监控的工具,面试官说指代码层面上,只争对线程池,没答上
+
+线程池执行类ThreadPoolExecutor给了相关的API来监控某一个线程池的执行状态,能实时获取线程池当前活动线程数、正在排队线程数、已执行线程数、总线程数等。
+
+```java
+ThreadPoolExecutor tpe = ((ThreadPoolExecutor) es);
+while (true) {
+ System.out.println();
+
+ int queueSize = tpe.getQueue().size();
+ System.out.println("当前排队线程数:" + queueSize);
+
+ int activeCount = tpe.getActiveCount();
+ System.out.println("当前活动线程数:" + activeCount);
+
+ long completedTaskCount = tpe.getCompletedTaskCount();
+ System.out.println("执行完成线程数:" + completedTaskCount);
+
+ long taskCount = tpe.getTaskCount();
+ System.out.println("总线程数:" + taskCount);
+
+ Thread.sleep(3000);
+}
+```
+
+8、java中有几种锁
+
+9、锁升级的过程(自旋的缺点,CAS有什么不足)
+
+10、对象头的结构
+
+11、synchronized和ReentrantLock区别
+
+12、ReentrantLock是怎么实现的,讲到AQS,顺便说了AQS
+
+13、还有哪些基于AQS的同步工具
+
+14、volatile作用
+
+15、volatile怎么保证可见性和防止指令重排序
+
+16、mysql的隔离级别
+
+17、事务acid
+
+18、mysql如何保证acid
+
+19、redo log和undo log区别
+
+20、redo log和undo log是如何生成的(这块细节忘了,只说了先写内存,然后再刷盘)
+
+21、介绍几种消息队列
+
+22、说说rabittmq架构(说了分为虚拟机、交换机和队列,然后说了下消息的传递过程,面试官否认了,说这只是应用层面)
+
+23、jvm的内存模型
+
+24、对象什么情况会进去老年代
+
+25、spring ioc aop
+
+26、注解底层怎么实现的(动态代理)
+
+27、注解失效有哪些原因(自己还经历过@Transaction失效的bug的,当时没答上来,被自己气死)
+
+28、bean的加载过程
+
+算法:有序数组生成平衡二叉树,当时已满60分钟,面试官给了5分钟的时间限制,看我思考了一会,问我有没有思路,我说暂时还没,然后就换了一道题
+
+## 面经2-二面
+
+1、聊项目
+
+2、mysql默认隔离级别
+
+3、如何实现可重复读
+
+4、如何解决幻读
+
+5、间隙锁和nextkey锁
+
+6、mysql锁是锁的什么(索引)
+
+7、mysql的索引结构,有什么优点
+
+8、怎么实现读写分离
+
+9、主从复制是怎么实现同步的,答传bin log文件,后续数据更新怎么同步,答mysq不了解,但我知道redis主从复制后续是通过一个复制缓存区来记录新增的命令,通过发送这些命令实现同步
+
+10、说说redis架构(单线程,io多路复用)
+
+11、redis的底层数据结构知道吗(只知道用到了跳表,然后说了下跳表)
+
+12、缓存穿透和缓存雪崩,解决方法
+
+13、缓存和数据库怎么保证一致性
+
+14、说说threadlocal怎么实现的
+
+15、threadlocalmap中key为啥要用弱引用,key被gc后value怎么办
+
+16、说说四种引用
+
+17、spring事务传播机制
+
+18、spring如何解决循环依赖
+
+19、说说tcp协议
+
+20、tcp如何保证不会接受重复的报文
+
+21、tcp如何保证有序
+
+算法:lc124. 二叉树中的最大路径和
+
+部门:商业化技术部
+
+## 面经2-三面
+
+1. 自我介绍
+3. Redis 是单线程还是多线程?为什么快?
+4. IO多路复用和非阻塞IO? IO多路复用提升了什么性能? IO多路复用提升了CPU哪方面的指标
+5. 线程池使用过吗?线程池的运行原理?
+6. IO密集型和CPU密集型的区别
+7. IO密集型的线程数配置过多会对CPU有什么影响?
+8. Zookeeper 的原理
+9. 为什么使用Zookeeper
+10. Zookeeper为什么要主从,选举机制
+11. MySQL的主从是什么原理
+12. TCP为什么是可靠的
+13. 能提前实习吗?
+14. 未来三到五年的规划?
+15. 算法题 lc简单题
+17. 能来提前实习吗?
+
+反问:对应届生的要求。
+
+
+
+
+
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
\ No newline at end of file
diff --git a/docs/campus-recruit/interview/6-meituan.md b/docs/campus-recruit/interview/6-meituan.md
index 43a1b71..32cb5bf 100644
--- a/docs/campus-recruit/interview/6-meituan.md
+++ b/docs/campus-recruit/interview/6-meituan.md
@@ -68,3 +68,12 @@
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
\ No newline at end of file
diff --git a/docs/campus-recruit/lack-project-experience.md b/docs/campus-recruit/lack-project-experience.md
index 4b69e83..c8b6848 100644
--- a/docs/campus-recruit/lack-project-experience.md
+++ b/docs/campus-recruit/lack-project-experience.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 没有项目经验,怎么办?
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 项目经验
+ - - meta
+ - name: description
+ content: 没有项目经验,怎么办?
+---
+
# 没有项目经验,怎么办?
这个问题有很多人问过我了,今天来聊聊具体解决方案。
@@ -35,7 +50,7 @@ https://github.com/newbee-ltd/newbee-mall
newbee-mall 项目是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 2.X 及相关技术栈开发。 前台商城系统包含首页门户、商品分类、新品上线、首页轮播、商品推荐、商品搜索、商品展示、购物车、订单结算、订单流程、个人订单管理、会员中心、帮助中心等模块。 后台管理系统包含数据面板、轮播图管理、商品管理、订单管理、会员管理、分类管理、设置等模块。
-
+
## litemall
@@ -55,17 +70,19 @@ https://github.com/linlinjava/litemall
- 优惠券列表、优惠券选择
- ...
-
+
+
+
-
+在这里也分享一份大彬精心整理的**大厂面试手册**,包含**计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
-在这里也分享一份非常棒的Java学习笔记,**Github标星137k+**!这份笔记主要Java基础、容器、Java IO、并发和虚拟机等内容,排版精良,内容更是无可挑剔。
+
-
+
-需要的小伙伴可自行下载:
+需要的小伙伴可以自行**下载**:
-http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=100000392&idx=1&sn=f6c8e84651ce48f6ef5b0d496f0f6adf&chksm=4e98ffce79ef76d8dcebdc4787ae8b37760ec193574da9036e46954ae8954ebd56c78792726f#rd
+http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd
## eladmin
@@ -81,9 +98,9 @@ https://github.com/elunez/eladmin
使用的技术栈也比较新,给作者点赞!
-
+
-
+
## vhr
@@ -93,7 +110,7 @@ https://github.com/lenve/vhr
微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。项目加入常见的企业级应用所涉及到的技术点,例如 Redis、RabbitMQ 等。
-
+
## My-Blog
@@ -103,7 +120,7 @@ https://github.com/ZHENFENG13/My-Blog
My Blog 是由 SpringBoot + Mybatis + Thymeleaf 等技术实现的 Java 博客系统,页面美观、功能齐全、部署简单及完善的代码,一定会给使用者无与伦比的体验!
-
+
@@ -115,7 +132,7 @@ https://github.com/saysky/ForestBlog
一个简单漂亮的SSM(Spring+SpringMVC+Mybatis)博客系统。该博客是基于SSM实现的个人博客系统,适合初学SSM和个人博客制作的同学学习。
-
+
## Blog
@@ -125,7 +142,7 @@ https://github.com/zhisheng17/blog
`My-Blog` 使用的是 Docker + SpringBoot + Mybatis + thymeleaf 打造的一个个人博客模板。此项目在 [Tale](https://github.com/otale/tale) 博客系统基础上进行修改的。
-
+
## community
@@ -135,7 +152,7 @@ https://github.com/codedrinker/community
码问社区。开源论坛、问答系统,现有功能提问、回复、通知、最新、最热、消除零回复功能。技术栈 Spring、Spring Boot、MyBatis、MySQL/H2、Bootstrap。
-
+
@@ -164,7 +181,7 @@ V部落,Vue+SpringBoot实现的多用户博客管理平台!
5.mavon-editor
6.vue-router
-
+
## gpmall
@@ -176,9 +193,9 @@ https://github.com/2227324689/gpmall
后端的主要架构是基于springboot+dubbo+mybatis。
-
+
-
+
## guns
@@ -192,7 +209,7 @@ Guns基于**插件化架构**,在建设系统时,可以自由组合细粒度
使用Guns可以快速开发出各类信息化管理系统,例如OA办公系统、项目管理系统、商城系统、供应链系统、客户关系管理系统等。
-
+
## music-website
@@ -204,7 +221,7 @@ https://github.com/Yin-Hongwei/music-website
前端技术栈:Vue3.0 + TypeScript + Vue-Router + Vuex + Axios + ElementPlus + Echarts。
-
+
diff --git a/docs/campus-recruit/layoffs-solution.md b/docs/campus-recruit/layoffs-solution.md
index 8a42189..6cdfa85 100644
--- a/docs/campus-recruit/layoffs-solution.md
+++ b/docs/campus-recruit/layoffs-solution.md
@@ -1,3 +1,7 @@
+---
+sidebar: heading
+---
+
今年确实是互联网寒冬啊!
从2022年5月中旬以来,包括腾讯、阿里巴巴、字节跳动、美团、拼多多、快手、百度、京东、网易等在内的十余家企业被爆出裁员消息。
diff --git a/docs/campus-recruit/leetcode-guide.md b/docs/campus-recruit/leetcode-guide.md
index 269149d..f65e6f8 100644
--- a/docs/campus-recruit/leetcode-guide.md
+++ b/docs/campus-recruit/leetcode-guide.md
@@ -1,9 +1,18 @@
---
sidebar: heading
+title: LeetCode刷题经验分享
+category: 分享
+tag:
+ - 刷题
+head:
+ - - meta
+ - name: keywords
+ content: LeetCode刷题经验,刷题
+ - - meta
+ - name: description
+ content: LeetCode刷题经验分享
---
-
-
分享几点我自己的刷题经验,看看我是如何在最短时间内搞定数据结构与算法,达到应付面试的程度的。
主要有以下3点技巧:
@@ -141,4 +150,4 @@ LeetCode上面的题目都有进行分类,建议在一个时间段只刷同一
**做好总结很重要**,特别是对于没思路的题目,看了其他大佬的解法之后,多思考有哪些题目也是类似解法,这种题目的关键解题步骤,把自己的理解写下来,方便自己日后查看。
-虽然总结可能会花费你半个钟甚至更多的时间,但是不总结的话,下次你遇到这个题目,可能会花更多的时间去思考、解答。
\ No newline at end of file
+虽然总结可能会花费你半个钟甚至更多的时间,但是不总结的话,下次你遇到这个题目,可能会花更多的时间去思考、解答。
diff --git a/docs/campus-recruit/program-language/java-or-c++.md b/docs/campus-recruit/program-language/java-or-c++.md
index b4dc813..5ae61fe 100644
--- a/docs/campus-recruit/program-language/java-or-c++.md
+++ b/docs/campus-recruit/program-language/java-or-c++.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: Java和C++怎么选?
+category: 分享
+tag:
+ - 职业规划
+head:
+ - - meta
+ - name: keywords
+ content: java和c++,语言选择
+ - - meta
+ - name: description
+ content: 努力打造最优质的Java学习网站
---
# Java和C++怎么选?
diff --git a/docs/campus-recruit/program-language/java-or-golang.md b/docs/campus-recruit/program-language/java-or-golang.md
index 22ce39f..524ccab 100644
--- a/docs/campus-recruit/program-language/java-or-golang.md
+++ b/docs/campus-recruit/program-language/java-or-golang.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: Java和Golang怎么选?
+category: 分享
+tag:
+ - 职业规划
+head:
+ - - meta
+ - name: keywords
+ content: java和golang,语言选择
+ - - meta
+ - name: description
+ content: 努力打造最优质的Java学习网站
---
# Java和Golang怎么选?
@@ -70,4 +81,4 @@ Java社区非常活跃,各种文档和学习资料非常丰富。因为使用
## 四、Java学习路线
-[点这里](https://topjavaer.cn/learning-resources/java-learn-guide.html#%E8%87%AA%E5%AD%A6%E8%B7%AF%E7%BA%BF)
\ No newline at end of file
+[点这里](https://topjavaer.cn/learning-resources/java-learn-guide.html#%E8%87%AA%E5%AD%A6%E8%B7%AF%E7%BA%BF)
diff --git a/docs/campus-recruit/project-experience.md b/docs/campus-recruit/project-experience.md
index d4da20a..cad72fa 100644
--- a/docs/campus-recruit/project-experience.md
+++ b/docs/campus-recruit/project-experience.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 项目经验怎么回答
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 项目经验
+ - - meta
+ - name: description
+ content: 项目经验怎么回答
---
# 项目经验怎么回答
@@ -121,4 +132,4 @@ sidebar: heading
-> 链接:https://www.nowcoder.com/discuss/150755
\ No newline at end of file
+> 链接:https://www.nowcoder.com/discuss/150755
diff --git a/docs/campus-recruit/question-ask-me.md b/docs/campus-recruit/question-ask-me.md
index 94b05d7..9d2e98e 100644
--- a/docs/campus-recruit/question-ask-me.md
+++ b/docs/campus-recruit/question-ask-me.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 面试官:你有什么要问我的吗?
+category: 分享
+tag:
+ - 面试题
+head:
+ - - meta
+ - name: keywords
+ content: 面试高频题
+ - - meta
+ - name: description
+ content: 面试官:你有什么要问我的吗?
+---
+
## 你有什么要问我的吗?
很多时候,在面试接近尾声的时候,面试官会问应聘者一个问题:“你有什么要问我的吗?”。
@@ -116,4 +131,4 @@
**工作环境篇**
1. 办公室布局是什么样的,是开放式/小隔间还是办公室?
-2. 我的新团队是否有支持/市场等团队支持?
\ No newline at end of file
+2. 我的新团队是否有支持/市场等团队支持?
diff --git a/docs/campus-recruit/resume.md b/docs/campus-recruit/resume.md
index 484253c..b4c1636 100644
--- a/docs/campus-recruit/resume.md
+++ b/docs/campus-recruit/resume.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 简历应该怎么写?
+category: 分享
+tag:
+ - 简历
+head:
+ - - meta
+ - name: keywords
+ content: 简历编写,写简历
+ - - meta
+ - name: description
+ content: 简历应该怎么写?
---
很多同学刚开始找工作时,投出去很多简历,但是都石沉大海了,没有下文。
diff --git a/docs/campus-recruit/share/1-23-backend.md b/docs/campus-recruit/share/1-23-backend.md
index 9ac81d6..7e39e0f 100644
--- a/docs/campus-recruit/share/1-23-backend.md
+++ b/docs/campus-recruit/share/1-23-backend.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 双非本,非科班自学转码分享
+category: 分享
+tag:
+ - 校招
+head:
+ - - meta
+ - name: keywords
+ content: 非科班转码,自学java,双非转码
+ - - meta
+ - name: description
+ content: 自学Java经验分享
---
# 双非本,非科班的自我救赎之路
diff --git a/docs/campus-recruit/share/2-no-offer.md b/docs/campus-recruit/share/2-no-offer.md
index 087a45f..037ad5b 100644
--- a/docs/campus-recruit/share/2-no-offer.md
+++ b/docs/campus-recruit/share/2-no-offer.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 非科班,秋招还没offer,该怎么办
+category: 分享
+tag:
+ - 校招
+head:
+ - - meta
+ - name: keywords
+ content: 非科班转码,秋招没offer,23秋招,秋招
+ - - meta
+ - name: description
+ content: 秋招经验分享
---
# 非科班,秋招还没offer,该怎么办
diff --git a/docs/campus-recruit/share/2-years-tech-upgrade.md b/docs/campus-recruit/share/2-years-tech-upgrade.md
new file mode 100644
index 0000000..5322d98
--- /dev/null
+++ b/docs/campus-recruit/share/2-years-tech-upgrade.md
@@ -0,0 +1,61 @@
+---
+sidebar: heading
+title: 工作两年多,技术水平没有很大提升,该怎么办?
+category: 分享
+tag:
+ - 星球
+head:
+ - - meta
+ - name: keywords
+ content: 职业规划,技术提升
+ - - meta
+ - name: description
+ content: 星球问题摘录
+---
+
+## 工作两年多,技术水平没有很大提升,该怎么办?
+
+最近在大彬的[知识星球](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247492252&idx=1&sn=8fc12e97763e3b994b0dd0e717a4b674&chksm=ce9b1fdaf9ec96cca6c03cb6e7b61156d3226dbb587f81cea27b71be6671b81b537c9b7e9b2d#rd)中,有小伙伴提了问题:**工作两年多,技术水平没有很大提升,该怎么办?**
+
+**原问题如下**:
+
+大彬大佬能不能给点学习建议,我是**非计算机专业**,**培训**的java后端,现在开发工作两年多了,越是工作其实就越会发现自己的**知识面很窄**,没办法提升技术水平,所以想要自己学习提升下,但是**没啥方向**,不知道该先学什么,所以想请大佬指点下,谢谢!
+
+---
+
+**大彬的回答**:
+
+最简单的一个方法就是找一个比你现在公司**技术方面强一些**的公司,到他们招聘网站看看**岗位职责描述**(1-3年工作经验的Java开发),对比下自己缺少哪些技能,**查漏补缺**。以跳槽到更好的公司为目标进行学习,这样既有动力也有学习方向。
+
+> 附上阿里菜鸟1-3年的JD:
+>
+> 1. 扎实的编程基础,精通java开发语言,熟悉jvm,web开发、缓存,分布式架构、消息中间件等核心技术;
+> 2. 掌握多线程编码及性能调优,有丰富的高并发、高性能系统、幂等设计和开发经验;
+> 3. 精通Java EE相关的主流开源框架,能了解到它的原理和机制,如SpringBoot、Spring、Mybatis等;
+> 4. 熟悉Oracle、MySql等数据库技术,对sql优化有一定的经验;
+> 5. 思路清晰,良好的沟通能力与技术学习能力;
+> 6. 有大型网站构建经验优先考虑;
+
+如果你在一个小公司或外包公司的话,一般一到两年时间就把用到的技术栈基本都摸透了,因为业务量不大,很难接触到像**高并发、分布式、灾备、异地多活、分片**等,每天都是重复的增删改查,**很难有技术沉淀**。
+
+工作久了之后,你就会发现,到职业中后期,**公司的技术上限也是你的技术上限**,单靠自己盲目去学,缺少实践机会,技术上也很难精进。只有去更大的平台,你能接触到的业务场景、技术就会更多,技术能力也就能随着慢慢变强了。
+
+---
+
+最后,推荐大家加入我的[知识星球](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247492252&idx=1&sn=8fc12e97763e3b994b0dd0e717a4b674&chksm=ce9b1fdaf9ec96cca6c03cb6e7b61156d3226dbb587f81cea27b71be6671b81b537c9b7e9b2d&scene=21#wechat_redirect),目前已经有200多位小伙伴加入了,星球已经更新了多篇**高质量文章、优质资源、经验分享**,利用好的话价值是**远超**门票的。
+
+
+
+星球提供以下这些**服务**:
+
+1. 星球内部**知识图谱**,汇总了**优质资源、面试高频问题、大厂面经、踩坑分享、面试资料**,让你少走一些弯路
+2. 四个**优质专栏**、Java**面试手册完整版**(包含场景设计、系统设计、分布式、微服务等),持续更新
+3. **一对一答疑**,我会尽自己最大努力为你答疑解惑
+4. **免费的简历修改、面试指导服务**,绝对赚回门票
+5. **中大厂内推**,助你更快走完流程、拿到offer
+6. 各个阶段的**优质学习资源**(新手小白到架构师),包括一些大彬自己花钱买的课程,都分享到星球了,超值
+7. 打卡学习、读书分享活动,**大学自习室的氛围**,一起蜕变成长
+
+**加入方式**:**扫描二维码**领取优惠券即可加入~
+
+
diff --git a/docs/campus-recruit/share/3-power-grid-vs-pdd.md b/docs/campus-recruit/share/3-power-grid-vs-pdd.md
index 356e504..e3db044 100644
--- a/docs/campus-recruit/share/3-power-grid-vs-pdd.md
+++ b/docs/campus-recruit/share/3-power-grid-vs-pdd.md
@@ -1,3 +1,19 @@
+---
+sidebar: heading
+title: 国家电网还是拼多多,怎么选?
+category: 分享
+tag:
+ - offer选择
+head:
+ - - meta
+ - name: keywords
+ content: offer选择,秋招offer比较
+ - - meta
+ - name: description
+ content: 秋招offer选择经验分享
+---
+
+
今天在知乎上看到这个职业选择的问题:“**国家电网还是拼多多,怎么选?**”,其中高赞的观点我觉得非常具有参考价值,今天分享出来,也希望能给球友们一些启发和帮助。
**原问题**
@@ -128,4 +144,4 @@
**任何选择,都可能伴有不甘和遗憾,我们普通人很难有洞见未来的能力,但我们还是可以基于过往去做出最适合自己的决定。**
-那做完决定之后,最重要的是,不要陷入自我怀疑当中,如果纠错的成本可以接受,那完全可以反悔,就像你秋招接了一个不是自己预期的 offer 保底,心有不甘,春招又冲了一个理想的 offer,那就赔偿违约金就好了。
\ No newline at end of file
+那做完决定之后,最重要的是,不要陷入自我怀疑当中,如果纠错的成本可以接受,那完全可以反悔,就像你秋招接了一个不是自己预期的 offer 保底,心有不甘,春招又冲了一个理想的 offer,那就赔偿违约金就好了。
diff --git a/docs/campus-recruit/share/4-agricultural-bank.md b/docs/campus-recruit/share/4-agricultural-bank.md
index a0cd647..1e4c071 100644
--- a/docs/campus-recruit/share/4-agricultural-bank.md
+++ b/docs/campus-recruit/share/4-agricultural-bank.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 农业银行软件开发工作体验
+category: 分享
+tag:
+ - 工作体验
+head:
+ - - meta
+ - name: keywords
+ content: 工作体验,银行工作体验
+ - - meta
+ - name: description
+ content: 农业银行软件开发工作体验
+---
+
分享一位22届的学弟分享自己在入职农业银行-软件开发岗位2个月后的体验。
我是22届的学生一枚,秋招季选择了农业银行软件开发一职,现在入职大概2个月了,也就是九月份,趁着这段时间就聊聊这段时间的工作现状吧。
@@ -62,4 +77,4 @@
## 未来展望
-希望接下来的几个月通过自己的努力,能够逐渐胜任目前的工作,然后顺利转正,趁自己的学习和记忆能力还没有下降太多,把该考的证书都考下来,希望能更上一层楼吧 。
\ No newline at end of file
+希望接下来的几个月通过自己的努力,能够逐渐胜任目前的工作,然后顺利转正,趁自己的学习和记忆能力还没有下降太多,把该考的证书都考下来,希望能更上一层楼吧 。
diff --git a/docs/campus-recruit/share/5-feizhu-meituan-internship.md b/docs/campus-recruit/share/5-feizhu-meituan-internship.md
index 97e55f1..87e703e 100644
--- a/docs/campus-recruit/share/5-feizhu-meituan-internship.md
+++ b/docs/campus-recruit/share/5-feizhu-meituan-internship.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 阿里和美团实习的经历分享
+category: 分享
+tag:
+ - 工作体验
+head:
+ - - meta
+ - name: keywords
+ content: 实习经历分享
+ - - meta
+ - name: description
+ content: 阿里飞猪和美团基础架构组实习的经历分享
+---
+
分享一位学弟在阿里飞猪和美团基础架构组实习的经历,很不错的分享,非常用心!
## 为什么选飞猪
@@ -204,4 +219,4 @@
但我们也不能放弃,争取拿到全局最差区间的最优解吧。
-共勉,人生的道路还很长,一时的 offer 也说明不了什么。最近看到阿里和虾皮的 22 届,明明那么优秀,收到这么高的 offer,但是最终应届被裁一场空,也感觉很悲哀的。
\ No newline at end of file
+共勉,人生的道路还很长,一时的 offer 也说明不了什么。最近看到阿里和虾皮的 22 届,明明那么优秀,收到这么高的 offer,但是最终应届被裁一场空,也感觉很悲哀的。
diff --git a/docs/campus-recruit/share/6-2023-autumn-recruit.md b/docs/campus-recruit/share/6-2023-autumn-recruit.md
index c352908..787bec9 100644
--- a/docs/campus-recruit/share/6-2023-autumn-recruit.md
+++ b/docs/campus-recruit/share/6-2023-autumn-recruit.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 23届秋招,寒气逼人。。
+category: 分享
+tag:
+ - 校招
+head:
+ - - meta
+ - name: keywords
+ content: 秋招经历分享
+ - - meta
+ - name: description
+ content: 2023 届秋招经历分享
+---
+
## 23届秋招,寒气逼人。。
分享一篇牛客网友的 2023 届秋招经历分享,写的很不错,很真实。
@@ -158,4 +173,4 @@
4. 多刷 力扣 Hot 100,或者 Codetop 热门题,反复刷;
5. **选择大于努力**;
-**在寒气逼人的 2022,我们需要抱团取暖。**
\ No newline at end of file
+**在寒气逼人的 2022,我们需要抱团取暖。**
diff --git a/docs/career-plan/3-years-reflect.md b/docs/career-plan/3-years-reflect.md
index c4ff5ac..21e4cfc 100644
--- a/docs/career-plan/3-years-reflect.md
+++ b/docs/career-plan/3-years-reflect.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 工作3年半,最近岗位有变动,有点迷茫
+category: 分享
+tag:
+ - 星球
+head:
+ - - meta
+ - name: keywords
+ content: 职业规划
+ - - meta
+ - name: description
+ content: 星球问题摘录
+---
+
## 工作3年半,最近岗位有变动,有点迷茫
最近在大彬的[学习圈](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247492252&idx=1&sn=8fc12e97763e3b994b0dd0e717a4b674&chksm=ce9b1fdaf9ec96cca6c03cb6e7b61156d3226dbb587f81cea27b71be6671b81b537c9b7e9b2d#rd)中,有小伙伴提了一个关于职业规划的和自学方面的问题,挺有有代表性的,跟大家分享一下。
@@ -80,5 +95,5 @@
**加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~
-
+
diff --git a/docs/career-plan/4-years-reflect.md b/docs/career-plan/4-years-reflect.md
index 5debaf6..8fcf541 100644
--- a/docs/career-plan/4-years-reflect.md
+++ b/docs/career-plan/4-years-reflect.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 4年工作经验的程序员分享
+category: 分享
+tag:
+ - 工作经历分享
+head:
+ - - meta
+ - name: keywords
+ content: 工作经历分享
+ - - meta
+ - name: description
+ content: 4年工作经验的程序员分享
+---
+
今天给大家分享一个**4年工作经验的程序员**的经历,应该对各位会有所启发。
2022年,我彻底失业了。**在面试了10多家单位后,居然没有一个人给offer**,为此对自己做出了反思。
@@ -20,4 +35,4 @@
然后30岁了。发现有点干不动了。(一到下午就困地不行情绪浮躁,思维混乱,很难集中注意力在工作上,工作10分钟休息半小时)网上说的30多岁身体跟不上是真的。如果加上自己技术一般的话。其实不会多少技术精进的欲望的。30多岁干基层的活真的是干不动的(他需要脑子的灵活和反应快,不需要什么太多智慧技巧,这种年轻人显然更合适)高级的靠智慧的架构管理工作除外,其实应该考虑转型,可以在计算机行业里面,目前我打算做实施。主要脑子反应没有之前快了。做不了机械重复的脑力劳动了,而且想做一做和人打交道的工作,突破一下自己。
-**以上是对自己的4年程序生涯的总结**,兄弟们会看到自己的影子吗。或者会被启发到吗。此文案例真实,希望能帮助大家规避类似错误,找到自己在技术圈的真实定位。
\ No newline at end of file
+**以上是对自己的4年程序生涯的总结**,兄弟们会看到自己的影子吗。或者会被启发到吗。此文案例真实,希望能帮助大家规避类似错误,找到自己在技术圈的真实定位。
diff --git a/docs/career-plan/guoqi-programmer.md b/docs/career-plan/guoqi-programmer.md
index e1daae6..5db7cc2 100644
--- a/docs/career-plan/guoqi-programmer.md
+++ b/docs/career-plan/guoqi-programmer.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 在国企做开发,是什么样的体验
+category: 分享
+tag:
+ - 国企
+head:
+ - - meta
+ - name: keywords
+ content: 国企工作,工作经历分享
+ - - meta
+ - name: description
+ content: 4年工作经验的程序员分享
+---
+
## 在国企做开发,是什么样的体验
> 本文已经收录到Github仓库,该仓库包含**计算机基础、Java核心知识点、多线程、JVM、常见框架、分布式、微服务、设计模式、架构**等核心知识点,欢迎star~
@@ -64,4 +79,4 @@
2、国企搞开发,**技术不会特别新**,很多时候是项目管理的角色。工作内容基本体现为领导的决定。
-3、国企研究技术没有意义,想当领导,就多学习做PPT和领导搞好关系。或者当一个平庸的人,混吃等死,把时间留给家人,也不乏是一种好选择。
\ No newline at end of file
+3、国企研究技术没有意义,想当领导,就多学习做PPT和领导搞好关系。或者当一个平庸的人,混吃等死,把时间留给家人,也不乏是一种好选择。
diff --git a/docs/career-plan/how-to-prepare-job-hopping.md b/docs/career-plan/how-to-prepare-job-hopping.md
index a245cfa..59aee75 100644
--- a/docs/career-plan/how-to-prepare-job-hopping.md
+++ b/docs/career-plan/how-to-prepare-job-hopping.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 工作一年想要跳槽,不知道应该怎么准备?
+category: 分享
+tag:
+ - 星球
+head:
+ - - meta
+ - name: keywords
+ content: 职业规划,跳槽怎么准备,程序员跳槽
+ - - meta
+ - name: description
+ content: 星球问题摘录
+---
+
## 工作一年想要跳槽,不知道应该怎么准备?
最近在大彬的[学习圈](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247492252&idx=1&sn=8fc12e97763e3b994b0dd0e717a4b674&chksm=ce9b1fdaf9ec96cca6c03cb6e7b61156d3226dbb587f81cea27b71be6671b81b537c9b7e9b2d#rd)中,有小伙伴问我怎么准备跳槽、面试,在这里跟大家分享一下。
@@ -70,4 +85,4 @@
**加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~
-
\ No newline at end of file
+
diff --git a/docs/career-plan/java-or-bigdata.md b/docs/career-plan/java-or-bigdata.md
index 529b2df..cf98ec1 100644
--- a/docs/career-plan/java-or-bigdata.md
+++ b/docs/career-plan/java-or-bigdata.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 24届校招,Java开发和大数据开发怎么选
+category: 分享
+tag:
+ - 星球
+head:
+ - - meta
+ - name: keywords
+ content: 职业规划,岗位选择,Java还是大数据
+ - - meta
+ - name: description
+ content: 星球问题摘录
+---
+
## 24届校招,Java开发和大数据开发怎么选
最近在大彬的[学习圈](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247492252&idx=1&sn=8fc12e97763e3b994b0dd0e717a4b674&chksm=ce9b1fdaf9ec96cca6c03cb6e7b61156d3226dbb587f81cea27b71be6671b81b537c9b7e9b2d#rd)中,有小伙伴提了一个关于方向选择的问题:**24届校招,Java开发和大数据开发怎么选**?
@@ -44,4 +59,4 @@
**加入方式**:**扫描二维码**领取优惠券加入(**即将恢复原价**)~
-
\ No newline at end of file
+
diff --git a/docs/computer-basic/algorithm.md b/docs/computer-basic/algorithm.md
index d97594f..3b02e9f 100644
--- a/docs/computer-basic/algorithm.md
+++ b/docs/computer-basic/algorithm.md
@@ -1,8 +1,24 @@
---
sidebar: heading
+title: 常见算法总结
+category: 计算机基础
+tag:
+ - 算法
+head:
+ - - meta
+ - name: keywords
+ content: 算法知识总结,二叉树遍历,排序算法,动态规划,回溯算法,贪心算法,双指针
+ - - meta
+ - name: description
+ content: 算法常见知识点总结
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## 二叉树的遍历
diff --git a/docs/computer-basic/data-structure.md b/docs/computer-basic/data-structure.md
index 904020c..e6e60be 100644
--- a/docs/computer-basic/data-structure.md
+++ b/docs/computer-basic/data-structure.md
@@ -1,8 +1,24 @@
---
sidebar: heading
+title: 常见数据结构总结
+category: 计算机基础
+tag:
+ - 数据结构
+head:
+ - - meta
+ - name: keywords
+ content: 数据结构知识总结,数据结构应用场景,数组,链表,哈希表,栈,队列,树,图
+ - - meta
+ - name: description
+ content: 数据结构常见知识点总结
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## 各种数据结构应用场景
diff --git a/docs/computer-basic/network.md b/docs/computer-basic/network.md
index f2829b8..831488a 100644
--- a/docs/computer-basic/network.md
+++ b/docs/computer-basic/network.md
@@ -1,27 +1,48 @@
+---
+sidebar: heading
+title: 计算机网络常见面试题
+category: 计算机基础
+tag:
+ - 网络
+head:
+ - - meta
+ - name: keywords
+ content: 计算机网络常见面试题
+ - - meta
+ - name: description
+ content: 计算机网络常见面试题,努力打造最优质的Java学习网站
+---
+
**计算机网络重要知识点&高频面试题**是我的[知识星球](https://topjavaer.cn/zsxq/introduce.html)**内部专属资料**,已经整理到**Java面试手册完整版**。

-除了Java面试手册完整版之外,星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
+如果你正在打算准备跳槽、面试,星球还提供**简历指导、修改服务**,大彬已经帮**120**+个小伙伴修改了简历,相对还是比较有经验的。
-
+
-
+
-
+另外星球也提供**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
+
+
-**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
+

-
+星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
-另外星球还提供**简历指导、修改服务**,大彬已经帮**90**+个小伙伴修改了简历,相对还是比较有经验的。
+
-
+
-
+
+
+怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)?
+
+**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。
-[知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
+随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。
-
\ No newline at end of file
+
diff --git a/docs/computer-basic/operate-system.md b/docs/computer-basic/operate-system.md
index 016f114..64e08f0 100644
--- a/docs/computer-basic/operate-system.md
+++ b/docs/computer-basic/operate-system.md
@@ -1,7 +1,25 @@
---
sidebar: heading
+title: 操作系统常见面试题总结
+category: 计算机基础
+tag:
+ - 操作系统
+head:
+ - - meta
+ - name: keywords
+ content: 操作系统面试题,进程线程,并发和并行,协程,进程通信,死锁,进程调度策略,分页分段,页面置换算法,用户态和内核态,IO多路复用
+ - - meta
+ - name: description
+ content: 操作系统常见知识点和面试题总结,让天下没有难背的八股文!
---
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
## 操作系统的四个特性?
并发:同一段时间内多个程序执行(与并行区分,并行指的是同一时刻有多个事件,多处理器系统可以使程序并行执行)
diff --git a/docs/computer-basic/tcp.md b/docs/computer-basic/tcp.md
new file mode 100644
index 0000000..f351994
--- /dev/null
+++ b/docs/computer-basic/tcp.md
@@ -0,0 +1,217 @@
+---
+sidebar: heading
+title: TCP常见面试题总结
+category: 计算机基础
+tag:
+ - TCP
+head:
+ - - meta
+ - name: keywords
+ content: TCP面试题,三次握手,四次挥手,TCP协议,滑动窗口,粘包,TCP和UDP,TCP特点,TCP可靠性
+ - - meta
+ - name: description
+ content: TCP常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+# TCP协议面试题
+
+## 为什么需要TCP协议?
+
+IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
+
+因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
+
+## 说说TCP的三次握手
+
+假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
+
+
+
+1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
+2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
+3. 第三次握手:客户端收到服务端发来的报文后,会再向服务端发送报文,其中包含标志位`ACK=1`,序列号`seq=x+1`,确认号`ack=y+1`。第三次握手前客户端的状态为`SYN-SENT`,第三次握手后客户端和服务端的状态都为`ESTABLISHED`。**此时连接建立完成。**
+
+## 两次握手可以吗?
+
+之所以需要第三次握手,主要为了**防止已失效的连接请求报文段**突然又传输到了服务端,导致产生问题。
+
+- 比如客户端A发出连接请求,可能因为网络阻塞原因,A没有收到确认报文,于是A再重传一次连接请求。
+- 然后连接成功,等待数据传输完毕后,就释放了连接。
+- 然后A发出的第一个连接请求等到连接释放以后的某个时间才到达服务端B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段。
+- 如果不采用三次握手,只要B发出确认,就建立新的连接了,**此时A不会响应B的确认且不发送数据,则B一直等待A发送数据,浪费资源。**
+
+## 说说TCP的四次挥手
+
+
+
+1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
+2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
+3. A收到B的确认后,进入`FIN-WAIT-2`(终止等待2)状态,等待B发出的连接释放报文段。
+4. B发送完数据,就会发出连接释放报文段(`FIN=1,ACK=1,seq=w,ack=u+1`),B进入`LAST-ACK`(最后确认)状态,等待A的确认。
+5. A收到B的连接释放报文段后,对此发出确认报文段(`ACK=1,seq=u+1,ack=w+1`),A进入`TIME-WAIT`(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间`2MSL`(最大报文段生存时间)后,A才进入`CLOSED`状态。B收到A发出的确认报文段后关闭连接,若没收到A发出的确认报文段,B就会重传连接释放报文段。
+
+## 第四次挥手为什么要等待2MSL?
+
+- **保证A发送的最后一个ACK报文段能够到达B**。这个`ACK`报文段有可能丢失,B收不到这个确认报文,就会超时重传连接释放报文段,然后A可以在`2MSL`时间内收到这个重传的连接释放报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入到`CLOSED`状态,若A在`TIME-WAIT`状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到B重传的连接释放报文段,所以不会再发送一次确认报文段,B就无法正常进入到`CLOSED`状态。
+- **防止已失效的连接请求报文段出现在本连接中**。A在发送完最后一个`ACK`报文段后,再经过2MSL,就可以使这个连接所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现旧的连接请求报文段。
+
+## 为什么是四次挥手?
+
+因为当Server端收到Client端的`SYN`连接请求报文后,可以直接发送`SYN+ACK`报文。**但是在关闭连接时,当Server端收到Client端发出的连接释放报文时,很可能并不会立即关闭SOCKET**,所以Server端先回复一个`ACK`报文,告诉Client端我收到你的连接释放报文了。只有等到Server端所有的报文都发送完了,这时Server端才能发送连接释放报文,之后两边才会真正的断开连接。故需要四次挥手。
+
+## SIN/FIN不包含数据却要消耗序列号
+
+凡是需要对端确认的,一定消耗TCP报文的序列号。SYN和FIN需要对端的确认,因此需要消耗一个序列号。
+
+SYN作为三次握手的确认。FIN作为四次挥手的确认。如果没有序列号,会导致SYN请求多次重发,服务端多次处理,造成资源浪费
+
+## 说说TCP报文首部有哪些字段,其作用又分别是什么?
+
+
+
+- **16位端口号**:源端口号,主机该报文段是来自哪里;目标端口号,要传给哪个上层协议或应用程序
+- **32位序号**:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。
+- **32位确认号**:用作对另一方发送的tcp报文段的响应。其值是收到的TCP报文段的序号值加1。
+- **4位头部长度**:表示tcp头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。
+- **6位标志位**:URG(紧急指针是否有效),ACk(表示确认号是否有效),PSH(缓冲区尚未填满),RST(表示要求对方重新建立连接),SYN(建立连接消息标志接),FIN(表示告知对方本端要关闭连接了)
+- **16位窗口大小**:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
+- **16位校验和**:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。
+- **16位紧急指针**:一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此,确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。
+
+## TCP有哪些特点?
+
+- TCP是**面向连接**的运输层协议。
+- **点对点**,每一条TCP连接只能有两个端点。
+- TCP提供**可靠交付**的服务。
+- TCP提供**全双工通信**。
+- **面向字节流**。
+
+## TCP和UDP的区别?
+
+1. TCP**面向连接**;UDP是无连接的,即发送数据之前不需要建立连接。
+2. TCP提供**可靠的服务**;UDP不保证可靠交付。
+3. TCP**面向字节流**,把数据看成一连串无结构的字节流;UDP是面向报文的。
+4. TCP有**拥塞控制**;UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如实时视频会议等)。
+5. 每一条TCP连接只能是**点到点**的;UDP支持一对一、一对多、多对一和多对多的通信方式。
+6. TCP首部开销20字节;UDP的首部开销小,只有8个字节。
+
+## TCP 和 UDP 分别对应的常见应用层协议有哪些?
+
+**基于TCP的应用层协议有:HTTP、FTP、SMTP、TELNET、SSH**
+
+- **HTTP**:HyperText Transfer Protocol(超文本传输协议),默认端口80
+- **FTP**: File Transfer Protocol (文件传输协议), 默认端口(20用于传输数据,21用于传输控制信息)
+- **SMTP**: Simple Mail Transfer Protocol (简单邮件传输协议) ,默认端口25
+- **TELNET**: Teletype over the Network (网络电传), 默认端口23
+- **SSH**:Secure Shell(安全外壳协议),默认端口 22
+
+**基于UDP的应用层协议:DNS、TFTP、SNMP**
+
+- **DNS** : Domain Name Service (域名服务),默认端口 53
+- **TFTP**: Trivial File Transfer Protocol (简单文件传输协议),默认端口69
+- **SNMP**:Simple Network Management Protocol(简单网络管理协议),通过UDP端口161接收,只有Trap信息采用UDP端口162。
+
+## TCP的粘包和拆包
+
+TCP是面向流,没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一**个完整的包可能会被TCP拆分成多个包进行发送**,**也有可能把多个小的包封装成一个大的数据包发送**,这就是所谓的TCP粘包和拆包问题。
+
+**为什么会产生粘包和拆包呢?**
+
+- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;
+- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
+- 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;
+- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。
+
+**解决方案:**
+
+- 发送端将每个数据包封装为固定长度
+- 在数据尾部增加特殊字符进行分割
+- 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小。
+
+## 说说TCP是如何确保可靠性的呢?
+
+- TCP的连接是基于**三次握手**,而断开则是基于**四次挥手**。确保连接和断开的可靠性。
+- TCP的可靠性,还体现在**有状态**。TCP会记录哪些数据发送了,哪些数据被接收了,哪些没有被接受,并且保证数据包按序到达,保证数据传输不出差错。
+- 确认和重传机制:建立连接时三次握手同步双方的“序列号 + 确认号 + 窗口大小信息”,是确认重传、流控的基础。传输过程中,如果Checksum校验失败、丢包或延时,发送端重传
+- 流量控制:窗口和计时器的使用。TCP窗口中会指明双方能够发送接收的最大数据量
+- 拥塞控制
+
+## TCP的重传机制是什么?
+
+由于TCP的下层网络(网络层)可能出现丢失、重复或失序的情况,TCP协议提供可靠数据传输服务。为保证数据传输的正确性,TCP会重传其认为已丢失(包括报文中的比特错误)的包。TCP使用两套独立的机制来完成重传,一是基于时间,二是基于确认信息。
+
+TCP在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到发送数据的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。
+
+## 说下TCP的滑动窗口机制
+
+TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
+
+
+
+
+TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
+
+## 详细讲一下拥塞控制?
+
+防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
+
+
+
+**慢开始**
+
+把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。 为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。
+
+ 当 cwnd < ssthresh 时,使用慢开始算法。
+
+ 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
+
+ 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
+
+**拥塞避免**
+
+让拥塞窗口cwnd缓慢地增大,每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长。
+
+无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送 方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。
+
+**快重传**
+
+有时个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为1,因而降低了传输效率。
+
+快重传算法可以避免这个问题。快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认,使发送方及早知道有报文段没有到达对方。
+
+发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待重传计时器到期。由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。
+
+**快恢复**
+
+当发送方连续收到三个重复确认,就会把慢开始门限ssthresh减半,接着把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大。
+
+在采用快恢复算法时,慢开始算法只是在TCP连接建立时和网络出现超时时才使用。 采用这样的拥塞控制方法使得TCP的性能有明显的改进。
+
+## 什么是 SYN 攻击?
+
+我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到 一个 SYN 报文,就进入 SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的ACK 应答,久而久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。
+
+
+
+## 如何唯一确定一个TCP连接呢?
+
+TCP 四元组可以唯一的确定一个连接,四元组包括如下: 源地址 源端口 目的地址 目的端口。
+
+源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
+
+源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
+
+## 说说TCP KeepAlive 的基本原理?
+
+TCP 的连接,实际上是一种纯软件层面的概念,在物理层面并没有“连接”这种概念。TCP 通信双方建立交互的连接,但是并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会。在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启等各种意外,当这些意外发生之后,这些 TCP 连接并未来得及正常释放,在软件层面上,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用 TCP 的 KeepAlive 机制实现来实现。主流的操作系统基本都在内核里支持了这个特性。
+
+TCP KeepAlive 的基本原理是,隔一段时间给连接对端发送一个探测包,如果收到对方回应的 ACK,则认为连接还是存活的,在超过一定重试次数之后还是没有收到对方的回应,则丢弃该 TCP 连接。
+
+> 参考链接:https://hit-alibaba.github.io/interview/basic/network/TCP.html
diff --git "a/docs/computer-basic/\345\233\276\350\247\243HTTP.md" "b/docs/computer-basic/\345\233\276\350\247\243HTTP.md"
index 3a543c7..c9def51 100644
--- "a/docs/computer-basic/\345\233\276\350\247\243HTTP.md"
+++ "b/docs/computer-basic/\345\233\276\350\247\243HTTP.md"
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: HTTP常见知识点总结
+category: 计算机基础
+tag:
+ - TCP
+head:
+ - - meta
+ - name: keywords
+ content: HTTP面试题,HTTP
+ - - meta
+ - name: description
+ content: HTTP常见知识点总结,让天下没有难背的八股文!
---
## 简介
diff --git a/docs/database/es/1-es-architect.md b/docs/database/es/1-es-architect.md
index 22d95de..065404c 100644
--- a/docs/database/es/1-es-architect.md
+++ b/docs/database/es/1-es-architect.md
@@ -1,4 +1,19 @@
-## 面试题
+---
+sidebar: heading
+title: ES 的分布式架构原理
+category: 数据库
+tag:
+ - ES
+head:
+ - - meta
+ - name: keywords
+ content: es面试题,非关系型数据库,elastic search面试题,es,ES 的分布式架构原理
+ - - meta
+ - name: description
+ content: MongoDB常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+## ES 的分布式架构原理
ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?
@@ -50,4 +65,4 @@ ES 集群多个节点,会自动选举一个节点为 master 节点,这个 ma
-> 来源:https://github.com/doocs/advanced-java
\ No newline at end of file
+> 来源:https://github.com/doocs/advanced-java
diff --git a/docs/database/es/es-basic.md b/docs/database/es/es-basic.md
index 86c588c..96f2fd4 100644
--- a/docs/database/es/es-basic.md
+++ b/docs/database/es/es-basic.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: ES常见知识点总结
+category: 数据库
+tag:
+ - ES
+head:
+ - - meta
+ - name: keywords
+ content: ES常见知识点总结,非关系型数据库,elastic search面试题,es
+ - - meta
+ - name: description
+ content: ES常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
跟大家分享Elasticsearch的基础知识,它是做什么的以及它的使用和基本原理。
## 一、生活中的数据
@@ -656,4 +671,4 @@ JVM 调优建议如下:
- 确保堆内存最小值( Xms )与最大值( Xmx )的大小是相同的,防止程序在运行时改变堆内存大小。Elasticsearch 默认安装后设置的堆内存是 1GB。可通过` ../config/jvm.option` 文件进行配置,但是最好不要超过物理内存的50%和超过 32GB。
- GC 默认采用 CMS 的方式,并发但是有 STW 的问题,可以考虑使用 G1 收集器。
-- ES 非常依赖文件系统缓存(Filesystem Cache),快速搜索。一般来说,应该至少确保物理上有一半的可用内存分配到文件系统缓存。
\ No newline at end of file
+- ES 非常依赖文件系统缓存(Filesystem Cache),快速搜索。一般来说,应该至少确保物理上有一半的可用内存分配到文件系统缓存。
diff --git a/docs/database/mongodb.md b/docs/database/mongodb.md
new file mode 100644
index 0000000..78cab53
--- /dev/null
+++ b/docs/database/mongodb.md
@@ -0,0 +1,308 @@
+---
+sidebar: heading
+title: MongoDB常见面试题总结
+category: 数据库
+tag:
+ - MongoDB
+head:
+ - - meta
+ - name: keywords
+ content: MongoDB面试题,非关系型数据库,MongoDB特点,MongoDB使用场景,MongoDB优势,MongoDB
+ - - meta
+ - name: description
+ content: MongoDB常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+## mongodb是什么?
+
+MongoDB 是由 C++语言编写的,是一个基于分布式文件存储的开源数据库系统。 再高负载的情况下,添加更多的节点,可以保证服务器性能。 MongoDB 旨在给 WEB 应用提供可扩展的高性能数据存储解决方案。
+
+MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。 MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
+
+## mongodb有哪些特点?
+
+(1)MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
+
+(2)你可以在 MongoDB 记录中设置任何属性的索引 (如: FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。
+
+(3)你可以通过本地或者网络创建数据镜像,这使得 MongoDB 有更强的扩展性。
+
+(4)如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
+
+(5)Mongo 支持丰富的查询表达式。查询指令使用 JSON 形式的标记,可轻易查询文档中内嵌的对象及数组。
+
+(6)MongoDb 使用 update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
+
+(7)Mongodb 中的 Map/reduce 主要是用来对数据进行批量处理和聚合操作。
+
+(8)Map 和 Reduce。 Map 函数调用 emit(key,value)遍历集合中所有的记录,将 key 与 value 传给 Reduce 函数进行处理。
+
+(9)Map 函数和 Reduce 函数是使用 Javascript 编写的,并可以通过 db.runCommand 或 mapreduce 命令来执行 MapReduce 操作。
+
+(10)GridFS 是 MongoDB 中的一个内置功能,可以用于存放大量小文件。
+
+(11) MongoDB 允许在服务端执行脚本, 可以用 Javascript 编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
+
+## 什么是非关系型数据库
+
+非关系型数据库是对不同于传统关系型数据库的统称。非关系型数据库的显著特点是不使用SQL作为查询语言,数据存储不需要特定的表格模式。由于简单的设计和非常好的性能所以被用于大数据和Web Apps等
+
+## 为什么用MongoDB?
+
+- 架构简单
+- 没有复杂的连接
+- 深度查询能力,MongoDB支持动态查询。
+- 容易调试
+- 容易扩展
+- 不需要转化/映射应用对象到数据库对象
+- 使用内部内存作为存储工作区,以便更快的存取数据。
+
+## 在哪些场景使用MongoDB
+
+- 大数据
+- 内容管理系统
+- 移动端Apps
+- 数据管理
+
+## MySQL与MongoDB之间最基本的差别是什么?
+
+MySQL和MongoDB两者都是免费开源的数据库。MySQL和MongoDB有许多基本差别包括数据的表示(data representation),查询,关系,事务,schema的设计和定义,标准化(normalization),速度和性能。
+
+通过比较MySQL和MongoDB,实际上我们是在比较关系型和非关系型数据库,即数据存储结构不同。
+
+## MongoDB成为最好NoSQL数据库的原因是什么?
+
+以下特点使得MongoDB成为最好的NoSQL数据库:
+
+- 面向文件的
+- 高性能
+- 高可用性
+- 易扩展性
+- 丰富的查询语言
+
+## journal回放在条目(entry)不完整时(比如恰巧有一个中途故障了)会遇到问题吗?
+
+每个journal (group)的写操作都是一致的,除非它是完整的否则在恢复过程中它不会回放。
+
+## 分析器在MongoDB中的作用是什么?
+
+MongoDB中包括了一个可以显示数据库中每个操作性能特点的数据库分析器。通过这个分析器你可以找到比预期慢的查询(或写操作);利用这一信息,比如,可以确定是否需要添加索引。
+
+## 名字空间(namespace)是什么?
+
+MongoDB存储BSON对象在丛集(collection)中。数据库名字和丛集名字以句点连结起来叫做名字空间(namespace)。
+
+## 允许空值null吗?
+
+对于对象成员而言,是的。然而用户不能够添加空值(null)到数据库丛集(collection)因为空值不是对象。然而用户能够添加空对象{}。
+
+
+
+## 更新操作立刻fsync到磁盘?
+
+不会,磁盘写操作默认是延迟执行的。写操作可能在两三秒(默认在60秒内)后到达磁盘。例如,如果一秒内数据库收到一千个对一个对象递增的操作,仅刷新磁盘一次。(注意,尽管fsync选项在命令行和经过getLastError_old是有效的)
+
+## 如何执行事务/加锁?
+
+MongoDB没有使用传统的锁或者复杂的带回滚的事务,因为它设计的宗旨是轻量,快速以及可预计的高性能。可以把它类比成MySQLMylSAM的自动提交模式。通过精简对事务的支持,性能得到了提升,特别是在一个可能会穿过多个服务器的系统里。
+
+## 启用备份故障恢复需要多久?
+
+从备份数据库声明主数据库宕机到选出一个备份数据库作为新的主数据库将花费10到30秒时间。这期间在主数据库上的操作将会失败--包括
+
+写入和强一致性读取(strong consistent read)操作。然而,你还能在第二数据库上执行最终一致性查询(eventually consistent query)(在slaveOk模式下),即使在这段时间里。
+
+## 什么是master或primary?
+
+它是当前备份集群(replica set)中负责处理所有写入操作的主要节点/成员。在一个备份集群中,当失效备援(failover)事件发生时,一个另外的成员会变成primary。
+
+## 什么是secondary或slave?
+
+Seconday从当前的primary上复制相应的操作。它是通过跟踪复制oplog(local.oplog.rs)做到的。
+
+## 应该启动一个集群分片(sharded)还是一个非集群分片的 MongoDB 环境?
+
+为开发便捷起见,我们建议以非集群分片(unsharded)方式开始一个 MongoDB 环境,除非一台服务器不足以存放你的初始数据集。从非集群分片升级到集群分片(sharding)是无缝的,所以在你的数据集还不是很大的时候没必要考虑集群分片(sharding)。
+
+## 分片(sharding)和复制(replication)是怎样工作的?
+
+每一个分片(shard)是一个分区数据的逻辑集合。分片可能由单一服务器或者集群组成,我们推荐为每一个分片(shard)使用集群。
+
+## 数据在什么时候才会扩展到多个分片(shard)里?
+
+MongoDB 分片是基于区域(range)的。所以一个集合(collection)中的所有的对象都被存放到一个块(chunk)中。只有当存在多余一个块的时后,才会有多个分片获取数据的选项。现在,每个默认块的大小是 64Mb,所以你需要至少 64 Mb 空间才可以实施一个迁移。
+
+## 如果在一个分片(shard)停止或者很慢的时候,发起一个查询会怎样?
+
+如果一个分片(shard)停止了,除非查询设置了“Partial”选项,否则查询会返回一个错误。如果一个分片(shard)响应很慢,MongoDB则会等待它的响应。
+
+## 当更新一个正在被迁移的块(Chunk)上的文档时会发生什么?
+
+更新操作会立即发生在旧的块(Chunk)上,然后更改才会在所有权转移前复制到新的分片上。
+
+## MongoDB在A:{B,C}上建立索引,查询A:{B,C}和A:{C,B}都会使用索引吗?
+
+不会,只会在A:{B,C}上使用索引。
+
+## 如果一个分片(Shard)停止或很慢的时候,发起一个查询会怎样?
+
+如果一个分片停止了,除非查询设置了“Partial”选项,否则查询会返回一个错误。如果一个分片响应很慢,MongoDB会等待它的响应。
+
+## MongoDB支持存储过程吗?如果支持的话,怎么用?
+
+MongoDB支持存储过程,它是javascript写的,保存在db.system.js表中。
+
+## 如何理解MongoDB中的GridFS机制,MongoDB为何使用GridFS来存储文件?
+
+GridFS是一种将大型文件存储在MongoDB中的文件规范。使用GridFS可以将大文件分隔成多个小文档存放,这样我们能够有效的保存大文档,而且解决了BSON对象有限制的问题。
+
+## mongodb的数据结构
+
+数据库中存储的对象设计bson,一种类似json的二进制文件,由键值对组成。
+
+## MongoDB的优势有哪些
+
+- 面向文档的存储:以 JSON 格式的文档保存数据。
+- 任何属性都可以建立索引。
+- 复制以及高可扩展性。
+- 自动分片。
+- 丰富的查询功能。
+- 快速的即时更新。
+- 来自 MongoDB 的专业支持。
+
+## 什么是集合
+
+集合就是一组 MongoDB 文档。它相当于关系型数据库(RDBMS)中的表这种概念。集合位于单独的一个数据库中。一个集合内的多个文档可以有多个不同的字段。一般来说,集合中的文档都有着相同或相关的目的。
+
+## 什么是文档
+
+文档由一组key value组成。文档是动态模式,这意味着同一集合里的文档不需要有相同的字段和结构。在关系型数据库中table中的每一条记录相当于MongoDB中的一个文档。
+
+## 什么是”mongod“
+
+mongod是处理MongoDB系统的主要进程。它处理数据请求,管理数据存储,和执行后台管理操作。当我们运行mongod命令意味着正在启动MongoDB进程,并且在后台运行。
+
+## "mongod"参数有什么
+
+- 传递数据库存储路径,默认是"/data/db"
+- 端口号 默认是 "27017"
+
+## 什么是"mongo"
+
+它是一个命令行工具用于连接一个特定的mongod实例。当我们没有带参数运行mongo命令它将使用默认的端口号和localhost连接
+
+## MongoDB哪个命令可以切换数据库
+
+MongoDB 用 use +数据库名称的方式来创建数据库。 use 会创建一个新的数据库,如果该数据库存在,则返回这个数据库。
+
+## MongoDB中的命名空间是什么意思?
+
+MongoDB内部有预分配空间的机制,每个预分配的文件都用0进行填充。
+
+数据文件每新分配一次,它的大小都是上一个数据文件大小的2倍,每个数据文件最大2G。
+
+MongoDB每个集合和每个索引都对应一个命名空间,这些命名空间的元数据集中在16M的*.ns文件中,平均每个命名占用约 628 字节,也即整个数据库的命名空间的上限约为24000。
+
+如果每个集合有一个索引(比如默认的_id索引),那么最多可以创建12000个集合。如果索引数更多,则可创建的集合数就更少了。同时,如果集合数太多,一些操作也会变慢。
+
+要建立更多的集合的话,MongoDB 也是支持的,只需要在启动时加上“--nssize”参数,这样对应数据库的命名空间文件就可以变得更大以便保存更多的命名。这个命名空间文件(.ns文件)最大可以为 2G。
+
+每个命名空间对应的盘区不一定是连续的。与数据文件增长相同,每个命名空间对应的盘区大小都是随分配次数不断增长的。目的是为了平衡命名空间浪费的空间与保持一个命名空间数据的连续性。
+
+需要注意的一个命名空间$freelist,这个命名空间用于记录不再使用的盘区(被删除的Collection或索引)。每当命名空间需要分配新盘区时,会先查看$freelist是否有大小合适的盘区可以使用,如果有就回收空闲的磁盘空间。
+
+## 在MongoDB中如何创建一个新的数据库
+
+MongoDB 用 use + 数据库名称 的方式来创建数据库。 use 会创建一个新的数据库,如果该数据库存在,则返回这个数据库。
+
+## MongoDB中的分片是什么意思
+
+分片是将数据水平切分到不同的物理节点。当应用数据越来越大的时候,数据量也会越来越大。当数据量增长时,单台机器有可能无法存储数据或可接受的读取写入吞吐量。利用分片技术可以添加更多的机器来应对数据量增加以及读写操作的要求。
+
+## 什么是复制
+
+复制是将数据同步到多个服务器的过程,通过多个数据副本存储到多个服务器上增加数据可用性。复制可以保障数据的安全性,灾难恢复,无需停机维护(如备份,重建索引,压缩),分布式读取数据。
+
+## 在MongoDB中如何在集合中插入一个文档
+
+要想将数据插入 MongoDB 集合中,需要使用 insert() 或 save() 方法。
+
+```stylus
+>db.collectionName.insert({"key":"value"})
+>db.collectionName.save({"key":"value"})
+```
+
+## 为什么要在MongoDB中使用分析器
+
+数据库分析工具(Database Profiler)会针对正在运行的mongod实例收集数据库命令执行的相关信息。包括增删改查的命令以及配置和管理命令。分析器(profiler)会写入所有收集的数据到 system.profile集合,一个capped集合在管理员数据库。分析器默认是关闭的你能通过per数据库或per实例开启。
+
+## MongoDB支持主键外键关系吗
+
+默认MongoDB不支持主键和外键关系。 用Mongodb本身的API需要硬编码才能实现外键关联,不够直观且难度较大。
+
+## MongoDB支持哪些数据类型
+
+String、Integer、Double、Boolean、Object、Object ID、Arrays、Min/Max Keys、Datetime、Code、Regular Expression等
+
+## 86、"ObjectID"由哪些部分组成
+
+一共有四部分组成:时间戳、客户端ID、客户进程ID、三个字节的增量计数器
+
+_id是一个 12 字节长的十六进制数,它保证了每一个文档的唯一性。在插入文档时,需要提供 _id 。如果你不提供,那么 MongoDB 就会为每一文档提供一个唯一的 id。 _id 的头 4 个字节代表的是当前的时间戳,接着的后 3 个字节表示的是机器 id 号,接着的 2 个字节表示MongoDB 服务器进程 id,最后的 3 个字节代表递增值。
+
+## MongoDb索引
+
+索引用于高效的执行查询。没有索引MongoDB将扫描查询整个集合中的所有文档这种扫描效率很低,需要处理大量数据。索引是一种特殊的数据结构,将一小块数据集保存为容易遍历的形式。索引能够存储某种特殊字段或字段集的值,并按照索引指定的方式将字段值进行排序。
+
+## 如何添加索引
+
+使用 db.collection.createIndex() 在集合中创建一个索引
+
+```reasonml
+>db.collectionName.createIndex({columnName:1})
+```
+
+## 在MongoDB中如何更新数据
+
+update() 与 save() 方法都能用于更新集合中的文档。 update() 方法更新已有文档中的值,而 save() 方法则是用传入该方法的文档来替换已有文档。
+
+## 如何删除文档
+
+MongoDB 利用 remove() 方法 清除集合中的文档。它有 2 个可选参数:
+
+- deletion criteria:(可选)删除文档的标准。
+- justOne:(可选)如果设为 true 或 1,则只删除一个文档。
+
+```maxima
+>db.collectionName.remove({key:value})
+```
+
+## 在MongoDB中如何排序
+
+MongoDB 中的文档排序是通过 sort() 方法来实现的。 sort() 方法可以通过一些参数来指定要进行排序的字段,并使用 1 和 -1 来指定排
+
+序方式,其中 1 表示升序,而 -1 表示降序。
+
+```stylus
+>db.connectionName.find({key:value}).sort({columnName:1})
+```
+
+## 什么是聚合
+
+聚合操作能够处理数据记录并返回计算结果。聚合操作能将多个文档中的值组合起来,对成组数据执行各种操作,返回单一的结果。它相当于 SQL 中的 count(*) 组合 group by。对于 MongoDB 中的聚合操作,应该使用 aggregate() 方法。
+
+```stylus
+>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
+```
+
+## 在MongoDB中什么是副本集
+
+在MongoDB中副本集由一组MongoDB实例组成,包括一个主节点多个次节点,MongoDB客户端的所有数据都写入主节点(Primary),副节点从主节点同步写入数据,以保持所有复制集内存储相同的数据,提高数据可用性。
+
diff --git a/docs/database/mysql-lock.md b/docs/database/mysql-lock.md
new file mode 100644
index 0000000..230db1b
--- /dev/null
+++ b/docs/database/mysql-lock.md
@@ -0,0 +1,184 @@
+---
+sidebar: heading
+title: MySQL锁相关面试题
+category: 数据库
+tag:
+ - MySQL
+head:
+ - - meta
+ - name: keywords
+ content: MySQL面试题,MySQL锁面试题,MySQL锁,意向锁,全局锁,排他锁,乐观锁,悲观锁
+ - - meta
+ - name: description
+ content: MySQL锁常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+# MySQL锁相关面试题
+
+## 为什么需要加锁
+
+如果有多个并发请求存取数据,在数据就可能会产生多个事务同时操作同一行数据。如果并发操作不加控制,不加锁的话,就可能写入了不正确的数据,或者导致读取了不正确的数据,破坏了数据的一致性。因此需要考虑加锁。
+
+## 表级锁和行级锁有什么区别?
+
+MyISAM 仅仅支持表级锁,一锁就锁整张表,这在并发写的情况下性非常差。
+
+InnoDB 不光支持表级锁,还支持行级锁,默认为行级锁。行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。
+
+**表级锁和行级锁对比** :
+
+- **表级锁:** MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
+- **行级锁:** MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
+
+## 共享锁和排他锁有什么区别?
+
+不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:
+
+- **共享锁(S 锁)** :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
+- **排他锁(X 锁)** :又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。
+
+排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。
+
+| | S 锁 | X 锁 |
+| ---- | ------ | ---- |
+| S 锁 | 不冲突 | 冲突 |
+| X 锁 | 冲突 | 冲突 |
+
+由于 MVCC 的存在,对于一般的 `SELECT` 语句,InnoDB 不会加任何锁。不过, 你可以通过以下语句显式加共享锁或排他锁。
+
+```sql
+# 共享锁
+SELECT ... LOCK IN SHARE MODE;
+# 排他锁
+SELECT ... FOR UPDATE;
+```
+
+## 意向锁有什么作用?
+
+如果需要用到表锁的话,如何判断表中的记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。
+
+意向锁是表级锁,共有两种:
+
+- **意向共享锁(Intention Shared Lock,IS 锁)**:事务有意向对表中的某些加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
+- **意向排他锁(Intention Exclusive Lock,IX 锁)**:事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
+
+意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
+
+意向锁之间是互相兼容的。
+
+| | IS 锁 | IX 锁 |
+| ----- | ----- | ----- |
+| IS 锁 | 兼容 | 兼容 |
+| IX 锁 | 兼容 | 兼容 |
+
+意向锁和共享锁和排它锁互斥(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的共享锁和排他锁互斥)。
+
+| | IS 锁 | IX 锁 |
+| ---- | ----- | ----- |
+| S 锁 | 兼容 | 互斥 |
+| X 锁 | 互斥 | 互斥 |
+
+## InnoDB 有哪几类行锁?
+
+**按锁粒度分类**,有行级锁、表级锁和页级锁。
+
+1. 行级锁是mysql中锁定粒度最细的一种锁。表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大。行级锁的类型主要有三类:
+ - Record Lock,记录锁,也就是仅仅把一条记录锁上;
+ - Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
+ - Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
+2. 表级锁是mysql中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分mysql引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。
+3. 页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折衷的页级锁,一次锁定相邻的一组记录。
+
+**按锁级别分类**,有共享锁、排他锁和意向锁。
+
+1. 共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
+2. 排他锁又称写锁、独占锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
+3. 意向锁是表级锁,其设计目的主要是为了在一个事务中揭示下一行将要被请求锁的类型。InnoDB 中的两个表锁:
+
+意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁;
+
+意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
+
+意向锁是 InnoDB 自动加的,不需要用户干预。
+
+对于INSERT、UPDATE和DELETE,InnoDB 会自动给涉及的数据加排他锁;对于一般的SELECT语句,InnoDB 不会加任何锁,事务可以通过以下语句显式加共享锁或排他锁。
+
+共享锁:`SELECT … LOCK IN SHARE MODE;`
+
+排他锁:`SELECT … FOR UPDATE;`
+
+## 什么是死锁?如何防止死锁?
+
+**什么是死锁?**
+
+死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。
+
+**如何防止死锁?**
+
+- 尽量约定固定的顺序访问表,因为交叉访问更容易造成事务等待回路。
+- 尽量避免大事务,建议拆成多个小事务。因为大事务占用的锁资源越多,越容易出现死锁。
+- 降低数据库隔离级别,比如RR降低为RC,因为RR隔离级别,存在GAP锁,死锁概率大很多。
+- 死锁与索引是密不可分的,合理优化你的索引,死锁概率降低。
+- 如果业务处理不好可以用分布式事务锁或者使用乐观锁
+
+## 如何处理死锁?
+
+通过innodblockwait_timeout来设置超时时间,一直等待直到超时。
+
+发起死锁检测,发现死锁之后,主动回滚死锁中的事务,不需要其他事务继续。
+
+## 什么是全局锁?它的应用场景有哪些?
+
+全局锁就是对整个数据库实例加锁,它的典型使用场景就是做全库逻辑备份,这个命令可以使用整个库处于只读状态,使用该命令之后,数据更新语句,数据定义语句,更新类事务的提交语句等操作都会被阻塞。
+
+## 使用全局锁会导致的问题?
+
+如果在主库备份,在备份期间不能更新,业务停止,所以更新业务会处于等待状态。
+
+如果在从库备份,在备份期间不能执行主库同步的binlog,导致主从延迟。
+
+## 乐观锁和悲观锁是什么?
+
+数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。
+
+* 悲观锁:假定会发生并发冲突,会对操作的数据进行加锁,直到提交事务,才会释放锁,其他事务才能进行修改。实现方式:使用数据库中的锁机制。
+* 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否数据是否被修改过。给表增加`version`字段,在修改提交之前检查`version`与原来取到的`version`值是否相等,若相等,表示数据没有被修改,可以更新,否则,数据为脏数据,不能更新。实现方式:乐观锁一般使用版本号机制或`CAS`算法实现。
+
+## select for update加的是表锁还是行锁
+
+需要情况讨论:RR和RC隔离级别,还有查询条件(唯一索引、主键、一般索引、无索引)
+
+**在RC隔离级别下**
+
+- 如果查询条件是唯一索引,会加`IX`意向排他锁(表级别的锁,不影响插入)、两把`X`排他锁(行锁,分别对应唯一索引,主键索引)
+- 如果查询条件是主键,会加`IX`意向排他锁(表级别的锁,不影响插入)、一把对应主键的`X`排他锁(行锁,会锁住主键索引那一行)。
+- 如果查询条件是普通索引,**如果查询命中记录**,会加`IX`意向排他锁(表锁)、两把`X`排他锁(行锁,分别对应普通索引的`X`锁,对应主键的`X`锁);**如果没有命中数据库表的记录**,只加了一把`IX`意向排他锁(表锁,不影响插入)
+- 如果查询条件是无索引,会加两把锁,IX意向排他锁(表锁)、一把X排他锁(行锁,对应主键的X锁)。
+
+> 查询条件是无索引,为什么不锁表呢? MySQL会走聚簇(主键)索引进行全表扫描过滤。每条记录都会加上X锁。但是,为了效率考虑,MySQL在这方面进行了改进,在扫描过程中,若记录不满足过滤条件,会进行解锁操作。同时优化违背了2PL原则。
+
+**在RR隔离级别**
+
+- 如果查询条件是唯一索引,命中数据库表记录时,一共会加三把锁:一把IX意向排他锁 (表锁,不影响插入),一把对应主键的X排他锁(行锁),一把对应唯一索引的X排他锁 (行锁)。
+- 如果查询条件是主键,会加`IX`意向排他锁(表级别的锁,不影响插入)、一把对应主键的`X`排他锁(行锁,会锁住主键索引那一行)。
+- 如果查询条件是普通索引,命中查询记录的话,除了会加X锁(行锁),IX锁(表锁,不影响插入),还会加Gap 锁(间隙锁,会影响插入)。
+- 如果查询条件是无索引,会加一个IX锁(表锁,不影响插入),每一行实际记录行的X锁,还有对应于supremum pseudo-record的虚拟全表行锁。这种场景,通俗点讲,其实就是锁表了。
+
+> 参考链接:https://juejin.cn/post/7199666255884009532
+
+## 优化锁方面有什么建议?
+
+- 尽量使用较低的隔离级别。
+
+- 精心设计索引, 并尽量使用索引访问数据, 使加锁更精确, 从而减少锁冲突的机会。
+
+- 选择合理的事务大小,小事务发生锁冲突的几率也更小。
+
+- 给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。
+
+- 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会。
+
+- 不要申请超过实际需要的锁级别。
+
+- 除非必须,查询时不要显示加锁。 MySQL 的 MVCC 可以实现事务中的查询不用加锁,优化事务性能;
+
diff --git a/docs/database/mysql.md b/docs/database/mysql.md
index b148336..c8b0032 100644
--- a/docs/database/mysql.md
+++ b/docs/database/mysql.md
@@ -1,8 +1,34 @@
---
sidebar: heading
+title: MySQL常见面试题总结
+category: 数据库
+tag:
+ - MySQL
+head:
+ - - meta
+ - name: keywords
+ content: MySQL面试题,事务特性,事务隔离级别,MySQL编码和字符集,MySQL索引,索引分类,最左匹配原则,聚集索引,覆盖索引,索引失效,索引下推,存储引擎,MVCC原理,MySQL日志,MySQL分区,MySQL主从同步,MySQL深分页,MySQL慢查询,分库分表,乐观锁和悲观锁
+ - - meta
+ - name: description
+ content: MySQL常见知识点和面试题总结,让天下没有难背的八股文!
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+## 更新记录
+
+- 2024.06.05,更新[MySQL查询 limit 1000,10 和limit 10 速度一样快吗?](###MySQL查询 limit 1000,10 和limit 10 速度一样快吗?)
+
+- 2024.5.15,新增[B树和B+树的区别?](###B树和B+树的区别?)
+
+## 什么是MySQL
+
+MySQL是一个关系型数据库,它采用表的形式来存储数据。你可以理解成是Excel表格,既然是表的形式存储数据,就有表结构(行和列)。行代表每一行数据,列代表该行中的每个值。列上的值是有数据类型的,比如:整数、字符串、日期等等。
## 事务的四大特性?
@@ -160,7 +186,9 @@ utf8 就像是阉割版的utf8mb4,只支持部分字符。比如`emoji`表情
### 什么是索引?
-索引是存储引擎用于提高数据库表的访问速度的一种**数据结构**。
+索引是存储引擎用于提高数据库表的访问速度的一种**数据结构**。它可以比作一本字典的目录,可以帮你快速找到对应的记录。
+
+索引一般存储在磁盘的文件中,它是占用物理空间的。
### 索引的优缺点?
@@ -240,6 +268,18 @@ Index_comment:
- 哈希索引**不支持模糊查询**及多列索引的最左前缀匹配。
- 因为哈希表中会**存在哈希冲突**,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。
+### B树和B+树的区别?
+
+
+
+
+
+1. **数据存储方式**:在B树中,每个节点都包含键和对应的值,叶子节点存储了实际的数据记录;而B+树中,仅仅只有叶子节点存储了实际的数据记录,非叶子节点只包含键信息和指向子节点的指针。
+2. **数据检索方式**:在B树中,由于非叶子节点也存储了数据,所以查询时可以直接在非叶子节点找到对应的数据,具有更短的查询路径;
+ 而B+树的所有数据都存储在叶子节点上,只有通过子节点才能获取到完整的数据。
+3. **范围查询效率**:由于B+树的所有数据都存储在叶子节点上,并且叶子节点之间使用链表连接,所以范围查询的效率比较高。而在B树中,范围查询需要通过遍历多个层级的节点,效率相对较低。
+4. **适用场景**:B树适合进行随机读写操作,因为每个节点都包含了数据。而B+树适合进行范围查询和顺序访问,因为数据都存储在叶子节点上,并且叶子节点之间使用链表连接,便于进行顺序遍历。
+
### 为什么B+树比B树更适合实现数据库索引?
- 由于B+树的数据都存储在叶子结点中,叶子结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,而在数据库中基于范围的查询是非常频繁的,所以通常B+树用于数据库索引。
@@ -885,7 +925,7 @@ select * from xxx where id >=(select id from xxx order by id limit 500000, 1) o
在拿到了上面的id之后,假设这个id正好等于500000,那sql就变成了
-```
+```mysql
select * from xxx where id >=500000 order by id limit 10;
```
@@ -895,12 +935,10 @@ select * from xxx where id >=500000 order by id limit 10;
将所有的数据**根据id主键进行排序**,然后分批次取,将当前批次的最大id作为下次筛选的条件进行查询。
-```
+```mysql
select * from xxx where id > start_id order by id limit 10;
```
-mysql
-
通过主键索引,每次定位到start_id的位置,然后往后遍历10个数据,这样不管数据多大,查询性能都较为稳定。
## 高度为3的B+树,可以存放多少数据?
@@ -954,12 +992,15 @@ B+树中**非叶子节点存的是key + 指针**;**叶子节点存的是数据
当MySQL单表记录数过大时,数据库的性能会明显下降,一些常见的优化措施如下:
* 合理建立索引。在合适的字段上建立索引,例如在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
+* 索引优化,SQL优化。索引要符合最左匹配原则等,参考:https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95
* 建立分区。对关键字段建立水平分区,比如时间字段,若查询条件往往通过时间范围来进行查询,能提升不少性能
* 利用缓存。利用Redis等缓存热点数据,提高查询效率
* 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内
* 读写分离。经典的数据库拆分方案,主库负责写,从库负责读
* 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分
-
+* 数据异构到es
+* 冷热数据分离。几个月之前不常用的数据放到冷库中,最新的数据比较新的数据放到热库中
+* 升级数据库类型,换一种能兼容MySQL的数据库(OceanBase、TiDB等)
## 说说count(1)、count(*)和count(字段名)的区别
嗯,先说说count(1) and count(字段名)的区别。
@@ -1116,6 +1157,79 @@ canal的原理如下:
3. **管理困难**。存储过程的目录是扁平的,而不是文件系统那样的树形结构,脚本少的时候还好办,一旦多起来,目录就会陷入混乱。
4. 存储过程是**只优化一次**,有的时候随着数据量的增加或者数据结构的变化,原来存储过程选择的执行计划也许并不是最优的了,所以这个时候需要手动干预或者重新编译了。
+## MySQL update 是锁行还是锁表?
+
+首先,InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
+
+1. 当执行update语句时,where中的过滤条件列,如果用到索引,就是锁行;如果无法用索引,就是锁表。
+2. 如果两个update语句同时执行,第一个先执行触发行锁,但是第二个没有索引触发表锁,因为有个行锁住了,所以还是会等待行锁释放,才能锁表。
+3. 当执行insert或者delete语句时,锁行。
+
+## select...for update会锁表还是锁行?
+
+如果查询条件用了索引/主键,那么`select ... for update`就会加行锁。
+
+如果是普通字段(没有索引/主键),那么`select ..... for update`就会加表锁。
+
+## MySQL的binlog有几种格式?分别有什么区别?
+
+有三种格式,statement,row和mixed。
+
+- statement:每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
+- row:不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大。
+- mixed:一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。
+
+## 阿里手册为什么禁止使用 count(列名)或 count(常量)来替代 count(*)
+
+先看下这几种方式的区别。
+
+count(主键id):InnoDB引擎会遍历整张表,把每一行id值都取出来,返给server层。server层拿到id后,判断是不可能为空的,就按行累加,不再对每个值进行NULL判断。
+
+count(常量):InnoDB引擎会遍历整张表,但不取值。server层对于返回的每一行,放一个常量进去,判断是不可能为空的,按行累加,不再对每个值进行NULL判断。count(常量)比count(主键id)执行的要快,因为从引擎放回id会涉及解析数据行,以及拷贝字段值的操作。
+
+count(字段):全表扫描,分情况讨论。
+
+1、如果参数字段定义NOT NULL,判断是不可能为空的,按行累加,不再对每个值进行NULL判断。
+2、如果参数字段定义允许为NULL,那么执行的时候,判断可能是NULL,还要把值取出来再判断一下,不是NULL才累加。
+
+count(*):统计所有的列,相当于行数,统计结果中会包含字段值为null的列;
+
+COUNT(`*`)是SQL92定义的标准统计行数的语法,效率高,MySQL对它进行了很多优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来降低成本。
+
+所以,建议使用COUNT(\*)查询表的行数!
+
+## 存储MD5值应该用VARCHAR还是用CHAR?
+
+首先说说CHAR和VARCHAR的区别:
+
+1、存储长度:
+
+CHAR类型的长度是固定的
+
+当我们当定义CHAR(10),输入的值是"abc",但是它占用的空间一样是10个字节,会包含7个空字节。当输入的字符长度超过指定的数时,CHAR会截取超出的字符。而且,当存储为CHAR的时候,MySQL会自动删除输入字符串末尾的空格。
+
+VARCHAR的长度是可变的
+
+比如VARCHAR(10),然后输入abc三个字符,那么实际存储大小为3个字节。
+
+除此之外,VARCHAR还会保留1个或2个额外的字节来记录字符串的实际长度。如果定义的最大长度小于等于255个字节,那么,就会预留1个字节;如果定义的最大长度大于255个字节,那么就会预留2个字节。
+
+2、存储效率
+
+CHAR类型每次修改后的数据长度不变,效率更高。
+
+VARCHAR每次修改的数据要更新数据长度,效率更低。
+
+3、存储空间
+
+CHAR存储空间是初始的预计长度字符串再加上一个记录字符串长度的字节,可能会存在多余的空间。
+
+VARCHAR存储空间的时候是实际字符串再加上一个记录字符串长度的字节,占用空间较小。
+
+
+
+根据以上的分析,由于MD5是一个定长的值,所以MD5值适合使用CHAR存储。对于固定长度的非常短的列,CHAR比VARCHAR效率也更高。
+

diff --git a/docs/database/sharding-id.md b/docs/database/sharding-id.md
new file mode 100644
index 0000000..2b4c95d
--- /dev/null
+++ b/docs/database/sharding-id.md
@@ -0,0 +1,169 @@
+## 分库分表之后,ID主键该如何处理?
+
+其实这是分库分表之后你必然要面对的一个问题,就是 id 怎么生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那就不对了,需要一个**全局唯一**的 id 来支持。所以这都是实际生产环境中必须考虑的问题。
+
+### 基于数据库的实现方案
+
+#### 数据库自增 id
+
+这个就是说你的系统里每次得到一个 id,都是往一个库的一个表里插入一条没什么业务含义的数据,然后获取一个数据库自增的一个 id。拿到这个 id 之后再往对应的分库分表里去写入。
+
+这个方案的好处就是方便简单,谁都会用;**缺点就是单库生成**自增 id,要是高并发的话,就会有瓶颈的;如果你硬是要改进一下,那么就专门开一个服务出来,这个服务每次就拿到当前 id 最大值,然后自己递增几个 id,一次性返回一批 id,然后再把当前最大 id 值修改成递增几个 id 之后的一个值;但是**无论如何都是基于单个数据库**。
+
+**适合的场景**:分库分表一般就俩原因,要不就是单库并发太高,要不就是单库数据量太大;除非是你**并发不高,但是数据量太大**导致的分库分表扩容,你可以用这个方案,因为可能每秒最高并发最多就几百,那么就走单独的一个库和表生成自增主键即可。
+
+#### 设置数据库 sequence 或者表自增字段步长
+
+可以通过设置数据库 sequence 或者表的自增字段步长来进行水平伸缩。
+
+比如说,现在有 8 个服务节点,每个服务节点使用一个 sequence 功能来产生 ID,每个 sequence 的起始 ID 不同,并且依次递增,步长都是 8。
+
+
+
+**适合的场景**:在用户防止产生的 ID 重复时,这种方案实现起来比较简单,也能达到性能目标。但是服务节点固定,步长也固定,将来如果还要增加服务节点,就不好处理了。
+
+### UUID
+
+好处就是本地生成,不要基于数据库来了;不好之处就是,UUID 太长、占用空间大,**作为主键性能太差**了;更重要的是,UUID 不具有有序性,会导致 B+ 树索引在写的时候有过多的随机写操作(连续的 ID 可以产生部分顺序写),还有,由于在写的时候不能产生有顺序的 append 操作,而需要进行 insert 操作,将会读取整个 B+ 树节点到内存,在插入这条记录后会将整个节点写回磁盘,这种操作在记录占用空间比较大的情况下,性能下降明显。
+
+适合的场景:如果你是要随机生成个什么文件名、编号之类的,你可以用 UUID,但是作为主键是不能用 UUID 的。
+
+```java
+UUID.randomUUID().toString().replace("-", "") -> sfsdf23423rr234sfdafCopy to clipboardErrorCopied
+```
+
+### 获取系统当前时间
+
+这个就是获取当前时间即可,但是问题是,**并发很高的时候**,比如一秒并发几千,**会有重复的情况**,这个是肯定不合适的。基本就不用考虑了。
+
+适合的场景:一般如果用这个方案,是将当前时间跟很多其他的业务字段拼接起来,作为一个 id,如果业务上你觉得可以接受,那么也是可以的。你可以将别的业务字段值跟当前时间拼接起来,组成一个全局唯一的编号。
+
+### snowflake 算法
+
+snowflake 算法是 twitter 开源的分布式 id 生成算法,采用 Scala 语言实现,是把一个 64 位的 long 型的 id,1 个 bit 是不用的,用其中的 41 bits 作为毫秒数,用 10 bits 作为工作机器 id,12 bits 作为序列号。
+
+- 1 bit:不用,为啥呢?因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。
+- 41 bits:表示的是时间戳,单位是毫秒。41 bits 可以表示的数字多达 `2^41 - 1` ,也就是可以标识 `2^41 - 1` 个毫秒值,换算成年就是表示 69 年的时间。
+- 10 bits:记录工作机器 id,代表的是这个服务最多可以部署在 2^10 台机器上,也就是 1024 台机器。但是 10 bits 里 5 个 bits 代表机房 id,5 个 bits 代表机器 id。意思就是最多代表 `2^5` 个机房(32 个机房),每个机房里可以代表 `2^5` 个机器(32 台机器)。
+- 12 bits:这个是用来记录同一个毫秒内产生的不同 id,12 bits 可以代表的最大正整数是 `2^12 - 1 = 4096` ,也就是说可以用这个 12 bits 代表的数字来区分**同一个毫秒内**的 4096 个不同的 id。
+
+```java
+public class IdWorker {
+
+ private long workerId;
+ private long datacenterId;
+ private long sequence;
+
+ public IdWorker(long workerId, long datacenterId, long sequence) {
+ // sanity check for workerId
+ // 这儿不就检查了一下,要求就是你传递进来的机房id和机器id不能超过32,不能小于0
+ if (workerId > maxWorkerId || workerId < 0) {
+ throw new IllegalArgumentException(
+ String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
+ }
+ if (datacenterId > maxDatacenterId || datacenterId < 0) {
+ throw new IllegalArgumentException(
+ String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
+ }
+ System.out.printf(
+ "worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
+ timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
+
+ this.workerId = workerId;
+ this.datacenterId = datacenterId;
+ this.sequence = sequence;
+ }
+
+ private long twepoch = 1288834974657L;
+
+ private long workerIdBits = 5L;
+ private long datacenterIdBits = 5L;
+
+ // 这个是二进制运算,就是 5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
+ private long maxWorkerId = -1L ^ (-1L << workerIdBits);
+
+ // 这个是一个意思,就是 5 bit最多只能有31个数字,机房id最多只能是32以内
+ private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
+ private long sequenceBits = 12L;
+
+ private long workerIdShift = sequenceBits;
+ private long datacenterIdShift = sequenceBits + workerIdBits;
+ private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
+ private long sequenceMask = -1L ^ (-1L << sequenceBits);
+
+ private long lastTimestamp = -1L;
+
+ public long getWorkerId() {
+ return workerId;
+ }
+
+ public long getDatacenterId() {
+ return datacenterId;
+ }
+
+ public long getTimestamp() {
+ return System.currentTimeMillis();
+ }
+
+ public synchronized long nextId() {
+ // 这儿就是获取当前时间戳,单位是毫秒
+ long timestamp = timeGen();
+
+ if (timestamp < lastTimestamp) {
+ System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
+ throw new RuntimeException(String.format(
+ "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+ }
+
+ if (lastTimestamp == timestamp) {
+ // 这个意思是说一个毫秒内最多只能有4096个数字
+ // 无论你传递多少进来,这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
+ sequence = (sequence + 1) & sequenceMask;
+ if (sequence == 0) {
+ timestamp = tilNextMillis(lastTimestamp);
+ }
+ } else {
+ sequence = 0;
+ }
+
+ // 这儿记录一下最近一次生成id的时间戳,单位是毫秒
+ lastTimestamp = timestamp;
+
+ // 这儿就是将时间戳左移,放到 41 bit那儿;
+ // 将机房 id左移放到 5 bit那儿;
+ // 将机器id左移放到5 bit那儿;将序号放最后12 bit;
+ // 最后拼接起来成一个 64 bit的二进制数字,转换成 10 进制就是个 long 型
+ return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
+ | (workerId << workerIdShift) | sequence;
+ }
+
+ private long tilNextMillis(long lastTimestamp) {
+ long timestamp = timeGen();
+ while (timestamp <= lastTimestamp) {
+ timestamp = timeGen();
+ }
+ return timestamp;
+ }
+
+ private long timeGen() {
+ return System.currentTimeMillis();
+ }
+
+ // ---------------测试---------------
+ public static void main(String[] args) {
+ IdWorker worker = new IdWorker(1, 1, 1);
+ for (int i = 0; i < 30; i++) {
+ System.out.println(worker.nextId());
+ }
+ }
+
+}
+```
+
+就是说 41 bit 是当前毫秒单位的一个时间戳;然后 5 bit 是你传递进来的一个**机房** id(但是最大只能是 32 以内),另外 5 bit 是你传递进来的**机器** id(但是最大只能是 32 以内),剩下的那个 12 bit 序列号,就是如果跟你上次生成 id 的时间还在一个毫秒内,那么会把顺序给你累加,最多在 4096 个序号以内。
+
+所以利用这个工具类,搞一个服务,然后对每个机房的每个机器都初始化这么一个东西,刚开始这个机房的这个机器的序号就是 0。然后每次接收到一个请求,说这个机房的这个机器要生成一个 id,你就找到对应的 Worker 生成。
+
+利用这个 snowflake 算法,你可以开发自己公司的服务,甚至对于机房 id 和机器 id,反正给你预留了 5 bit + 5 bit,你换成别的有业务含义的东西也可以的。
+
+这个 snowflake 算法相对来说还是比较靠谱的,所以你要真是搞分布式 id 生成,如果是高并发啥的,那么用这个应该性能比较好,一般每秒几万并发的场景,也足够你用了。
\ No newline at end of file
diff --git a/docs/database/sql-optimize.md b/docs/database/sql-optimize.md
new file mode 100644
index 0000000..5adea55
--- /dev/null
+++ b/docs/database/sql-optimize.md
@@ -0,0 +1,376 @@
+---
+sidebar: heading
+title: SQL优化相关面试题
+category: 数据库
+tag:
+ - MySQL
+head:
+ - - meta
+ - name: keywords
+ content: MySQL面试题,SQL优化,慢查询优化,索引失效,MySQL执行计划
+ - - meta
+ - name: description
+ content: SQL优化常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+## 聊聊explain执行计划
+
+使用 explain 输出 SELECT 语句执行的详细信息,包括以下信息:
+
+- 表的加载顺序
+- sql 的查询类型
+- 可能用到哪些索引,实际上用到哪些索引
+- 读取的行数
+
+`Explain` 执行计划包含字段信息如下:分别是 `id`、`select_type`、`table`、`partitions`、`type`、`possible_keys`、`key`、`key_len`、`ref`、`rows`、`filtered`、`Extra` 12个字段。
+
+通过explain extended + show warnings可以在原本explain的基础上额外提供一些查询优化的信息,得到优化以后的可能的查询语句(不一定是最终优化的结果)。
+
+先搭建测试环境:
+
+```sql
+CREATE TABLE `blog` (
+ `blog_id` int NOT NULL AUTO_INCREMENT COMMENT '唯一博文id--主键',
+ `blog_title` varchar(255) NOT NULL COMMENT '博文标题',
+ `blog_body` text NOT NULL COMMENT '博文内容',
+ `blog_time` datetime NOT NULL COMMENT '博文发布时间',
+ `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+ `blog_state` int NOT NULL COMMENT '博文状态--0 删除 1正常',
+ `user_id` int NOT NULL COMMENT '用户id',
+ PRIMARY KEY (`blog_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
+
+CREATE TABLE `user` (
+ `user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户唯一id--主键',
+ `user_name` varchar(30) NOT NULL COMMENT '用户名--不能重复',
+ `user_password` varchar(255) NOT NULL COMMENT '用户密码',
+ PRIMARY KEY (`user_id`),
+ KEY `name` (`user_name`)
+) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
+
+CREATE TABLE `discuss` (
+ `discuss_id` int NOT NULL AUTO_INCREMENT COMMENT '评论唯一id',
+ `discuss_body` varchar(255) NOT NULL COMMENT '评论内容',
+ `discuss_time` datetime NOT NULL COMMENT '评论时间',
+ `user_id` int NOT NULL COMMENT '用户id',
+ `blog_id` int NOT NULL COMMENT '博文id',
+ PRIMARY KEY (`discuss_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8
+```
+
+**id**
+
+表示查询中执行select子句或者操作表的顺序,**`id`的值越大,代表优先级越高,越先执行**。
+
+```mysql
+explain select discuss_body
+from discuss
+where blog_id = (
+ select blog_id from blog where user_id = (
+ select user_id from user where user_name = 'admin'));
+```
+
+三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
+
+
+
+**select_type**
+
+表示 `select` 查询的类型,主要是用于区分各种复杂的查询,例如:`普通查询`、`联合查询`、`子查询`等。
+
+1. SIMPLE:表示最简单的 select 查询语句,在查询中不包含子查询或者交并差集等操作。
+2. PRIMARY:查询中最外层的SELECT(存在子查询的外层的表操作为PRIMARY)。
+3. SUBQUERY:子查询中首个SELECT。
+4. DERIVED:被驱动的SELECT子查询(子查询位于FROM子句)。
+5. UNION:在SELECT之后使用了UNION。
+
+**table**
+
+查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
+
+
+
+**partitions**
+
+查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
+
+
+
+**type**
+
+查询使用了何种类型,它在 `SQL`优化中是一个非常重要的指标。
+
+**system**
+
+当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
+
+
+
+**const**
+
+单表操作的时候,查询使用了主键或者唯一索引。
+
+
+
+**eq_ref**
+
+**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
+
+
+
+**ref**
+
+查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
+
+
+
+**ref_or_null**
+
+类似 ref,会额外搜索包含`NULL`值的行。
+
+**index_merge**
+
+使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
+
+
+
+**range**
+
+有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
+
+
+
+**index**
+
+index包括select索引列,order by主键两种情况。
+
+1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
+
+ 
+
+2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
+
+ 
+
+**all**
+
+全表扫描,查询没有用到索引,性能最差。
+
+
+
+**possible_keys**
+
+此次查询中可能选用的索引。**但这个索引并不定一会是最终查询数据时所被用到的索引**。
+
+**key**
+
+此次查询中确切使用到的索引。
+
+**ref**
+
+`ref` 列显示使用哪个列或常数与`key`一起从表中选择数据行。常见的值有`const`、`func`、`NULL`、具体字段名。当 `key` 列为 `NULL`,即不使用索引时。如果值是`func`,则使用的值是某个函数的结果。
+
+以下SQL的执行计划`ref`为`const`,因为使用了组合索引`(user_id, blog_id)`,`where user_id = 13`中13为常量。
+
+```mysql
+mysql> explain select blog_id from user_like where user_id = 13;
++----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
++----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
+| 1 | SIMPLE | user_like | NULL | ref | ul1,ul2 | ul1 | 4 | const | 2 | 100.00 | Using index |
++----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
+```
+
+而下面这个SQL的执行计划`ref`值为`NULL`,因为`key`为`NULL`,查询没有用到索引。
+
+```mysql
+mysql> explain select user_id from user_like where status = 1;
++----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
++----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
+| 1 | SIMPLE | user_like | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where |
++----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
+```
+
+**rows**
+
+估算要找到所需的记录,需要读取的行数。评估`SQL` 性能的一个比较重要的数据,`mysql`需要扫描的行数,很直观的显示 `SQL` 性能的好坏,一般情况下 `rows` 值越小越好。
+
+**filtered**
+
+存储引擎返回的数据在经过过滤后,剩下满足条件的记录数量的比例。
+
+**extra**
+
+表示额外的信息说明。为了方便测试,这里新建两张表。
+
+```sql
+CREATE TABLE `t_order` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `user_id` int DEFAULT NULL,
+ `order_id` int DEFAULT NULL,
+ `order_status` tinyint DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_userid_order_id_createdate` (`user_id`,`order_id`,`create_date`)
+) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8
+
+CREATE TABLE `t_orderdetail` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `order_id` int DEFAULT NULL,
+ `product_name` varchar(100) DEFAULT NULL,
+ `cnt` int DEFAULT NULL,
+ `create_date` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_orderid_productname` (`order_id`,`product_name`)
+) ENGINE=InnoDB AUTO_INCREMENT=152 DEFAULT CHARSET=utf8
+```
+
+**1.using where**
+
+查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
+
+
+
+**2.using index**
+
+查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
+
+
+
+**3.Using where&Using index**
+
+查询的列被索引覆盖,但无法通过索引查找找到符合条件的数据,不过可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
+
+包括两种情况(组合索引为(user_id, orde)):
+
+- where筛选条件不符合最左前缀原则
+
+ 
+
+- where筛选条件是索引列前导列的一个范围
+
+ 
+
+**4.null**
+
+查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
+
+
+
+**5.using index condition**
+
+索引下推(index condition pushdown,ICP),先使用where条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。
+
+不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
+
+
+
+使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
+
+
+
+下面的例子使用了ICP:
+
+```sql
+explain select user_id, order_id, order_status
+from t_order where user_id > 1 and user_id < 5\G;
+```
+
+
+
+关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
+
+
+
+**6.using temporary**
+
+使用了临时表保存中间结果,常见于 order by 和 group by 中。典型的,当group by和order by同时存在,且作用于不同的字段时,就会建立临时表,以便计算出最终的结果集。
+
+**7.filesort**
+
+文件排序。表示无法利用索引完成排序操作,以下情况会导致filesort:
+
+- order by 的字段不是索引字段
+- select 查询字段不全是索引字段
+- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
+
+
+
+**8.using join buffer**
+
+Block Nested Loop,需要进行嵌套循环计算。两个关联表join,关联字段均未建立索引,就会出现这种情况。比如内层和外层的type均为ALL,rows均为4,需要循环进行4*4次计算。常见的优化方案是,在关联字段上添加索引,避免每次嵌套循环计算。
+
+## 大表查询慢怎么优化?
+
+某个表有近千万数据,查询比较慢,如何优化?
+
+当MySQL单表记录数过大时,数据库的性能会明显下降,一些常见的优化措施如下:
+
+* 合理建立索引。在合适的字段上建立索引,例如在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
+* 索引优化,SQL优化。最左匹配原则等,参考:https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95
+* 建立分区。对关键字段建立水平分区,比如时间字段,若查询条件往往通过时间范围来进行查询,能提升不少性能
+* 利用缓存。利用Redis等缓存热点数据,提高查询效率
+* 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内
+* 读写分离。经典的数据库拆分方案,主库负责写,从库负责读
+* 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分
+* 合理建立索引。在合适的字段上建立索引,例如在WHERE和ORDERBY命令上涉及的列建立索引
+
+7. 数据异构到es
+8. 冷热数据分离。几个月之前不常用的数据放到冷库中,最新的数据比较新的数据放到热库中
+9. 升级数据库类型,换一种能兼容MySQL的数据库(OceanBase、tidb)
+
+## 深分页怎么优化?
+
+还是以上面的SQL为空:`select * from xxx order by id limit 500000, 10;`
+
+**方法一**:
+
+从上面的分析可以看出,当offset非常大时,server层会从引擎层获取到很多无用的数据,而当select后面是*号时,就需要拷贝完整的行信息,**拷贝完整数据**相比**只拷贝行数据里的其中一两个列字段**更耗费时间。
+
+因为前面的offset条数据最后都是不要的,没有必要拷贝完整字段,所以可以将sql语句修改成:
+
+```
+select * from xxx where id >=(select id from xxx order by id limit 500000, 1) order by id limit 10;
+```
+
+先执行子查询 `select id from xxx by id limit 500000, 1`, 这个操作,其实也是将在innodb中的主键索引中获取到`500000+1`条数据,然后server层会抛弃前500000条,只保留最后一条数据的id。
+
+但不同的地方在于,在返回server层的过程中,只会拷贝数据行内的id这一列,而不会拷贝数据行的所有列,当数据量较大时,这部分的耗时还是比较明显的。
+
+在拿到了上面的id之后,假设这个id正好等于500000,那sql就变成了
+
+```
+select * from xxx where id >=500000 order by id limit 10;
+```
+
+这样innodb再走一次**主键索引**,通过B+树快速定位到id=500000的行数据,时间复杂度是lg(n),然后向后取10条数据。
+
+**方法二:**
+
+将所有的数据**根据id主键进行排序**,然后分批次取,将当前批次的最大id作为下次筛选的条件进行查询。
+
+```mysql
+select * from xxx where id > start_id order by id limit 10;
+```
+
+通过主键索引,每次定位到start_id的位置,然后往后遍历10个数据,这样不管数据多大,查询性能都较为稳定。
+
+## 导致MySQL慢查询有哪些原因?
+
+1. 没有索引,或者索引失效。
+2. 单表数据量太大
+3. 查询使用了临时表
+4. join 或者子查询过多
+5. in元素过多。如果使用了in,即使后面的条件加了索引,还是要注意in后面的元素不要过多哈。in元素一般建议不要超过500个,如果超过了,建议分组,每次500一组进行。
+
+## 索引什么时候会失效?
+
+导致索引失效的情况:
+
+- 对于组合索引,不是使用组合索引最左边的字段,则不会使用索引
+- 以%开头的like查询如`%abc`,无法使用索引;非%开头的like查询如`abc%`,相当于范围查询,会使用索引
+- 查询条件中列类型是字符串,没有使用引号,可能会因为类型不同发生隐式转换,使索引失效
+- 判断索引列是否不等于某个值时
+- 对索引列进行运算
+- 查询条件使用`or`连接,也会导致索引失效
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 的各个功能模块中的执行过程。
+
+
+
+整体来说,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/distributed/1-global-unique-id.md b/docs/distributed/1-global-unique-id.md
deleted file mode 100644
index 295dfda..0000000
--- a/docs/distributed/1-global-unique-id.md
+++ /dev/null
@@ -1,489 +0,0 @@
----
-sidebar: heading
----
-
-传统的单体架构的时候,我们基本是单库然后业务单表的结构。每个业务表的ID一般我们都是从1增,通过`AUTO_INCREMENT=1`设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。
-
-
-
-如上图,如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1。我们系统的架构虽然是分布式的,但是在用户层应是无感知的,重复的订单主键显而易见是不被允许的。那么针对分布式系统如何做到主键唯一性呢?
-
-## UUID
-
-UUID (Universally Unique Identifier),通用唯一识别码的缩写。UUID是由一组32位数的16进制数字所构成,所以UUID理论上的总数为 1632=2128,约等于 3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
-
-生成的UUID是由 `8-4-4-4-12`格式的数据组成,其中32个字符和4个连字符' - ',一般我们使用的时候会将连字符删除 `uuid.toString().replaceAll("-","")`。
-
-目前UUID的产生方式有5种版本,每个版本的算法不同,应用范围也不同。
-
-- **基于时间的UUID - 版本1**:
- 这个一般是通过当前时间,随机数,和本地Mac地址来计算出来,可以通过 `org.apache.logging.log4j.core.util`包中的 `UuidUtil.getTimeBasedUuid()`来使用或者其他包中工具。由于使用了MAC地址,因此能够确保唯一性,但是同时也暴露了MAC地址,私密性不够好。
-- **DCE安全的UUID - 版本2**
- DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。
-- **基于名字的UUID(MD5)- 版本3**
- 基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。
-- **随机UUID - 版本4**
- 根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但是重复的可能性可以忽略不计,因此该版本也是被经常使用的版本。JDK中使用的就是这个版本。
-- **基于名字的UUID(SHA1) - 版本5**
- 和基于名字的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。
-
-我们 Java中 JDK自带的 UUID产生方式就是版本4根据随机数生成的 UUID 和版本3基于名字的 UUID,有兴趣的可以去看看它的源码。
-
-```java
-public static void main(String[] args) {
-
- //获取一个版本4根据随机字节数组的UUID。
- UUID uuid = UUID.randomUUID();
- System.out.println(uuid.toString().replaceAll("-",""));
-
- //获取一个版本3(基于名称)根据指定的字节数组的UUID。
- byte[] nbyte = {10, 20, 30};
- UUID uuidFromBytes = UUID.nameUUIDFromBytes(nbyte);
- System.out.println(uuidFromBytes.toString().replaceAll("-",""));
-}
-```
-
-得到的UUID结果,
-
-```undefined
-59f51e7ea5ca453bbfaf2c1579f09f1d
-7f49b84d0bbc38e9a493718013baace6
-```
-
-虽然 UUID 生成方便,本地生成没有网络消耗,但是使用起来也有一些缺点,
-
-- 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
-- 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置。
-- 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能,可以查阅 Mysql 索引原理 B+树的知识。
-
-## 数据库生成
-
-是不是一定要基于外界的条件才能满足分布式唯一ID的需求呢,我们能不能在我们分布式数据库的基础上获取我们需要的ID?
-
-由于分布式数据库的起始自增值一样所以才会有冲突的情况发生,那么我们将分布式系统中数据库的同一个业务表的自增ID设计成不一样的起始值,然后设置固定的步长,步长的值即为分库的数量或分表的数量。
-
-以MySQL举例,利用给字段设置`auto_increment_increment`和`auto_increment_offset`来保证ID自增。
-
-- auto_increment_offset:表示自增长字段从那个数开始,他的取值范围是1 .. 65535。
-- auto_increment_increment:表示自增长字段每次递增的量,其默认值是1,取值范围是1 .. 65535。
-
-假设有三台机器,则DB1中order表的起始ID值为1,DB2中order表的起始值为2,DB3中order表的起始值为3,它们自增的步长都为3,则它们的ID生成范围如下图所示:
-
-
-
-通过这种方式明显的优势就是依赖于数据库自身不需要其他资源,并且ID号单调自增,可以实现一些对ID有特殊要求的业务。
-
-但是缺点也很明显,首先它强依赖DB,当DB异常时整个系统不可用。虽然配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。还有就是ID发号性能瓶颈限制在单台MySQL的读写性能。
-
-## 使用redis实现
-
-Redis实现分布式唯一ID主要是通过提供像 *INCR* 和 *INCRBY* 这样的自增原子命令,由于Redis自身的单线程的特点所以能保证生成的 ID 肯定是唯一有序的。
-
-但是单机存在性能瓶颈,无法满足高并发的业务需求,所以可以采用集群的方式来实现。集群的方式又会涉及到和数据库集群同样的问题,所以也需要设置分段和步长来实现。
-
-为了避免长期自增后数字过大可以通过与当前时间戳组合起来使用,另外为了保证并发和业务多线程的问题可以采用 Redis + Lua的方式进行编码,保证安全。
-
-Redis 实现分布式全局唯一ID,它的性能比较高,生成的数据是有序的,对排序业务有利,但是同样它依赖于redis,需要系统引进redis组件,增加了系统的配置复杂性。
-
-当然现在Redis的使用性很普遍,所以如果其他业务已经引进了Redis集群,则可以资源利用考虑使用Redis来实现。
-
-## 雪花算法-Snowflake
-
-Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的。
-
-- 第1位占用1bit,其值始终是0,可看做是符号位不使用。
-- 第2位开始的41位是时间戳,41-bit位可表示2^41个数,每个数代表毫秒,那么雪花算法可用的时间年限是(1L<<41)/(1000L*3600*24*365)=69 年的时间。
-- 中间的10-bit位可表示机器数,即2^10 = 1024台机器,但是一般情况下我们不会部署这么台机器。如果我们对IDC(互联网数据中心)有需求,还可以将 10-bit 分 5-bit 给 IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,具体的划分可以根据自身需求定义。
-- 最后12-bit位是自增序列,可表示2^12 = 4096个数。
-
-这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。
-
-
-
-Snowflake 的[Twitter官方原版](https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala)是用Scala写的,对Scala语言有研究的同学可以去阅读下,以下是 Java 版本的写法。
-
-```java
-package com.jajian.demo.distribute;
-
-/**
- * Twitter_Snowflake
- * SnowFlake的结构如下(每部分用-分开):
- * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
- * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
- * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
- * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
- * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
- * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
- * 加起来刚好64位,为一个Long型。
- * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
- */
-public class SnowflakeDistributeId {
-
-
- // ==============================Fields===========================================
- /**
- * 开始时间截 (2015-01-01)
- */
- private final long twepoch = 1420041600000L;
-
- /**
- * 机器id所占的位数
- */
- private final long workerIdBits = 5L;
-
- /**
- * 数据标识id所占的位数
- */
- private final long datacenterIdBits = 5L;
-
- /**
- * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
- */
- private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
-
- /**
- * 支持的最大数据标识id,结果是31
- */
- private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
-
- /**
- * 序列在id中占的位数
- */
- private final long sequenceBits = 12L;
-
- /**
- * 机器ID向左移12位
- */
- private final long workerIdShift = sequenceBits;
-
- /**
- * 数据标识id向左移17位(12+5)
- */
- private final long datacenterIdShift = sequenceBits + workerIdBits;
-
- /**
- * 时间截向左移22位(5+5+12)
- */
- private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
-
- /**
- * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
- */
- private final long sequenceMask = -1L ^ (-1L << sequenceBits);
-
- /**
- * 工作机器ID(0~31)
- */
- private long workerId;
-
- /**
- * 数据中心ID(0~31)
- */
- private long datacenterId;
-
- /**
- * 毫秒内序列(0~4095)
- */
- private long sequence = 0L;
-
- /**
- * 上次生成ID的时间截
- */
- private long lastTimestamp = -1L;
-
- //==============================Constructors=====================================
-
- /**
- * 构造函数
- *
- * @param workerId 工作ID (0~31)
- * @param datacenterId 数据中心ID (0~31)
- */
- public SnowflakeDistributeId(long workerId, long datacenterId) {
- if (workerId > maxWorkerId || workerId < 0) {
- throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
- }
- if (datacenterId > maxDatacenterId || datacenterId < 0) {
- throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
- }
- this.workerId = workerId;
- this.datacenterId = datacenterId;
- }
-
- // ==============================Methods==========================================
-
- /**
- * 获得下一个ID (该方法是线程安全的)
- *
- * @return SnowflakeId
- */
- public synchronized long nextId() {
- long timestamp = timeGen();
-
- //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
- if (timestamp < lastTimestamp) {
- throw new RuntimeException(
- String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
- }
-
- //如果是同一时间生成的,则进行毫秒内序列
- if (lastTimestamp == timestamp) {
- sequence = (sequence + 1) & sequenceMask;
- //毫秒内序列溢出
- if (sequence == 0) {
- //阻塞到下一个毫秒,获得新的时间戳
- timestamp = tilNextMillis(lastTimestamp);
- }
- }
- //时间戳改变,毫秒内序列重置
- else {
- sequence = 0L;
- }
-
- //上次生成ID的时间截
- lastTimestamp = timestamp;
-
- //移位并通过或运算拼到一起组成64位的ID
- return ((timestamp - twepoch) << timestampLeftShift) //
- | (datacenterId << datacenterIdShift) //
- | (workerId << workerIdShift) //
- | sequence;
- }
-
- /**
- * 阻塞到下一个毫秒,直到获得新的时间戳
- *
- * @param lastTimestamp 上次生成ID的时间截
- * @return 当前时间戳
- */
- protected long tilNextMillis(long lastTimestamp) {
- long timestamp = timeGen();
- while (timestamp <= lastTimestamp) {
- timestamp = timeGen();
- }
- return timestamp;
- }
-
- /**
- * 返回以毫秒为单位的当前时间
- *
- * @return 当前时间(毫秒)
- */
- protected long timeGen() {
- return System.currentTimeMillis();
- }
-}
-```
-
-测试的代码如下
-
-```java
-public static void main(String[] args) {
- SnowflakeDistributeId idWorker = new SnowflakeDistributeId(0, 0);
- for (int i = 0; i < 1000; i++) {
- long id = idWorker.nextId();
-// System.out.println(Long.toBinaryString(id));
- System.out.println(id);
- }
-}
-```
-
-雪花算法提供了一个很好的设计思想,雪花算法生成的ID是趋势递增,不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的,而且可以根据自身业务特性分配bit位,非常灵活。
-
-但是雪花算法强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。如果恰巧回退前生成过一些ID,而时间回退后,生成的ID就有可能重复。官方对于此并没有给出解决方案,而是简单的抛错处理,这样会造成在时间被追回之前的这段时间服务不可用。
-
-很多其他类雪花算法也是在此思想上的设计然后改进规避它的缺陷,后面介绍的百度 UidGenerator 和 美团分布式ID生成系统 Leaf 中snowflake模式都是在 snowflake 的基础上演进出来的。
-
-## 百度-UidGenerator
-
-[百度的 UidGenerator](https://github.com/baidu/uid-generator) 是百度开源基于Java语言实现的唯一ID生成器,是在雪花算法 snowflake 的基础上做了一些改进。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略,适用于docker等虚拟化环境下实例自动重启、漂移等场景。
-
-在实现上,UidGenerator 提供了两种生成唯一ID方式,分别是 DefaultUidGenerator 和 CachedUidGenerator,官方建议如果有性能考虑的话使用 CachedUidGenerator 方式实现。
-
-UidGenerator 依然是以划分命名空间的方式将 64-bit位分割成多个部分,只不过它的默认划分方式有别于雪花算法 snowflake。它默认是由 1-28-22-13 的格式进行划分。可根据你的业务的情况和特点,自己调整各个字段占用的位数。
-
-- 第1位仍然占用1bit,其值始终是0。
-- 第2位开始的28位是时间戳,28-bit位可表示2^28个数,**这里不再是以毫秒而是以秒为单位**,每个数代表秒则可用(1L<<28)/ (3600*24*365) ≈ 8.51 年的时间。
-- 中间的 workId (数据中心+工作机器,可以其他组成方式)则由 22-bit位组成,可表示 2^22 = 4194304个工作ID。
-- 最后由13-bit位构成自增序列,可表示2^13 = 8192个数。
-
-
-
-其中 workId (机器 id),最多可支持约420w次机器启动。内置实现为在启动时由数据库分配(表名为 WORKER_NODE),默认分配策略为用后即弃,后续可提供复用策略。
-
-```sql
-DROP TABLE IF EXISTS WORKER_NODE;
-CREATE TABLE WORKER_NODE
-(
-ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
-HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
-PORT VARCHAR(64) NOT NULL COMMENT 'port',
-TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
-LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
-MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
-CREATED TIMESTAMP NOT NULL COMMENT 'created time',
-PRIMARY KEY(ID)
-)
- COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
-```
-
-### DefaultUidGenerator 实现
-
-DefaultUidGenerator 就是正常的根据时间戳和机器位还有序列号的生成方式,和雪花算法很相似,对于时钟回拨也只是抛异常处理。仅有一些不同,如以秒为为单位而不再是毫秒和支持Docker等虚拟化环境。
-
-```java
-protected synchronized long nextId() {
- long currentSecond = getCurrentSecond();
-
- // Clock moved backwards, refuse to generate uid
- if (currentSecond < lastSecond) {
- long refusedSeconds = lastSecond - currentSecond;
- throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
- }
-
- // At the same second, increase sequence
- if (currentSecond == lastSecond) {
- sequence = (sequence + 1) & bitsAllocator.getMaxSequence();
- // Exceed the max sequence, we wait the next second to generate uid
- if (sequence == 0) {
- currentSecond = getNextSecond(lastSecond);
- }
-
- // At the different second, sequence restart from zero
- } else {
- sequence = 0L;
- }
-
- lastSecond = currentSecond;
-
- // Allocate bits for UID
- return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);
-}
-```
-
-如果你要使用 DefaultUidGenerator 的实现方式的话,以上划分的占用位数可通过 spring 进行参数配置。
-
-```xml
-
-
-
-
-
-
-
-
-
-```
-
-### CachedUidGenerator 实现
-
-而官方建议的性能较高的 CachedUidGenerator 生成方式,是使用 RingBuffer 缓存生成的id。数组每个元素成为一个slot。RingBuffer容量,默认为Snowflake算法中sequence最大值(2^13 = 8192)。可通过 boostPower 配置进行扩容,以提高 RingBuffer 读写吞吐量。
-
-Tail指针、Cursor指针用于环形数组上读写slot:
-
-- Tail指针
- 表示Producer生产的最大序号(此序号从0开始,持续递增)。Tail不能超过Cursor,即生产者不能覆盖未消费的slot。当Tail已赶上curosr,此时可通过rejectedPutBufferHandler指定PutRejectPolicy
-- Cursor指针
- 表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail,即不能消费未生产的slot。当Cursor已赶上tail,此时可通过rejectedTakeBufferHandler指定TakeRejectPolicy
-
-
-
-CachedUidGenerator采用了双RingBuffer,Uid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费)。
-
-由于数组元素在内存中是连续分配的,可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题,为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。
-
-
-
-RingBuffer填充时机
-
-- 初始化预填充
- RingBuffer初始化时,预先填充满整个RingBuffer。
-- 即时填充
- Take消费时,即时检查剩余可用slot量(tail - cursor),如小于设定阈值,则补全空闲slots。阈值可通过paddingFactor来进行配置,请参考Quick Start中CachedUidGenerator配置。
-- 周期填充
- 通过Schedule线程,定时补全空闲slots。可通过scheduleInterval配置,以应用定时填充功能,并指定Schedule时间间隔。
-
-## 美团Leaf
-
-Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的著名的一句话:“There are no two identical leaves in the world”,世间不可能存在两片相同的叶子。
-
-Leaf 也提供了两种ID生成的方式,分别是 Leaf-segment 数据库方案和 Leaf-snowflake 方案。
-
-### Leaf-segment 数据库方案
-
-Leaf-segment 数据库方案,是在上文描述的在使用数据库的方案上,做了如下改变:
-
-- 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。
-- 各个业务不同的发号需求用 `biz_tag`字段来区分,每个biz-tag的ID获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对biz_tag分库分表就行。
-
-数据库表设计如下:
-
-```sql
-CREATE TABLE `leaf_alloc` (
- `biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '业务key',
- `max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前已经分配了的最大id',
- `step` int(11) NOT NULL COMMENT '初始步长,也是动态调整的最小步长',
- `description` varchar(256) DEFAULT NULL COMMENT '业务key的描述',
- `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- PRIMARY KEY (`biz_tag`)
-) ENGINE=InnoDB;
-```
-
-原来获取ID每次都需要写数据库,现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从1减小到了1/step,大致架构如下图所示:
-
-
-
-同时Leaf-segment 为了解决 TP999(满足千分之九百九十九的网络请求所需要的最低耗时)数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,TP999 数据会出现偶尔的尖刺的问题,提供了双buffer优化。
-
-简单的说就是,Leaf 取号段的时机是在号段消耗完的时候进行的,也就意味着号段临界点的ID下发时间取决于下一次从DB取回号段的时间,并且在这期间进来的请求也会因为DB号段没有取回来,导致线程阻塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢。
-
-为了DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中,而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的 TP999 指标。详细实现如下图所示:
-
-
-
-采用双buffer的方式,Leaf服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。
-
-- 每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。
-- 每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。
-
-对于这种方案依然存在一些问题,它仍然依赖 DB的稳定性,需要采用主从备份的方式提高 DB的可用性,还有 Leaf-segment方案生成的ID是趋势递增的,这样ID号是可被计算的,例如订单ID生成场景,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。
-
-### Leaf-snowflake方案
-
-Leaf-snowflake方案完全沿用 snowflake 方案的bit位设计,对于workerID的分配引入了Zookeeper持久顺序节点的特性自动对snowflake节点配置 wokerID。避免了服务规模较大时,动手配置成本太高的问题。
-
-Leaf-snowflake是按照下面几个步骤启动的:
-
-- 启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
-- 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
-- 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。
-
-
-
-为了减少对 Zookeeper的依赖性,会在本机文件系统上缓存一个workerID文件。当ZooKeeper出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。
-
-上文阐述过在类 snowflake算法上都存在时钟回拨的问题,Leaf-snowflake在解决时钟回拨的问题上是通过校验自身系统时间与 `leaf_forever/${self}`节点记录时间做比较然后启动报警的措施。
-
-
-
-美团官方建议是由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警。
-
-在性能上官方提供的数据目前 Leaf 的性能在4C8G 的机器上QPS能压测到近5w/s,TP999 1ms。
-
-## 总结
-
-以上基本列出了所有常用的分布式ID生成方式,其实大致分类的话可以分为两类:
-
-一种是类DB型的,根据设置不同起始值和步长来实现趋势递增,需要考虑服务的容错性和可用性。
-
-另一种是类snowflake型,这种就是将64位划分为不同的段,每段代表不同的涵义,基本就是时间戳、机器ID和序列数。这种方案就是需要考虑时钟回拨的问题以及做一些 buffer的缓冲设计提高性能。
-
-而且可通过将三者(时间戳,机器ID,序列数)划分不同的位数来改变使用寿命和并发数。
-
-例如对于并发数要求不高、期望长期使用的应用,可增加时间戳位数,减少序列数的位数. 例如配置成{"workerBits":23,"timeBits":31,"seqBits":9}时, 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年。
-
-对于节点重启频率频繁、期望长期使用的应用, 可增加工作机器位数和时间戳位数, 减少序列数位数. 例如配置成{"workerBits":27,"timeBits":30,"seqBits":6}时, 可支持37个节点以整体并发量2400 UID/s的速度持续运行34年。
-
-
-
-[参考链接](https://www.cnblogs.com/jajian/p/11101213.html)
diff --git a/docs/distributed/2-distributed-lock.md b/docs/distributed/2-distributed-lock.md
deleted file mode 100644
index 2e2e671..0000000
--- a/docs/distributed/2-distributed-lock.md
+++ /dev/null
@@ -1,259 +0,0 @@
----
-sidebar: heading
----
-
-## 为什么要使用分布式锁
-
-在单机环境下,当存在多个线程可以同时改变某个变量(可变共享变量)时,就会出现线程安全问题。这个问题可以通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一些线程安全的类等来避免。
-
-而在多机部署环境,需要在多进程下保证线程的安全性,Java提供的这些API仅能保证在单个JVM进程内对多线程访问共享资源的线程安全,已经不满足需求了。这时候就需要使用分布式锁来保证线程安全。通过分布式锁,可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
-
-## 分布式锁应该具备哪些条件
-
-在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:
-
-1. 互斥性。在任意时刻,只有一个客户端能持有锁。
-2. 不会死锁。具备锁失效机制,防止死锁。即使有客户端在持有锁的期间崩溃而没有主动解锁,也要保证后续其他客户端能加锁。
-3. 加锁和解锁必须是同一个客户端。客户端a不能将客户端b的锁解开,即不能误解锁。
-4. 高性能、高可用的获取锁与释放锁。
-5. 具备可重入特性。
-6. 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
-
-## 分布式锁的三种实现方式
-
-1. 基于数据库实现分布式锁;
-2. 基于缓存(Redis等)实现分布式锁;
-3. 基于Zookeeper实现分布式锁。
-
-### 基于数据库的实现方式
-
-**悲观锁**
-
-创建一张锁表,然后通过操作该表中的数据来实现加锁和解锁。当要锁住某个方法或资源时,就向该表插入一条记录,表中设置方法名为唯一键,这样多个请求同时提交数据库时,只有一个操作可以成功,判定操作成功的线程获得该方法的锁,可以执行方法内容;想要释放锁的时候就删除这条记录,其他线程就可以继续往数据库中插入数据获取锁。
-
-**乐观锁**
-
-每次更新操作,都觉得不会存在并发冲突,只有更新失败后,才重试。
-
-扣减余额就可以使用这种方案。
-
-具体实现:增加个version字段,每次更新修改,都会自增加一,然后去更新余额时,把查出来的那个版本号,带上条件去更新,如果是上次那个版本号,就更新,如果不是,表示别人并发修改过了,就继续重试。
-
-### 基于Redis的实现方式
-
-#### 简单实现
-
-Redis 2.6.12 之前的版本中采用 setnx + expire 方式实现分布式锁,在 Redis 2.6.12 版本后 setnx 增加了过期时间参数:
-
-```java
-SET lockKey value NX PX expire-time
-```
-
-所以在Redis 2.6.12 版本后,只需要使用setnx就可以实现分布式锁了。
-
-加锁逻辑:
-
-1. setnx争抢key的锁,如果已有key存在,则不作操作,过段时间继续重试,保证只有一个客户端能持有锁。
-2. value设置为 requestId(可以使用机器ip拼接当前线程名称),表示这把锁是哪个请求加的,在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
-3. 再用expire给锁加一个过期时间,防止异常导致锁没有释放。
-
-解锁逻辑:
-
-首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。使用lua脚本实现原子操作,保证线程安全。
-
-下面我们通过Jedis(基于java语言的redis客户端)来演示分布式锁的实现。
-
-**Jedis实现分布式锁**
-
-引入Jedis jar包,在pom.xml文件增加代码:
-
-```xml
-
- redis.clients
- jedis
- 2.9.0
-
-```
-
-**加锁**
-
-调用jedis的set()实现加锁,加锁代码如下:
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-01 17:13
- */
-public class RedisTest {
-
- private static final String LOCK_SUCCESS = "OK";
- private static final String SET_IF_NOT_EXIST = "NX";
- private static final String SET_EXPIRE_TIME = "PX";
-
- @Autowired
- private JedisPool jedisPool;
-
- public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
- Jedis jedis = jedisPool.getResource();
- String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_EXPIRE_TIME, expireTime);
-
- if (LOCK_SUCCESS.equals(result)) {
- return true;
- }
- return false;
- }
-}
-```
-
-各参数说明:
-
-- lockKey:使用key来当锁,需要保证key是唯一的。可以使用系统号拼接自定义的key。
-- requestId:表示这把锁是哪个请求加的,可以使用机器ip拼接当前线程名称。在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
-- NX:意思是SET IF NOT EXIST,保证如果已有key存在,则不作操作,过段时间继续重试。NX参数保证只有一个客户端能持有锁。
-- PX:给key加一个过期的设置,具体时间由expireTime决定。
-- expireTime:设置key的过期时间,防止异常导致锁没有释放。
-
-**解锁**
-
-首先需要获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。这里使用lua脚本实现原子操作,保证线程安全。
-
-使用eval命令执行Lua脚本的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。lua脚本如下:
-
-```
-//KEYS[1]是lockKey,ARGV[1]是requestId
-String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
-Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
-```
-
-Jedis的eval()方法源码如下:
-
-```java
-public Object eval(String script, List keys, List args) {
- return this.eval(script, keys.size(), getParams(keys, args));
-}
-```
-
-lua脚本的意思是:调用get获取锁(KEYS[1])对应的value值,检查是否与requestId(ARGV[1])相等,如果相等则调用del删除锁。否则返回0。
-
-完整的解锁代码如下:
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-01 17:13
- */
-public class RedisTest {
- private static final Long RELEASE_SUCCESS = 1L;
-
- @Autowired
- private JedisPool jedisPool;
-
- public boolean releaseDistributedLock(String lockKey, String requestId) {
- Jedis jedis = jedisPool.getResource();
- ////KEYS[1]是lockKey,ARGV[1]是requestId
- String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
- Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
-
- if (RELEASE_SUCCESS.equals(result)) {
- return true;
- }
- return false;
- }
-}
-```
-
-#### RedLock
-
-前面的方案是基于**Redis单机版**的分布式锁讨论,还不是很完美。因为Redis一般都是集群部署的。
-
-如果线程一在`Redis`的`master`节点上拿到了锁,但是加锁的`key`还没同步到`slave`节点。恰好这时,`master`节点发生故障,一个`slave`节点就会升级为`master`节点。线程二就可以顺理成章获取同个`key`的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。
-
-为了解决这个问题,Redis作者antirez提出一种高级的分布式锁算法:**Redlock**。它的核心思想是这样的:
-
-部署多个Redis master,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。
-
-我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。
-
-RedLock的实现步骤:
-
-1. 获取当前时间,以毫秒为单位。
-2. 按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。
-3. 客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)
-4. 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。
-5. 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。
-
-简化下步骤就是:
-
-- 按顺序向5个master节点请求加锁
-- 根据设置的超时时间来判断,是不是要跳过该master节点。
-- 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
-- 如果获取锁失败,解锁!
-
-### 基于ZooKeeper的实现方式
-
-ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:
-
-(1)创建一个目录mylock;
-(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
-(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
-(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
-(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
-
-这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。
-
-优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
-
-缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。
-
-## 三种实现方式对比
-
-**数据库分布式锁实现**
-
-优点:
-
-- 简单,使用方便,不需要引入`Redis、zookeeper`等中间件。
-
-缺点:
-
-- 不适合高并发的场景
-- db操作性能较差;
-
-**Redis分布式锁实现**
-
-优点:
-
-- 性能好,适合高并发场景
-- 较轻量级
-- 有较好的框架支持,如Redisson
-
-缺点:
-
-- 过期时间不好控制
-- 需要考虑锁被别的线程误删场景
-
-**Zookeeper分布式锁实现**
-
-缺点:
-
-- 性能不如redis实现的分布式锁
-- 比较重的分布式锁。
-
-优点:
-
-- 有较好的性能和可靠性
-- 有封装较好的框架,如Curator
-
-**对比汇总**
-
-- 从性能角度(从高到低)Redis > Zookeeper >= 数据库;
-- 从理解的难易程度角度(从低到高)数据库 > Redis > Zookeeper;
-- 从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库;
-- 从可靠性角度(从高到低)Zookeeper > Redis > 数据库。
-
-
-
-## 参考链接
-
-https://mp.weixin.qq.com/s/xQknd6xsVDPBr4TbETTk2A
diff --git a/docs/distributed/2.1-rpc.md b/docs/distributed/2.1-rpc.md
deleted file mode 100644
index 188e2ce..0000000
--- a/docs/distributed/2.1-rpc.md
+++ /dev/null
@@ -1,123 +0,0 @@
----
-sidebar: heading
----
-
-## RPC简介
-
-RPC,英文全名remote procedure call,即远程过程调用。就是说一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
-
-可以这么说,RPC就是要像调用本地的函数一样去调远程函数。
-
-RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。
-
-其中,通信协议包含**http协议**(如gRPC使用http2)、**自定义报文的tcp协议**(如dubbo)。序列化协议包含**基于文本编码**的xml、json,**基于二进制编码**的protobuf、hessian等。
-
-> protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储,占用空间小,但也带来了可读性差的缺点(二进制协议,因为不可读而难以调试,不好定位问题)。
->
-> Protobuf序列化协议相比JSON有什么优点?
->
-> 1:序列化后体积相比Json和XML很小,适合网络传输
->
-> 2:支持跨平台多语言
->
-> 3:序列化反序列化速度很快,快于Json的处理速速
-
-**一个完整的RPC过程,都可以用下面这张图来描述**:
-
-> stub说的都是“一小块代码”,通常是有个caller要调用callee的时候,中间需要一些特殊处理的逻辑,就会用这种“小块代码”去做。
-
-
-
-1. 服务消费端(client)以本地调用的方式调用远程服务;
-2. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`;
-3. 客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端;
-4. 服务端 Stub(桩)收到消息将消息反序列化为Java对象: `RpcRequest`;
-5. 服务端 Stub(桩)根据`RpcRequest`中的类、方法、方法参数等信息调用本地的方法;
-6. 服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:`RpcResponse`(序列化)发送至消费方;
-7. 客户端 Stub(client stub)接收到消息并将消息反序列化为Java对象:`RpcResponse` ,这样也就得到了最终结果。
-
-## RPC 解决了什么问题?
-
-让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。
-
-## 常见的 RPC 框架有哪些?
-
-- **Dubbo:** Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。
-- **gRPC** :基于HTTP2。gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
-- **Hessian:** Hessian是一个轻量级的remoting on http工具,使用简单的方法提供了RMI的功能。 采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
-- **Thrift:** Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。
-
-## 有了HTTP ,为啥还要用RPC进行服务调用?
-
-首先,RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。而HTTP只是一个通信协议,不是一个完整的远程调用方案。这两者不是对等的概念,用来比较不太合适。
-
-RPC框架可以使用 **HTTP协议**作为传输协议或者直接使用**自定义的TCP协议**作为传输协议,使用不同的协议一般也是为了适应不同的场景。
-
-HTTP+Restful,其优势很大。它**可读性好**,且**应用广、跨语言的支持**。
-
-但是使用该方案也有其缺点,这是与其优点相对应的:
-
-- 首先是**有用信息占比少**,毕竟HTTP工作在第七层,包含了大量的HTTP头等信息。
-- 其次是**效率低**,还是因为第七层的缘故,必须按照HTTP协议进行层层封装。
-
-而使用**自定义tcp协议**的话,可以极大地精简了传输内容,这也是为什么有些后端服务之间会采用自定义tcp协议的rpc来进行通信的原因。
-
-## 各种序列化技术
-
-### XML
-
-XML序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能要求不高,而且QPS较低的企业级内部系统之间的数据交换场景。同时XML又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的WebService,就是采用XML格式对数据进行序列化的。XML序列化/反序列化的实现方式有很多,熟知的方式有XStream和Java自带的XML序列化和反序列化两种。
-
-### JSON
-
-JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML来说,JSON的字节流更小,而且可读性也非常好。现在JSON数据格式在企业运用是最普遍的。
-
-JSON序列化常用的开源工具有很多:
-
-1. Jackson(https://github.com/FasterXML/jackson )
-2. 阿里开源的FastJson(https://github.com/alibaba/fastjon)
-3. 谷歌的GSON(https://github.com/google/gson)
-
-这几种json的序列化工具中,jackson与fastjson要比GSON的性能好,但是jackson、GSON的稳定性腰比Fastjson好。而fastjson的优势在于提供的api非常容易使用。
-
-### Hession
-
-Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说,Hession具有更好的性能和易读性,而且支持多种不同的语言。
-
-实际上Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行了重构,性能更高。
-
-### Avro
-
-Avro是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据。动态语言友好,Avro提供的机制是动态语言可以方便的处理Avro数据。
-
-### Kryo
-
-Kryo是一种非常成熟的序列化实现,已经在Hive、Storm中使用的比较广泛,不过它不能夸语言。目前Dubbo已经在2.6版本支持kyro的序列化机制。它的性能要由于之前的hessian2。
-
-### Protobuf
-
-Protobuf是Google的一种数据交换格式,它独立于语言、独立于平台。Google提供了多种语言来实现,比如Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件,Protobuf是一个纯粹的表示层协议,可以和各种传输层协议一起使用。
-
-Protobuf使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的RPC调用。另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中。
-
-但是要使用Protobuf会相对来说麻烦些,因为他有自己的语法,有自己的编译器,如果需要用到的话必须要去投入成本在这个技术的学习中。
-
-Protobuf有个缺点就是要传输每一个类的结构都要生成对应的proto文件,如果某个类发生修改,还得重新生成该类对应的proto文件。
-
-## 序列化技术的选型
-
-### 技术层面
-
-1. 序列化空间开销,也就是序列化产生的结果大小,这个影响到传输性能。
-2. 序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间。
-3. 序列化协议是否支持夸平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的。
-4. 可扩展性、兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们来采用序列化协议具有良好的可扩展性、兼容性,比如现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务。
-5. 技术的流行程度,越流行的技术意味着使用的公司越多,那么很多坑都已经淌过并且得到了解决,技术解决方案也相对成熟。
-6. 学习难度和易用性。
-
-### 选型建议
-
-1. 对性能要求不高的场景,可以采用基于XML的SOAP协议
-2. 性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro都可以。
-3. 基于前后端分离,或者独立的对外API服务,选用JSON是比较好的,对于调试、可读性都很不错。
-4. Avro设计理念偏于动态类型语言,那么这类的场景使用Avro是可以的。
diff --git a/docs/distributed/3-micro-service.md b/docs/distributed/3-micro-service.md
deleted file mode 100644
index a5e6705..0000000
--- a/docs/distributed/3-micro-service.md
+++ /dev/null
@@ -1,197 +0,0 @@
----
-sidebar: heading
----
-
-## 什么是微服务?
-
-微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务和服务之间采用轻量级的通信机制进行协作。每个服务可以被独立的部署到生产环境。
-
-[从单体应用到微服务](https://www.zhihu.com/question/65502802/answer/802678798)
-
-单体系统的缺点:
-
-1. 修改一个小功能,就需要将整个系统重新部署上线,影响其他功能的运行;
-2. 功能模块互相依赖,强耦合,扩展困难。如果出现性能瓶颈,需要对整体应用进行升级,虽然影响性能的可能只是其中一个小模块;
-
-单体系统的优点:
-
-1. 容易部署,程序单一,不存在分布式集群的复杂部署环境;
-2. 容易测试,没有复杂的服务调用关系。
-
-微服务的**优点**:
-
-1. 不同的服务可以使用不同的技术;
-2. 隔离性。一个服务不可用不会导致其他服务不可用;
-3. 可扩展性。某个服务出现性能瓶颈,只需对此服务进行升级即可;
-4. 简化部署。服务的部署是独立的,哪个服务出现问题,只需对此服务进行修改重新部署;
-
-微服务的**缺点**:
-
-1. 网络调用频繁。性能相对函数调用较差。
-2. 运维成本增加。系统由多个独立运行的微服务构成,需要设计一个良好的监控系统对各个微服务的运行状态进行监控。
-
-
-
-## 分布式和微服务的区别
-
-从概念理解,分布式服务架构强调的是服务化以及服务的**分散化**,微服务则更强调服务的**专业化和精细分工**;
-
-从实践的角度来看,**微服务架构通常是分布式服务架构**,反之则未必成立。
-
-一句话概括:分布式:分散部署;微服务:分散能力。
-
-
-
-## 服务怎么划分?
-
-横向拆分:按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。
-
-纵向拆分:把一个业务功能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,形成相对独立的原子服务层。这样一纵一横,就可以实现业务的服务化拆分。
-
-要做好微服务的分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求
-
-总之,微服务的设计一定要 **渐进式** 的,总的原则是 **服务内部高内聚,服务之间低耦合。**
-
- ## 微服务设计原则
-**单一职责原则**
-
-意思是每个微服务只需要实现自己的业务逻辑就可以了,比如订单管理模块,它只需要处理订单的业务逻辑就可以了,其它的不必考虑。
-
-**服务自治原则**
-
-意思是每个微服务从开发、测试、运维等都是独立的,包括存储的数据库也都是独立的,自己就有一套完整的流程,我们完全可以把它当成一个项目来对待。不必依赖于其它模块。
-
-**轻量级通信原则**
-
-首先是通信的语言非常的轻量,第二,该通信方式需要是跨语言、跨平台的,之所以要跨平台、跨语言就是为了让每个微服务都有足够的独立性,可以不受技术的钳制。
-
-**接口明确原则**
-
-由于微服务之间可能存在着调用关系,为了尽量避免以后由于某个微服务的接口变化而导致其它微服务都做调整,在设计之初就要考虑到所有情况,让接口尽量做的更通用,更灵活,从而尽量避免其它模块也做调整。
-
-
-
-## 微服务之间是如何通讯的?
-
-**1、RPC**
-
-优点:简单,常见。因为没有中间件代理,系统更简单
-
-缺点:
-
-1. 只支持请求/响应的模式,不支持别的,比如通知、发布/订阅
-2. 降低了可用性,因为客户端和服务端在请求过程中必须都是可用的
-
-**2、消息队列**
-
-除了标准的基于RPC通信的微服务架构,还有基于消息队列通信的微服务架构,这种架构下的微服务采用发送消息(Publish Message)与监听消息(Subscribe Message)的方式来实现彼此之间的交互。
-
-网易的蜂巢平台就采用了基于消息队列的微服务架构设计思路,如下图所示,微服务之间通过RabbitMQ传递消息,实现通信。
-
-与上面几种微服务架构相比,基于消息队列的微服务架构并不多,案例也相对较少,更多地体现为一种与业务相关的设计经验,各家有各家的实现方式,缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台。因此,如果需要实施这种微服务架构,则基本上需要项目组自己从零开始去设计实现一个微服务架构基础平台,其代价是成本高、风险大。
-
-**优点**:
-
-- 把客户端和服务端解耦,更松耦合提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
-- 支持很多通信机制比如通知、发布/订阅等
-
-**缺点**:
-
-- 缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台
-- 成本高、风险大
-
-## 熔断器
-
-雪崩效应:假如存在这样的调用链路,a服务->b服务->c服务,当c服务挂了的时候,b服务调用c服务会等待超时,a服务调用b服务也会等待超时,调用方长时间等不到相应而占用线程,如果有大量的请求过来,就会造成线程池打满,导致整个链路的服务奔溃。
-
-为了解决分布式系统的雪崩效应,分布式系统引进了**熔断器机制** 。
-
-当一个服务的处理用户请求的失败次数在一定时间内小于设定的阀值时,熔断器出于关闭状态,服务正常。
-
-当服务处理用户请求失败次数在一定时间内大于设定的阀值时,说明服务出现故障,打开熔断器,这时所有的请求会快速返回失败的错误信息,不执行业务逻辑,从而防止故障的蔓延。
-
-当处于打开状态的熔断器时,一段时间后出于半打开状态,并执行一定数量的请求,剩余的请求会执行快速失败,若执行请求失败了,则继续打开熔断器,若成功了,则将熔断器关闭。
-
-## 服务网关
-
-### 何为网关?
-
-通俗一点的讲:网关就是要去别的网络的时候,把报文首先发送到的那台设备。稍微专业一点的术语,网关就是当前主机的默认路由。
-
-网关一般就是一台路由器,有点像“一个小区中的一个邮局”,小区里面的住户互相是知道怎么走,但是要向外地投递东西就不知道了,怎么办?把地址写好送到本小区的邮局就好了。
-
-那么,怎么区分是“本小区”和“外地小区”的呢?根据IP地址 + 掩码。如果是在一个范围内的,就是本小区(局域网内部),如果掩不住的,就是外地的。
-
-例如,你的机器的IP地址是:192.168.0.2/24,网关是192.168.0.1
-
-如果机器访问的IP地址范围是:192.168.0.1~192.168.0.254的,说明是一个小区的邻居,你的机器就直接发送了(和网关没任何关系)。如果你访问的IP地址不是这个范围的,则就投递到192.168.0.1上,让这台设备来转发。
-
-参考:https://www.zhihu.com/question/362842680/answer/951412213
-
-### 何为API网关
-
-假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。
-
-那么这里就会遇到一个问题,APP/Browser怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都分配一个独立的域名(`https://service.api.company.com`),但这种方式会有几个问题:
-
-- 每个业务都会需要鉴权、限流、权限校验等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
-- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名
-- 每上线一个新的服务,都需要运维参与,申请域名、配置Nginx等,当上线、下线服务器时,同样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名自己进行判断。
-- 另外还有一个问题,后端每个微服务可能采用了不同的协议,比如HTTP、AMQP、自定义TCP协议等,但是你不可能要求客户端去适配这么多种协议,这是一项非常有挑战的工作,项目会变的非常复杂且很难维护。
-
-更好的方式是采用API网关(也叫做服务网关),实现一个API网关**接管所有的入口流量**,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。
-
-
-
-通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。
-
-网关层通常以集群的形式存在。并在服务网关层前通常会加上Nginx 用来负载均衡。
-
-**服务网关基本功能**:
-
-
-
-- 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。
-- 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
-- API监控:监控经过网关的请求,以及网关本身的一些性能指标(gc等)
-- 限流:与监控配合,进行限流操作
-- API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志
-
-当然,网关实现这些功能,需要做高可用,否则网关很可能成为架构的瓶颈,最常用的网关组件Zuul、Nginx
-
-## 服务配置统一管理
-
-在微服务架构中,需要有统一管理配置文件的组件,例如:SpringCloud Config组件、阿里的Diamond、百度的Disconf、携程的Apollo等
-
-## 服务链路追踪
-
-在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与、参与顺序,是每个请求链路清晰可见,便于问题快速定位。
-
-常用链路追踪组件有Google的Dapper、Twitter 的Zipkin,以及阿里Eagleeye。
-
-
-
-## 微服务框架
-
-市面常用微服务框架有:Spring Cloud 、Dubbo 、kubernetes
-
-- 从功能模块上考虑,Dubbo缺少很多功能模块,例如网关、链路追踪等
-- 从学习成本上考虑,Dubbo 版本趋于稳定,稳定完善、可以即学即用,难度简单,Spring cloud 基于Spring Boot,需要先掌握Spring Boot ,例外Spring cloud 大多为英文文档,要求学习者有一定的英文阅读能力
-- 从开发风格考虑,Dubbo倾向于xml的配置方式,Spring cloud 基于Spring Boot ,采用基于注解和JavaBean配置方式的敏捷开发
-- 从开发速度上考虑,Spring cloud 具有更高的开发和部署速度
-- 从通信方式上考虑,Spring cloud 基于HTTP Restful 风格,服务于服务之间完全无关、无耦合。Dubbo 基于远程调用,对接口、平台和语言有强依赖性,如果需要实现跨平台,需要有额外的中间件。
-
-所以Dubbo专注于服务治理;Spring Cloud关注于微服务架构生态。
-
-## 其他
-
-### Spring Cloud基础知识
-
-[Spring Cloud基础知识](../framework/springcloud-overview.md)
-
-### 什么是Service Mesh?
-
-Service Mesh 是微服务时代的 TCP/IP 协议。
-
-参考:https://zhuanlan.zhihu.com/p/61901608
-
diff --git a/docs/distributed/4-distibuted-arch.md b/docs/distributed/4-distibuted-arch.md
deleted file mode 100644
index 30b0b32..0000000
--- a/docs/distributed/4-distibuted-arch.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# 分布式架构,微服务、限流、熔断....
-
-[分布式架构,微服务、限流、熔断....](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
\ No newline at end of file
diff --git a/docs/distributed/5-distributed-transaction.md b/docs/distributed/5-distributed-transaction.md
deleted file mode 100644
index bf61cb7..0000000
--- a/docs/distributed/5-distributed-transaction.md
+++ /dev/null
@@ -1,199 +0,0 @@
----
-sidebar: heading
----
-
-## 简介
-
-### 事务
-
-事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。事务应该具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。
-
-### 分布式事务
-
-分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务的特性。
-
-### 强一致性、弱一致性、最终一致性
-
-**强一致性**
-
-任何一次读都能读到某个数据的最近一次写的数据。系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。简言之,在任意时刻,所有节点中的数据是一样的。
-
-**弱一致性**
-
-数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。
-
-**最终一致性**
-
-不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。简单说,就是在一段时间后,节点间的数据会最终达到一致状态。
-
-由于分布式事务方案,无法做到完全的ACID的保证,没有一种完美的方案,能够解决掉所有业务问题。因此在实际应用中,会根据业务的不同特性,选择最适合的分布式事务方案。
-
-## 分布式事务的基础
-
-### CAP理论
-
-**Consistency**(一致性):数据一致更新,所有数据变动都是同步的(强一致性)。
-
-**Availability**(可用性):好的响应性能。
-
-**Partition tolerance**(分区容错性) :可靠性。
-
-定理:任何分布式系统**只可同时满足二点**,没法三者兼顾。
-
-CA系统(放弃P):指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性得到满足。
-
-CP系统(放弃A):如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。
-
-AP系统(放弃C):这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。
-
-### BASE理论
-
-BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展。
-
-1. 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
-2. 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
-3. 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。
-
-BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。
-
-## 分布式事务解决方案
-
-分布式事务的实现主要有以下 6 种方案:
-
-- 2PC 方案
-- TCC 方案
-- 本地消息表
-- MQ事务
-- Saga事务
-- 最大努力通知方案
-
-### 2PC方案
-
-2PC方案分为两阶段:
-
-第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
-
-第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
-
-优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。
-
-缺点:
-
-- 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
-- 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
-- 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
-
-总的来说,2PC方案比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。
-
-### TCC
-
-TCC 的全称是:`Try`、`Confirm`、`Cancel`。
-
-- **Try 阶段**:这个阶段说的是对各个服务的资源做检测以及对资源进行 **锁定或者预留**。
-- **Confirm 阶段**:这个阶段说的是在各个服务中执行实际的操作。
-- **Cancel 阶段**:如果任何一个服务的业务方法执行出错,那么这里就需要 **进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
--
-
-举个简单的例子如果你用100元买了一瓶水, Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。
-
-如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。
-
-如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)。
-
-这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个**事务回滚实际上是严重依赖于你自己写代码来回滚和补偿**了,会造成补偿代码巨大。
-
-### 本地消息表
-
-本地消息表的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。
-
-
-
-对于本地消息队列来说核心是把大事务转变为小事务。还是举上面用100元去买一瓶水的例子。
-
-1.当你扣钱的时候,你需要在你扣钱的服务器上新增加一个本地消息表,你需要把你扣钱和写入减去水的库存到本地消息表放入同一个事务(依靠数据库本地事务保证一致性。
-
-2.这个时候有个定时任务去轮询这个本地事务表,把没有发送的消息,扔给商品库存服务器,叫他减去水的库存,到达商品服务器之后这个时候得先写入这个服务器的事务表,然后进行扣减,扣减成功后,更新事务表中的状态。
-
-3.商品服务器通过定时任务扫描消息表或者直接通知扣钱服务器,扣钱服务器本地消息表进行状态更新。
-
-4.针对一些异常情况,定时扫描未成功处理的消息,进行重新发送,在商品服务器接到消息之后,首先判断是否是重复的,如果已经接收,在判断是否执行,如果执行在马上又进行通知事务,如果未执行,需要重新执行需要由业务保证幂等,也就是不会多扣一瓶水。
-
-本地消息队列是BASE理论,是最终一致模型,适用于对一致性要求不高的。实现这个模型时需要注意重试的幂等。
-
-### MQ事务
-
-基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。
-
-MQ事务方案整体流程和本地消息表的流程很相似,如下图:
-
-
-
-从上图可以看出和本地消息表方案唯一不同就是将本地消息表存在了MQ内部,而不是业务数据库中。
-
-那么MQ内部的处理尤为重要,下面主要基于 RocketMQ 4.3 之后的版本介绍 MQ 的分布式事务方案。
-
-在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ 的事务消息相对于普通 MQ提供了 2PC 的提交接口,方案如下:
-
-**正常情况:事务主动方发消息**
-
-
-
-这种情况下,事务主动方服务正常,没有发生故障,发消息流程如下:
-
-- 发送方向 MQ 服务端(MQ Server)发送 half 消息。
-- MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。
-- 发送方开始执行本地事务逻辑。
-- 发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。
-- MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。
-
-**异常情况:事务主动方消息恢复**
-
-
-
-在断网或者应用重启等异常情况下,图中 4 提交的二次确认超时未到达 MQ Server,此时处理逻辑如下:
-
-- MQ Server 对该消息发起消息回查。
-- 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
-- 发送方根据检查得到的本地事务的最终状态再次提交二次确认。
-- MQ Server基于 commit/rollback 对消息进行投递或者删除。
-
-**优点**
-
-相比本地消息表方案,MQ 事务方案优点是:
-
-- 消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
-- 吞吐量大于使用本地消息表方案。
-
-**缺点**
-
-- 一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。
-- 业务处理服务需要实现消息状态回查接口。
-
-### Saga事务
-
-Saga是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发Saga中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作。
-
-Saga的实现有很多种方式,其中最流行的两种方式是:
-
-- **基于事件的方式**。这种方式没有协调中心,整个模式的工作方式就像舞蹈一样,各个舞蹈演员按照预先编排的动作和走位各自表演,最终形成一只舞蹈。处于当前Saga下的各个服务,会产生某类事件,或者监听其它服务产生的事件并决定是否需要针对监听到的事件做出响应。
-- **基于命令的方式**。这种方式的工作形式就像一只乐队,由一个指挥家(协调中心)来协调大家的工作。协调中心来告诉Saga的参与方应该执行哪一个本地事务。
-
-### 最大努力通知方案
-
-最大努力通知也称为定期校对,是对MQ事务方案的进一步优化。它在事务主动方增加了消息校对的接口,如果事务被动方没有接收到消息,此时可以调用事务主动方提供的消息校对的接口主动获取。
-
-最大努力通知的整体流程如下图:
-
-
-
-在可靠消息事务中,事务主动方需要将消息发送出去,并且消息接收方成功接收,这种可靠性发送是由事务主动方保证的;
-
-但是最大努力通知,事务主动方尽最大努力(重试,轮询....)将事务发送给事务接收方,但是仍然存在消息接收不到,此时需要事务被动方主动调用事务主动方的消息校对接口查询业务消息并消费,这种通知的可靠性是由事务被动方保证的。
-
-最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。
-
-## 参考文章
-
-https://www.pdai.tech/md/arch/arch-z-transection.html
-
-https://juejin.cn/post/6844903647197806605#heading-15
diff --git "a/docs/distributed/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" "b/docs/distributed/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
deleted file mode 100644
index 5ecd5d1..0000000
--- "a/docs/distributed/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
+++ /dev/null
@@ -1,50 +0,0 @@
-一、背景
-基于RokcetMQ可以实现异步处理、流量削锋、业务解耦,通常是依赖RocketMQ的发布订阅模式。今天分享RocketMQ的新特性,基于Request/Reply模式来实现RPC的功能。该模式是在v4.6作为RocketMQ新特性引入,但在在v4.7.1上才比较完善。
-
-二、设计思路
-从整个数据流的角度上来说,发布/订阅模式中生产者和消费者之间是异步处理的,生产者负责把消息投递到RocketMQ上,而消费者负责处理数据,如果把生产者当做上游系统,消费者是下游系统,那么上下游系统之间是没有任何的状态交流的。而我们知道,RPC上下游系统之间是需要状态交互的,简单来说,要想实现RPC功能,在整个数据链路上,原先上下游系统之间是异步交互的模式,首先需要把异步模式换成同步模式。
-
-异步模式:
-
-把异步模式换成同步模式,需要在生产者发送消息到MQ之后,保持原有的状态,比如可以用一个Map集合去统一维护,等到消费者处理完数据返回响应后,再从Map集合中拿到对应的请求进行处理。其中涉及到怎么去标识一个请求,这里可以用UUID或者雪花id去标记。
-
-同步模式:
-
-RocketMQ整体的处理思路跟上面是类似的,DefaultMQProducerImpl#request负责RPC消息的下发,而DefaultMQPushConsumer中负责消息的消费。具体用法可以看RocketMQ源码example中的RPC部分。
-
-
-三、结构定义
-RocketMQ中是依赖于Message的Properties来区分不同的请求,在调用DefaultMQProducerImpl#request进行消息下发之间会先给消息设置不同的属性,通过属性来保证上下游之间的处理是同一个请求。
-
-设置的属性有:
-
-CORRELATION_ID:消息的标识Id,这里对应是一个UUID
-REPLY_TO_CLIENT:消息下发的客户端Id
-TTL:消息下发的超时时间,单位ms
-
-其实就类似于HTTP请求中的头部内容一样。
-
-之后还会校验一下消息中对应Topic的一个合法性。
-
-四、消息下发
-RocketMQ将下发的客户端封装成RequestResponseFuture,包含客户端Id,请求超时时间,同时根据客户端Id维护在ConcurrentHashMap,调用DefaultMQProducerImpl#sendDefaultImpl下发消息,根据下发消息的回调函数确认消息下发的状态。
-
-消息下发后会调用waitResponse,waitResponse调用CountDownLatch进入阻塞状态,等待消息消费之后的响应。
-
-CountDownLatch中的计数器是1,说明每个请求都会独立隔离阻塞。
-
-
-五、消息响应
-当服务端(消费者)收到消息处理完返回响应时,会调用ReplyMessageProcessor#pushReplyMessage封装响应的内容,处理响应的头部信息和返回body的参数,最终封装成一个PUSH_REPLY_MESSAGE_TO_CLIENT的请求命令发给客户端。
-
-客户端(生产者)收到请求后,会调用ClientRemotingProcessor#processRequest,判断是PUSH_REPLY_MESSAGE_TO_CLIENT命令会调用receiveReplyMessage,将接收到的数据封装成新的消息,接着调用响应处理的处理器。
-
-ClientRemotingProcessor#processReplyMessage中主要做的从消息中获取消息的Id,从ConcurrentHashMap中定位到具体的请求,将返回消息封装到RequestResponseFuture中,同时CountDownLatch的计数值减1,此时线程阻塞状态被释放,之后便将消息响应给到客户端。
-
-六、总结
-所以整体上看来,RocketMQ的Request/Reply模式,其实是利用客户端线程阻塞来换取请求异步变同步以及RocketMQ的回调机制从而间接的实现了RPC效果,但是相比直接RPC调用,数据的链路更长,性能肯定是会有损耗,但是请求会持久化,所以给了重复下发提供了可能。
-
-
-
-版权声明:本文为CSDN博主「林风自在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
-原文链接:https://blog.csdn.net/lveex/article/details/122514893
\ No newline at end of file
diff --git a/docs/framework/mybatis.md b/docs/framework/mybatis.md
index 2ec41a5..b21cb9d 100644
--- a/docs/framework/mybatis.md
+++ b/docs/framework/mybatis.md
@@ -1,7 +1,25 @@
---
sidebar: heading
+title: MyBatis常见面试题总结
+category: 框架
+tag:
+ - MyBatis
+head:
+ - - meta
+ - name: keywords
+ content: MyBatis面试题,Hibernate,Executor,MyBatis分页,MyBatis插件运行原理,MyBatis延迟加载,MyBatis预编译,一级缓存和二级缓存
+ - - meta
+ - name: description
+ content: 高质量的MyBatis常见知识点和面试题总结,让天下没有难背的八股文!
---
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
## Mybatis是什么?
- MyBatis框架是一个开源的数据持久层框架。
@@ -9,6 +27,17 @@ sidebar: heading
- MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件当中,实现SQL的灵活配置。
- 这样做的好处是将SQL与程序代码分离,可以在不修改代码的情况下,直接在配置文件当中修改SQL。
+## 为什么使用Mybatis代替JDBC?
+
+MyBatis 是一种优秀的 ORM(Object-Relational Mapping)框架,与 JDBC 相比,有以下几点优势:
+
+1. 简化了 JDBC 的繁琐操作:使用 JDBC 进行数据库操作需要编写大量的样板代码,如获取连接、创建 Statement/PreparedStatement,设置参数,处理结果集等。而使用 MyBatis 可以将这些操作封装起来,通过简单的配置文件和 SQL 语句就能完成数据库操作,从而大大简化了开发过程。
+2. 提高了 SQL 的可维护性:使用 JDBC 进行数据库操作,SQL 语句通常会散布在代码中的各个位置,当 SQL 语句需要修改时,需要找到所有使用该语句的地方进行修改,这非常不方便,也容易出错。而使用 MyBatis,SQL 语句都可以集中在配置文件中,可以更加方便地修改和维护,同时也提高了 SQL 语句的可读性。
+3. 支持动态 SQL:MyBatis 提供了强大的动态 SQL 功能,可以根据不同的条件生成不同的 SQL 语句,这对于复杂的查询操作非常有用。
+4. 易于集成:MyBatis 可以与 Spring 等流行的框架集成使用,可以通过 XML 或注解配置进行灵活的配置,同时 MyBatis 也提供了非常全面的文档和示例代码,学习和使用 MyBatis 非常方便。
+
+综上所述,使用 MyBatis 可以大大简化数据库操作的代码,提高 SQL 语句的可维护性和可读性,同时还提供了强大的动态 SQL 功能,易于集成使用。因此,相比于直接使用 JDBC,使用 MyBatis 更为便捷、高效和方便。
+
## **ORM是什么**
ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
@@ -89,7 +118,7 @@ Mybatis仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`Statemen
编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。
-## .Mybatis 是否支持延迟加载?
+## Mybatis 是否支持延迟加载?
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载`lazyLoadingEnabled=true|false`。
@@ -138,9 +167,13 @@ mybatis底层使用`PreparedStatement`,默认情况下,将对所有的 sql
缓存:合理使用缓存是优化中最常见的方法之一,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
+Mybatis里面设计了二级缓存来提升数据的检索效率,避免每次数据的访问都需要去查询数据库。
+
**一级缓存是SqlSession级别的缓存**:Mybatis对缓存提供支持,默认情况下只开启一级缓存,一级缓存作用范围为同一个SqlSession。在SQL和参数相同的情况下,我们使用同一个SqlSession对象调用同一个Mapper方法,往往只会执行一次SQL。因为在使用SqlSession第一次查询后,Mybatis会将结果放到缓存中,以后再次查询时,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession只会取出当前缓存的数据,不会再次发送SQL到数据库。若使用不同的SqlSession,因为不同的SqlSession是相互隔离的,不会使用一级缓存。
-**二级缓存是mapper级别的缓存**:可以使缓存在各个SqlSession之间共享。二级缓存默认不开启,需要在mybatis-config.xml开启二级缓存:
+**二级缓存是mapper级别的缓存**:可以使缓存在各个SqlSession之间共享。当多个用户在查询数据的时候,只要有任何一个SqlSession拿到了数据就会放入到二级缓存里面,其他的SqlSession就可以从二级缓存加载数据。
+
+二级缓存默认不开启,需要在mybatis-config.xml开启二级缓存:
```xml
diff --git a/docs/framework/spring.md b/docs/framework/spring.md
index 33ae081..4ea008b 100644
--- a/docs/framework/spring.md
+++ b/docs/framework/spring.md
@@ -1,8 +1,24 @@
---
sidebar: heading
+title: Spring常见面试题总结
+category: 框架
+tag:
+ - Spring
+head:
+ - - meta
+ - name: keywords
+ content: Spring面试题,Spring设计模式,Spring AOP,Spring IOC,Spring 动态代理,Bean生命周期,自动装配,Spring注解,Spring事务,Async注解
+ - - meta
+ - name: description
+ content: 高质量的Spring常见知识点和面试题总结,让天下没有难背的八股文!
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## Spring的优点
@@ -86,6 +102,18 @@ AOP有两种实现方式:静态代理和动态代理。
动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
+
## Spring AOP的实现原理
`Spring`的`AOP`实现原理其实很简单,就是通过**动态代理**实现的。如果我们为`Spring`的某个`bean`配置了切面,那么`Spring`在创建这个`bean`的时候,实际上创建的是这个`bean`的一个代理对象,我们后续对`bean`中方法的调用,实际上调用的是代理类重写的代理方法。而`Spring`的`AOP`使用了两种动态代理,分别是**JDK的动态代理**,以及**CGLib的动态代理**。
@@ -941,8 +969,34 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
2.调用本类的异步方法是不会起作用的。这种方式绕过了代理而直接调用了方法,@Async注解会失效。
+## 为什么 Spring和IDEA 都不推荐使用 @Autowired 注解?
+
+idea 在我们经常使用的`@Autowired` 注解上添加了警告。警告内容是: `Field injection is not recommended`, 译为: **不推荐使用属性注入**。
+
+Spring常用的注入方式有:属性注入, 构造方法注入, set 方法注入
+
+- 构造器注入:利用构造方法的参数注入依赖
+- set方法注入:调用setter的方法注入依赖
+- 属性注入:在字段上使用@Autowired/Resource注解
+
+其中,基于属性注入的方式,容易导致Spring 初始化失败。因为在Spring在初始化的时候,可能由于属性在被注入前就引用而导致空指针异常,进而导致容器初始化失败。
+
+如果可能的话,尽量使用构造器注入。Lombok提供了一个注解`@RequiredArgsConstructor`, 可以方便我们快速进行构造注入。
+
+@Autowired是属性注入,而且@Autowired默认是按照类型匹配(ByType),因此有可能会出现两个相同的类型bean,进而导致Spring 装配失败。
+
+如果要使用属性注入的话,可以使用 `@Resource` 代替 `@Autowired` 注解。@Resource默认是按照名称匹配(ByName),如果找不到则是ByType注入。另外,@Autowired是Spring提供的,@Resource是JSR-250提供的,是Java标准,我们使用的IoC容器会去兼容它,这样即使更换容器,也可以正常工作。
+> 最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+>
+> 
+>
+> 
+>
+> **200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+>
+> 备用链接:https://pan.quark.cn/s/3f1321952a16

diff --git a/docs/framework/springboot.md b/docs/framework/springboot.md
index 3f0e485..a0b52ad 100644
--- a/docs/framework/springboot.md
+++ b/docs/framework/springboot.md
@@ -1,27 +1,459 @@
-**Springboot重要知识点&高频面试题**是我的[知识星球](https://topjavaer.cn/zsxq/introduce.html)**内部专属资料**,已经整理到Java面试手册**完整版**。
+---
+sidebar: heading
+title: Springboot常见面试题总结
+category: 框架
+tag:
+ - SpringBoot
+head:
+ - - meta
+ - name: keywords
+ content: Spring Boot面试题,Spring Boot,自动配置,Spring Boot注解,Spring Boot多数据源
+ - - meta
+ - name: description
+ content: 高质量的Springboot常见知识点和面试题总结,让天下没有难背的八股文!
+---
-
+::: tip 这是一则或许对你有帮助的信息
-除了Java面试手册完整版之外,星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
-
+:::
-
+## Springboot的优点
-
+- 内置servlet容器,不需要在服务器部署 tomcat。只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目
+- SpringBoot提供了starter,把常用库聚合在一起,简化复杂的环境配置,快速搭建spring应用环境
+- 可以快速创建独立运行的spring项目,集成主流框架
+- 准生产环境的运行应用监控
-**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
+## Javaweb、spring、springmvc和springboot有什么区别,都是做什么用的?
-
+JavaWeb是 Java 语言的 Web 开发技术,主要用于开发 Web 应用程序,包括基于浏览器的客户端和基于服务器的 Web 服务器。
-
+Spring是一个轻量级的开源开发框架,主要用于管理 Java 应用程序中的组件和对象,并提供各种服务,如事务管理、安全控制、面向切面编程和远程访问等。它是一个综合性框架,可应用于所有类型的 Java 应用程序。
-另外星球还提供**简历指导、修改服务**,大彬已经帮**90**+个小伙伴修改了简历,相对还是比较有经验的。
+SpringMVC是 Spring 框架中的一个模块,用于开发 Web 应用程序并实现 MVC(模型-视图-控制器)设计模式,它将请求和响应分离,从而使得应用程序更加模块化、可扩展和易于维护。
-
+Spring Boot是基于 Spring 框架开发的用于开发 Web 应用程序的框架,它帮助开发人员快速搭建和配置一个独立的、可执行的、基于 Spring 的应用程序,从而减少了繁琐和重复的配置工作。
-
+综上所述,JavaWeb是基于 Java 语言的 Web 开发技术,而 Spring 是一个综合性的开发框架,SpringMVC用于开发 Web 应用程序实现 MVC 设计模式,而 Spring Boot 是基于 Spring 的 Web 应用程序开发框架。
-[知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
+## SpringBoot 中的 starter 到底是什么 ?
-
\ No newline at end of file
+starter提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。
+
+## 运行 SpringBoot 有哪几种方式?
+
+1. 打包用命令或者者放到容器中运行
+2. 用 Maven/Gradle 插件运行
+3. 直接执行 main 方法运行
+
+## SpringBoot 常用的 Starter 有哪些?
+
+1. spring-boot-starter-web :提供 Spring MVC + 内嵌的 Tomcat 。
+2. spring-boot-starter-data-jpa :提供 Spring JPA + Hibernate 。
+3. spring-boot-starter-data-Redis :提供 Redis 。
+4. mybatis-spring-boot-starter :提供 MyBatis 。
+
+## Spring Boot 的核心注解是哪个?
+
+启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
+
+- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
+- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
+- @ComponentScan:Spring组件扫描。
+
+## 有哪些常用的SpringBoot注解?
+
+- @SpringBootApplication。这个注解是Spring Boot最核心的注解,用在 Spring Boot的主类上,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力
+
+- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
+
+- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
+
+- @ComponentScan:Spring组件扫描。
+
+- @Repository:用于标注数据访问组件,即DAO组件。
+
+- @Service:一般用于修饰service层的组件
+
+- **@RestController**。用于标注控制层组件(如struts中的action),表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的合集。
+
+- **@ResponseBody**。表示该方法的返回结果直接写入HTTP response body中
+
+- **@Component**。泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
+
+- **@Bean**,相当于XML中的``,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
+
+- **@AutoWired**,byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
+
+- **@Qualifier**。当有多个同一类型的Bean时,可以用@Qualifier("name")来指定。与@Autowired配合使用
+
+- **@Resource(name="name",type="type")**。没有括号内内容的话,默认byName。与@Autowired干类似的事。
+
+- **@RequestMapping**
+
+ RequestMapping是一个用来处理请求地址映射的注解;提供路由信息,负责URL到Controller中的具体函数的映射,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
+
+- **@RequestParam**
+
+ 用在方法的参数前面。
+
+- ### @Scope
+
+ 用于声明一个Spring`Bean`实例的作用域
+
+- ### @Primary
+
+ 当同一个对象有多个实例时,优先选择该实例。
+
+- ### @PostConstruct
+
+ 用于修饰方法,当对象实例被创建并且依赖注入完成后执行,可用于对象实例的初始化操作。
+
+- ### @PreDestroy
+
+ 用于修饰方法,当对象实例将被Spring容器移除时执行,可用于对象实例持有资源的释放。
+
+- ### @EnableTransactionManagement
+
+ 启用Spring基于注解的事务管理功能,需要和`@Configuration`注解一起使用。
+
+- ### @Transactional
+
+ 表示方法和类需要开启事务,当作用与类上时,类中所有方法均会开启事务,当作用于方法上时,方法开启事务,方法上的注解无法被子类所继承。
+
+- ### @ControllerAdvice
+
+ 常与`@ExceptionHandler`注解一起使用,用于捕获全局异常,能作用于所有controller中。
+
+- ### @ExceptionHandler
+
+ 修饰方法时,表示该方法为处理全局异常的方法。
+
+## 自动配置原理
+
+SpringBoot实现自动配置原理图解:
+
+> 公众号【程序员大彬】,回复【自动配置】下载高清图片
+
+
+
+在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
+
+@SpringBootApplication是@Configuration、@EnableAutoConfiguration和@ComponentScan的组合。
+
+@Configuration表示该类是Java配置类。
+
+@ComponentScan开启自动扫描符合条件的bean(添加了@Controller、@Service等注解)。
+
+@EnableAutoConfiguration会根据类路径中的jar依赖为项目进行自动配置,比如添加了`spring-boot-starter-web`依赖,会自动添加Tomcat和Spring MVC的依赖,然后Spring Boot会对Tomcat和Spring MVC进行自动配置(spring.factories EnableAutoConfiguration配置了`WebMvcAutoConfiguration`)。
+
+```java
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@AutoConfigurationPackage
+@Import(EnableAutoConfigurationImportSelector.class)
+public @interface EnableAutoConfiguration {
+}
+```
+
+EnableAutoConfiguration主要由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)这两个注解组成的。
+
+@AutoConfigurationPackage用于将启动类所在的包里面的所有组件注册到spring容器。
+
+@Import 将EnableAutoConfigurationImportSelector注入到spring容器中,EnableAutoConfigurationImportSelector通过SpringFactoriesLoader从类路径下去读取META-INF/spring.factories文件信息,此文件中有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,定义了一组需要自动配置的bean。
+
+```properties
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
+org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
+org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
+org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
+org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
+```
+
+这些配置类不是都会被加载,会根据xxxAutoConfiguration上的@ConditionalOnClass等条件判断是否加载,符合条件才会将相应的组件被加载到spring容器。(比如mybatis-spring-boot-starter,会自动配置sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件)
+
+```java
+@Configuration
+@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
+ AnnotatedElement.class }) //类路径存在EnableAspectJAutoProxy等类文件,才会加载此配置类
+@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
+public class AopAutoConfiguration {
+
+ @Configuration
+ @EnableAspectJAutoProxy(proxyTargetClass = false)
+ @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
+ public static class JdkDynamicAutoProxyConfiguration {
+
+ }
+
+ @Configuration
+ @EnableAspectJAutoProxy(proxyTargetClass = true)
+ @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
+ public static class CglibAutoProxyConfiguration {
+
+ }
+
+}
+```
+
+全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的?
+
+@ConfigurationProperties的作用就是将配置文件的属性绑定到对应的bean上。全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties bean,通过这个 bean 获取相应的属性(serverProperties.getPort())。
+
+```java
+//server.port = 8080
+@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
+public class ServerProperties {
+ private Integer port;
+ private InetAddress address;
+
+ @NestedConfigurationProperty
+ private final ErrorProperties error = new ErrorProperties();
+ private Boolean useForwardHeaders;
+ private String serverHeader;
+ //...
+}
+```
+
+## 实现自动配置
+
+实现当某个类存在时,自动配置这个类的bean,并且可以在application.properties中配置bean的属性。
+
+(1)新建Maven项目spring-boot-starter-hello,修改pom.xml如下:
+
+```xml
+
+
+ 4.0.0
+
+ com.tyson
+ spring-boot-starter-hello
+ 1.0-SNAPSHOT
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ 1.3.0.M1
+
+
+ junit
+ junit
+ 3.8.1
+
+
+
+
+```
+
+(2)属性配置
+
+```java
+public class HelloService {
+ private String msg;
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public String sayHello() {
+ return "hello" + msg;
+
+ }
+}
+
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix="hello")
+public class HelloServiceProperties {
+ private static final String MSG = "world";
+ private String msg = MSG;
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+}
+```
+
+(3)自动配置类
+
+```java
+import com.tyson.service.HelloService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties(HelloServiceProperties.class) //1
+@ConditionalOnClass(HelloService.class) //2
+@ConditionalOnProperty(prefix="hello", value = "enabled", matchIfMissing = true) //3
+public class HelloServiceAutoConfiguration {
+
+ @Autowired
+ private HelloServiceProperties helloServiceProperties;
+
+ @Bean
+ @ConditionalOnMissingBean(HelloService.class) //4
+ public HelloService helloService() {
+ HelloService helloService = new HelloService();
+ helloService.setMsg(helloServiceProperties.getMsg());
+ return helloService;
+ }
+}
+```
+
+1. @EnableConfigurationProperties 注解开启属性注入,将带有@ConfigurationProperties 注解的类注入为Spring 容器的 Bean。
+
+2. 当 HelloService 在类路径的条件下。
+3. 当设置 hello=enabled 的情况下,如果没有设置则默认为 true,即条件符合。
+4. 当容器没有这个 Bean 的时候。
+
+(4)注册配置
+
+想要自动配置生效,需要注册自动配置类。在 src/main/resources 下新建 META-INF/spring.factories。添加以下内容:
+
+```factories
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.tyson.config.HelloServiceAutoConfiguration
+```
+
+"\\"是为了换行后仍然能读到属性。若有多个自动配置,则用逗号隔开。
+
+(5)使用starter
+
+在 Spring Boot 项目的 pom.xml 中添加:
+
+```xml
+
+ com.tyson
+ spring-boot-starter-hello
+ 1.0-SNAPSHOT
+
+```
+
+运行类如下:
+
+```java
+import com.tyson.service.HelloService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@SpringBootApplication
+public class SpringbootDemoApplication {
+
+ @Autowired
+ public HelloService helloService;
+
+ @RequestMapping("/")
+ public String index() {
+ return helloService.getMsg();
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringbootDemoApplication.class, args);
+ }
+
+}
+```
+
+在项目中没有配置 HelloService bean,但是我们可以注入这个bean,这是通过自动配置实现的。
+
+在 application.properties 中添加 debug 属性,运行配置类,在控制台可以看到:
+
+```java
+ HelloServiceAutoConfiguration matched:
+ - @ConditionalOnClass found required class 'com.tyson.service.HelloService' (OnClassCondition)
+ - @ConditionalOnProperty (hello.enabled) matched (OnPropertyCondition)
+
+ HelloServiceAutoConfiguration#helloService matched:
+ - @ConditionalOnMissingBean (types: com.tyson.service.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)
+```
+
+可以在 application.properties 中配置 msg 的内容:
+
+```properties
+hello.msg=大彬
+```
+
+## @Value注解的原理
+
+@Value的解析就是在bean初始化阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类`AutowiredAnnotationBeanPostProcessor`为bean中的@Autowired和@Value注解的注入功能提供支持。
+
+## Spring Boot 需要独立的容器运行吗?
+
+不需要,内置了 Tomcat/ Jetty 等容器。
+
+## Spring Boot 支持哪些日志框架?
+
+Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架,但是不管是那种日志框架他都支持将配置文件输出到控制台或者文件中。
+
+## YAML 配置的优势在哪里 ?
+
+YAML 配置和传统的 properties 配置相比之下,有这些优势:
+
+- 配置有序
+- 简洁明了,支持数组,数组中的元素可以是基本数据类型也可以是对象
+
+缺点就是不支持 @PropertySource 注解导入自定义的 YAML 配置。
+
+## 什么是 Spring Profiles?
+
+在项目的开发中,有些配置文件在开发、测试或者生产等不同环境中可能是不同的,例如数据库连接、redis的配置等等。那我们如何在不同环境中自动实现配置的切换呢?Spring给我们提供了profiles机制给我们提供的就是来回切换配置文件的功能
+
+Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。假设我们的要求是 Swagger 文档仅适用于 QA 环境,并且禁用所有其他文档。这可以使用配置文件来完成。Spring Boot 使得使用配置文件非常简单。
+
+## SpringBoot多数据源事务如何管理
+
+第一种方式是在service层的@TransactionManager中使用transactionManager指定DataSourceConfig中配置的事务。
+
+第二种是使用jta-atomikos实现分布式事务管理。
+
+## spring-boot-starter-parent 有什么用 ?
+
+新创建一个 Spring Boot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:
+
+1. 定义了 Java 编译版本。
+2. 使用 UTF-8 格式编码。
+3. 执行打包操作的配置。
+4. 自动化的资源过滤。
+5. 自动化的插件配置。
+6. 针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。
+
+## Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
+
+- Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 `java -jar xxx.jar` 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
+- Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 `\BOOT-INF\classes` 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
+
+## SpringBoot多数据源拆分的思路
+
+先在properties配置文件中配置两个数据源,创建分包mapper,使用@ConfigurationProperties读取properties中的配置,使用@MapperScan注册到对应的mapper包中 。
+
+
+
+
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、打卡学习,**大学自习室的氛围**,一起蜕变成长
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/framework/springboot/springboot-dev-tools.md b/docs/framework/springboot/springboot-dev-tools.md
new file mode 100644
index 0000000..f87a182
--- /dev/null
+++ b/docs/framework/springboot/springboot-dev-tools.md
@@ -0,0 +1,164 @@
+# SpringBoot 三大开发工具,你都用过么?
+
+## 一、SpringBoot Dedevtools
+
+他是一个让SpringBoot支持热部署的工具,下面是引用的方法
+
+要么在创建项目的时候直接勾选下面的配置:
+
+
+
+要么给springBoot项目添加下面的依赖:
+
+```xml
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+复制代码
+```
+
+- idea修改完代码后再按下 ctrl + f9 使其重新编译一下,即完成了热部署功能
+- eclipse是按ctrl + s保存 即可自动编译
+
+如果你想一修改代码就自动重新编译,无需按ctrl+f9。只需要下面的操作:
+
+### 1.在idea的setting中把下面的勾都打上
+
+
+
+### 2.进入pom.xml,在build的反标签后给个光标,然后按Alt+Shift+ctrl+/
+
+
+
+### 3.然后勾选下面的东西,接着重启idea即可
+
+
+
+## 二、Lombok
+
+Lombok是简化JavaBean开发的工具,让开发者省去构造器,getter,setter的书写。
+
+在项目初始化时勾选下面的配置,即可使用Lombok
+
+
+
+或者在项目中导入下面的依赖:
+
+```xml
+
+ org.projectlombok
+ lombok
+ true
+
+复制代码
+```
+
+使用时,idea还需要下载下面的插件:
+
+
+
+下面的使用的例子
+
+```kotlin
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor//全参构造器
+@NoArgsConstructor//无参构造器
+@Data//getter + setter
+public class User {
+ private Long id;
+ private String name;
+ private Integer age;
+ private String email;
+}
+复制代码
+```
+
+### 三、Spring Configuration Processor
+
+该工具是给实体类的属性注入开启提示,自我感觉该工具意义不是特别大!
+
+因为SpringBoot存在属性注入,比如下面的实体类:
+
+```typescript
+package org.lzl.HelloWorld.entity;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Lenovo
+ *
+ */
+@Component
+@ConfigurationProperties(prefix = "mypet")
+public class Pet {
+ private String nickName;
+ private String strain;
+ public String getNickName() {
+ return nickName;
+ }
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+ public String getStrain() {
+ return strain;
+ }
+ public void setStrain(String strain) {
+ this.strain = strain;
+ }
+ @Override
+ public String toString() {
+ return "Pet [nickName=" + nickName + ", strain=" + strain + "]";
+ }
+
+
+}
+复制代码
+```
+
+想要在`application.properties`和`application.yml`中给mypet注入属性,却没有任何的提示,为了解决这一问题,我们在创建SpringBoot的时候勾选下面的场景:
+
+
+
+或者直接在项目中添加下面的依赖:
+
+```xml
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+复制代码
+```
+
+并在build的标签中排除对该工具的打包:(减少打成jar包的大小)
+
+```xml
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+
+
+
+
+
+
+```
+
+
+
+> 原文:blog.csdn.net/MoastAll/article/details/108237154
\ No newline at end of file
diff --git a/docs/framework/springcloud-interview.md b/docs/framework/springcloud-interview.md
index b2ee2a9..f95f36b 100644
--- a/docs/framework/springcloud-interview.md
+++ b/docs/framework/springcloud-interview.md
@@ -1,27 +1,91 @@
---
sidebar: heading
+title: Spring Cloud常见面试题总结
+category: 框架
+tag:
+ - Spring Cloud
+head:
+ - - meta
+ - name: keywords
+ content: Spring Cloud面试题,微服务,Spring Cloud优势,服务熔断,Eureka,Ribbon,Feign,Spring Cloud核心组件,Spring Cloud Bus,Spring Cloud Config,Spring Cloud Gateway
+ - - meta
+ - name: description
+ content: Spring Cloud常见知识点和面试题总结,让天下没有难背的八股文!
---
-今天给大家分享SpringCloud高频面试题。
-
# Spring Cloud核心知识总结
-下面是一张Spring Cloud核心组件关系图:
-
-
+::: tip 这是一则或许对你有帮助的信息
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
-从这张图中,其实我们是可以获取很多信息的,希望大家细细品尝。
+:::
-话不多说,我们直接开始 Spring Cloud 连环炮。
+## 更新记录
+- 2024.5.15,完善[Spring、SpringMVC、Springboot、 Springcloud 的区别是什么?](##Spring、SpringMVC、Springboot、 Springcloud 的区别是什么?)
## 1、什么是Spring Cloud ?
Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成。Spring cloud Task,一个生命周期短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。
+
+
+Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成。Spring cloud Task,一个生命周期短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。
+
+## Spring、SpringMVC、Springboot、 Springcloud 的区别是什么?
+
+### Spring
+
+Spring是一个生态体系(也可以说是技术体系),是集大成者,它包含了Spring Framework、Spring Boot、Spring Cloud等。**它是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架**,为开发者提供了一个简易的开发方式。
+
+Spring的核心特性思想之一IOC,它实现了容器对Bean对象的管理、降低组件耦合,使各层服务解耦。
+
+Spring的另一个核心特性就是AOP,面向切面编程。面向切面编程需要将程序逻辑分解为称为所谓关注点的不同部分。跨越应用程序多个点的功能称为跨领域问题,这些跨领域问题在概念上与应用程序的业务逻辑分离。有许多常见的例子,如日志记录,声明式事务,安全性,缓存等。
+
+**如果说IOC依赖注入可以帮助我们将应用程序对象相互分离,那么AOP可以帮助我们将交叉问题与它们所影响的对象分离。二者目的都是使服务解耦,使开发简易。**
+
+当然,除了Spring 的两大核心功能,还有如下这些,如:
+
+- Spring JDBC
+- Spring MVC
+- Spring ORM
+- Spring Test
+
+### SpringMVC
+
+Spring与MVC可以更好地解释什么是SpringMVC,MVC为现代web项目开发的一种很常见的模式,简言之C(控制器)将V(视图、用户客户端)与M(模块,业务)分开构成了MVC ,业内常见的MVC模式的开发框架有Struts。
+
+Spring MVC是Spring的一部分,主要用于开发WEB应用和网络接口,它是Spring的一个模块,通过DispatcherServlet, ModelAndView 和View Resolver,让应用开发变得很容易。
+
+### SpringBoot
+
+SpringBoot是一套整合了框架的框架。
+
+它的初衷:解决Spring框架配置文件的繁琐、搭建服务的复杂性。
+
+它的设计理念:**约定优于配置**(convention over configuration)。
+
+基于此理念实现了**自动配置**,且降低项目搭建的复杂度。
+
+搭建一个接口服务,通过SpringBoot几行代码即可实现。基于Spring Boot,不是说原来的配置没有了,而是Spring Boot有一套默认配置,我们可以把它看做比较通用的约定,而Spring Boot遵循的是**约定优于配置**原则,同时,如果你需要使用到Spring以往提供的各种复杂但功能强大的配置功能,Spring Boot一样支持。
+
+在Spring Boot中,你会发现引入的所有包都是starter形式,如:
+
+- spring-boot-starter-web-services,针对SOAP Web Services
+- spring-boot-starter-web,针对Web应用与网络接口
+- spring-boot-starter-jdbc,针对JDBC
+- spring-boot-starter-cache,针对缓存支持
+
+Spring Boot是基于 Spring 框架开发的用于开发 Web 应用程序的框架,它帮助开发人员快速搭建和配置一个独立的、可执行的、基于 Spring 的应用程序,从而减少了繁琐和重复的配置工作。
+
+### Spring Cloud
+
+Spring Cloud事实上是一整套基于Spring Boot的微服务解决方案。它为开发者提供了很多工具,用于快速构建分布式系统的一些通用模式,例如:配置管理、注册中心、服务发现、限流、网关、链路追踪等。Spring Boot是build anything,而Spring Cloud是coordinate anything,Spring Cloud的每一个微服务解决方案都是基于Spring Boot构建的。
+
## 2、什么是微服务?
微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分为一组小的服务,每个服务运行在其独立的自己的进程中,服务之间相互协调、互相配合,为用户提供最终价值。服务之间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API),每个服务都围绕着具体的业务进行构建,并且能够被独立的构建在生产环境、类生产环境等。另外,应避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
@@ -182,7 +246,7 @@ Spring Boot是Spring推出用于解决传统框架配置文件冗余,装配组
## 18、说说微服务之间是如何独立通讯的?
-#### 远程过程调用(Remote Procedure Invocation)
+**远程过程调用(Remote Procedure Invocation)**
也就是我们常说的服务的注册与发现,直接通过远程过程调用来访问别的service。
@@ -190,7 +254,7 @@ Spring Boot是Spring推出用于解决传统框架配置文件冗余,装配组
**缺点**:只支持请求/响应的模式,不支持别的,比如通知、请求/异步响应、发布/订阅、发布/异步响应,降低了可用性,因为客户端和服务端在请求过程中必须都是可用的。
-#### 消息
+**消息**
使用异步消息来做服务间通信。服务间通过消息管道来交换消息,从而通信。
@@ -246,4 +310,16 @@ Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代
使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由RouteLocatorBuilder可以让你添加各种predicates和filters,predicates断言的意思,顾名思义就是根据具体的请求的规则,由具体的route去处理,filters是各种过滤器,用来对请求做各种判断和修改。
-> 参考:http://1pgqu.cn/M0NZo
\ No newline at end of file
+## Spring Cloud各个微服务之间为什么要用http交互?难道不慢吗?
+
+Spring Cloud是一个为分布式微服务架构构建应用程序的开发工具箱,是Spring Boot的扩展,通过各种微服务组件的集成,极大地简化了微服务应用程序的构建和开发。在分布式系统中,各个微服务之间的通信是非常重要的,而HTTP作为通信协议具有普遍性和可扩展性,是Spring Cloud微服务架构中主流的通信方式。
+
+尽管使用HTTP作为微服务之间的通信协议存在一定的网络开销,但是这种不可避免的网络开销远低于我们所能得到的好处。使用HTTP通信可以实现松耦合和异步通信,微服务之间可以彼此独立地进行开发和测试,单个微服务的故障不会影响整个系统的运行,也可以支持各种不同的技术栈之间的互操作性。
+
+另外,使用HTTP作为通信协议还具有优秀的可扩展性。HTTP协议定义了不同的请求方法(例如 GET、POST、DELETE 等),不同请求方法的扩展格式也很灵活,可以用来传递各种类型的数据和格式,同时HTTP协议支持缓存,减少重复性的数据传输和带宽开销。
+
+当然,为了提高微服务之间的通信效率,我们也可以通过一些优化手段来减少HTTP协议的网络开销。例如,使用数据压缩和缓存技术来压缩和缓存请求和响应,减少网络数据传输量和响应时间;使用负载均衡技术来合理地分配请求和响应,避免单个微服务出现性能瓶颈;使用高速缓存技术来缓存请求和响应,避免重复的请求和响应等等。
+
+因此,Spring Cloud各个微服务之间使用HTTP交互是一个比较成熟的选择。虽然它可能存在一些网络开销,但是在实际应用中,这种开销是可以优化和控制的,甚至可以提高系统的可扩展性和可靠性。
+
+> 参考:http://1pgqu.cn/M0NZo
diff --git a/docs/framework/springmvc.md b/docs/framework/springmvc.md
index 68e8b3d..6bf2369 100644
--- a/docs/framework/springmvc.md
+++ b/docs/framework/springmvc.md
@@ -1,188 +1,50 @@
---
sidebar: heading
+title: Spring MVC常见面试题总结
+category: 框架
+tag:
+ - Spring MVC
+head:
+ - - meta
+ - name: keywords
+ content: Spring MVC面试题,MVC模式,Spring MVC和Struts,Spring MVC工作原理,Spring MVC常用注解,Spring MVC异常处理,Spring MVC拦截器,REST
+ - - meta
+ - name: description
+ content: 高质量的Spring MVC常见知识点和面试题总结,让天下没有难背的八股文!
---
-## 说说你对 SpringMVC 的理解
-SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块。
-它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
+**Spring MVC高频面试题**是我的[知识星球](https://topjavaer.cn/zsxq/introduce.html)**内部专属资料**,已经整理到**Java面试手册完整版**。
-## 什么是MVC模式?
+
-MVC的全名是`Model View Controller`,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。
+如果你正在打算准备跳槽、面试,星球还提供**简历指导、修改服务**,大彬已经帮**120**+个小伙伴修改了简历,相对还是比较有经验的。
-View,视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。
+
-model,模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
+
-controller,控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
+另外星球也提供**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
-## SpringMVC 有哪些优点?
+
-1. 与 Spring 集成使用非常方便,生态好。
-2. 配置简单,快速上手。
-3. 支持 RESTful 风格。
-4. 支持各种视图技术,支持各种请求资源映射策略。
+
-## Spring MVC和Struts的区别
+
-1. Spring MVC是基于方法开发,Struts2是基于类开发的。
- - Spring MVC会将用户请求的URL路径信息与Controller的某个方法进行映射,所有请求参数会注入到对应方法的形参上,生成Handler对象,对象中只有一个方法;
- - Struts每处理一次请求都会实例一个Action,Action类的所有方法使用的请求参数都是Action类中的成员变量,随着方法增多,整个Action也会变得混乱。
-2. Spring MVC支持单例开发模式,Struts只能使用多例
+星球还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
- - Struts由于只能通过类的成员变量接收参数,故只能使用多例。
-3. Struts2 的核心是基于一个Filter即StrutsPreparedAndExcuteFilter,Spring MVC的核心是基于一个Servlet即DispatcherServlet(前端控制器)。
-4. Struts处理速度稍微比Spring MVC慢,Struts使用了Struts标签,加载数据较慢。
+
-## Spring MVC的工作原理
+
-Spring MVC的工作原理如下:
+
-1. DispatcherServlet 接收用户的请求
-2. 找到用于处理request的 handler 和 Interceptors,构造成 HandlerExecutionChain 执行链
-3. 找到 handler 相对应的 HandlerAdapter
-4. 执行所有注册拦截器的preHandler方法
-5. 调用 HandlerAdapter 的 handle() 方法处理请求,返回 ModelAndView
-6. 倒序执行所有注册拦截器的postHandler方法
-7. 请求视图解析和视图渲染
+怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)?
-
+**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。
-## Spring MVC的主要组件?
+随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。
-- 前端控制器(DispatcherServlet):接收用户请求,给用户返回结果。
-- 处理器映射器(HandlerMapping):根据请求的url路径,通过注解或者xml配置,寻找匹配的Handler。
-- 处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的方法处理请求。
-- 处理器(Handler):执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。
-- 视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。
-- 视图(View):接口类,实现类可支持不同的View类型(JSP、FreeMarker、Excel等)。
-
-## Spring MVC的常用注解由有哪些?
-- @Controller:用于标识此类的实例是一个控制器。
-- @RequestMapping:映射Web请求(访问路径和参数)。
-- @ResponseBody:注解返回数据而不是返回页面
-- @RequestBody:注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。
-- @PathVariable:获得URL中路径变量中的值
-- @RestController:@Controller+@ResponseBody
-- @ExceptionHandler标识一个方法为全局异常处理的方法。
-
-## @Controller 注解有什么用?
-
-`@Controller` 注解标记一个类为 Spring Web MVC 控制器。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 `@RequestMapping` 注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象,在上面的 HandlerMapping 和 HandlerAdapter组件中讲到过。
-
-当然,除了添加 `@Controller` 注解这种方式以外,你还可以实现 Spring MVC 提供的 `Controller` 或者 `HttpRequestHandler` 接口,对应的实现类也会被作为一个处理器对象
-
-## @RequestMapping 注解有什么用?
-
-`@RequestMapping` 注解,用于配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀。
-
-## @RestController 和 @Controller 有什么区别?
-
-`@RestController` 注解,在 `@Controller` 基础上,增加了 `@ResponseBody` 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回 JSON 数据格式。
-
-## @RequestMapping 和 @GetMapping 注解有什么不同?
-
-1. `@RequestMapping`:可注解在类和方法上;`@GetMapping` 仅可注册在方法上
-2. `@RequestMapping`:可进行 GET、POST、PUT、DELETE 等请求方法;`@GetMapping` 是 `@RequestMapping` 的 GET 请求方法的特例。
-
-## @RequestParam 和 @PathVariable 两个注解的区别
-
-两个注解都用于方法参数,获取参数值的方式不同,`@RequestParam` 注解的参数从请求携带的参数中获取,而 `@PathVariable` 注解从请求的 URI 中获取
-
-## @RequestBody和@RequestParam的区别
-
-@RequestBody一般处理的是在ajax请求中声明contentType: "application/json; charset=utf-8"时候。也就是json数据或者xml数据。
-
-@RequestParam一般就是在ajax里面没有声明contentType的时候,为默认的`x-www-form-urlencoded`格式时。
-
-## Spring MVC的异常处理
-
-可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。
-
-- 使用系统定义好的异常处理器 SimpleMappingExceptionResolver
-- 使用自定义异常处理器
-- 使用异常处理注解
-
-## SpringMVC 用什么对象从后台向前台传递数据的?
-
-1. 将数据绑定到 request;
-2. 返回 ModelAndView;
-3. 通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前端就可以通过el表达式拿到;
-4. 绑定数据到 Session中。
-
-## SpringMvc的Controller是不是单例模式?
-
-单例模式。在多线程访问的时候有线程安全问题,解决方案是在控制器里面不要写可变状态量,如果需要使用这些可变状态,可以使用ThreadLocal,为每个线程单独生成一份变量副本,独立操作,互不影响。
-
-## 介绍下 Spring MVC 拦截器?
-
-Spring MVC 拦截器对应HandlerInterceor接口,该接口位于org.springframework.web.servlet的包中,定义了三个方法,若要实现该接口,就要实现其三个方法:
-
-1. **前置处理(preHandle()方法)**:该方法在执行控制器方法之前执行。返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。
-2. **后置处理(postHandle()方法)**:该方法在执行控制器方法调用之后,且在返回ModelAndView之前执行。由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图,可通过此方法对请求域中的模型和视图做进一步的修改。
-3. **已完成处理(afterCompletion()方法)**:该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。
-
-可以通过拦截器进行权限检验,参数校验,记录日志等操作
-
-## SpringMvc怎么配置拦截器?
-
-有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可:
-
-```java
-
-
-
-
-
-
-
-
-
-
-```
-
-## Spring MVC 的拦截器和 Filter 过滤器有什么差别?
-
-有以下几点:
-
-- **功能相同**:拦截器和 Filter 都能实现相应的功能
-- **容器不同**:拦截器构建在 Spring MVC 体系中;Filter 构建在 Servlet 容器之上
-- **使用便利性不同**:拦截器提供了三个方法,分别在不同的时机执行;过滤器仅提供一个方法
-
-## 什么是REST?
-
-REST,英文全称,Resource Representational State Transfer,对资源的访问状态的变化通过url的变化表述出来。
-
-Resource:**资源**。资源是REST架构或者说整个网络处理的核心。
-
-Representational:**某种表现形式**,比如用JSON,XML,JPEG等。
-
-State Transfer:**状态变化**。通过HTTP method实现。
-
-REST描述的是在网络中client和server的一种交互形式。用大白话来说,就是**通过URL就知道要什么资源,通过HTTP method就知道要干什么,通过HTTP status code就知道结果如何**。
-
-举个例子:
-
-```java
-GET /tasks 获取所有任务
-POST /tasks 创建新任务
-GET /tasks/{id} 通过任务id获取任务
-PUT /tasks/{id} 更新任务
-DELETE /tasks/{id} 删除任务
-```
-
-GET代表获取一个资源,POST代表添加一个资源,PUT代表修改一个资源,DELETE代表删除一个资源。
-
-server提供的RESTful API中,URL中只使用名词来指定资源,原则上不使用动词。用`HTTP Status Code`传递server的状态信息。比如最常用的 200 表示成功,500 表示Server内部错误等。
-
-## 使用REST有什么优势呢?
-
-第一,**风格统一**了,不会出现`delUser/deleteUser/removeUser`各种命名的代码了。
-
-第二,**面向资源**,一目了然,具有自解释性。
-
-第三,**充分利用 HTTP 协议本身语义**。
-
-
+
\ No newline at end of file
diff --git a/docs/interview/concurrent/1-forbid-default-executor.md b/docs/interview/concurrent/1-forbid-default-executor.md
index c413f09..c7b313d 100644
--- a/docs/interview/concurrent/1-forbid-default-executor.md
+++ b/docs/interview/concurrent/1-forbid-default-executor.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 为什么阿里禁止使用Java内置线程池?
+category: Java
+tag:
+ - Java并发
+head:
+ - - meta
+ - name: keywords
+ content: Java并发,多线程,线程池,为什么禁止使用Java内置线程池
+ - - meta
+ - name: description
+ content: 高质量的Java并发常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
## 为什么阿里禁止使用Java内置线程池?
首先要了解一下线程池 ThreadPoolExecutor 的参数及其作用。
@@ -40,4 +55,4 @@ ThreadPoolExecutor有以下这些参数。
**手册获取方式**:微信搜索「**程序员大彬**」或者扫描下面的二维码,关注后发送关键字「**手册**」就可以找到下载链接了(**无套路,无解压密码**)。
-
\ No newline at end of file
+
diff --git a/docs/interview/java/1-create-object.md b/docs/interview/java/1-create-object.md
index fc88db5..144141b 100644
--- a/docs/interview/java/1-create-object.md
+++ b/docs/interview/java/1-create-object.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Java创建对象有几种方式?
+category: Java
+tag:
+ - Java基础
+head:
+ - - meta
+ - name: keywords
+ content: java创建对象的方式
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
## Java创建对象有几种方式?
Java创建对象有以下几种方式:
diff --git a/docs/java/basic/reflect-affect-permance.md b/docs/java/basic/reflect-affect-permance.md
index c8ca558..1ef95a1 100644
--- a/docs/java/basic/reflect-affect-permance.md
+++ b/docs/java/basic/reflect-affect-permance.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 反射是怎么影响性能的?
+category: Java
+tag:
+ - Java基础
+head:
+ - - meta
+ - name: keywords
+ content: java反射
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
今天来聊聊反射的性能问题。反射具体是怎么影响性能的?
# 01 反射真的存在性能问题吗?
diff --git a/docs/java/basic/serialization.md b/docs/java/basic/serialization.md
index d4a5e2b..9f3381f 100644
--- a/docs/java/basic/serialization.md
+++ b/docs/java/basic/serialization.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 为什么要序列化?
+category: Java
+tag:
+ - Java基础
+head:
+ - - meta
+ - name: keywords
+ content: 序列化
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
> 本文转自爱笑的架构师
凡事都要问为什么,在讲解序列化概念和原理前,我们先来了解一下为什么需要序列化。
@@ -135,4 +150,4 @@ JSON 序列化常见的框架有:
(5)序列化技术的选型
-选型最重要的就是要考虑这三个方面:**协议是否支持跨平台**、**序列化的速度**、**序列化生成的体积**。
\ No newline at end of file
+选型最重要的就是要考虑这三个方面:**协议是否支持跨平台**、**序列化的速度**、**序列化生成的体积**。
diff --git a/docs/java/java-basic.md b/docs/java/java-basic.md
index 82984a6..ad1b6a4 100644
--- a/docs/java/java-basic.md
+++ b/docs/java/java-basic.md
@@ -1,26 +1,24 @@
---
sidebar: heading
-
+title: Java基础常见面试题总结
+category: Java
+tag:
+ - Java基础
+head:
+ - - meta
+ - name: keywords
+ content: JVM,JDK,JRE,面向对象,Java 基本数据类型,装箱和拆箱,Java数组,值传递和引用传递,String,深拷贝,final关键字,同步和异步,Java8新特性,序列化和反序列化,反射,泛型
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
---
+::: tip 这是一则或许对你有帮助的信息
-> 欢迎加入[大彬的学习圈](https://topjavaer.cn/zsxq/introduce.html),学习圈整理了最新的**Java面试手册完整版**(本网站的面试题补充版,更全面),还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
->
-> 
->
-> 
->
-> 
->
-> **专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
->
-> 
->
-> 
->
-> [大彬的学习圈](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
->
-> 
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## Java的特点
@@ -97,6 +95,19 @@ JRE是Java的运行环境,并不是一个开发环境,所以没有包含任

+
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
+
## Java程序是编译执行还是解释执行?
先看看什么是编译型语言和解释型语言。
@@ -276,6 +287,8 @@ Integer x = 1; // 装箱 调⽤ Integer.valueOf(1)
int y = x; // 拆箱 调⽤了 X.intValue()
```
+## 两个Integer 用== 比较不相等的原因
+
下面看一道常见的面试题:
```java
@@ -295,7 +308,7 @@ true
false
```
-为什么第三个输出是false?看看 Integer 类的源码就知道啦。
+为什么第二个输出是false?看看 Integer 类的源码就知道啦。
```java
public static Integer valueOf(int i) {
@@ -305,7 +318,7 @@ public static Integer valueOf(int i) {
}
```
-`Integer c = 200;` 会调用 调⽤`Integer.valueOf(200)`。而从Integer的valueOf()源码可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache。
+`Integer c = 200;` 会调用`Integer.valueOf(200)`。而从Integer的valueOf()源码可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache。
```java
private static class IntegerCache {
@@ -1055,6 +1068,15 @@ public class B extends A {
- finally 是异常处理语句结构的一部分,一般以`try-catch-finally`出现,`finally`代码块表示总是被执行。
- finalize 是Object类的一个方法,该方法一般由垃圾回收器来调用,当我们调用`System.gc()`方法的时候,由垃圾回收器调用`finalize()`方法,回收垃圾,JVM并不保证此方法总被调用。
+## Java中的finally一定会被执行吗?
+
+答案是不一定。
+
+有以下两种情况finally不会被执行:
+
+- 程序未执行到try代码块
+- 如果当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。
+
## final关键字的作用?
- final 修饰的类不能被继承。
@@ -1502,21 +1524,19 @@ server {
过滤器和拦截器底层实现不同。过滤器是基于函数回调的,拦截器是基于Java的反射机制(动态代理)实现的。一般自定义的过滤器中都会实现一个doFilter()方法,这个方法有一个FilterChain参数,而实际上它是一个回调接口。
-2、**使用范围不同**。
+2、**触发时机不同**。
-过滤器实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。而拦截器是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。拦截器不仅能应用在web程序中,也可以用于Application、Swing等程序中。
-
-3、**使用的场景不同**。
+过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
-因为拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:日志记录、权限判断等业务。而过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、响应数据压缩等功能。
+拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
-4、**触发时机不同**。
+
-过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
+3、**使用的场景不同**。
-拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
+因为拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:日志记录、权限判断等业务。而过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、响应数据压缩等功能。
-5、**拦截的请求范围不同**。
+4、**拦截的请求范围不同**。
请求的执行顺序是:请求进入容器 -> 进入过滤器 -> 进入 Servlet -> 进入拦截器 -> 执行控制器。可以看到过滤器和拦截器的执行时机也是不同的,过滤器会先执行,然后才会执行拦截器,最后才会进入真正的要调用的方法。
@@ -1621,5 +1641,17 @@ server {
> 参考:https://juejin.cn/post/7167153109158854687
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
+
+
+
-
\ No newline at end of file
+
diff --git a/docs/java/java-collection.md b/docs/java/java-collection.md
index c91d8ce..df13bad 100644
--- a/docs/java/java-collection.md
+++ b/docs/java/java-collection.md
@@ -1,8 +1,24 @@
---
sidebar: heading
+title: Java集合常见面试题总结
+category: Java
+tag:
+ - Java集合
+head:
+ - - meta
+ - name: keywords
+ content: Java集合,ArrayList,LinkedList,HashMap扩容,HashMap线程不安全,LinkedHashMap底层原理,TreeMap介绍,HashSet,fail fast,fail safe,Iterator,并发容器
+ - - meta
+ - name: description
+ content: 高质量的Java集合常见知识点和面试题总结,让天下没有难背的八股文!
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## 常见的集合有哪些?
@@ -136,7 +152,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容。而HashMap每次扩容都需要重建hash表,非常影响性能。所以建议开发者在创建HashMap的时候指定初始化容量。
-### 扩容过程?
+### HashMap扩容过程是怎样的?
1.8扩容机制:当元素个数大于`threshold`时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。
@@ -144,7 +160,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
原数组的元素在重新计算hash之后,因为数组容量n变为2倍,那么n-1的mask范围在高位多1bit。在元素拷贝过程不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”(根据`e.hash & oldCap == 0`判断) 。这样可以省去重新计算hash值的时间,而且由于新增的1bit是0还是1可以认为是随机的,因此resize的过程会均匀的把之前的冲突的节点分散到新的bucket。
-### put方法流程?
+### 说说HashMapput方法的流程?
1. 如果table没有初始化就先进行初始化过程
2. 使用hash算法计算key的索引
diff --git a/docs/java/java-concurrent.md b/docs/java/java-concurrent.md
index 85fce55..09c408f 100644
--- a/docs/java/java-concurrent.md
+++ b/docs/java/java-concurrent.md
@@ -1,28 +1,30 @@
---
sidebar: heading
+title: Java并发常见面试题总结
+category: Java
+tag:
+ - Java并发
+head:
+ - - meta
+ - name: keywords
+ content: Java并发,多线程,线程池执行原理,线程池参数,线程和进程,死锁,volatile,synchronized,ThreadLocal,Java锁,Java并发工具,原子类,AQS
+ - - meta
+ - name: description
+ content: 高质量的Java并发常见知识点和面试题总结,让天下没有难背的八股文!
---
-> 欢迎加入[大彬的学习圈](https://topjavaer.cn/zsxq/introduce.html),学习圈整理了最新的**Java面试手册完整版**(本网站的面试题补充版,更全面),还有很多其他**优质资料**,比如包括Java项目、进阶知识、实战经验总结、优质书籍、笔试面试资源等等。
->
-> 
->
-> 
->
-> 
->
-> **专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
->
-> 
->
-> 
->
-> [大彬的学习圈](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
->
-> 
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
## 线程池
-线程池:一个管理线程的池子。
+### 什么是线程池,如何使用?为什么要使用线程池?
+
+线程池就是事先将多个线程对象放到一个容器中,使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高了代码执行效率。
### 为什么平时都是使用线程池创建线程,直接new一个线程不好吗?
@@ -35,7 +37,7 @@ sidebar: heading
系统资源有限,每个人针对不同业务都可以手动创建线程,并且创建线程没有统一标准,比如创建的线程有没有名字等。当系统运行起来,所有线程都在抢占资源,毫无规则,混乱场面可想而知,不好管控。
-**频繁手动创建线程为什么开销会大?跟new Object() 有什么差别?**
+### 频繁手动创建线程为什么开销会大?跟new Object() 有什么差别?
虽然Java中万物皆对象,但是new Thread() 创建一个线程和 new Object()还是有区别的。
@@ -203,7 +205,41 @@ public static ExecutorService newCachedThreadPool() {
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
+### 怎么判断线程池的任务是不是执行完了?
+
+有几种方法:
+
+1、使用线程池的原生函数**isTerminated()**;
+
+executor提供一个原生函数isTerminated()来判断线程池中的任务是否全部完成。如果全部完成返回true,否则返回false。
+
+2、**使用重入锁,维持一个公共计数**。
+
+所有的普通任务维持一个计数器,当任务完成时计数器加一(这里要加锁),当计数器的值等于任务数时,这时所有的任务已经执行完毕了。
+
+3、**使用CountDownLatch**。
+
+它的原理跟第二种方法类似,给CountDownLatch一个计数值,任务执行完毕后,调用countDown()执行计数值减一。最后执行的任务在调用方法的开始调用await()方法,这样整个任务会阻塞,直到这个计数值为零,才会继续执行。
+
+这种方式的**缺点**就是需要提前知道任务的数量。
+
+4、**submit向线程池提交任务,使用Future判断任务执行状态**。
+
+使用submit向线程池提交任务与execute提交不同,submit会有Future类型的返回值。通过future.isDone()方法可以知道任务是否执行完成。
+
+### 为什么要使用Executor线程池框架呢?
+
+- 每次执行任务都通过new Thread()去创建线程,比较消耗性能,创建一个线程是比较耗时、耗资源的
+- 调用new Thread()创建的线程缺乏管理,可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪
+- 直接使用new Thread()启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不好实现
+
+## execute和submit的区别
+execute只能提交Runnable类型的任务,无返回值。submit既可以提交Runnable类型的任务,也可以提交Callable类型的任务,会有一个类型为Future的返回值,但当任务类型为Runnable时,返回值为null。
+
+execute在执行任务时,如果遇到异常会直接抛出,而submit不会直接抛出,只有在使用Future的get方法获取返回值时,才会抛出异常
+
+execute所属顶层接口是Executor,submit所属顶层接口是ExecutorService,实现类ThreadPoolExecutor重写了execute方法,抽象类AbstractExecutorService重写了submit方法。
## 进程线程
@@ -517,7 +553,10 @@ interrupt() 并不能真正的中断线程,需要被调用的线程自己进
使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,线程自动转为Runnable状态。
+### 如何停止一个正在运行的线程?
+1. 使用共享变量的方式。共享变量可以被多个执行相同任务的线程用来作为是否停止的信号,通知停止线程的执行。
+2. 使用interrupt方法终止线程。当一个线程被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这时候可以使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
## volatile底层原理
@@ -537,6 +576,16 @@ interrupt() 并不能真正的中断线程,需要被调用的线程自己进
> 指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。Java编译器会在生成指令系列时在适当的位置会插入`内存屏障`指令来禁止处理器重排序。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。对一个volatile字段进行写操作,Java内存模型将在写操作后插入一个写屏障指令,这个指令会把之前的写入值都刷新到内存。
+## volatile为什么不能保证原子性?
+
+volatile可以保证可见性和顺序性,但是它不能保证原子性。
+
+举个例子。一个变量i被volatile修饰,两个线程想对这个变量修改,都对其进行自增操作i++,i++的过程可以分为三步,首先获取i的值,其次对i的值进行加1,最后将得到的新值写会到缓存中。
+
+假如i的初始值为100。线程A首先得到了i的初始值100,但是还没来得及修改,就阻塞了,这时线程B开始了,它也去取i的值,由于i的值未被修改,即使是被volatile修饰,主存的变量还没变化,那么线程B得到的值也是100,之后对其进行加1操作,得到101,将新值写入到缓存中,再刷入主存中。根据可见性的原则,这个主存的值可以被其他线程可见。
+
+那么问题来了,线程A之前已经读取到了i的值为100,线程A阻塞结束后,继续将100这个值加1,得到101,再将值写到缓存,最后刷入主存。这样i经过两次自增之后,结果值只加了1,明显是有问题的。所以说即便volatile具有可见性,也不能保证对它修饰的变量具有原子性。
+
## synchronized的用法有哪些?
1. **修饰普通方法**:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
@@ -686,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`,而值对应线程的变量副本。
@@ -789,6 +852,23 @@ ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方
比如Java web应用中,每个线程有自己单独的`Session`实例,就可以使用`ThreadLocal`来实现。
+## 什么是AQS?
+
+AQS(AbstractQueuedSynchronizer)是java.util.concurrent包下的核心类,我们经常使用的ReentrantLock、CountDownLatch,都是基于AQS抽象同步式队列实现的。
+
+AQS作为一个抽象类,通常是通过继承来使用的。它本身是没有同步接口的,只是定义了同步状态和同步获取和同步释放的方法。
+
+JUC包下面大部分同步类,都是基于AQS的同步状态的获取与释放来实现的,然后AQS是个双向链表。
+
+## 为什么AQS是双向链表而不是单向的?
+
+双向链表有两个指针,一个指针指向前置节点,一个指针指向后继节点。所以,双向链表可以支持常量 O(1) 时间复杂度的情况下找到前驱节点。因此,双向链表在插入和删除操作的时候,要比单向链表简单、高效。
+
+从双向链表的特性来看,AQS 使用双向链表有2个方面的原因:
+
+1. 没有竞争到锁的线程加入到阻塞队列,并且阻塞等待的前提是,当前线程所在节点的前置节点是正常状态,这样设计是为了避免链表中存在异常线程导致无法唤醒后续线程的问题。所以,线程阻塞之前需要判断前置节点的状态,如果没有指针指向前置节点,就需要从 Head 节点开始遍历,性能非常低。
+2. 在 Lock 接口里面有一个lockInterruptibly()方法,这个方法表示处于锁阻塞的线程允许被中断。也就是说,没有竞争到锁的线程加入到同步队列等待以后,是允许外部线程通过interrupt()方法触发唤醒并中断的。这个时候,被中断的线程的状态会修改成 CANCELLED。而被标记为 CANCELLED 状态的线程,是不需要去竞争锁的,但是它仍然存在于双向链表里面。这就意味着在后续的锁竞争中,需要把这个节点从链表里面移除,否则会导致锁阻塞的线程无法被正常唤醒。在这种情况下,如果是单向链表,就需要从 Head 节点开始往下逐个遍历,找到并移除异常状态的节点。同样效率也比较低,还会导致锁唤醒的操作和遍历操作之间的竞争。
+
## AQS原理
AQS,`AbstractQueuedSynchronizer`,抽象队列同步器,定义了一套多线程访问共享资源的同步器框架,许多并发工具的实现都依赖于它,如常用的`ReentrantLock/Semaphore/CountDownLatch`。
@@ -1139,17 +1219,6 @@ public final void lazySet(int i, int newValue)//最终 将index=i 位置的元
- AtomicStampedReference:带有版本号的引用类型原子类。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
- AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
-## 为什么要使用Executor线程池框架呢?
-
-- 每次执行任务都通过new Thread()去创建线程,比较消耗性能,创建一个线程是比较耗时、耗资源的
-- 调用new Thread()创建的线程缺乏管理,可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪
-- 直接使用new Thread()启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不好实现
-
-## 如何停止一个正在运行的线程?
-
-1. 使用共享变量的方式。共享变量可以被多个执行相同任务的线程用来作为是否停止的信号,通知停止线程的执行。
-2. 使用interrupt方法终止线程。当一个线程被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这时候可以使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
-
## 什么是Daemon线程?
后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。
@@ -1165,28 +1234,6 @@ SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能
JDK1.8 ConcurrentHashMap采用CAS和synchronized来保证并发安全。数据结构采用数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,支持并发访问、修改。
另外ConcurrentHashMap使用了一种不同的迭代方式。当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
-## 怎么判断线程池的任务是不是执行完了?
-
-有几种方法:
-
-1、使用线程池的原生函数**isTerminated()**;
-
-executor提供一个原生函数isTerminated()来判断线程池中的任务是否全部完成。如果全部完成返回true,否则返回false。
-
-2、**使用重入锁,维持一个公共计数**。
-
-所有的普通任务维持一个计数器,当任务完成时计数器加一(这里要加锁),当计数器的值等于任务数时,这时所有的任务已经执行完毕了。
-
-3、**使用CountDownLatch**。
-
-它的原理跟第二种方法类似,给CountDownLatch一个计数值,任务执行完毕后,调用countDown()执行计数值减一。最后执行的任务在调用方法的开始调用await()方法,这样整个任务会阻塞,直到这个计数值为零,才会继续执行。
-
-这种方式的**缺点**就是需要提前知道任务的数量。
-
-4、**submit向线程池提交任务,使用Future判断任务执行状态**。
-
-使用submit向线程池提交任务与execute提交不同,submit会有Future类型的返回值。通过future.isDone()方法可以知道任务是否执行完成。
-
## 什么是Future?
在并发编程中,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。
@@ -1203,8 +1250,127 @@ Future接口主要包括5个方法:
4. isDone()方法判断当前方法是否完成
5. isCancel()方法判断当前方法是否取消
+## select、poll、epoll之间的区别
+
+select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
+select的时间复杂度O(n)。它仅仅知道有I/O事件发生了,却并不知道是哪那几个流,只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的时间复杂度,同时处理的流越多,轮询时间就越长。
+
+poll的时间复杂度O(n)。poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
+
+epoll的时间复杂度O(1)。epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动的。
> 参考链接:https://blog.csdn.net/u014209205/article/details/80598209
+## ReadWriteLock 和 StampedLock 的区别
+
+在多线程编程中,对于共享资源的访问控制是一个非常重要的问题。在并发环境下,多个线程同时访问共享资源可能会导致数据不一致的问题,因此需要一种机制来保证数据的一致性和并发性。
+
+Java提供了多种机制来实现并发控制,其中 ReadWriteLock 和 StampedLock 是两个常用的锁类。本文将分别介绍这两个类的特性、使用场景以及示例代码。
+
+**ReadWriteLock**
+
+ReadWriteLock 是Java提供的一个接口,全类名:`java.util.concurrent.locks.ReentrantLock`。它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种机制可以提高读取操作的并发性,但写入操作需要独占资源。
+
+**特性**
+
+- 多个线程可以同时获取读锁,但只有一个线程可以获取写锁。
+- 当一个线程持有写锁时,其他线程无法获取读锁和写锁,读写互斥。
+- 当一个线程持有读锁时,其他线程可以同时获取读锁,读读共享。
+
+**使用场景**
+
+**ReadWriteLock** 适用于读多写少的场景,例如缓存系统、数据库连接池等。在这些场景中,读取操作占据大部分时间,而写入操作较少。
+
+**示例代码**
+
+下面是一个使用 ReadWriteLock 的示例,实现了一个简单的缓存系统:
+
+```java
+public class Cache {
+ private Map data = new HashMap<>();
+ private ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public Object get(String key) {
+ lock.readLock().lock();
+ try {
+ return data.get(key);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public void put(String key, Object value) {
+ lock.writeLock().lock();
+ try {
+ data.put(key, value);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+}
+```
+
+在上述示例中,Cache 类使用 ReadWriteLock 来实现对 data 的并发访问控制。get 方法获取读锁并读取数据,put 方法获取写锁并写入数据。
+
+**StampedLock**
+
+StampedLock 是Java 8 中引入的一种新的锁机制,全类名:`java.util.concurrent.locks.StampedLock`,它提供了一种乐观读的机制,可以进一步提升读取操作的并发性能。
+
+**特性**
+
+- 与 ReadWriteLock 类似,StampedLock 也支持多个线程同时获取读锁,但只允许一个线程获取写锁。
+- 与 ReadWriteLock 不同的是,StampedLock 还提供了一个乐观读锁(Optimistic Read Lock),即不阻塞其他线程的写操作,但在读取完成后需要验证数据的一致性。
+
+**使用场景**
+
+StampedLock 适用于读远远大于写的场景,并且对数据的一致性要求不高,例如统计数据、监控系统等。
+
+**示例代码**
+
+下面是一个使用 StampedLock 的示例,实现了一个计数器:
+
+```java
+public class Counter {
+ private int count = 0;
+ private StampedLock lock = new StampedLock();
+
+ public int getCount() {
+ long stamp = lock.tryOptimisticRead();
+ int value = count;
+ if (!lock.validate(stamp)) {
+ stamp = lock.readLock();
+ try {
+ value = count;
+ } finally {
+ lock.unlockRead(stamp);
+ }
+ }
+ return value;
+ }
+
+ public void increment() {
+ long stamp = lock.writeLock();
+ try {
+ count++;
+ } finally {
+ lock.unlockWrite(stamp);
+ }
+ }
+}
+```
+
+在上述示例中,Counter 类使用 StampedLock 来实现对计数器的并发访问控制。getCount 方法首先尝试获取乐观读锁,并读取计数器的值,然后通过 validate 方法验证数据的一致性。如果验证失败,则获取悲观读锁,并重新读取计数器的值。increment 方法获取写锁,并对计数器进行递增操作。
+
+**总结**
+
+**ReadWriteLock** 和 **StampedLock** 都是Java中用于并发控制的重要机制。
+
+- **ReadWriteLock** 适用于读多写少的场景;
+- **StampedLock** 则适用于读远远大于写的场景,并且对数据的一致性要求不高;
+
+在实际应用中,我们需要根据具体场景来选择合适的锁机制。通过合理使用这些锁机制,我们可以提高并发程序的性能和可靠性。
+
+
+

diff --git a/docs/java/java8/1-functional-program.md b/docs/java/java8/1-functional-program.md
index e41f981..70bfa84 100644
--- a/docs/java/java8/1-functional-program.md
+++ b/docs/java/java8/1-functional-program.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 函数式编程
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: 函数式编程
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 函数式编程
面向对象编程:面向对象的语言,一切皆对象,如果想要调用一个函数,函数必须属于一个类或对象,然后在使用类或对象进行调用。面向对象编程可能需要多写很多重复的代码行。
diff --git a/docs/java/java8/2-lambda.md b/docs/java/java8/2-lambda.md
index ae09126..99576a6 100644
--- a/docs/java/java8/2-lambda.md
+++ b/docs/java/java8/2-lambda.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Lambda表达式
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: Lambda表达式
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# Lambda 表达式
在Java8以前,使用`Collections`的sort方法对字符串排序的写法:
diff --git a/docs/java/java8/3-functional-interface.md b/docs/java/java8/3-functional-interface.md
index f3f9b81..8cc60bd 100644
--- a/docs/java/java8/3-functional-interface.md
+++ b/docs/java/java8/3-functional-interface.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 函数式接口
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: 函数式接口
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 函数式接口
Functional Interface:函数式接口,只包含一个抽象方法的接口。只有函数式接口才能缩写成 Lambda 表达式。@FunctionalInterface 定义类为一个函数式接口,如果添加了第二个抽象方法,编译器会立刻抛出错误提示。
diff --git a/docs/java/java8/4-inner-functional-interface.md b/docs/java/java8/4-inner-functional-interface.md
index 5fcc9be..6075c36 100644
--- a/docs/java/java8/4-inner-functional-interface.md
+++ b/docs/java/java8/4-inner-functional-interface.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 内置的函数式接口
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: 函数式接口,内置的函数式接口
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 内置的函数式接口
Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterface 注解,以用来支持 Lambda 表达式。
diff --git a/docs/java/java8/5-stream.md b/docs/java/java8/5-stream.md
index 129f075..784f5ea 100644
--- a/docs/java/java8/5-stream.md
+++ b/docs/java/java8/5-stream.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Stream
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: java stream,java流操作
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# Stream
使用 `java.util.Stream` 对一个包含一个或多个元素的集合做各种操作,原集合不变,返回新集合。只能对实现了 `java.util.Collection` 接口的类做流的操作。`Map` 不支持 `Stream` 流。`Stream` 流支持同步执行,也支持并发执行。
diff --git a/docs/java/java8/6-parallel-stream.md b/docs/java/java8/6-parallel-stream.md
index f1370c8..37368b8 100644
--- a/docs/java/java8/6-parallel-stream.md
+++ b/docs/java/java8/6-parallel-stream.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Parallel-Streams
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: Parallel-Streams,并行流
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# Parallel-Streams
并行流。`stream` 流是支持**顺序**和**并行**的。顺序流操作是单线程操作,串行化的流无法带来性能上的提升,通常我们会使用多线程来并行执行任务,处理速度更快。
diff --git a/docs/java/java8/7-map.md b/docs/java/java8/7-map.md
index 9eee660..4cd983b 100644
--- a/docs/java/java8/7-map.md
+++ b/docs/java/java8/7-map.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Map集合
+category: Java
+tag:
+ - Java8
+head:
+ - - meta
+ - name: keywords
+ content: map集合,java map
+ - - meta
+ - name: description
+ content: 高质量的Java基础常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# Map 集合
Java8 针对 map 操作增加了一些方法,非常方便
diff --git a/docs/java/jvm.md b/docs/java/jvm.md
index 11ea98c..5400aac 100644
--- a/docs/java/jvm.md
+++ b/docs/java/jvm.md
@@ -12,16 +12,22 @@
**专属一对一的提问答疑**,帮你解答各种疑难问题,包括自学Java路线、职业规划、面试问题等等。大彬会**优先解答**球友的问题。
-
+
+
+
-
+
-另外星球还提供**简历指导、修改服务**,大彬已经帮**90**+个小伙伴修改了简历,相对还是比较有经验的。
+如果你正在打算准备跳槽、面试,星球还提供**简历指导、修改服务**,大彬已经帮**120**+个小伙伴修改了简历,相对还是比较有经验的。


-[知识星球](https://topjavaer.cn/zsxq/introduce.html)**加入方式**:
+怎么加入[知识星球](https://topjavaer.cn/zsxq/introduce.html)?
+
+**扫描以下二维码**领取50元的优惠券即可加入。星球定价**188**元,减去**50**元的优惠券,等于说只需要**138**元的价格就可以加入,服务期一年,**每天只要4毛钱**(0.37元),相比培训班几万块的学费,非常值了,星球提供的服务可以说**远超**门票价格了。
+
+随着星球内容不断积累,星球定价也会不断**上涨**(最初原价**68**元,现在涨到**188**元了,后面还会持续**上涨**),所以,想提升自己的小伙伴要趁早加入,**早就是优势**(优惠券只有50个名额,用完就恢复**原价**了)。
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/java/jvm/jvm-heap-memory-share.md b/docs/java/jvm/jvm-heap-memory-share.md
index 784bda8..6efdde7 100644
--- a/docs/java/jvm/jvm-heap-memory-share.md
+++ b/docs/java/jvm/jvm-heap-memory-share.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Java堆内存是线程共享的?
+category: Java
+tag:
+ - JVM
+head:
+ - - meta
+ - name: keywords
+ content: 堆内存内存共享,内存共享
+ - - meta
+ - name: description
+ content: 高质量的Java常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
> 本文转自Hollis
Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解。可以说,关于JVM的相关知识,基本是每个Java开发者必学的知识点,也是面试的时候必考的知识点。
diff --git a/docs/learn/ghelper.md b/docs/learn/ghelper.md
index a640f63..f5de885 100644
--- a/docs/learn/ghelper.md
+++ b/docs/learn/ghelper.md
@@ -1,4 +1,17 @@
-# 科学上网教程
+---
+sidebar: heading
+title: 科学上网教程
+category: 工具
+tag:
+ - 工具
+head:
+ - - meta
+ - name: keywords
+ content: ghelper
+ - - meta
+ - name: description
+ content: 提高工作效率的工具
+---
@@ -64,10 +77,16 @@ Ghelper可以在google play商店进行下载,需要访问google商店,无
-> 最后给大家分享一个Github仓库,上面有大彬整理的**300多本经典的计算机书籍PDF**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、架构、分布式、微服务、机器学习、编程人生**等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
-
-
+最后给大家分享一个Github仓库,上面有大彬整理的**300多本经典的计算机书籍PDF**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
-**Github地址**:https://github.com/Tyson0314/java-books
\ No newline at end of file
+
+
+
+
+[**Github地址**](https://github.com/Tyson0314/java-books)
+
+如果访问不了Github,可以访问码云地址。
+
+[码云地址](https://gitee.com/tysondai/java-books)
\ No newline at end of file
diff --git a/docs/learning-resources/cs-learn-guide.md b/docs/learning-resources/cs-learn-guide.md
index 9b6cdb1..dae9863 100644
--- a/docs/learning-resources/cs-learn-guide.md
+++ b/docs/learning-resources/cs-learn-guide.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: CS自学路线
+category: 学习路线
+tag:
+ - 计算机
+head:
+ - - meta
+ - name: keywords
+ content: CS学习路线,经验分享,数据库,数据结构与算法,操作系统,计算机网络
+ - - meta
+ - name: description
+ content: CS自学路线分享
---
大家好,我是大彬~今天给大家分享CS自学路线。
diff --git a/docs/learning-resources/java-learn-guide.md b/docs/learning-resources/java-learn-guide.md
index 27bedbd..f17c203 100644
--- a/docs/learning-resources/java-learn-guide.md
+++ b/docs/learning-resources/java-learn-guide.md
@@ -1,7 +1,25 @@
---
sidebar: heading
+title: Java自学路线
+category: 学习路线
+tag:
+ - Java
+head:
+ - - meta
+ - name: keywords
+ content: java学习路线,经验分享,java,Spring,mysql,redis,springboot
+ - - meta
+ - name: description
+ content: 绝对全面的Java自学路线
---
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
大家好,我是大彬~
我本科学的不是计算机,大四开始自学Java,并且找到了中大厂的offer。自学路上遇到不少问题,每天晚上都是坚持到一两点才睡觉,**最终也拿到了30w的offer**。
@@ -83,7 +101,23 @@ http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224
- 《head first java》
- 《JAVA核心技术卷》
-head first系列的书籍讲解比较有趣,比较好理解。《JAVA核心技术卷》难度相对适中,内容也比较全面,部分章节(如Swing)可以跳过。
+这本书我看了两遍,是一本非常棒的书。不得不说,head first系列的书籍质量是真的高,清晰的条理,生动的图示,偶尔来点老外的幽默,阅读体验非常舒畅。
+
+
+
+《JAVA核心技术卷》难度相对适中,内容也比较全面,部分章节(如Swing)可以跳过。
+
+
+
+> 这些书籍,我已经整理了电子版,放到github上了,总共**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~(花了一个多月的时间整理的,希望对大家有帮助,欢迎star~)
+>
+> 仓库持续更新中~
+>
+> 
+>
+> 有需要的自取:
+>
+> github仓库:https://github.com/Tyson0314/java-books
视频推荐动力节点老杜的视频教程,1000w的播放量!视频总体上质量很不错,讲解挺详细,适合新手。跟着老杜的视频学下来,可以学到很多知识!
@@ -252,6 +286,12 @@ https://www.bilibili.com/video/BV18E411x7eT
并发编程的相关内容可以看看《JAVA并发编程实战》这本书。
+豆瓣评分9.0,本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则等。
+
+
+
+本书关于并发编程的细节介绍得非常详细,看得出有很多实践功底,而不是一个理论派,建议每一个学并发的同学看看。
+
视频推荐狂神说Java,很不错的视频:
https://www.bilibili.com/video/BV1B7411L7tE
@@ -300,6 +340,10 @@ https://www.bilibili.com/video/BV1L4411y7mn
JVM也是面试经常会问的内容。Java开发者不用自己进行内存管理、垃圾回收,JVM帮我们做了,但是还是有必要了解下JVM的工作原理,这样在出现oom等问题的时候,才有思路去排查和解决问题。书籍推荐周老师的《深入理解Java虚拟机》。
+关于Java虚拟机的书很少,这本书算是我心目中写的最好的关于JVM的书籍了。这本书不光在理论方面具有相当深度,在实操方面也极具实用性,特别是书的第二部分,2--5章全部是讲GC的。真正做到了:理论与实践相结合。叙述方面:深入浅出,行文流畅,言辞优美,读起来非常舒服。
+
+
+
视频推荐尚硅谷宋红康的全套课程,全套课程分为三个篇章:《内存与垃圾回收篇》、《字节码与类的加载篇》和《性能监控与调优篇》。
尚硅谷JVM全套教程:
@@ -469,7 +513,11 @@ LintCode的UI、tagging、filter更加灵活,更有优点,大家选择其中
- 《MySQL必知必会》
- 《高性能mysql》
-《MySQL必知必会》主要是Mysql的基础语法,很好理解。后面有了基础再看《高性能mysql》,这本书主要讲解索引、SQL优化、高级特性等,很多Mysql相关面试题出自《高性能MySQL》这本书,值得一看。
+《MySQL必知必会》主要是Mysql的基础语法,很好理解。后面有了基础再看《高性能mysql》,这本书主要讲解索引、SQL优化、高级特性等。
+
+
+
+非常好的一本书,五星力荐,即使你不是DBA也值得一读。
**视频推荐**
diff --git a/docs/mass-data/1-count-phone-num.md b/docs/mass-data/1-count-phone-num.md
index f35fb9a..7f09f2e 100644
--- a/docs/mass-data/1-count-phone-num.md
+++ b/docs/mass-data/1-count-phone-num.md
@@ -1,7 +1,25 @@
---
sidebar: heading
+title: 统计不同号码的个数
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,电话号码统计,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
# 统计不同号码的个数
题目来自百度二面。
diff --git a/docs/mass-data/2-find-hign-frequency-word.md b/docs/mass-data/2-find-hign-frequency-word.md
index 0d549bb..f5eecbe 100644
--- a/docs/mass-data/2-find-hign-frequency-word.md
+++ b/docs/mass-data/2-find-hign-frequency-word.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 出现频率最高的100个词
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,高频词统计,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
# 出现频率最高的100个词
diff --git a/docs/mass-data/3-find-same-url.md b/docs/mass-data/3-find-same-url.md
index 6faedcf..f704049 100644
--- a/docs/mass-data/3-find-same-url.md
+++ b/docs/mass-data/3-find-same-url.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 查找两个大文件共同的URL
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,大文件共同的URL,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
# 查找两个大文件共同的URL
diff --git a/docs/mass-data/4-find-mid-num.md b/docs/mass-data/4-find-mid-num.md
index 2fdcdff..6e94690 100644
--- a/docs/mass-data/4-find-mid-num.md
+++ b/docs/mass-data/4-find-mid-num.md
@@ -1,6 +1,16 @@
---
sidebar: heading
-
+title: 如何在100亿数据中找到中位数?
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,100亿数据找中位数,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
# 如何在100亿数据中找到中位数?
@@ -33,4 +43,4 @@ sidebar: heading
-> 参考链接:https://blog.csdn.net/qq_41306849/article/details/119828746
\ No newline at end of file
+> 参考链接:https://blog.csdn.net/qq_41306849/article/details/119828746
diff --git a/docs/mass-data/5-find-hot-string.md b/docs/mass-data/5-find-hot-string.md
index d6862a4..4829bb6 100644
--- a/docs/mass-data/5-find-hot-string.md
+++ b/docs/mass-data/5-find-hot-string.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 如何查询最热门的查询串?
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,查询最热门的查询串,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
# 如何查询最热门的查询串?
@@ -51,4 +62,4 @@ sidebar: heading
> 作者:yanglbme
-> 链接:https://juejin.cn/post/6844904003998842887
\ No newline at end of file
+> 链接:https://juejin.cn/post/6844904003998842887
diff --git a/docs/mass-data/6-top-500-num.md b/docs/mass-data/6-top-500-num.md
index c5c5428..751136a 100644
--- a/docs/mass-data/6-top-500-num.md
+++ b/docs/mass-data/6-top-500-num.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 如何找出排名前 500 的数?
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,排名前500的数,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
## 如何找出排名前 500 的数?
diff --git a/docs/mass-data/7-query-frequency-sort.md b/docs/mass-data/7-query-frequency-sort.md
index 3001b61..f10013f 100644
--- a/docs/mass-data/7-query-frequency-sort.md
+++ b/docs/mass-data/7-query-frequency-sort.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 如何按照 query 的频度排序?
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
---
## 如何按照 query 的频度排序?
@@ -30,4 +41,4 @@ sidebar: heading
> 作者:yanglbme
-> 链接:https://juejin.cn/post/6844904003998842887
\ No newline at end of file
+> 链接:https://juejin.cn/post/6844904003998842887
diff --git a/docs/mass-data/8-topk-template.md b/docs/mass-data/8-topk-template.md
index 09bf8fd..fee618e 100644
--- a/docs/mass-data/8-topk-template.md
+++ b/docs/mass-data/8-topk-template.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 数据中 TopK 问题的常用套路
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,topk问题,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
+---
+
## 大数据中 TopK 问题的常用套路
今天想跟大家聊一些**常见的 topK 问题**。
diff --git a/docs/mass-data/9-sort-500-million-large-files.md b/docs/mass-data/9-sort-500-million-large-files.md
index 5799206..96ba8f3 100644
--- a/docs/mass-data/9-sort-500-million-large-files.md
+++ b/docs/mass-data/9-sort-500-million-large-files.md
@@ -1,5 +1,18 @@
-5亿个数的大文件怎么排序?
-==================================================
+---
+sidebar: heading
+title: 5亿个数的大文件怎么排序?
+category: 海量数据
+tag:
+ - 海量数据
+head:
+ - - meta
+ - name: keywords
+ content: 海量数据面试题,5亿个数排序,海量数据
+ - - meta
+ - name: description
+ content: 海量数据常见面试题总结,让天下没有难背的八股文!
+---
+
## **问题**
给你1个文件`bigdata`,大小4663M,5亿个数,文件中的数据随机,一行一个整数:
@@ -294,4 +307,4 @@ private boolean get(int v) {
>
> 也就是说,最小值属于哪个文件,那么就从哪个文件当中取下一行数据。(因为小文件内部有序,下一行数据代表了它当前的最小值)
-感兴趣的小伙伴可以自己尝试去实现下~
\ No newline at end of file
+感兴趣的小伙伴可以自己尝试去实现下~
diff --git a/docs/message-queue/kafka.md b/docs/message-queue/kafka.md
index 5dd9255..57a7d1f 100644
--- a/docs/message-queue/kafka.md
+++ b/docs/message-queue/kafka.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: Kafka常见面试题总结
+category: 消息队列
+tag:
+ - Kafka
+head:
+ - - meta
+ - name: keywords
+ content: Kafka 面试题,Kafka设计架构,Kafka分区,Kafka Producer,Kafka pull模式,Kafka topic,Kafka优缺点
+ - - meta
+ - name: description
+ content: 高质量的Kafka常见知识点和面试题总结,让天下没有难背的八股文!
---
@@ -165,4 +176,4 @@ Kafka 分区数据不支持减少是由很多原因的,比如减少的分区
-> 参考:https://blog.51cto.com/u_15127589/2679155
\ No newline at end of file
+> 参考:https://blog.51cto.com/u_15127589/2679155
diff --git a/docs/message-queue/mq.md b/docs/message-queue/mq.md
index 836831a..1a688bc 100644
--- a/docs/message-queue/mq.md
+++ b/docs/message-queue/mq.md
@@ -1,5 +1,16 @@
---
sidebar: heading
+title: 消息队列常见面试题总结
+category: 消息队列
+tag:
+ - 消息队列
+head:
+ - - meta
+ - name: keywords
+ content: 消息队列面试题,消息队列优缺点,消息队列对比,MQ常用协议,MQ通讯模式,消息重复消费,消息队列高可用
+ - - meta
+ - name: description
+ content: 高质量的消息队列常见知识点和面试题总结,让天下没有难背的八股文!
---
## 为什么要使用消息队列?
diff --git a/docs/message-queue/mq/consume-by-order.md b/docs/message-queue/mq/consume-by-order.md
new file mode 100644
index 0000000..e3d0a84
--- /dev/null
+++ b/docs/message-queue/mq/consume-by-order.md
@@ -0,0 +1,52 @@
+---
+sidebar: heading
+title: 如何保证消息的顺序性?
+category: 消息队列
+tag:
+ - 消息队列
+head:
+ - - meta
+ - name: keywords
+ content: 消息队列面试题,如何保证消息消费的顺序性
+ - - meta
+ - name: description
+ content: 高质量的消息队列常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+## 如何保证消息的顺序性?
+
+假设有这样一个场景,使用 MySQL `binlog` 将数据从一个 MySQL 库原封不动地同步到另一个 MySQL 库里面去。
+
+你在 MySQL 里增删改一条数据,对应出来了增删改 3 条 `binlog` 日志,接着这三条 `binlog` 发送到 MQ 里面,再消费出来依次执行,这时需要保证按照顺序去执行,不然本来是:增加、修改、删除;调换了顺序变成执行成删除、修改、增加,这就有问题了。
+
+本来这个数据同步过来,应该最后这个数据被删除了;结果搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。
+
+先看看顺序会错乱的俩场景:
+
+- **RabbitMQ**:一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者 2 先执行完操作,把 data2 存入数据库,然后是 data1/data3。这明显就乱了。
+
+
+
+- **Kafka**:比如说我们建了一个 topic,有三个 partition。生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。
+- 消费者从 partition 中取出来数据的时候,也一定是有顺序的。到这里,顺序还是 ok 的,没有错乱。接着,我们在消费者里可能会搞**多个线程来并发处理消息**。因为如果消费者是单线程消费处理,而处理比较耗时的话,比如处理一条消息耗时几十 ms,那么 1 秒钟只能处理几十条消息,这吞吐量太低了。而多个线程并发跑的话,顺序可能就乱掉了。
+
+
+
+### 解决方案
+
+#### RabbitMQ
+
+拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点,这样也会造成吞吐量下降,可以在消费者内部采用多线程的方式取消费。
+
+
+
+或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
+
+注意,这里消费者不直接消费消息,而是将消息根据关键值(比如:订单 id)进行哈希,哈希值相同的消息保存到相同的内存队列里。也就是说,需要保证顺序的消息存到了相同的内存队列,然后由一个唯一的 worker 去处理。
+
+#### Kafka
+
+- 一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
+- 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
+
+
diff --git a/docs/message-queue/rabbitmq.md b/docs/message-queue/rabbitmq.md
index d78fb46..169d0a8 100644
--- a/docs/message-queue/rabbitmq.md
+++ b/docs/message-queue/rabbitmq.md
@@ -1,7 +1,17 @@
---
sidebar: heading
+title: RabbitMQ常见面试题总结
+category: 消息队列
+tag:
+ - RabbitMQ
+head:
+ - - meta
+ - name: keywords
+ content: RabbitMQ面试题,RabbitMQ组件,RabbitMQ Exchange,RabbitMQ消息丢失,消息重复消费,死信队列
+ - - meta
+ - name: description
+ content: RabbitMQ常见知识点和面试题总结,让天下没有难背的八股文!
---
-
## 什么是RabbitMQ?
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
diff --git a/docs/note/README.md b/docs/note/README.md
new file mode 100644
index 0000000..3a19bc2
--- /dev/null
+++ b/docs/note/README.md
@@ -0,0 +1,6 @@
+- [一小时彻底吃透Redis](https://topjavaer.cn/note/redis-note.html)
+- [21个写SQL的好习惯](https://topjavaer.cn/note/write-sql.html)
+- [Docker详解与部署微服务实战](https://topjavaer.cn/note/docker-note.html)
+- [计算机专业的同学都看看这几点建议](https://topjavaer.cn/note/computor-advice.html)
+- [建议计算机专业同学都看看这门课](https://topjavaer.cn/note/computor-advice.html)
+
diff --git a/docs/note/computer-blogger.md b/docs/note/computer-blogger.md
new file mode 100644
index 0000000..9c23b7d
--- /dev/null
+++ b/docs/note/computer-blogger.md
@@ -0,0 +1,44 @@
+## 湖科大教书匠——计算机网络
+
+“宝藏老师”、“干货满满”、“羡慕湖科大”...这些都是网友对这门网课的评价,可见网课质量之高!
+
+湖南科技大学《计算机网络》微课堂是该校高军老师精心制作的视频课程,用简单的语言描述复杂的问题,用生动的动画演示抽象概念,更加便于学生理解和记忆。推荐初学者去看看,一定不会亏!
+
+
+
+
+
+## 鱼C-小甲鱼——带你学C带你飞
+
+小甲鱼的课程是很多转码人的第一门编程课,非常不错,对于自学的初学者来说挺友好。小甲鱼会从学生的角度思考问题,会针对一些初学者的疑惑进行解答,比很多大学老师教学水平强太多!
+
+另外,小甲鱼的零基础入门学习python系列课程也很不错,推荐~
+
+
+
+## 跟李沐学AI——机器学习
+
+bilibili 2021新人奖UP主、亚马逊资深首席科学家,李沐老师的机器学习课程,可以说是机器学习入门课程的天花板,非常适合新手入门,没有很复杂的推导过程和数学知识,偏向于运用的角度。
+
+
+
+## 莫烦Python——Python
+
+莫烦python的Python基础课程非常适合刚入门, 或者是以前使用过其语言的朋友,每一段视频都不会很长,节节相连,对于迅速掌握基础的使用方法很有帮助。
+
+
+
+
+
+## 懒猫老师——用动画讲编程
+
+每个视频都用心制作,形象生动,用动画讲编程,在快乐中学习编程,太赞了。
+
+
+
+## 尚硅谷——Java教程
+
+虽然是培训机构,但是尚硅谷也在某站上传了很多编程入门的视频,质量相对还是不错的,Java入门教程的播放量达到千万了,很多人都是看尚硅谷的视频学Java的哈哈(尚硅谷打qian!)。
+
+
+
diff --git a/docs/note/computer-course.md b/docs/note/computer-course.md
new file mode 100644
index 0000000..f6f859f
--- /dev/null
+++ b/docs/note/computer-course.md
@@ -0,0 +1,65 @@
+## GitHub
+
+
+
+GitHub是一个面向开源及私有软件项目的托管平台,因为只支持Git作为唯一的版本库格式进行托管,故名GitHub。
+
+作为开源代码库以及版本控制系统,Github拥有超过900万开发者用户。随着越来越多的应用程序转移到了云上,Github已经成为了管理软件开发以及发现已有代码的首选方法。
+
+在GitHub,用户甚至可以十分轻易地找到海量的开源代码。
+
+
+
+## LeetCode
+
+
+
+力扣,强推!力扣虐我千百遍,我待力扣如初恋!
+
+LeetCode是一个集合了大量算法面试题和AI面试题的网站,它为全世界的码农提供了练习自我技能的良好平台。
+
+除了在题库中直接找题目之外,还可以根据该网站提供的阶梯训练进行练习。阶梯训练板块收纳了中美等不同公司的面试真题并以关卡的形式呈现给挑战者
+
+## GeeksforGeeks
+
+
+
+GeeksforGeeks是一个主要专注于计算机科学的网站。它有大量的算法,解决方案和编程问题。该网站也有很多面试中经常问到的问题。由于该网站更多地涉及计算机科学,因此你可以找到很多编程问题在大多数著名语言下的解决方案。
+
+## StackOverflow
+
+
+
+程序员最痛苦的事莫过于深陷于BUG的泥潭,而stack overflow作为全球最大的技术问答网站,可以说每个搞过技术的人是必上的网站。开发过程中遇到什么 bug,上去搜一下,只要搜索的方式对,百分之 99 的问题都能搜到答案。
+
+在 Stackoverflow 你也可以看到很多经典的问题,我们也可以从这些问题中学习如何去提问,如何和答题者沟通。
+
+## Codebeautify
+
+
+
+由于我们是程序员,所以美不是我们所关心的。很多时候,我们的代码很难被其他人阅读。Codebeautify可以使你的代码易于阅读。该网站有大多数可以美化的语言。另外,如果你想让你的代码不能被某人读取,你也可以这样做。
+
+
+
+## 菜鸟教程
+
+菜鸟教程提供了编程的基础技术教程, 介绍了HTML、CSS、Javascript、Python,Java,Ruby,C,PHP , MySQL等各种编程语言的基础知识,对于计算机小白和初学者非常有用!
+
+
+
+
+
+## 中国大学MOOC
+
+
+
+中国大学MOOC(慕课) 是国内优质的中文MOOC学习平台,由网易有道与高教社携手推出的中国大学生MOOC承载了一万多门开放课、1400多门国家级精品课,与803所高校开展合作,已经成为最大的中文慕课平台。课程相对比较优质,推荐。
+
+
+
+## 牛客网
+
+
+
+牛客网是一个集笔面试系统、题库、课程教育、社群交流、招聘内推于一体的招聘类网站。牛客网题库中包含几万道题目,题库涵盖六类行业题目,包含:IT技术类、硬件类、产品运营类、金融财会类、市场营销类、管理类、职能类。而且牛客网每年都会组织往年求职者分享面试经验,以供后来者学习、参考、交流。
\ No newline at end of file
diff --git a/docs/note/computer-site.md b/docs/note/computer-site.md
new file mode 100644
index 0000000..bafafc6
--- /dev/null
+++ b/docs/note/computer-site.md
@@ -0,0 +1,77 @@
+## LeetCode
+
+力扣,强推!力扣虐我千百遍,我待力扣如初恋!
+
+
+
+
+
+从现在开始,每天一道力扣算法题,坚持几个月的时间,你会感谢我的(傲娇脸)
+
+我刚开始刷算法题的时候,就选择在力扣上刷。最初刷easy级别题目的时候,都感觉有点吃力,坚持半年之后,遇到中等题目甚至hard级别的题目都不慌了。
+
+不过是熟能生巧罢了。
+
+## Programming by Doing
+
+网站的宗旨就是:“学习的最好方法就是去做”。
+
+以作业的形式整理的编程基础题,题目相对还是比较简单的,适合刚入门的初学者。
+
+
+
+## 洛谷
+
+洛谷上的题目很多,还有很多的基础题,使用体验良好。
+
+缺点是没有相应的阶梯训练,筛选方式比较少。
+
+
+
+
+
+## 牛客网
+
+牛客网拥有超级丰富的 IT题库,题库+面试+学习+求职+讨论,基本涵盖所有面试笔试题型,堪称"互联网求职神器"。在这里不仅可以刷题,还可以跟其他牛友讨论交流,一起成长。牛客上还会各种的内推机会,对于求职的同学也是极其不错的。
+
+
+
+## LintCode
+
+与Leetcode类似的刷题网站。
+
+LeetCode/LintCode的题目量差不多。LeetCode的test case比较完备,并且LeetCode有讨论区,看别人的代码还是比较有意义的。
+
+LintCode的UI、tagging、filter更加灵活,更有优点,大家选择其中一个进行刷题即可。
+
+
+
+## AcCoder
+
+AtCoder是日本最大的算法竞技网站,支持日语和英语两种语言,顺带可以学学日文,太妙了!
+
+tips:右上角椭圆内可以切换英语日语
+
+
+
+## Timus Online Judge
+
+俄罗斯最大的刷题网站——Timus Online Judge,网站有比较进阶的算法题目,难度偏高,想在算法层面精进的的小伙伴可以试一试哦。
+
+
+
+
+
+## UVa Online Judge
+
+西班牙Valladolid大学的Online Judge,最古老也是全世界最知名的Online Judge,题库有详细的分类,题目类型非常广泛。最重要的是,题目类型属于中等,适合有一定基础的刷题选手。
+
+
+
+## Codeforces
+
+Codeforce是一个位于俄罗斯的编程比赛网站,它会定期举办竞赛,会有全球顶尖的程序员们参赛。在这个网站,可以练习从初级到高级的题目。
+
+Codeforce每周会有2-3场比赛,感兴趣的小伙伴可以去挑战下~
+
+
\ No newline at end of file
diff --git a/docs/note/computer-teacher.md b/docs/note/computer-teacher.md
new file mode 100644
index 0000000..69dd09f
--- /dev/null
+++ b/docs/note/computer-teacher.md
@@ -0,0 +1,86 @@
+## C语言教程——翁凯老师、赫斌
+
+翁恺老师是土生土长的浙大码农,从本科到博士都毕业于浙大计算机系,后来留校教书,一教就是20多年。
+
+翁恺老师的c语言课程非常好,讲解特别有趣,很适合初学者学习。
+
+
+
+郝斌老师的思路是以初学者的思路来思考的,非常适合小白,你不理解的问题,基本上他都会详细说一下。
+
+
+
+
+
+## C++——侯捷老师
+
+看了候老师的课有种醍醐灌顶的感觉,强烈建议自学c++ 者仔细看候捷老师的课,会受益匪浅。
+
+
+
+## 数据结构课程——陈越、王卓老师
+
+青岛大学王卓老师的数据结构与算法基础,课程评价很高,通俗易懂,适合零基础入门。
+
+
+
+陈越老师,被同学们尊称为“姥姥”,为各种同学答疑解惑,事无巨细,非常有热情和接地气,是个亲切的长辈一样的存在。
+
+陈姥姥的课简单易懂,评价也非常高,认真学了肯定有很大的收获~
+
+
+
+
+
+## 操作系统——李治军老师、向勇、陈渝
+
+李治军老师的操作系统课程非常棒,讲解很清晰、详细,配图也相当丰富,对初学者很友好。
+
+
+
+我看过不同老师讲的操作系统课程,觉得比较好的入门级课程是清华大学开设的网课《操作系统》,该课程由清华大学老师向勇和陈渝授课,虽然大彬上不了清华大学,但是至少可以在网上选择听清华大学的课嘛。
+
+
+
+## 计算机网络——郑烇、杨坚老师
+
+中科大郑烇、杨坚老师的计算机网络,老师讲课很幽默,思路很清晰,最重要的是,可以跟中科大学生一起完成专业知识的学习~
+
+
+
+
+
+## 数据库——战德臣老师
+
+哈尔滨工业大学战德臣老师的数据库系统原理,是国家精品课程,值得大家去学习。
+
+
+
+## 机器学习——吴恩达、李沐
+
+吴恩达老师,斯坦福计算机系的副教授,师从机器学习的大师级人物 Michael I. Jordan。吴老是,徒弟遍布美国名校,他们这一大学派的主要研究和贡献集中在统计机器学习(Statistical Machine Learning)和图模型(Probabilistic Graphical model)等。
+
+更重要的是,他在学术圈内圈外知名度很高!除了师承之外,还有一个重要原因是他在斯坦福公开课里面主讲机器学习,讲的的确是非常好,在工程界非常受欢迎。
+
+
+
+bilibili 2021新人奖UP主、亚马逊资深首席科学家,李沐老师的机器学习课程,可以说是机器学习入门课程的天花板,非常适合新手入门,没有很复杂的推导过程和数学知识,偏向于运用的角度。
+
+
+
+## python系列课程——嵩天
+
+嵩天老师是国内大学Python教育的先行者, 是Python语言进入计算机等级考试的关键人物,为推广Python语言进入大学教育序列,作了很大贡献。嵩天老师的python系列视频深入浅出,值得零基础入学。
+
+
+
+
+
+## Java基础课程——韩顺平
+
+韩顺平老师的Java课程主要面向初学者,讲课幽默风趣,通俗易懂,善于用已知的概念解释编程问题,对初学者非常友好,是自学Java很不错的选择!
+
+
+
+
+
diff --git a/docs/note/computor-advice.md b/docs/note/computor-advice.md
new file mode 100644
index 0000000..7db0369
--- /dev/null
+++ b/docs/note/computor-advice.md
@@ -0,0 +1,9 @@
+计算机专业的同学都看看这几点建议!
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/note/crash-course-computer-science.md b/docs/note/crash-course-computer-science.md
new file mode 100644
index 0000000..0c00bf6
--- /dev/null
+++ b/docs/note/crash-course-computer-science.md
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/note/docker-note.md b/docs/note/docker-note.md
new file mode 100644
index 0000000..68a8f62
--- /dev/null
+++ b/docs/note/docker-note.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/note/freshman-planning.md b/docs/note/freshman-planning.md
new file mode 100644
index 0000000..77d5149
--- /dev/null
+++ b/docs/note/freshman-planning.md
@@ -0,0 +1,46 @@
+自学计算机的大彬来分享下几点宝贵经验。
+
+1、看下**计算机科学速成课**,一门很全面的计算机原理入门课程,短短10分钟可以把大学老师十几节课讲的东西讲清楚!整个系列一共41个视频,B站上有中文字幕版。
+
+每个视频都是一个特定的主题,例如软件工程、人工智能、操作系统等,主题之间都是紧密相连的,比国内很多大学计算机课程强太多!
+
+这门课程通过生动形象的讲解方式,向普通人介绍了计算机科学相关的基础知识,包括**计算机的发展史、二进制、指令和程序、数据结构与算法、人工智能、计算机视觉、自然语言处理**等等。
+
+每节课程短小精悍,只有短短十几分钟,适合平时碎片化时间观看。
+
+
+
+2、**学会使用google搜索**。很多同学遇到问题,不会利用好搜索引擎,而是在一些交流群咨询,往往“事倍功半”,问了半天也没得到想要的答案。建议题主学习下搜索的技巧,多用谷歌搜索,少用百度搜索,谷歌搜出来答案更准确,而不是通篇复制粘贴的“垃圾”。
+
+
+
+3、**多逛技术社区**。平时多逛逛全球最大的同xing交友社区Github、StackoverFlow等技术社区,关注最新的技术动态,尽量参与到开源项目建设,如果能给优秀的开源项目奉献自己的代码,那是非常nice的,对于以后找工作面试也有非常大的帮助。
+
+
+
+4、**多动手写代码**,切忌眼高手低!如果你确信自己对大多数的基础知识和概念足够熟悉,并且能够以某种方式将它们联系起来,那么你就可以进行下一步了,你可以开始尝试编写一些有趣的 Java 程序。刚开始动手编写程序时,请可能会困难重重。但是一旦挺过去,接下来即使这些问题再次出现,你也能轻松解决。
+
+5、**阅读经典书籍**,比如《深入理解计算机系统》、《数据库系统概念》、《代码整洁之道》等等,这些都是非常优秀的书籍,每次阅读都会有新的收获。PS:不要看那种3天学会Java之类的垃圾书,内容很浅没深度!
+
+6、学好英语,干计算机这行,要想走在前列,就必须学好英语。因为计算机很多术语都是英文,中文翻译的话经常翻译的非常生涩。而且很多前沿的东西都是国外的,国内教材资料需要等待一段时间才能跟上,因此良好的英语能力能让你快人一步获取一手资料。
+
+7、**每天刷一道算法题**,养成刷题的习惯。很多互联网公司都会考察手写算法题,如果平时没有练习,那么笔试或面试的时候大概率会脑袋空白,game over。建议从大二开始,每天抽空到leetcode上刷刷题。
+
+8、**参与计算机竞赛**。比如ACM国际大学生程序设计竞赛、GPLT团队程序设计天梯赛、蓝桥杯、中国大学生计算机设计大赛等,或者企业主办的比赛,如华为软件杯精英挑战赛、百度之星程序设计大赛等,参加这些比赛对找工作和保研都有加分,并且对你的代码能力、团队合作能力和逻辑思维能力也有很大的提升。
+
+9、**绩点要刷高一点**,绩点高对你保研、考研或者找工作都有很大的帮助。尽量提高绩点,还有就是不能挂科!挂科对你以后发展影响挺大,切记!
+
+10、**打牢计算机基础**
+
+要特别重视计算机基础,无论以后是找工作还是考研,基础很重要。
+
+计算机专业课程里边,**计算机基础课程无非以下几个:**
+
+1. **计算机组成原理**
+2. **操作系统**
+3. **编译原理**
+4. **计算机网络**
+5. **数据结构与算法**
+6. **数据库基础**
+
+11、**培养写文档的能力**。写文档是计算机专业学生的必备技能。有空可以学习下markdown语法,比word好用太多了。markdown编辑器推荐Typora(最近收费了)、语雀。
\ No newline at end of file
diff --git a/docs/note/redis-note.md b/docs/note/redis-note.md
new file mode 100644
index 0000000..02e5569
--- /dev/null
+++ b/docs/note/redis-note.md
@@ -0,0 +1,28 @@
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+## 一小时彻底吃透Redis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/note/write-sql.md b/docs/note/write-sql.md
new file mode 100644
index 0000000..14faa3a
--- /dev/null
+++ b/docs/note/write-sql.md
@@ -0,0 +1,93 @@
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+## 21个写SQL的好习惯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## MySQL面试题
+
+下面分享MySQL常考的**面试题目**。
+
+- [事务的四大特性?](https://topjavaer.cn/database/mysql.html#%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%89%B9%E6%80%A7)
+- [数据库的三大范式](https://topjavaer.cn/database/mysql.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%B8%89%E5%A4%A7%E8%8C%83%E5%BC%8F)
+- [事务隔离级别有哪些?](https://topjavaer.cn/database/mysql.html#%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [生产环境数据库一般用的什么隔离级别呢?](https://topjavaer.cn/database/mysql.html#%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%80%E8%88%AC%E7%94%A8%E7%9A%84%E4%BB%80%E4%B9%88%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E5%91%A2)
+- [编码和字符集的关系](https://topjavaer.cn/database/mysql.html#%E7%BC%96%E7%A0%81%E5%92%8C%E5%AD%97%E7%AC%A6%E9%9B%86%E7%9A%84%E5%85%B3%E7%B3%BB)
+- [utf8和utf8mb4的区别](https://topjavaer.cn/database/mysql.html#utf8%E5%92%8Cutf8mb4%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [什么是索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E7%B4%A2%E5%BC%95)
+- [索引的优缺点?](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9)
+- [索引的作用?](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E7%9A%84%E4%BD%9C%E7%94%A8)
+- [什么情况下需要建索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E9%9C%80%E8%A6%81%E5%BB%BA%E7%B4%A2%E5%BC%95)
+- [什么情况下不建索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E4%B8%8D%E5%BB%BA%E7%B4%A2%E5%BC%95)
+- [索引的数据结构](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)
+- [Hash索引和B+树索引的区别?](https://topjavaer.cn/database/mysql.html#hash%E7%B4%A2%E5%BC%95%E5%92%8Cb%E6%A0%91%E7%B4%A2%E5%BC%95%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [为什么B+树比B树更适合实现数据库索引?](https://topjavaer.cn/database/mysql.html#%E4%B8%BA%E4%BB%80%E4%B9%88b%E6%A0%91%E6%AF%94b%E6%A0%91%E6%9B%B4%E9%80%82%E5%90%88%E5%AE%9E%E7%8E%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B4%A2%E5%BC%95)
+- [索引有什么分类?](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E6%9C%89%E4%BB%80%E4%B9%88%E5%88%86%E7%B1%BB)
+- [什么是最左匹配原则?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%80%E5%B7%A6%E5%8C%B9%E9%85%8D%E5%8E%9F%E5%88%99)
+- [什么是聚集索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%81%9A%E9%9B%86%E7%B4%A2%E5%BC%95)
+- [什么是覆盖索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95)
+- [索引的设计原则?](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99)
+- [索引什么时候会失效?](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BC%9A%E5%A4%B1%E6%95%88)
+- [什么是前缀索引?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E5%89%8D%E7%BC%80%E7%B4%A2%E5%BC%95)
+- [索引下推](https://topjavaer.cn/database/mysql.html#%E7%B4%A2%E5%BC%95%E4%B8%8B%E6%8E%A8)
+- [常见的存储引擎有哪些?](https://topjavaer.cn/database/mysql.html#%E5%B8%B8%E8%A7%81%E7%9A%84%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [MyISAM和InnoDB的区别?](https://topjavaer.cn/database/mysql.html#myisam%E5%92%8Cinnodb%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [MySQL有哪些锁?](https://topjavaer.cn/database/mysql.html#mysql%E6%9C%89%E5%93%AA%E4%BA%9B%E9%94%81)
+- [MVCC 实现原理?](https://topjavaer.cn/database/mysql.html#mvcc-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
+- [快照读和当前读](https://topjavaer.cn/database/mysql.html#%E5%BF%AB%E7%85%A7%E8%AF%BB%E5%92%8C%E5%BD%93%E5%89%8D%E8%AF%BB)
+- [共享锁和排他锁](https://topjavaer.cn/database/mysql.html#%E5%85%B1%E4%BA%AB%E9%94%81%E5%92%8C%E6%8E%92%E4%BB%96%E9%94%81)
+- [bin log/redo log/undo log](https://topjavaer.cn/database/mysql.html#bin-logredo-logundo-log)
+- [bin log和redo log有什么区别?](https://topjavaer.cn/database/mysql.html#bin-log%E5%92%8Credo-log%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
+- [讲一下MySQL架构?](https://topjavaer.cn/database/mysql.html#%E8%AE%B2%E4%B8%80%E4%B8%8Bmysql%E6%9E%B6%E6%9E%84)
+- [分库分表](https://topjavaer.cn/database/mysql.html#%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8)
+- [什么是分区表?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%86%E5%8C%BA%E8%A1%A8)
+- [分区表类型](https://topjavaer.cn/database/mysql.html#%E5%88%86%E5%8C%BA%E8%A1%A8%E7%B1%BB%E5%9E%8B)
+- [分区的问题?](https://topjavaer.cn/database/mysql.html#%E5%88%86%E5%8C%BA%E7%9A%84%E9%97%AE%E9%A2%98)
+- [查询语句执行流程?](https://topjavaer.cn/database/mysql.html#%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B)
+- [更新语句执行过程?](https://topjavaer.cn/database/mysql.html#%E6%9B%B4%E6%96%B0%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
+- [exist和in的区别?](https://topjavaer.cn/database/mysql.html#exist%E5%92%8Cin%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [MySQL中int()和char()的区别?](https://topjavaer.cn/database/mysql.html#mysql%E4%B8%ADint10%E5%92%8Cchar10%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [truncate、delete与drop区别?](https://topjavaer.cn/database/mysql.html#truncatedelete%E4%B8%8Edrop%E5%8C%BA%E5%88%AB)
+- [having和where区别?](https://topjavaer.cn/database/mysql.html#having%E5%92%8Cwhere%E5%8C%BA%E5%88%AB)
+- [什么是MySQL主从同步?](https://topjavaer.cn/database/mysql.html#%E4%BB%80%E4%B9%88%E6%98%AFmysql%E4%B8%BB%E4%BB%8E%E5%90%8C%E6%AD%A5)
+- [为什么要做主从同步?](https://topjavaer.cn/database/mysql.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%81%9A%E4%B8%BB%E4%BB%8E%E5%90%8C%E6%AD%A5)
+- [乐观锁和悲观锁是什么?](https://topjavaer.cn/database/mysql.html#%E4%B9%90%E8%A7%82%E9%94%81%E5%92%8C%E6%82%B2%E8%A7%82%E9%94%81%E6%98%AF%E4%BB%80%E4%B9%88)
+- [用过processlist吗?](https://topjavaer.cn/database/mysql.html#%E7%94%A8%E8%BF%87processlist%E5%90%97)
+- [MySQL查询 limit 1000,10 和limit 10 速度一样快吗?](https://topjavaer.cn/database/mysql.html#mysql%E6%9F%A5%E8%AF%A2-limit-100010-%E5%92%8Climit-10-%E9%80%9F%E5%BA%A6%E4%B8%80%E6%A0%B7%E5%BF%AB%E5%90%97)
+- [深分页怎么优化?](https://topjavaer.cn/database/mysql.html#%E6%B7%B1%E5%88%86%E9%A1%B5%E6%80%8E%E4%B9%88%E4%BC%98%E5%8C%96)
+- [高度为3的B+树,可以存放多少数据?](https://topjavaer.cn/database/mysql.html#%E9%AB%98%E5%BA%A6%E4%B8%BA3%E7%9A%84b%E6%A0%91%E5%8F%AF%E4%BB%A5%E5%AD%98%E6%94%BE%E5%A4%9A%E5%B0%91%E6%95%B0%E6%8D%AE)
+- [MySQL单表多大进行分库分表?](https://topjavaer.cn/database/mysql.html#mysql%E5%8D%95%E8%A1%A8%E5%A4%9A%E5%A4%A7%E8%BF%9B%E8%A1%8C%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8)
+- [大表查询慢怎么优化?](https://topjavaer.cn/database/mysql.html#%E5%A4%A7%E8%A1%A8%E6%9F%A5%E8%AF%A2%E6%85%A2%E6%80%8E%E4%B9%88%E4%BC%98%E5%8C%96)
+- [说说count()、count()和count()的区别](https://topjavaer.cn/database/mysql.html#%E8%AF%B4%E8%AF%B4count1count%E5%92%8Ccount%E5%AD%97%E6%AE%B5%E5%90%8D%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [MySQL中DATETIME 和 TIMESTAMP有什么区别?](https://topjavaer.cn/database/mysql.html#mysql%E4%B8%ADdatetime-%E5%92%8C-timestamp%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
+- [说说为什么不建议用外键?](https://topjavaer.cn/database/mysql.html#%E8%AF%B4%E8%AF%B4%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AE%E7%94%A8%E5%A4%96%E9%94%AE)
+- [使用自增主键有什么好处?](https://topjavaer.cn/database/mysql.html#%E4%BD%BF%E7%94%A8%E8%87%AA%E5%A2%9E%E4%B8%BB%E9%94%AE%E6%9C%89%E4%BB%80%E4%B9%88%E5%A5%BD%E5%A4%84)
+- [自增主键保存在什么地方?](https://topjavaer.cn/database/mysql.html#%E8%87%AA%E5%A2%9E%E4%B8%BB%E9%94%AE%E4%BF%9D%E5%AD%98%E5%9C%A8%E4%BB%80%E4%B9%88%E5%9C%B0%E6%96%B9)
+- [自增主键一定是连续的吗?](https://topjavaer.cn/database/mysql.html#%E8%87%AA%E5%A2%9E%E4%B8%BB%E9%94%AE%E4%B8%80%E5%AE%9A%E6%98%AF%E8%BF%9E%E7%BB%AD%E7%9A%84%E5%90%97)
+- [InnoDB的自增值为什么不能回收利用?](https://topjavaer.cn/database/mysql.html#innodb%E7%9A%84%E8%87%AA%E5%A2%9E%E5%80%BC%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E8%83%BD%E5%9B%9E%E6%94%B6%E5%88%A9%E7%94%A8)
+- [MySQL数据如何同步到Redis缓存?](https://topjavaer.cn/database/mysql.html#mysql%E6%95%B0%E6%8D%AE%E5%A6%82%E4%BD%95%E5%90%8C%E6%AD%A5%E5%88%B0redis%E7%BC%93%E5%AD%98)
\ No newline at end of file
diff --git a/docs/other/log-print.md b/docs/other/log-print.md
new file mode 100644
index 0000000..b3c6a90
--- /dev/null
+++ b/docs/other/log-print.md
@@ -0,0 +1,119 @@
+# 代码日志打印规范
+
+日志文件提供精确的系统记录,根据日志可以定位到错误详情和根源,方便开发排查问题。
+
+**日志有什么作用呢**
+
+- **打印调试**:即用日志来记录变量或者某个逻辑。记录程序运行的流程,即程序运行了哪些代码,方便排查逻辑问题。
+- **问题定位**:程序出异常或者出故障时快速的定位问题,方便后期解决问题。因为线上生产环境无法 debug,在测试环境去模拟一套生产环境,费时费力。所以依靠日志记录的信息定位问题,这点非常重要。还可以记录流量,后期可以通过 ELK(包括 EFK 进行流量统计)。
+- **用户行为日志**:记录用户的操作行为,用于大数据分析,比如监控、风控、推荐等等。这种日志,一般是给其他团队分析使用,而且可能是多个团队,因此一般会有一定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使用。当然,要记录哪些行为、操作,一般也是约定好的,因此,开发者主要是执行的角色。
+- **根因分析(甩锅必备)**:即在关键地方记录日志。方便在和各个终端定位问题时,别人说时你的程序问题,你可以理直气壮的拿出你的日志说,看,我这里运行了,状态也是对的。这样,对方就会乖乖去定位他的代码,而不是互相推脱。
+
+## **什么时候记录日志?**
+
+上文说了日志的重要性,那么什么时候需要记录日志。
+
+- **系统初始化**:系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录 INFO 日志,打印出参数以及启动完成态服务表述。
+- **编程语言提示异常**:如今各类主流的编程语言都包括异常机制,业务相关的流行框架有完整的异常模块。这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用 WARN 或者 ERROR 级别。
+- **业务流程预期不符**:除开平台以及编程语言异常之外,项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
+- **系统核心角色,组件关键动作**:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录 INFO 级别日志,比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改;核心组件运行等等,如果日志频度高或者打印量特别大,可以提炼关键点 INFO 记录,其余酌情考虑 DEBUG 级别。
+- **第三方服务远程调用**:微服务架构体系中有一个重要的点就是第三方永远不可信,对于第三方服务远程调用建议打印请求和响应的参数,方便在和各个终端定位问题,不会因为第三方服务日志的缺失变得手足无措。
+
+## **日志级别**
+
+常用的分为以下几种:
+
+**1、ERROR**
+
+**影响到程序正常运行、当前请求正常运行的异常情况**。如:
+
+1)打开配置文件失败
+
+2)所有第三方对接的异常(包括第三方返回错误码)
+
+3)所有影响功能使用的异常,包括:SQLException 、空指针异常以及业务异常之外的所有异常
+
+**2、WARN**
+
+**告警日志。不应该出现但是不影响程序、当前请求正常运行的异常情况。**
+
+但是一旦出现了也需要关注,因此一般该级别的日志达到一定的阈值之后,就得提示给用户或者需要关注的人了。如:
+
+1)有**容错机制**的时候出现的错误情况
+
+2)找不到配置文件,但是系统能自动创建配置文件
+
+**3、INFO**
+
+记录输入输出、程序关键节点等必要信息。平时无需关注,但出问题时可根据INFO日志诊断出问题
+
+1)Service方法中对于系统/业务状态的变更
+
+2)调用第三方时的调用参数和调用结果(入参和出参)
+
+3)提供方,需要记录入参
+
+4)定时任务的开始执行与结束执行
+
+**4、DEBUG**
+
+调试信息,对系统每一步的运行状态进行精确的记录。
+
+**5、TRACE**
+
+特别详细的服务调用流程各个节点信息。业务代码中,除非涉及到多级服务的调用,否则不要使用(除非有特殊用意,否则请使用DEBUG级别替代)
+
+## **日志打印规范**
+
+1、`增删改`操作需要打印参数日志(以便定位一些异常业务问题);
+
+2、`条件分支`需要打印日志:包括条件值以及重要参数;
+
+3、明确日志打印`级别`与包含的`信息`
+
+1)`提供方`服务,建议以 **INFO** 级别记录入参,出参可选
+
+2)`消费队列消息`,务必打印消息内容
+
+3)`调用方服务`,建议以 **INFO** 级别记录入参和出参
+
+4)`运行环境问题`,如网络错误、建议以 **WARN** 级别记录错误堆栈
+
+5)`定时任务`,务必打印任务开始时间、结束时间。涉及扫描数据的任务,务必打印扫描范围
+
+4、异常信息应该包括`两类信息`:`案发现场信息`和`异常堆栈信息`。如果不处理,那么通过关键字**throws/throw** 往上抛出,由父级方法处理
+
+5、`谨慎地记录日志`
+
+1)`生产环境禁止输出 debug 日志`
+
+2)有选择地输出 **info** 日志
+
+3)如果使用 **warn** 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志
+
+6、可以使用 **warn** 日志级别来记录用户输入参数错误的情况
+
+7、对 `trace/debug/info`级别的日志输出,必须使用`条件输出形式`或者使用`占位符`的方式
+
+8、`不允许记录日志后又抛出异常`,因为这样会多次记录日志,只允许记录一次日志
+
+9、不允许出现`System print`(包括System.out.println和System.error.println)语句作为日志的打印
+
+10、不允许出现 `e.printStackTrace`
+
+11、`日志性能的考虑`。如果代码为核心代码,执行频率非常高,则输出日志建议增加判断,尤其是低级别的输出
+
+12、【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 `SLF4J` 中的 API,使用`门面模式的日志框架`,有利于维护和各个类的日志处理方式统一
+
+13、【强制】`避免重复打印日志,浪费磁盘空间`。务必在 log4j.xml 中设置 `additivity=false`
+
+
+
+> 参考链接:
+>
+> https://cloud.tencent.com/developer/article/1559519
+>
+> https://www.jianshu.com/p/d94cf3069568
+
+
+
diff --git a/docs/other/site-diary.md b/docs/other/site-diary.md
index 116ad7f..477801a 100644
--- a/docs/other/site-diary.md
+++ b/docs/other/site-diary.md
@@ -8,18 +8,38 @@ sidebar: heading
## 更新记录
-- 2022.12.18,新增[order by是怎么工作的?](/advance/excellent-article/13-order-by-work.md)
+- 2024.06.11,更新-Redis大key怎么处理?
-- 2022.12.17,新增[单点登录(SSO)设计与实现](/advance/system-design/8-sso-design.md)
+- 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)。
+
+- 2023.08.10,分布式锁实现-[RedLock](http://topjavaer.cn/advance/distributed/2-distributed-lock.html)补充图片
+
+- 2023.05.04,导航栏增加图标。
+
+- 2023.05.01,新增[Tomcat基础知识总结](/web/tomcat.html)
+
+- 2023.03.25,新增[TCP面试题](/computer-basic/tcp.html)、[MongoDB面试题](/database/mongodb.html)、[ZooKeeper面试题](/zookeeper/zk.html)
+
+- 2023.02.08,新增[10w级别数据Excel导入优化](/advance/system-design)
+
+- 2022.12.18,新增[order by是怎么工作的?](/advance/excellent-article/13-order-by-work.html)
+
+- 2022.12.17,新增[单点登录(SSO)设计与实现](/advance/system-design)
- 2022.12.05,新增[ES的分布式架构原理](/database/es/1-es-architect.html)
-- 2022.11.28,新增[系统设计-微信红包系统如何设计](/advance/system-design/6-wechat-redpacket-design.html)
+- 2022.11.28,新增[系统设计-微信红包系统如何设计](/advance/system-design)
- 2022.11.20,新增[系统设计-短链系统](/advance/system-design/4-short-url.html)
-- 2022.11.20,新增[校招分享-双非本,非科班的自我救赎之路](/campus-recruit/share/1-23-backend.html),[校招分享-秋招还没offer,该怎么办](/campus-recruit/share/2-no-offer.html)
-
- 2022.11.14,[修复Java集合uml图错误](https://topjavaer.cn/java/java-collection.html#%E5%B8%B8%E8%A7%81%E7%9A%84%E9%9B%86%E5%90%88%E6%9C%89%E5%93%AA%E4%BA%9B)
- 2022.11.12,[120道LeetCode题解(高频)](/leetcode/README.md),已整理**58**道
diff --git a/docs/practice/service-performance-optimization.md b/docs/practice/service-performance-optimization.md
index 68ba334..ec2d0af 100644
--- a/docs/practice/service-performance-optimization.md
+++ b/docs/practice/service-performance-optimization.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 线上接口很慢怎么办?
+category: 实践经验
+tag:
+ - 实践经验
+head:
+ - - meta
+ - name: keywords
+ content: 线上接口很慢怎么处理
+ - - meta
+ - name: description
+ content: 编程实践经验分享
+---
+
## 线上接口很慢怎么办?
首先需要明确一个问题,是只有**一个接口**变慢,还是**多个接口**变慢。
@@ -22,4 +37,4 @@
-> 参考链接:https://juejin.cn/post/7064140627578978334
\ No newline at end of file
+> 参考链接:https://juejin.cn/post/7064140627578978334
diff --git a/docs/redis/article/cache-db-consistency.md b/docs/redis/article/cache-db-consistency.md
index 340c60a..088fa50 100644
--- a/docs/redis/article/cache-db-consistency.md
+++ b/docs/redis/article/cache-db-consistency.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: 缓存和数据库一致性问题,看这篇就够了
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: 缓存和数据库一致性问题,缓存一致性
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
> 本文转自水滴与银弹
如何保证缓存和数据库一致性,这是一个老生常谈的话题了。
@@ -378,4 +393,4 @@
4、订阅变更日志的思想,本质是把权威数据源(例如 MySQL)当做 leader 副本,让其它异质系统(例如 Redis / Elasticsearch)成为它的 follower 副本,通过同步变更日志的方式,保证 leader 和 follower 之间保持一致
-很多一致性问题,都会采用这些方案来解决,希望我的这些心得对你有所启发。
\ No newline at end of file
+很多一致性问题,都会采用这些方案来解决,希望我的这些心得对你有所启发。
diff --git a/docs/redis/article/redis-cluster-work.md b/docs/redis/article/redis-cluster-work.md
index ce1ac7c..94e7129 100644
--- a/docs/redis/article/redis-cluster-work.md
+++ b/docs/redis/article/redis-cluster-work.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis 集群模式的工作原理
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis集群模式
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
## Redis 集群模式的工作原理
Redis 集群模式的工作原理?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?
@@ -132,4 +147,4 @@ Redis cluster 的高可用的原理,几乎跟哨兵是类似的。
-> 参考链接:https://doocs.github.io/advanced-java/#/docs/high-concurrency/redis-cluster
\ No newline at end of file
+> 参考链接:https://doocs.github.io/advanced-java/#/docs/high-concurrency/redis-cluster
diff --git a/docs/redis/article/redis-duration.md b/docs/redis/article/redis-duration.md
index b59c2f6..1aea863 100644
--- a/docs/redis/article/redis-duration.md
+++ b/docs/redis/article/redis-duration.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis持久化详解
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis持久化
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# Redis持久化详解
**一、持久化简介**
@@ -172,4 +187,4 @@ Redis 同样也提供了另外两种策略,一个是 **永不 `fsync`**,来

-于是在 Redis 重启的时候,可以先加载 `rdb` 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
\ No newline at end of file
+于是在 Redis 重启的时候,可以先加载 `rdb` 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
diff --git a/docs/redis/article/redis-multi-thread.md b/docs/redis/article/redis-multi-thread.md
index dbab01c..b8f88f3 100644
--- a/docs/redis/article/redis-multi-thread.md
+++ b/docs/redis/article/redis-multi-thread.md
@@ -1,4 +1,17 @@
-
+---
+sidebar: heading
+title: 为什么Redis 6.0 引入多线程
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: redis多线程,redis6.0
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
> 本文转载自Hollis
@@ -162,4 +175,4 @@ https://xie.infoq.cn/article/b3816e9fe3ac77684b4f29348
https://jishuin.proginn.com/p/763bfbd2a1c2
-《极客时间:Redis核心技术与实战》
\ No newline at end of file
+《极客时间:Redis核心技术与实战》
diff --git a/docs/redis/redis-basic/1-introduce.md b/docs/redis/redis-basic/1-introduce.md
index a1aa20f..a7d3657 100644
--- a/docs/redis/redis-basic/1-introduce.md
+++ b/docs/redis/redis-basic/1-introduce.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis简介
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis简介,redis优缺点,io多路复用,Memcached和Redis的区别,redis应用场景
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 简介
Redis是一个高性能的key-value数据库。Redis对数据的操作都是原子性的。
diff --git a/docs/redis/redis-basic/10-lua.md b/docs/redis/redis-basic/10-lua.md
index 5c0b1d9..8bf0f28 100644
--- a/docs/redis/redis-basic/10-lua.md
+++ b/docs/redis/redis-basic/10-lua.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis LUA脚本
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis LUA脚本,lua脚本
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# LUA脚本
Redis 通过 LUA 脚本创建具有原子性的命令: 当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。
diff --git a/docs/redis/redis-basic/11-deletion-policy.md b/docs/redis/redis-basic/11-deletion-policy.md
index d479872..6754972 100644
--- a/docs/redis/redis-basic/11-deletion-policy.md
+++ b/docs/redis/redis-basic/11-deletion-policy.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis删除策略
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis删除策略
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 删除策略
1. 被动删除。在访问key时,如果发现key已经过期,那么会将key删除。
diff --git a/docs/redis/redis-basic/12-others.md b/docs/redis/redis-basic/12-others.md
index b9b6613..755b96f 100644
--- a/docs/redis/redis-basic/12-others.md
+++ b/docs/redis/redis-basic/12-others.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis其他知识点
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis客户端,redis慢查询,redis数据一致性
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 其他
## 客户端
diff --git a/docs/redis/redis-basic/2-data-type.md b/docs/redis/redis-basic/2-data-type.md
index 1f2c31a..7625375 100644
--- a/docs/redis/redis-basic/2-data-type.md
+++ b/docs/redis/redis-basic/2-data-type.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis数据类型
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis数据类型
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 数据类型
Redis支持五种数据类型:
diff --git a/docs/redis/redis-basic/3-data-structure.md b/docs/redis/redis-basic/3-data-structure.md
index 2d97ee1..34842e1 100644
--- a/docs/redis/redis-basic/3-data-structure.md
+++ b/docs/redis/redis-basic/3-data-structure.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis数据结构
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis数据结构,动态字符串
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 数据结构
## 动态字符串
diff --git a/docs/redis/redis-basic/4-implement.md b/docs/redis/redis-basic/4-implement.md
index ea52186..d846cd0 100644
--- a/docs/redis/redis-basic/4-implement.md
+++ b/docs/redis/redis-basic/4-implement.md
@@ -1,4 +1,19 @@
-# 底层实现
+---
+sidebar: heading
+title: Redis底层实现
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis底层实现
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
+#
## string
diff --git a/docs/redis/redis-basic/5-sort.md b/docs/redis/redis-basic/5-sort.md
index 75969b2..294d874 100644
--- a/docs/redis/redis-basic/5-sort.md
+++ b/docs/redis/redis-basic/5-sort.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis排序
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis排序
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 排序
```
diff --git a/docs/redis/redis-basic/6-transaction.md b/docs/redis/redis-basic/6-transaction.md
index 74b60b5..67f0145 100644
--- a/docs/redis/redis-basic/6-transaction.md
+++ b/docs/redis/redis-basic/6-transaction.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis事务
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis事务
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 事务
事务的原理是将一个事务范围内的若干命令发送给Redis,然后再让Redis依次执行这些命令。
diff --git a/docs/redis/redis-basic/7-message-queue.md b/docs/redis/redis-basic/7-message-queue.md
index aa10a6b..13bc12e 100644
--- a/docs/redis/redis-basic/7-message-queue.md
+++ b/docs/redis/redis-basic/7-message-queue.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis实现消息队列
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis实现消息队列
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 消息队列
使用一个列表,让生产者将任务使用LPUSH命令放进列表,消费者不断用RPOP从列表取出任务。
diff --git a/docs/redis/redis-basic/8-persistence.md b/docs/redis/redis-basic/8-persistence.md
index f109548..7691cb2 100644
--- a/docs/redis/redis-basic/8-persistence.md
+++ b/docs/redis/redis-basic/8-persistence.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis持久化
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis持久化,RDB,AOF
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 持久化
Redis支持两种方式的持久化,一种是RDB的方式,一种是AOF的方式。前者会根据指定的规则定时将内存中的数据存储在硬盘上,而后者在每次执行完命令后将命令记录下来。一般将两者结合使用。
diff --git a/docs/redis/redis-basic/9-cluster.md b/docs/redis/redis-basic/9-cluster.md
index 908fc00..712b514 100644
--- a/docs/redis/redis-basic/9-cluster.md
+++ b/docs/redis/redis-basic/9-cluster.md
@@ -1,3 +1,18 @@
+---
+sidebar: heading
+title: Redis集群
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis集群,主从复制,redis读写分离,哨兵Sentinel
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
+---
+
# 集群
## 主从复制
diff --git a/docs/redis/redis.md b/docs/redis/redis.md
index 8559880..0890449 100644
--- a/docs/redis/redis.md
+++ b/docs/redis/redis.md
@@ -1,8 +1,29 @@
---
sidebar: heading
+title: Redis常见面试题总结
+category: 缓存
+tag:
+ - Redis
+head:
+ - - meta
+ - name: keywords
+ content: Redis面试题,Redis优缺点,Redis应用场景,Redis数据类型,Redis和Memcached,Redis keys命令,Redis事务,Redis持久化机制,Redis内存淘汰策略,缓存常见问题,LUA脚本,RedLock,Redis大key,Redis集群
+ - - meta
+ - name: description
+ content: Redis常见知识点和面试题总结,让天下没有难背的八股文!
---
-
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
+
+## 更新记录
+
+- 2024.1.7,新增[Redis存在线程安全问题吗?](##Redis存在线程安全问题吗?)
+- 2024.5.9,补充[Redis应用场景有哪些?](##Redis应用场景有哪些?)
## Redis是什么?
@@ -31,6 +52,20 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
- **IO多路复用模型**:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。
- **高效的数据结构**:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。
+## 既然Redis那么快,为什么不用它做主数据库,只用它做缓存?
+
+虽然Redis非常快,但它也有一些局限性,不能完全替代主数据库。有以下原因:
+
+**事务处理:**Redis只支持简单的事务处理,对于复杂的事务无能为力,比如跨多个键的事务处理。
+
+**数据持久化:**Redis是内存数据库,数据存储在内存中,如果服务器崩溃或断电,数据可能丢失。虽然Redis提供了数据持久化机制,但有一些限制。
+
+**数据处理:**Redis只支持一些简单的数据结构,比如字符串、列表、哈希表等。如果需要处理复杂的数据结构,比如关系型数据库中的表,那么Redis可能不是一个好的选择。
+
+**数据安全:**Redis没有提供像主数据库那样的安全机制,比如用户认证、访问控制等等。
+
+因此,虽然Redis非常快,但它还有一些限制,不能完全替代主数据库。所以,使用Redis作为缓存是一种很好的方式,可以提高应用程序的性能,并减少数据库的负载。
+
## 讲讲Redis的线程模型?
Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
@@ -42,12 +77,18 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
## Redis应用场景有哪些?
-1. **缓存热点数据**,缓解数据库的压力。
-2. 利用 Redis 原子性的自增操作,可以实现**计数器**的功能,比如统计用户点赞数、用户访问数等。
-3. **分布式锁**。在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
-4. **简单的消息队列**,可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列,实现异步操作。
-5. **限速器**,可用于限制某个用户访问某个接口的频率,比如秒杀场景用于防止用户快速点击带来不必要的压力。
-6. **好友关系**,利用集合的一些命令,比如交集、并集、差集等,实现共同好友、共同爱好之类的功能。
+Redis作为一种优秀的基于key/value的缓存,有非常不错的性能和稳定性,无论是在工作中,还是面试中,都经常会出现。
+
+下面来聊聊Redis的常见的8种应用场景。
+
+1. **缓存热点数据**,缓解数据库的压力。例如:热点数据缓存(例如报表、明星出轨),对象缓存、全页缓存、可以提升热点数据的访问数据。
+2. **计数器**。利用 Redis 原子性的自增操作,可以实现**计数器**的功能,内存操作,性能非常好,非常适用于这些计数场景,比如统计用户点赞数、用户访问数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。
+3. **分布式会话**。集群模式下,在应用不多的情况下一般使用容器自带的session复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以Redis等内存数据库为中心的session服务,session不再由容器管理,而是由session服务及内存数据库管理。
+4. **分布式锁**。在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX(SET if Not eXists) 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
+5. **简单的消息队列**,消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列,实现异步操作。
+6. **限速器**,可用于限制某个用户访问某个接口的频率,比如秒杀场景用于防止用户快速点击带来不必要的压力。
+7. **社交网络**,点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据,Redis提供的哈希、集合等数据结构能很方便的的实现这些功能。
+8. **排行榜**。很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。
## Memcached和Redis的区别?
@@ -110,6 +151,22 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。
+## Redis存在线程安全问题吗?
+
+首先,从Redis 服务端层面分析。
+
+Redis Server本身是一个线程安全的K-V数据库,也就是说在Redis Server上执行的指令,不需要任何同步机制,不会存在线程安全问题。
+
+虽然Redis 6.0里面,增加了多线程的模型,但是增加的多线程只是用来处理网络IO事件,对于指令的执行过程,仍然是由主线程来处理,所以不会存在多个线程通知执行操作指令的情况。
+
+第二个,从Redis客户端层面分析。
+
+虽然Redis Server中的指令执行是原子的,但是如果有多个Redis客户端同时执行多个指令的时候,就无法保证原子性。
+
+假设两个redis client同时获取Redis Server上的key1, 同时进行修改和写入,因为多线程环境下的原子性无法被保障,以及多进程情况下的共享资源访问的竞争问题,使得数据的安全性无法得到保障。
+
+当然,对于客户端层面的线程安全性问题,解决方法有很多,比如尽可能的使用Redis里面的原子指令,或者对多个客户端的资源访问加锁,或者通过Lua脚本来实现多个指令的操作等等。
+
## keys命令存在的问题?
redis的单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是`O(1)`,但是要真正实现keys的功能,需要执行多次scan。
@@ -390,25 +447,95 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
**内存淘汰策略可以通过配置文件来修改**,相应的配置项是`maxmemory-policy`,默认配置是`noeviction`。
-## 如何保证缓存与数据库双写时的数据一致性?
+## MySQL 与 Redis 如何保证数据一致性
+
+**缓存不一致是如何产生的**
+
+如果数据一直没有变更,那么就不会出现缓存不一致的问题。
+
+通常缓存不一致是发生在数据有变更的时候。 因为每次数据变更你需要同时操作数据库和缓存,而他们又属于不同的系统,无法做到同时操作成功或失败,总会有一个时间差。在并发读写的时候可能就会出现缓存不一致的问题(理论上通过分布式事务可以保证这一点,不过实际上基本上很少有人这么做)。
+
+虽然没办法在数据有变更时,保证缓存和数据库强一致,但对缓存的更新还是有一定设计方法的,遵循这些设计方法,能够让这个不一致的影响时间和影响范围最小化。
+
+缓存更新的设计方法大概有以下四种:
-**1、先删除缓存再更新数据库**
+- 先删除缓存,再更新数据库(这种方法在并发下最容易出现长时间的脏数据,不可取)
+- 先更新数据库,删除缓存(Cache Aside Pattern)
+- 只更新缓存,由缓存自己同步更新数据库(Read/Write Through Pattern)
+- 只更新缓存,由缓存自己异步更新数据库(Write Behind Cache Pattern)
-进行更新操作时,先删除缓存,然后更新数据库,后续的请求再次读取时,会从数据库读取后再将新数据更新到缓存。
+**先删除缓存,再更新数据库**
-存在的问题:删除缓存数据之后,更新数据库完成之前,这个时间段内如果有新的读请求过来,就会从数据库读取旧数据重新写到缓存中,再次造成不一致,并且后续读的都是旧数据。
+这种方法在并发读写的情况下容易出现缓存不一致的问题
-**2、先更新数据库再删除缓存**
+
-进行更新操作时,先更新MySQL,成功之后,删除缓存,后续读取请求时再将新数据回写缓存。
+如上图所示,其可能的执行流程顺序为:
-存在的问题:更新MySQL和删除缓存这段时间内,请求读取的还是缓存的旧数据,不过等数据库更新完成,就会恢复一致,影响相对比较小。
+- 客户端1 触发更新数据A的逻辑
+- 客户端2 触发查询数据A的逻辑
+- 客户端1 删除缓存中数据A
+- 客户端2 查询缓存中数据A,未命中
+- 客户端2 从数据库查询数据A,并更新到缓存中
+- 客户端1 更新数据库中数据A
-**3、异步更新缓存**
+可见,最后缓存中的数据A跟数据库中的数据A是不一致的,缓存中的数据A是旧的脏数据。
-数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
+因此一般不建议使用这种方式。
-以上几个方案都不完美,需要根据业务需求,评估哪种方案影响较小,然后选择相应的方案。
+**先更新数据库,再让缓存失效**
+
+这种方法在并发读写的情况下,也可能会出现短暂缓存不一致的问题
+
+
+
+如上图所示,其可能执行的流程顺序为:
+
+- 客户端1 触发更新数据A的逻辑
+- 客户端2 触发查询数据A的逻辑
+- 客户端3 触发查询数据A的逻辑
+- 客户端1 更新数据库中数据A
+- 客户端2 查询缓存中数据A,命中返回(旧数据)
+- 客户端1 让缓存中数据A失效
+- 客户端3 查询缓存中数据A,未命中
+- 客户端3 查询数据库中数据A,并更新到缓存中
+
+可见,最后缓存中的数据A和数据库中的数据A是一致的,理论上可能会出现一小段时间数据不一致,不过这种概率也比较低,大部分的业务也不会有太大的问题。
+
+**只更新缓存,由缓存自己同步更新数据库(Read/Write Through Pattern)**
+
+这种方法相当于是业务只更新缓存,再由缓存去同步更新数据库。 一个Write Through的 例子如下:
+
+
+
+如上图所示,其可能执行的流程顺序为:
+
+- 客户端1 触发更新数据A的逻辑
+- 客户端2 触发查询数据A的逻辑
+- 客户端1 更新缓存中数据A,缓存同步更新数据库中数据A,再返回结果
+- 客户端2 查询缓存中数据A,命中返回
+
+Read Through 和 WriteThrough 的流程类似,只是在客户端查询数据A时,如果缓存中数据A失效了(过期或被驱逐淘汰),则缓存会同步去数据库中查询数据A,并缓存起来,再返回给客户端
+
+这种方式缓存不一致的概率极低,只不过需要对缓存进行专门的改造。
+
+**只更新缓存,由缓存自己异步更新数据库(Write Behind Cache Pattern)**
+
+这种方式性详单于是业务只操作更新缓存,再由缓存异步去更新数据库,例如:
+
+
+
+如上图所示,其可能的执行流程顺序为:
+
+- 客户端1 触发更新数据A的逻辑
+- 客户端2 触发查询数据A的逻辑
+- 客户端1 更新缓存中的数据A,返回
+- 客户端2 查询缓存中的数据A,命中返回
+- 缓存异步更新数据A到数据库中
+
+这种方式的优势是读写的性能都非常好,基本上只要操作完内存后就返回给客户端了,但是其是非强一致性,存在丢失数据的情况。
+
+如果在缓存异步将数据更新到数据库中时,缓存服务挂了,此时未更新到数据库中的数据就丢失了。
## 缓存常见问题
@@ -671,6 +798,121 @@ Redis集群的节点会按照以下规则发ping消息:
3、哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩。bitmap的填充率越低,**压缩率**越高。其中bitmap 填充率 = slots / N (N表示节点数)。所以,插槽数越低, 填充率会降低,压缩率会提高。
+## Redis存在线程安全的问题吗
+
+首先从Redis 服务端层面来看。
+
+Redis Server本身是一个线程安全的K-V数据库,也就是说在Redis Server上执行的指令,不需要任何同步机制,不会存在线程安全问题。
+
+虽然Redis 6.0里面,增加了多线程的模型,但是增加的多线程只是用来处理网络IO事件,对于指令的执行过程,仍然是由主线程来处理,所以不会存在多个线程通知执行操作指令的情况。
+
+然后从Redis客户端层面来看。
+
+虽然Redis Server中的指令执行是原子的,但是如果有多个Redis客户端同时执行多个指令的时候,就无法保证原子性。
+
+假设两个redis client同时获取Redis Server上的key1, 同时进行修改和写入,因为多线程环境下的原子性无法被保障,以及多进程情况下的共享资源访问的竞争问题,使得数据的安全性无法得到保障。
+
+对于客户端层面的线程安全性问题,解决方法有很多,比如尽可能的使用Redis里面的原子指令,或者对多个客户端的资源访问加锁,或者通过Lua脚本来实现多个指令的操作等等。
+
+
+
+## Redis遇到哈希冲突怎么办?
+
+当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时, 我们称这些键发生了冲突(collision)。
+
+Redis 的哈希表使用链地址法(separate chaining)来解决键冲突: 每个哈希表节点都有一个 `next` 指针, 多个哈希表节点可以用 `next` 指针构成一个单向链表, 被分配到同一个索引上的多个节点可以用这个单向链表连接起来, 这就解决了键冲突的问题。
+
+原理跟 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设置任意值,伪代码如下:
+
+
+
+缺陷:加锁与设置过期时间是非原子操作,如果加锁后未来得及设置过期时间系统异常等,会导致其他线程永远获取不到锁。
+
+**Redis分布式锁方案二**:SETNX+value值(系统时间+过期时间)
+
+为了解决方案一,「发生异常锁得不到释放的场景」,有小伙伴认为,可以把过期时间放到setnx的value值里面。如果加锁失败,再拿出value值校验一下即可。
+
+这个方案的优点是,避免了expire 单独设置过期时间的操作,把「过期时间放到setnx的value值」里面来。解决了方案一发生异常,锁得不到释放的问题。
+
+但是这个方案有别的缺点:过期时间是客户端自己生成的(System.currentTimeMillis()是当前系统的时间),必须要求分布式环境下,每个客户端的时间必须同步。如果锁过期的时候,并发多个客户端同时请求过来,都执行jedis.get()和set(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。该锁没有保存持有者的唯一标识,可能坡别的客户端释放/解锁
+
+**分布式锁方案三:使用Lua脚本(包含SETNX+EXPIRE两条指令)**
+
+实际上,我们还可以使用Lua脚本来保证原子性(包含setnx和expire两条指令),lua脚本如下:
+
+
+
+加锁代码如下:
+
+
+
+**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存在时设置值。
+
+伪代码如下:
+
+
+
+**Redis分布式锁方案五:Redisson框架**
+
+方案四还是可能存在「锁过期释放,业务没执行完」的问题。设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。当前开源框架Redisson解决了这个问题。一起来看下Redisson底层原理图:
+
+
+
+只要线程一加锁成功,就会启动一个watchdog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了「锁过期释放,业务没执行完」问题。
+
+**分布式锁方案六:多机实现的分布式锁Redlock+Redisson**
+
+前面五种方案都是基于单机版的讨论,那么集群部署该怎么处理?
+
+答案是多机实现的分布式锁Redlock+Redisson
+

diff --git a/docs/snippets/ads.md b/docs/snippets/ads.md
new file mode 100644
index 0000000..31525ce
--- /dev/null
+++ b/docs/snippets/ads.md
@@ -0,0 +1,6 @@
+::: tip 这是一则或许对你有帮助的信息
+
+- **面试手册**:这是一份大彬精心整理的[**大厂面试手册**](https://topjavaer.cn/zsxq/mianshishouce.html)最新版,目前已经更新迭代了**19**个版本,质量很高(专为面试打造)
+- **知识星球**:**专属面试手册/一对一交流/简历修改/超棒的学习氛围/学习路线规划**,欢迎加入[大彬的知识星球](https://topjavaer.cn/zsxq/introduce.html)(点击链接查看星球的详细介绍)
+
+:::
\ No newline at end of file
diff --git "a/docs/source/mybatis/# MyBatis \346\272\220\347\240\201\345\210\206\346\236\220\357\274\210\344\270\203\357\274\211\357\274\232\346\216\245\345\217\243\345\261\202.md" "b/docs/source/mybatis/# MyBatis \346\272\220\347\240\201\345\210\206\346\236\220\357\274\210\344\270\203\357\274\211\357\274\232\346\216\245\345\217\243\345\261\202.md"
new file mode 100644
index 0000000..b691b56
--- /dev/null
+++ "b/docs/source/mybatis/# MyBatis \346\272\220\347\240\201\345\210\206\346\236\220\357\274\210\344\270\203\357\274\211\357\274\232\346\216\245\345\217\243\345\261\202.md"
@@ -0,0 +1,266 @@
+# MyBatis 源码分析(七):接口层
+
+## sql 会话创建工厂
+
+`SqlSessionFactoryBuilder` 经过复杂的解析逻辑之后,会根据全局配置创建 `DefaultSqlSessionFactory`,该类是 `sql` 会话创建工厂抽象接口 `SqlSessionFactory` 的默认实现,其提供了若干 `openSession` 方法用于打开一个会话,在会话中进行相关数据库操作。这些 `openSession` 方法最终都会调用 `openSessionFromDataSource` 或 `openSessionFromConnection` 创建会话,即基于数据源配置创建还是基于已有连接对象创建。
+
+### 基于数据源配置创建会话
+
+要使用数据源打开一个会话需要先从全局配置中获取当前生效的数据源环境配置,如果没有生效配置或没用设置可用的事务工厂,就会创建一个 `ManagedTransactionFactory` 实例作为默认事务工厂实现,其与 `MyBatis` 提供的另一个事务工厂实现 `JdbcTransactionFactory` 的区别在于其生成的事务实现 `ManagedTransaction` 的提交和回滚方法是空实现,即希望将事务管理交由外部容器管理。
+
+```java
+ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
+ Transaction tx = null;
+ try {
+ final Environment environment = configuration.getEnvironment();
+ // 获取事务工厂
+ final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
+ // 创建事务配置
+ tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
+ // 创建执行器
+ final Executor executor = configuration.newExecutor(tx, execType);
+ // 创建 sql 会话
+ return new DefaultSqlSession(configuration, executor, autoCommit);
+ } catch (Exception e) {
+ closeTransaction(tx); // may have fetched a connection so lets call close()
+ throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+
+ /**
+ * 获取生效数据源环境配置的事务工厂
+ */
+ private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
+ if (environment == null || environment.getTransactionFactory() == null) {
+ // 未配置数据源环境或事务工厂,默认使用 ManagedTransactionFactory
+ return new ManagedTransactionFactory();
+ }
+ return environment.getTransactionFactory();
+ }
+```
+
+随后会根据入参传入的 `execType` 选择对应的执行器 `Executor`,`execType` 的取值来源于 `ExecutorType`,这是一个枚举类。在下一章将会详细分析各类 `Executor` 的作用及其实现。
+
+获取到事务工厂配置和执行器对象后会结合传入的数据源自动提交属性创建 `DefaultSqlSession`,即 `sql` 会话对象。
+
+### 基于数据库连接创建会话
+
+基于连接创建会话的流程大致与基于数据源配置创建相同,区别在于自动提交属性 `autoCommit` 是从连接对象本身获取的。
+
+```java
+ private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
+ try {
+ // 获取自动提交配置
+ boolean autoCommit;
+ try {
+ autoCommit = connection.getAutoCommit();
+ } catch (SQLException e) {
+ // Failover to true, as most poor drivers
+ // or databases won't support transactions
+ autoCommit = true;
+ }
+ final Environment environment = configuration.getEnvironment();
+ // 获取事务工厂
+ final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
+ // 创建事务配置
+ final Transaction tx = transactionFactory.newTransaction(connection);
+ // 创建执行器
+ final Executor executor = configuration.newExecutor(tx, execType);
+ // 创建 sql 会话
+ return new DefaultSqlSession(configuration, executor, autoCommit);
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+```
+
+## sql 会话
+
+`SqlSession` 是 `MyBatis` 面向用户编程的接口,其提供了一系列方法用于执行相关数据库操作,默认实现为 `DefaultSqlSession`,在该类中,增删查改对应的操作最终会调用 `selectList`、`select` 和 `update` 方法,其分别用于普通查询、执行存储过程和修改数据库记录。
+
+```java
+ /**
+ * 查询结果集
+ */
+ @Override
+ public List selectList(String statement, Object parameter, RowBounds rowBounds) {
+ try {
+ MappedStatement ms = configuration.getMappedStatement(statement);
+ return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+
+ /**
+ * 调用存储过程
+ */
+ @Override
+ public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
+ try {
+ MappedStatement ms = configuration.getMappedStatement(statement);
+ executor.query(ms, wrapCollection(parameter), rowBounds, handler);
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+
+ /**
+ * 修改
+ */
+ @Override
+ public int update(String statement, Object parameter) {
+ try {
+ dirty = true;
+ MappedStatement ms = configuration.getMappedStatement(statement);
+ return executor.update(ms, wrapCollection(parameter));
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+```
+
+以上操作均是根据传入的 `statement` 名称到全局配置中查找对应的 `MappedStatement` 对象,并将操作委托给执行器对象 `executor` 完成。`select`、`selectMap` 等方法则是对 `selectList` 方法返回的结果集做处理来实现的。
+
+此外,提交和回滚方法也是基于 `executor` 实现的。
+
+```java
+ /**
+ * 提交事务
+ */
+ @Override
+ public void commit(boolean force) {
+ try {
+ executor.commit(isCommitOrRollbackRequired(force));
+ dirty = false;
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+
+ /**
+ * 回滚事务
+ */
+ @Override
+ public void rollback(boolean force) {
+ try {
+ executor.rollback(isCommitOrRollbackRequired(force));
+ dirty = false;
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+ }
+
+ /**
+ * 非自动提交且事务未提交 || 强制提交或回滚 时返回 true
+ */
+ private boolean isCommitOrRollbackRequired(boolean force) {
+ return (!autoCommit && dirty) || force;
+ }
+```
+
+在执行 `update` 方法时,会设置 `dirty` 属性为 `true` ,意为事务还未提交,当事务提交或回滚后才会将 `dirty` 属性修改为 `false`。如果当前会话不是自动提交且 `dirty` 熟悉为 `true`,或者设置了强制提交或回滚的标志,则会将强制标志提交给 `executor` 处理。
+
+## Sql 会话管理器
+
+`SqlSessionManager` 同时实现了 `SqlSessionFactory` 和 `SqlSession` 接口,使得其既能够创建 `sql` 会话,又能够执行 `sql` 会话的相关数据库操作。
+
+```java
+ /**
+ * sql 会话创建工厂
+ */
+ private final SqlSessionFactory sqlSessionFactory;
+
+ /**
+ * sql 会话代理对象
+ */
+ private final SqlSession sqlSessionProxy;
+
+ /**
+ * 保存线程对应 sql 会话
+ */
+ private final ThreadLocal localSqlSession = new ThreadLocal<>();
+
+ private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
+ this.sqlSessionFactory = sqlSessionFactory;
+ this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
+ SqlSessionFactory.class.getClassLoader(),
+ new Class[]{SqlSession.class},
+ new SqlSessionInterceptor());
+ }
+
+ @Override
+ public SqlSession openSession() {
+ return sqlSessionFactory.openSession();
+ }
+
+ /**
+ * 设置当前线程对应的 sql 会话
+ */
+ public void startManagedSession() {
+ this.localSqlSession.set(openSession());
+ }
+
+ /**
+ * sql 会话代理逻辑
+ */
+ private class SqlSessionInterceptor implements InvocationHandler {
+
+ public SqlSessionInterceptor() {
+ // Prevent Synthetic Access
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ // 获取当前线程对应的 sql 会话对象并执行对应方法
+ final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
+ if (sqlSession != null) {
+ try {
+ return method.invoke(sqlSession, args);
+ } catch (Throwable t) {
+ throw ExceptionUtil.unwrapThrowable(t);
+ }
+ } else {
+ // 如果当前线程没有对应的 sql 会话,默认创建不自动提交的 sql 会话
+ try (SqlSession autoSqlSession = openSession()) {
+ try {
+ final Object result = method.invoke(autoSqlSession, args);
+ autoSqlSession.commit();
+ return result;
+ } catch (Throwable t) {
+ autoSqlSession.rollback();
+ throw ExceptionUtil.unwrapThrowable(t);
+ }
+ }
+ }
+ }
+ }
+```
+
+`SqlSessionManager` 的构造方法要求 `SqlSessionFactory` 对象作为入参传入,其各个创建会话的方法实际是由该传入对象完成的。执行 `sql` 会话的操作由 `sqlSessionProxy` 对象完成,这是一个由 `JDK` 动态代理创建的对象,当执行方法时会去 `ThreadLocal` 对象中查找当前线程有没有对应的 `sql` 会话对象,如果有则使用已有的会话对象执行,否则创建新的会话对象执行,而线程对应的会话对象需要使用 `startManagedSession` 方法来维护。
+
+之所以 `SqlSessionManager` 需要为每个线程维护会话对象,是因为 `DefaultSqlSession` 是非线程安全的,多线程操作会导致执行错误。如上文中提到的 `dirty` 属性,其修改是没有经过任何同步操作的。
+
+## 小结
+
+`SqlSession` 是 `MyBatis` 提供的面向开发者编程的接口,其提供了一系列数据库相关操作,并屏蔽了底层细节。使用 `MyBatis` 的正确方式应该是像 `SqlSessionManager` 那样为每个线程创建 `sql` 会话对象,避免造成线程安全问题。
+
+- `org.apache.ibatis.session.SqlSessionFactory`:`sql` 会话创建工厂。
+- `org.apache.ibatis.session.defaults.DefaultSqlSessionFactory`: `sql` 会话创建工厂默认实现。
+- `org.apache.ibatis.session.SqlSession`:`sql` 会话。
+- `org.apache.ibatis.session.defaults.DefaultSqlSession`:`sql` 会话默认实现。
+- `org.apache.ibatis.session.SqlSessionManager`:`sql` 会话管理器
\ No newline at end of file
diff --git a/docs/source/mybatis/1-overview.md b/docs/source/mybatis/1-overview.md
new file mode 100644
index 0000000..550a96a
--- /dev/null
+++ b/docs/source/mybatis/1-overview.md
@@ -0,0 +1,43 @@
+---
+sidebar: heading
+title: MyBatis源码分析
+category: 源码分析
+tag:
+ - MyBatis
+head:
+ - - meta
+ - name: keywords
+ content: MyBatis面试题,MyBatis源码分析,MyBatis整体架构,MyBatis源码,Hibernate,Executor,MyBatis分页,MyBatis插件运行原理,MyBatis延迟加载,MyBatis预编译,一级缓存和二级缓存
+ - - meta
+ - name: description
+ content: 高质量的MyBatis源码分析总结
+---
+
+`MyBatis` 是一款旨在帮助开发人员屏蔽底层重复性原生 `JDBC` 代码的持久化框架,其支持通过映射文件配置或注解将 `ResultSet` 映射为 `Java` 对象。相对于其它 `ORM` 框架,`MyBatis` 更为轻量级,支持定制化 `SQL` 和动态 `SQL`,方便优化查询性能,同时包含了良好的缓存机制。
+
+## MyBatis 整体架构
+
+
+
+### 基础支持层
+
+- 反射模块:提供封装的反射 `API`,方便上层调用。
+- 类型转换:为简化配置文件提供了别名机制,并且实现了 `Java` 类型和 `JDBC` 类型的互转。
+- 日志模块:能够集成多种第三方日志框架。
+- 资源加载模块:对类加载器进行封装,提供加载类文件和其它资源文件的功能。
+- 数据源模块:提供数据源实现并能够集成第三方数据源模块。
+- 事务管理:可以和 `Spring` 集成开发,对事务进行管理。
+- 缓存模块:提供一级缓存和二级缓存,将部分请求拦截在缓存层。
+- `Binding` 模块:在调用 `SqlSession` 相应方法执行数据库操作时,需要指定映射文件中的 `SQL` 节点,`MyBatis` 通过 `Binding` 模块将自定义 `Mapper` 接口与映射文件关联,避免拼写等错误导致在运行时才发现相应异常。
+
+### 核心处理层
+
+- 配置解析:`MyBatis` 初始化时会加载配置文件、映射文件和 `Mapper` 接口的注解信息,解析后会以对象的形式保存到 `Configuration` 对象中。
+- `SQL` 解析与 `scripting` 模块:`MyBatis` 支持通过配置实现动态 `SQL`,即根据不同入参生成 `SQL`。
+- `SQL` 执行与结果解析:`Executor` 负责维护缓存和事务管理,并将数据库相关操作委托给 `StatementHandler`,`ParmeterHadler` 负责完成 `SQL` 语句的实参绑定并通过 `Statement` 对象执行 `SQL`,通过 `ResultSet` 返回结果,交由 `ResultSetHandler` 处理。
+
+- 插件:支持开发者通过插件接口对 `MyBatis` 进行扩展。
+
+### 接口层
+
+`SqlSession` 接口定义了暴露给应用程序调用的 `API`,接口层在收到请求时会调用核心处理层的相应模块完成具体的数据库操作。
\ No newline at end of file
diff --git a/docs/source/mybatis/2-reflect.md b/docs/source/mybatis/2-reflect.md
new file mode 100644
index 0000000..06e4ea7
--- /dev/null
+++ b/docs/source/mybatis/2-reflect.md
@@ -0,0 +1,634 @@
+---
+sidebar: heading
+title: MyBatis源码分析
+category: 源码分析
+tag:
+ - MyBatis
+head:
+ - - meta
+ - name: keywords
+ content: MyBatis面试题,MyBatis源码分析,MyBatis整体架构,MyBatis反射,MyBatis源码,Hibernate,Executor,MyBatis分页,MyBatis插件运行原理,MyBatis延迟加载,MyBatis预编译,一级缓存和二级缓存
+ - - meta
+ - name: description
+ content: 高质量的MyBatis源码分析总结
+---
+
+大家好,我是大彬。今天分享Mybatis源码的反射模块。
+
+`MyBatis` 在进行参数处理、结果映射时等操作时,会涉及大量的反射操作。为了简化这些反射相关操作,`MyBatis` 在 `org.apache.ibatis.reflection` 包下提供了专门的反射模块,对反射操作做了近一步封装,提供了更为简洁的 `API`。
+
+## 缓存类的元信息
+
+`MyBatis` 提供 `Reflector` 类来缓存类的字段名和 `getter/setter` 方法的元信息,使得反射时有更好的性能。使用方式是将原始类对象传入其构造方法,生成 `Reflector` 对象。
+
+```java
+ public Reflector(Class> clazz) {
+ type = clazz;
+ // 如果存在,记录无参构造方法
+ addDefaultConstructor(clazz);
+ // 记录字段名与get方法、get方法返回值的映射关系
+ addGetMethods(clazz);
+ // 记录字段名与set方法、set方法参数的映射关系
+ addSetMethods(clazz);
+ // 针对没有getter/setter方法的字段,通过Filed对象的反射来设置和读取字段值
+ addFields(clazz);
+ // 可读的字段名
+ readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
+ // 可写的字段名
+ writablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
+ // 保存一份所有字段名大写与原始字段名的隐射
+ for (String propName : readablePropertyNames) {
+ caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
+ }
+ for (String propName : writablePropertyNames) {
+ caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
+ }
+ }
+```
+
+`addGetMethods` 和 `addSetMethods` 分别获取类的所有方法,从符合 `getter/setter` 规范的方法中解析出字段名,并记录方法的参数类型、返回值类型等信息:
+
+```java
+ private void addGetMethods(Class> cls) {
+ // 字段名-get方法
+ Map> conflictingGetters = new HashMap<>();
+ // 获取类的所有方法,及其实现接口的方法,并根据方法签名去重
+ Method[] methods = getClassMethods(cls);
+ for (Method method : methods) {
+ if (method.getParameterTypes().length > 0) {
+ // 过滤有参方法
+ continue;
+ }
+ String name = method.getName();
+ if ((name.startsWith("get") && name.length() > 3)
+ || (name.startsWith("is") && name.length() > 2)) {
+ // 由get属性获取对应的字段名(去除前缀,首字母转小写)
+ name = PropertyNamer.methodToProperty(name);
+ addMethodConflict(conflictingGetters, name, method);
+ }
+ }
+ // 保证每个字段只对应一个get方法
+ resolveGetterConflicts(conflictingGetters);
+ }
+```
+
+对 `getter/setter` 方法进行去重是通过类似 `java.lang.String#getSignature:java.lang.reflect.Method` 的方法签名来实现的,如果子类在实现过程中,参数、返回值使用了不同的类型(使用原类型的子类),则会导致方法签名不一致,同一字段就会对应不同的 `getter/setter` 方法,因此需要进行去重。
+
+> 分享一份大彬精心整理的大厂面试手册,包含计**算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享**等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~
+>
+> 
+>
+> 
+>
+> 需要的小伙伴可以自行**下载**:
+>
+> 链接:https://pan.xunlei.com/s/VNgU60NQQNSDaEy9z955oufbA1?pwd=y9fy#
+>
+> 备用链接:https://pan.quark.cn/s/cbbb681e7c19
+
+```java
+ private void resolveGetterConflicts(Map> conflictingGetters) {
+ for (Entry> entry : conflictingGetters.entrySet()) {
+ Method winner = null;
+ // 属性名
+ String propName = entry.getKey();
+ for (Method candidate : entry.getValue()) {
+ if (winner == null) {
+ winner = candidate;
+ continue;
+ }
+ // 字段对应了多个get方法
+ Class> winnerType = winner.getReturnType();
+ Class> candidateType = candidate.getReturnType();
+ if (candidateType.equals(winnerType)) {
+ // 返回值类型相同
+ if (!boolean.class.equals(candidateType)) {
+ throw new ReflectionException(
+ "Illegal overloaded getter method with ambiguous type for property "
+ + propName + " in class " + winner.getDeclaringClass()
+ + ". This breaks the JavaBeans specification and can cause unpredictable results.");
+ } else if (candidate.getName().startsWith("is")) {
+ // 返回值为boolean的get方法可能有多个,如getIsSave和isSave,优先取is开头的
+ winner = candidate;
+ }
+ } else if (candidateType.isAssignableFrom(winnerType)) {
+ // OK getter type is descendant
+ // 可能会出现接口中的方法返回值是List,子类实现方法返回值是ArrayList,使用子类返回值方法
+ } else if (winnerType.isAssignableFrom(candidateType)) {
+ winner = candidate;
+ } else {
+ throw new ReflectionException(
+ "Illegal overloaded getter method with ambiguous type for property "
+ + propName + " in class " + winner.getDeclaringClass()
+ + ". This breaks the JavaBeans specification and can cause unpredictable results.");
+ }
+ }
+ // 记录字段名对应的get方法对象和返回值类型
+ addGetMethod(propName, winner);
+ }
+ }
+```
+
+去重的方式是使用更规范的方法以及使用子类的方法。在确认字段名对应的唯一 `getter/setter` 方法后,记录方法名对应的方法、参数、返回值等信息。`MethodInvoker` 可用于调用 `Method` 类的 `invoke` 方法来执行 `getter/setter` 方法(`addSetMethods` 记录映射关系的方式与 `addGetMethods` 大致相同)。
+
+```java
+private void addGetMethod(String name, Method method) {
+ // 过滤$开头、serialVersionUID的get方法和getClass()方法
+ if (isValidPropertyName(name)) {
+ // 字段名-对应get方法的MethodInvoker对象
+ getMethods.put(name, new MethodInvoker(method));
+ Type returnType = TypeParameterResolver.resolveReturnType(method, type);
+ // 字段名-运行时方法的真正返回类型
+ getTypes.put(name, typeToClass(returnType));
+ }
+}
+```
+
+接下来会执行 `addFields` 方法,此方法针对没有 `getter/setter` 方法的字段,通过包装为 `SetFieldInvoker` 在需要时通过 `Field` 对象的反射来设置和读取字段值。
+
+```java
+private void addFields(Class> clazz) {
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ if (!setMethods.containsKey(field.getName())) {
+ // issue #379 - removed the check for final because JDK 1.5 allows
+ // modification of final fields through reflection (JSR-133). (JGB)
+ // pr #16 - final static can only be set by the classloader
+ int modifiers = field.getModifiers();
+ if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
+ // 非final的static变量,没有set方法,可以通过File对象做赋值操作
+ addSetField(field);
+ }
+ }
+ if (!getMethods.containsKey(field.getName())) {
+ addGetField(field);
+ }
+ }
+ if (clazz.getSuperclass() != null) {
+ // 递归查找父类
+ addFields(clazz.getSuperclass());
+ }
+}
+```
+
+## 抽象字段赋值与读取
+
+`Invoker` 接口用于抽象设置和读取字段值的操作。对于有 `getter/setter` 方法的字段,通过 `MethodInvoker` 反射执行;对应其它字段,通过 `GetFieldInvoker` 和 `SetFieldInvoker` 操作 `Field` 对象的 `getter/setter` 方法反射执行。
+
+```java
+/**
+ * 用于抽象设置和读取字段值的操作
+ *
+ * {@link MethodInvoker} 反射执行getter/setter方法
+ * {@link GetFieldInvoker} {@link SetFieldInvoker} 反射执行Field对象的get/set方法
+ *
+ * @author Clinton Begin
+ */
+public interface Invoker {
+
+ /**
+ * 通过反射设置或读取字段值
+ *
+ * @param target
+ * @param args
+ * @return
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
+
+ /**
+ * 字段类型
+ *
+ * @return
+ */
+ Class> getType();
+}
+```
+
+## 解析参数类型
+
+针对 `Java-Type` 体系的多种实现,`TypeParameterResolver` 提供一系列方法来解析指定类中的字段、方法返回值或方法参数的类型。
+
+`Type` 接口包含 4 个子接口和 1 个实现类:
+
+
+
+- `Class`:原始类型
+- `ParameterizedType`:泛型类型,如:`List`
+- `TypeVariable`:泛型类型变量,如: `List` 中的 `T`
+- `GenericArrayType`:组成元素是 `ParameterizedType` 或 `TypeVariable` 的数组类型,如:`List[]`、`T[]`
+- `WildcardType`:通配符泛型类型变量,如:`List>` 中的 `?`
+
+`TypeParameterResolver` 分别提供 `resolveFieldType`、`resolveReturnType`、`resolveParamTypes` 方法用于解析字段类型、方法返回值类型和方法入参类型,这些方法均调用 `resolveType` 来获取类型信息:
+
+```java
+/**
+ * 获取类型信息
+ *
+ * @param type 根据是否有泛型信息签名选择传入泛型类型或简单类型
+ * @param srcType 引用字段/方法的类(可能是子类,字段和方法在父类声明)
+ * @param declaringClass 字段/方法声明的类
+ * @return
+ */
+private static Type resolveType(Type type, Type srcType, Class> declaringClass) {
+ if (type instanceof TypeVariable) {
+ // 泛型类型变量,如:List 中的 T
+ return resolveTypeVar((TypeVariable>) type, srcType, declaringClass);
+ } else if (type instanceof ParameterizedType) {
+ // 泛型类型,如:List
+ return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
+ } else if (type instanceof GenericArrayType) {
+ // TypeVariable/ParameterizedType 数组类型
+ return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
+ } else {
+ // 原始类型,直接返回
+ return type;
+ }
+}
+```
+
+`resolveTypeVar` 用于解析泛型类型变量参数类型,如果字段或方法在当前类中声明,则返回泛型类型的上界或 `Object` 类型;如果在父类中声明,则递归解析父类;父类也无法解析,则递归解析实现的接口。
+
+```java
+private static Type resolveTypeVar(TypeVariable> typeVar, Type srcType, Class> declaringClass) {
+ Type result;
+ Class> clazz;
+ if (srcType instanceof Class) {
+ // 原始类型
+ clazz = (Class>) srcType;
+ } else if (srcType instanceof ParameterizedType) {
+ // 泛型类型,如 TestObj
+ ParameterizedType parameterizedType = (ParameterizedType) srcType;
+ // 取原始类型TestObj
+ clazz = (Class>) parameterizedType.getRawType();
+ } else {
+ throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
+ }
+
+ if (clazz == declaringClass) {
+ // 字段就是在当前引用类中声明的
+ Type[] bounds = typeVar.getBounds();
+ if (bounds.length > 0) {
+ // 返回泛型类型变量上界,如:T extends String,则返回String
+ return bounds[0];
+ }
+ // 没有上界返回Object
+ return Object.class;
+ }
+
+ // 字段/方法在父类中声明,递归查找父类泛型
+ Type superclass = clazz.getGenericSuperclass();
+ result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
+ if (result != null) {
+ return result;
+ }
+
+ // 递归泛型接口
+ Type[] superInterfaces = clazz.getGenericInterfaces();
+ for (Type superInterface : superInterfaces) {
+ result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
+ if (result != null) {
+ return result;
+ }
+ }
+ return Object.class;
+}
+```
+
+通过调用 `scanSuperTypes` 实现递归解析:
+
+```java
+private static Type scanSuperTypes(TypeVariable> typeVar, Type srcType, Class> declaringClass, Class> clazz, Type superclass) {
+ if (superclass instanceof ParameterizedType) {
+ // 父类是泛型类型
+ ParameterizedType parentAsType = (ParameterizedType) superclass;
+ Class> parentAsClass = (Class>) parentAsType.getRawType();
+ // 父类中的泛型类型变量集合
+ TypeVariable>[] parentTypeVars = parentAsClass.getTypeParameters();
+ if (srcType instanceof ParameterizedType) {
+ // 子类可能对父类泛型变量做过替换,使用替换后的类型
+ parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
+ }
+ if (declaringClass == parentAsClass) {
+ // 字段/方法在当前父类中声明
+ for (int i = 0; i < parentTypeVars.length; i++) {
+ if (typeVar == parentTypeVars[i]) {
+ // 使用变量对应位置的真正类型(可能已经被替换),如父类 A,子类 B extends A,则返回String
+ return parentAsType.getActualTypeArguments()[i];
+ }
+ }
+ }
+ // 字段/方法声明的类是当前父类的父类,继续递归
+ if (declaringClass.isAssignableFrom(parentAsClass)) {
+ return resolveTypeVar(typeVar, parentAsType, declaringClass);
+ }
+ } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class>) superclass)) {
+ // 父类是原始类型,继续递归父类
+ return resolveTypeVar(typeVar, superclass, declaringClass);
+ }
+ return null;
+}
+```
+
+解析方法返回值和方法参数的逻辑大致与解析字段类型相同,`MyBatis` 源码的`TypeParameterResolverTest` 类提供了相关的测试用例。
+
+## 元信息工厂
+
+`MyBatis` 还提供 `ReflectorFactory` 接口用于实现 `Reflector` 容器,其默认实现为 `DefaultReflectorFactory`,其中可以使用 `classCacheEnabled` 属性来配置是否使用缓存。
+
+```java
+public class DefaultReflectorFactory implements ReflectorFactory {
+
+ /**
+ * 是否缓存Reflector类信息
+ */
+ private boolean classCacheEnabled = true;
+
+ /**
+ * Reflector缓存容器
+ */
+ private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap<>();
+
+ public DefaultReflectorFactory() {
+ }
+
+ @Override
+ public boolean isClassCacheEnabled() {
+ return classCacheEnabled;
+ }
+
+ @Override
+ public void setClassCacheEnabled(boolean classCacheEnabled) {
+ this.classCacheEnabled = classCacheEnabled;
+ }
+
+ /**
+ * 获取类的Reflector信息
+ *
+ * @param type
+ * @return
+ */
+ @Override
+ public Reflector findForClass(Class> type) {
+ if (classCacheEnabled) {
+ // synchronized (type) removed see issue #461
+ // 如果缓存Reflector信息,放入缓存容器
+ return reflectorMap.computeIfAbsent(type, Reflector::new);
+ } else {
+ return new Reflector(type);
+ }
+ }
+
+}
+```
+
+## 对象创建工厂
+
+`ObjectFactory` 接口是 `MyBatis` 对象创建工厂,其默认实现 `DefaultObjectFactory` 通过构造器反射创建对象,支持使用无参构造器和有参构造器。
+
+## 属性工具集
+
+`MyBatis` 在映射文件定义 `resultMap` 支持如下形式:
+
+```xml
+
+
+
+ ...
+
+```
+
+`orders[0].items[0].name` 这样的表达式是由 `PropertyTokenizer` 解析的,其构造方法能够对表达式进行解析;同时还实现了 `Iterator` 接口,能够迭代解析表达式。
+
+```java
+public PropertyTokenizer(String fullname) {
+ // orders[0].items[0].name
+ int delim = fullname.indexOf('.');
+ if (delim > -1) {
+ // name = orders[0]
+ name = fullname.substring(0, delim);
+ // children = items[0].name
+ children = fullname.substring(delim + 1);
+ } else {
+ name = fullname;
+ children = null;
+ }
+ // orders[0]
+ indexedName = name;
+ delim = name.indexOf('[');
+ if (delim > -1) {
+ // 0
+ index = name.substring(delim + 1, name.length() - 1);
+ // order
+ name = name.substring(0, delim);
+ }
+}
+
+ /**
+ * 是否有children表达式继续迭代
+ *
+ * @return
+ */
+ @Override
+ public boolean hasNext() {
+ return children != null;
+ }
+
+ /**
+ * 分解出的 . 分隔符的 children 表达式可以继续迭代
+ * @return
+ */
+ @Override
+ public PropertyTokenizer next() {
+ return new PropertyTokenizer(children);
+ }
+```
+
+`PropertyNamer` 可以根据 `getter/setter` 规范解析字段名称;`PropertyCopier` 则支持对有相同父类的对象,通过反射拷贝字段值。
+
+## 封装类信息
+
+`MetaClass` 类依赖 `PropertyTokenizer` 和 `Reflector` 查找表达式是否可以匹配 `Java` 对象中的字段,以及对应字段是否有 `getter/setter` 方法。
+
+```java
+/**
+ * 验证传入的表达式,是否存在指定的字段
+ *
+ * @param name
+ * @param builder
+ * @return
+ */
+private StringBuilder buildProperty(String name, StringBuilder builder) {
+ // 映射文件表达式迭代器
+ PropertyTokenizer prop = new PropertyTokenizer(name);
+ if (prop.hasNext()) {
+ // 复杂表达式,如name = items[0].name,则prop.getName() = items
+ String propertyName = reflector.findPropertyName(prop.getName());
+ if (propertyName != null) {
+ builder.append(propertyName);
+ // items.
+ builder.append(".");
+ // 加载内嵌字段类型对应的MetaClass
+ MetaClass metaProp = metaClassForProperty(propertyName);
+ // 迭代子字段
+ metaProp.buildProperty(prop.getChildren(), builder);
+ }
+ } else {
+ // 非复杂表达式,获取字段名,如:userid->userId
+ String propertyName = reflector.findPropertyName(name);
+ if (propertyName != null) {
+ builder.append(propertyName);
+ }
+ }
+ return builder;
+}
+```
+
+## 包装字段对象
+
+相对于 `MetaClass` 关注类信息,`MetalObject` 关注的是对象的信息,除了保存传入的对象本身,还会为对象指定一个 `ObjectWrapper` 将对象包装起来。`ObejctWrapper` 体系如下:
+
+
+
+`ObjectWrapper` 的默认实现包括了对 `Map`、`Collection` 和普通 `JavaBean` 的包装。`MyBatis` 还支持通过 `ObjectWrapperFactory` 接口对 `ObejctWrapper` 进行扩展,生成自定义的包装类。`MetaObject` 对对象的具体操作,就委托给真正的 `ObjectWrapper` 处理。
+
+```java
+private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
+ this.originalObject = object;
+ this.objectFactory = objectFactory;
+ this.objectWrapperFactory = objectWrapperFactory;
+ this.reflectorFactory = reflectorFactory;
+
+ // 根据传入object类型不同,指定不同的wrapper
+ if (object instanceof ObjectWrapper) {
+ this.objectWrapper = (ObjectWrapper) object;
+ } else if (objectWrapperFactory.hasWrapperFor(object)) {
+ this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
+ } else if (object instanceof Map) {
+ this.objectWrapper = new MapWrapper(this, (Map) object);
+ } else if (object instanceof Collection) {
+ this.objectWrapper = new CollectionWrapper(this, (Collection) object);
+ } else {
+ this.objectWrapper = new BeanWrapper(this, object);
+ }
+}
+```
+
+例如赋值操作,`BeanWrapper` 的实现如下:
+
+```java
+ @Override
+ public void set(PropertyTokenizer prop, Object value) {
+ if (prop.getIndex() != null) {
+ // 当前表达式是集合,如:items[0],就需要获取items集合对象
+ Object collection = resolveCollection(prop, object);
+ // 在集合的指定索引上赋值
+ setCollectionValue(prop, collection, value);
+ } else {
+ // 解析完成,通过Invoker接口做赋值操作
+ setBeanProperty(prop, object, value);
+ }
+ }
+
+ protected Object resolveCollection(PropertyTokenizer prop, Object object) {
+ if ("".equals(prop.getName())) {
+ return object;
+ } else {
+ // 在对象信息中查到此字段对应的集合对象
+ return metaObject.getValue(prop.getName());
+ }
+ }
+```
+
+根据 `PropertyTokenizer` 对象解析出的当前字段是否存在 `index` 索引来判断字段是否为集合。如果当前字段对应集合,则需要在对象信息中查到此字段对应的集合对象:
+
+```javascript
+public Object getValue(String name) {
+ PropertyTokenizer prop = new PropertyTokenizer(name);
+ if (prop.hasNext()) {
+ // 如果表达式仍可迭代,递归寻找字段对应的对象
+ MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
+ if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
+ return null;
+ } else {
+ return metaValue.getValue(prop.getChildren());
+ }
+ } else {
+ // 字段解析完成
+ return objectWrapper.get(prop);
+ }
+}
+```
+
+如果字段是简单类型,`BeanWrapper` 获取字段对应的对象逻辑如下:
+
+```java
+@Override
+public Object get(PropertyTokenizer prop) {
+ if (prop.getIndex() != null) {
+ // 集合类型,递归获取
+ Object collection = resolveCollection(prop, object);
+ return getCollectionValue(prop, collection);
+ } else {
+ // 解析完成,反射读取
+ return getBeanProperty(prop, object);
+ }
+}
+```
+
+可以看到,仍然是会判断表达式是否迭代完成,如果未解析完字段会不断递归,直至找到对应的类型。前面说到 `Reflector` 创建过程中将对字段的读取和赋值操作通过 `Invoke` 接口抽象出来,针对最终获取的字段,此时就会调用 `Invoke` 接口对字段反射读取对象值:
+
+```java
+/**
+ * 通过Invoker接口反射执行读取操作
+ *
+ * @param prop
+ * @param object
+ */
+private Object getBeanProperty(PropertyTokenizer prop, Object object) {
+ try {
+ Invoker method = metaClass.getGetInvoker(prop.getName());
+ try {
+ return method.invoke(object, NO_ARGUMENTS);
+ } catch (Throwable t) {
+ throw ExceptionUtil.unwrapThrowable(t);
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
+ }
+}
+```
+
+对象读取完毕再通过 `setCollectionValue` 方法对集合指定索引进行赋值或通过 `setBeanProperty` 方法对简单类型反射赋值。`MapWrapper` 的操作与 `BeanWrapper` 大致相同,`CollectionWrapper` 相对更会简单,只支持对原始集合对象进行添加操作。
+
+## 小结
+
+`MyBatis` 根据自身需求,对反射 `API` 做了近一步封装。其目的是简化反射操作,为对象字段的读取和赋值提供更好的性能。
+
+- `org.apache.ibatis.reflection.Reflector`:缓存类的字段名和 getter/setter 方法的元信息,使得反射时有更好的性能。
+- `org.apache.ibatis.reflection.invoker.Invoker:`:用于抽象设置和读取字段值的操作。
+- `org.apache.ibatis.reflection.TypeParameterResolver`:针对 Java-Type 体系的多种实现,解析指定类中的字段、方法返回值或方法参数的类型。
+- `org.apache.ibatis.reflection.ReflectorFactory`:反射信息创建工厂抽象接口。
+- `org.apache.ibatis.reflection.DefaultReflectorFactory`:默认的反射信息创建工厂。
+- `org.apache.ibatis.reflection.factory.ObjectFactory`:MyBatis 对象创建工厂,其默认实现 DefaultObjectFactory 通过构造器反射创建对象。
+- `org.apache.ibatis.reflection.property`:property 工具包,针对映射文件表达式进行解析和 Java 对象的反射赋值。
+- `org.apache.ibatis.reflection.MetaClass`:依赖 PropertyTokenizer 和 Reflector 查找表达式是否可以匹配 Java 对象中的字段,以及对应字段是否有 getter/setter 方法。
+- `org.apache.ibatis.reflection.MetaObject`:对原始对象进行封装,将对象操作委托给 ObjectWrapper 处理。
+- `org.apache.ibatis.reflection.wrapper.ObjectWrapper`:对象包装类,封装对象的读取和赋值等操作。
+
+
+
+
+
+最后给大家分享**200多本计算机经典书籍PDF电子书**,包括**C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生**等,感兴趣的小伙伴可以自取:
+
+
+
+
+
+**200多本计算机经典书籍PDF电子书**:https://pan.xunlei.com/s/VNlmlh9jBl42w0QH2l4AJaWGA1?pwd=j8eq#
+
+备用链接:https://pan.quark.cn/s/3f1321952a16
\ No newline at end of file
diff --git "a/docs/source/mybatis/MyBatis \346\272\220\347\240\201\345\210\206\346\236\2203--\345\237\272\347\241\200\346\224\257\346\214\201\346\250\241\345\235\227.md" "b/docs/source/mybatis/MyBatis \346\272\220\347\240\201\345\210\206\346\236\2203--\345\237\272\347\241\200\346\224\257\346\214\201\346\250\241\345\235\227.md"
new file mode 100644
index 0000000..234ffcf
--- /dev/null
+++ "b/docs/source/mybatis/MyBatis \346\272\220\347\240\201\345\210\206\346\236\2203--\345\237\272\347\241\200\346\224\257\346\214\201\346\250\241\345\235\227.md"
@@ -0,0 +1,1563 @@
+## 类型转换
+
+`JDBC` 规范定义的数据类型与 `Java` 数据类型并不是完全对应的,所以在 `PrepareStatement` 为 `SQL` 语句绑定参数时,需要从 `Java` 类型转为 `JDBC` 类型;而从结果集中获取数据时,则需要将 `JDBC` 类型转为 `Java` 类型。
+
+### 类型转换操作
+
+`MyBatis` 中的所有类型转换器都继承自 `BaseTypeHandler` 抽象类,此类实现了 `TypeHandler` 接口。接口中定义了 1 个向 `PreparedStatement` 对象中设置参数的方法和 3 个从结果集中取值的方法:
+
+```java
+ public interface TypeHandler {
+
+ /**
+ * 为PreparedStatement对象设置参数
+ *
+ * @param ps SQL 预编译对象
+ * @param i 参数索引
+ * @param parameter 参数值
+ * @param jdbcType 参数 JDBC类型
+ * @throws SQLException
+ */
+ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
+
+ /**
+ * 根据列名从结果集中取值
+ *
+ * @param rs 结果集
+ * @param columnName 列名
+ * @return
+ * @throws SQLException
+ */
+ T getResult(ResultSet rs, String columnName) throws SQLException;
+
+ /**
+ * 根据索引从结果集中取值
+ * @param rs 结果集
+ * @param columnIndex 索引
+ * @return
+ * @throws SQLException
+ */
+ T getResult(ResultSet rs, int columnIndex) throws SQLException;
+
+ /**
+ * 根据索引从存储过程函数中取值
+ *
+ * @param cs 存储过程对象
+ * @param columnIndex 索引
+ * @return
+ * @throws SQLException
+ */
+ T getResult(CallableStatement cs, int columnIndex) throws SQLException;
+
+ }
+```
+
+### BaseTypeHandler 及其实现
+
+`BaseTypeHandler` 实现了 `TypeHandler` 接口,针对 `null` 和异常处理做了封装,但是具体逻辑封装成 4 个抽象方法仍交由相应的类型转换器子类实现,以 `IntegerTypeHandler` 为例,其实现如下:
+
+```java
+ public class IntegerTypeHandler extends BaseTypeHandler {
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
+ throws SQLException {
+ ps.setInt(i, parameter);
+ }
+
+ @Override
+ public Integer getNullableResult(ResultSet rs, String columnName)
+ throws SQLException {
+ int result = rs.getInt(columnName);
+ // 如果列值为空值返回控制否则返回原值
+ return result == 0 && rs.wasNull() ? null : result;
+ }
+
+ @Override
+ public Integer getNullableResult(ResultSet rs, int columnIndex)
+ throws SQLException {
+ int result = rs.getInt(columnIndex);
+ return result == 0 && rs.wasNull() ? null : result;
+ }
+
+ @Override
+ public Integer getNullableResult(CallableStatement cs, int columnIndex)
+ throws SQLException {
+ int result = cs.getInt(columnIndex);
+ return result == 0 && cs.wasNull() ? null : result;
+ }
+ }
+```
+
+其实现主要是调用 `JDBC API` 设置查询参数或取值,并对 `null` 等特定情况做特殊处理。
+
+### 类型转换器注册
+
+`TypeHandlerRegistry` 是 `TypeHandler` 的注册类,在其无参构造方法中维护了 `JavaType`、`JdbcType` 和 `TypeHandler` 的关系。其主要使用的容器如下:
+
+```java
+ /**
+ * JdbcType - TypeHandler对象
+ * 用于将Jdbc类型转为Java类型
+ */
+ private final Map> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
+
+ /**
+ * JavaType - JdbcType - TypeHandler对象
+ * 用于将Java类型转为指定的Jdbc类型
+ */
+ private final Map>> typeHandlerMap = new ConcurrentHashMap<>();
+
+ /**
+ * TypeHandler类型 - TypeHandler对象
+ * 注册所有的TypeHandler类型
+ */
+ private final Map, TypeHandler>> allTypeHandlersMap = new HashMap<>();
+```
+
+## 别名注册
+
+### 别名转换器注册
+
+`TypeAliasRegistry` 提供了多种方式用于为 `Java` 类型注册别名。包括直接指定别名、注解指定别名、为指定包下类型注册别名:
+
+```java
+ /**
+ * 注册指定包下所有类型别名
+ *
+ * @param packageName
+ */
+ public void registerAliases(String packageName) {
+ registerAliases(packageName, Object.class);
+ }
+
+ /**
+ * 注册指定包下指定类型的别名
+ *
+ * @param packageName
+ * @param superType
+ */
+ public void registerAliases(String packageName, Class> superType) {
+ ResolverUtil> resolverUtil = new ResolverUtil<>();
+ // 找出该包下superType所有的子类
+ resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
+ Set>> typeSet = resolverUtil.getClasses();
+ for (Class> type : typeSet) {
+ // Ignore inner classes and interfaces (including package-info.java)
+ // Skip also inner classes. See issue #6
+ if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
+ registerAlias(type);
+ }
+ }
+ }
+
+ /**
+ * 注册类型别名,默认为简单类名,优先从Alias注解获取
+ *
+ * @param type
+ */
+ public void registerAlias(Class> type) {
+ String alias = type.getSimpleName();
+ // 从Alias注解读取别名
+ Alias aliasAnnotation = type.getAnnotation(Alias.class);
+ if (aliasAnnotation != null) {
+ alias = aliasAnnotation.value();
+ }
+ registerAlias(alias, type);
+ }
+
+ /**
+ * 注册类型别名
+ *
+ * @param alias 别名
+ * @param value 类型
+ */
+ public void registerAlias(String alias, Class> value) {
+ if (alias == null) {
+ throw new TypeException("The parameter alias cannot be null");
+ }
+ // issue #748
+ String key = alias.toLowerCase(Locale.ENGLISH);
+ if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
+ throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
+ }
+ typeAliases.put(key, value);
+ }
+
+ /**
+ * 注册类型别名
+ * @param alias 别名
+ * @param value 指定类型全名
+ */
+ public void registerAlias(String alias, String value) {
+ try {
+ registerAlias(alias, Resources.classForName(value));
+ } catch (ClassNotFoundException e) {
+ throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + e, e);
+ }
+ }
+```
+
+所有别名均注册到名为 `typeAliases` 的容器中。`TypeAliasRegistry` 的无参构造方法默认为一些常用类型注册了别名,如 `Integer`、`String`、`byte[]` 等。
+
+## 日志配置
+
+`MyBatis` 支持与多种日志工具集成,包括 `Slf4j`、`log4j`、`log4j2`、`commons-logging` 等。这些第三方工具类对应日志的实现各有不同,`MyBatis` 通过适配器模式抽象了这些第三方工具的集成过程,按照一定的优先级选择具体的日志工具,并将真正的日志实现委托给选择的日志工具。
+
+### 日志适配接口
+
+`Log` 接口是 `MyBatis` 的日志适配接口,支持 `trace`、`debug`、`warn`、`error` 四种级别。
+
+### 日志工厂
+
+`LogFactory` 负责对第三方日志工具进行适配,在类加载时会通过静态代码块按顺序选择合适的日志实现。
+
+```java
+ static {
+ // 按顺序加载日志实现,如果有某个第三方日志实现可以成功加载,则不继续加载其它实现
+ tryImplementation(LogFactory::useSlf4jLogging);
+ tryImplementation(LogFactory::useCommonsLogging);
+ tryImplementation(LogFactory::useLog4J2Logging);
+ tryImplementation(LogFactory::useLog4JLogging);
+ tryImplementation(LogFactory::useJdkLogging);
+ tryImplementation(LogFactory::useNoLogging);
+ }
+
+ /**
+ * 初始化 logConstructor
+ *
+ * @param runnable
+ */
+ private static void tryImplementation(Runnable runnable) {
+ if (logConstructor == null) {
+ try {
+ // 同步执行
+ runnable.run();
+ } catch (Throwable t) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * 配置第三方日志实现适配器
+ *
+ * @param implClass
+ */
+ private static void setImplementation(Class extends Log> implClass) {
+ try {
+ Constructor extends Log> candidate = implClass.getConstructor(String.class);
+ Log log = candidate.newInstance(LogFactory.class.getName());
+ if (log.isDebugEnabled()) {
+ log.debug("Logging initialized using '" + implClass + "' adapter.");
+ }
+ logConstructor = candidate;
+ } catch (Throwable t) {
+ throw new LogException("Error setting Log implementation. Cause: " + t, t);
+ }
+ }
+```
+
+`tryImplementation` 按顺序加载第三方日志工具的适配实现,如 `Slf4j` 的适配器 `Slf4jImpl`:
+
+```java
+public Slf4jImpl(String clazz) {
+ Logger logger = LoggerFactory.getLogger(clazz);
+
+ if (logger instanceof LocationAwareLogger) {
+ try {
+ // check for slf4j >= 1.6 method signature
+ logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
+ log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
+ return;
+ } catch (SecurityException | NoSuchMethodException e) {
+ // fail-back to Slf4jLoggerImpl
+ }
+ }
+
+ // Logger is not LocationAwareLogger or slf4j version < 1.6
+ log = new Slf4jLoggerImpl(logger);
+}
+```
+
+如果 `Slf4jImpl` 能成功执行构造方法,则 `LogFactory` 的 `logConstructor` 被成功赋值,`MyBatis` 就找到了合适的日志实现,可以通过 `getLog` 方法获取 `Log` 对象。
+
+### JDBC 日志代理
+
+`org.apache.ibatis.logging.jdbc` 包提供了 `Connection`、`PrepareStatement`、`Statement`、`ResultSet` 类中的相关方法执行的日志记录代理。`BaseJdbcLogger` 在创建时整理了 `PreparedStatement` 执行的相关方法名,并提供容器保存列值:
+
+```java
+ /**
+ * PreparedStatement 接口中的 set* 方法名称集合
+ */
+ protected static final Set SET_METHODS;
+
+ /**
+ * PreparedStatement 接口中的 部分执行方法
+ */
+ protected static final Set EXECUTE_METHODS = new HashSet<>();
+
+ /**
+ * 列名-列值
+ */
+ private final Map