├── CNAME ├── docs ├── resource │ ├── tools.md │ ├── books.md │ ├── tutorial.md │ └── README.md ├── guide │ ├── java │ │ ├── README.md │ │ ├── java8 │ │ │ ├── 7-map.md │ │ │ ├── 2-lambda.md │ │ │ ├── 5-stream.md │ │ │ ├── README.md │ │ │ ├── 1-functional-program.md │ │ │ ├── 6-parallel-stream.md │ │ │ ├── 3-functional-interface.md │ │ │ └── 4-inner-functional-interface.md │ │ ├── distributed │ │ │ ├── README.md │ │ │ ├── 3-rpc.md │ │ │ ├── 4-micro-service.md │ │ │ ├── 6-distributed-transaction.md │ │ │ └── 2-distributed-lock.md │ │ ├── jvm │ │ │ └── java-jvm.md │ │ ├── database │ │ │ └── redis │ │ │ │ └── java-redis.md │ │ └── collection │ │ │ └── java-collection-code.md │ ├── system-design │ │ ├── 3-short-url.md │ │ ├── 9-coupon-design.md │ │ ├── 11-realtime-subscribe-push.md │ │ ├── 10-pdd-visit-statistics.md │ │ ├── README.md │ │ └── 1-scan-code-login.md │ ├── advance │ │ └── excellent-article │ │ │ ├── 11-8-architect-pattern.md │ │ │ ├── README.md │ │ │ ├── 14-architect-forward.md │ │ │ ├── 19-java19.md │ │ │ ├── 18-db-connect-resource.md │ │ │ ├── 28-springboot-forbid-tomcat.md │ │ │ ├── 8-interface-idempotent.md │ │ │ ├── 27-mq-usage.md │ │ │ ├── 5-jvm-optimize.md │ │ │ ├── 2-spring-transaction.md │ │ │ ├── 16-what-is-jwt.md │ │ │ ├── 22-distributed-scheduled-task.md │ │ │ ├── 26-java-stream.md │ │ │ ├── 6-spring-three-cache.md │ │ │ ├── 15-http-vs-rpc.md │ │ │ ├── 25-select-count-slow-query.md │ │ │ ├── 9-jvm-optimize-param.md │ │ │ ├── 24-generic.md │ │ │ └── 17-limit-scheme.md │ ├── mass-data │ │ ├── README.md │ │ ├── 7-query-frequency-sort.md │ │ ├── 3-find-same-url.md │ │ ├── 4-find-mid-num.md │ │ ├── 5-find-hot-string.md │ │ ├── 1-count-phone-num.md │ │ ├── 2-find-hign-frequency-word.md │ │ ├── 6-top-500-num.md │ │ ├── 0-topk-template.md │ │ └── 9-sort-500-million-large-files.md │ ├── memoir │ │ ├── readme.md │ │ ├── 2.md │ │ ├── 1.md │ │ ├── 3.md │ │ └── 4.md │ ├── interview │ │ ├── six-method.md │ │ ├── recruit.md │ │ └── project.md │ └── README.md ├── column │ ├── mysql │ │ ├── lock │ │ │ └── lock-01.md │ │ └── README.md │ ├── redis │ │ └── README.md │ ├── concurrent │ │ └── README.md │ └── jvm │ │ └── README.md ├── learn │ ├── ghelper.md │ └── leetcode.md ├── cc.png ├── logo.png ├── other │ ├── leave-a-message.md │ └── site-diary.md ├── notes │ ├── README.md │ └── BAT.md ├── navigation │ ├── experience.md │ ├── imgs │ │ ├── 书籍-JVM.png │ │ ├── 书籍-计网02.png │ │ ├── 书籍-计网图解.png │ │ ├── 书籍-计网经典.png │ │ ├── 书籍-Git秘籍1.png │ │ ├── 书籍-Java基础.png │ │ ├── 书籍-Java并发.png │ │ ├── 书籍-MySQL实战.png │ │ ├── 书籍-git秘籍2.png │ │ ├── 书籍-jvm视频.png │ │ ├── 书籍-mysql.png │ │ ├── 书籍-nginx.png │ │ ├── 书籍-redis.png │ │ ├── 书籍-图解设计模式.png │ │ ├── 书籍-图解设计模式2.png │ │ ├── 书籍-操作系统01.png │ │ ├── 书籍-操作系统国外.png │ │ ├── 书籍-操作系统李治军.png │ │ ├── 书籍-计网cs144.png │ │ ├── 书籍-计网半小时看懂.png │ │ ├── 书籍-鸟哥的私房菜.png │ │ ├── 小龙coding.jpg │ │ ├── 书籍-猴子都能懂的git.png │ │ ├── project │ │ │ ├── 简易Doker.png │ │ │ └── database.png │ │ ├── 书籍-Java并发网站01.png │ │ ├── 书籍-MySQL实战45讲.png │ │ ├── 书籍-猴子都能懂的git2.png │ │ └── 书籍-计网网络是怎样连接的.png │ ├── README.md │ └── projects │ │ ├── 1-work-projects.md │ │ └── 0-work-projects.md ├── java2top │ ├── todo.md │ ├── intro.md │ └── contribution.md ├── hot │ └── query.md ├── projects │ └── README.md └── README.md ├── .gitignore └── README.md /CNAME: -------------------------------------------------------------------------------- 1 | java2top.cn 2 | -------------------------------------------------------------------------------- /docs/resource/tools.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/guide/java/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/column/mysql/lock/lock-01.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/learn/ghelper.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/learn/leetcode.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/7-map.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/column/redis/README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 3 | -------------------------------------------------------------------------------- /docs/guide/java/java8/2-lambda.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/5-stream.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/README.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/1-functional-program.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/6-parallel-stream.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/3-functional-interface.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/java8/4-inner-functional-interface.md: -------------------------------------------------------------------------------- 1 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/cc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/cc.png -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/logo.png -------------------------------------------------------------------------------- /docs/other/leave-a-message.md: -------------------------------------------------------------------------------- 1 | # 留言区 2 | 3 | 大家有什么建议,或者觉得某个知识点理解有误的,欢迎到评论区留言~ 4 | 5 | -------------------------------------------------------------------------------- /docs/notes/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: Java2Top(Java学习&&面试指南) 4 | --- 5 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/system-design/3-short-url.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3.如何设计一个短链系统? 3 | --- 4 | 5 | # 短链系统 6 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/navigation/experience.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 二本上岸大厂,我的经验分享 3 | category: 开源项目 4 | icon: project 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-JVM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-JVM.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网02.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网图解.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网图解.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网经典.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网经典.png -------------------------------------------------------------------------------- /docs/guide/system-design/9-coupon-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 9.如何设计一个优惠券系统? 3 | --- 4 | 5 | # 如何设计一个优惠券系统? 6 | 7 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-Git秘籍1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-Git秘籍1.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-Java基础.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-Java基础.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-Java并发.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-Java并发.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-MySQL实战.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-MySQL实战.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-git秘籍2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-git秘籍2.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-jvm视频.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-jvm视频.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-mysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-mysql.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-nginx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-nginx.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-redis.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-图解设计模式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-图解设计模式.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-图解设计模式2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-图解设计模式2.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-操作系统01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-操作系统01.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-操作系统国外.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-操作系统国外.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-操作系统李治军.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-操作系统李治军.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网cs144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网cs144.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网半小时看懂.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网半小时看懂.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-鸟哥的私房菜.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-鸟哥的私房菜.png -------------------------------------------------------------------------------- /docs/navigation/imgs/小龙coding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/小龙coding.jpg -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-猴子都能懂的git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-猴子都能懂的git.png -------------------------------------------------------------------------------- /docs/guide/system-design/11-realtime-subscribe-push.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 11.实时订阅推送设计与实现? 3 | --- 4 | 5 | # 实时订阅推送设计与实现 6 | 7 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/navigation/imgs/project/简易Doker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/project/简易Doker.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-Java并发网站01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-Java并发网站01.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-MySQL实战45讲.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-MySQL实战45讲.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-猴子都能懂的git2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-猴子都能懂的git2.png -------------------------------------------------------------------------------- /docs/navigation/imgs/书籍-计网网络是怎样连接的.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/书籍-计网网络是怎样连接的.png -------------------------------------------------------------------------------- /docs/navigation/imgs/project/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xlcoding/Java2Top/HEAD/docs/navigation/imgs/project/database.png -------------------------------------------------------------------------------- /docs/notes/BAT.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 基于人工智能的智慧校园助手 4 | --- 5 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304182303253.jpg) -------------------------------------------------------------------------------- /docs/other/site-diary.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: heading 3 | --- 4 | 5 | 小龙的小破站是23年4月份开始搭建的,收录了我平时梳理的一些文章,网站内容还在不断更新中~ 6 | 7 | 在此记录下小破站的迭代更新记录。 8 | 9 | ## 更新记录 10 | 11 | - 2023.03.20-2023.03.10,网站搭建,规划网站发展 12 | -------------------------------------------------------------------------------- /docs/guide/system-design/10-pdd-visit-statistics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 10.如何用 Redis 统计用户访问量? 3 | --- 4 | 5 | 如何用 Redis 统计用户访问量? 6 | -------------------------------------------------- 7 | 拼多多有数亿的用户,那么对于某个网页,怎么使用Redis来统计一个网站的用户访问数呢? 8 | 9 | 更新完善中。。。 -------------------------------------------------------------------------------- /docs/guide/java/distributed/README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 3 | - [全局唯一ID生成方案](./1-global-unique-id.md) 4 | - [分布式锁](./2-distributed-lock.md) 5 | - [RPC](./3-rpc.md) 6 | - [微服务](./4-micro-service.md) 7 | - [分布式架构,微服务、限流、熔断....](./6-distibuted-arch.md) 8 | - [分布式事务](./7-distributed-transaction.md) 9 | -------------------------------------------------------------------------------- /docs/java2top/todo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 网站规划(ing🔥) 3 | category: 走近项目 4 | --- 5 | 6 | - [x] 面试指南完成 7 | - [x] 网站框架初构 8 | - [x] 《面试实录》合集 9 | - [ ] 编程资源(ing) 10 | - [ ] 实时面经上传,面体频次排行 11 | - [ ] 全名实战项目(待开始) 12 | 13 | 欢迎参与 Java2Top 的维护工作,这是一件非常有意义的事情。详细信息请看:[Java2Top 贡献指南](./contribution.md) 。 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/11-8-architect-pattern.md: -------------------------------------------------------------------------------- 1 | # 8种架构模式 2 | 3 | [8种架构模式](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490779&idx=2&sn=eff9e8cf9b15c29630514a137f102701&chksm=ce98e19df9ef688bd9c7b775658c704a51b7961347a7aabf70e6c555cb57560aa5e8b1e497a1&token=1170645384&lang=zh_CN#rd) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules/ 4 | *.bat 5 | web/ 6 | .idea/ 7 | *.sh 8 | *.py 9 | package.json 10 | package-lock.json 11 | bash 12 | docs/.vuepress/.temp/ 13 | docs/.vuepress/.cache/ 14 | docs/.vuepress/components/ 15 | docs/.vuepress/* 16 | ByteDanceVerify.html 17 | google053c90e5a7354c40.html 18 | sogousiteverification.txt -------------------------------------------------------------------------------- /docs/guide/java/jvm/java-jvm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《BAT高频面点》JVM高频面试题八股文(推荐⭐) 3 | category: 4 | - Java 5 | - JVM 6 | tag: 7 | - heap 8 | - stack 9 | - namespace 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: JVM,堆,栈,程序计数器,元空间,对象,内存溢出 14 | - name: description 15 | content: 图解《JVM-BAT高频面题》全网质量最高计算机网络常见知识点和面试题总结,希望对你有帮助 16 | --- 17 | -------------------------------------------------------------------------------- /docs/guide/system-design/README.md: -------------------------------------------------------------------------------- 1 | 0.开篇-如何设计大型系统? 2 | 3 | 1.扫码登录的原理? 4 | 5 | 2.订单30分钟未支付自动取消怎么实现? 6 | 7 | 3.如何把——个文件较快的发送到100服务器? 8 | 9 | 4.如何设计一个短链系统? 10 | 11 | 5.如何解决超卖问题? 12 | 13 | 6.如何设计一个秒杀系统? 14 | 15 | 7.微信红包后台系统设计详解? 16 | 17 | 8.单点登录(SSO)的设计与实现? 18 | 19 | 9.如何设计一个优惠券系统? 20 | 21 | 10.如何用Redis 统计用户访问量? 22 | 23 | 11.实时订阅推送设计与实现 24 | 25 | 12.如何设计一个抢红包系统如何设计一个消息队列?购物车系统怎么设计? -------------------------------------------------------------------------------- /docs/resource/books.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 书籍 4 | --- 5 | 6 | ## awesome-fenix(凤凰架构) 7 | 8 | 这是一遍讨论“如何构建大型且可靠的分布式系统”的开源书籍,推荐给想成为架构师的你。 9 | 10 | > https://github.com/fenixsoft/awesome-fenix 11 | 12 | ## JavaScript-Algorithms(教你从零构建前端算法体系) 13 | 14 | 学习算法不仅是为了面试,也是每个前端进阶必备的技能之一。该项目包含了前端的进阶算法、常见面试题、手写源码等,帮你构建完整的数据结构和算法的知识体系。 15 | 16 | > https://github.com/sisterAn/JavaScript-Algorithms -------------------------------------------------------------------------------- /docs/resource/tutorial.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 程序员必备开源教程 4 | --- 5 | ## cdn-up-and-running(从零开始构建 CDN 的教程) 6 | 7 | ![image-20230419233647618](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304192336786.png) 8 | 9 | 为了让你在实战中学习 CDN 的工作原理,这里会从创建一个单一的后端服务开始,逐渐扩展到多个节点、模拟延迟、可视化、可测试的 CDN 服务。因为设计 CDN 会涉及 Nginx、Lua、Docker、Grafana 等知识点,所以学习该教程需要有一定的编程基础。 10 | 11 | > https://github.com/leandromoreira/cdn-up-and-running -------------------------------------------------------------------------------- /docs/guide/mass-data/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 海量数据面试题(更新中 ing 🔥) 3 | 4 | - [如何查询最热门的查询串?](./5-find-hot-string.md) 5 | - [统计不同号码的个数](./1-count-phone-num.md) 6 | - [出现频率最高的100个词](./2-find-hign-frequency-word.md) 7 | - [查找两个大文件共同的URL](./3-find-same-url.md) 8 | - [如何在100亿数据中找到中位数?](./4-find-mid-num) 9 | - [如何查询最热门的查询串?](./5-find-hot-string) 10 | - [如何找出排名前 500 的数?](./6-top-500-num) 11 | - [如何按照 query 的频度排序?](./7-query-frequency-sort) 12 | - [大数据中 TopK 问题的常用套路](./8-topk-template) 13 | -------------------------------------------------------------------------------- /docs/guide/java/database/redis/java-redis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《BAT高频面点》Redis高频面试题八股文(hot🔥) 3 | category: 4 | - Java 5 | - Redis 6 | tag: 7 | - 事务 8 | - 锁机制 9 | - 索引 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: Redis,事务,缓冲,索引原理,日志,内存,架构 14 | - name: description 15 | content: 图解《Redis-BAT高频面题》全网质量最高Redis常见知识点和面试题总结,希望对你有ji 16 | --- 17 | 18 | ## 架构 19 | 20 | ## 索引 21 | 22 | ## 事务 23 | 24 | ## 锁 25 | 26 | ## 日志 27 | 28 | ## 高可用 29 | 30 | ## MYSQL调优 31 | -------------------------------------------------------------------------------- /docs/resource/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 编程资源 4 | --- 5 | 6 | 网站初建,内容完善中,本模块会在后面更新,疯狂 coding 中 7 | 8 | **移步其他精彩:** 9 | 10 | 「Java 面试指南 (强推)🔥」 11 | 12 | - [Java 面试指南](./guide/README.md) :一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 Java2Top! 13 | 14 | 「大厂学习导航」 15 | 16 | - [路线规划](./navigation/3-job.md):带你从进入大学方向选择到逐步学习至校招面试最后作为coder融入职场的整个路线规划~(搬运更新中 🔥) 17 | - [求职项目精选](././navigation/open-source-projects.md):根据当下**面试热点,技术潮流**持续收集适合用于《`校招面试求职`》的精品**项目**,和人手一个的秒杀,商城,管理系统说拜拜吧,您~。 18 | - [Java 书单精选](./navigation/books.md) : 本站会根据当下**面试热点**,技术潮流**持续收集**适合用于《`校招面试求职`》的精品**书籍**,带你 畅游计算机~ 19 | - [经验分享](./navigation/experience.md):分享我的个人二本上岸大厂经历。校招斩获腾讯、美团、网易SP、ViVo提前批、讯飞多个大厂offer。深感一路的不易。**希望我的分享可以帮助更多的小伙伴,我踩过的坑你们不要再踩**! -------------------------------------------------------------------------------- /docs/column/concurrent/README.md: -------------------------------------------------------------------------------- 1 | ## JUC 并发编程与源码解析 2 | 3 | ### 一、前言 4 | 5 | ### 二、计算机底层原理篇 6 | 7 | - 指令级并行 8 | - CPU 缓存结构原理 9 | - JMM 模型深度剖析:JSR-133: Java Memory Model and Thread Specification 10 | 11 | ### 三、并发原理篇 12 | 13 | - [1.线程底层如何开启? start0 底层源码分析](./juc-principle/0-start0.md) 14 | - **2、Java对象内存布局和对象头** 15 | - [3.Synchronized 原理分析](./juc-principle/1-synchronized.md) 16 | - [4.锁优化 & 升级](./juc-principle/2-lock.md) 17 | - [5.AQS 底层原理分析](./juc-principle/3-AQS.md) 18 | - [6.ReentrantLock 重入锁源码分析](./juc-principle/4-reentrantLock.md) 19 | - [7.CAS 算法和 ABA 问题](./juc-principle/1-synchronized.md) 20 | - [8.Volatile 原理与源码分析](./juc-principle/6-volatile.md) 21 | - 9.并发编程工具 22 | - 10.ThreadLocal底层原理 23 | - 11.线程池底层原理 24 | 25 | ### 四、并发实战案例篇 26 | 27 | -------------------------------------------------------------------------------- /docs/hot/query.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 热题统计 4 | --- 5 | 6 | ::: info 7 | 8 | Java系列「`校招面试求职,大厂学习导航`」认准「**Java2Top**」认准「**小龙coding**」 9 | 10 | 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差~ 11 | 12 | ::: 13 | 14 | 网站初建,内容完善中,本模块会在后面更新,疯狂 coding 中 15 | 16 | **移步其他精彩:** 17 | 18 | 「Java 面试指南 (强推)🔥」 19 | 20 | - [Java 面试指南](./guide/README.md) :一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 Java2Top! 21 | 22 | 「大厂学习导航」 23 | 24 | - [路线规划](./navigation/3-job.md):带你从进入大学方向选择到逐步学习至校招面试最后作为coder融入职场的整个路线规划~(搬运更新中 🔥) 25 | - [求职项目精选](././navigation/open-source-projects.md):根据当下**面试热点,技术潮流**持续收集适合用于《`校招面试求职`》的精品**项目**,和人手一个的秒杀,商城,管理系统说拜拜吧,您~。 26 | - [Java 书单精选](./navigation/books.md) : 本站会根据当下**面试热点**,技术潮流**持续收集**适合用于《`校招面试求职`》的精品**书籍**,带你 畅游计算机~ 27 | - [经验分享](./navigation/experience.md):分享我的个人二本上岸大厂经历。校招斩获腾讯、美团、网易SP、ViVo提前批、讯飞多个大厂offer。深感一路的不易。**希望我的分享可以帮助更多的小伙伴,我踩过的坑你们不要再踩**! -------------------------------------------------------------------------------- /docs/guide/mass-data/7-query-frequency-sort.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 如何按照 query 的频度排序? 4 | --- 5 | 6 | ### 题目描述 7 | 8 | 有 10 个文件,每个文件大小为 1G,每个文件的每一行存放的都是用户的 query,每个文件的 query 都可能重复。要求按照 query 的频度排序。 9 | 10 | ### 解答思路 11 | 12 | 如果 query 的重复度比较大,可以考虑一次性把所有 query 读入内存中处理;如果 query 的重复率不高,那么可用内存不足以容纳所有的 query,这时候就需要采用分治法或其他的方法来解决。 13 | 14 | #### 方法一:HashMap 法 15 | 16 | 如果 query 重复率高,说明不同 query 总数比较小,可以考虑把所有的 query 都加载到内存中的 HashMap 中。接着就可以按照 query 出现的次数进行排序。 17 | 18 | #### 方法二:分治法 19 | 20 | 分治法需要根据数据量大小以及可用内存的大小来确定问题划分的规模。对于这道题,可以顺序遍历 10 个文件中的 query,通过 Hash 函数 `hash(query) % 10` 把这些 query 划分到 10 个小文件中。之后对每个小文件使用 HashMap 统计 query 出现次数,根据次数排序并写入到零外一个单独文件中。 21 | 22 | 接着对所有文件按照 query 的次数进行排序,这里可以使用归并排序(由于无法把所有 query 都读入内存,因此需要使用外排序)。 23 | 24 | ### 方法总结 25 | 26 | - 内存若够,直接读入进行排序; 27 | - 内存不够,先划分为小文件,小文件排好序后,整理使用外排序进行归并。 28 | 29 | 30 | 31 | > 作者:yanglbme 32 | > 链接:https://juejin.cn/post/6844904003998842887 -------------------------------------------------------------------------------- /docs/guide/mass-data/3-find-same-url.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 查找两个大文件共同的URL 4 | --- 5 | 6 | ## 题目 7 | 8 | 给定 a、b 两个文件,各存放 50 亿个 URL,每个 URL 各占 64B,找出 a、b 两个文件共同的 URL。内存限制是 4G。 9 | 10 | ## 分析 11 | 12 | 每个 URL 占 64B,那么 50 亿个 URL占用的空间大小约为 320GB。 13 | 14 | 5,000,000,000 * 64B ≈ 320GB 15 | 16 | 由于内存大小只有 4G,因此,不可能一次性把所有 URL 加载到内存中处理。 17 | 18 | 可以采用分治策略,也就是把一个文件中的 URL 按照某个特征划分为多个小文件,使得每个小文件大小不超过 4G,这样就可以把这个小文件读到内存中进行处理了。 19 | 20 | 首先遍历文件a,对遍历到的 URL 进行哈希取余 `hash(URL) % 1000`,根据计算结果把遍历到的 URL 存储到 a0, a1,a2, ..., a999,这样每个大小约为 300MB。使用同样的方法遍历文件 b,把文件 b 中的 URL 分别存储到文件 b0, b1, b2, ..., b999 中。这样处理过后,所有可能相同的 URL 都在对应的小文件中,即 a0 对应 b0, ..., a999 对应 b999,不对应的小文件不可能有相同的 URL。那么接下来,我们只需要求出这 1000 对小文件中相同的 URL 就好了。 21 | 22 | 接着遍历 ai( `i∈[0,999]`),把 URL 存储到一个 HashSet 集合中。然后遍历 bi 中每个 URL,看在 HashSet 集合中是否存在,若存在,说明这就是共同的 URL,可以把这个 URL 保存到一个单独的文件中。 23 | 24 | ## 总结 25 | 26 | 最后总结一下: 27 | 28 | 1. 分而治之,进行哈希取余; 29 | 2. 对每个子文件进行 HashSet 统计。 30 | -------------------------------------------------------------------------------- /docs/navigation/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: Java2Top(大厂学习导航) 4 | --- 5 | 6 | 「`Java学习+校招面试求职+大厂学习导航`」一份涵盖 Javacoder 从零基础到进阶大厂的**全面学习**与面试指南~。 7 | 8 | **本指南名曰**:[《Java2Top》](www.java2top.cn),意为 Java To Top 希望大家都能成为 Java 界的佼佼者,毕业能进行业的 Top 企业公司。 9 | 10 | > 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差。 11 | > 12 | 13 | 你可以把这份指南整体架构想象成一本功法,不同模块根据学习者层次而设定。 14 | 15 | - 「`编程资源`」属于**功法库**—— 收录涵盖各个经典 开源项目,编程资源,编程工具;`加餐补给以国外优质网站资源为主`。 16 | - 「`大厂导航`」旨在**基础招式**——会带着你`从懵懂无知畅游Java之海而到初有感悟`,有对应书籍、视频、专栏学习指引; 17 | 18 | - 「专题进阶」旨在**内功夯实**——会带着你 进一步夯实基础,根据大厂面试常考重点难点设置对应专题,大部分对应着大厂面试题; 19 | 20 | - 「`面试指南`」属于**后期功法也可称秘法**——当你一步一步从头学到这,完毕必定大成,根基稳固;(切记)`若你着急找工作也可借此秘法快速达到大厂面试要求,但是可能实力虚浮`; 21 | 22 | - 「校招求职」属于**功法斗技**,建议面试求职前一定认真阅读,对面试求职必定事半功倍。 23 | - 「`aurora`」属于**万人练级**,后期我会准备一个 「**全民项目**」,从 `提需求->开发->提 PR ->merge` ,把大家召集起来一起开发,项目会`将当下大厂常问的各种热门技术点尽可能融入`,大家互相讨论进步。最后此项目可以 作为软件竞赛,校招求职项目。 24 | 25 | 其中不乏各种精品专栏,大厂高频面试题:**Java基础, 多线程, JVM, 虚拟机, 数据库, MySQL, Spring, Redis, MyBatis, 系统设计, 分布式, RPC, 高可用, 高并发 **等各块知识。 -------------------------------------------------------------------------------- /docs/guide/mass-data/4-find-mid-num.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 如何从大量的 URL 中找出相同的 URL? 4 | --- 5 | 6 | ### 题目描述 7 | 8 | 给定 a、b 两个文件,各存放 50 亿个 URL,每个 URL 各占 64B,内存限制是 4G。请找出 a、b 两个文件共同的 URL。 9 | 10 | ### 解答思路 11 | 12 | #### 1. 分治策略 13 | 14 | 每个 URL 占 64B,那么 50 亿个 URL 占用的空间大小约为 320GB。 15 | 16 | > 5, 000, 000, 000 _ 64B ≈ 5GB _ 64 = 320GB 17 | 18 | 由于内存大小只有 4G,因此,我们不可能一次性把所有 URL 加载到内存中处理。对于这种类型的题目,一般采用 **分治策略**,即:把一个文件中的 URL 按照某个特征划分为多个小文件,使得每个小文件大小不超过 4G,这样就可以把这个小文件读到内存中进行处理了。 19 | 20 | **思路如下**: 21 | 22 | 首先遍历文件 a,对遍历到的 URL 求 `hash(URL) % 1000` ,根据计算结果把遍历到的 URL 存储到 a0, a1, a2, ..., a999,这样每个大小约为 300MB。使用同样的方法遍历文件 b,把文件 b 中的 URL 分别存储到文件 b0, b1, b2, ..., b999 中。这样处理过后,所有可能相同的 URL 都在对应的小文件中,即 a0 对应 b0, ..., a999 对应 b999,不对应的小文件不可能有相同的 URL。那么接下来,我们只需要求出这 1000 对小文件中相同的 URL 就好了。 23 | 24 | 接着遍历 ai( `i∈[0,999]` ),把 URL 存储到一个 HashSet 集合中。然后遍历 bi 中每个 URL,看在 HashSet 集合中是否存在,若存在,说明这就是共同的 URL,可以把这个 URL 保存到一个单独的文件中。 25 | 26 | #### 2. 前缀树 27 | 28 | 一般而言,URL 的长度差距不会不大,而且前面几个字符,绝大部分相同。这种情况下,非常适合使用 **字典树**(trie tree) 这种数据结构来进行存储,降低存储成本的同时,提高查询效率。 -------------------------------------------------------------------------------- /docs/projects/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Java 优质开源项目精选 3 | --- 4 | 精选 GitHub 和 Gitee 上优质的 Java 开源项目,**可以写进简历的项目精选** 5 | 6 | ## 优质面试项目 7 | 8 | 1、[从零实现一个操作系统内核](./work-projects.html#_1、从零实现一个操作系统内核) 9 | 10 | 2、[proxyee-down (Http下载器)](./work-projects.html#_2、proxyee-down-http下载器) 11 | 12 | 3、[基于人工智能的智慧校园助手](./work-projects.html#_2、proxyee-down-http下载器) 13 | 14 | 4、[一个手把手教你造轮子的项目](./work-projects.html#_2、proxyee-down-http下载器) 15 | 16 | 5、[牛客论坛](./work-projects.html#_2、proxyee-down-http下载器) 17 | 18 | 6、[community(1.3k star)](./work-projects.html#_2、proxyee-down-http下载器) 19 | 20 | 7、[favorites-web (4.2k star)](./work-projects.html#_2、proxyee-down-http下载器) 21 | 22 | 8、 [SpringBoot-Shiro-Vue(4.2k)](./work-projects.html#_2、proxyee-down-http下载器) 23 | 24 | 9、[Java2Top(推荐)](./work-projects.html#_2、proxyee-down-http下载器) 25 | 26 | 10、[SnowJena](./work-projects.html#_2、proxyee-down-http下载器) 27 | 28 | ## 优质开源学习 29 | 30 | 1、电商类 31 | 32 | 2、管理系统类 33 | 34 | 3、博客类 35 | 36 | 4、其他 37 | 38 | ## Github 项目精选 39 | 40 | 综合 41 | 42 | 面试刷题 43 | 44 | 编程语言 45 | 46 | 学习提升 47 | 48 | 工具应用 -------------------------------------------------------------------------------- /docs/guide/memoir/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《面试实录》合集 3 | category: 4 | - 对线面试官 5 | - 面试实录 6 | - Java 7 | tag: 8 | - Java 9 | - Map 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: 面试实录,Sychronized,JAVA,大厂面试,锁 14 | - name: description 15 | content: BAT高频面点,面试实录,大厂面试合集专栏 16 | --- 17 | ## 序言 18 | 19 | 大家好,我是小龙。 20 | 21 | 曾几何时,大家是否有这样的困惑? 22 | 23 | 知道相关的知识点,但是面试时面试官**换一种问法**,你便不知道怎样回答,**给你一个场景**,你就联系不到相关的知识点上,或者不知道用对应知识去分析相关问题。 24 | 25 | 比如 "`你知道垂直定理,但是你遇到相关几何题,就是想不到用这个定理去解决问题"`。 26 | 27 | 新的篇章将开启,本系列会`结合小龙面试经历`以及`身边上岸大厂的朋友们的面试经历、面试录音、面试分享`,尽可能**还原面试现场**。 28 | 29 | 带大家**感受面试现场**,**结合情景学会灵活的应用知识**。看一看大佬们都是怎样面试的。 30 | 31 | 记得每期结束,问问自己,你真的对知识掌握了吗? 32 | 33 | ## 实录速达 34 | 35 | 大厂进阶【**面试实录**】系列文章,持续更新中.....。 36 | 37 | - 欢迎关注+订阅,持续更新中!!!致力打造校招核心面试攻略~ 38 | - 根据秋招春招上岸大厂面试经历以及身边朋友**上岸面试录音模拟面试现场**,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 39 | 40 | 【**面试实录**】专栏系列目前已经连载 **N** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 41 | 42 | - [《面试实录》百度二面,Sychronized原理详解](./1) 43 | - [《面试实录》字节二面,SQL执行慢的原因?如何优化?](./2) 44 | - [《面试实录》 字节二面, MySQL自增主键一定连续吗?](./3) 45 | - [《面试实录》 阿里一面,深剖Map底层结构与原理](./4) 46 | - [《面试实录》携程二面,JVM 底层剖析](./5) -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 优质文章汇总 3 | 4 | - [Redis如何实现库存扣减操作和防止被超卖?](./1-redis-stock-minus.md) 5 | - [@Transactional事务注解详解](./2-spring-transaction.md) 6 | - [SpringBoot自动装配原理](./3-springboot-auto-assembly.md) 7 | - [干掉“重复代码”的技巧有哪些](./4-remove-duplicate-code.md) 8 | - [一次简单的 *JVM* 调优,拿去写到简历里](./5-jvm-optimize.md) 9 | - [Spring为何需要三级缓存解决循环依赖,而不是二级缓存?](./6-spring-three-cache.md) 10 | - [8种最坑SQL语法](./7-sql-optimize.md) 11 | - [面试官:如何保证接口幂等性?一口气说了12种方法!](./8-interface-idempotent.md) 12 | - [美团面试:熟悉哪些JVM调优参数?](./9-jvm-optimize-param.md) 13 | - [大文件上传时如何做到秒传?](./10-file-upload.md) 14 | - [8种架构模式](./11-8-architect-pattern.md) 15 | - [MySQL最大建议行数 2000w,靠谱吗?](./12-mysql-table-max-rows.md) 16 | - [order by是怎么工作的?](./13-order-by-work.md) 17 | - [架构的演进](./14-architect-forward.md) 18 | - [有了HTTP,为啥还要用RPC](./15-http-vs-rpc.md) 19 | - [什么是JWT](./16-what-is-jwt.md) 20 | - [限流的几种方案](./17-limit-scheme.md) 21 | - [为什么说数据库连接很消耗资源](./18-db-connect-resource.md) 22 | - [Java19新特性](./19-java19.md) 23 | - [几种常见的架构模式](./20-architect-pattern.md) 24 | - [新一代分布式任务调度框架](./22-distributed-scheduled-task.md) 25 | - [Arthas 常用命令](./23-arthas-intro.md) -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | icon: home 4 | title: Java 面试指南 5 | heroImage: /cc.png 6 | heroText: Java2Top 7 | tagline: 「Java学习+面试指南」一份助你进阶大厂的学习与面试指南。准备 Java 面试,导航大厂学习,首选 Java2Top! 8 | actions: 9 | - text: 开始阅读📚 10 | link: /guide/README.md 11 | type: primary 12 | - text: 原创项目🔥 13 | link: /guide/README.md 14 | type: primary 15 | --- 16 | 17 | ### 📖 必看指南 18 | 19 | - [👉《Java 面试指南》](./guide/README.md) :一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 Java2Top! 20 | 21 | - [👉《面试实录》](./guide/memoir/):根据秋春招各个**上岸面试录音及真题面经 1vs1 模拟还原面试现场**~(火热更新中 🔥) 22 | - [🔥《JUC 并发编程与源码解析》]() : 从场景到原理,带你彻底理解 并发编程 23 | - [🔥《MySQL 核心面试系列》](./guide/java/database/mysql/) :带你从架构、内存、事务、锁、日志等方面吃透MySQL BAT面试高频问题。 24 | 25 | ## 📚求职项目 26 | 27 | - [👉《基于人工智能的智慧校园助手》](./notes/BAT.md) 28 | - [👉《项目精选》](./projects/readme.md):收录当下**面试热点**,**技术潮流**的精品项目 29 | 30 | ## 秋招提前批信息汇总 31 | 32 | [秋招提前批及正式批信息汇总(含内推)](https://flowus.cn/af5163d0-8820-4300-9f2d-7a70f415ee14) 33 | 34 | ### 💭 关于作者 35 | 36 | 最新更新会第一时间同步在公众号,强烈推荐大家关注一波!另外,公众号上有很多干货不会同步在线阅读网站。 37 | 38 | ![我的公众号](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303202215699.png) 39 | 40 | 1. 如果您对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。 41 | 2. 对于文中我没有涉及到知识点,欢迎提交 PR。 42 | 3. 如果您有文章推荐请以 markdown 格式到邮箱 `xlcoding@163.com`, 43 | [中文技术文档的写作规范指南](https://github.com/ruanyf/document-style-guide)。 44 | 45 | -------------------------------------------------------------------------------- /docs/guide/mass-data/5-find-hot-string.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 如何查询最热门的查询串? 4 | --- 5 | 6 | ## 题目描述 7 | 8 | 搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询床的长度不超过 255 字节。 9 | 10 | 假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w,但如果除去重复后,则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。(一个查询串的重复度越高,说明查询它的用户越多,也就越热门。) 11 | 12 | ## 解答思路 13 | 14 | 每个查询串最长为 255B,1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。 15 | 16 | ### 方法一:分治法 17 | 18 | 分治法依然是一个非常实用的方法。 19 | 20 | 划分为多个小文件,保证单个小文件中的字符串能被直接加载到内存中处理,然后求出每个文件中出现次数最多的 10 个字符串;最后通过一个小顶堆统计出所有文件中出现最多的 10 个字符串。 21 | 22 | 方法可行,但不是最好,下面介绍其他方法。 23 | 24 | ### 方法二:HashMap 法 25 | 26 | 虽然字符串总数比较多,但去重后不超过 300w,因此,可以考虑把所有字符串及出现次数保存在一个 HashMap 中,所占用的空间为 300w*(255+4)≈777M(其中,4表示整数占用的4个字节)。由此可见,1G 的内存空间完全够用。 27 | 28 | **思路如下**: 29 | 30 | 首先,遍历字符串,若不在 map 中,直接存入 map,value 记为 1;若在 map 中,则把对应的 value 加 1,这一步时间复杂度 `O(N)`。 31 | 32 | 接着遍历 map,构建一个 10 个元素的小顶堆,若遍历到的字符串的出现次数大于堆顶字符串的出现次数,则进行替换,并将堆调整为小顶堆。 33 | 34 | 遍历结束后,堆中 10 个字符串就是出现次数最多的字符串。这一步时间复杂度 `O(Nlog10)`。 35 | 36 | ### 方法三:前缀树法 37 | 38 | 方法二使用了 HashMap 来统计次数,当这些字符串有大量相同前缀时,可以考虑使用前缀树来统计字符串出现的次数,树的结点保存字符串出现次数,0 表示没有出现。 39 | 40 | **思路如下**: 41 | 42 | 在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数加 1,否则为这个字符串构建新结点,构建完成后把叶子结点中字符串的出现次数置为 1。 43 | 44 | 最后依然使用小顶堆来对字符串的出现次数进行排序。 45 | 46 | ## 方法总结 47 | 48 | 前缀树经常被用来统计字符串的出现次数。它的另外一个大的用途是字符串查找,判断是否有重复的字符串等。 49 | 50 | 51 | 52 | > 作者:yanglbme 53 | > 链接:https://juejin.cn/post/6844904003998842887 -------------------------------------------------------------------------------- /docs/guide/mass-data/1-count-phone-num.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 统计不同号码的个数 4 | --- 5 | 6 | ## 题目描述 7 | 8 | **已知某个文件内包含大量电话号码,每个号码为8位数字,如何统计不同号码的个数?** 9 | 10 | ## 思路分析 11 | 12 | 这类题目其实是求解数据重复的问题。对于这类问题,可以使用**位图法**处理 13 | 14 | 8位电话号码可以表示的范围为00000000~99999999。如果用 bit表示一个号码,那么总共需要1亿个bit,总共需要大约**10MB**的内存。 15 | 16 | 申请一个位图并初始化为0,然后遍历所有电话号码,**把遍历到的电话号码对应的位图中的bit设置为1**。当遍历完成后,如果bit值为1,则表示这个电话号码在文件中存在,否则这个bit对应的电话号码在文件中不存在。 17 | 18 | 最后这个**位图中bit值为1的数量**就是不同电话号码的个数了。 19 | 20 | 那么如何确定电话号码对应的是位图中的哪一位呢? 21 | 22 | 可以使用下面的方法来做**电话号码和位图的映射**。 23 | 24 | ```java 25 | 00000000 对应位图最后一位:0×0000…000001。 26 | 00000001 对应位图倒数第二位:0×0000…0000010(1 向左移 1 位)。 27 | 00000002 对应位图倒数第三位:0×0000…0000100(1 向左移 2 位)。 28 | …… 29 | 00000012 对应位图的倒数第十三位:0×0000…0001 0000 0000 0000(1 向左移 12 位)。 30 | ``` 31 | 32 | 也就是说,电话号码就是1这个数字左移的次数。 33 | 34 | ## 具体实现 35 | 36 | 首先位图可以使用一个**int数组**来实现(在Java中int占用**4byte**)。 37 | 38 | 假设电话号码为 P,而通过电话号码获取位图中对应位置的方法为: 39 | 40 | **第一步**,因为int整数占用4*8=32bit,通过 **P/32** 就可以计算出该电话号码在 bitmap 数组中的下标,从而可以确定它对应的 bit 在数组中的位置。 41 | 42 | **第二步**,通过 **P%32** 就可以计算出这个电话号码在这个int数字中具体的bit的位置。只要把1向左移 **P%32** 位,然后把得到的值与这个数组中的值做或运算,就可以把这个电话号码在位图中对应的位设置为1。 43 | 44 | 以00000100号码为例。 45 | 46 | 1. 首先计算数组下标,100 / 32 = 3,得到数组下标位3。 47 | 2. 然后计算电话号码在这个int数字中具体的bit的位置,100 % 32 = 4。取余为0左移1位,故取余为4左移5位,得到000...000010000 48 | 3. 将位图中对应的位设置为 1,即arr[2] = arr[2] **|** 000..00010000。 49 | 4. 这就将电话号码映射到了位图的某一位了。 50 | 51 | ![](http://img.topjavaer.cn/img/20220423094735.png) 52 | 53 | 最后,统计位图中bit值为1的数量,便能得到不同电话号码的个数了。 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/guide/system-design/1-scan-code-login.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 1.扫码登录原理? 4 | 5 | --- 6 | 7 | ## 扫码登录原理 8 | 9 | 扫码登录功能主要分为三个阶段:**待扫描、已扫描待确认、已确认**。 10 | 11 | 整体流程图如图。 12 | 13 | ![](http://img.topjavaer.cn/img/整个流程.png) 14 | 15 | 下面分阶段来看看设计原理。 16 | 17 | **1、待扫描阶段** 18 | 19 | 首先是待扫描阶段,这个阶段是 PC 端跟服务端的交互过程。 20 | 21 | 每次用户打开PC端登陆请求,系统返回一个**唯一的二维码ID**,并将二维码ID的信息绘制成二维码返回给用户。 22 | 23 | 这里的二维码ID一定是唯一的,后续流程会将二维码ID跟身份信息绑定,不唯一的话就会造成你登陆了其他用户的账号或者其他用户登陆你的账号。 24 | 25 | 此时在 PC 端会启动一个定时器,**轮询查询二维码是否被扫描**。 26 | 27 | 如果移动端未扫描的话,那么一段时间后二维码将会失效。 28 | 29 | 这个阶段的交互过程如下图所示。 30 | 31 | ![](http://img.topjavaer.cn/img/第一阶段.png) 32 | 33 | **2、已扫描待确认阶段** 34 | 35 | 第二个阶段是已扫描待确认阶段,主要是移动端跟服务端交互的过程。 36 | 37 | 首先移动端扫描二维码,获取二维码 ID,然后**将手机端登录的凭证(token)和 二维码 ID 作为参数发送给服务端** 38 | 39 | 此时的手机在之前已经是登录的,不存在没登录的情况。 40 | 41 | 服务端接受请求后,会将 token 与二维码 ID 关联,然后会生成一个临时token,这个 token 会返回给移动端,临时 token 用作确认登录的凭证。 42 | 43 | PC 端的定时器,会轮询到二维码的状态已经发生变化,会将 PC 端的二维码更新为已扫描,请在手机端确认。 44 | 45 | **这里为什么要有手机端确认的操作?** 46 | 47 | 假设没有确认这个环节,很容易就会被坏人拦截token去冒充登录。所以二维码扫描一定要有这个确认的页面,让用户去确认是否进行登录。 48 | 49 | 另外,二维码扫描确认之后,再往用户app或手机等发送登录提醒的通知,告知如果不是本人登录的,则建议用户立即修改密码。 50 | 51 | 这个阶段是交互过程如下图所示。 52 | 53 | ![](http://img.topjavaer.cn/img/20220411002823.png) 54 | 55 | **3、已确认** 56 | 57 | 扫码登录的最后阶段,用户点击确认登录,移动端携带上一步骤中获取的临时 token访问服务端。 58 | 59 | 服务端校对完成后,会更新二维码状态,并且给 PC 端生成一个正式的 token。 60 | 61 | 后续 PC 端就是持有这个 token 访问服务端。 62 | 63 | 这个阶段是交互过程如下图所示。 64 | 65 | ![](http://img.topjavaer.cn/img/20220411002832.png) 66 | 67 | 以上就是整个扫码登录功能的详细设计! 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /docs/guide/mass-data/2-find-hign-frequency-word.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 出现频率最高的100个词 4 | --- 5 | 6 | ## 题目描述 7 | 8 | 假如有一个**1G**大小的文件,文件里每一行是一个词,每个词的大小不超过**16byte**,要求返回出现频率最高的100个词。内存大小限制是**10M** 9 | 10 | ## 解法1 11 | 12 | 由于内存限制,我们无法直接将大文件的所有词一次性读到内存中。 13 | 14 | 可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于10M,进而直接将单个小文件读取到内存中进行处理。 15 | 16 | **第一步**,首先遍历大文件,对遍历到的每个词x,执行 `hash(x) % 500`,将结果为i的词存放到文件f(i)中,遍历结束后,可以得到500个小文件,每个小文件的大小为2M左右; 17 | 18 | **第二步**,接着统计每个小文件中出现频数最高的100个词。可以使用HashMap来实现,其中key为词,value为该词出现的频率。 19 | 20 | 对于遍历到的词x,如果在map中不存在,则执行 `map.put(x, 1)。` 21 | 22 | 若存在,则执行 `map.put(x, map.get(x)+1)`,将该词出现的次数加1。 23 | 24 | **第三步**,在第二步中找出了每个文件出现频率最高的100个词之后,通过维护一个**小顶堆**来找出所有小文件中出现频率最高的100个词。 25 | 26 | 具体方法是,遍历第一个文件,把第一个文件中出现频率最高的100个词构建成一个小顶堆。 27 | 28 | 如果第一个文件中词的个数小于100,可以继续遍历第二个文件,直到构建好有100个结点的小顶堆为止。 29 | 30 | 继续遍历其他小文件,如果遍历到的词的出现次数大于堆顶上词的出现次数,可以用新遍历到的词替换堆顶的词,然后重新调整这个堆为小顶堆。 31 | 32 | 当遍历完所有小文件后,这个小顶堆中的词就是出现频率最高的100个词。 33 | 34 | 总结一下,这种解法的主要思路如下: 35 | 36 | 1. 采用**分治**的思想,进行哈希取余 37 | 2. 使用**HashMap**统计每个小文件单词出现的次数 38 | 3. 使用**小顶堆**,遍历步骤2中的小文件,找出词频top100的单词 39 | 40 | 但是很容易可以发现问题,在第二步中,如果这个1G的大文件中有某个词词频过高,可能导致小文件大小超过10m。这种情况下该怎么处理呢? 41 | 42 | 接下来看另外一种解法。 43 | 44 | ## 解法2 45 | 46 | **第一步**:使用多路归并排序对大文件进行排序,这样相同的单词肯定是紧挨着的 47 | 48 | 多路归并排序对大文件进行排序的步骤如下: 49 | 50 | ① 将文件按照顺序切分成大小不超过2m的小文件,总共500个小文件 51 | 52 | ② 使用10MB内存**分别**对 500 个小文件中的单词进行**排序** 53 | 54 | ③ 使用一个大小为500大小的堆,对500个小文件进行**多路排序**,结果写到一个大文件中 55 | 56 | 其中第三步,对500个小文件进行多路排序的思路如下: 57 | 58 | - 初始化一个最小堆,大小就是有序小文件的个数500。堆中的每个节点存放每个有序小文件对应的输入流。 59 | - 按照每个有序文件中的下一行数据对所有文件输入流进行排序,单词小的输入文件流放在堆顶。 60 | - 拿出堆顶的输入流,并其下一行数据写入到最终排序的文件中,如果拿出来的输入流中还有数据的话,那么将这个输入流再一次添加到栈中。否则说明该文件输入流中没有数据了,那么可以关闭这个流。 61 | - 循环这个过程,直到所有文件输入流都没有数据为止。 62 | 63 | **第二步**: 64 | 65 | ① 初始化一个100个节点的**小顶堆**,用于保存100个出现频率最多的单词 66 | 67 | ② 遍历整个文件,一个单词一个单词的从文件中取出来,并计数 68 | 69 | ③ 等到遍历的单词和上一个单词不同的话,那么上一个单词及其频率如果大于堆顶的词的频率,那么放在堆中,否则不放 70 | 71 | 最终,小顶堆中就是出现频率前100的单词了。 72 | 73 | 解法2相对解法1,更加严谨,如果某个词词频过高或者整个文件都是同一个词的话,解法1不适用。 74 | 75 | -------------------------------------------------------------------------------- /docs/column/jvm/README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 3 | - [1.两数之和-梦开始的地方!!](./1-two-sum.md) 4 | - [3.无重复字符的最长子串](./3-longest-substring-without-repeating-characters.md) 5 | - [5.最长回文子串](./5-longest-palindromic-substring.md) 6 | - [7.整数反转](./7-reverse-integer.md) 7 | - [15.三数之和](./15-3sum.md) 8 | - [19.删除链表的倒数第N个结点](./19-remove-nth-node-from-end-of-list.md) 9 | - [22.括号生成](./22-generate-parentheses.md) 10 | - [28.对称的二叉树](./28-mirror-binary-tree.md) 11 | - [40.组合总和II](./40-combination-sum-ii.md) 12 | - [46.全排列](./46-permutations.md) 13 | - [47.全排列II](./47-permutations-ii.md) 14 | - [53.最大子数组和](./53-maximum-subarray.md) 15 | - [55.跳跃游戏](./55-jump-game.md) 16 | - [56.合并区间](./56-merge-intervals.md) 17 | - [62.不同路径](./62-unique-paths.md) 18 | - [92.反转链表II](./92-reverse-linked-list-ii.md) 19 | - [98.验证二叉搜索树](./98-validate-binary-search-tree.md) 20 | - [103.二叉树的锯齿形层序遍历](./103-binary-tree-zigzag-level-order-traversal.md) 21 | - [104.二叉树的最大深度](./104-maximum-depth-of-binary-tree.md) 22 | - [120.三角形最小路径和](./120-triangle.md) 23 | - [121.买卖股票的最佳时机](./121-best-time-to-buy-and-sell-stock.md) 24 | - [122.买卖股票的最佳时机II](./122-best-time-to-buy-and-sell-stock-ii.md) 25 | - [131.分割回文串](./131-palindrome-partion.md) 26 | - [133.克隆图](./133-clone-graph.md) 27 | - [134.加油站](./134-gas-station.md) 28 | - [141.环形链表II](./141-linked-list-cycle.md) 29 | - [146.LRU缓存](./146-lru-cache.md) 30 | - [152.乘积最大子数组](./152-maximum-product-subarray.md) 31 | - [160.相交链表](./160-intersection-of-two-linked-lists.md) 32 | - [169.多数元素](./169-majority-element.md) 33 | - [199.二叉树的右视图](./199-binary-tree-right-side-view.md) 34 | - [200.岛屿数量](./200-number-of-islands.md) 35 | - [206.反转链表](./206-reverse-linked-list.md) 36 | - [215.数组中的第K个最大元素](./215-kth-largest-element-in-an-array.md) 37 | - [234.回文链表](./234-palindrome-linked-list.md) 38 | - [236.二叉树的最近公共祖先](./236-lowest-common-ancestor-of-a-binary-tree.md) 39 | - [415.字符串相加](./415-add-strings.md) 40 | - [543.二叉树的直径](./543-diameter-of-binary-tree.md) 41 | - [1143.最长公共子序列](./1143-longest-common-subquence.md) 42 | -------------------------------------------------------------------------------- /docs/column/mysql/README.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 3 | - [1.两数之和-梦开始的地方!!](./1-two-sum.md) 4 | - [3.无重复字符的最长子串](./3-longest-substring-without-repeating-characters.md) 5 | - [5.最长回文子串](./5-longest-palindromic-substring.md) 6 | - [7.整数反转](./7-reverse-integer.md) 7 | - [15.三数之和](./15-3sum.md) 8 | - [19.删除链表的倒数第N个结点](./19-remove-nth-node-from-end-of-list.md) 9 | - [22.括号生成](./22-generate-parentheses.md) 10 | - [28.对称的二叉树](./28-mirror-binary-tree.md) 11 | - [40.组合总和II](./40-combination-sum-ii.md) 12 | - [46.全排列](./46-permutations.md) 13 | - [47.全排列II](./47-permutations-ii.md) 14 | - [53.最大子数组和](./53-maximum-subarray.md) 15 | - [55.跳跃游戏](./55-jump-game.md) 16 | - [56.合并区间](./56-merge-intervals.md) 17 | - [62.不同路径](./62-unique-paths.md) 18 | - [92.反转链表II](./92-reverse-linked-list-ii.md) 19 | - [98.验证二叉搜索树](./98-validate-binary-search-tree.md) 20 | - [103.二叉树的锯齿形层序遍历](./103-binary-tree-zigzag-level-order-traversal.md) 21 | - [104.二叉树的最大深度](./104-maximum-depth-of-binary-tree.md) 22 | - [120.三角形最小路径和](./120-triangle.md) 23 | - [121.买卖股票的最佳时机](./121-best-time-to-buy-and-sell-stock.md) 24 | - [122.买卖股票的最佳时机II](./122-best-time-to-buy-and-sell-stock-ii.md) 25 | - [131.分割回文串](./131-palindrome-partion.md) 26 | - [133.克隆图](./133-clone-graph.md) 27 | - [134.加油站](./134-gas-station.md) 28 | - [141.环形链表II](./141-linked-list-cycle.md) 29 | - [146.LRU缓存](./146-lru-cache.md) 30 | - [152.乘积最大子数组](./152-maximum-product-subarray.md) 31 | - [160.相交链表](./160-intersection-of-two-linked-lists.md) 32 | - [169.多数元素](./169-majority-element.md) 33 | - [199.二叉树的右视图](./199-binary-tree-right-side-view.md) 34 | - [200.岛屿数量](./200-number-of-islands.md) 35 | - [206.反转链表](./206-reverse-linked-list.md) 36 | - [215.数组中的第K个最大元素](./215-kth-largest-element-in-an-array.md) 37 | - [234.回文链表](./234-palindrome-linked-list.md) 38 | - [236.二叉树的最近公共祖先](./236-lowest-common-ancestor-of-a-binary-tree.md) 39 | - [415.字符串相加](./415-add-strings.md) 40 | - [543.二叉树的直径](./543-diameter-of-binary-tree.md) 41 | - [1143.最长公共子序列](./1143-longest-common-subquence.md) 42 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/14-architect-forward.md: -------------------------------------------------------------------------------- 1 | # 架构的演进 2 | 3 | ### 传统单体应用架构 4 | 5 | 十多年前主流的应用架构都是单体应用,部署形式就是一台服务器加一个数据库,在这种架构下,运维人员会小心翼翼地维护这台服务器,以保证服务的可用性。 6 | 7 | ![](http://img.topjavaer.cn/img/架构演进1.png) 8 | 9 | #### 单体应用架构面临的问题 10 | 11 | 随着业务的增长,这种最简单的单体应用架构很快就面临两个问题。首先,这里只有一台服务器,如果这台服务器出现故障,例如硬件损坏,那么整个服务就会不可用;其次,业务量变大之后,一台服务器的资源很快会无法承载所有流量。 12 | 13 | 解决这两个问题最直接的方法就是在流量入口加一个负载均衡器,使单体应用同时部署到多台服务器上,这样服务器的单点问题就解决了,与此同时,这个单体应用也具备了水平伸缩的能力。 14 | 15 | ![单体架构(水平伸缩)](http://img.topjavaer.cn/img/架构演进2.png) 16 | 17 | ### 微服务架构 18 | 19 | #### 1. 微服务架构演进出通用服务 20 | 21 | 随着业务的进一步增长,更多的研发人员加入到团队中,共同在单体应用上开发特性。由于单体应用内的代码没有明确的物理边界,大家很快就会遇到各种冲突,需要人工协调,以及大量的 conflict merge 操作,研发效率直线下降。 22 | 23 | 因此大家开始把单体应用拆分成一个个可以独立开发、独立测试、独立部署的微服务应用,服务和服务之间通过 API 通讯,如 HTTP、GRPC 或者 DUBBO。基于领域驱动设计中 Bounded Context 拆分的微服务架构能够大幅提升中大型团队的研发效率。 24 | 25 | #### 2. 微服务架构给运维带来挑战 26 | 27 | 应用从单体架构演进到微服务架构,从物理的角度看,分布式就成了默认选项,这时应用架构师就不得不面对分布式带来的新挑战。在这个过程中,大家都会开始使用一些分布式服务和框架,例如缓存服务 Redis,配置服务 ACM,状态协调服务 ZooKeeper,消息服务 Kafka,还有通讯框架如 GRPC 或者 DUBBO,以及分布式追踪系统等。 28 | 29 | 除分布式环境带来的挑战之外,微服务架构给运维也带来新挑战。研发人员原来只需要运维一个应用,现在可能需要运维十个甚至更多的应用,这意味着安全 patch 升级、容量评估、故障诊断等事务的工作量呈现成倍增长,这时,应用分发标准、生命周期标准、观测标准、自动化弹性等能力的重要性也更加凸显。 30 | 31 | ![](http://img.topjavaer.cn/img/微服务架构.png) 32 | 33 | ### 云原生 34 | 35 | #### 1. 基于云产品架构 36 | 37 | 一个架构是否是云原生,就看这个架构是否是长在云上的,这是对“云原生”的简单理解。这个“长在云上”不是简单地说用云的 IaaS 层服务,比如简单的 ECS、OSS 这些基本的计算存储;而是应该理解成有没有使用云上的分布式服务,比如 Redis、Kafka 等,这些才是直接影响到业务架构的服务。微服务架构下,分布式服务是必要的,原来大家都是自己研发这样的服务,或者基于开源版本自己运维这样的服务。而到了云原生时代,业务则可以直接使用云服务。 38 | 39 | 另外两个不得不提的技术就是 Docker 和 Kubenetes,其中,前者标准化了应用分发的标准,不论是 Spring Boot 写的应用,还是 NodeJS 写的应用,都以镜像的方式分发;而后者在前者的技术上又定义了应用生命周期的标准,一个应用从启动到上线,到健康检查,再到下线,都有了统一的标准。 40 | 41 | #### 2. 应用生命周期托管 42 | 43 | 有了应用分发的标准和生命周期的标准,云就能提供标准化的应用托管服务。包括应用的版本管理、发布、上线后的观测、自愈等。例如对于无状态的应用来说,一个底层物理节点的故障根本不会影响到研发,因为应用托管服务基于标准化应用生命周期可以自动完成腾挪工作,在故障物理节点上将应用的容器下线,在新的物理节点上启动同等数量的应用容器。可以看出,云原生进一步释放了价值红利。 44 | 45 | 在此基础上,由于应用托管服务能够感知到应用运行期的数据,例如业务流量的并发、cpu load、内存占用等,业务就可以配置基于这些指标的伸缩规则,再由平台执行这些规则,根据业务流量的实际情况增加或者减少容器数量,这就是最基本的 auto scaling——自动伸缩。这能够帮助用户避免在业务低峰期限制资源,节省成本,提升运维效率。 46 | 47 | ### 本文总结 48 | 49 | 在架构的演进过程中,研发运维人员逐渐把关注点从机器上移走,希望更多地由平台系统管理机器,而不是由人去管理,这就是一个对 Serverless 的朴素理解。 50 | 51 | > 本文部分内容摘抄自网络 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/19-java19.md: -------------------------------------------------------------------------------- 1 | # Java19新特性 2 | 3 | JDK 19 / Java 19 已正式发布。 4 | 5 | ![](http://img.topjavaer.cn/img/java19.png) 6 | 7 | 新版本总共包含 7 个新的 JEP: 8 | 9 | | 405: | Record Patterns (Preview) | 10 | | ---- | ------------------------------------------- | 11 | | 422: | Linux/RISC-V Port | 12 | | 424: | Foreign Function & Memory API (Preview) | 13 | | 425: | Virtual Threads (Preview) | 14 | | 426: | Vector API (Fourth Incubator) | 15 | | 427: | Pattern Matching for switch (Third Preview) | 16 | | 428: | Structured Concurrency (Incubator) | 17 | 18 | - 405:记录模式 (Record Patterns) 进入预览阶段 19 | 20 | Record Patterns 可对 record 的值进行解构,Record patterns 和 Type patterns 通过嵌套能够实现强大的、声明性的、可组合的数据导航和处理形式。 21 | 22 | 该特性目前处于预览阶段。 23 | 24 | - 422:将 JDK 移植到 Linux/RISC-V 平台 25 | 26 | 目前只支持 RISC-V 的 RV64GV 配置,它是一个通用的 64 位 ISA。将来会考虑支持其他的 RISC-V 配置,例如通用的 32 位配置 (RV32G)。 27 | 28 | - 424:外部函数和内存 API (Foreign Function & Memory API) 进入预览阶段 29 | 30 | Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数(即 JVM 之外的代码)和安全地访问外部内存(即不受 JVM 管理的内存),该 API 使 Java 程序能够调用本机库并处理本机数据,而不会像 JNI 那样危险和脆弱。 31 | 32 | 一句话总结:该特性让 Java 调用普通 native 代码更加方便和高效。 33 | 34 | - 425:虚拟线程 (Virtual Threads) 进入预览阶段 35 | 36 | 为 Java 引入虚拟线程,虚拟线程是 JDK 实现的轻量级线程,它在其他多线程语言中已经被证实是十分有用的,比如 Go 中的 Goroutine、Erlang 中的进程。另外,最新 Java 面试题整理好了,大家可以在Java面试库小程序在线刷题。 37 | 38 | 虚拟线程避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂,可以有效减少编写、维护和观察高吞吐量并发应用程序的工作量。 39 | 40 | - 426:向量 API (Vector API) 进入第 4 孵化阶段 41 | 42 | 向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算,该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。 43 | 44 | - 427:switch 模式匹配 (Pattern Matching for switch) 进入第 3 预览阶段 45 | 46 | 用 `switch` 表达式和语句的模式匹配,以及对模式语言的扩展来增强 Java 编程语言。将模式匹配扩展到 `switch` 中,允许针对一些模式测试表达式,这样就可以简明而安全地表达复杂的面向数据的查询。 47 | 48 | - 428:结构化并发 (Structured Concurrency) 进入孵化阶段 49 | 50 | JDK 19 引入了结构化并发,这是一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代 java.util.concurrent,目前处于孵化阶段。 51 | 52 | 结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。 53 | 54 | 下载地址:https://jdk.java.net/19/ 55 | 56 | Release Note:https://jdk.java.net/19/release-notes -------------------------------------------------------------------------------- /docs/java2top/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 项目介绍 3 | category: 走近项目 4 | --- 5 | 6 | ## 背景故事 7 | 8 | 空暇之余,经常有很多粉丝、学弟学妹问我 "`如何学习 Java,什么时候学?` `学什么?` `有没有相关资料资源推荐?` `网上各自路线太多太杂,不知道究竟学什么?` 哪里该学哪里可以不用学?如果我要进大厂应该学到什么程度?要不要去培训?" 9 | 10 | 作为一个 `完全靠自学二本上岸大厂的 JavaCoder,深知没人带一路摸索的艰辛,想把自己的心路历程和经验所得收获,毫无保留全部梳理出来分享给大家`。 11 | 12 | 都说 “`计算机是穷苦孩子出路最好的方法`” ,但是由于自己身处环境的差异,导致你的信息与所见所感总不及 “`本就赢在起跑线`” 的那些人。 13 | 14 | 有人说,若是在一开始给我**相同的资源与指导,有良师给我指明方向**,我绝对不比 那些 “`天赋高,大城市,高学历`“ 的人差。 15 | 16 | 因此,我想打造一个全网最佳的学习进阶平台。我想让所有人,不管是什么身份,什么学历,什么能力程度的万千学子,在学习计算机这条路上,尽最大程度减少信息差。 17 | 18 | 知道**尽可能优质全面的学习体系、学习资源,走尽可能少的弯路成长为一名成熟的RD**。 19 | 20 | Java系列「`校招面试求职,大厂学习导航`」认准「**Java2Top**」认准「**小龙coding**」。 21 | 22 | > 校招面试求职,大厂学习导航~ 23 | > 24 | > 致力打造全网最佳学习进阶平台 25 | 26 | ## 作者介绍 27 | 28 | ⼤家好,我是小龙,是这本`我要进大厂 | Java2Top `学习指南的作者,电⼦书 or 专栏 的内容都会在 功粽hao「**小龙coding**」⾥首发,全网同步。最后会及时在 功粽hao 同步 PDF 与 Git仓库。 29 | 30 | 还没关注的朋友,可以 V 搜索「**小龙coding**」,关注我的 功粽hao,**后续最新版本的 PDF 会在我的 功粽hao 第⼀时间发布**,各种硬核编程资源,重磅专栏会在其及时更新同步。 31 | 32 | Java系列「`校招面试求职,大厂学习导航`」认准「**小龙coding**」。 33 | 34 | ## 学前须知 35 | 36 | ### 1、指南介绍 37 | 38 | **本指南名曰**:《**Java2Top**》,意为 Java To Top 希望大家都能成为 Java 界的佼佼者,毕业能进行业的Top 企业公司。 39 | 40 | 简单介绍下这份 学习指南 ,本指南 or 专栏是`根据作者从末流二本依靠自己摸爬滚最终上岸大厂一路的心得感悟与经验`, 41 | 42 | 整体框架是 「带你从进入大学方向选择到逐步学习至校招面试最后作为coder融入职场的整个过程」 [手动狗头]。 43 | 44 | ⽂字都是⼩龙⼀个字⼀个字敲出来的,图⽚都是⼩⼀个点⼀条线画出来的,⾮常的不容易。专栏书籍等资源全是小龙在整个学习成长以及秋招求职期间学习研读过觉得特别经典有用,进而收集的。 45 | 46 | 此版本为 '`Java2TopV1.0`' 后期会根据各方面因素调整,不过总的框架不会变,也就是说你不管根据哪个版本学习都没问题,只是随着版本迭代,后面涉及考虑的地方会更加细致,更加全面。 47 | 48 | ### 2、这份指南适合什么群体呢? 49 | 50 | 这份指南主要是面向 「**JavaCoder**」,当然其他方向也可以参考其中某些模块,比如 「**CS基础**」 等。 51 | 52 | 指南分**不同模块**,适合不同人群: 53 | 54 | 你可以是零基础小白,从上往下学习。 55 | 56 | 你若是着急找工作,直接看`《专题进阶》`、`《面试指南》`。 57 | 58 | ### 3、这份指南要怎么阅读呢? 59 | 60 | 这份指南分为不同模块,「编程资源」 「零基础小白」、「专题学习」、「校招求职」、「面试速成」 「基于人工智能的智慧校园助手」等模块。 61 | 62 | 你可以把这份指南整体架构想象成一本功法,不同模块根据学习者层次而设定。 63 | 64 | - 「编程资源」属于**功法库**—— 收录涵盖各个经典 开源项目,编程资源,编程工具;`加餐补给以国外优质网站资源为主`。 65 | - 「大厂导航」旨在**基础招式**——会带着你`从懵懂无知畅游Java之海而到初有感悟`,有对应书籍、视频、专栏学习指引; 66 | 67 | - 「专题进阶」旨在**内功夯实**——会带着你 进一步夯实基础,根据大厂面试常考重点难点设置对应专题,大部分对应着大厂面试题; 68 | 69 | - 「面试指南」属于**后期功法也可称秘法**——当你一步一步从头学到这,完毕必定大成,根基稳固;(切记)`若你着急找工作也可借此秘法快速达到大厂面试要求,但是可能实力虚浮`; 70 | 71 | - 「校招求职」属于**功法斗技**,建议面试求职前一定认真阅读,对面试求职必定事半功倍。 72 | - 「aurora」属于**万人练级**,后期我会准备一个 「**全民项目**」,从 `提需求->开发->提 PR ->merge` ,把大家召集起来一起开发,项目会`将当下大厂常问的各种热门技术点尽可能融入`,大家互相讨论进步。最后此项目可以 作为软件竞赛,校招求职项目。 73 | 74 | 若你是刚入门或者一问三不知的零基础小白,建议从头阅读,根基打牢;若你有一定 基础,对面试认知欠缺,可直接看「面试指南」、「aurora」项目模块。 75 | 76 | **狡辩一下:** 77 | 78 | 如果看到某块内容缺失,别急,作者在加班加点熬夜肝中(作者在互联网大厂工作,平时开发任务重,只有下班或者周末才有时间完善更新); 79 | 80 | 也有的模块已经梳理好,正在排版画图以最通俗有趣的形式去展现给大家。 81 | 82 | ## 勘误事宜 83 | 84 | 小龙不是神,或许有不足也有出错的地方**如果你发现有任何错误或者疑惑的地方或者任何补充,欢迎你通过下⽅的邮箱反馈给⼩龙**; 85 | 86 | 小龙会逐个修正,然后发布新版本的大厂进阶学习指南 or 专栏,⼀起迭代出更好的大厂进阶指南 or 专栏! 87 | 88 | 勘误邮箱:xlcoding@163.com -------------------------------------------------------------------------------- /docs/java2top/contribution.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 项目贡献 3 | category: 走近项目 4 | --- 5 | 6 | ## 背景故事 7 | 8 | 空暇之余,经常有很多粉丝、学弟学妹问我 "`如何学习 Java,什么时候学?` `学什么?` `有没有相关资料资源推荐?` `网上各自路线太多太杂,不知道究竟学什么?` 哪里该学哪里可以不用学?如果我要进大厂应该学到什么程度?要不要去培训?" 9 | 10 | 作为一个 `完全靠自学二本上岸大厂的 JavaCoder,深知没人带一路摸索的艰辛,想把自己的心路历程和经验所得收获,毫无保留全部梳理出来分享给大家`。 11 | 12 | 都说 “`计算机是穷苦孩子出路最好的方法`” ,但是由于自己身处环境的差异,导致你的信息与所见所感总不及 “`本就赢在起跑线`” 的那些人。 13 | 14 | 有人说,若是在一开始给我**相同的资源与指导,有良师给我指明方向**,我绝对不比 那些 “`天赋高,大城市,高学历`“ 的人差。 15 | 16 | 因此,我想打造一个全网最佳的学习进阶平台。我想让所有人,不管是什么身份,什么学历,什么能力程度的万千学子,在学习计算机这条路上,尽最大程度减少信息差。 17 | 18 | 知道**尽可能优质全面的学习体系、学习资源,走尽可能少的弯路成长为一名成熟的RD**。 19 | 20 | Java系列「`校招面试求职,大厂学习导航`」认准「**Java2Top**」认准「**小龙coding**」。 21 | 22 | > 校招面试求职,大厂学习导航~ 23 | > 24 | > 致力打造全网最佳学习进阶平台 25 | 26 | ## 作者介绍 27 | 28 | ⼤家好,我是小龙,是这本`我要进大厂 | Java2Top `学习指南的作者,电⼦书 or 专栏 的内容都会在 功粽hao「**小龙coding**」⾥首发,全网同步。最后会及时在 功粽hao 同步 PDF 与 Git仓库。 29 | 30 | 还没关注的朋友,可以 V 搜索「**小龙coding**」,关注我的 功粽hao,**后续最新版本的 PDF 会在我的 功粽hao 第⼀时间发布**,各种硬核编程资源,重磅专栏会在其及时更新同步。 31 | 32 | Java系列「`校招面试求职,大厂学习导航`」认准「**小龙coding**」。 33 | 34 | ## 学前须知 35 | 36 | ### 1、指南介绍 37 | 38 | **本指南名曰**:《**Java2Top**》,意为 Java To Top 希望大家都能成为 Java 界的佼佼者,毕业能进行业的Top企业公司。 39 | 40 | 简单介绍下这份 学习指南 ,本指南 or 专栏是`根据作者从末流二本依靠自己摸爬滚最终上岸大厂一路的心得感悟与经验`, 41 | 42 | 整体框架是 「带你从进入大学方向选择到逐步学习至校招面试最后作为coder融入职场的整个过程」 [手动狗头]。 43 | 44 | ⽂字都是⼩龙⼀个字⼀个字敲出来的,图⽚都是⼩⼀个点⼀条线画出来的,⾮常的不容易。专栏书籍等资源全是小龙在整个学习成长以及秋招求职期间学习研读过觉得特别经典有用,进而收集的。 45 | 46 | 此版本为 '`Java2TopV1.0`' 后期会根据各方面因素调整,不过总的框架不会变,也就是说你不管根据哪个版本学习都没问题,只是随着版本迭代,后面涉及考虑的地方会更加细致,更加全面。 47 | 48 | ### 2、这份指南适合什么群体呢? 49 | 50 | 这份指南主要是面向 「**JavaCoder**」,当然其他方向也可以参考其中某些模块,比如 「**CS基础**」 等。 51 | 52 | 指南分**不同模块**,适合不同人群: 53 | 54 | 你可以是零基础小白,从上往下学习。 55 | 56 | 你若是着急找工作,直接看`《专题进阶》`、`《面试指南》`。 57 | 58 | ### 3、这份指南要怎么阅读呢? 59 | 60 | 这份指南分为不同模块,「编程资源」 「零基础小白」、「专题学习」、「校招求职」、「面试速成」 「基于人工智能的智慧校园助手」等模块。 61 | 62 | 你可以把这份指南整体架构想象成一本功法,不同模块根据学习者层次而设定。 63 | 64 | - 「编程资源」属于**功法库**—— 收录涵盖各个经典 开源项目,编程资源,编程工具;`加餐补给以国外优质网站资源为主`。 65 | - 「大厂导航」旨在**基础招式**——会带着你`从懵懂无知畅游Java之海而到初有感悟`,有对应书籍、视频、专栏学习指引; 66 | 67 | - 「专题进阶」旨在**内功夯实**——会带着你 进一步夯实基础,根据大厂面试常考重点难点设置对应专题,大部分对应着大厂面试题; 68 | 69 | - 「面试指南」属于**后期功法也可称秘法**——当你一步一步从头学到这,完毕必定大成,根基稳固;(切记)`若你着急找工作也可借此秘法快速达到大厂面试要求,但是可能实力虚浮`; 70 | 71 | - 「校招求职」属于**功法斗技**,建议面试求职前一定认真阅读,对面试求职必定事半功倍。 72 | - 「aurora」属于**万人练级**,后期我会准备一个 「**全民项目**」,从 `提需求->开发->提 PR ->merge` ,把大家召集起来一起开发,项目会`将当下大厂常问的各种热门技术点尽可能融入`,大家互相讨论进步。最后此项目可以 作为软件竞赛,校招求职项目。 73 | 74 | 若你是刚入门或者一问三不知的零基础小白,建议从头阅读,根基打牢;若你有一定 基础,对面试认知欠缺,可直接看「面试指南」、「aurora」项目模块。 75 | 76 | **狡辩一下:** 77 | 78 | 如果看到某块内容缺失,别急,作者在加班加点熬夜肝中(作者在互联网大厂工作,平时开发任务重,只有下班或者周末才有时间完善更新); 79 | 80 | 也有的模块已经梳理好,正在排版画图以最通俗有趣的形式去展现给大家。 81 | 82 | ## 勘误事宜 83 | 84 | 小龙不是神,或许有不足也有出错的地方**如果你发现有任何错误或者疑惑的地方或者任何补充,欢迎你通过下⽅的邮箱反馈给⼩龙**; 85 | 86 | 小龙会逐个修正,然后发布新版本的大厂进阶学习指南 or 专栏,⼀起迭代出更好的大厂进阶指南 or 专栏! 87 | 88 | 勘误邮箱:xlcoding@163.com -------------------------------------------------------------------------------- /docs/guide/mass-data/6-top-500-num.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 如何找出排名前 500 的数? 3 | --- 4 | 5 | ## 题目描述 6 | 7 | 有 1w 个数组,每个数组有 500 个元素,并且有序排列。如何在这 10000*500 个数中找出前 500 的数? 8 | 9 | ## 方法1 10 | 11 | 题目中每个数组是排好序的,可以使用**归并**的方法。 12 | 13 | 先将第1个和第2个归并,得到500个数据。然后再将结果和第3个数组归并,得到500个数据,以此类推,直到最后找出前500个的数。 14 | 15 | ## 方法2 16 | 17 | 对于这种topk问题,更常用的方法是使用**堆排序**。 18 | 19 | 对本题而言,假设数组降序排列,可以采用以下方法: 20 | 21 | 首先建立大顶堆,堆的大小为数组的个数,即为10000 ,把每个数组最大的值存到堆中。 22 | 23 | 接着删除堆顶元素,保存到另一个大小为 500 的数组中,然后向大顶堆插入删除的元素所在数组的下一个元素。 24 | 25 | 重复上面的步骤,直到删除完第 500 个元素,也即找出了最大的前 500 个数。 26 | 27 | **示例代码如下**: 28 | 29 | ```java 30 | import lombok.Data; 31 | 32 | import java.util.Arrays; 33 | import java.util.PriorityQueue; 34 | 35 | @Data 36 | public class DataWithSource implements Comparable { 37 | /** 38 | * 数值 39 | */ 40 | private int value; 41 | 42 | /** 43 | * 记录数值来源的数组 44 | */ 45 | private int source; 46 | 47 | /** 48 | * 记录数值在数组中的索引 49 | */ 50 | private int index; 51 | 52 | public DataWithSource(int value, int source, int index) { 53 | this.value = value; 54 | this.source = source; 55 | this.index = index; 56 | } 57 | 58 | /** 59 | * 60 | * 由于 PriorityQueue 使用小顶堆来实现,这里通过修改 61 | * 两个整数的比较逻辑来让 PriorityQueue 变成大顶堆 62 | */ 63 | @Override 64 | public int compareTo(DataWithSource o) { 65 | return Integer.compare(o.getValue(), this.value); 66 | } 67 | } 68 | 69 | 70 | class Test { 71 | public static int[] getTop(int[][] data) { 72 | int rowSize = data.length; 73 | int columnSize = data[0].length; 74 | 75 | // 创建一个columnSize大小的数组,存放结果 76 | int[] result = new int[columnSize]; 77 | 78 | PriorityQueue maxHeap = new PriorityQueue<>(); 79 | for (int i = 0; i < rowSize; ++i) { 80 | // 将每个数组的最大一个元素放入堆中 81 | DataWithSource d = new DataWithSource(data[i][0], i, 0); 82 | maxHeap.add(d); 83 | } 84 | 85 | int num = 0; 86 | while (num < columnSize) { 87 | // 删除堆顶元素 88 | DataWithSource d = maxHeap.poll(); 89 | result[num++] = d.getValue(); 90 | if (num >= columnSize) { 91 | break; 92 | } 93 | 94 | d.setValue(data[d.getSource()][d.getIndex() + 1]); 95 | d.setIndex(d.getIndex() + 1); 96 | maxHeap.add(d); 97 | } 98 | return result; 99 | 100 | } 101 | 102 | public static void main(String[] args) { 103 | int[][] data = { 104 | {29, 17, 14, 2, 1}, 105 | {19, 17, 16, 15, 6}, 106 | {30, 25, 20, 14, 5}, 107 | }; 108 | 109 | int[] top = getTop(data); 110 | System.out.println(Arrays.toString(top)); // [30, 29, 25, 20, 19] 111 | } 112 | } 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/18-db-connect-resource.md: -------------------------------------------------------------------------------- 1 | # 为什么说数据库连接很消耗资源 2 | 3 | 相信有过工作经验的同学都知道数据库连接是一个比较耗资源的操作。那么资源到底是耗费在哪里呢? 4 | 5 | 本文主要想探究一下连接数据库的细节,尤其是在`Web`应用中要使用数据库来连接池,以免每次发送一次请求就重新建立一次连接。对于这个问题,答案都是一致的,建立数据库连接很耗时,但是这个耗时是都多少呢,又是分别在哪些方面产生的耗时呢? 6 | 7 | 本文以连接`MySQL`数据库为例,因为`MySQL`数据库是开源的,其通信协议是公开的,所以我们能够详细分析建立连接的整个过程。 8 | 9 | > 在本文中,消耗资源的分析主要集中在网络上,当然,资源也包括内存、`CPU`等计算资源,使用的编程语言是`Java`,但是不排除编程语言也会有一定的影响。 10 | 11 | 首先先看一下连接数据库的`Java`代码,如下: 12 | 13 | ```java 14 | Class.forName("com.mysql.jdbc.Driver"); 15 | 16 | String name = "xttblog2"; 17 | String password = "123456"; 18 | String url = "jdbc:mysql://xxx:3306/xttblog2"; 19 | Connection conn = DriverManager.getConnection(url, name, password); 20 | // 之后程序终止,连接被强制关闭 21 | ``` 22 | 23 | 然后通过`Wireshark`,分析整个连接的建立过程,如下: 24 | 25 | ![](http://img.topjavaer.cn/img/db-connect-1.png) 26 | 27 | ## **Wireshark抓包** 28 | 29 | 在上图中显示的连接过程中,可以看出`MySQL`的通信协议是基于`TCP`传输协议的,而且该协议是二进制协议,不是类似于`HTTP`的文本协议,其中建立连接的过程具体如下: 30 | 31 | - 第1步:建立`TCP`连接,通过三次握手实现; 32 | - 第2步:服务器发送给客户端「握手信息」 ,客户端响应该握手消息; 33 | - 第3步:客户端「发送认证包」 ,用于用户验证,验证成功后,服务器返回`OK`响应,之后开始执行命令; 34 | 35 | 用户验证成功之后,会进行一些连接变量的设置,**比如字符集、是否自动提交事务等,其间会有多次数据的交互**。完成了这些步骤后,才会执行真正的数据查询和更新等操作。 36 | 37 | 在本文的测试中,只用了`5`行代码来建立连接,但是并没有通过该连接去执行任何操作,所以在程序执行完毕之后,连接不是通过`Connection.close()`关闭的,而是由于程序执行完毕,导致进程终止,造成与数据库的连接异常关闭,所以最后会出现`TCP`的`RST`报文。 38 | 39 | 在这个最简单的代码中,没有设置任何额外的连接属性,所以在设置属性上占用的时间可以认为是最少的(其实,虽然我们没有设置任何属性,但是驱动仍然设置了字符集、事务自动提交等,这取决于具体的驱动实现),所以整个连接所使用的时间可以认为是最少的。 40 | 41 | 但从统计信息中可以看出,在不包括最后`TCP`的`RST`报文时(因为该报文不需要服务器返回任何响应),但是其中仍需在客户端和服务器之间进行往返「`7`」 次,「也就是说完成一次连接,可以认为,数据在客户端和服务器之间需要至少往返`7`次」 ,从时间上来看,从开始`TCP`的三次握手,到最终连接强制断开为止(不包括最后的`RST`报文),总共花费了: 42 | 43 | ```java 44 | 10.416042 - 10.190799 = 0.225243s = 225.243ms!!! 45 | ``` 46 | 47 | 这意味着,建立一次数据库连接需要`225ms`,而这还是还可以认为是最少的,当然「花费的时间可能受到网络状况、数据库服务器性能以及应用代码是否高效的影响」 ,但是这里只是一个最简单的例子,已经足够说明问题了! 48 | 49 | 由于上面是程序异常终止了,但是在正常的应用程序中,连接的关闭一般都是通过`Connection.close()`完成的,代码如下: 50 | 51 | ```java 52 | Class.forName("com.mysql.jdbc.Driver"); 53 | 54 | String name = "shine_user"; 55 | String password = "123"; 56 | String url = "jdbc:mysql://xxx:3306/clever_mg_test"; 57 | Connection conn = DriverManager.getConnection(url, name, password); 58 | conn.close(); 59 | ``` 60 | 61 | 这样的话,情况发生了变化,主要体现在与数据库连接的断开,如下图: 62 | 63 | ![](http://img.topjavaer.cn/img/db-connect-2.png) 64 | 65 | ## **网络抓包** 66 | 67 | - 第1步:此时处于`MySQL`通信协议阶段,客户端发送关闭连接请求,而且不用等待服务端的响应; 68 | - 第2步:`TCP`断开连接,`4`次挥手完成连接断开; 69 | 70 | 这里是完整地完成了从数据库连接的建立到关闭,整个过程花费了: 71 | 72 | ``` 73 | 747.284311 - 747.100954 = 0.183357s = 183.357ms 74 | ``` 75 | 76 | 这里可能也有网络状况的影响,比上述的`225ms`少了,但是也几乎达到了`200ms`的级别。 77 | 78 | 那么问题来了,想象一下这个场景,对于一个日活`2万`的网站来说,假设每个用户只会发送`5`个请求,那么一天就是`10万`个请求,对于建立数据库连接,我们保守一点计算为`150ms`好了,那么一天当中花费在建立数据库连接的时间有(还不包括执行查询和更新操作): 79 | 80 | ``` 81 | 100000 * 150ms = 15000000ms = 15000s = 250min = 4.17h 82 | ``` 83 | 84 | 也就说每天花费在建立数据库连接上的时间已经达到「`4个小时`」 ,所以说数据库连接池是必须的,而且当日活增加时,单单使用数据库连接池也不能完全保证你的服务能够正常运行,还需要考虑其他的解决方案: 85 | 86 | - 缓存 87 | - SQL 的预编译 88 | - 负载均衡 89 | - …… 90 | 91 | 92 | 93 | 总之,数据库连接真的很耗时,所以不要频繁的**建立连接**。 94 | 95 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/28-springboot-forbid-tomcat.md: -------------------------------------------------------------------------------- 1 | # 大公司为什么禁止SpringBoot项目使用Tomcat? 2 | 3 | ## 前言 4 | 5 | 在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。同时,SpringBoot也支持Undertow容器,我们可以很方便的用Undertow替换Tomcat,而Undertow的性能和内存使用方面都优于Tomcat,那我们如何使用Undertow技术呢?本文将为大家细细讲解。 6 | 7 | ## SpringBoot中的Tomcat容器 8 | 9 | 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)。 10 | 11 | ## SpringBoot设置Undertow 12 | 13 | 对于Tomcat技术,Java程序员应该都非常熟悉,它是Web应用最常用的容器技术。我们最早的开发的项目基本都是部署在Tomcat下运行,那除了Tomcat容器,SpringBoot中我们还可以使用什么容器技术呢?没错,就是题目中的Undertow容器技术。SrpingBoot已经完全继承了Undertow技术,我们只需要引入Undertow的依赖即可,如下图所示。 14 | 15 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/89d3d0c3442f49be8eefc65eca316c49~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 16 | 17 | ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed1025c2627f426a858507ad19ae8e31~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 18 | 19 | 配置好以后,我们启动应用程序,发现容器已经替换为Undertow。那我们为什么需要替换Tomcat为Undertow技术呢? 20 | 21 | ## Tomcat与Undertow的优劣对比 22 | 23 | Tomcat是Apache基金下的一个轻量级的Servlet容器,支持Servlet和JSP。Tomcat具有Web服务器特有的功能,包括 Tomcat管理和控制平台、安全局管理和Tomcat阀等。Tomcat本身包含了HTTP服务器,因此也可以视作单独的Web服务器。但是,Tomcat和ApacheHTTP服务器不是一个东西,ApacheHTTP服务器是用C语言实现的HTTP Web服务器。Tomcat是完全免费的,深受开发者的喜爱。 24 | 25 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d06f4449d9e4d2983b24e09b4fda910~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image) 26 | 27 | Undertow是Red Hat公司的开源产品, 它完全采用Java语言开发,是一款灵活的高性能Web服务器,支持阻塞IO和非阻塞IO。由于Undertow采用Java语言开发,可以直接嵌入到Java项目中使用。同时, Undertow完全支持Servlet和Web Socket,在高并发情况下表现非常出色。 28 | 29 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36ee9da8d1404eefa1518f0f2796a76d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image) 30 | 31 | 我们在相同机器配置下压测Tomcat和Undertow,得到的测试结果如下所示:**QPS测试结果对比:** Tomcat 32 | 33 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/785bf4e5e3174556b158bb8e484a14b9~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image) 34 | 35 | Undertow 36 | 37 | ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0ec493f88954686b9f6d3554684c9b4~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image) 38 | 39 | **内存使用对比:** 40 | 41 | Tomcat 42 | 43 | ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d593ce0dad884e3c8b07e0d6602fdac6~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 44 | 45 | Undertow 46 | 47 | ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88a059beec2e4779ba971891fbfc8638~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 48 | 49 | 通过测试发现,在高并发系统中,Tomcat相对来说比较弱。在相同的机器配置下,模拟相等的请求数,Undertow在性能和内存使用方面都是最优的。并且Undertow新版本默认使用持久连接,这将会进一步提高它的并发吞吐能力。所以,如果是高并发的业务系统,Undertow是最佳选择。 50 | 51 | ## 最后 52 | 53 | SpingBoot中我们既可以使用Tomcat作为Http服务,也可以用Undertow来代替。Undertow在高并发业务场景中,性能优于Tomcat。所以,如果我们的系统是高并发请求,不妨使用一下Undertow,你会发现你的系统性能会得到很大的提升。 54 | 55 | 56 | 57 | > 参考链接:原文地址:toutiao.com/a677547665941699021 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/8-interface-idempotent.md: -------------------------------------------------------------------------------- 1 | # 面试官:如何保证接口幂等性?一口气说了12种方法! 2 | 3 | 大家好,我是大彬~ 4 | 5 | 今天来聊聊接口幂等性。 6 | 7 | 什么是接口幂等性?如何保证接口幂等性? 8 | 9 | ## 什么是接口幂等性? 10 | 11 | 首先看看幂等性的概念: 12 | 13 | 幂等性原本是数学上的概念,用在接口上就可以理解为:**同一个接口,多次发出同一个请求,必须保证操作只执行一次**。调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。 14 | 15 | 比如下面这些情况,如果没有实现接口幂等性会有很严重的后果:支付接口,重复支付会导致多次扣钱 ;订单接口,同一个订单可能会多次创建。 16 | 17 | ## 为什么会产生接口幂等性问题? 18 | 19 | 那么,什么情况下,会产生接口幂等性的问题呢? 20 | 21 | - 网络波动, 可能会引起重复请求 22 | - 用户重复操作,用户在操作时候可能会无意触发多次下单交易,甚至没有响应而有意触发多次交易应用 23 | - 使用了失效或超时重试机制(Nginx重试、RPC重试或业务层重试等) 24 | - 页面重复刷新 25 | - 使用浏览器后退按钮重复之前的操作,导致重复提交表单 26 | - 使用浏览器历史记录重复提交表单 27 | - 浏览器重复的HTTP请求 28 | - 定时任务重复执行 29 | - 用户双击提交按钮 30 | 31 | ## 如何保证接口幂等性? 32 | 33 | 那么最关键的来了,如何保证接口幂等性? 34 | 35 | 解决办法分为两个方向,一个方向是客户端防止重复调用,一个是服务端进行校验。当然,客户端防止重复提交并不是绝对可靠的,优点是实现起来比较简单。 36 | 37 | ### **按钮只可操作一次** 38 | 39 | 一般是提交后把按钮置灰或loding状态,消除用户因为重复点击而产生的重复记录,比如添加操作,由于点击两次而产生两条记录 40 | 41 | ### **token机制** 42 | 43 | 功能上允许重复提交,但要保证重复提交不产生副作用,比如点击n次只产生一条记录,具体实现就是进入页面时申请一个token,然后后面所有的请求都带上这个token,后端根据token来避免重复请求。 44 | 45 | ![](http://img.topjavaer.cn/img/接口幂等.png) 46 | 47 | ### **使用Post/Redirect/Get模式** 48 | 49 | 在提交后执行页面重定向,这就是所谓的Post-Redirect—Get(PRG)模式,简单来说就是当用户提交连表单后,跳转到一个重定向的信息页面,这样就避免用户按F5刷新导致的重复提交,而且也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退导致同样重复提交的问题。 50 | 51 | ### **在session存放特殊标志** 52 | 53 | 在服务端,生成一个唯一的标识符,将它存入session,同时前端获取这个标识符的值将它写入表单的隐藏中,用于用户输入信息后点击一起提交,在服务器端,获取表单中隐藏字段的值,与session中的唯一标识符比较,相等说明是首次提交,就处理本次请求,然后将session中的唯一标识符移除,不相等则表示是重复提交,不再做处理。 54 | 55 | ### **使用唯一索引防止新增脏数据** 56 | 57 | 利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。 58 | 59 | ### **乐观锁** 60 | 61 | 如果更新已有数据,可以进行加锁更新,也可以设计表结构时使用乐观锁,通过version来做乐观锁,这样既能保证执行效率,又能保证幂等, 乐观锁的version版本在更新业务数据要自增 62 | update table set version = version + 1 where id = #{id} and version = #{version} 63 | 示例: 当有重复请求的时候,第一个请求会获取当前商品的version版本号,得到的version为1,紧接着由于第一个请求还没更新商品的version,第二个请求获取的version依然也是1, 这时候第一个请求操作更新的时候带上version并作为条件并且自增更新,这时候商品的version就会变成2,当第二个请求去操作更新的时候明显version不一致导致更新失败。 64 | 65 | ### **select + insert or update or delete** 66 | 67 | 该方案就是操作之前先查询一下,符合要求再插入,该方案在没有并发的系统中可以解决幂等问题,在单JVM有并发的时候可以用JVM加锁来保证幂等性,在分布式环境它是无法保证幂等性,可以使用分布式来保证。 68 | 69 | ### **分布式锁** 70 | 71 | 如果是分布式系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁。要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供)。 72 | 73 | ### **状态机幂等** 74 | 75 | 在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机(状态变更图),就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。注意:订单等单据类业务,存在很长的状态流转,一定要深刻理解状态机,对业务系统设计能力提高有很大帮助 。 76 | 77 | ### **防重表** 78 | 79 | 以支付为例: 使用唯一主键去做防重表的唯一索引,比如使用订单号作为防重表的唯一索引,每一次请求都根据订单号向防重表中插入一条数据,插入成功说明可以处理后面的业务,当处理完业务逻辑之后删除防重表中的订单号数据,后续如果有重复请求,则会因为防重表唯一索引原因导致插入失败,直接返回操作失败,直到第一次请求返回结果,可以看出防重表作用就是加锁的功能。 80 | 注: 最好结合状态机幂等先判断一下 81 | 82 | ### **缓冲队列** 83 | 84 | 将请求都快速地接收下来后放入缓冲队列中,后续使用异步任务处理队列中的数据,过滤掉重复的请求,该解决方案优点是同步处理改成异步处理、高吞吐量,缺点则是不能及时地返回请求结果,需要后续轮询得处理结果。 85 | 86 | ### **全局唯一号** 87 | 88 | 比如通过source来源 + 唯一序列号传入给后端,后端来判断请求是否重复,在并发时只能处理一个请求,其他相同并发请求要么返回请求重复,要么等待前面请求执行完成后再执行。 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/27-mq-usage.md: -------------------------------------------------------------------------------- 1 | ## 消息队列常见的使用场景 2 | 3 | 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题 4 | 5 | 实现高性能,高可用,可伸缩和最终一致性架构。 6 | 7 | 使用较多的消息队列有 RocketMQ,RabbitMQ,Kafka,ZeroMQ,MetaMQ 8 | 9 | 以下介绍消息队列在实际应用中常用的使用场景。 10 | 11 | 异步处理,应用解耦,流量削锋、日志处理和消息通讯五个场景。 12 | 13 | #### 场景 1:异步处理 14 | 15 | 场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式 16 | 17 | (1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端 18 | 19 | ![](http://img.topjavaer.cn/img/mq使用场景1.png) 20 | 21 | (2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间 22 | 23 | ![](http://img.topjavaer.cn/img/mq使用场景2.png) 24 | 25 | 假设三个业务节点每个使用 50 毫秒钟,不考虑网络等其他开销,则串行方式的时间是 150 毫秒,并行的时间可能是 100 毫秒。 26 | 27 | 因为 CPU 在单位时间内处理的请求数是一定的,假设 CPU1 秒内吞吐量是 100 次。则串行方式 1 秒内 CPU 可处理的请求量是 7 次(1000/150)。并行方式处理的请求量是 10 次(1000/100) 28 | 29 | > 小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢? 30 | 31 | 引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下: 32 | 33 | ![](http://img.topjavaer.cn/img/mq使用场景3.png) 34 | 35 | 按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是 50 毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是 50 毫秒。因此架构改变后,系统的吞吐量提高到每秒 20 QPS。比串行提高了 3 倍,比并行提高了两倍 36 | 37 | #### 场景 2:应用解耦 38 | 39 | 场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图 40 | 41 | ![](http://img.topjavaer.cn/img/mq使用场景4.png) 42 | 43 | 传统模式的缺点: 44 | 45 | - 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败 46 | - 订单系统与库存系统耦合 47 | 48 | > 如何解决以上问题呢?引入应用消息队列后的方案,如下图: 49 | 50 | ![](http://img.topjavaer.cn/img/mq使用场景5.png) 51 | 52 | - 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功 53 | - 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作 54 | - 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦 55 | 56 | #### 场景 3:流量削锋 57 | 58 | 流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛 59 | 60 | 应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。 61 | 62 | - 可以控制活动的人数 63 | - 可以缓解短时间内高流量压垮应用 64 | 65 | ![](http://img.topjavaer.cn/img/mq使用场景6.png) 66 | 67 | - 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面 68 | - 秒杀业务根据消息队列中的请求信息,再做后续处理 69 | 70 | #### 场景 4:日志处理 71 | 72 | 日志处理是指将消息队列用在日志处理中,比如 Kafka 的应用,解决大量日志传输的问题。架构简化如下 73 | 74 | ![](http://img.topjavaer.cn/img/mq使用场景7.png) 75 | 76 | - 日志采集客户端,负责日志数据采集,定时写入 Kafka 队列 77 | - Kafka 消息队列,负责日志数据的接收,存储和转发 78 | - 日志处理应用:订阅并消费 kafka 队列中的日志数据 79 | 80 | 以下是新浪 kafka 日志处理应用案例 81 | 82 | ![](http://img.topjavaer.cn/img/mq使用场景8.png) 83 | 84 | (1)、Kafka:接收用户日志的消息队列 85 | 86 | (2)、Logstash:做日志解析,统一成 JSON 输出给 Elasticsearch 87 | 88 | (3)、Elasticsearch:实时日志分析服务的核心技术,一个 schemaless,实时的数据存储服务,通过 index 组织数据,兼具强大的搜索和统计功能 89 | 90 | (4)、Kibana:基于 Elasticsearch 的数据可视化组件,超强的数据可视化能力是众多公司选择 ELK stack 的重要原因 91 | 92 | #### 场景 5:消息通讯 93 | 94 | 消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等 95 | 96 | 点对点通讯: 97 | 98 | ![](http://img.topjavaer.cn/img/mq使用场景9.png) 99 | 100 | 客户端 A 和客户端 B 使用同一队列,进行消息通讯。 101 | 102 | 聊天室通讯: 103 | 104 | ![](http://img.topjavaer.cn/img/mq使用场景10.png) 105 | 106 | 客户端 A,客户端 B,客户端 N 订阅同一主题,进行消息发布和接收。实现类似聊天室效果。 107 | 108 | 以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。 109 | 110 | *本文转载自:https://www.cnblogs.com/ruiati/p/6649868.html* -------------------------------------------------------------------------------- /docs/navigation/projects/1-work-projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 二期 | 优质面试求职开源项目 3 | category: 4 | - 开源项目 5 | - Java项目 6 | - Java经典项目 7 | - Java2Top 8 | - 求职项目 9 | icon: 10 | 11 | --- 12 | 13 | > Java2Top,校招面试求职,大厂学习导航~ 14 | > 15 | > 致力于打造全网最佳大厂学习进阶平台! 16 | 17 | 对于校招求职,关于项目这块是特别重要的,可以说项目间接决定了你的生死。在此前的文章介绍了可以从 Github 等找寻一个适合自己的开源项目,但是网上项目资源满天飞,去找到一款可以让面试官眼前一亮的项目还是有点困难。 18 | 19 | **本站会根据当下面试热点,技术潮流持续收集适合用于《校招面试求职》的精品项目,和人手一个的秒杀,商城,管理系统说拜拜吧,您~。** 20 | 21 | ## 求职面试推荐 22 | 23 | **上一期推荐**:[一期 | 优质面试求职开源项目](./0-work-projects.md) 24 | 25 | ## community(1.3k star) 26 | 27 | 开源论坛、问答系统,现有功能提问、回复、通知、最新、最热、消除零回复功能。功能持续更新中…… 技术栈 Spring、Spring Boot、MyBatis、MySQL/H2、Bootstrap。 28 | 29 | 根据自己的情况结合再结合一些热点技术改造,作为面试项目也是很不错的 30 | 31 | image-20230422223519772 32 | 33 | > 项目地址:[https://github.com/codedrinker/community](https://link.zhihu.com/?target=https%3A//github.com/codedrinker/community) 34 | 35 | ## favorites-web (4.2k star) 36 | 37 | 基于 Spring Boot 2.X 的开源项目。favorites-web(云收藏)是一个使用 Spring Boot 构建的开源网站,可以让用户在线随时随地收藏的一个网站,在网站上分类整理收藏的网站或者文章。 38 | 39 | ![image-20230422223612105](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202304222236213.png) 40 | 41 | > 项目地址:[https://github.com/cloudfavorites/favorites-web](https://link.zhihu.com/?target=https%3A//github.com/cloudfavorites/favorites-web) 42 | 43 | ## SpringBoot-Shiro-Vue(4.2k star) 44 | 45 | 提供一套基于 Spring Boot-Shiro-Vue 的权限管理思路.前后端都加以控制,做到按钮/接口级别的权限 46 | 47 | ![image](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202304222233377.png) 48 | 49 | > 项目地址: [https://github.com/Heeexy/SpringBoot-Shiro-Vue](https://link.zhihu.com/?target=https%3A//github.com/Heeexy/SpringBoot-Shiro-Vue) 50 | 51 | ## Java2Top (推荐🔥) 52 | 53 | 「**Java学习+校招面试求职+大厂学习导航**」一份涵盖 Javacoder 从零基础到进阶大厂的全面学习与面试指南~。 54 | 55 | **本指南名曰**:[《Java2Top》](https://github.com/xlcoding/Java2Top/blob/master/www.java2top.cn),意为 Java To Top 希望大家都能成为 Java 界的佼佼者,毕业能进行业的 Top 企业公司。 56 | 57 | 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差。 58 | 59 | ![image-20230422223130798](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202304222233513.png) 60 | 61 | 你可以把这份指南整体架构想象成一本功法,不同模块根据学习者层次而设定。 62 | 63 | - 「编程资源」属于**功法库**—— 收录涵盖各个经典 开源项目,编程资源,编程工具;`加餐补给以国外优质网站资源为主`。 64 | - 「学习导航」旨在**基础招式**——会带着你`从懵懂无知畅游Java之海而到初有感悟`,有对应书籍、视频、专栏学习指引; 65 | - 「专题进阶」旨在**内功夯实**——会带着你 进一步夯实基础,根据大厂面试常考重点难点设置对应专题,大部分对应着大厂面试题; 66 | - 「面试指南」属于**后期功法也可称秘法**——当你一步一步从头学到这,完毕必定大成,根基稳固;(切记)`若你着急找工作也可借此秘法快速达到大厂面试要求,但是可能实力虚浮`; 67 | - 「校招求职」属于**功法斗技**,建议面试求职前一定认真阅读,对面试求职必定事半功倍。 68 | - 「aurora」属于**万人练级**,后期我会准备一个 「**全民项目**」,从 `提需求->开发->提 PR ->merge` ,把大家召集起来一起开发,项目会`将当下大厂常问的各种热门技术点尽可能融入`,大家互相讨论进步。最后此项目可以 作为软件竞赛,校招求职项目。 69 | 70 | 其中不乏各种精品专栏,大厂高频面试题:**Java基础, 多线程, JVM, 虚拟机, 数据库, MySQL, Spring, Redis, MyBatis, 系统设计, 分布式, RPC, 高可用, 高并发 **等各块知识。 71 | 72 | > 项目地址:https://github.com/xlcoding/Java2Top 73 | 74 | ## SnowJena 75 | 76 | 基于令牌桶算法和漏桶算法实现的分布式无锁限流框架,支持嵌入SpringBoot、SpringCloud应用,支持接口限流、方法限流、系统限流、IP限流、用户限流等规则,支持熔断降级,支持流量塑形,支持可视化QPS监控,支持支持设置系统启动保护时间(保护时间内不允许访问),提供快速失败与匀速器两种限流方案,开箱即用。 77 | 78 | > 项目地址:https://gitee.com/abigdreamer/SnowJena 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/5-jvm-optimize.md: -------------------------------------------------------------------------------- 1 | # 一次简单的 JVM 调优,拿去写到简历里 2 | 3 | 大家好,我是大彬。 4 | 5 | JVM调优一直是面试官很喜欢问的问题。周末在网上看到一篇JVM调优的文章,给大家分享一下。 6 | 7 | > 来源地址:https://zhenbianshu.github.io 8 | 9 | ## **背景** 10 | 11 | 最近对负责的项目进行了一次性能优化,其中包括对 JVM 参数的调整,算是进行了一次简单的 JVM 调优,JVM 参数调整之后,服务的整体性能有 5% 左右的提升,还算不错。 12 | 13 | 先介绍一下项目的基本情况: 14 | 15 | 项目是一个高 QPS 压力的 web 服务,单机 QPS 一直维持在 1.5K 以上,由于旧机器的”拖累”,配置的堆大小是 8G,其中 young 区是 4G,垃圾回收器用的是 parNew + CMS。 16 | 17 | ## **旧状** 18 | 19 | 首先是查看当前 GC 的情况,主要是使用 `jstat` 查看 GC 的概况,再查看 gc log,分析单次 gc 的详细状况。 20 | 21 | 使用 `jstat -gcutil pid 1000` 每隔一秒打印一次 gc 统计信息。 22 | 23 | ![](http://img.topjavaer.cn/img/jvm调优1.png) 24 | 25 | 可以看到,单次 gc 平均耗时是 60ms 左右,还算可以接受,但 YGC 非常频繁,基本上每秒一次,有的时候还会一秒两次,在一秒两次的时候,服务对业务响应时长的压力就会变得很大。 26 | 27 | 接着查看 gc log,打印 gc log 需要在 JVM 启动参数里添加以下参数: 28 | 29 | - `-XX:+PrintGCDateStamps`:打印 gc 发生的时间戳。 30 | - `-XX:+PrintTenuringDistribution`:打印 gc 发生时的分代信息。 31 | - `-XX:+PrintGCApplicationStoppedTime`:打印 gc 停顿时长 32 | - `-XX:+PrintGCApplicationConcurrentTime`:打印 gc 间隔的服务运行时长 33 | - `-XX:+PrintGCDetails`:打印 gc 详情,包括 gc 前/内存等。 34 | - `-Xloggc:../gclogs/gc.log.date`:指定 gc log 的路径 35 | 36 | 看到的 gc log 形如: 37 | 38 | ![](http://img.topjavaer.cn/img/jvm调优2.png) 39 | 40 | 单次 GC 方面并不能直接看出问题,但可以看到 gc 前有很多次 18ms 左右的停顿。 41 | 42 | ## **分析和调整** 43 | 44 | ### YGC 频繁 45 | 46 | 直接查看 gc log 并不直观,我们可以借用一些可视化工具来帮助我们分析, `[gceasy](https://gceasy.io/)` 是个挺不错的网站,我们把 gc log 上传上去后, gceasy 可以帮助我们生成各个维度的图表帮助分析。 47 | 48 | 查看 gceasy 生成的报告,发现我们服务的 gc 吞吐量是 95%,它指的是 JVM 运行业务代码的时长占 JVM 总运行时长的比例,这个比例确实有些低了,运行 100 分钟就有 5 分钟在执行 gc。幸好这些 GC 中绝大多数都是 YGC,单次时长可控且分布平均,这使得我们服务还能平稳运行。 49 | 50 | 解决这个问题要么是减少对象的创建,要么就增大 young 区。前者不是一时半会儿都解决的,需要查找代码里可能有问题的点,分步优化。 51 | 52 | 而后者虽然改一下配置就行,但以我们对 GC 最直观的印象来说,增大 young 区,YGC 的时长也会迅速增大。 53 | 54 | 其实这点不必太过担心,我们知道 YGC 的耗时是由 `GC 标记 + GC 复制` 组成的,相对于 GC 复制,GC 标记是非常快的。而 young 区内大多数对象的生命周期都非常短,如果将 young 区增大一倍,GC 标记的时长会提升一倍,但到 GC 发生时被标记的对象大部分已经死亡, GC 复制的时长肯定不会提升一倍,所以我们可以放心增大 young 区大小。 55 | 56 | 由于低内存旧机器都被换掉了,我把堆大小调整到了 12G,young 区保留为 8G。 57 | 58 | ### 分代调整 59 | 60 | 除了 GC 太频繁之外,GC 后各分代的平均大小也需要调整。 61 | 62 | ![](http://img.topjavaer.cn/img/jvm调优3.png) 63 | 64 | 我们知道 GC 的提升机制,每次 GC 后,JVM 存活代数大于 `MaxTenuringThreshold` 的对象提升到老年代。当然,JVM 还有动态年龄计算的规则:按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了 survivor 区的一半时,取这个年龄和 MaxTenuringThreshold 中更小的一个值,作为新的晋升年龄阈值,但看各代总的内存大小,是达不到 survivor 区的一半的。 65 | 66 | ![](http://img.topjavaer.cn/img/jvm调优4.png) 67 | 68 | 所以这十五个分代内的对象会一直在两个 survivor 区之间来回复制,再观察各分代的平均大小,可以看到,四代以上的对象已经有一半都会保留到老年区了,所以可以将这些对象直接提升到老年代,以减少对象在两个 survivor 区之间复制的性能开销。 69 | 70 | 所以我把 MaxTenuringThreshold 的值调整为 4,将存活超过四代的对象直接提升到老年代。 71 | 72 | ### 偏向锁停顿 73 | 74 | 还有一个问题是 gc log 里有很多 18ms 左右的停顿,有时候连续有十多条,虽然每次停顿时长不长,但连续多次累积的时间也非常可观。 75 | 76 | 是因为 1.8 之后 JVM 对锁进行了优化,添加了偏向锁的概念,避免了很多不必要的加锁操作,但偏向锁一旦遇到锁竞争,取消锁需要进入 `safe point`,导致 STW。 77 | 78 | 解决方式很简单,JVM 启动参数里添加 `-XX:-UseBiasedLocking` 即可。 79 | 80 | ## **结果** 81 | 82 | 调整完 JVM 参数后先是对服务进行压测,发现性能确实有提升,也没有发生严重的 GC 问题,之后再把调整好的配置放到线上机器进行灰度,同时收集 gc log,再次进行分析。 83 | 84 | 由于 young 区大小翻倍了,所以 YGC 的频率减半了,GC 的吞量提升到了 97.75%。平均 GC 时长略有上升,从 60ms 左右提升到了 66ms,还是挺符合预期的。 85 | 86 | 由于 CMS 在进行 GC 时也会清理 young 区,CMS 的时长也受到了影响,CMS 的最终标记和并发清理阶段耗时增加了,也比较正常。 87 | 88 | 另外我还统计了对业务的影响,之前因为 GC 导致超时的请求大大减少了。 89 | 90 | ## **小结** 91 | 92 | 总之,这是一次挺成功的 GC 调整,让我对 GC 有了更深的理解,但由于没有深入到 old 区,之前学习到的 CMS 相关的知识还没有复习到。 93 | 94 | 不过性能优化并不是一朝一夕的事,需要时刻关注问题,及时做出调整。 -------------------------------------------------------------------------------- /docs/guide/interview/six-method.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 校招求职六大策略📚 3 | category: 校招求职 4 | tag: 5 | - 求职策略 6 | head: 7 | - - meta 8 | - name: keywords 9 | content: 校招,求职,策略 10 | - name: description 11 | content: 校招求职,抱团取暖策略、简历制作策略、选投策略、面前复习策略、面中应对策略、面后复盘策略 12 | --- 13 | 前面和大家分享了大厂面试硬核实力需要学习注意的地方,但是你单单技术能力强可能运气不好还是会错过很多机会 14 | 15 | 根据我和身边上岸大厂的朋友的一致经验来看。对于找工作,**过硬的专业技能我认为其实只占 20%**。 16 | 17 | 因为,如果`你连面试的机会都没有`又何谈向面试官充分展现自己的才华;假如你有面试机会,专业技能又十分强,但是你`不会沟通表达`,面对面试官不能很好的表达自己的想法。 18 | 19 | 那么即使你本身很优秀,那也没办法,面试官并不知道,短短几十分钟get到的东西很少,若你不能耍点小心机,可能就被能力还不如你的人刷下去,就是那么现实。 20 | 21 | 我将整个找工作过程的技巧分为如下一套组合: 22 | 23 | 六大策略思想:【小龙出版-(●'◡'●)】 24 | 25 | `抱团取暖策略+简历制作策略+选投策略+面前复习策略+面中应对策略+面后复盘策略` 26 | 27 | ## 一、抱团取暖 28 | 29 | ### **打开大门,了解更多消息** 30 | 31 | 由于学历差(专科、专升本、二本等),我们会处于一个消息闭锁的状态,就是笔者个人感觉学的东西和985 211名校同学其实差不了多少,大多同学都靠自学甚至可能比985 211部分同学学的还好一点。 32 | 33 | 但是名校的各种途径的消息却是十分及时与开放的,因为各个老师教授可能都是业界的大佬,十分了解行情;也会有很多大公司去他们学校开讲座办宣讲会,它会及时分享一些当下信息给你 34 | 35 | 简单一点说,也许你在读我这篇文章时还不知道秋招与春招,不知道学历差需要提前实习去弥补缺陷,可以利用牛客与力扣这些求职平台获取更多最新消息。 36 | 37 | 你可能还在处于一个“乡下”状态,而985 211同学可能大一大二就开始出去大厂实习,那真正秋招时,你拿什么和别人比呢?你可能在学基础,别人开始学框架;你还在学框架,别人算法已经练的 "飞起来" ,而你还不知道大厂必考算法。 38 | 39 | ### **互相鼓励,互相陪伴** 40 | 41 | 这又怎么说呢?就拿我们学校来说,大三秋招找工作时,身边的同学几乎都没有意识,整个专业找工作的不超过5个,当然,别人可能在考研什么的。 42 | 43 | 但这不是重点,重点是**你没有并肩作战的伙伴**,即使有几个,而你们都是一个学校的,可能得到的消息与大致眼界基本在一个维度。 44 | 45 | 这时,假如能结识和你学历在一个等次或者差一点的不同学校的大佬,你不仅有并肩作伴的伙伴,而且大家互相分享消息,学习讨论进步,分享秋招进度,分享遇到的挫折,这会让你心里有个底,也会有个精神支撑。 46 | 47 | 因为对于学历差的同学想进大厂,**最大的困难不是面试难度高,而是连面试机会都没有就直接简历挂**。自己能力不足面试挂了,我可以努力,就怕我即使再怎么努力,却连面试机会也没有。 48 | 49 | 试想你投了很多简历都没消息,你一个人作战可能你就彷徨了,怀疑了,想放弃了。而此时,你得知有一个和你学历一样甚至比你差的朋友已经拿到阿里offer,此时,你是不是会有一种打鸡血的感觉。知道即使学历差,通过自己努力也可以成功,而不是即使自己再怎么努力也没用。 50 | 51 | 而且**你可以去请教在这条路取得成功的同学,看它的方法**,然后知道自己哪里不足再去改,因为你们在一个维度(同学历)而不是自己瞎琢磨,然后自暴自弃,最后不了了之。因为你身边的同学都叫你放弃,去干其他的。 52 | 53 | **回过来说说我的经历:** 54 | 55 | 上述全是我的亲身感受,实习投了无数简历,没消息,当时差点自闭了。后来群里认识了一个朋友"招手",他是三本学历,也在和我一样努力的找工作,于是我们惺惺相惜互加了好友。 56 | 57 | 然后感谢这位兄弟把我拉进了一个群,这个群里基本都是二本,专升本的大佬们,大家一起抱团取暖。 58 | 59 | 当我进群以后,有些群友约了阿里二面,有些拿了一些公司的offer,有些面了字节,当我了解这些以后,我就到处去请教经验,顿时迷茫的我点燃了希望的火苗,瞬间充满动力,知道,学历差,努力也会有结果。就这样开启了我的春招与秋招之路,之后发生的就是我 [我的秋招 | 大厂上岸经验一(末流二本)](https://mp.weixin.qq.com/s?__biz=MzkxMjE5NzUxNQ==&mid=2247483736&idx=1&sn=e8839e0bdb0b8997f4656b88aad0ce69&chksm=c111d3bcf6665aaa2448dba17acad5abc89820141eff1d6539a9f2a9b93e09e8bfe83022e5c5&token=715955624&lang=zh_CN&scene=21#wechat_redirect)所讲的啦。 60 | 61 | 最后,秋招正式批开始,我们群几乎人手大厂,人均3个offer,BAT TMD 占满,甚至还有wxg sp、阿里 sp等。 62 | 63 | 如果大家没合适的抱团群,也可以加入我的技术交流群,关注公众号加群即可。后期会带领大家一起冲刺大厂。给大家提供一个抱团平台。 64 | 65 | ## 二、简历制作策略 66 | 67 | 前文专门讲过,这里不再赘述 68 | 69 | [求职 | 学弟用了我的简历给我说去腾讯啦👍](www.java2top.c/guide/interview/recruit.html) 70 | 71 | ## 三、选投策略 72 | 73 | 关于投递策略也很讲究的。投早感觉自己没准备好,投晚了没hc了。 74 | 75 | 我的建议是,边投边复习,以面代练习。不过之前投的尽量投中小厂,开始第一家可以找一些小公司练练手,找找感觉,后面你面多了,你会发现,面试都背成固定模板了,越来越得心应手。 76 | 77 | 此时,你可以投想去的中大厂了,关于大厂,建议在第二批面试/笔试开始前几天投,因为投递早了,面试官可能持保留意见,想等等看,投晚了,基本hc无了,难度增加不说,可能还连面试机会都无。 78 | 79 | ## 四、面前复习策略 80 | 81 | 临近面试几天不必学习太多新知识了,把自己之前做的笔记拿出来背熟悉,一定得保证你的专长领域发挥好,就比如高中考试一般,薄弱学科尽量答好,强势学科争取高分。 82 | 83 | 若是中大厂,基本都会考算法,建议多抽时间出来背背该公司常考的算法,或者去牛客看看高频题。 84 | 85 | 推荐网站:https://codetop.cc/home 86 | 87 | https://www.nowcoder.com/activity/oj 88 | 89 | ## 五、面中应对策略 90 | 91 | 面试时的状态与技巧十分看重,也许你是个特牛逼的人面出来效果还不如能力差一点的人。 92 | 93 | 需要注意这些方面: 94 | 95 | 1、**精神面貌**:面试切记萎靡不振的样子,想睡觉就去洗个冷水脸,面试都是视频面对面与面试官交流,要就要好的第一映像。 96 | 97 | 开局的自我介绍极为重要,不是说你介绍得多棒,是要在短短1-2分钟内,给面试管感觉你这小伙子挺机灵,挺朝气的,还不错的感觉。 98 | 99 | 抬头挺胸,在自我介绍时可以适当伴随演讲手势,说话抑扬顿挫,信心满满,精神满满。 100 | 101 | 2、**引导与装逼**:正式进入问答时,你回答问题要尽量往你擅长的方向带,回答他这个问题时故意暴露一些关键信息,比如叫你介绍ACID、事务,你别只介绍ACID概念,你可以说完概念假装暴露他们的实现原理日志,MVCC、锁相关的内容,因为这方面是你的拿手好戏。 102 | 103 | 誒,等你说完,面试官果然问具体实现,然后你又可以装一波逼了,时间也拖过去了,又在你拿手领域回答得很出彩。不过具体怎样把握与引导得看你的面试经验了,所以说要多面。 104 | 105 | 问你一些解决方案时,不要装逼,直接把最优方案说出来,你得先说劣质方案,然后你可以说这个方案存在哪些问题,或者等他问你怎样优化,然后再顺理成章引出最优方案。 106 | 107 | 介绍项目时,对于项目的难点,最好准备两套方案,等他问”你觉得还可以怎样优化时?“,你又可以装逼了!即便这两套方案并没实际优化多少,但是不同实现他就觉得可以了,因为短短几分钟他也不清楚你项目具体咋实现的,除非牛逼得不得了的面试官。 108 | 109 | **总结,提前为可能问的问题做好准备,学会引导,学会方案备选,学会带面试官装逼((●'◡'●))。** 110 | 111 | ## 六、面后复盘策略 112 | 113 | 面试就像考试一样,要学会错题整理。面完之后,要学会及时复盘,把面试过程中不会的,不熟悉忘了的,记录下来,过后查阅资料解决。 114 | 115 | 之后面试前拿出来复习,由此形成良好循环,你会发现面试基本就那几个问题,都背烂了,教科书式回答。 116 | 117 | 今天就差不多到这里啦,我的秋招经验心得基本形成文字分享给大家啦,希望大家看了有一定的收获,结合自己的学习方式形成自己的一套,祝大家早日斩获心仪offer! -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: Java2Top(Java学习&&面试指南) 4 | --- 5 | 6 | ::: info 7 | 8 | Java系列「`校招面试求职,大厂学习导航`」认准「**Java2Top**」认准「**小龙coding**」 9 | 10 | 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差 11 | 12 | ::: 13 | 14 |
15 |

16 | 阅读 17 |

18 |

19 | Github | 20 | Gitee 21 |

22 |
23 | 24 | 推荐你通过在线阅读网站进行阅读,体验更好,速度更快! 25 | 26 | * [Java2Top 在线阅读网站(推荐👍)](http://java2top.cn/) 27 | * [Java2Top 在线阅读版(备用)](https://xlcoding.github.io/Java2Top/) 28 | 29 | 学前须知: 30 | 31 | * [项目介绍](../java2top/intro.md) 32 | * [网站规划](../java2top/todo.md) 33 | 34 | ## 面前准备 35 | 36 | [面试官眼前一亮的项目怎样写?](./interview/project.md) 37 | 38 | [求职 | 学弟用了我的简历,给我说去腾讯了!👍](./interview/recruit.md) 39 | 40 | [校招求职六大策略📚](./interview/six-method.md) 41 | 42 | ## BAT高频面题 43 | 44 | ### Java基础 45 | 46 | **知识点/面试题总结** : (必看:+1: ): 47 | 48 | [《BAT高频面点》Java基础高频面试题合集(精选)](./java/basic/java-basic.md) 49 | 50 | ### JDK集合 51 | 52 | **知识点/面试题总结** : (必看:+1: ): 53 | 54 | [《BAT高频面点》JDK集合框架面试题精选](./java/collection/java-collection.md) 55 | 56 | **源码分析** 57 | 58 | * [ArrayList 源码+扩容机制分析](docs/java/collection/arraylist-source-code.md) 59 | * [HashMap(JDK1.8)源码+底层数据结构分析](docs/java/collection/hashmap-source-code.md) 60 | * [ConcurrentHashMap 源码+底层数据结构分析](docs/java/collection/concurrent-hash-map-source-code.md) 61 | 62 | [源码分析(推荐👍)](./java/collection/java-collection-code.md) 63 | 64 | ### 并发编程 65 | 66 | [《BAT高频面点》(Java并发编程面试题八股文)必看](./java/concurrent/java-concurrent.md) 67 | 68 | ### JVM 69 | 70 | [《BAT高频面点》JVM高频面试题八股文🔥](./java/jvm/java-jvm.md) 71 | 72 | ### 计算机基础 73 | 74 | [《BAT高频面点》计算机网络高频面试题合集(必看👍)](./java/computer-basic/network.md) 75 | 76 | [《BAT高频面点》操作系统高频面试题合集(必看👍)](./java/computer-basic/operate-system.md) 77 | 78 | ### MySQL 79 | 80 | [MySQL-架构篇(hot🔥)](./java/database/mysql/java-mysql-0-structure.md) 81 | 82 | MySQL-索引篇(hot🔥) 83 | 84 | MySQL-事务篇(hot🔥) 85 | 86 | MySQL-锁机制篇(hot🔥) 87 | 88 | MySQL-架构日志篇(hot🔥) 89 | 90 | MySQL-高可用篇(hot🔥) 91 | 92 | MySQL-调优篇(hot🔥) 93 | 94 | ### 分布式 95 | 96 | ## 海量数据统计(更新中 ing 🔥) 97 | 98 | [大数据中 TopK 问题的常用套路🎈](./mass-data/0-topk-template.md) 99 | 100 | [1.统计不同号码的个数](./mass-data/1-count-phone-num.md) 101 | 102 | [2.出现频率最高的100个词](./mass-data/2-find-hign-frequency-word.md) 103 | 104 | [3.查找两个大文件共同的URL](./mass-data/3-find-same-url.md) 105 | 106 | [4.如何从大量的 URL 中找出相同的 URL?](./mass-data/4-find-mid-num.md) 107 | 108 | [5.如何查询最热门的查询串?](./mass-data/5-find-hot-string.md) 109 | 110 | [6.如何找出排名前 500 的数?](./mass-data/6-top-500-num.md) 111 | 112 | [7.如何按照 query 的频度排序?](./mass-data/7-query-frequency-sort.md) 113 | 114 | [8.如何从大量数据中找出高频词?](./mass-data/8-high-frequency.md) 115 | 116 | [9.几亿个数的大文件怎么排序?](./mass-data/9-sort-500-million-large-files.md) 117 | 118 | ## 系统设计 119 | 120 | [0.开篇-如何设计大型系统?]() 121 | 122 | [1.扫码登录的原理?]() 123 | 124 | [2.订单30分钟未支付自动取消怎么实现?]() 125 | 126 | [3.如何把——个文件较快的发送到 100 服务器?]() 127 | 128 | [4.如何设计一个短链系统?]() 129 | 130 | [5.如何解决超卖问题?]() 131 | 132 | [6.如何设计一个秒杀系统?]() 133 | 134 | [7.微信红包后台系统设计详解?]() 135 | 136 | [8.单点登录(SSO)的设计与实现?]() 137 | 138 | [9.如何设计一个优惠券系统?]() 139 | 140 | [10.如何用Redis 统计用户访问量?]() 141 | 142 | [11.实时订阅推送设计与实现]() 143 | 144 | [12.如何设计一个抢红包系统如何设计一个消息队列?购物车系统怎么设计?]() 145 | 146 | ## 面试实录 (必看 :+1:) 147 | 148 | 根据秋招春招上岸大厂面试经历以及身边朋友**上岸面试录音模拟面试现场**,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 149 | 150 | 专栏系列目前已经连载 **N** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 151 | 152 | - [《面试实录》百度二面,Sychronized原理详解](.//memoir/1.md) 153 | - [《面试实录》字节二面,SQL执行慢的原因?如何优化?](.//memoir/2.md) 154 | - [《面试实录》 字节二面, MySQL自增主键一定连续吗?](.//memoir/3.md) 155 | - [《面试实录》 阿里一面,深剖Map底层结构与原理](.//memoir/4.md) 156 | - [《面试实录》携程二面,JVM 底层剖析](./memoir/5.md) 157 | 158 | ## 👨‍💻关于作者 159 | 160 | 小龙二本出身,一路自学 Java,校招斩获腾讯、美团、网易SP、ViVo提前批、讯飞多个大厂offer。作为一名后端 coder,深感一路的不易。**希望我的分享可以帮助更多的小伙伴,我踩过的坑你们不要再踩**! 161 | 162 | > 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差。 163 | 164 | ## 公众号 165 | 166 | ![我的公众号](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303202215699.png) 167 | 168 | 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。【**小龙coding**】 169 | 170 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/2-spring-transaction.md: -------------------------------------------------------------------------------- 1 | # @Transactional 事务注解详解 2 | 3 | ## Spring事务的传播行为 4 | 5 | **先简单介绍一下Spring事务的传播行为:** 6 | 7 | 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在`TransactionDefinition`定义中括了如下几个表示传播行为的常量: 8 | 9 | - `TransactionDefinition.PROPAGATION_REQUIRED`:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 10 | - `TransactionDefinition.PROPAGATION_REQUIRES_NEW`:创建一个新的事务,如果当前存在事务,则把当前事务挂起。 11 | - `TransactionDefinition.PROPAGATION_SUPPORTS`:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 12 | - `TransactionDefinition.PROPAGATION_NOT_SUPPORTED`:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 13 | - `TransactionDefinition.PROPAGATION_NEVER`:以非事务方式运行,如果当前存在事务,则抛出异常。 14 | - `TransactionDefinition.PROPAGATION_MANDATORY`:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 15 | - `TransactionDefinition.PROPAGATION_NESTED`:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于`TransactionDefinition.PROPAGATION_REQUIRED`。‍ 16 | 17 | ## Spring事务的回滚机制 18 | 19 | **然后说一下Spring事务的回滚机制:** 20 | 21 | Spring的AOP即声明式事务管理默认是针对`unchecked exception`回滚。Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行`commit or rollback`(Spring默认取决于是否抛出`runtimeException`)。 22 | 23 | 如果你在方法中有`try{}catch(Exception e){}`处理,那么try里面的代码块就脱离了事务的管理,若要事务生效需要在catch中`throw new RuntimeException ("xxxxxx");`这一点也是面试中会问到的事务失效的场景。 24 | 25 | ## @Transactional注解实现原理 26 | 27 | 再简单介绍一下`@Transactional`注解底层实现方式吧,毫无疑问,是通过动态代理,那么动态代理又分为JDK自身和CGLIB,这个也不多赘述了,毕竟今天的主题是如何将`@Transactional`对于事物的控制应用到炉火纯青。哈哈~ 28 | 29 | 第一点要注意的就是在`@Transactional`注解的方法中,再调用本类中的其他方法method2时,那么method2方法上的`@Transactional`注解是不!会!生!效!的!但是加上也并不会报错,拿图片简单帮助理解一下吧。这一点也是面试中会问到的事务失效的场景。 30 | 31 | ![](http://img.topjavaer.cn/img/spring事务1.png) 32 | 33 | 通过代理对象在目标对象前后进行方法增强,也就是事务的开启提交和回滚。那么继续调用本类中其他方法是怎样呢,如下图: 34 | 35 | ![](http://img.topjavaer.cn/img/spring事务2.png) 36 | 37 | 可见目标对象内部的自我调用,也就是通过this.指向的目标对象将不会执行方法的增强。 38 | 39 | 先说第二点需要注意的地方,等下说如何解决上面第一点的问题。第二点就是`@Transactional`注解的方法必须是公共方法,就是必须是public修饰符!!! 40 | 41 | 至于这个的原因,发表下个人的理解吧,因为JVM的动态代理是基于接口实现的,通过代理类将目标方法进行增强,想一下也是啦,没有权限访问那么你让我怎么进行,,,好吧,这个我也没有深入研究底层,个人理解个人理解。 42 | 43 | 在这里我也放个问题吧,希望有高手可以回复指点指点我,因为JVM动态代理是基于接口实现的,那么是不是service层都要按照接口和实现类的开发模式,注解才会生效呢,就是说`controller`层直接调用没有接口的service层,加了注解也一样不起作用吧,这个懒了,没有测试,其一是因为没有人会这么开发吧,其二是我就认为是不起作用的,哈哈。 44 | 45 | **下面来解决一下第一点的问题,如何在方法中调用本类中其他方法呢。** 46 | 47 | 通过`AopContext.currentProxy ()`获取到本类的代理对象,再去调用就好啦。因为这个是CGLIB实现,所以要开启AOP,当然也很简单,在springboot启动类上加上注解`@EnableAspectJAutoProxy(exposeProxy = true)`就可以啦,这个依赖大家自行搜一下就好啦。要注意,注意,代理对象调用的方法也要是public修饰符,否则方法中获取不到注入的bean,会报空指针错误。 48 | 49 | emmmm,我先把调用的方式和结果说下吧。自己简单写了代码,有点粗糙,就不要介意啦,嘿嘿。。。 50 | 51 | Controller中调用Service 52 | 53 | ``` 54 | @RestController 55 | public class TransactionalController { 56 | 57 | @Autowired 58 | private TransactionalService transactionalService; 59 | 60 | @PostMapping("transactionalTest") 61 | public void transacionalTest(){ 62 | transactionalService.transactionalMethod(); 63 | } 64 | } 65 | ``` 66 | 67 | Service中实现对事务的控制:接口 68 | 69 | ``` 70 | public interface TransactionalService { 71 | void transactionalMethod(); 72 | } 73 | ``` 74 | 75 | Service中实现对事务的控制:实现类(各种情况的说明都写在图片里了,这样方便阅读,有助于快速理解吧) 76 | 77 | ![](http://img.topjavaer.cn/img/spring事务3.png) 78 | 79 | ![](http://img.topjavaer.cn/img/spring事务4.png) 80 | 81 | 上面两种情况不管使不使用代理调用方法1和方法2,方法`transactionalMethod`都处在一个事务中,四条更新操作全部失败。 82 | 83 | 那么有人可能会有疑问了,在方法1和方法2上都加`@Transactional`注解呢?答案是结果和上面是一致的。 84 | 85 | 小结只要方法`transactionalMethod`上有注解,并且方法1和方法2都处于当前事务中(不使用代理调用,方法1和方法2上的`@Transactional`注解是不生效的;使用代理,需要方法1和方法2都处在`transactionalMethod`方法的事务中,默认或者嵌套事务均可,当然也可以不加`@Transactional`注解),那么整体保持事务一致性。 86 | 87 | 如果想要方法1和方法2均单独保持事务一致性怎么办呢,刚说过了,如果不是用代理调用`@Transactional`注解是不生效的,所以一定要使用代理调用实现,然后让方法1和方法2分别单独开启新的事务,便OK啦。下面摆上图片。 88 | 89 | ![](http://img.topjavaer.cn/img/spring事务5.png) 90 | 91 | ![](http://img.topjavaer.cn/img/spring事务6.png) 92 | 93 | 这两种情况都是方法1和方法2均处在单独的事务中,各自保持事务的一致性。 94 | 95 | 接下来进行进一步的优化,可以在`transactionalMethod`方法中分别对方法1和方法2进行控制。要将代码的艺术发挥到极致嘛,下面装逼开始。 96 | 97 | ![](http://img.topjavaer.cn/img/spring事务7.png) 98 | 99 | 代码太长了,超过屏幕了,粘贴出来截的图,红框注释需要仔细看,希望不要影响你的阅读体验,至此,本篇关于`@Transactioinal`注解的使用就到此为止啦, 100 | 101 | 简单总结一下吧: 102 | 103 | 1、就是`@Transactional`注解保证的是每个方法处在一个事务,如果有try一定在catch中抛出运行时异常。 104 | 105 | 2、方法必须是public修饰符。否则注解不会生效,但是加了注解也没啥毛病,不会报错,只是没卵用而已。 106 | 107 | 3、this.本方法的调用,被调用方法上注解是不生效的,因为无法再次进行切面增强。 108 | 109 | 110 | 111 | > 原文:blog.csdn.net/fanxb92/article/details/81296005 -------------------------------------------------------------------------------- /docs/guide/memoir/2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《面试实录》 字节二面,SQL执行慢的原因?如何优化? 3 | category: 4 | - 对线面试官 5 | - 面试实录 6 | - Java 7 | - MySQL 8 | tag: 9 | - MySQL 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: 面试实录,MySQL,JAVA,大厂面试,慢SQL优化 14 | - name: description 15 | content: 字节二面,SQL执行慢的原因?如何优化?涉及知识点:MySQL慢 SQL 查询排查、SQL优化相关 16 | --- 17 | 18 | 本期是【**面试实录**】系列文章的第 **2** 期,持续更新中.....。 19 | 20 | - 欢迎关注+订阅,持续更新中!!!致力打造校招核心面试攻略~ 21 | - 根据秋招春招上岸大厂面试经历以及身边朋友上岸面试录音模拟面试现场,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 22 | 23 | 【**面试实录**】专栏系列目前已经连载 **2** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 24 | 25 | [【BAT高频面点】百度二面,Sychronized原理详解](./1) 26 | 27 | # 考题速查 28 | 29 | 本期会通过面试模拟探讨MySQL高级,`一条SQL执行慢的原因,并给予解决方案` 30 | 31 | `本期题改编自 ——202届秋招 字节 二面` 32 | 33 | # 面试现场 34 | 35 | 叮叮叮...... 36 | 37 | **面试官**:“你好,我是XX面试官,请问是小龙吗?” 38 | 39 | **小龙**:“您好,面试官,我是小龙” 40 | 41 | **面试官**:“好的,现在有空吗,我们开始面试吧” 42 | 43 | **小龙**:“嗯嗯,准备好啦” 44 | 45 | ....... 46 | 47 | other questions 48 | 49 | ....... 50 | 51 | **面试官**:“我看你简历写熟悉 `MySQ`L 使用,还有相关调优经验,是吗?” 52 | 53 | **小龙**:“是的呢!在实习时和主管一起解决过相关问题。” 54 | 55 | **面试官**:“好的,那你们项目中假如遇到慢 SQL 查询,你们是怎样排查和解决的呢?” 56 | 57 | **独白**:“送分题来了” 58 | 59 | **小龙**:”总的来说,我们应该从`索引、架构、网络、I/O吞吐量、内存、锁、SQL语句`等各个方面去分析,但是由于涉及范围太广,如果不能理清思路去逐步分析,便会使得排查效率极低。“ 60 | 61 | **面试官**:”噢,怎样做呢?“ 62 | 63 | **小龙**:”为了快速定位,针对这个问题,我们得对系统有个全局监控,然后分情况讨论啦。“ 64 | 65 | **小龙**:”如果`大多数情况下都正常,偶尔很慢`,则可能是:数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘;或者执行的时候,**遇到锁**,如表锁、行锁;又或许你这次执行的 SQL 写的有问题,由于真实业务数据量又大,就导致速度极慢。“ 66 | 67 | **小龙**:”当然这是我们主要考虑的原因,另外,也有可能是当时网络不好,内存不足,I/O吞吐量小,形成了瓶颈效应,不过一般公司不会出现这种情况,用的设施都很好的。“ 68 | 69 | **面试官**:”嗯嗯,接着说。“ 70 | 71 | **小龙**:”还有就是假如`这条 SQL 语句一直执行的很慢`。可能是没有用上索引或则索引失效:**例如该字段没有索引;或则由于对字段进行运算、函数操作导致无法用索引**。“ 72 | 73 | **小龙**:”然后这时候我们可以有个大概方向,然后我们还得结合具体手段去定位慢查询 SQL。“ 74 | 75 | **面试官**:”说说看哈~“ 76 | 77 | **小龙**:”首先数据库中设置 SQL 慢查询,我们可以修改配置文件,在 my.ini 增加几行: 主要是慢查询的定义时间(超过2秒就是慢查询),以及慢查询log日志记录( slow_query_log)或者直接通过命令行,通过MySQL数据库开启慢查询。“ 78 | 79 | ```sql 80 | [mysqlld] 81 | //定义查过多少秒的查询算是慢查询,我这里定义的是2秒 82 | long_query_time=2 83 | #5.8、5.1等版本配置如下选项 84 | log-slow-queries="mysql_slow_query.log" 85 | #5.5及以上版本配置如下选项 86 | slow-query-log=On 87 | slow_query_log_file="mysql_slow_query. log" 88 | 89 | //记录下没有使用索引的query 90 | log-query-not-using-indexestpspb16glos dndnorte/t 91 | 92 | 93 | mysql>set global slow_query_log=ON 94 | mysql>set global long_query_time = 3600; 95 | mysql>set global log_querise_not_using_indexes=ON; 96 | ``` 97 | 98 | **小龙**:”然后当出现慢查询时,我们可以去分析**慢查询日志**。我们可以使用 `show processlist` 命令定位低效率执行 SQL,也可以用 `explain` 分析 SQL 的**执行计划**。“ 99 | 100 | **面试官**:”那你使用 `explain`字段,一般会关注哪些字段呢? “ 101 | 102 | **小龙**:”其实使用这个我们主要是看有没有使用到索引,索引失效,访问类型等问题。因此,我们大多情况下都是看 **possible_keys**、**key**、**key_len**(这三个一般套起来分析),还有就是**Extra**、**type**(看全表扫描还是索引、还是索引范围扫描)等等。“ 103 | 104 | **小龙**:“**possible_keys**: 表示查询可能使用的索引,**key**: 实际使用的索引,**key_len**: 使用索引字段的长度,结合起来可以看出索引使用情况。” 105 | 106 | **小龙**:“**Extra**(using index:覆盖索引,不回表,尽量覆盖,可以提高效率;**using filesort**:需要额外的排序,不能通过索引得到排序结果,尽量避免这种情况,会使得速度很慢)” 107 | 108 | **面试官**:”好的,那么你们是怎样尝试去优化的呢?“ 109 | 110 | **小龙**:”对于 MYSQL 慢 SQL 语句的优化,我们也可以分几个方面来进行分析(基本覆盖全面啦):可以从这几方面考虑:`索引+SQL 语句+数据库结构优化+优化器优化+架构优化+I/O+内存+网络。`“ 111 | 112 | **小龙**:”1、`对于索引`。我们需要从建立索引就开始考虑,索引一般建在 where 和 order by,数据基数要大,区分度要高,不要过度索引,在提高速度同时节约内存。“ 113 | 114 | **小龙**:”避免索引失效,然后可以尽量覆盖索引,5.6支持索引下推可以使得速度更快;“ 115 | 116 | **小龙**:“在写多读少的场景下,可以选择`普通索引`而不要`唯一索引`。因为更新时,**普通索引可以使用change buffer进行优化,减少磁盘IO,将更新操作记录到change bufer**,等查询来了将数据读到内存再进行修改。” 117 | 118 | **小龙**:”2、`对于 SQL 语句`。我们有很多优化手段,随便举几个,比如分页查询优化,该方案适用于主键自增的表,可以把 Limit 查询转换成某个位置的查询。select * from tb_sku where id>20000 limit 10;” 119 | 120 | **小龙**:“Insert 插入语句时,多条插入语句写成一条,同时可以利用主键索引特性让数据有序插入而使效率更高。” 121 | 122 | **小龙**:“当然还有很多关于 SQL 写法的优化,这里略提。比如还有,注意 union 和 union all 的区别,union all好;注意使用DISTINCT,在没有必要时不要用,它同 union 一样会使查询变慢,注意临时表、视图等等。” 123 | 124 | **面试官**:“好的呢,还有吗?” 125 | 126 | **小龙**:“3、对于`数据库结构`。我们可以考虑将字段多的表分解成多个表。有些字段使用频率高,有些低,数据量大时,会由于使用频率低的存在而变慢,可以考虑分开;而对于经常联合查询的表,可以考虑建立中间表。” 127 | 128 | **小龙**:“4、对于`架构`,在真实业务场景中,数据量大,并发压力大,我们可以考虑分库分表,纵向、横向分割表,减少表的尺寸,还有采用读/写分离(主库写,从库读)集群模式。” 129 | 130 | **小龙**:“当然采用集群,无疑要增加成本,分库分表又要考虑分布式事务、分布式Id、一致性等等问题,因此有好也有坏,当你采用某种措施之前也得考虑其性价比,最终带来的好处更多还是坏处更多。” 131 | 132 | **面试官**:“考虑的很全面,好的,还有补充吗?” 133 | 134 | **小龙**:“除了这些,我们有时也会考虑,把数据、日志、索引放到不同的 `I/O` 设备上,增加读取速度,`升级硬件`,`提高网速`等等。不过也不能一味的去追求速度,因**为也得考虑成本,所以具体问题具体分析吧**!” 135 | 136 | **面试官**:“好的,虽然还有很多细节问题,但是整体思路还行,可见有去思考过。继续加油~” 137 | 138 | # 知识总结 139 | 140 | 本期我们通过面试模拟深入探讨了`慢SQL优化` 的套路,下期再见。**订阅+关注** 持续追更。 141 | 142 | ## **面试重点** 143 | 144 | `MySQL慢 SQL 查询排查、SQL优化相关` -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/16-what-is-jwt.md: -------------------------------------------------------------------------------- 1 | # 什么是JWT 2 | 3 | JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。 4 | 5 | ## 传统的session认证 6 | 7 | http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。 8 | 9 | 这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来. 10 | 11 | #### 基于session认证所显露的问题 12 | 13 | **Session**: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。 14 | 15 | **扩展性**: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。 16 | 17 | **CSRF**: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。 18 | 19 | ## 基于token的鉴权机制 20 | 21 | 基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。 22 | 23 | 流程上是这样的: 24 | 25 | - 用户使用用户名密码来请求服务器 26 | - 服务器进行验证用户的信息 27 | - 服务器通过验证发送给用户一个token 28 | - 客户端存储token,并在每次请求时附送上这个token值 29 | - 服务端验证token值,并返回数据 30 | 31 | 这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持`CORS(跨来源资源共享)`策略,一般我们在服务端这么做就可以了`Access-Control-Allow-Origin: *`。 32 | 33 | ## JWT长什么样? 34 | 35 | JWT是由三段信息构成的,将这三段信息文本用`.`链接一起就构成了Jwt字符串。就像这样: 36 | 37 | ```css 38 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW 39 | ``` 40 | 41 | ## JWT的构成 42 | 43 | 第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature). 44 | 45 | ### header 46 | 47 | jwt的头部承载两部分信息: 48 | 49 | - 声明类型,这里是jwt 50 | - 声明加密的算法 通常直接使用 HMAC SHA256 51 | 52 | 完整的头部就像下面这样的JSON: 53 | 54 | ```bash 55 | { 56 | 'typ': 'JWT', 57 | 'alg': 'HS256' 58 | } 59 | ``` 60 | 61 | 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分. 62 | 63 | ```undefined 64 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 65 | ``` 66 | 67 | ### playload 68 | 69 | 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分 70 | 71 | - 标准中注册的声明 72 | - 公共的声明 73 | - 私有的声明 74 | 75 | **标准中注册的声明** (建议但不强制使用) : 76 | 77 | - **iss**: jwt签发者 78 | - **sub**: jwt所面向的用户 79 | - **aud**: 接收jwt的一方 80 | - **exp**: jwt的过期时间,这个过期时间必须要大于签发时间 81 | - **nbf**: 定义在什么时间之前,该jwt都是不可用的. 82 | - **iat**: jwt的签发时间 83 | - **jti**: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 84 | 85 | **公共的声明** : 86 | 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. 87 | 88 | **私有的声明** : 89 | 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。 90 | 91 | 定义一个payload: 92 | 93 | ```json 94 | { 95 | "sub": "1234567890", 96 | "name": "John Doe", 97 | "admin": true 98 | } 99 | ``` 100 | 101 | 然后将其进行base64加密,得到Jwt的第二部分。 102 | 103 | ```undefined 104 | eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 105 | ``` 106 | 107 | ### signature 108 | 109 | jwt的第三部分是一个签证信息,这个签证信息由三部分组成: 110 | 111 | - header (base64后的) 112 | - payload (base64后的) 113 | - secret 114 | 115 | 这个部分需要base64加密后的header和base64加密后的payload使用`.`连接组成的字符串,然后通过header中声明的加密方式进行加盐`secret`组合加密,然后就构成了jwt的第三部分。 116 | 117 | ```csharp 118 | // javascript 119 | var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); 120 | 121 | var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 122 | ``` 123 | 124 | 将这三部分用`.`连接成一个完整的字符串,构成了最终的jwt: 125 | 126 | ```css 127 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 128 | ``` 129 | 130 | **注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。** 131 | 132 | ### 如何应用 133 | 134 | 一般是在请求头里加入`Authorization`,并加上`Bearer`标注: 135 | 136 | ```bash 137 | fetch('api/user/1', { 138 | headers: { 139 | 'Authorization': 'Bearer ' + token 140 | } 141 | }) 142 | ``` 143 | 144 | 服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的: 145 | 146 | ![](http://img.topjavaer.cn/img/jwt.png) 147 | 148 | ## 总结 149 | 150 | ### 优点 151 | 152 | - 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。 153 | - 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。 154 | - 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。 155 | - 它不需要在服务端保存会话信息, 所以它易于应用的扩展 156 | 157 | ### 安全相关 158 | 159 | - 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。 160 | - 保护好secret私钥,该私钥非常重要。 161 | - 如果可以,请使用https协议 162 | 163 | 164 | 165 | ## 参考链接 166 | 167 | - [什么是 JWT](https://www.jianshu.com/p/576dbf44b2ae) 168 | 169 | - [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) -------------------------------------------------------------------------------- /docs/guide/interview/recruit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 求职 | 学弟用了我的简历,给我说去腾讯了!👍 3 | category: 4 | - 简历 5 | - java 6 | tag: 7 | - 简历 8 | head: 9 | - - meta 10 | - name: keywords 11 | content: 项目,简历,JAVA,大厂面试,项目包装 12 | - name: description 13 | content: 大厂offer收割机的简历是这样写的! 14 | --- 15 | ## 前言 16 | 17 | 最近很多小伙伴都在咨询简历究竟怎样去写? 18 | 19 | 然后我也陆陆续续帮很多小伙伴修改了简历,有好几个再改完简历后已经斩获多家大厂offer了,然后为了统一详细的解答大家的疑惑,这篇文章就来好好谈谈如何写一份好的简历,**文中附简历模板**~ 20 | 21 | > 需要帮忙修改简历的朋友,可以后台加我微信,可以提一点拙见~。 22 | 23 | **简历的重要性** 24 | 25 | 即使你有很牛逼的技术,但是你初筛过不了,连面试机会都没有,你说个der.....。 26 | 27 | **初筛环节**:在招聘高峰期,HR一天就要看好几百份乃至更多简历,每份简历不可能花太多时间,所以你得学会如何在短短几十秒去吸引抓住HR眼球,符合招聘要求并且还觉得你很牛逼。 28 | 29 | **面试环节**:面试中,面试官一般是根据你的简历提问,而你把简历写好,可以很好的引导面试官去提问,趋利避害。比如,你在简历上把你擅长的部分突出出来,“熟悉掌握MYSQL,MYSQL架构、**索引原理**、**事务原理与锁机制**、日志机制等,有一定SQL优化经验”,那么面试官刚开始拿到简历,可能还没想到问什么,第一眼便会看到你的 “`索引、事务、锁`” 相关的,然后便很大概率去问你这方面的内容。 30 | 31 | 简历的内容在精不在多,尽量保持在一页即可,少用口水话,直接抓住核心+亮点+你擅长的。 32 | 33 | 我不搞花里胡哨,讲太多其实没用。 34 | 35 | ## 简历框架 36 | 37 | **1、简历风格朴素大方即可,不必搞得花里胡哨** 38 | 39 | 推荐 **超级简历** 制作简历,美观大方。 40 | 41 | 这里分享些我收集的优质简历模板。 42 | 43 | - 超级简历 :https://www.wondercv.com 44 | 45 | - Markdown+css 自定义简历模板 :https://github.com/CyC2018/Markdown-Resume 46 | - Markdown 在线版简历排版工具:https://resume.mdnice.com 47 | 48 | **2、选项包括哪些?** 49 | 50 | 教育背景+实习经历+校园经历+专业技能+项目经历 51 | 52 | 内容在精不在多,什么个人评价不必了 53 | 54 | **3、具体内容怎样写?** 55 | 56 | 扬长避短+适当虚假与夸张+针对准备 57 | 58 | **什么意思呢?** 59 | 60 | **扬长避短**:假如你是二本,却有腾讯实习,可以把腾讯实习经历摆在第一项,学历第二项,给HR第一眼映像很好。 61 | 62 | **适当虚假与夸张**:悄悄说,除了学历千万千万不能造假,其他的都可以适当参点水分。 63 | 64 | **比如:** 65 | 66 | 校园经历,你没有比赛怎么写呢?那你在学校你总参加过一些活动吧,你可以适当包装一下,然后说自己是活动项目负责人,不过千万要清除整个流程(或者百度 (●'◡'●)); 67 | 68 | **项目**:[【备战春招-大厂项目套路解析】末流二本如何包装简历项目?项目亮点怎么写、项目一定分布式吗?如何与面试官聊项目?](https://mp.weixin.qq.com/s?__biz=MzkxMjE5NzUxNQ==&mid=2247485711&idx=1&sn=2d78f6b46e1af4d2b6c1029627fc2193&chksm=c111dbebf66652fdaf92b784c7f88d12b4d834d28221b4ba1a47d10583b107af0d0fd065c308&token=1311259124&lang=zh_CN#rd) 69 | 70 | **专业技能**:有两种方法。 71 | 72 | - 第一种:自己根据自己会的技术栈老老实实写,自己不会的千万别写,写了的认真研究可能会问的问题,关注公众号后期会持续更新大厂面试题。 73 | 74 | - 第二种:找适合自己的大佬,参照他的技术栈,适当增删,然后再根据技术栈去针对学,因为这些基本就是大厂要求的技术栈,或者大佬们靠他通过了简历筛选。 75 | 76 | 下图是小龙校招时使用的简历模板,是使用 **超级简历** 书写的。 77 | 78 | ![](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/network202304032305278.png) 79 | 80 | 81 | 82 | > 注意:我这里写了 3 个项目是想把各种项目类型尽可能多的给大家做个实例,一般写 1-2 个,简历控制在 一 页~ 83 | 84 | 这份简历模板,pdf+word版都有,**点击下方公众号名片,后台回复*「简历」*即可获取**。 85 | 86 | 下面详细说下每一部分该怎么写~ 87 | 88 | ## 具体细节 89 | 90 | 教育信息、校园经历、实习经历这些就是一些基本信息,除了校园经历可以适当包装,其他如实写就是,最主要是`专业技能`和`项目模块`。 91 | 92 | `切记,学历啥的千万别作假哎,即使学历低也要写上去` 93 | 94 | ### 专业技能 95 | 96 | 把握好专业技能的描述,专业技能的熟悉一般分为三个层次: 97 | 98 | - 精通:最好不要写精通,会被面试官问到怀疑人生....。 99 | - 熟悉/熟练/掌握:一般都写熟悉XX的使用,对于经常使用到的技术,主要用的技术栈,一般都这样写。比如使用SSM、MySQL等。 100 | - 了解:知道这玩意,或则自己只会一点,或则准备不充分,害怕被面试官问穿的。比如 “分布式”。 101 | 102 | > 注意:简历上提到的技术栈,一定要是你能回答上来的,即使你不会,但是问你问题你要能答(八股~),建议写自己会的,然后再背八股比较好(点击—[面试笔记](https://mp.weixin.qq.com/s?__biz=MzkxMjE5NzUxNQ==&mid=2247484408&idx=1&sn=7187ec64a0c77fa79cdf364ae60a7596&chksm=c111d11cf666580a3ce70684b103e14e667e54c2c7de9845ac0fee807a2159e284379b1dc61f&token=1311259124&lang=zh_CN#rd)) 103 | 104 | 技术点尽量写详细一点,如果要写MySQL相关,不要只写熟悉 MySQL的原理和基本使用。可以换成 “熟悉掌握MYSQL,MYSQL架构、**索引原理**、**事务原理与锁机制**、日志机制等,有一定SQL优化经验”,然后把你最擅长最牛逼的点用 `黑色加粗凸` 显出来,不要只写泛泛概念,一定 `落实到具体哪个点`。 105 | 106 | **为什么这样做?** 107 | 108 | 对面试官来讲,面试官刚拿到你的简历,他可能没有想好具体的问题。 109 | 110 | 我们把专业技能写的很细,落实到具体某个点,面试官有很大可能去问这些专业技能了,然后这又是你熟悉的领域,如果回答的很漂亮,将是很大的加分项。 111 | 112 | 并且面试是有时间限制的,这方面聊的多了,那么其他方面就聊的少了,遇到不熟悉的概率就更小了。 113 | 114 | **比如:** 115 | 116 | - 具有扎实Java基础,熟练掌握集合、多线程、反射等基础框架,对**集合**和**多线程**有深入研究。 117 | - 熟悉常用的**数据结构与算法**、**设计模式**。 118 | - 掌握计算机网络,掌握TCP,UDP,HTTP等协议。 119 | - 掌握**进程通信**、**内存管理、虚拟内存**等操作系统知识。 120 | - 熟系掌握MYSQL,**MYSQL架构、索引原理、事务原理与锁机制、日志机制等,有一定SQL优化经验**熟悉 121 | - 掌握Redis核心**底层数据结构**,主从复制,持久化、**Redis分布式锁等。** 122 | - 熟悉JVM虚拟机基本原理,包括**内存模型**,**垃圾回收机制**,**类加载机制等**。 123 | - 掌握SpringMVC、Spring、Mybatis、Springboot、Vue.js等主流框架使用。熟悉前后端分离开发,有 SpringCloud项目经验。 124 | - 了解Element UI、Vant UI等组件库。 125 | 126 | ### 项目模块 127 | 128 | 因为项目这块特别重要,因此我前面出了专门一篇文章来讲解了,还没看过的回头看看。 129 | 130 | > 文字版本:[【备战春招-大厂项目套路解析】末流二本如何包装简历项目?项目亮点怎么写、项目一定分布式吗?如何与面试官聊项目?](https://mp.weixin.qq.com/s?__biz=MzkxMjE5NzUxNQ==&mid=2247485711&idx=1&sn=2d78f6b46e1af4d2b6c1029627fc2193&chksm=c111dbebf66652fdaf92b784c7f88d12b4d834d28221b4ba1a47d10583b107af0d0fd065c308&token=1311259124&lang=zh_CN#rd) 131 | 132 | > 视频版本:https://www.bilibili.com/video/BV17a411z7PL?spm_id_from=333.999.0.0 133 | 134 | 最好,简历控制在一页,用pdf形式导出,命名要么按照投递公司要求命名,要么就 “**姓名+电话+学校+应聘岗位+学历(学历高)**” 135 | 136 | 照片可贴可不贴,有的同学很纠结这个问题,你是技术岗没必要在意这些,要是觉得颜值过人,贴上去说不定可以开挂,哈哈。 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/22-distributed-scheduled-task.md: -------------------------------------------------------------------------------- 1 | # 新一代分布式任务调度框架 2 | 3 | 我们先思考下面几个业务场景的解决方案: 4 | 5 | - 支付系统每天凌晨1点跑批,进行一天清算,每月1号进行上个月清算 6 | - 电商整点抢购,商品价格8点整开始优惠 7 | - 12306购票系统,超过30分钟没有成功支付订单的,进行回收处理 8 | - 商品成功发货后,需要向客户发送短信提醒 9 | 10 | > 类似的业务场景非常多,我们怎么解决? 11 | 12 | ## 为什么我们需要定时任务 13 | 14 | 很多业务场景需要我们某一特定的时刻去做某件任务,定时任务解决的就是这种业务场景。一般来说,系统可以使用消息传递代替部分定时任务,两者有很多相似之处,可以相互替换场景。 15 | 16 | 如,上面发货成功发短信通知客户的业务场景,我们可以在发货成功后发送MQ消息到队列,然后去消费mq消息,发送短信。但在某些场景下不能互换: 17 | 18 | - **时间驱动/事件驱动:**内部系统一般可以通过时间来驱动,但涉及到外部系统,则只能使用时间驱动。如怕取外部网站价格,每小时爬一次 19 | - **批量处理/逐条处理:**批量处理堆积的数据更加高效,在不需要实时性的情况下比消息中间件更有优势。而且有的业务逻辑只能批量处理。如移动每个月结算我们的话费 20 | - **实时性/非实时性:**消息中间件能够做到实时处理数据,但是有些情况下并不需要实时,比如:vip升级 21 | - **系统内部/系统解耦:**定时任务调度一般是在系统内部,而消息中间件可用于两个系统间 22 | 23 | ## java有哪些定时任务的框架 24 | 25 | ### **单机** 26 | 27 | - **timer:**是一个定时器类,通过该类可以为指定的定时任务进行配置。TimerTask类是一个定时任务类,该类实现了Runnable接口,缺点异常未检查会中止线程 28 | - **ScheduledExecutorService:**相对延迟或者周期作为定时任务调度,缺点没有绝对的日期或者时间 29 | - **spring定时框架:**配置简单功能较多,如果系统使用单机的话可以优先考虑spring定时器 30 | 31 | ### **分布** 32 | 33 | - **Quartz:**Java事实上的定时任务标准。但Quartz关注点在于定时任务而非数据,并无一套根据数据处理而定制化的流程。虽然Quartz可以基于数据库实现作业的高可用,但缺少分布式并行调度的功能 34 | - **TBSchedule:**阿里早期开源的分布式任务调度系统。代码略陈旧,使用timer而非线程池执行任务调度。众所周知,timer在处理异常状况时是有缺陷的。而且TBSchedule作业类型较为单一,只能是获取/处理数据一种模式。还有就是文档缺失比较严重 35 | - **elastic-job:**当当开发的弹性分布式任务调度系统,功能丰富强大,采用zookeeper实现分布式协调,实现任务高可用以及分片,目前是版本2.15,并且可以支持云开发 36 | - **Saturn:**是唯品会自主研发的分布式的定时任务的调度平台,基于当当的elastic-job 版本1开发,并且可以很好的部署到docker容器上。 37 | - **xxl-job:** 是大众点评员工徐雪里于2015年发布的分布式任务调度平台,是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 38 | 39 | ## 分布式任务调度系统对比 40 | 41 | > 参与对比的可选系统方案:elastic——job (以下简称E-Job)与 xxx-job(以下简称X-Job) 42 | 43 | ### **支持集群部署** 44 | 45 | **X-Job**:集群部署唯一要求为:保证每个集群节点配置(db和登陆账号等)保持一致。调度中心通过db配置区分不同集群。 46 | 47 | > 执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。集群部署唯一要求为:保证集群中每个执行器的配置项 “`xxl.job.admin.addresses/调度中心地址`” 保持一致,执行器根据该配置进行执行器自动注册等操作。 48 | 49 | **E-Job**:重写Quartz基于数据库的分布式功能,改用Zookeeper实现注册中心 50 | 51 | > 作业注册中心:基于Zookeeper和其客户端Curator实现的全局作业注册控制中心。用于注册,控制和协调分布式作业执行。 52 | 53 | ### **多节点部署时任务不能重复执行** 54 | 55 | **X-Job**:使用Quartz基于数据库的分布式功能 56 | 57 | **E-Job**:将任务拆分为n个任务项后,各个服务器分别执行各自分配到的任务项。一旦有新的服务器加入集群,或现有服务器下线,elastic-job将在保留本次任务执行不变的情况下,下次任务开始前触发任务重分片。 58 | 59 | ### **日志可追溯** 60 | 61 | **X-Job**:支持,有日志查询界面 62 | 63 | **E-Job**:可通过事件订阅的方式处理调度过程的重要事件,用于查询、统计和监控。Elastic-Job目前提供了基于关系型数据库两种事件订阅方式记录事件。 64 | 65 | ### **监控告警** 66 | 67 | **X-Job**:调度失败时,将会触发失败报警,如发送报警邮件。 68 | 69 | > 任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔 70 | 71 | **E-Job**:通过事件订阅方式可自行实现 72 | 73 | > 作业运行状态监控、监听作业服务器存活、监听近期数据处理成功、数据流类型作业(可通过监听近期数据处理成功数判断作业流量是否正常,如果小于作业正常处理的阀值,可选择报警。)、监听近期数据处理失败(可通过监听近期数据处理失败数判断作业处理结果,如果大于0,可选择报警。) 74 | 75 | ### **弹性扩容缩容** 76 | 77 | **X-Job**:使用Quartz基于数据库的分布式功能,服务器超出一定数量会给数据库造成一定的压力 78 | 79 | **E-Job**:通过zk实现各服务的注册、控制及协调 80 | 81 | ### **支持并行调度** 82 | 83 | **X-Job**:调度系统多线程(默认10个线程)触发调度运行,确保调度精确执行,不被堵塞。 84 | 85 | **E-Job**:采用任务分片方式实现。将一个任务拆分为n个独立的任务项,由分布式的服务器并行执行各自分配到的分片项。 86 | 87 | ### **高可用策略** 88 | 89 | **X-Job**:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行; 90 | 91 | **E-Job**:调度器的高可用是通过运行几个指向同一个ZooKeeper集群的`Elastic-Job-Cloud-Scheduler`实例来实现的。ZooKeeper用于在当前主`Elastic-Job-Cloud-Scheduler`实例失败的情况下执行领导者选举。通过至少两个调度器实例来构成集群,集群中只有一个调度器实例提供服务,其他实例处于”待命”状态。当该实例失败时,集群会选举剩余实例中的一个来继续提供服务。 92 | 93 | ### **失败处理策略** 94 | 95 | **X-Job**:调度失败时的处理策略,策略包括:失败告警(默认)、失败重试; 96 | 97 | **E-Job**:弹性扩容缩容在下次作业运行前重分片,但本次作业执行的过程中,下线的服务器所分配的作业将不会重新被分配。失效转移功能可以在本次作业运行中用空闲服务器抓取孤儿作业分片执行。同样失效转移功能也会牺牲部分性能。 98 | 99 | ### **动态分片策略** 100 | 101 | **X-Job:**分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。 102 | 103 | 执行器集群部署时,任务路由策略选择”分片广播”情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时传递分片参数;可根据分片参数开发分片任务; 104 | 105 | **E-Job:**支持多种分片策略,可自定义分片策略 106 | 107 | 默认包含三种分片策略:基于平均分配算法的分片策略、 作业名的哈希值奇偶数决定IP升降序算法的分片策略、根据作业名的哈希值对Job实例列表进行轮转的分片策略,支持自定义分片策略 108 | 109 | elastic-job的分片是通过zookeeper来实现的。分片的分片由主节点分配,如下三种情况都会触发主节点上的分片算法执行:a、新的Job实例加入集群 b、现有的Job实例下线(如果下线的是leader节点,那么先选举然后触发分片算法的执行) c、主节点选举” 110 | 111 | ### **和quartz框架对比** 112 | 113 | - 调用API的的方式操作任务,不人性化; 114 | - 需要持久化业务`QuartzJobBean`到底层数据表中,系统侵入性相当严重。 115 | - 调度逻辑和`QuartzJobBean`耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况加,此时调度系统的性能将大大受限于业务; 116 | - Quartz关注点在于定时任务而非数据,并无一套根据数据处理而定制化的流程。虽然Quartz可以基于数据库实现作业的高可用,但缺少分布式并行调度的功能。 117 | 118 | ## 综合对比 119 | 120 | ![](http://img.topjavaer.cn/img/定时任务框架选型.png) 121 | 122 | ## 总结和结论 123 | 124 | **共同点:** 125 | 126 | E-Job和X-job都有广泛的用户基础和完整的技术文档,都能满足定时任务的基本功能需求。 127 | 128 | **不同点:** 129 | 130 | - X-Job 侧重的业务实现的简单和管理的方便,学习成本简单,失败策略和路由策略丰富。推荐使用在“用户基数相对少,服务器数量在一定范围内”的情景下使 131 | - E-Job 关注的是数据,增加了弹性扩容和数据分片的思路,以便于更大限度的利用分布式服务器的资源。但是学习成本相对高些,推荐在“数据量庞大,且部署服务器数量较多”时使用 132 | 133 | 134 | 135 | > 摘录自网络 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Java2Top |「Java学习+面试指南+编程资源」 4 | 5 | ## 📖在线阅读网址👉:[www.java2top.cn](http://www.java2top.cn/) 6 | 7 | ## 👁‍🗨Java2Top介绍 8 | 9 | 「Java学习+面试指南+编程资源」一份涵盖 Javacoder 从零基础到进阶大厂的全面学习与面试指南~。 10 | 11 | **本指南名曰**:[《Java2Top》](www.java2top.cn),意为 Java To Top 希望大家都能成为 Java 界的佼佼者,毕业能进行业的 Top 企业公司。 12 | 13 | 致力于打造全网最佳 Java 学习进阶平台,让你 BAT Code Road 没有信息差。 14 | 15 | - **求个Star**:如果觉得 Java2Top 的内容对你有帮助的话,还请点个免费的 Star,这是对我最大的鼓励,感谢各位一起同行,共勉!Github 地址:[https://github.com/xlcoding/Java2Top](https://github.com/xlcoding/Java2Top) 。 16 | 17 | ## 👨‍💻关于作者 18 | 19 | 小龙二本出身,一路自学 Java,校招斩获腾讯、美团、网易SP、ViVo提前批、讯飞多个大厂offer。作为一名后端 coder,深感一路的不易。**希望我的分享可以帮助更多的小伙伴,我踩过的坑你们不要再踩**! 20 | 21 | [](https://img.shields.io/badge/WhChat-微信交流群-yellowgreen)[](https://mp.weixin.qq.com/s/9HZng1BcsLHnOpPKVBt6IQ)[](https://www.zhihu.com/people/jakelong-37)[](https://mp.weixin.qq.com/s/Co4UCJfPfCsbfwUXQC24Wg) 22 | 23 | **学前须知:** 24 | 25 | * [项目介绍](../java2top/intro.md) 26 | * [网站规划](../java2top/todo.md) 27 | 28 | ## 面前准备 29 | 30 | [面试官眼前一亮的项目怎样写?](./docs/guide/interview/project.md) 31 | 32 | [求职 | 学弟用了我的简历,给我说去腾讯了!👍](./docs/guide/interview/recruit.md) 33 | 34 | [校招求职六大策略📚](./docs/guide/interview/six-method.md) 35 | 36 | ## BAT高频面题 37 | 38 | ### Java基础 39 | 40 | **知识点/面试题总结** : (必看:+1: ): 41 | 42 | [《BAT高频面点》Java基础高频面试题合集(精选)](./docs/guide/java/basic/java-basic.md) 43 | 44 | ### JDK集合 45 | 46 | **知识点/面试题总结** : (必看:+1: ): 47 | 48 | [《BAT高频面点》JDK集合框架面试题精选](./docs/guide/java/collection/java-collection.md) 49 | 50 | **源码分析** 51 | 52 | * [ArrayList 源码+扩容机制分析](docs/java/collection/arraylist-source-code.md) 53 | * [HashMap(JDK1.8)源码+底层数据结构分析](docs/java/collection/hashmap-source-code.md) 54 | * [ConcurrentHashMap 源码+底层数据结构分析](docs/java/collection/concurrent-hash-map-source-code.md) 55 | 56 | [源码分析(推荐👍)](./docs/guide/java/collection/java-collection-code.md) 57 | 58 | ### 并发编程 59 | 60 | [《BAT高频面点》(Java并发编程面试题八股文)必看](./docs/guide/java/concurrent/java-concurrent.md) 61 | 62 | ### JVM 63 | 64 | [《BAT高频面点》JVM高频面试题八股文🔥](./docs/guide/java/jvm/java-jvm.md) 65 | 66 | ### 计算机基础 67 | 68 | [《BAT高频面点》计算机网络高频面试题合集(必看👍)](./docs/guide/java/computer-basic/network.md) 69 | 70 | [《BAT高频面点》操作系统高频面试题合集(必看👍)](./docs/guide/java/computer-basic/operate-system.md) 71 | 72 | ### MySQL 73 | 74 | [MySQL-架构篇(hot🔥)](./docs/guidejava/database/mysql/java-mysql-0-structure.md) 75 | 76 | MySQL-索引篇(hot🔥) 77 | 78 | MySQL-事务篇(hot🔥) 79 | 80 | MySQL-锁机制篇(hot🔥) 81 | 82 | MySQL-架构日志篇(hot🔥) 83 | 84 | MySQL-高可用篇(hot🔥) 85 | 86 | MySQL-调优篇(hot🔥) 87 | 88 | ### 分布式 89 | 90 | ## 海量数据统计(更新中 ing 🔥) 91 | 92 | [大数据中 TopK 问题的常用套路🎈](./docs/guide/mass-data/0-topk-template.md) 93 | 94 | [1.统计不同号码的个数](./docs/guide/mass-data/1-count-phone-num.md) 95 | 96 | [2.出现频率最高的100个词](./mass-data/2-find-hign-frequency-word.md) 97 | 98 | [3.查找两个大文件共同的URL](./mass-data/3-find-same-url.md) 99 | 100 | [4.如何从大量的 URL 中找出相同的 URL?](./mass-data/4-find-mid-num.md) 101 | 102 | [5.如何查询最热门的查询串?](./mass-data/5-find-hot-string.md) 103 | 104 | [6.如何找出排名前 500 的数?](./mass-data/6-top-500-num.md) 105 | 106 | [7.如何按照 query 的频度排序?](./mass-data/7-query-frequency-sort.md) 107 | 108 | [8.如何从大量数据中找出高频词?](./mass-data/8-high-frequency.md) 109 | 110 | [9.几亿个数的大文件怎么排序?](./mass-data/9-sort-500-million-large-files.md) 111 | 112 | ## 系统设计 113 | 114 | [0.开篇-如何设计大型系统?]() 115 | 116 | [1.扫码登录的原理?]() 117 | 118 | [2.订单30分钟未支付自动取消怎么实现?]() 119 | 120 | [3.如何把——个文件较快的发送到 100 服务器?]() 121 | 122 | [4.如何设计一个短链系统?]() 123 | 124 | [5.如何解决超卖问题?]() 125 | 126 | [6.如何设计一个秒杀系统?]() 127 | 128 | [7.微信红包后台系统设计详解?]() 129 | 130 | [8.单点登录(SSO)的设计与实现?]() 131 | 132 | [9.如何设计一个优惠券系统?]() 133 | 134 | [10.如何用Redis 统计用户访问量?]() 135 | 136 | [11.实时订阅推送设计与实现]() 137 | 138 | [12.如何设计一个抢红包系统如何设计一个消息队列?购物车系统怎么设计?]() 139 | 140 | ## 面试实录 (必看 :+1:) 141 | 142 | 根据秋招春招上岸大厂面试经历以及身边朋友**上岸面试录音模拟面试现场**,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 143 | 144 | 专栏系列目前已经连载 **N** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 145 | 146 | - [《面试实录》百度二面,Sychronized原理详解](./docs/guide/memoir/1.md) 147 | - [《面试实录》字节二面,SQL执行慢的原因?如何优化?](./docs/guide/memoir/2.md) 148 | - [《面试实录》 字节二面, MySQL自增主键一定连续吗?](./docs/guide/memoir/3.md) 149 | - [《面试实录》 阿里一面,深剖Map底层结构与原理](./docs/guide/memoir/4.md) 150 | - [《面试实录》携程二面,JVM 底层剖析](./docs/guide/memoir/5.md) 151 | 152 | ## 公众号 153 | 154 | ![我的公众号](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303202215699.png) 155 | 156 | 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。【**小龙coding**】 157 | 158 | -------------------------------------------------------------------------------- /docs/guide/java/distributed/3-rpc.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: RPC原理详解 4 | --- 5 | 6 | # RPC 7 | 8 | ## RPC简介 9 | 10 | RPC,英文全名remote procedure call,即远程过程调用。就是说一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。 11 | 12 | 可以这么说,RPC就是要像调用本地的函数一样去调远程函数。 13 | 14 | RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。 15 | 16 | 其中,通信协议包含**http协议**(如gRPC使用http2)、**自定义报文的tcp协议**(如dubbo)。序列化协议包含**基于文本编码**的xml、json,**基于二进制编码**的protobuf、hessian等。 17 | 18 | > protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储,占用空间小,但也带来了可读性差的缺点(二进制协议,因为不可读而难以调试,不好定位问题)。 19 | > 20 | > Protobuf序列化协议相比JSON有什么优点? 21 | > 22 | > 1:序列化后体积相比Json和XML很小,适合网络传输 23 | > 24 | > 2:支持跨平台多语言 25 | > 26 | > 3:序列化反序列化速度很快,快于Json的处理速速 27 | 28 | **一个完整的RPC过程,都可以用下面这张图来描述**: 29 | 30 | > stub说的都是“一小块代码”,通常是有个caller要调用callee的时候,中间需要一些特殊处理的逻辑,就会用这种“小块代码”去做。 31 | 32 | ![image-20230528211928557](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202305282119063.png) 33 | 34 | 1. 服务消费端(client)以本地调用的方式调用远程服务; 35 | 2. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`; 36 | 3. 客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端; 37 | 4. 服务端 Stub(桩)收到消息将消息反序列化为Java对象: `RpcRequest`; 38 | 5. 服务端 Stub(桩)根据`RpcRequest`中的类、方法、方法参数等信息调用本地的方法; 39 | 6. 服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:`RpcResponse`(序列化)发送至消费方; 40 | 7. 客户端 Stub(client stub)接收到消息并将消息反序列化为Java对象:`RpcResponse` ,这样也就得到了最终结果。 41 | 42 | ## RPC 解决了什么问题? 43 | 44 | 让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。 45 | 46 | ## 常见的 RPC 框架有哪些? 47 | 48 | - **Dubbo:** Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。 49 | - **gRPC** :基于HTTP2。gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。 50 | - **Hessian:** Hessian是一个轻量级的remoting on http工具,使用简单的方法提供了RMI的功能。 采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。 51 | - **Thrift:** Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。 52 | 53 | ## 有了HTTP ,为啥还要用RPC进行服务调用? 54 | 55 | 首先,RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。而HTTP只是一个通信协议,不是一个完整的远程调用方案。这两者不是对等的概念,用来比较不太合适。 56 | 57 | RPC框架可以使用 **HTTP协议**作为传输协议或者直接使用**自定义的TCP协议**作为传输协议,使用不同的协议一般也是为了适应不同的场景。 58 | 59 | HTTP+Restful,其优势很大。它**可读性好**,且**应用广、跨语言的支持**。 60 | 61 | 但是使用该方案也有其缺点,这是与其优点相对应的: 62 | 63 | - 首先是**有用信息占比少**,毕竟HTTP工作在第七层,包含了大量的HTTP头等信息。 64 | - 其次是**效率低**,还是因为第七层的缘故,必须按照HTTP协议进行层层封装。 65 | 66 | 而使用**自定义tcp协议**的话,可以极大地精简了传输内容,这也是为什么有些后端服务之间会采用自定义tcp协议的rpc来进行通信的原因。 67 | 68 | ## 各种序列化技术 69 | 70 | ### XML 71 | 72 | XML序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能要求不高,而且QPS较低的企业级内部系统之间的数据交换场景。同时XML又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的WebService,就是采用XML格式对数据进行序列化的。XML序列化/反序列化的实现方式有很多,熟知的方式有XStream和Java自带的XML序列化和反序列化两种。 73 | 74 | ### JSON 75 | 76 | JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML来说,JSON的字节流更小,而且可读性也非常好。现在JSON数据格式在企业运用是最普遍的。 77 | 78 | JSON序列化常用的开源工具有很多: 79 | 80 | 1. Jackson(https://github.com/FasterXML/jackson ) 81 | 2. 阿里开源的FastJson(https://github.com/alibaba/fastjon) 82 | 3. 谷歌的GSON(https://github.com/google/gson) 83 | 84 | 这几种json的序列化工具中,jackson与fastjson要比GSON的性能好,但是jackson、GSON的稳定性腰比Fastjson好。而fastjson的优势在于提供的api非常容易使用。 85 | 86 | ### Hession 87 | 88 | Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说,Hession具有更好的性能和易读性,而且支持多种不同的语言。 89 | 90 | 实际上Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行了重构,性能更高。 91 | 92 | ### Avro 93 | 94 | Avro是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据。动态语言友好,Avro提供的机制是动态语言可以方便的处理Avro数据。 95 | 96 | ### Kryo 97 | 98 | Kryo是一种非常成熟的序列化实现,已经在Hive、Storm中使用的比较广泛,不过它不能夸语言。目前Dubbo已经在2.6版本支持kyro的序列化机制。它的性能要由于之前的hessian2。 99 | 100 | ### Protobuf 101 | 102 | Protobuf是Google的一种数据交换格式,它独立于语言、独立于平台。Google提供了多种语言来实现,比如Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件,Protobuf是一个纯粹的表示层协议,可以和各种传输层协议一起使用。 103 | 104 | Protobuf使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的RPC调用。另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中。 105 | 106 | 但是要使用Protobuf会相对来说麻烦些,因为他有自己的语法,有自己的编译器,如果需要用到的话必须要去投入成本在这个技术的学习中。 107 | 108 | Protobuf有个缺点就是要传输每一个类的结构都要生成对应的proto文件,如果某个类发生修改,还得重新生成该类对应的proto文件。 109 | 110 | ## 序列化技术的选型 111 | 112 | ### 技术层面 113 | 114 | 1. 序列化空间开销,也就是序列化产生的结果大小,这个影响到传输性能。 115 | 2. 序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间。 116 | 3. 序列化协议是否支持夸平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的。 117 | 4. 可扩展性、兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们来采用序列化协议具有良好的可扩展性、兼容性,比如现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务。 118 | 5. 技术的流行程度,越流行的技术意味着使用的公司越多,那么很多坑都已经淌过并且得到了解决,技术解决方案也相对成熟。 119 | 6. 学习难度和易用性。 120 | 121 | ### 选型建议 122 | 123 | 1. 对性能要求不高的场景,可以采用基于XML的SOAP协议 124 | 2. 性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro都可以。 125 | 3. 基于前后端分离,或者独立的对外API服务,选用JSON是比较好的,对于调试、可读性都很不错。 126 | 4. Avro设计理念偏于动态类型语言,那么这类的场景使用Avro是可以的。 127 | -------------------------------------------------------------------------------- /docs/guide/interview/project.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 面试官眼前一亮的项目怎样写? 3 | category: 4 | - 项目 5 | - 简历 6 | - java 7 | tag: 8 | - 项目 9 | head: 10 | - - meta 11 | - name: keywords 12 | content: 项目,简历,JAVA,大厂面试,项目包装 13 | - name: description 14 | content: 大厂收割机的简历项目是这样打造的,深挖亮点,层层叠加 15 | --- 16 | 此篇和大家聊聊校招和日常实习关于项目如何准备。 17 | 18 | ## 前文 19 | 20 | 相信大家都曾疑惑过这个问题: 21 | 22 | `没有项目怎么办?简历上项目该怎样写?我只做过简单的业务感觉没什么亮点该怎么办,会不会直接被刷下去?不懂项目怎样包装?项目是不是必须要涉及分布式、微服务这些?` 23 | 24 | 当初我开始找工作时也遇到过相同的问题,然后到处网上B站等搜各种视频(关于项目怎样写?),也看过很多贴子。我发现网上都有一个通病,就是感觉说了又感觉没说,你觉得讲得很有道理,但是听完它讲的你依旧不知道项目怎样写。 25 | 26 | 然后你也没有合适的项目,即使你做过几个项目,但是都是一些及其简单的项目,没什么亮点,不懂怎样就你自己的项目怎样去包装,网上的视频,帖子就扯一些泛泛的说法,然后全是一些什么负载均衡,分布式、微服务相关的点,我不可能为了亮点而去强行添加这些看起来很牛逼的技术啊; 27 | 28 | 而且个人觉得大部分校招生刚毕业怎么可能很好理解这些技术(ZK、SpringlCoud、JVM调优、数据库调优)去解决具体的业务问题,即使真的会一点,都是绝少部分同学,假如我作为面试官感觉校招生扯这些一听就是背八股,你背得好,然我找不出问题还行,可以让你过,背得不好我只会觉得你没有独立思考能力,然后实际code能力也不行。 29 | 30 | 其实当时我想找的是那些**怎样抽丝剥茧的结合具体场景去教你包装项目**,然后你可能模仿它的方法去包装自己的项目,而不是你只是告诉我简历上项目部分要start法则,要突出亮点,要怎么怎么写,我肯定知道要突出亮点,问题**是我压根不知道亮点是啥啊**,呜呜~ 31 | 32 | 我想通过本篇文章全方面的分析项目究竟怎样准备,你看了绝对可以自己打造一个满意的项目出来。 33 | 34 | ## 项目来源与分类 35 | 36 | 简历上的项目无非要么是`实习的项目`,要么是`自己真实做过的其他项目`(比赛,跟着网上做、实验室等),要么是`压根就不存在这样一个项目的项目` 37 | 38 | **怎样去获得项目(宏观)?** 39 | 40 | ### **实习项目** 41 | 42 | 可以将实习的项目写在简历上。 43 | 44 | **怎样去写?** 45 | 46 | 相信绝大部分同学去实习也只是打打杂,即使上手写代码更多的可能也就是简单的调调接口,写写CRUD,不可能涉及到复杂的核心业务,系统优化,调优这些。 47 | 48 | 这时,你得打开你的格局,虽然你没做过这个功能点,没有进行优化升级,但是不代表和你一起做这个项目的同事,老员工,leader也没有做啊,你可以去请教你的同事看看他们做的好的功能点,业务点,觉得不错的亮点拿过来写进你的简历。 49 | 50 | `切记,一定要自己弄清楚` 51 | 52 | 当初我实习时,主管分配给我的任务也很简单,简单的我都不好意思写进简历,但是我的同事他们(正式员工)做的很多难点,亮点,其中什么分布式锁去解决某个问题,定时任务去解决某个问题等等,我向他们取经学习然后写进自己的简历,这就是亮点啊; 53 | 54 | 而且是公司实际用来去解决了某个具体业务问题,你可以就这个业务场景先说出现了什么问题,然后你用了什么去解决了这个问题,相比之下这个更能让面试官觉得不错。 55 | 56 | ### 自己做的一些项目 57 | 58 | 你可能在比赛时做过某个项目,跟着网上视频做过某个项目等。 59 | 60 | 如果是比赛,实验室里面的一些有着相关背景的项目,建议可以从项目背景、项目成果、相关技术栈、有没有真正落地实现,用户量多少这些方面下手。 61 | 62 | 比如:我参加的XX比赛,获得XX奖,这个获奖比例权重是XX,然后我做的这个项目具体是去解决XX背景下的XX问题,用了XX技术栈,最后上线,目前用户量是XX。 63 | 64 | 如果是跟着网上视频做的项目,避免分布式微服务,秒杀,商城,实现避免不了,把场景变换一下,技术栈根据求职要求适当调整。 65 | 66 | 然后这只是一个宏观概念,具体你得落实到和职位相关的一些技术点,`具体细节包装后文统一介绍`。 67 | 68 | ### 虚拟的项目 69 | 70 | 这个适合急于找工作,但是一个项目也没做过的同学。 71 | 72 | 可以根据当下面试热门技术点,自己构建一个场景,再把热门技术点融入进去,取个合适的名字,项目背景技术栈搞定。 73 | 74 | 然后百度或请教别人如何用对应技术实现相应功能,项目可编,但一定面试要自己能说清楚。提前想好可能问的问题。 75 | 76 | 然后还可以去github找一个合适的开源项目,自己再包装一下。`具体细节包装后文统一介绍`。 77 | 78 | ## 关于项目怎样包装 79 | 80 | 通过上文,你大概学会了怎样去获取一个适合你的项目。 81 | 82 | 当时上面述说也有点宏观,关于项目的包装,简历上的具体亮点怎样写你可能还是迷茫,接着往下看~ 83 | 84 | **关键词**:分布式、微服务、用户量、QPS、TPS、技术栈、业务复杂程度 85 | 86 | 并不是要求这几个方面大家都满足,无需焦虑,你要相信绝大多数程序员都没有这种项目经验的,那么这种情况怎样去突出你的项目亮点呢? 87 | 88 | 结合业务,突出业务难点,不要凭空捏造许多需求 89 | 90 | **第一个点(分布式微服务)** 91 | 92 | 这个是现在大型项目必备的嘛,不一定,只是说现在主流的互联网架构模式是这样的,一般的大型项目这样设计,但是你的项目可能由于某些原因,成本、历史什么的,或则完全没有必要,没关系,大胆承认就是,不过你没有用分布式,并不代表你不了解分布式。 93 | 94 | 突出项目为啥用单体架构,不用分布式,后面项目达到什么程度需要做重构,可以讲讲你的`提前设计的方案,思想`,并不一定你一定做过分布式项目,我觉得这就是一个亮点,比你直接去讲你做分布式项目感觉更亲切,更让人接受,更亮点。 95 | 96 | `一方面`:这体现了你的思考过程,思维、见解。 97 | 98 | `二方面`:这也代表你对分布式有一定了解,并且融入了自己的思考,有一定的自我理解。 99 | 100 | `三方面`:弥补了你单体架构项目的缺陷,也不用为你项目是分布式项目而担心被问穿,反而拔高了你自己的思考的高度 101 | 102 | **第二点(项目指标)** 103 | 104 | `用户量`、`峰值`、`QPS`、`TPS`。对于这几个点估计大多数校招生是没有这个意识的,一般是社招面试面试官会问你公司项目,你得把公司项目的这几个指标介绍清楚,怎样去优化,不过这是社招的事了,校招生可以了解一下,提前准备好一套说辞,一般不会问,问了你也得能灵活应对。 105 | 106 | **第三点(技术栈)** 107 | 108 | 大家要走出这样一个误区,技术栈不是堆得越多越好,若是你讲不清你为啥用这个技术,可能就变成了一个扣分项了,比如:你用redis,那你说说为啥你要用redis,在你项目中咋体现的,不用可不可以?(所以你得提前想好一套说辞) 109 | 110 | **第三点(业务需求)** 111 | 112 | `可能有的朋友就会问,我没有做过啥复杂的项目,没有复杂的需求,怎样去写这个简历?` 113 | 114 | 这里举个`案例`帮助大家去找理解: 115 | 116 | 假设现在我需要做这样一个功能 ”`在不同业务事件触发情况下,需要去通知用户邮件、短信、站内信等`“,不同业务场景下,需要用不同组合方式去通知用户,你的项目可能就是这样写的,可能就是不同的业务场景,if else 判断一下,然后去调哪几个; 117 | 118 | 此时如果你懂的适当优化,包装亮点,你可以用`观察者模式`,让事件的发生与通知去处理,在代码上便进行了解耦,关于每一个业务场景他有哪几种通知,又可以抽取一个策略模式的接口,动态的去组合每一种业务; 119 | 120 | 而且如果后期需要增删其他的,用`策略模式`可以灵活的变更,而不是改大量的` if else` 代码,如果之后更灵活了,还可以把这个映射关系放到配置中心去,于是后期每来一个新业务,只需要改一下配置中心的配置,便可以组合不同的通知的模式。 121 | 122 | 通过上述例子,你可以发现,一个简单的不起眼的业务需求,后者明显更具有扩展性,更解耦,它包好了你对业务的理解,对设计模式的理解,代码编写能力等多个方面,这不就是项目亮点吗,而且我觉得对于一个校招生实习生,这比起烂大街的秒杀,分布式来得不知高级多少倍。 123 | 124 | 所以大家不要一味追求高并发,各种调优,`避免网上千篇一律的八股,简单而真实,平淡中彰显美`,我们这就叫”朴素美“。 125 | 126 | 各种技术只是工具,我们之所以用各种技术是为了去解决问题,当有问题时能更好更合理的选择某种技术去应对,而不是单纯为了使用某种技术去使用。 127 | 128 | ## 简历上怎样写 129 | 130 | 简历上项目都可以分为**技术栈**、**项目介绍**、**项目职责**进行描述 131 | 132 | ![图片](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304151655481.png) 133 | 134 | **技术栈**:主要的核心技术即可 135 | 136 | **项目介绍**:大致介绍+主要亮点用到的**什么技术解决了什么问题**突出即可,也不用面面俱到,精简切记 137 | 138 | **项目职责**:主要写项目亮点和你负责的那块。 139 | 140 | 1. 参与了XX开发,熟悉XX开发流程(宏观项目开发) 141 | 2. 负责XX功能点(自己负责那块) 142 | 3. 使用XX技术解决了XX问题,使得系统或则其他方面得到多大改进(亮点突出) 143 | 4. 优化了XX,使得系统XX模块提高了n倍速度。 144 | 5. 实际操作-找出你项目中希望被面试官问到的点,或者你认为属于项目亮点的部分,突出你对项目的优化部分(黑体加粗) 145 | 146 | **举例**: 147 | 148 | - 使用nginx实现了负载均衡,提高了访问速度 149 | - 利用Redis实现分布式锁解决了某个问题 150 | - 定义热点数据并缓存在Redis,降低了数据库访问压力 151 | - 利用zookeeper实现HA,解决单点故障 152 | - 利用某个设计模式对代码重构,避免了XX,增强代码可读性,扩展性等 153 | - 对项目架构进行了重构,将单体架构构建成分布式微服务的,XXXXX 154 | 155 | 最后,简历项目建议写2个即可。 156 | 157 | 好的,关于项目这块我想的经验和想说的大致都说清楚啦,希望对你有帮助。 158 | 159 | 如果实在还是不懂,可以后台联系我讲我的项目案例给你做过参考。需要简历模板的也可以联系我。 160 | 161 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/26-java-stream.md: -------------------------------------------------------------------------------- 1 | # Java Stream常见用法汇总,开发效率大幅提升 2 | 3 | Java8 新增的 Stream 流大大减轻了我们代码的工作量,但是 Stream 流的用法较多,实际使用的时候容易遗忘,整理一下供大家参考。 4 | 5 | ## 1. 概述 6 | 7 | Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来对 Java 集合运算和表达的高阶抽象。 8 | 9 | Stream API 可以极大提高 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。 10 | 11 | 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。 12 | 13 | ![](http://img.topjavaer.cn/img/stream1.png) 14 | 15 | ## 2. 创建 16 | 17 | ### 2.1 集合自带 Stream 流方法 18 | 19 | ``` 20 | List list = new ArrayList<>(); 21 | // 创建一个顺序流 22 | Stream stream = list.stream(); 23 | // 创建一个并行流 24 | Stream parallelStream = list.parallelStream(); 25 | ``` 26 | 27 | ### 2.1 通过 Array 数组创建 28 | 29 | ``` 30 | int[] array = {1,2,3,4,5}; 31 | IntStream stream = Arrays.stream(array); 32 | ``` 33 | 34 | ### 2.3 使用 Stream 的静态方法创建 35 | 36 | ``` 37 | Stream stream = Stream.of(1, 2, 3, 4, 5); 38 | Stream stream = Stream.iterate(0, (x) -> x + 3).limit(3); // 输出 0,3,6 39 | 40 | Stream stream = Stream.generate(() -> "Hello").limit(3); // 输出 Hello,Hello,Hello 41 | Stream stream = Stream.generate(Math::random).limit(3); // 输出3个随机数 42 | ``` 43 | 44 | ### 2.3 数值流 45 | 46 | ``` 47 | // 生成有限的常量流 48 | IntStream intStream = IntStream.range(1, 3); // 输出 1,2 49 | IntStream intStream = IntStream.rangeClosed(1, 3); // 输出 1,2,3 50 | // 生成一个等差数列 51 | IntStream.iterate(1, i -> i + 3).limit(5).forEach(System.out::println); // 输出 1,4,7,10,13 52 | // 生成无限常量数据流 53 | IntStream generate = IntStream.generate(() -> 10).limit(3); // 输出 10,10,10 54 | ``` 55 | 56 | 另外还有 LongStream、DoubleStream 都有这几个方法。 57 | 58 | ## 3. 使用 59 | 60 | 初始化一些数据,示例中使用。 61 | 62 | ``` 63 | public class Demo { 64 | class User{ 65 | // 姓名 66 | private String name; 67 | 68 | // 年龄 69 | private Integer age; 70 | } 71 | 72 | public static void main(String[] args) { 73 | List users = new ArrayList<>(); 74 | users.add(new User("Tom", 1)); 75 | users.add(new User("Jerry", 2)); 76 | } 77 | } 78 | ``` 79 | 80 | ### 3.1 遍历 forEach 81 | 82 | ``` 83 | // 循环输出user对象 84 | users.stream().forEach(user -> System.out.println(user)); 85 | ``` 86 | 87 | ### 3.2 查找 find 88 | 89 | ``` 90 | // 取出第一个对象 91 | User user = users.stream().findFirst().orElse(null); // 输出 {"age":1,"name":"Tom"} 92 | // 随机取出任意一个对象 93 | User user = users.stream().findAny().orElse(null); 94 | ``` 95 | 96 | ### 3.3 匹配 match 97 | 98 | ``` 99 | // 判断是否存在name是Tom的用户 100 | boolean existTom = users.stream().anyMatch(user -> "Tom".equals(user.getName())); 101 | // 判断所有用户的年龄是否都小于5 102 | boolean checkAge = users.stream().allMatch(user -> user.getAge() < 5); 103 | ``` 104 | 105 | ### 3.4 筛选 filter 106 | 107 | ``` 108 | // 筛选name是Tom的用户 109 | users.stream() 110 | .filter(user -> "Tom".equals(user.name)) 111 | .forEach(System.out::println); // 输出 {"age":1,"name":"Tom"} 112 | ``` 113 | 114 | ### 3.5 映射 map/flatMap 115 | 116 | ``` 117 | // 打印users里的name 118 | users.stream().map(User::getName).forEach(System.out::println); // 输出 Tom Jerry 119 | // List> 转 List 120 | List> userList = new ArrayList<>(); 121 | List users = userList.stream().flatMap(Collection::stream).collect(Collectors.toList()); 122 | ``` 123 | 124 | ### 3.6 归约 reduce 125 | 126 | ``` 127 | // 求用户年龄之和 128 | Integer sum = users.stream().map(User::getAge).reduce(Integer::sum).orElse(0); 129 | // 求用户年龄的乘积 130 | Integer product = users.stream().map(User::getAge).reduce((x, y) -> x * y).orElse(0); 131 | ``` 132 | 133 | ### 3.7 排序 sorted 134 | 135 | ``` 136 | // 按年龄倒序排 137 | List collect = users.stream() 138 | .sorted(Comparator.comparing(User::getAge).reversed()) 139 | .collect(Collectors.toList()); 140 | 141 | //多属性排序 142 | List result = persons.stream() 143 | .sorted(Comparator.comparing((Person p) -> p.getNamePinyin()) 144 | .thenComparing(Person::getAge)).collect(Collectors.toList()); 145 | ``` 146 | 147 | ### 3.8 收集 collect 148 | 149 | ``` 150 | // list转换成map 151 | Map map = users.stream() 152 | .collect(Collectors.toMap(User::getAge, Function.identity())); 153 | 154 | // 按年龄分组 155 | Map> userMap = users.stream().collect(Collectors.groupingBy(User::getAge)); 156 | 157 | // 求平均年龄 158 | Double ageAvg = users.stream().collect(Collectors.averagingInt(User::getAge)); // 输出 1.5 159 | 160 | // 求年龄之和 161 | Integer ageSum = users.stream().collect(Collectors.summingInt(User::getAge)); 162 | 163 | // 求年龄最大的用户 164 | User user = users.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge))).orElse(null); 165 | 166 | // 把用户姓名拼接成逗号分隔的字符串输出 167 | String names = users.stream().map(User::getName).collect(Collectors.joining(",")); // 输出 Tom,Jerry 168 | ``` 169 | 170 | ### 3.9 List 转换成 Map 时遇到重复主键 171 | 172 | ![](http://img.topjavaer.cn/img/stream2.png) 173 | 174 | 这样转换会报错,因为 ID 重复。 175 | 176 | ![](http://img.topjavaer.cn/img/stream3.png) 177 | 178 | 可以这样做 179 | 180 | ![](http://img.topjavaer.cn/img/stream4.png) 181 | 182 | 183 | 184 | --end-- -------------------------------------------------------------------------------- /docs/guide/mass-data/0-topk-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 大数据中 TopK 问题的常用套路🎈 4 | --- 5 | 6 | **常见的 topK 问题**。 7 | 8 | 对于海量数据到处理经常会涉及到 topK 问题。在设计数据结构和算法的时候,主要需要考虑的应该是当前算法(包括数据结构)跟给定情境(比如数据量级、数据类型)的适配程度,和当前问题最核心的瓶颈(如降低时间复杂度,还是降低空间复杂度)是什么。 9 | 10 | 首先,我们来举几个常见的 topK 问题的例子: 11 | 12 | 1. 给定 100 个 int 数字,在其中找出最大的 10 个; 13 | 1. 给定 10 亿个 int 数字,在其中找出最大的 10 个(这 10 个数字可以无序); 14 | 1. 给定 10 亿个 int 数字,在其中找出最大的 10 个(这 10 个数字依次排序); 15 | 1. 给定 10 亿个不重复的 int 数字,在其中找出最大的 10 个; 16 | 1. 给定 10 个数组,每个数组中有 1 亿个 int 数字,在其中找出最大的 10 个; 17 | 1. 给定 10 亿个 string 类型的数字,在其中找出最大的 10 个(仅需要查 1 次); 18 | 1. 给定 10 亿个 string 类型的数字,在其中找出最大的 k 个(需要反复多次查询,其中 k 是一个随机数字)。 19 | 20 | 上面这些问题看起来很相似,但是解决的方式却千差万别。稍有不慎,就可能使得 topK 问题成为系统的瓶颈。不过也不用太担心,接下来我会总结几种常见的解决思路,遇到问题的时候,大家把这些基础思路融会贯通并且杂糅组合,即可做到见招拆招。 21 |
22 | 23 | ### 1. 堆排序法 24 | 25 | 这里说的是堆排序法,而不是快排或者希尔排序。虽然理论时间复杂度都是 `O(nlogn)`,但是堆排在做 topK 的时候有一个优势,就是可以维护一个仅包含 k 个数字的小顶堆(想清楚,为啥是小顶堆哦),当新加入的数字大于堆顶数字的时候,将堆顶元素剔除,并加入新的数字。 26 | 27 | 用 C++ 来说明,堆在 stl 中是 priority_queue(不是 set)。 28 | 29 | ```cpp 30 | int main() { 31 | const int topK = 3; 32 | vector vec = {4,1,5,8,7,2,3,0,6,9}; 33 | priority_queue, greater<>> pq; // 小顶堆 34 | for (const auto& x : vec) { 35 | pq.push(x); 36 | if (pq.size() > topK) { 37 | // 如果超出个数,则弹出堆顶(最小的)数据 38 | pq.pop(); 39 | } 40 | } 41 | 42 | while (!pq.empty()) { 43 | cout << pq.top() << endl; // 输出依次为7,8,9 44 | pq.pop(); 45 | } 46 | 47 | return 0; 48 | } 49 | ``` 50 | 51 | > Java 中同样提供了 PriorityQueue 的数据结构。 52 | 53 | ### 2. 类似快排法 54 | 55 | 快排大家都知道,针对 topK 问题,可以对快排进行改进。仅对部分数据进行递归计算。比如,在 100 个数字中,找最大的 10 个,第一次循环的时候,povit 被移动到了 80 的位置,则接下来仅需要在后面的 20 个数字中找最大的 10 个即可。 56 | 57 | 这样做的优势是,理论最优时间复杂度可以达到 `O(n)`,不过平均时间复杂度还是 `O(nlogn)`。需要说明的是,通过这种方式,找出来的最大的 k 个数字之间,是无序的。 58 | 59 | ```cpp 60 | int partition(vector& arr, int begin, int end) { 61 | int left = begin; 62 | int right = end; 63 | int povit = arr[begin]; 64 | 65 | while (left < right) { 66 | while (left < right && arr[right] >= povit) {right--;} 67 | while (left < right && arr[left] <= povit) {left++;} 68 | if (left < right) {swap(arr[left], arr[right]);} 69 | } 70 | 71 | swap(arr[begin], arr[left]); 72 | return left; 73 | } 74 | 75 | void partSort(vector& arr, int begin, int end, int target) { 76 | if (begin >= end) { 77 | return; 78 | } 79 | 80 | int povit = partition(arr, begin, end); 81 | if (target < povit) { 82 | partSort(arr, begin, povit - 1, target); 83 | } else if (target > povit) { 84 | partSort(arr, povit + 1, end, target); 85 | } 86 | } 87 | 88 | vector getMaxNumbers(vector& arr, int k) { 89 | int size = (int)arr.size(); 90 | // 把求最大的k个数,转换成求最小的size-k个数字 91 | int target = size - k; 92 | partSort(arr, 0, size - 1, target); 93 | vector ret(arr.end() - k, arr.end()); 94 | return ret; 95 | } 96 | 97 | int main() { 98 | vector vec = {4,1,5,8,7,2,3,0,6,9}; 99 | auto ret = getMaxNumbers(vec, 3); 100 | 101 | for (auto x : ret) { 102 | cout << x << endl; // 输出7,8,9(理论上无序) 103 | } 104 | 105 | return 0; 106 | } 107 | ``` 108 | 109 |
110 | 111 | ### 3. 使用 bitmap 112 | 113 | 有时候 topK 问题会遇到数据量过大,内存无法全部加载。这个时候,可以考虑将数据存放至 bitmap 中,方便查询。 114 | 115 | 比如,给出 10 个 int 类型的数据,分别是【13,12,11,1,2,3,4,5,6,7】,int 类型的数据每个占据 4 个字节,那这个数组就占据了 40 个字节。现在,把它们放到一个 16 个长度 bool 的 bitmap 中,结果就是【0,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0】,在将空间占用降低至 4 字节的同时,也可以很方便的看出,最大的 3 个数字,分别是 11,12 和 13。 116 | 117 | 需要说明的是,bitmap 结合跳表一起使用往往有奇效。比如以上数据还可以记录成:从第 1 位开始,有连续 7 个 1;从第 11 位开始,有连续 3 个 1。这样做,空间复杂度又得到了进一步的降低。 118 | 119 | 这种做法的优势,当然是降低了空间复杂度。不过需要注意一点,bitmap 比较适合不重复且有范围(比如,数据均在 0 ~ 10 亿之间)的数据的查询。至于有重复数据的情况,可以考虑与 hash 等结构的混用。 120 |
121 | 122 | ### 4. 使用 hash 123 | 124 | 如果遇到了查询 string 类型数据的大小,可以考虑 hash 方法。 125 | 126 | 举个例子,10 个 string 数字【"1001","23","1002","3003","2001","1111","65","834","5","987"】找最大的 3 个。我们先通过长度进行 hash,得到长度最大为 4,且有 5 个长度为 4 的 string。接下来再通过最高位值做 hash,发现有 1 个最高位为"3"的,1 个为"2"的,3 个为"1"的。接下来,可以通过再设计 hash 函数,或者是循环的方式,在 3 个最高位为"1"的 string 中找到最大的一个,即可找到 3 个最值大的数据。 127 | 128 | 这种方法比较适合网址或者电话号码的查询。缺点就是如果需要多次查询的话,需要多次计算 hash,并且需要根据实际情况设计多个 hash 函数。 129 |
130 | 131 | ### 5. 字典树 132 | 133 | 字典树(trie)的具体结构和查询方式,不在这里赘述了,自行百度一下就有很多。这里主要说一下优缺点。 134 | 135 | 字典树的思想,还是通过前期建立索引信息,后期可以反复多次查询,并且后期增删数据也很方便。比较适合于需要反复多次查询的情况。 136 | 137 | 比如,反复多次查询字符序(例如:z>y>...>b>a)最大的 k 个 url 这种,使用字典树把数据存储一遍,就非常适合。既减少了空间复杂度,也加速了查询效率。 138 |
139 | 140 | ### 6. 混合查询 141 | 142 | 以上几种方法,都是比较独立的方法。其实,在实际工作中,遇到更多的问题还是混合问题,这就需要我们对相关的内容,融会贯通并且做到活学活用。 143 | 144 | 我举个例子:我们的分布式服务跑在 10 台不同机器上,每台机器上部署的服务均被请求 10000 次,并且记录了个这 10000 次请求的耗时(耗时值为 int 数据),找出这 10\*10000 次请求中,从高到低的找出耗时最大的 50 个。看看这个问题,很现实吧。我们试着用上面介绍的方法,组合一下来求解。 145 | 146 | #### 方法一 147 | 148 | 首先,对每台机器上的 10000 个做类似快排,找出每台机器上 top50 的耗时信息。此时,单机上的这 50 条数据是无序的。 149 | 150 | 然后,再将 10 台机器上的 50 条数据(共 500 条)放到一起,再做一次类似快排,找到最大的 50 个(此时应该这 50 个应该是无序的)。 151 | 152 | 最后,对这 50 个数据做快排,从而得到最终结果。 153 | 154 | #### 方法二 155 | 156 | 首先通过堆排,分别找出 10 台机器上耗时最高的 50 个数据,此时的这 50 个数据,已经是从大到小有序的了。 157 | 158 | 然后,我们依次取出 10 台机器中,耗时最高的 5 条放入小顶堆中。 159 | 160 | 最后,遍历 10 台机器上的数据,每台机器从第 6 个数据开始往下循环,如果这个值比堆顶的数据大,则抛掉堆顶数据并且把它加入,继续用下一个值进行同样比较。如果这个值比堆顶的值小,则结束当前循环,并且在下一台机器上做同样操作。 161 | 162 | 以上我介绍了两种方法,并不是为了说明哪种方法更好,或者时间复杂度更低。而是想说同样的事情有多种不同的解决方法,而且随着数据量的增加,可能会需要更多组合形式。在这个领域,数据决定了数据结构,数据结构决定了算法。 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /docs/guide/memoir/1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《面试实录》百度二面,Sychronized 原理详解 3 | category: 4 | - 对线面试官 5 | - 面试实录 6 | - Java 7 | tag: 8 | - Java 9 | - Map 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: 面试实录,Sychronized,JAVA,大厂面试,锁 14 | - name: description 15 | content: 百度二面,Sychronized原理详解。涉及知识点:synchronized加锁原理、锁特性、锁升级过程 16 | --- 17 | 本期是【**面试实录**】系列文章的第**1**期,持续更新中.....。 18 | 19 | - 欢迎关注+订阅,持续更新中!!!致力打造校招核心面试攻略~ 20 | - 根据秋招春招上岸大厂面试经历以及身边朋友**上岸面试录音模拟面试现场**,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 21 | 22 | 【**面试实录**】专栏系列目前已经连载 **1** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 23 | 24 | # 序言 25 | 26 | 大家好,我是小龙。 27 | 28 | 曾几何时,大家是否有这样的困惑? 29 | 30 | 知道相关的知识点,但是面试时面试官**换一种问法**,你便不知道怎样回答,**给你一个场景**,你就联系不到相关的知识点上,或者不知道用对应知识去分析相关问题。 31 | 32 | 比如 "`你知道垂直定理,但是你遇到相关几何题,就是想不到用这个定理去解决问题"`。 33 | 34 | 新的篇章将开启,本系列会`结合小龙面试经历`以及`身边上岸大厂的朋友们的面试经历、面试录音、面试分享`,尽可能**还原面试现场**。 35 | 36 | 带大家**感受面试现场**,**结合情景学会灵活的应用知识**。看一看大佬们都是怎样面试的。 37 | 38 | 记得每期结束,问问自己,你真的对知识掌握了吗? 39 | 40 | # 考题速查 41 | 42 | 本期会通过面试模拟复现`百度提前批二面`,关于 `synchronized` 的考察,以此讲解其原理。 43 | 44 | `本期题改编自 ——2022届秋招 百度 二面` 45 | 46 | # 面试现场 47 | 48 | 叮叮叮...... 49 | 50 | **面试官**:“你好,我是XX面试官,请问是小龙吗?” 51 | 52 | **小龙**:“您好,面试官,我是小龙” 53 | 54 | **面试官**:“好的,现在有空吗,我们开始面试吧” 55 | 56 | **小龙**:“嗯嗯,准备好啦” 57 | 58 | ....... 59 | 60 | other questions 61 | 62 | ....... 63 | 64 | **面试官**:“`synchronized` 分别修饰了一个静态方法和一个实例方法,现在对其并发访问,这是否线程安全?” 65 | 66 | **独白**:“我记不太清原问题咋问的了,反正大概考的知识点就是这个,不是直白的考,而是给你一个实际例子叫你分析” 67 | 68 | **独白**:“当时可能刚开始面试第一个问题还没进入状态,又有点紧张,面试官说话又不清楚,所以一下还没反应过来,卡了几秒,映像比较深刻。” 69 | 70 | **小龙**:“这个当然不安全,**因为访问静态** **`synchronized`** **方法占用的锁是当前类的锁,而访问非静态** **`synchronized`** **方法占用的锁是当前实例对象锁**。” 71 | 72 | **面试官**:“好的,那你知道 `synchronized` 实现加锁的本质原理吗?” 73 | 74 | **独白**:“这个不就是 monitorenter 那两条命令吗,拿捏~” 75 | 76 | **小龙**:“如果 synchronized 修饰代码块,javac 编译时,会在代码块前后生成 `monitorenter` 和 `monitorexit` ,当执行遇到 monitorenter指令便会去尝试获取锁。” 77 | 78 | **独白**:“以为拿捏了,为了让面试官觉得我理解的很透彻,再补充了一点。” 79 | 80 | **小龙**:“使用 synchronized 在出现异常时,还可以保证锁的释放。因为它还会隐式的加一个 try-finnaly,finnaly 中也有 monitorexit 命令以便出现异常可以释放锁。” 81 | 82 | **面试官**:“好的,你说的这些没问题。那你知道当执行到 monitorenter 指令时,它是怎样去尝试获取锁的吗?这个锁究竟是啥?你还是没说明白呢,哈哈。” 83 | 84 | **独白**:“原来想问 monitor 噢,还是很基础的。” 85 | 86 | **小龙**:“其实追根朔源,每个对象都有一个 `monitor` 与之关联,而当且一个 monitor 被持有后,它便处于锁定状态啦,而线程执行到 monitorenter 指令时,便会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁。” 87 | 88 | > Moniter=WaitSet+EntryList(当锁被占用,其他线程来就会进入阻塞队列,等锁释放再一起竞争)+Owner(指向持有锁的线程) 89 | 90 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803291.png) 91 | 92 | **面试官**:“真的是这样吗?还有吗?” 93 | 94 | **小龙**:"你别急嘛,我还没说完,上面说法其实不完全正确,那是针对重量级锁。" 95 | 96 | **小龙**:“如果是 synchronized 没被优化之前,它是重量级锁,仅依赖对象对应的 moniter,但是后面进行了优化。” 97 | 98 | **面试官**:“噢,展开说说。” 99 | 100 | **小龙**:“我们的对象总的来说是由 **对象头、实例数据、对齐填充**构成,而对象头里面就存了 `Mark Word`,Mark Word 里面存了对象自身的一些运行信息,比如:`hashcode、GC分代年龄、锁状态标志、持有的锁`。” 101 | 102 | **小龙**:“若 synchronized 给该对象加锁后,那么该对象头的 Mark Word 就会发生相应的变化,优化后的 synchronized 会迎合不同场景升级锁,随着锁升级,这个变化也不同。” 103 | 104 | **小龙**:“偏向锁依赖 `当前线程ID`,重量级锁依赖 `monitor` ,轻量级锁依赖 `锁记录lock-record`。” 105 | 106 | **面试官**:“好的,你说详细说一下**锁的升级过程**吗?” 107 | 108 | **小龙**:“synchronized 总的升级流程是这样:**无锁 ----> 偏向锁 ----> 轻量级锁----> 锁自旋 ----> 重量级锁**。” 109 | 110 | `偏向锁` 111 | 112 | **小龙**:“首先会判断Mark Word里面是否有当前线程Id,若有则处于偏向锁,若无则尝试用 CAS 将 Mark Word 替换为线程Id,若成功则偏向锁设置成功,失败则有竞争要升级成轻量级锁。” 113 | 114 | `轻量级锁` 115 | 116 | **小龙**:“而对于轻量级锁里面涉及的就更复杂,详细展开说就是,开始会创建锁记录(Lock Record)对象,然后我们每个线程的栈帧都会包含一个`锁记录`的结构,内部可以用来存储锁定对象的 Mark Word;” 117 | 118 | > 锁记录(Lock Record)包含了 lock record 地址 00、Object reference 119 | > 120 | > 对象(Obejct)组成上面说过了,此处不再赘述 121 | 122 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803227.png) 123 | 124 | **小龙**:“然后让锁记录中 `Object reference` 指向锁对象,并尝试用 CAS(原子操作)替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录;” 125 | 126 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803174.png) 127 | 128 | **小龙**:“如果 CAS 替换成功,对象头中存储了`锁记录地址和状态00`,表示由该线程给对象加锁;” 129 | 130 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803683.png) 131 | 132 | **面试官**:“那如果失败又是怎样处理的呢?” 133 | 134 | **小龙**:“如果cas失败,有两种情况:” 135 | 136 | - 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程(也就是升级成重量级锁---monitor) 137 | - 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数。 138 | 139 | > 同一线程对同一对象加了多次锁--锁重入 140 | 141 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803195.png) 142 | 143 | **小龙**:“当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录表示重入计数减一;” 144 | 145 | **小龙**:“当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 CAS 将 Mark Word 的值恢复给对象头(也就是将之前锁记录和对象CAS替换的部分又替换回来,换回原来各自的);” 146 | 147 | **小龙**:“若上面操作成功,则解锁成功;失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程。” 148 | 149 | `重量级锁` 150 | 151 | **面试官**:“那重量级锁又是怎么回事呢?” 152 | 153 | **小龙**:“如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。” 154 | 155 | **小龙**:“当 T1 进行轻量级加锁时,T0 已经对该对象加了轻量级锁。” 156 | 157 | **小龙**:“这时 T1 加轻量级锁失败,进入锁膨胀流程 (因为 T1 加锁失败,被 T0 占了,但是 T1 不能在这干耗着啊,于是进入锁膨胀,申请一个monitor 去阻塞,升级成重量级锁--这时才有阻塞)” 158 | 159 | **小龙**:“实际上就是为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址。然后自己进入 Monitor 的 EntryList BLOCKED ” 160 | 161 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051803885.png) 162 | 163 | **小龙**:“当 T0 退出同步块解锁时,使用 `CAS` 将 Mark Word 的值恢复给对象头,失败(`因为object mark-word 里放的是 monitor对象的地址了,不是T0 的 Lock Recode里面那个地址了`)。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner为 null,唤醒 EntryList 中 BLOCKED 线程。” 164 | 165 | **面试官**:“给个大大的赞,继续加油!” 166 | 167 | # 知识总结 168 | 169 | 本期我们通过面试模拟深入探讨了 `synchronized` 的底层实现原理,下期再见。**订阅+关注** 持续追更。 170 | 171 | ## **面试重点** 172 | 173 | `synchronized加锁原理`、`锁特性`、 `锁升级过程` 174 | 175 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/6-spring-three-cache.md: -------------------------------------------------------------------------------- 1 | # Spring 为何需要三级缓存解决循环依赖,而不是二级缓存? 2 | 3 | ## **前言** 4 | 5 | 在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决循环依赖问题,对我们开发者来说是无感知的,下面具体分析一下spring是如何解决bean之间循环依赖,为什么要使用到三级缓存,而不是二级缓存? 6 | 7 | ## **bean生命周期** 8 | 9 | 首先大家需要了解一下bean在spring中的生命周期,bean在spring的加载流程,才能够更加清晰知道spring是如何解决循环依赖的。 10 | 11 | ![](http://img.topjavaer.cn/img/三级依赖1.png) 12 | 13 | 我们在spring的BeanFactory工厂列举了很多接口,代表着bean的生命周期,我们主要记住的是我圈红线圈出来的接口, 再结合spring的源码来看这些接口主要是在哪里调用的 14 | 15 | ![](http://img.topjavaer.cn/img/三级依赖2.png) 16 | 17 | AbstractAutowireCapableBeanFactory类的doCreateBean方法是创建bean的开始,我们可以看到首先需要实例化这个bean,也就是在堆中开辟一块内存空间给这个对象,createBeanInstance方法里面逻辑大概就是采用反射生成实例对象,进行到这里表示对象还并未进行属性的填充,也就是@Autowired注解的属性还未得到注入 18 | 19 | ![](http://img.topjavaer.cn/img/三级依赖3.png) 20 | 21 | 我们可以看到第二步就是填充bean的成员属性,populateBean方法里面的逻辑大致就是对使用到了注入属性的注解就会进行注入,如果在注入的过程发现注入的对象还没生成,则会跑去生产要注入的对象,第三步就是调用initializeBean方法初始化bean,也就是调用我们上述所提到的接口 22 | 23 | ![](http://img.topjavaer.cn/img/三级缓存4.png) 24 | 25 | 可以看到initializeBean方法中,首先调用的是使用的Aware接口的方法,我们具体看一下invokeAwareMethods方法中会调用Aware接口的那些方法 26 | 27 | ![](http://img.topjavaer.cn/img/三级缓存5.png) 28 | 29 | 我们可以知道如果我们实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware三个Aware接口的话,会依次调用setBeanName(), setBeanClassLoader(), setBeanFactory()方法,再看applyBeanPostProcessorsBeforeInitialization源码 30 | 31 | ![](http://img.topjavaer.cn/img/三级缓存6.png) 32 | 33 | 发现会如果有类实现了BeanPostProcessor接口,就会执行postProcessBeforeInitialization方法,这里需要注意的是:如果多个类实现BeanPostProcessor接口,那么多个实现类都会执行postProcessBeforeInitialization方法,可以看到是for循环依次执行的,还有一个注意的点就是如果加载A类到spring容器中,A类也重写了BeanPostProcessor接口的postProcessBeforeInitialization方法,这时要注意A类的postProcessBeforeInitialization方法并不会得到执行,因为A类还未加载完成,还未完全放到spring的singletonObjects一级缓存中。 34 | 35 | 再看一个注意的点 36 | 37 | ![](http://img.topjavaer.cn/img/三级缓存7.png) 38 | 39 | ![](http://img.topjavaer.cn/img/三级缓存8.png) 40 | 41 | 可以看到ApplicationContextAwareProcessor也实现了BeanPostProcessor接口,重写了postProcessBeforeInitialization方法,方法里面并调用了invokeAwareInterfaces方法,而invokeAwareInterfaces方法也写着如果实现了众多的Aware接口,则会依次执行相应的方法,值得注意的是ApplicationContextAware接口的setApplicationContext方法,再看一下invokeInitMethods源码 42 | 43 | ![](http://img.topjavaer.cn/img/三级缓存9.png) 44 | 45 | 发现如果实现了InitializingBean接口,重写了afterPropertiesSet方法,则会调用afterPropertiesSet方法,最后还会调用是否指定了init-method,可以通过标签,或者@Bean注解的initMethod指定,最后再看一张applyBeanPostProcessorsAfterInitialization源码图 46 | 47 | ![](http://img.topjavaer.cn/img/三级缓存10.png) 48 | 49 | 发现跟之前的postProcessBeforeInitialization方法类似,也是循环遍历实现了BeanPostProcessor的接口实现类,执行postProcessAfterInitialization方法。整个bean的生命执行流程就如上面截图所示,哪个接口的方法在哪里被调用,方法的执行流程。 50 | 51 | 最后,对bean的生命流程进行一个流程图的总结 52 | 53 | ![](http://img.topjavaer.cn/img/三级缓存11.png) 54 | 55 | ## **三级缓存解决循环依赖** 56 | 57 | 上一小节对bean的生命周期做了一个整体的流程分析,对spring如何去解决循环依赖的很有帮助。前面我们分析到填充属性时,如果发现属性还未在spring中生成,则会跑去生成属性对象实例。 58 | 59 | ![](http://img.topjavaer.cn/img/三级缓存12.png) 60 | 61 | 我们可以看到填充属性的时候,spring会提前将已经实例化的bean通过ObjectFactory半成品暴露出去,为什么称为半成品是因为这时候的bean对象实例化,但是未进行属性填充,是一个不完整的bean实例对象 62 | 63 | ![](http://img.topjavaer.cn/img/三级缓存13.png) 64 | 65 | spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的,所说的缓存其实也就是三个Map 66 | 67 | ![](http://img.topjavaer.cn/img/三级缓存14.png) 68 | 69 | 可以看到三级缓存各自保存的对象,这里重点关注二级缓存earlySingletonObjects和三级缓存singletonFactory,一级缓存可以进行忽略。前面我们讲过先实例化的bean会通过ObjectFactory半成品提前暴露在三级缓存中 70 | 71 | ![](http://img.topjavaer.cn/img/三级缓存15.png) 72 | 73 | singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法。再来看看循环依赖中是怎么拿其它半成品的实例对象的。 74 | 75 | 我们假设现在有这样的场景AService依赖BService,BService依赖AService 76 | 77 | \1. AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中 78 | 79 | \2. 填充属性BService,发现BService还未进行过加载,就会先去加载BService 80 | 81 | \3. 再加载BService的过程中,实例化,也通过ObjectFactory半成品暴露在三级缓存 82 | 83 | \4. 填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory 84 | 85 | ![](http://img.topjavaer.cn/img/三级缓存16.png) 86 | 87 | 拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例。 88 | 89 | 这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。 90 | 91 | 下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题??? 92 | 93 | 只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景 94 | 95 | ![](http://img.topjavaer.cn/img/三级缓存17.png) 96 | 97 | 我们发现AService的testAopProxy被AOP代理了,看看传入的匿名内部类的getEarlyBeanReference返回的是什么对象。 98 | 99 | ![](http://img.topjavaer.cn/img/三级缓存18.png) 100 | 101 | 发现singletonFactory.getObject()返回的是一个AService的代理对象,还是被CGLIB代理的。再看一张再执行一遍singletonFactory.getObject()返回的是否是同一个AService的代理对象 102 | 103 | ![](http://img.topjavaer.cn/img/三级缓存19.png) 104 | 105 | 我们会发现再执行一遍singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象。 106 | 107 | 假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。还有一个注意的点 108 | 109 | ![](http://img.topjavaer.cn/img/三级缓存20.png) 110 | 111 | 既然singleFactory.getObject()返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过CGLIB代理的AService对象。所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象 112 | 113 | ## **总结** 114 | 115 | 前面先讲到bean的加载流程,了解了bean加载流程对spring如何解决循环依赖的问题很有帮助,后面再分析到spring为什么需要利用到三级缓存解决循环依赖问题,而不是二级缓存。网上可以试试AOP的情形,实践一下就能明白二级缓存为什么解决不了AOP代理的场景了 116 | 117 | 在工作中,一直认为编程代码不是最重要的,重要的是在工作中所养成的编程思维。 118 | 119 | 原文:cnblogs.com/semi-sub/p/13548479.html -------------------------------------------------------------------------------- /docs/navigation/projects/0-work-projects.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 一期 | 优质面试求职开源项目 3 | category: 4 | - 开源项目 5 | - Java项目 6 | - Java经典项目 7 | - Java2Top 8 | - 求职项目 9 | icon: 10 | 11 | --- 12 | 13 | > Java2Top,校招面试求职,大厂学习导航~ 14 | > 15 | > 致力于打造全网最佳大厂学习进阶平台! 16 | 17 | 对于校招求职,关于项目这块是特别重要的,可以说项目间接决定了你的生死。在此前的文章介绍了可以从 Github 等找寻一个适合自己的开源项目,但是网上项目资源满天飞,去找到一款可以让面试官眼前一亮的项目还是有点困难。 18 | 19 | **本站会根据当下面试热点,技术潮流持续收集适合用于《校招面试求职》的精品项目,和人手一个的秒杀,商城,管理系统说拜拜吧,您~。** 20 | 21 | ## 求职面试推荐 22 | 23 | ### 1、从零实现一个操作系统内核 24 | 25 | 对于编程学习来说, 学习操作系统有助于我们了解计算机的工作原理。操作系统中的很多思想、很多经典的算法,你都可以在我们日常开发使用的各种工具或者框架中找到它们的影子。 26 | 27 | ![img](https://camo.githubusercontent.com/0cb75538b5ea92e01bafbaaa6c94f9f10910823731c0e896ae6d6140e0eb0c00/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f30303833317253546c793167646c366a38627877376a333137733075307464392e6a7067) 28 | 29 | 在学校我们老师也会叫我们写一个简单的操作系统来体会其中的原理。 30 | 31 | 如果你能够自己独立写一个操作系统内核的话,即使是简易的实现,我相信也能够为自己的简历加分不少。 32 | 33 | > 项目地址:https://github.com/Simple-XX/SimpleKernel 34 | 35 | ### 2、proxyee-down(Http下载器) 36 | 37 | `Proxyee Down` 是一款开源的免费 HTTP 高速下载器,底层使用`netty`开发,支持自定义 HTTP 请求下载且支持扩展功能,可以通过安装扩展实现特殊的下载需求。 38 | 39 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202304231414330.jpeg) 40 | 41 | 本项目后端主要使用 `java` + `spring` + `boot` + `netty`,前端使用 `vue.js` + `iview` 42 | 43 | > 项目地址:https://github.com/proxyee-down-org/proxyee-down 44 | 45 | ### 3、基于人工智能的智慧校园助手 46 | 47 | 本项目旨在为同城高校学子提供一个集校园服务,商城服务,二手交易,智能问答,消息推送,动态分享功能于一体的综合性智慧服务平台 48 | 49 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304182303253.jpg) 50 | 51 | 技术栈:SpringBoot+SpringCloud+Redis+Kafka+XXL-Job+Vue.js+其他第三方 SDK 等 52 | 53 | - 将校内服务与百度地图结合实现信息视觉呈现;采用人脸识别打造安全机制,通过模型训练数据采集提供了智能系统问 54 | 55 | 答服务 56 | 57 | - 对系统慢 SQL 进行优化,使得系统性能大幅度提高。 58 | 59 | - 用 Redis 存储登录 ticket 和验证码,解决分布式 session 问题 60 | 61 | - 定义热点数据并缓存在 Redis,降低了数据库访问压力 62 | 63 | - 将校内服务与百度地图结合实现信息视觉呈现;采用人脸识别打造安全机制,通过模型训练数据采集提供了智能系统问 64 | 65 | 答服务 66 | 67 | - 对热帖排行模块,使用分布式缓存 Redis 和本地缓存 Caffeine 作为多级缓存,避免了缓存雪崩,将 QPS 提升了20倍 68 | 69 | (10-200),大大提升了网站访问速度。并使用 Quartz 定时更新热帖排行 70 | 71 | - 利用 JVM 指令排查出 GC 问题,调整 JVM 配置,降低 GC 次数使 72 | 73 | - 用 Kafka 打造强大的异步消息系统 74 | 75 | version1.0 可以公众号后台回复【**基于人工智能的智慧校园助手**】 76 | 77 | 后期迭代升级更新优化 会发布 github 持续关注,第一时间接收通知。 78 | 79 | > 项目演示:https://www.bilibili.com/video/BV1XT4y1w7Xc 80 | 81 | ### 4、一个手把手教你造轮子的项目 82 | 83 | 通过这个项目,你能学会如何创造自己的操作系统、编程语言、搜索引擎、框架,工具库…… 84 | 85 | 这个项目聚集了很多车轮子项目。 86 | 87 | **构建一个简易Doker** 88 | 89 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303201054928.png) 90 | 91 | > https://github.com/danistefanovic/build-your-own-x#build-your-own-docker 92 | 93 | **构建一个简易数据库** 94 | 95 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303201054095.png) 96 | 97 | > https://github.com/danistefanovic/build-your-own-x#build-your-own-database 98 | 99 | ......... 100 | 101 | **整个仓库地址** 102 | 103 | > 项目地址:https://github.com/danistefanovic/build-your-own-x 104 | 105 | ### 5、牛客论坛 106 | 107 | 一个仿照牛客网实现的讨论社区,不仅实现了基本的注册,登录,发帖,评论,点赞,回复功能,同时使用前缀树实现敏感词过滤,使用wkhtmltopdf 生成长图和 PDF,实现网站 UV 和 DAU 统计,并将用户头像等信息存于七牛云服务器。 108 | 109 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java202304231453823.png) 110 | 111 | **功能介绍** 112 | 113 | - 使用 Spring Security 做权限控制,替代拦截器的拦截控制,并使用自己的认证方案替代Security 认证流程,使权限认证和控制更加方便灵活。 114 | - 使用 Redis 的 set 实现点赞,zset 实现关注,并使用 Redis 存储登录 ticket 和验证码,解决分布式 session 问题。 115 | - 使用 Redis 高级数据类型 HyperLogLog 统计 UV(Unique Visitor), 使用 Bitmap 统计 DAU (Daily Active User)。 116 | - 使用 Kafka 处理发送评论、点赞和关注等系统通知,并使用事件进行封装,构建了强大的异步消息系统。 117 | - 使用 Elasticsearch 做全局搜索,并通过事件封装,增加关键词高亮显示等功能。 118 | - 对热帖排行模块,使用分布式缓存 Redis 和本地缓存 Caffeine 作为多级缓存,避免了缓存雪崩,将 QPS 提升了 20倍(10-200),大大提升了网站访问速度。并使用 Quartz 定时更新热帖排行。 119 | 120 | > 项目地址:https://www.nowcoder.com/study/live/246 121 | 122 | ## 其他学习项目 123 | 124 | 这里收集的一些开源项目也可以看看~ 125 | 126 | **电商类** 127 | 128 | - 天猫整站 Springboot:https://how2j.cn/module/156.html 129 | - mall-learning:https://github.com/macrozheng/mall-learningmall(学习教程,架构、业务、技术要点全方位解析) 130 | - miaosha:https://github.com/qiurunze123/miaosha (秒杀系统设计与实现) 131 | - mall-swarm:https://github.com/macrozheng/mall-swarm(一套微服务商城系统,采用了 Spring Cloud Greenwich、Spring Boot 2、MyBatis、Docker、Elasticsearch 等核心技术,同时提供了基于 Vue 的管理后台方便快速搭建系统) 132 | 133 | **管理系统类** 134 | 135 | - [Spring-Cloud-Admin](https://github.com/wxiaoqi/Spring-Cloud-Admin):Cloud-Admin 是国内首个基于 Spring Cloud 微服务化开发平台,具有统一授权、认证后台管理系统,其中包含具备用户管理、资源权限管理、网关 API 管理等多个模块,支持多业务系统并行开发,可以作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。核心技术采用 Spring Boot2 以及 Spring Cloud Gateway 相关核心组件,前端采用 vue-element-admin 组件。 136 | - [pig](https://gitee.com/log4j/pig):基于 Spring Boot 2.2、 Spring Cloud Hoxton & Alibaba、 OAuth2 的 RBAC 权限管理系统。 137 | - [悟空CRM](https://github.com/72crm/72crm-java):基于jfinal+vue+ElementUI的前后端分离CRM系统 138 | 139 | **博客类** 140 | 141 | - [favorites-web](https://github.com/cloudfavorites/favorites-web):云收藏 Spring Boot 2.X 开源项目。云收藏是一个使用 Spring Boot 构建的开源网站,可以让用户在线随时随地收藏的一个网站,在网站上分类整理收藏的网站或者文章。 142 | - [community](https://github.com/codedrinker/community):码问,开源论坛、问答系统,现有功能提问、回复、通知、最新、最热、消除零回复功能。技术栈 Spring、Spring Boot、MyBatis、MySQL/H2、Bootstrap 143 | - [vhr](https://github.com/lenve/vhr):微人事是一个前后端分离的人力资源管理系统,项目采用 SpringBoot+Vue 开发。 144 | 145 | **其他** 146 | 147 | - [PassJava-Platform](https://github.com/Jackson0714/PassJava-Platform):一款面试刷题的 Spring Cloud 开源系统 148 | - [moti-cloud](https://github.com/373675032/moti-cloud):莫提网盘,基于 SpringBoot+MyBatis+ThymeLeaf+BootStrap,适合初学者 149 | - [threadandjuc](https://github.com/qiurunze123/threadandjuc):three-high-import 高可用\高可靠\高性能,三高多线程导入系统(该项目意义为理论贯通) 150 | 151 | ## 篇末 152 | 153 | 我个人觉得,现在这个状况,相比于做分布式什么的,做一些偏基础或者车轮子(**体会计算机运行原理,体会先人思想**)的项目可能反而让面试官眼前一面,只是个人看法,每个人根据自己的想法去找到一个适合自己项目认真准备就可以。 154 | 155 | -------------------------------------------------------------------------------- /docs/guide/memoir/3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《面试实录》 字节二面, MySQL自增主键一定连续吗? 3 | category: 4 | - 对线面试官 5 | - 面试实录 6 | - Java 7 | tag: 8 | - Java 9 | - Map 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: 面试实录,集合,JAVA,大厂面试,Map 14 | - name: description 15 | content: 阿里一面面试现场,深剖Java Map底层结构与原理。涉及知识点:Map 常使用实现类使用场景,特性;Hash算法;HashMap原 理剖析;分段锁;ConcurrentHashMap; 16 | 17 | --- 18 | 19 | 本期是【**面试实录**】系列文章的第 **3** 期,持续更新中.....。 20 | 21 | - 欢迎关注+订阅,持续更新中!!!致力打造校招核心面试攻略~ 22 | - 根据秋招春招上岸大厂面试经历以及身边朋友上岸面试录音模拟面试现场,并整合面试常考知识点,通俗有趣的去讲解 `八股文`,不一样的系列,轻松掌握知识~ 23 | 24 | 【**面试实录**】专栏系列目前已经连载 3 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 25 | 26 | - [【面试实录】百度二面,Sychronized原理详解](./1) 27 | - [【面试实录】字节二面,SQL执行慢的原因?如何优化?](./2) 28 | 29 | # 考题速查 30 | 31 | 本期会模拟面试 MySQL 相关内容。 32 | 33 | 涉及知识点,MySQL 自增主键的理解,关于 `自增值修改机制` 、`自增值修改时机`与 `自增值新增机制` 。 34 | 35 | `本期题改编自 ——2022届春招 阿里蚂蚁金服 二面` 36 | 37 | # 面试现场 38 | 39 | 叮叮叮...... 40 | 41 | **面试官**:“你好,我是XX面试官,请问是小龙吗?” 42 | 43 | **小龙**:“您好,面试官,我是小龙” 44 | 45 | **面试官**:“好的,现在有空吗,我们开始面试吧” 46 | 47 | **小龙**:“嗯嗯,准备好啦” 48 | 49 | ....... 50 | 51 | other questions 52 | 53 | ....... 54 | 55 | **面试官**:“我看你简历上有提到你对 MySQL 掌握得挺好的对吧?。” 56 | 57 | **小龙**:“哈哈,还算可以吧!” 58 | 59 | **面试官**:“好的,那平时对于 MySQL `主键`你一般怎样用的呢?能简单聊聊吗?” 60 | 61 | **独白**:“嘿嘿,面试官肯定又想问 InnoDB 引擎索引特性相关知识,幸好早烂熟于心了!” 62 | 63 | **小龙**:“平时主键我一般用自增主键!因为自增 ID 有序,会按顺序往最后插入,而 UUID 无序,随机生成,随机插入,会造成频繁页分裂,内存碎片化,大量随机 IO,巴拉巴拉。。。。。” 64 | 65 | **独白**:“这波稳啦![手动狗头] 😎” 66 | 67 | 然后,面试官又来一套组合拳。 68 | 69 | **面试官**:“en!好,那你知道`自增主键是否严格递增`呢?” 70 | 71 | **独白**:“这还不简单,肯定递增啊!不对,仔细一想,有一些情况下自增主键是断开的。” 72 | 73 | **小龙**:“肯定不是递增的啊!” 74 | 75 | 以为到这里结束啦,只是考考我是否实际真正用过,没想到还是天真啦。。。 76 | 77 | **面试官**:“那你知道为啥不是严格递增的吗?换句话来说,`为何不是连续的`?” 78 | 79 | **小龙**:“裂开!还真不知道,不会真要考那么底层吧!我去” 80 | 81 | **面试官**:“回去等通知吧。。。” 82 | 83 | ....... 84 | 85 | 哈哈,开玩笑的,接下来正式开始表演~ 86 | 87 | ....... 88 | 89 | **小龙**:“我们都知道,由于自增主键可以让主键索引尽量地`保持递增顺序插入`,避免了页分裂,大量的随机 IO。” 90 | 91 | **面试官**:“嗯嗯” 92 | 93 | **小龙**:“至于`为何自增主键不是严格递增的`?为何在某些时候会出现 `断层` ,我们可以创建一个表 xl_tb 来仔细剖析一下。” 94 | 95 | **小龙**:“其中 id 是自增主键字段、a 是唯一索引,如下表:” 96 | 97 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051818616.png) 98 | 99 | **小龙**:“然后插入一条数据:” 100 | 101 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051818987.png) 102 | 103 | **小龙**:“我们再查看它的表结构:” 104 | 105 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051818783.png) 106 | 107 | **小龙**:“可以看到,表定义里面出现了一个 AUTO_INCREMENT=4,表示下一次插入数据时,如果需要自动生成自增值,会生成 id=4 。” 108 | 109 | **小龙**:“这是我亲自实验过的” 110 | 111 | **独白**:“凸显自己动手实践能力,不是简单背八股(还真做过实验哈~)” 112 | 113 | **小龙**:“看到这里,你可能会问`自增值存在表结构里吗`?当然不是哈” 114 | 115 | **面试官**:“哈哈,那你说说看呢!” 116 | 117 | **小龙**:“这个嘛,还得看是什么引擎。不同的存储引擎,自增值保存策略不一样的。” 118 | 119 | **小龙**:“ MyISAM 引擎的自增值保存在数据文件中。” 120 | 121 | **面试官**:“嗯,那 InnoDB 呢?” 122 | 123 | **小龙**:“ InnoDB 引擎的自增值,其实是保存在了内存里,并且到了MySQL 8.0版本后,才有了 `自增值持久化` 的能力,也就是才实现了 `如果发生重启,表的自增值可以恢复为 MySQL 重启前的值`。” 124 | 125 | **面试官**:“能展开说说吗?” 126 | 127 | **小龙**:“好,在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值 max(id),然后将 max(id)+1 作为这个表当前的自增值。” 128 | 129 | **小龙**:“举例来说,如果一个表当前数据行里最大的 id 是 10,AUTO_INCREMENT=11。这时候,我们删除 id=10 的行,AUTO_INCREMENT 还是 11。但如果马上重启实例,重启后这个表的 AUTO_INCREMENT 就会变成 10。” 130 | 131 | **小龙**:“也就是说,MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值。” 132 | 133 | **小龙**:“在 MySQL 8.0 版本,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值” 134 | 135 | **面试官**:“好的,你能再给我讲讲关于自增值是怎样修改的吗?简单来说,当你插入一条数据,自增值会做出怎样变化?” 136 | 137 | **小龙**:“如果插入数据时 id 字段指定为 0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段。” 138 | 139 | **面试官**:“那如果 id 指定了具体值呢?” 140 | 141 | **小龙**:“ 如果插入数据时 id 字段指定了具体的值,id 就直接使用语句里指定的值 ” 142 | 143 | **小龙**:“如果准备插入的值 >= 当前自增值,新的自增值就是 `准备插入的值 +1,否则,自增值不变`” 144 | 145 | **面试官**:“你能举个具体例子吗?不太理解” 146 | 147 | **小龙**:“假设,表 xl_tb 里面已经有了(1,1,1)这条记录,这时我再执行一条插入数据命令:” 148 | 149 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051818129.png) 150 | 151 | **小龙**:“这个语句的执行流程就是:1、执行器调用 InnoDB 引擎接口写入一行,传入的这一行的值是 (0,1,1); 2、InnoDB发现用户没有指定自增 id 的值,获取表 xl_tb 当前的自增值 ;” 152 | 153 | **小龙**:“3、将传入的行的值改成 (4,1,1);4、将表的自增值改成5;继续执行插入数据操作,由于已经存在a=1的记录,所以报 Duplicate key error,语句返回。” 154 | 155 | **小龙**:“这个表的自增值改成 5,是在真正执行插入数据的操作之前。这个语句真正执行的时候,因为 **碰到唯一键a冲突**,所以 id=2 这一行并没有插入成功,但也没有将自增值再改回去。” 156 | 157 | **小龙**:“所以,在这之后,再插入新的数据行时,拿到的自增id就是5。也就是说,出现了自增主键不连续的情况。因此,**唯一键冲突是导致自增主键id不连续的第一种原因。**” 158 | 159 | **小龙**:“同样地,事务**回滚也会产生类似的现象,这就是第二种原因。**” 160 | 161 | **面试官**:“照你这样说,那为什么在出现唯一键冲突或者回滚的时候,MySQL 没有把表 xl_tb 的自增值改回去呢? 162 | 163 | **面试官**:“如果把表 xl_tb 的当前自增值从 5 改回 4,再插入新数据的时候,不就可以生成 id=2 的一行数据了吗?” 164 | 165 | **小龙**:“这个很好理解啊,我们接着分析” 166 | 167 | **小龙**:“首先,我们假设有两个并行执行的事务 A、B,在申请自增值的时候,为了避免两个事务申请到相同的自增 id,肯定要加锁,然后顺序申请。” 168 | 169 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/java/k202304051818430.png) 170 | 171 | **小龙**:“结合上图。首先,事务A申请到 id=2,此时当前自增值为 3,由于加锁顺序申请,事务 B 申请到 id=3(当前自增值),此时,当前自增值变为 3+1=4 ” 172 | 173 | **小龙**:“然后,事务 A、B都插入,假设事务B先插入然后成功插入,然后事务A插入发生了**唯一键冲突**” 174 | 175 | **小龙**:“如果假设允许**自增值后退,自增值就变为 2** 啦。假如事务A继续插入,申请到 id=2(当前自增值=2),成功插入;事务A继续申请id,申请到 id=3(之前自增值2+1=3),插入。由于之前事务 B 已经插入 id=3 的数据,此时发生 **主键冲突。**” 176 | 177 | **小龙**:“如果要解决这个问题” 178 | 179 | **小龙**:“`可以每次申请id之前,先判断表里面是否已经存在这个id` 或则 `扩大锁范围,必须等事务执行完,才能申请下一个`” 180 | 181 | **小龙**:“虽然这两种方法可以解决,但是无疑`性能极低`。” 182 | 183 | **小龙**:“于是,便让自增值不能回退,而避免造成主键冲突等问题。(也许还有其他问题我没有想到)。” 184 | 185 | **面试官**:“好的,已经可以啦。顺带问一句,这些你是哪里看到的?” 186 | 187 | **小龙**:“我平时有看很多付费专栏,书籍之类的,最主要还是官方文档~” 188 | 189 | **面试官**:“可以可以,继续加油!” 190 | 191 | # 知识总结 192 | 193 | 本期我们通过面试模拟简单介绍了 MySQL 关于自增主键的理解,通过例子重点剖析了 `自增值修改机制` 、`自增值修改时机`与 `自增值新增机制` 。下期再见,**订阅专栏+关注** 持续追更。 194 | 195 | **面试重点** 196 | 197 | `为什么自增主键不连续?` 198 | 199 | - 在MySQL 5.7及之前的版本,自增值保存在内存里,并没有持久化 200 | - 事务回滚(自增值不能回退,因为并发插入数据时,回退自增ID可能造成主键冲突) 201 | - 唯一键冲突(由于表的自增值已变,但是主键发生冲突没插进去,下一次插入主键=现在变了的子增值+1,所以不连续) -------------------------------------------------------------------------------- /docs/guide/java/distributed/4-micro-service.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 统计不同号码的个数 4 | --- 5 | 6 | # 微服务 7 | 8 | ## 什么是微服务? 9 | 10 | 微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务和服务之间采用轻量级的通信机制进行协作。每个服务可以被独立的部署到生产环境。 11 | 12 | [从单体应用到微服务](https://www.zhihu.com/question/65502802/answer/802678798) 13 | 14 | 单体系统的缺点: 15 | 16 | 1. 修改一个小功能,就需要将整个系统重新部署上线,影响其他功能的运行; 17 | 2. 功能模块互相依赖,强耦合,扩展困难。如果出现性能瓶颈,需要对整体应用进行升级,虽然影响性能的可能只是其中一个小模块; 18 | 19 | 单体系统的优点: 20 | 21 | 1. 容易部署,程序单一,不存在分布式集群的复杂部署环境; 22 | 2. 容易测试,没有复杂的服务调用关系。 23 | 24 | 微服务的**优点**: 25 | 26 | 1. 不同的服务可以使用不同的技术; 27 | 2. 隔离性。一个服务不可用不会导致其他服务不可用; 28 | 3. 可扩展性。某个服务出现性能瓶颈,只需对此服务进行升级即可; 29 | 4. 简化部署。服务的部署是独立的,哪个服务出现问题,只需对此服务进行修改重新部署; 30 | 31 | 微服务的**缺点**: 32 | 33 | 1. 网络调用频繁。性能相对函数调用较差。 34 | 2. 运维成本增加。系统由多个独立运行的微服务构成,需要设计一个良好的监控系统对各个微服务的运行状态进行监控。 35 | 36 | 37 | 38 | ## 分布式和微服务的区别 39 | 40 | 从概念理解,分布式服务架构强调的是服务化以及服务的**分散化**,微服务则更强调服务的**专业化和精细分工**; 41 | 42 | 从实践的角度来看,**微服务架构通常是分布式服务架构**,反之则未必成立。 43 | 44 | 一句话概括:分布式:分散部署;微服务:分散能力。 45 | 46 | 47 | 48 | ## 服务怎么划分? 49 | 50 | 横向拆分:按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。 51 | 52 | 纵向拆分:把一个业务功能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,形成相对独立的原子服务层。这样一纵一横,就可以实现业务的服务化拆分。 53 | 54 | 要做好微服务的分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求 55 | 56 | 总之,微服务的设计一定要 **渐进式** 的,总的原则是 **服务内部高内聚,服务之间低耦合。** 57 | 58 | ## 微服务设计原则 59 | **单一职责原则** 60 | 61 | 意思是每个微服务只需要实现自己的业务逻辑就可以了,比如订单管理模块,它只需要处理订单的业务逻辑就可以了,其它的不必考虑。 62 | 63 | **服务自治原则** 64 | 65 | 意思是每个微服务从开发、测试、运维等都是独立的,包括存储的数据库也都是独立的,自己就有一套完整的流程,我们完全可以把它当成一个项目来对待。不必依赖于其它模块。 66 | 67 | **轻量级通信原则** 68 | 69 | 首先是通信的语言非常的轻量,第二,该通信方式需要是跨语言、跨平台的,之所以要跨平台、跨语言就是为了让每个微服务都有足够的独立性,可以不受技术的钳制。 70 | 71 | **接口明确原则** 72 | 73 | 由于微服务之间可能存在着调用关系,为了尽量避免以后由于某个微服务的接口变化而导致其它微服务都做调整,在设计之初就要考虑到所有情况,让接口尽量做的更通用,更灵活,从而尽量避免其它模块也做调整。 74 | 75 | 76 | 77 | ## 微服务之间是如何通讯的? 78 | 79 | **1、RPC** 80 | 81 | 优点:简单,常见。因为没有中间件代理,系统更简单 82 | 83 | 缺点: 84 | 85 | 1. 只支持请求/响应的模式,不支持别的,比如通知、发布/订阅 86 | 2. 降低了可用性,因为客户端和服务端在请求过程中必须都是可用的 87 | 88 | **2、消息队列** 89 | 90 | 除了标准的基于RPC通信的微服务架构,还有基于消息队列通信的微服务架构,这种架构下的微服务采用发送消息(Publish Message)与监听消息(Subscribe Message)的方式来实现彼此之间的交互。 91 | 92 | 网易的蜂巢平台就采用了基于消息队列的微服务架构设计思路,如下图所示,微服务之间通过RabbitMQ传递消息,实现通信。 93 | 94 | 与上面几种微服务架构相比,基于消息队列的微服务架构并不多,案例也相对较少,更多地体现为一种与业务相关的设计经验,各家有各家的实现方式,缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台。因此,如果需要实施这种微服务架构,则基本上需要项目组自己从零开始去设计实现一个微服务架构基础平台,其代价是成本高、风险大。 95 | 96 | **优点**: 97 | 98 | - 把客户端和服务端解耦,更松耦合提高可用性,因为消息中间件缓存了消息,直到消费者可以消费 99 | - 支持很多通信机制比如通知、发布/订阅等 100 | 101 | **缺点**: 102 | 103 | - 缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台 104 | - 成本高、风险大 105 | 106 | ## 熔断器 107 | 108 | 雪崩效应:假如存在这样的调用链路,a服务->b服务->c服务,当c服务挂了的时候,b服务调用c服务会等待超时,a服务调用b服务也会等待超时,调用方长时间等不到相应而占用线程,如果有大量的请求过来,就会造成线程池打满,导致整个链路的服务奔溃。 109 | 110 | 为了解决分布式系统的雪崩效应,分布式系统引进了**熔断器机制** 。 111 | 112 | 当一个服务的处理用户请求的失败次数在一定时间内小于设定的阀值时,熔断器出于关闭状态,服务正常。 113 | 114 | 当服务处理用户请求失败次数在一定时间内大于设定的阀值时,说明服务出现故障,打开熔断器,这时所有的请求会快速返回失败的错误信息,不执行业务逻辑,从而防止故障的蔓延。 115 | 116 | 当处于打开状态的熔断器时,一段时间后出于半打开状态,并执行一定数量的请求,剩余的请求会执行快速失败,若执行请求失败了,则继续打开熔断器,若成功了,则将熔断器关闭。 117 | 118 | ## 服务网关 119 | 120 | ### 何为网关? 121 | 122 | 通俗一点的讲:网关就是要去别的网络的时候,把报文首先发送到的那台设备。稍微专业一点的术语,网关就是当前主机的默认路由。 123 | 124 | 网关一般就是一台路由器,有点像“一个小区中的一个邮局”,小区里面的住户互相是知道怎么走,但是要向外地投递东西就不知道了,怎么办?把地址写好送到本小区的邮局就好了。 125 | 126 | 那么,怎么区分是“本小区”和“外地小区”的呢?根据IP地址 + 掩码。如果是在一个范围内的,就是本小区(局域网内部),如果掩不住的,就是外地的。 127 | 128 | 例如,你的机器的IP地址是:192.168.0.2/24,网关是192.168.0.1 129 | 130 | 如果机器访问的IP地址范围是:192.168.0.1~192.168.0.254的,说明是一个小区的邻居,你的机器就直接发送了(和网关没任何关系)。如果你访问的IP地址不是这个范围的,则就投递到192.168.0.1上,让这台设备来转发。 131 | 132 | 参考:https://www.zhihu.com/question/362842680/answer/951412213 133 | 134 | ### 何为API网关 135 | 136 | 假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。 137 | 138 | 那么这里就会遇到一个问题,APP/Browser怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都分配一个独立的域名(`https://service.api.company.com`),但这种方式会有几个问题: 139 | 140 | - 每个业务都会需要鉴权、限流、权限校验等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。 141 | - 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名 142 | - 每上线一个新的服务,都需要运维参与,申请域名、配置Nginx等,当上线、下线服务器时,同样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名自己进行判断。 143 | - 另外还有一个问题,后端每个微服务可能采用了不同的协议,比如HTTP、AMQP、自定义TCP协议等,但是你不可能要求客户端去适配这么多种协议,这是一项非常有挑战的工作,项目会变的非常复杂且很难维护。 144 | 145 | 更好的方式是采用API网关(也叫做服务网关),实现一个API网关**接管所有的入口流量**,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。 146 | 147 | ![](http://img.topjavaer.cn/img/20220508185101.jpg) 148 | 149 | 通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。 150 | 151 | 网关层通常以集群的形式存在。并在服务网关层前通常会加上Nginx 用来负载均衡。 152 | 153 | **服务网关基本功能**: 154 | 155 | ![](http://img.topjavaer.cn/img/20220508120340.png) 156 | 157 | - 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。 158 | - 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用 159 | - API监控:监控经过网关的请求,以及网关本身的一些性能指标(gc等) 160 | - 限流:与监控配合,进行限流操作 161 | - API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志 162 | 163 | 当然,网关实现这些功能,需要做高可用,否则网关很可能成为架构的瓶颈,最常用的网关组件Zuul、Nginx 164 | 165 | ## 服务配置统一管理 166 | 167 | 在微服务架构中,需要有统一管理配置文件的组件,例如:SpringCloud Config组件、阿里的Diamond、百度的Disconf、携程的Apollo等 168 | 169 | ## 服务链路追踪 170 | 171 | 在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与、参与顺序,是每个请求链路清晰可见,便于问题快速定位。 172 | 173 | 常用链路追踪组件有Google的Dapper、Twitter 的Zipkin,以及阿里Eagleeye。 174 | 175 | ## 微服务框架 176 | 177 | 市面常用微服务框架有:Spring Cloud 、Dubbo 、kubernetes 178 | 179 | - 从功能模块上考虑,Dubbo缺少很多功能模块,例如网关、链路追踪等 180 | - 从学习成本上考虑,Dubbo 版本趋于稳定,稳定完善、可以即学即用,难度简单,Spring cloud 基于Spring Boot,需要先掌握Spring Boot ,例外Spring cloud 大多为英文文档,要求学习者有一定的英文阅读能力 181 | - 从开发风格考虑,Dubbo倾向于xml的配置方式,Spring cloud 基于Spring Boot ,采用基于注解和JavaBean配置方式的敏捷开发 182 | - 从开发速度上考虑,Spring cloud 具有更高的开发和部署速度 183 | - 从通信方式上考虑,Spring cloud 基于HTTP Restful 风格,服务于服务之间完全无关、无耦合。Dubbo 基于远程调用,对接口、平台和语言有强依赖性,如果需要实现跨平台,需要有额外的中间件。 184 | 185 | 所以Dubbo专注于服务治理;Spring Cloud关注于微服务架构生态。 186 | 187 | -------------------------------------------------------------------------------- /docs/guide/java/distributed/6-distributed-transaction.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 分布式事务详解 4 | --- 5 | 6 | # 分布式事务 7 | 8 | ## 简介 9 | 10 | ### 事务 11 | 12 | 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。事务应该具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。 13 | 14 | ### 分布式事务 15 | 16 | 分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务的特性。 17 | 18 | ### 强一致性、弱一致性、最终一致性 19 | 20 | **强一致性** 21 | 22 | 任何一次读都能读到某个数据的最近一次写的数据。系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。简言之,在任意时刻,所有节点中的数据是一样的。 23 | 24 | **弱一致性** 25 | 26 | 数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。 27 | 28 | **最终一致性** 29 | 30 | 不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。简单说,就是在一段时间后,节点间的数据会最终达到一致状态。 31 | 32 | 由于分布式事务方案,无法做到完全的ACID的保证,没有一种完美的方案,能够解决掉所有业务问题。因此在实际应用中,会根据业务的不同特性,选择最适合的分布式事务方案。 33 | 34 | ## 分布式事务的基础 35 | 36 | ### CAP理论 37 | 38 | **Consistency**(一致性):数据一致更新,所有数据变动都是同步的(强一致性)。 39 | 40 | **Availability**(可用性):好的响应性能。 41 | 42 | **Partition tolerance**(分区容错性) :可靠性。 43 | 44 | 定理:任何分布式系统**只可同时满足二点**,没法三者兼顾。 45 | 46 | CA系统(放弃P):指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性得到满足。 47 | 48 | CP系统(放弃A):如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。 49 | 50 | AP系统(放弃C):这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。 51 | 52 | ### BASE理论 53 | 54 | BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展。 55 | 56 | 1. 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。 57 | 2. 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。 58 | 3. 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。 59 | 60 | BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。 61 | 62 | ## 分布式事务解决方案 63 | 64 | 分布式事务的实现主要有以下 6 种方案: 65 | 66 | - 2PC 方案 67 | - TCC 方案 68 | - 本地消息表 69 | - MQ事务 70 | - Saga事务 71 | - 最大努力通知方案 72 | 73 | ### 2PC方案 74 | 75 | 2PC方案分为两阶段: 76 | 77 | 第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交. 78 | 79 | 第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。 80 | 81 | 优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。 82 | 83 | 缺点: 84 | 85 | - 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。 86 | - 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。 87 | - 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。 88 | 89 | 总的来说,2PC方案比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。 90 | 91 | ### TCC 92 | 93 | TCC 的全称是:`Try`、`Confirm`、`Cancel`。 94 | 95 | - **Try 阶段**:这个阶段说的是对各个服务的资源做检测以及对资源进行 **锁定或者预留**。 96 | - **Confirm 阶段**:这个阶段说的是在各个服务中执行实际的操作。 97 | - **Cancel 阶段**:如果任何一个服务的业务方法执行出错,那么这里就需要 **进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚) 98 | - 99 | 100 | 举个简单的例子如果你用100元买了一瓶水, Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。 101 | 102 | 如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。 103 | 104 | 如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)。 105 | 106 | 这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个**事务回滚实际上是严重依赖于你自己写代码来回滚和补偿**了,会造成补偿代码巨大。 107 | 108 | ### 本地消息表 109 | 110 | 本地消息表的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。 111 | 112 | ![](http://img.topjavaer.cn/img/本地消息表.png) 113 | 114 | 对于本地消息队列来说核心是把大事务转变为小事务。还是举上面用100元去买一瓶水的例子。 115 | 116 | 1.当你扣钱的时候,你需要在你扣钱的服务器上新增加一个本地消息表,你需要把你扣钱和写入减去水的库存到本地消息表放入同一个事务(依靠数据库本地事务保证一致性。 117 | 118 | 2.这个时候有个定时任务去轮询这个本地事务表,把没有发送的消息,扔给商品库存服务器,叫他减去水的库存,到达商品服务器之后这个时候得先写入这个服务器的事务表,然后进行扣减,扣减成功后,更新事务表中的状态。 119 | 120 | 3.商品服务器通过定时任务扫描消息表或者直接通知扣钱服务器,扣钱服务器本地消息表进行状态更新。 121 | 122 | 4.针对一些异常情况,定时扫描未成功处理的消息,进行重新发送,在商品服务器接到消息之后,首先判断是否是重复的,如果已经接收,在判断是否执行,如果执行在马上又进行通知事务,如果未执行,需要重新执行需要由业务保证幂等,也就是不会多扣一瓶水。 123 | 124 | 本地消息队列是BASE理论,是最终一致模型,适用于对一致性要求不高的。实现这个模型时需要注意重试的幂等。 125 | 126 | ### MQ事务 127 | 128 | 基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。 129 | 130 | MQ事务方案整体流程和本地消息表的流程很相似,如下图: 131 | 132 | ![](http://img.topjavaer.cn/img/MQ事务方案.png) 133 | 134 | 从上图可以看出和本地消息表方案唯一不同就是将本地消息表存在了MQ内部,而不是业务数据库中。 135 | 136 | 那么MQ内部的处理尤为重要,下面主要基于 RocketMQ 4.3 之后的版本介绍 MQ 的分布式事务方案。 137 | 138 | 在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ 的事务消息相对于普通 MQ提供了 2PC 的提交接口,方案如下: 139 | 140 | **正常情况:事务主动方发消息** 141 | 142 | ![](http://img.topjavaer.cn/img/事务主动方发消息.png) 143 | 144 | 这种情况下,事务主动方服务正常,没有发生故障,发消息流程如下: 145 | 146 | - 发送方向 MQ 服务端(MQ Server)发送 half 消息。 147 | - MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。 148 | - 发送方开始执行本地事务逻辑。 149 | - 发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。 150 | - MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。 151 | 152 | **异常情况:事务主动方消息恢复** 153 | 154 | ![](http://img.topjavaer.cn/img/事务主动方消息恢复.png) 155 | 156 | 在断网或者应用重启等异常情况下,图中 4 提交的二次确认超时未到达 MQ Server,此时处理逻辑如下: 157 | 158 | - MQ Server 对该消息发起消息回查。 159 | - 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。 160 | - 发送方根据检查得到的本地事务的最终状态再次提交二次确认。 161 | - MQ Server基于 commit/rollback 对消息进行投递或者删除。 162 | 163 | **优点** 164 | 165 | 相比本地消息表方案,MQ 事务方案优点是: 166 | 167 | - 消息数据独立存储 ,降低业务系统与消息系统之间的耦合。 168 | - 吞吐量大于使用本地消息表方案。 169 | 170 | **缺点** 171 | 172 | - 一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。 173 | - 业务处理服务需要实现消息状态回查接口。 174 | 175 | ### Saga事务 176 | 177 | Saga是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发Saga中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作。 178 | 179 | Saga的实现有很多种方式,其中最流行的两种方式是: 180 | 181 | - **基于事件的方式**。这种方式没有协调中心,整个模式的工作方式就像舞蹈一样,各个舞蹈演员按照预先编排的动作和走位各自表演,最终形成一只舞蹈。处于当前Saga下的各个服务,会产生某类事件,或者监听其它服务产生的事件并决定是否需要针对监听到的事件做出响应。 182 | - **基于命令的方式**。这种方式的工作形式就像一只乐队,由一个指挥家(协调中心)来协调大家的工作。协调中心来告诉Saga的参与方应该执行哪一个本地事务。 183 | 184 | ### 最大努力通知方案 185 | 186 | 最大努力通知也称为定期校对,是对MQ事务方案的进一步优化。它在事务主动方增加了消息校对的接口,如果事务被动方没有接收到消息,此时可以调用事务主动方提供的消息校对的接口主动获取。 187 | 188 | 最大努力通知的整体流程如下图: 189 | 190 | ![](http://img.topjavaer.cn/img/最大努力通知方案.png) 191 | 192 | 在可靠消息事务中,事务主动方需要将消息发送出去,并且消息接收方成功接收,这种可靠性发送是由事务主动方保证的; 193 | 194 | 但是最大努力通知,事务主动方尽最大努力(重试,轮询....)将事务发送给事务接收方,但是仍然存在消息接收不到,此时需要事务被动方主动调用事务主动方的消息校对接口查询业务消息并消费,这种通知的可靠性是由事务被动方保证的。 195 | 196 | 最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。 197 | 198 | ## 参考文章 199 | 200 | https://www.pdai.tech/md/arch/arch-z-transection.html 201 | 202 | https://juejin.cn/post/6844903647197806605#heading-15 203 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/15-http-vs-rpc.md: -------------------------------------------------------------------------------- 1 | # 有了HTTP,为啥还要用RPC 2 | 3 | > 原文链接:https://www.jianshu.com/p/9d42b926d40d 4 | 5 | ## 既然有 HTTP 请求,为什么还要用 RPC 调用? 6 | 7 | 一直以来都没有深究过RPC和HTTP的区别,不都是写一个服务然后在客户端调用么? 8 | 9 | HTTP和RPC最本质的区别,就是 **RPC 主要是基于 TCP/IP 协议的**,而 **HTTP 服务主要是基于 HTTP 协议的**。 10 | 11 | 我们都知道 HTTP 协议是在传输层协议 TCP 之上的,所以效率来看的话,RPC 当然是要更胜一筹啦! 12 | 13 | HTTP和RPC的相同点是,底层通讯都是基于socket,都可以实现远程调用,都可以实现服务调用服务 14 | 15 | ### HTTP 的本质 16 | 17 | 首先你要明确 HTTP 是一个协议,是一个超文本传输协议。 18 | 19 | HTTP 它是协议,不是运输通道。 20 | 21 | 它基于 TCP/IP 来传输文本、图片、视频、音频等。 22 | 23 | 重点来了。 24 | 25 | HTTP 不提供数据包的传输功能,也就是数据包从浏览器到服务端再来回的传输和它没关系。 26 | 27 | 这是 TCP/IP 干的。 28 | 29 | 那 HTTP 有啥用?我们来分析一波。 30 | 31 | 我们上网要么就是获取一些信息来看,要么就是修改一些信息。 32 | 33 | 比如你用浏览器刷微博就是获取信息,发微博就是修改信息。 34 | 35 | 所以说浏览器需要告知服务器它需要什么,这次的请求是要获取哪些信息?发怎么样的微博。 36 | 37 | 这就涉及到浏览器和服务器之间的通信交互。 38 | 39 | 而交互就需要一种格式。 40 | 41 | 像你我之间的谈话就用中文,你要突然换成俄语我听不懂那不就 GG 了。 42 | 43 | 所以说 HTTP 它规定了一种格式,一种通信格式,大家都用这个格式来交谈。 44 | 45 | 这样不论你是什么服务器、什么浏览器都能顺利的交流,减少交互的成本。 46 | 47 | 就像全世界如果都讲中文,那我们不就不需要学英文了,那不就较少交互的成本了。 48 | 49 | 不像现在我们还得学英文,不然就看不懂文档等等。 50 | 51 | 万一之后俄语又起来了,咱还得对接俄文,这交互成本是不是就上来了。 52 | 53 | 而网络世界还好,咱们现在的 Web 交互基本上就是 HTTP 了。 54 | 55 | 其实 HTTP 协议的格式很像我们信封,有个固定的格式。 56 | 57 | ![](http://img.topjavaer.cn/img/http-rpc1.png) 58 | 59 | 左上角写邮编,右上角贴邮票,然后地址姓名啥的依次来。 60 | 61 | 因为计算机是很死板的,不像我们人一样有一种立体扫描感,所以要规定先写头、再写尾。 62 | 63 | 你要是先写尾,再写头计算机就认不出来了。 64 | 65 | 所以 HTTP 就规定了请求先搞请求行、再搞请求报头、再搞请求体。 66 | 67 | 响应就状态行、响应报头、响应体。 68 | 69 | ![](http://img.topjavaer.cn/img/http-rpc2.png) 70 | 71 | 所以 HTTP 的本质是什么? 72 | 73 | **就是客户端和服务端约定好的一种通信格式。** 74 | 75 | ### HTTP 和 RPC 的关系 76 | 77 | HTTP 和 RPC 其实是两个维度的东西, HTTP 指的是通信协议。 78 | 79 | 而 RPC 则是远程调用,其对应的是本地调用。 80 | 81 | RPC 的通信可以用 HTTP 协议,也可以自定义协议,是不做约束的。 82 | 83 | 像之前的单体时代,我们的 service 调用就是自己实现的方法,是本地进程内的调用。 84 | 85 | ``` 86 | public User getUserById(Long id) { 87 | return userDao.getUserById(id); // 这叫本地调用 88 | } 89 | ``` 90 | 91 | 现在都是微服务了,根据业务模块做了不同的拆分,像用户的服务不用我这个小组负责,我这小组只要写订单服务就行了。 92 | 93 | 但是我们服务需要用到用户的信息,于是我们需要调用用户小组的服务,于是代码变成了以下这种 94 | 95 | ``` 96 | public User getUserById(Long id) { 97 | return userConsumer.getUserById(id); // 这是远程调用,逻辑是用户小组的服务实现的。 98 | } 99 | ``` 100 | 101 | 可能还有些小伙伴不太清楚,再来看个图。 102 | 103 | ![](http://img.topjavaer.cn/img/http-rpc3.png) 104 | 105 | 把之前的用户实现拆分出来弄了一个用户服务,订单相关的也拆成了订单服务,都单独部署。 106 | 107 | 这样订单相关的服务要获取用户的信息就需要远程调用了。 108 | 109 | 可以看到 RPC 就是通过网络进行远程调用,订单服务其实就是客户端,而用户服务是服务端。 110 | 111 | 这又涉及到交互了,所以也需要约定一个格式,至于要不要用 HTTP 这个格式,就是大家自己看着办。 112 | 113 | 至此相信你对 HTTP 是啥也清楚了。 114 | 115 | RPC 和 HTTP 的之间的关系也清楚了。 116 | 117 | ### 那为什么要有 RPC? 118 | 119 | 可能你常听到什么什么之间是 RPC 调用的,那你有没有想过为什么要 RPC, 我们直接 WebClient HTTP 调用不行么? 120 | 121 | 其实 RPC 调用是因为服务的拆分,或者本身公司内部的多个服务之间的通信。 122 | 123 | 服务的拆分独立部署,那服务间的调用就必然需要网络通信,用 WebClient 调用当然可行,但是比较麻烦。 124 | 125 | 我们想即使服务被拆分了但是使用起来还是和之前本地调用一样方便。 126 | 127 | 所以就出现了 RPC 框架,来屏蔽这些底层调用细节,使得我们编码上还是和之前本地调用相差不多。 128 | 129 | 并且 HTTP 协议比较的冗余,RPC 都是内部调用所以不需要太考虑通用性,只要公司内部保持格式统一即可。 130 | 131 | 所以可以做各种定制化的协议来使得通信更高效。 132 | 133 | 比如规定 yes 代表 yes的练级攻略,你看是不是更高效了,少传输的 5 个字。 134 | 135 | 就像特殊行动的暗号,高效简洁! 136 | 137 | 所以公司内部服务的调用一般都用 RPC,而 HTTP 的优势在于通用,大家都认可这个协议。 138 | 139 | 所以三方平台提供的接口都是通过 HTTP 协议调用的。 140 | 141 | 所以现在知道为什么我们调用第三方都是 HTTP ,公司内部用 RPC 了吧? 142 | 143 | 上面这段话看起来仿佛 HTTP 和 RPC 是对等关系,不过相信大家看了之前的解析心里应该都有数了。 144 | 145 | 下面来具体说一说 RPC 服务和 HTTP 服务的区别。 146 | 147 | #### OSI 网络七层模型 148 | 149 | 在说 RPC 和 HTTP 的区别之前,我觉的有必要了解一下 OSI 的七层网络结构模型( 150 | 151 | ![](http://img.topjavaer.cn/img/http-rpc4.png) 152 | 153 | 它可以分为以下几层:(从上到下) 154 | 155 | - **第一层:应用层。**定义了用于在网络中进行通信和传输数据的接口。 156 | - **第二层:表示层。**定义不同的系统中数据的传输格式,编码和解码规范等。 157 | - **第三层:会话层。**管理用户的会话,控制用户间逻辑连接的建立和中断。 158 | - **第四层:传输层。**管理着网络中的端到端的数据传输。 159 | - **第五层:网络层。**定义网络设备间如何传输数据。 160 | - **第六层:链路层。**将上面的网络层的数据包封装成数据帧,便于物理层传输。 161 | - **第七层:物理层。**这一层主要就是传输这些二进制数据。 162 | 163 | ![](http://img.topjavaer.cn/img/http-rpc5.png) 164 | 165 | > **实际应用过程中,五层协议结构里面是没有表示层和会话层的。应该说它们和应用层合并了。** 166 | 167 | 我们应该将重点放在应用层和传输层这两个层面。因为 HTTP 是应用层协议,而 TCP 是传输层协议。 168 | 169 | 好,知道了网络的分层模型以后我们可以更好地理解为什么 RPC 服务相比 HTTP 服务要 Nice 一些! 170 | 171 | ### RPC 服务 172 | 173 | 从三个角度来介绍 RPC 服务,分别是: 174 | 175 | - **RPC 架构** 176 | - **同步异步调用** 177 | - **流行的 RPC 框架** 178 | 179 | **RPC 架构** 180 | 181 | 先说说 RPC 服务的基本架构吧。我们可以很清楚地看到,一个完整的 RPC 架构里面包含了四个核心的组件。 182 | 183 | 分别是: 184 | 185 | - **Client** 186 | - **Server** 187 | - **Client Stub** 188 | - **Server Stub(这个Stub大家可以理解为存根)** 189 | 190 | ![](http://img.topjavaer.cn/img/http-rpc6.png) 191 | 192 | 分别说说这几个组件: 193 | 194 | - **客户端(Client),**服务的调用方。 195 | - **服务端(Server),**真正的服务提供者。 196 | - **客户端存根,**存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。微信搜索公众号:Linux技术迷,回复:linux 领取资料 。 197 | - 服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法。 198 | 199 | RPC 主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要的一块,这个时候 RPC 的优势就比较明显了。 200 | 201 | ![](http://img.topjavaer.cn/img/http-rpc7.png) 202 | 203 | 比如我们有一个处理订单的系统服务,先声明它的所有的接口,然后将整个项目打包,服务端这边引入,然后实现相应的功能,客户端这边也只需要引入就可以调用了。 204 | 205 | 为什么这么做? 206 | 207 | 主要是为了减少客户端这边的包大小,因为每一次打包发布的时候,包太多总是会影响效率。 208 | 209 | 另外也是将客户端和服务端解耦,提高代码的可移植性。 210 | 211 | **同步调用与异步调用** 212 | 213 | 什么是同步调用?什么是异步调用? 214 | 215 | 同步调用就是客户端等待调用执行完成并返回结果。 216 | 217 | 异步调用就是客户端不等待调用执行完成返回结果,不过依然可以通过回调函数等接收到返回结果的通知。如果客户端并不关心结果,则可以变成一个单向的调用。 218 | 219 | #### 流行的 RPC 框架 220 | 221 | 目前流行的开源 RPC 框架还是比较多的。下面重点介绍三种: 222 | 223 | **①gRPC** 是 Google 最近公布的开源软件,基于最新的 HTTP2.0 协议,并支持常见的众多编程语言。 224 | 225 | 我们知道 HTTP2.0 是基于二进制的 HTTP 协议升级版本,目前各大浏览器都在快马加鞭的加以支持。 226 | 227 | 这个 RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持。 228 | 229 | **②Thrift** 是 Facebook 的一个开源项目,主要是一个跨语言的服务开发框架。它有一个代码生成器来对它所定义的 IDL 定义文件自动生成服务代码框架。 230 | 231 | 用户只要在其之前进行二次开发就行,对于底层的 RPC 通讯等都是透明的。不过这个对于用户来说的话需要学习特定领域语言这个特性,还是有一定成本的。 232 | 233 | **③Dubbo** 是阿里集团开源的一个极为出名的 RPC 框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是及其鲜明的特色。 234 | 235 | ### HTTP 服务 236 | 237 | 通常,我们的开发模式一直定性为 HTTP 接口开发,也就是我们常说的 RESTful 风格的服务接口。 238 | 239 | 的确,对于在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。 240 | 241 | 利用现成的 HTTP 协议进行传输。 242 | 243 | 平时的工作主要就是进行接口的开发,还要写一大份接口文档,严格地标明输入输出是什么?说清楚每一个接口的请求方法,以及请求参数需要注意的事项等。 244 | 245 | ![](http://img.topjavaer.cn/img/http-rpc8.png) 246 | 247 | 比如下面这个例子: 248 | 249 | ``` 250 | POST http://www.httpexample.com/restful/buyer/info/shar 251 | ``` 252 | 253 | 接口可能返回一个 JSON 字符串或者是 XML 文档。然后客户端再去处理这个返回的信息,从而可以比较快速地进行开发。 254 | 255 | 但是对于大型企业来说,内部子系统较多、接口非常多的情况下,RPC 框架的好处就显示出来了,首先就是长链接,不必每次通信都要像 HTTP 一样去 3 次握手什么的,减少了网络开销。 256 | 257 | 其次就是 RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。 258 | 259 | ### 小结 260 | 261 | RPC 服务和 HTTP 服务还是存在很多的不同点的,一般来说,RPC 服务主要是针对大型企业的,而 HTTP 服务主要是针对小企业的,因为 RPC 效率更高,而 HTTP 服务开发迭代会更快。 262 | 263 | 很多RPC框架包含了重试机制,路由策略,负载均衡策略,高可用策略,流量控制策略等等。如果应用进程之间只使用HTTP协议通信,显然是无法完成上述功能的。 264 | 265 | 总之,选用什么样的框架不是按照市场上流行什么而决定的,而是要对整个项目进行完整地评估,从而在仔细比较两种开发框架对于整个项目的影响,最后再决定什么才是最适合这个项目的。 266 | 267 | 一定不要为了使用 RPC 而每个项目都用 RPC,而是要因地制宜,具体情况具体分析。 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/25-select-count-slow-query.md: -------------------------------------------------------------------------------- 1 | # SELECT COUNT(*) 会造成全表扫描?回去等通知吧 2 | 3 | ## 前言 4 | 5 | SELECT COUNT(*)会不会导致全表扫描引起慢查询呢? 6 | 7 | ``` 8 | SELECT COUNT(*) FROM SomeTable 9 | ``` 10 | 11 | 网上有一种说法,针对无 where_clause 的 **COUNT(\*)**,MySQL 是有优化的,优化器会选择成本最小的辅助索引查询计数,其实反而性能最高,这种说法对不对呢 12 | 13 | 针对这个疑问,我首先去生产上找了一个千万级别的表使用 EXPLAIN 来查询了一下执行计划 14 | 15 | ``` 16 | EXPLAIN SELECT COUNT(*) FROM SomeTable 17 | ``` 18 | 19 | 结果如下 20 | 21 | ![](http://img.topjavaer.cn/img/select-count1.png) 22 | 23 | 如图所示: 发现确实此条语句在此例中用到的并不是主键索引,而是辅助索引,实际上在此例中我试验了,不管是 COUNT(1),还是 COUNT(*),MySQL 都会用**成本最小**的辅助索引查询方式来计数,也就是使用 COUNT(*) 由于 MySQL 的优化已经保证了它的查询性能是最好的!随带提一句,COUNT(*)是 SQL92 定义的标准统计行数的语法,并且效率高,所以请直接使用COUNT(*)查询表的行数! 24 | 25 | 所以这种说法确实是对的。但有个前提,在 MySQL 5.6 之后的版本中才有这种优化。 26 | 27 | 那么这个成本最小该怎么定义呢,有时候在 WHERE 中指定了多个条件,为啥最终 MySQL 执行的时候却选择了另一个索引,甚至不选索引? 28 | 29 | 本文将会给你答案,本文将会从以下两方面来分析 30 | 31 | - SQL 选用索引的执行成本如何计算 32 | - 实例说明 33 | 34 | ## SQL 选用索引的执行成本如何计算 35 | 36 | 就如前文所述,在有多个索引的情况下, 在查询数据前,MySQL 会选择成本最小原则来选择使用对应的索引,这里的成本主要包含两个方面。 37 | 38 | - IO 成本: 即从磁盘把数据加载到内存的成本,默认情况下,读取数据页的 IO 成本是 1,MySQL 是以页的形式读取数据的,即当用到某个数据时,并不会只读取这个数据,而会把这个数据相邻的数据也一起读到内存中,这就是有名的程序局部性原理,所以 MySQL 每次会读取一整页,一页的成本就是 1。所以 IO 的成本主要和页的大小有关 39 | - CPU 成本:将数据读入内存后,还要检测数据是否满足条件和排序等 CPU 操作的成本,显然它与行数有关,默认情况下,检测记录的成本是 0.2。 40 | 41 | ## 实例说明 42 | 43 | 为了根据以上两个成本来算出使用索引的最终成本,我们先准备一个表(以下操作基于 MySQL 5.7.18) 44 | 45 | ``` 46 | CREATE TABLE `person` ( 47 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 48 | `name` varchar(255) NOT NULL, 49 | `score` int(11) NOT NULL, 50 | `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 51 | PRIMARY KEY (`id`), 52 | KEY `name_score` (`name`(191),`score`), 53 | KEY `create_time` (`create_time`) 54 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 55 | ``` 56 | 57 | 这个表除了主键索引之外,还有另外两个索引, name_score 及 create_time。然后我们在此表中插入 10 w 行数据,只要写一个存储过程调用即可,如下: 58 | 59 | ``` 60 | CREATE PROCEDURE insert_person() 61 | begin 62 | declare c_id integer default 1; 63 | while c_id<=100000 do 64 | insert into person values(c_id, concat('name',c_id), c_id+100, date_sub(NOW(), interval c_id second)); 65 | set c_id=c_id+1; 66 | end while; 67 | end 68 | ``` 69 | 70 | 插入之后我们现在使用 EXPLAIN 来计算下统计总行数到底使用的是哪个索引 71 | 72 | ``` 73 | EXPLAIN SELECT COUNT(*) FROM person 74 | ``` 75 | 76 | ![图片](https://mmbiz.qpic.cn/mmbiz_png/OyweysCSeLXtag3Q38nMHUxszkvkXfFaXejKpeg0pWWQcs16SwlOR1o1Fo5nw1B2RCAME61LN3hH3E5uDQuicXQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 77 | 78 | 从结果上看它选择了 create_time 辅助索引,显然 MySQL 认为使用此索引进行查询成本最小,这也是符合我们的预期,使用辅助索引来查询确实是性能最高的! 79 | 80 | 我们再来看以下 SQL 会使用哪个索引 81 | 82 | ``` 83 | SELECT * FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18' 84 | ``` 85 | 86 | ![图片](https://mmbiz.qpic.cn/mmbiz_png/OyweysCSeLXtag3Q38nMHUxszkvkXfFa4XR8ibNMfganIREu5JQF2bJYt0r7tZpYWdCcIaIW4emVC9SVWiajULYw/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 87 | 88 | 用了全表扫描!理论上应该用 name_score 或者 create_time 索引才对,从 WHERE 的查询条件来看确实都能命中索引,那是否是使用 **SELECT \*** 造成的回表代价太大所致呢,我们改成覆盖索引的形式试一下 89 | 90 | ``` 91 | SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18' 92 | ``` 93 | 94 | 结果 MySQL 依然选择了全表扫描!这就比较有意思了,理论上采用了覆盖索引的方式进行查找性能肯定是比全表扫描更好的,为啥 MySQL 选择了全表扫描呢,既然它认为全表扫描比使用覆盖索引的形式性能更好,那我们分别用这两者执行来比较下查询时间吧 95 | 96 | ``` 97 | -- 全表扫描执行时间: 4.0 ms 98 | SELECT create_time FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18' 99 | 100 | -- 使用覆盖索引执行时间: 2.0 ms 101 | SELECT create_time FROM person force index(create_time) WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18' 102 | ``` 103 | 104 | 从实际执行的效果看使用覆盖索引查询比使用全表扫描执行的时间快了一倍!说明 MySQL 在查询前做的成本估算不准!我们先来看看 MySQL 做全表扫描的成本有多少。 105 | 106 | 前面我们说了成本主要 IO 成本和 CPU 成本有关,对于全表扫描来说也就是分别和聚簇索引占用的页面数和表中的记录数。执行以下命令 107 | 108 | ``` 109 | SHOW TABLE STATUS LIKE 'person' 110 | ``` 111 | 112 | ![图片](https://mmbiz.qpic.cn/mmbiz_png/OyweysCSeLXtag3Q38nMHUxszkvkXfFaBxFRZE5oFBbjrVryW8vMSG0GLuznuVA6vd69ZdEw09rmQYKaSy9S1Q/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 113 | 114 | 可以发现 115 | 116 | 1. 行数是 100264,我们不是插入了 10 w 行的数据了吗,怎么算出的数据反而多了,其实这里的计算是**估算**,也有可能这里的行数统计出来比 10 w 少了,估算方式有兴趣大家去网上查找,这里不是本文重点,就不展开了。得知行数,那我们知道 CPU 成本是 100264 * 0.2 = 20052.8。 117 | 2. 数据长度是 5783552,InnoDB 每个页面的大小是 16 KB,可以算出页面数量是 353。 118 | 119 | 也就是说全表扫描的成本是 20052.8 + 353 = 20406。 120 | 121 | 这个结果对不对呢,我们可以用一个工具验证一下。在 MySQL 5.6 及之后的版本中,我们可以用 optimizer trace 功能来查看优化器生成计划的整个过程 ,它列出了选择每个索引的执行计划成本以及最终的选择结果,我们可以依赖这些信息来进一步优化我们的 SQL。 122 | 123 | optimizer_trace 功能使用如下 124 | 125 | ``` 126 | SET optimizer_trace="enabled=on"; 127 | SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18'; 128 | SELECT * FROM information_schema.OPTIMIZER_TRACE; 129 | SET optimizer_trace="enabled=off"; 130 | ``` 131 | 132 | 执行之后我们主要观察使用 name_score,create_time 索引及全表扫描的成本。 133 | 134 | 先来看下使用 name_score 索引执行的的预估执行成本: 135 | 136 | ``` 137 | { 138 | "index": "name_score", 139 | "ranges": [ 140 | "name84059 <= name" 141 | ], 142 | "index_dives_for_eq_ranges": true, 143 | "rows": 25372, 144 | "cost": 30447 145 | } 146 | ``` 147 | 148 | 可以看到执行成本为 30447,高于我们之前算出来的全表扫描成本:20406。所以没选择此索引执行 149 | 150 | 注意:这里的 30447 是查询二级索引的 IO 成本和 CPU 成本之和,再加上回表查询聚簇索引的 IO 成本和 CPU 成本之和。 151 | 152 | 再来看下使用 create_time 索引执行的的预估执行成本: 153 | 154 | ``` 155 | { 156 | "index": "create_time", 157 | "ranges": [ 158 | "0x5ec8c516 < create_time" 159 | ], 160 | "index_dives_for_eq_ranges": true, 161 | "rows": 50132, 162 | "cost": 60159, 163 | "cause": "cost" 164 | } 165 | ``` 166 | 167 | 可以看到成本是 60159,远大于全表扫描成本 20406,自然也没选择此索引。 168 | 169 | 再来看计算出的全表扫描成本: 170 | 171 | ``` 172 | { 173 | "considered_execution_plans": [ 174 | { 175 | "plan_prefix": [ 176 | ], 177 | "table": "`person`", 178 | "best_access_path": { 179 | "considered_access_paths": [ 180 | { 181 | "rows_to_scan": 100264, 182 | "access_type": "scan", 183 | "resulting_rows": 100264, 184 | "cost": 20406, 185 | "chosen": true 186 | } 187 | ] 188 | }, 189 | "condition_filtering_pct": 100, 190 | "rows_for_plan": 100264, 191 | "cost_for_plan": 20406, 192 | "chosen": true 193 | } 194 | ] 195 | } 196 | ``` 197 | 198 | 注意看 cost:20406,与我们之前算出来的完全一样!这个值在以上三者算出的执行成本中最小,所以最终 MySQL 选择了用全表扫描的方式来执行此 SQL。 199 | 200 | 实际上 optimizer trace 详细列出了覆盖索引,回表的成本统计情况,有兴趣的可以去研究一下。 201 | 202 | 从以上分析可以看出, MySQL 选择的执行计划未必是最佳的,原因有挺多,就比如上文说的行数统计信息不准,再比如 MySQL 认为的最优跟我们认为不一样,我们可以认为执行时间短的是最优的,但 MySQL 认为的成本小未必意味着执行时间短。 203 | 204 | ## 总结 205 | 206 | 本文通过一个例子深入剖析了 MySQL 的执行计划是如何选择的,以及为什么它的选择未必是我们认为的最优的,这也提醒我们,在生产中如果有多个索引的情况,使用 WHERE 进行过滤未必会选中你认为的索引,我们可以提前使用 EXPLAIN, optimizer trace 来优化我们的查询语句。 -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/9-jvm-optimize-param.md: -------------------------------------------------------------------------------- 1 | # 美团面试:熟悉哪些JVM调优参数? 2 | 3 | 今天来熟悉一下,关于`JVM`调优常用的一些参数。 4 | 5 | X或者XX开头的都是非标准化参数 6 | 7 | ![](http://img.topjavaer.cn/img/jvm参数1.png) 8 | 9 | 意思就是说标准化参数不会变,非标准化参数可能在每个`JDK`版本中有所变化,但是就目前来看X开头的非标准化的参数改变的也是非常少。 10 | 11 | ``` 12 | 格式:-XX:[+-] 表示启用或者禁用name属性。 13 | 例子:-XX:+UseG1GC(表示启用G1垃圾收集器) 14 | ``` 15 | 16 | `-XX:+PrintCommandLineFlags`查看当前`JVM`设置过的相关参数: 17 | 18 | ![](http://img.topjavaer.cn/img/jvm参数2.png) 19 | 20 | ## JVM参数分类 21 | 22 | 根据`JVM`参数开头可以区分参数类型,共三类:“`-`”、“`-X`”、“`-XX`”, 23 | 24 | 标准参数(-):所有的JVM实现都必须实现这些参数的功能,而且向后兼容; 25 | 26 | 例子:`-verbose:class`,`-verbose:gc`,`-verbose:jni……` 27 | 28 | 非标准参数(-X):默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容; 29 | 30 | 例子:`Xms20m`,`-Xmx20m`,`-Xmn20m`,`-Xss128k……` 31 | 32 | 非Stable参数(-XX):此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用; 33 | 34 | 例子:`-XX:+PrintGCDetails`,`-XX:-UseParallelGC`,`-XX:+PrintGCTimeStamps……` 35 | 36 | ## 堆参数设置 37 | 38 | ``` 39 | -Xms` 初始堆大小,ms是memory start的简称 ,等价于`-XX:InitialHeapSize``-Xmx` 最大堆大小,mx是memory max的简称 ,等价于参数`-XX:MaxHeapSize 40 | ``` 41 | 42 | > 注意:在通常情况下,服务器项目在运行过程中,堆空间会不断的收缩与扩张,势必会造成不必要的系统压力。 43 | > 44 | > 所以在生产环境中,`JVM`的`Xms`和`Xmx`要设置成大小一样的,能够避免`GC`在调整堆大小带来的不必要的压力。 45 | 46 | `-XX:NewSize=n` 设置年轻代大小`-XX:NewRatio=n` 设置年轻代和年老代的比值。 47 | 48 | 如:`-XX:NewRatio=3`,表示年轻代与年老代比值为`1:3`,年轻代占整个年轻代年老代和的1/4,`默认新生代和老年代的比例=1:2`。`-XX:SurvivorRatio=n` 年轻代中Eden区与两个Survivor区的比值。 49 | 50 | 注意Survivor区有两个,默认是8,表示:`Eden:S0:S1=8:1:1` 51 | 52 | 如:`-XX:SurvivorRatio=3`,表示`Eden:Survivor`=3:2,一个Survivor区占整个年轻代的1/5。 53 | 54 | ## 元空间参数 55 | 56 | **-XX:MetaspaceSize**:`Metaspace` 空间初始大小,如果不设置的话,默认是20.79M,这个初始大小是触发首次 `Metaspace Full GC`的阈值。 57 | 58 | 例如:`-XX:MetaspaceSize=256M` 59 | 60 | **-XX:MaxMetaspaceSize**:`Metaspace` 最大值,默认不限制大小,但是线上环境建议设置。 61 | 62 | 例如:`-XX:MaxMetaspaceSize=256M` 63 | 64 | **-XX:MinMetaspaceFreeRatio**:最小空闲比,当 `Metaspace` 发生 GC 后,会计算 `Metaspace` 的空闲比,如果空闲比(空闲空间/当前 `Metaspace` 大小)小于此值,就会触发 `Metaspace` 扩容。默认值是 40 ,也就是 40%,例如 -XX:MinMetaspaceFreeRatio=40 65 | 66 | **-XX:MaxMetaspaceFreeRatio**:最大空闲比,当 `Metaspace`发生 GC 后,会计算 `Metaspace` 的空闲比,如果空闲比(空闲空间/当前 Metaspace 大小)大于此值,就会触发 `Metaspace` 释放空间。默认值是 70 ,也就是 70%,例如 -`XX:MaxMetaspaceFreeRatio=70` 67 | 68 | > 建议将 `MetaspaceSize` 和 `MaxMetaspaceSize`设置为同样大小,避免频繁扩容。 69 | 70 | ## 栈参数设置 71 | 72 | **-Xss**:栈空间大小,栈是线程独占的,所以是一个线程使用栈空间的大小。 73 | 74 | 例如:`-Xss256K`,如果不设置此参数,默认值是`1M`,一般来讲设置成 `256K` 就足够了。 75 | 76 | ## 收集器参数设置 77 | 78 | Serial垃圾收集器(新生代) 79 | 80 | > 开启:-XX:+UseSerialGC 关闭:-XX:-UseSerialGC //新生代使用Serial 老年代则使用SerialOld 81 | 82 | ParNew垃圾收集器(新生代) 83 | 84 | > 开启 -XX:+UseParNewGC 关闭 -XX:-UseParNewGC //新生代使用功能ParNew 老年代则使用功能CMS 85 | 86 | Parallel Scavenge收集器(新生代) 87 | 88 | > 开启 -XX:+UseParallelOldGC 关闭 -XX:-UseParallelOldGC //新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器 89 | 90 | ParallelOl垃圾收集器(老年代) 91 | 92 | > 开启 -XX:+UseParallelGC 关闭 -XX:-UseParallelGC //新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器 93 | 94 | CMS垃圾收集器(老年代) 95 | 96 | > 开启 -XX:+UseConcMarkSweepGC 关闭 -XX:-UseConcMarkSweepGC 97 | 98 | G1垃圾收集器 99 | 100 | > 开启 -XX:+UseG1GC 关闭 -XX:-UseG1GC 101 | 102 | ## GC策略参数配置 103 | 104 | GC停顿时间,垃圾收集器会尝试用各种手段达到这个时间,比如减小年轻代 105 | 106 | > -XX:MaxGCPauseMillis 107 | 108 | 堆占用了多少比例的时候触发GC,就即触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45% 109 | 110 | > -XX:InitiatingHeapOccupancyPercent=n 111 | 112 | 新生代可容纳的最大对象,大于则直接会分配到老年代,0代表没有限制。 113 | 114 | > -XX:PretenureSizeThreshold=1000000 // 115 | 116 | 进入老年代最小的GC年龄,年轻代对象转换为老年代对象最小年龄值,默认值7 117 | 118 | > -XX:InitialTenuringThreshol=7 119 | 120 | 升级老年代年龄,最大值15 121 | 122 | > -XX:MaxTenuringThreshold 123 | 124 | GC并行执行线程数 125 | 126 | > -XX:ParallelGCThreads=16 127 | 128 | 禁用 System.gc(),由于该方法默认会触发 FGC,并且忽略参数中的 UseG1GC 和 UseConcMarkSweepGC,因此必要时可以禁用该方法。 129 | 130 | > -XX:-+DisableExplicitGC 131 | 132 | 设置吞吐量大小,默认99 133 | 134 | > XX:GCTimeRatio 135 | 136 | 打开自适应策略,各个区域的比率,晋升老年代的年龄等参数会被自动调整。以达到吞吐量,停顿时间的平衡点。 137 | 138 | > XX:UseAdaptiveSizePolicy 139 | 140 | 设置GC时间占用程序运行时间的百分比 141 | 142 | > GCTimeRatio 143 | 144 | ## Dump异常快照 145 | 146 | ``` 147 | -XX:+HeapDumpOnOutOfMemoryError 148 | -XX:HeapDumpPath 149 | ``` 150 | 151 | 堆内存出现`OOM`的概率是所有内存耗尽异常中最高的,出错时的堆内信息对解决问题非常有帮助。 152 | 153 | 所以给`JVM`设置这个参数(`-XX:+HeapDumpOnOutOfMemoryError`),让`JVM`遇到`OOM`异常时能输出堆内信息,并通过(`-XX:+HeapDumpPath`)参数设置堆内存溢出快照输出的文件地址。 154 | 155 | 这对于特别是对相隔数月才出现的`OOM`异常尤为重要。 156 | 157 | ``` 158 | -Xms10M -Xmx10M -Xmn2M -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError 159 | -XX:HeapDumpPath=D:\study\log_hprof\gc.hprof 160 | -XX:OnOutOfMemoryError 161 | ``` 162 | 163 | 表示发生`OOM后`,运行`jconsole.exe`程序。 164 | 165 | 这里可以不用加“”,因为`jconsole.exe`路径Program Files含有空格。利用这个参数,我们可以在系统`OOM`后,自定义一个脚本,可以用来发送邮件告警信息,可以用来重启系统等等。 166 | 167 | ``` 168 | -XX:OnOutOfMemoryError="C:\Program Files\Java\jdk1.8.0_151\bin\jconsole.exe" 169 | ``` 170 | 171 | ## 8G内存的服务器该如何设置 172 | 173 | ``` 174 | java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0 175 | ``` 176 | 177 | `-Xmx3500m` 设置`JVM`最大可用内存为3550M。 178 | 179 | `-Xms3500m` 设置`JVM``初始`内存为`3550m`。此值可以设置与`-Xmx`相同,以避免每次垃圾回收完成后JVM重新分配内存。`-Xmn2g` 设置年轻代大小为`2G`。 180 | 181 | > 整个堆大小=年轻代大小 + 年老代大小 + 方法区大小 182 | 183 | `-Xss128k` 设置每个线程的堆栈大小。 184 | 185 | `JDK1.5`以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 186 | 187 | `-XX:NewRatio=4` 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 。 188 | 189 | `-XX:SurvivorRatio=4` 设置年轻代中Eden区与Survivor区的大小比值。 190 | 191 | 设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6 `-XX:MaxPermSize=16m` 设置持久代大小为16m。 192 | 193 | `-XX:MaxTenuringThreshold=0` 设置垃圾最大年龄。 194 | 195 | 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概论。 196 | 197 | ## 项目中,GC日志配置 198 | 199 | 比如,我们启动一个user-service项目: 200 | 201 | ``` 202 | java 203 | -XX:+PrintGCDetails -XX:+PrintGCDateStamps 204 | -XX:+UseGCLogFileRotation 205 | -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 206 | -XX:GCLogFileSize=20M 207 | -Xloggc:/opt/user-service-gc-%t.log 208 | -jar user-service-1.0-SNAPSHOT.jar 209 | ``` 210 | 211 | 参数解释: 212 | 213 | ``` 214 | -Xloggc:/opt/app/ard-user/user-service-gc-%t.log 设置日志目录和日志名称 215 | -XX:+UseGCLogFileRotation 开启滚动生成日志 216 | -XX:NumberOfGCLogFiles=5 滚动GC日志文件数,默认0,不滚动 217 | -XX:GCLogFileSize=20M GC文件滚动大小,需开启UseGCLogFileRotation 218 | -XX:+PrintGCDetails 开启记录GC日志详细信息(包括GC类型、各个操作使用的时间),并且在程序运行结束打印出JVM的内存占用情况 219 | -XX:+ PrintGCDateStamps 记录系统的GC时间 220 | -XX:+PrintGCCause 产生GC的原因(默认开启) 221 | ``` 222 | 223 | ## 项目中没用过怎么办? 224 | 225 | 对于很多没用过的人来说,面试官问项目中这些参数是怎么用?此时,很容易选择妥协,傻傻的回答**没用过**。 226 | 227 | 偷偷的告诉你,很多面试官也没有用过。 228 | 229 | 另外,你可以自己搞个小项目,把`JVM`参数设置小点,使用测试工具`JMeter`,多线程测试一下。 230 | 231 | 在代码里可以自己编造以下问题: 232 | 233 | > 内存溢出 234 | > 235 | > 内存泄漏 236 | > 237 | > 栈溢出 238 | 239 | 然后使用`JVM`参数进行调优,或者通过`JVM`工具和相关命令找到问题,然后解决问题。 240 | 241 | > 自己一定要动手,别人永远是别人的,自己体会了自己经历了,那才是自己的。 242 | 243 | -------------------------------------------------------------------------------- /docs/guide/java/collection/java-collection-code.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 源码分析 3 | category: Java 4 | tag: 5 | - Java集合 6 | head: 7 | - - meta 8 | - name: keywords 9 | content: Collection,List,Set,Queue,Deque,PriorityQueue, 10 | - name: description 11 | content: JDK源码详解,希望对你有帮助 12 | --- 13 | ## H扩容机制 14 | 15 | > HashMap 的扩容方式? 16 | 17 | Hashmap 在容量超过负载因子所定义的容量之后,就会扩容。Java 里的数组是无法自动扩容的,方法是将 Hashmap 的大小扩大为原来数组的两倍,并将原来的对象放入新的数组中。 18 | 19 | 那扩容的具体步骤是什么?让我们看看源码。 20 | 21 | 先来看下JDK1.7 的代码: 22 | 23 | ```java 24 | void resize(int newCapacity) { //传入新的容量 25 | Entry[] oldTable = table; //引用扩容前的Entry数组 26 | int oldCapacity = oldTable.length; 27 | if (oldCapacity == MAXIMUM_CAPACITY) { //扩容前的数组大小如果已经达到最大(2^30)了 28 | threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了 29 | return; 30 | } 31 | 32 | Entry[] newTable = new Entry[newCapacity]; //初始化一个新的Entry数组 33 | transfer(newTable); //!!将数据转移到新的Entry数组里 34 | table = newTable; //HashMap的table属性引用新的Entry数组 35 | threshold = (int)(newCapacity * loadFactor);//修改阈值 36 | } 37 | ``` 38 | 39 | 这里就是使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。 40 | 41 | ```java 42 | void transfer(Entry[] newTable) { 43 | Entry[] src = table; //src引用了旧的Entry数组 44 | int newCapacity = newTable.length; 45 | for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组 46 | Entry e = src[j]; //取得旧Entry数组的每个元素 47 | if (e != null) { 48 | src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象) 49 | do { 50 | Entry next = e.next; 51 | int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置 52 | e.next = newTable[i]; //标记[1] 53 | newTable[i] = e; //将元素放在数组上 54 | e = next; //访问下一个Entry链上的元素 55 | } while (e != null); 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | newTable[i] 的引用赋给了 e.next ,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到 Entry 链的尾部(如果发生了 hash 冲突的话)。 62 | 63 | ### [JDK1.8的优化](https://www.javalearn.cn/#/doc/Java集合/面试题?id=jdk18的优化) 64 | 65 | > 扩容在JDK1.8中有什么不一样? 66 | 67 | JDK1.8做了两处优化: 68 | 69 | 1. resize 之后,元素的位置在原来的位置,或者原来的位置 +oldCap (原来哈希表的长度)。不需要像 JDK1.7 的实现那样重新计算hash ,只需要看看原来的 hash 值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引 + oldCap ”。这个设计非常的巧妙,省去了重新计算 hash 值的时间。 70 | 71 | 如下图所示,n 为 table 的长度,图(a)表示扩容前的 key1 和 key2 两种 key 确定索引位置的示例,图(b)表示扩容后 key1 和key2 两种 key 确定索引位置的示例,其中 hash1 是 key1 对应的哈希与高位运算结果。 72 | 73 | ![image-20210113115127725](http://blog-img.coolsen.cn/img/image-20210113115127725.png)元素在重新计算 hash 之后,因为 n 变为 2倍,那么 n-1 的 mask 范围在高位多 1 bit(红色),因此新的index就会发生这样的变化: 74 | 75 | ![image-20210113115401801](http://blog-img.coolsen.cn/img/image-20210113115401801.png) 76 | 77 | 1. JDK1.7 中 rehash 的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置(头插法)。JDK1.8 不会倒置,使用尾插法。 78 | 79 | 下图为 16扩充为 32 的 resize 示意图: 80 | 81 | ![JDK1.8中resize示意图](http://blog-img.coolsen.cn/img/image-20210113120605102.png) 82 | 83 | 感兴趣的小伙伴可以看下 JDK1.8 的 resize 源码: 84 | 85 | ```java 86 | final Node[] resize() { 87 | Node[] oldTab = table; 88 | int oldCap = (oldTab == null) ? 0 : oldTab.length; 89 | int oldThr = threshold; 90 | int newCap, newThr = 0; 91 | if (oldCap > 0) { 92 | // 超过最大值就不再扩充了,就只好随你碰撞去吧 93 | if (oldCap >= MAXIMUM_CAPACITY) { 94 | threshold = Integer.MAX_VALUE; 95 | return oldTab; 96 | } 97 | // 没超过最大值,就扩充为原来的2倍 98 | else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 99 | oldCap >= DEFAULT_INITIAL_CAPACITY) 100 | newThr = oldThr << 1; // double threshold 101 | } 102 | else if (oldThr > 0) // initial capacity was placed in threshold 103 | newCap = oldThr; 104 | else { // zero initial threshold signifies using defaults 105 | newCap = DEFAULT_INITIAL_CAPACITY; 106 | newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 107 | } 108 | // 计算新的resize上限 109 | if (newThr == 0) { 110 | 111 | float ft = (float)newCap * loadFactor; 112 | newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 113 | (int)ft : Integer.MAX_VALUE); 114 | } 115 | threshold = newThr; 116 | @SuppressWarnings({"rawtypes","unchecked"}) 117 | Node[] newTab = (Node[])new Node[newCap]; 118 | table = newTab; 119 | if (oldTab != null) { 120 | // 把每个bucket都移动到新的buckets中 121 | for (int j = 0; j < oldCap; ++j) { 122 | Node e; 123 | if ((e = oldTab[j]) != null) { 124 | oldTab[j] = null; 125 | if (e.next == null) 126 | newTab[e.hash & (newCap - 1)] = e; 127 | else if (e instanceof TreeNode) 128 | ((TreeNode)e).split(this, newTab, j, oldCap); 129 | else { // 链表优化重hash的代码块 130 | Node loHead = null, loTail = null; 131 | Node hiHead = null, hiTail = null; 132 | Node next; 133 | do { 134 | next = e.next; 135 | // 原索引 136 | if ((e.hash & oldCap) == 0) { 137 | if (loTail == null) 138 | loHead = e; 139 | else 140 | loTail.next = e; 141 | loTail = e; 142 | } 143 | // 原索引+oldCap 144 | else { 145 | if (hiTail == null) 146 | hiHead = e; 147 | else 148 | hiTail.next = e; 149 | hiTail = e; 150 | } 151 | } while ((e = next) != null); 152 | // 原索引放到bucket里 153 | if (loTail != null) { 154 | loTail.next = null; 155 | newTab[j] = loHead; 156 | } 157 | // 原索引+oldCap放到bucket里 158 | if (hiTail != null) { 159 | hiTail.next = null; 160 | newTab[j + oldCap] = hiHead; 161 | } 162 | } 163 | } 164 | } 165 | } 166 | return newTab; 167 | } 168 | ``` -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/24-generic.md: -------------------------------------------------------------------------------- 1 | Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型。 2 | 3 | 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 4 | 5 | ## 泛型带来的好处 6 | 7 | 在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。 8 | 9 | 那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。 10 | 11 | ``` 12 | public class GlmapperGeneric { 13 | private T t; 14 | public void set(T t) { this.t = t; } 15 | public T get() { return t; } 16 | 17 | public static void main(String[] args) { 18 | // do nothing 19 | } 20 | 21 | /** 22 | * 不指定类型 23 | */ 24 | public void noSpecifyType(){ 25 | GlmapperGeneric glmapperGeneric = new GlmapperGeneric(); 26 | glmapperGeneric.set("test"); 27 | // 需要强制类型转换 28 | String test = (String) glmapperGeneric.get(); 29 | System.out.println(test); 30 | } 31 | 32 | /** 33 | * 指定类型 34 | */ 35 | public void specifyType(){ 36 | GlmapperGeneric glmapperGeneric = new GlmapperGeneric(); 37 | glmapperGeneric.set("test"); 38 | // 不需要强制类型转换 39 | String test = glmapperGeneric.get(); 40 | System.out.println(test); 41 | } 42 | } 43 | ``` 44 | 45 | 上面这段代码中的 specifyType 方法中 省去了强制转换,可以在编译时候检查类型安全,可以用在类,方法,接口上。 46 | 47 | ## 泛型中通配符 48 | 49 | 我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢? 50 | 51 | ### 常用的 T,E,K,V,? 52 | 53 | 本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。**通常情况下,T,E,K,V,?是这样约定的:** 54 | 55 | - ?表示不确定的 java 类型 56 | - T (type) 表示具体的一个java类型 57 | - K V (key value) 分别代表java键值中的Key Value 58 | - E (element) 代表Element 59 | 60 | ### ?无界通配符 61 | 62 | 先从一个小例子看起,原文在 这里 。 63 | 64 | 我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表,我的第一个想法是像这样的: 65 | 66 | ``` 67 | List listAnimals 68 | ``` 69 | 70 | 但是老板的想法确实这样的: 71 | 72 | ``` 73 | List listAnimals 74 | ``` 75 | 76 | 为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。 77 | 78 | ``` 79 | static int countLegs (List animals ) { 80 | int retVal = 0; 81 | for ( Animal animal : animals ) 82 | { 83 | retVal += animal.countLegs(); 84 | } 85 | return retVal; 86 | } 87 | 88 | static int countLegs1 (List< Animal > animals ){ 89 | int retVal = 0; 90 | for ( Animal animal : animals ) 91 | { 92 | retVal += animal.countLegs(); 93 | } 94 | return retVal; 95 | } 96 | 97 | public static void main(String[] args) { 98 | List dogs = new ArrayList<>(); 99 | // 不会报错 100 | countLegs( dogs ); 101 | // 报错 102 | countLegs1(dogs); 103 | } 104 | ``` 105 | 106 | 当调用 countLegs1 时,就会飘红,提示的错误信息如下: 107 | 108 | ![](http://img.topjavaer.cn/img/泛型1.png) 109 | 110 | 所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 ),表示可以持有任何类型。像 countLegs 方法中,限定了上界,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错。而 countLegs1 就不行。 111 | 112 | ### 上界通配符 < ? extends E> 113 | 114 | > 上界:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。 115 | 116 | 在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处: 117 | 118 | - 如果传入的类型不是 E 或者 E 的子类,编译不成功 119 | - 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用 120 | 121 | ``` 122 | private E test(K arg1, E arg2){ 123 | E result = arg2; 124 | arg2.compareTo(arg1); 125 | //..... 126 | return result; 127 | } 128 | ``` 129 | 130 | > 类型参数列表中如果有多个类型参数上限,用逗号分开 131 | 132 | ### 下界通配符 < ? super E> 133 | 134 | > 下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object 135 | 136 | 在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。 137 | 138 | ``` 139 | private void test(List dst, List src){ 140 | for (T t : src) { 141 | dst.add(t); 142 | } 143 | } 144 | 145 | public static void main(String[] args) { 146 | List dogs = new ArrayList<>(); 147 | List animals = new ArrayList<>(); 148 | new Test3().test(animals,dogs); 149 | } 150 | // Dog 是 Animal 的子类 151 | class Dog extends Animal { 152 | 153 | } 154 | ``` 155 | 156 | dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。 157 | 158 | ### ?和 T 的区别 159 | 160 | ![](http://img.topjavaer.cn/img/泛型2.png) 161 | 162 | ?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 : 163 | 164 | ``` 165 | // 可以 166 | T t = operate(); 167 | 168 | // 不可以 169 | ?car = operate(); 170 | ``` 171 | 172 | 简单总结下: 173 | 174 | T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。 175 | 176 | #### 区别1:通过 T 来 确保 泛型参数的一致性 177 | 178 | ``` 179 | // 通过 T 来 确保 泛型参数的一致性 180 | public void 181 | test(List dest, List src) 182 | 183 | //通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型 184 | public void 185 | test(List dest, List src) 186 | ``` 187 | 188 | 像下面的代码中,约定的 T 是 Number 的子类才可以,但是申明时是用的 String ,所以就会飘红报错。 189 | 190 | ![](http://img.topjavaer.cn/img/泛型3.png) 191 | 192 | 不能保证两个 List 具有相同的元素类型的情况 193 | 194 | ``` 195 | GlmapperGeneric glmapperGeneric = new GlmapperGeneric<>(); 196 | List dest = new ArrayList<>(); 197 | List src = new ArrayList<>(); 198 | glmapperGeneric.testNon(dest,src); 199 | ``` 200 | 201 | 上面的代码在编译器并不会报错,但是当进入到 testNon 方法内部操作时(比如赋值),对于 dest 和 src 而言,就还是需要进行类型转换。 202 | 203 | #### 区别2:类型参数可以多重限定而通配符不行 204 | 205 | ![](http://img.topjavaer.cn/img/泛型4.png) 206 | 207 | 使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。 208 | 209 | #### 区别3:通配符可以使用超类限定而类型参数不行 210 | 211 | 类型参数 T 只具有 一种 类型限定方式: 212 | 213 | ``` 214 | T extends A 215 | ``` 216 | 217 | 但是通配符 ? 可以进行 两种限定: 218 | 219 | ``` 220 | ? extends A 221 | ? super A 222 | ``` 223 | 224 | ## `Class`和 `Class`区别 225 | 226 | 前面介绍了 ?和 T 的区别,那么对于,`Class`和 ``又有什么区别呢?`Class`和 `Class` 227 | 228 | 最常见的是在反射场景下的使用,这里以用一段发射的代码来说明下。 229 | 230 | ``` 231 | // 通过反射的方式生成 multiLimit 232 | // 对象,这里比较明显的是,我们需要使用强制类型转换 233 | MultiLimit multiLimit = (MultiLimit) 234 | Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance(); 235 | ``` 236 | 237 | 对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。 238 | 239 | 对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接 检查到类型的问题: 240 | 241 | ![](http://img.topjavaer.cn/img/泛型5.png) 242 | 243 | `Class`在实例化的时候,T 要替换成具体类。`Class`它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明: 244 | 245 | ``` 246 | // 可以 247 | public Class clazz; 248 | // 不可以,因为 T 需要指定类型 249 | public Class clazzT; 250 | ``` 251 | 252 | 所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class。 253 | 254 | ```java 255 | public class Test3 { 256 | public Class clazz; 257 | public Class clazzT; 258 | } 259 | ``` 260 | 261 | 那如果也想 `public Class clazzT;`这样的话,就必须让当前的类也指定 T , 262 | 263 | ```java 264 | public class Test3 { 265 | public Class clazz; 266 | // 不会报错 267 | public Class clazzT; 268 | } 269 | ``` 270 | 271 | ## 小结 272 | 273 | 本文零碎整理了下 JAVA 泛型中的一些点,不是很全,仅供参考。如果文中有不当的地方,欢迎指正。 274 | 275 | > 转自:juejin.im/post/5d5789d26fb9a06ad0056bd9 -------------------------------------------------------------------------------- /docs/guide/memoir/4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 《面试实录》 阿里一面,深剖Map底层结构与原理 3 | category: 4 | - 对线面试官 5 | - 面试实录 6 | - Java 7 | tag: 8 | - Java 9 | - Map 10 | head: 11 | - - meta 12 | - name: keywords 13 | content: 面试实录,集合,JAVA,大厂面试,Map 14 | - name: description 15 | content: 阿里一面面试现场,深剖Java Map底层结构与原理。涉及知识点:Map 常使用实现类使用场景,特性;Hash算法;HashMap原 理剖析;分段锁;ConcurrentHashMap; 16 | --- 17 | 本期是【**面试实录**】系列文章的第 **4** 期,持续更新中.....。 18 | 19 | - 欢迎关注+订阅,持续更新中!!!致力打造校招核心面试攻略~ 20 | - 根据秋招春招上岸大厂面试经历以及身边朋友上岸面试录音模拟面试现场,并整合面试常考知识点,通俗有趣的去讲解 "八股文",不一样的系列,轻松掌握知识~ 21 | 22 | 【**面试实录**】专栏系列目前已经连载 **4** 篇了,据说看了这个系列的朋友都拿到了大厂offer~ 23 | 24 | - [【面试实录】百度二面,Sychronized原理详解](./1) 25 | - [【面试实录】字节二面,SQL执行慢的原因?如何优化?](./2) 26 | - [《面试实录》 字节二面, MySQL自增主键一定连续吗?](./3) 27 | 28 | ## 考点速查 29 | 30 | 本期会模拟面试 Map 相关内容。 31 | 32 | 涉及知识点,Map 常使用实现类使用场景,特性;Hash算法;HashMap原理剖析;分段锁;ConcurrentHashMap; 33 | 34 | `本期题改编自 ——2022届春招 阿里供应链 一面` 35 | 36 | ## 面试现场 37 | 38 | > 1111 39 | 40 | 叮叮叮...... 41 | 42 | **面试官**:“你好,我是XX面试官,请问是小龙吗?” 43 | 44 | **小龙**:“您好,面试官,我是小龙” 45 | 46 | **面试官**:“好的,现在有空吗,我们开始面试吧” 47 | 48 | **小龙**:“嗯嗯,准备好啦” 49 | 50 | ....... 51 | 52 | other questions 53 | 54 | ....... 55 | 56 | **面试官**:“我看你简历上有提到你已经是 ‘老Map’ 了对吧。” 57 | 58 | **小龙**:“是的” 59 | 60 | **面试官**:“好的,那 `Map` 接口下,你常用的实现类有哪些?能简单聊聊吗?” 61 | 62 | **独白**:“又得扯HashMap ConCurrentHashMap 了....。” 63 | 64 | **小龙**:“我经常使用**HashMap**、 **LinkedHashMap**、**TreeMap**这些。” 65 | 66 | **面试官**:“那它们分别在什么场景下使用呢?” 67 | 68 | **小龙**:“一般情况涉及到键值对的存取,我们都会第一时间考虑使用 `HashMap`。不过,在某些特定场景,比如:我们需要根据 key 顺序去存储键值对,TreeMap 可能就更合适啦。“ 69 | 70 | **小龙**:”因为 TreeMap 底层是基于**红黑树**实现的,可以提供顺序访问,不过 `TreeMap` 键值都不能为 null,而且时间复杂度是 O(logn)。因此,在实际应用场景中,应当综合考虑二者的特性去择优处理。“ 71 | 72 | **小龙**:”而对于 `LinkedHashMap`,它的实现是通过为条目(键 值对)维护一个双向链表。平时主要用来处理一些特定的应用场景。” 73 | 74 | **小龙**:”比如:`去构建一个对空间占用很看重的一个资源池,可以自动将不常用的资源释放掉`,例如去模拟 LRU 缓存淘汰策略,就可以使用 LinkedHashMap 去模拟。“ 75 | 76 | **面试官**:“好的,我们接着往下聊哈。那你有了解过 `HashMap` 的底层实现原理吗?平时有没有仔细研究过它的源码呢?” 77 | 78 | **小龙**:“这个有仔细研读过,收获颇多。” 79 | 80 | **面试官**:“那首先你能给我讲讲你对 `Hash` 算法的理解吗?” 81 | 82 | **小龙**:“简单来说,其实它就是一种将任意长度的输入转为为固定长度的输出的映射规则。” 83 | 84 | **面试官**:“那这个映射会不会有什么问题呢?” 85 | 86 | **小龙**:“当然,由于任意长度—>固定长度,随着 hash 次数增加,后面必定出现 **哈希冲突**。” 87 | 88 | **面试官**:“那这个冲突能避免吗?” 89 | 90 | **小龙**:“hash冲突不可避免 只能说减少冲突。” 91 | 92 | **面试官**:“那你有了解过哪些方法可以去解决这个 Hash 冲突呢?” 93 | 94 | **小龙**:“嗯,比如:拉链法、开放地址、多哈希算法,当然在分布式某些场景中,我们还可以使用一致性Hash算法。” 95 | 96 | **面试官**:“好的,那接下来你说说 HashMap 的底层数据结构吧!” 97 | 98 | **小龙**:“JDK1.7 及以前是数组+链表,JDK1.8 是数组+链表+红黑树。” 99 | 100 | **面试官**:“默认负载因子是多少呢,并且这个负载因子有什么作用?” 101 | 102 | **小龙**:“负载因子默认0.75,它是在计算扩容阈值时用的。” 103 | 104 | **面试官**:“创建 HashMap 时,不指定散列表数组长度,初始长度是多少呢?” 105 | 106 | **独白**:“wc,问这么简单吗?” 107 | 108 | **小龙**:“默认初始长度16啊。” 109 | 110 | **面试官**:“那散列表是` new HashMap() `时创建的么?” 111 | 112 | **独白**:“这个到是稍微有点意思~” 113 | 114 | **小龙**:“不是在`new HashMap()`创建的,它使用懒加载,是当第一次调用 put() 方法时 执行putVal() 时才创建散列表。” 115 | 116 | **面试官**:“那说说 HashMap put() 写数据的具体流程吧,尽可能的详细点!” 117 | 118 | **独白**:“好吧,本来想以普通人身份相处,换来的却是疏远,不装了,摊牌了。是你叫我详细一点的,我可以直接把源码一条龙给你背下来.....。” 119 | 120 | **小龙**:“ 1、首先map.put(“公众号” , “小龙coding”); ” 121 | 122 | **小龙**:“2、调用 key 对象的 hashcode() 方法计算 key-"公众号" 的hash 。” 123 | 124 | **小龙**:“3、然后经过扰动函数使其 hash 值更散列(调用 key 对象的 hashcode() 方法计算出来 hash 值,将 hash 值的高 16 位右移并与原 hash 值取异或运算(^),混合高 16 位和低 16 位的值,得到一个更加散列的低 16 位的 hash 值)” 125 | 126 | **小龙**:“4、接下来进入putVal() 方法,判断散列表是否为空 也就是 put() 方法第一次调用才初始化 HashMap 的存储结构 Node[] table 散列表 初始为数组长度16” 127 | 128 | **小龙**:“5、调用 (n - 1) & hash 【细节解释:(**散列表数组长度-1**) 与 (**hash值得到将要把元素插到哪里的数组下标**)】” 129 | 130 | **小龙**:“6、判断数组该位置是否为空” 131 | 132 | **小龙**:“如果为空 新创建一个结点直接插入 tab[i] = newNode(hash, key, value, null);如果插入位置已经有值了tab[i]!=null,并且桶位中的该元素,与你当前插入的元素的 key 完全一致,表示后续需要进行替换操作,否则就需要往该结点后添加元素。” 133 | 134 | **独白**:“估计面试官与正在看的你已经蒙了,这是为了全面细致的拉通分析一遍,面试可以简单的说。” 135 | 136 | **小龙**:“插入前需要判断是否为树结构,若为树结构按照树结构的插入结点方法插入,不是树结构则按照链式结构插入结点方法插入。” 137 | 138 | **小龙**:“若为链表结构,遍历改链表,判断是否有与你要插入的 key 一致的 node。” 139 | 140 | **小龙**:“如果没有则将结点插入到该链表末尾(1.8尾插法 1.7头插法),并判断插入后是否达到树化条件(链表长度>=8 进入treeifyBin(tab, hash);进入该方法还需要判断当前数组长度>=64才能树化,如果<64则扩容)” 141 | 142 | **小龙**:“到相同元素则需要替换。” 143 | 144 | **小龙**:“7、完成插入操作了 ++modCount(散列表结构结构被修改的次数–替换 Node 元素的 value 不算)” 145 | 146 | **小龙**:“8、最好 size 自增,如果自增后的值大于扩容阈值,则触发扩容 resize();” 147 | 148 | **独白**:“没有源码,这里可能基础差的看起来很吃力,需要看全代码跟踪解析,有每一步调试+注释,一步一步跟着方法进,注释写的很清楚,需要可以公众号【**小龙coding**】后台回复【**HashMap**】” 149 | 150 | **面试官**:“叫你说详细一点,用不着这么详细,哈哈。” 151 | 152 | **面试官**:“我们加快脚步了,Node对象内部的 hash 字段,这个 `hash` 值是 key 对象的 hashcode() 返回值么?” 153 | 154 | **小龙**:“Node 对象里面的 hash 值并不是直接 key.hashcode() 得到, 还要经过 `扰动函数`。“ 155 | 156 | **面试官**:”这个 hash 值是怎么得到呢?“ 157 | 158 | **小龙**:”将 hash 值的高 16 位右移并与原 Hash 值取异或运算(^),混合高 16 位和低 16 位的值,得到一个更加散列的低 16 位的 Hash 值。“ 159 | 160 | **面试官**:”JDK8 HashMap 为什么引入红黑树?解决什么问题?“ 161 | 162 | **小龙**:”引入红黑树我认为是这样 当产生 hash 冲突时会形成链表 当数据多了冲突多了 链表越来越长 造成链化 此时查询将特别耗时 本来时间复杂度为O(1) 结构可能达到 O(n),引入红黑树可以优化查询。“ 163 | 164 | **面试官**:”HashMap 什么情况下会触发扩容呢?“ 165 | 166 | **小龙**:”当它未初始化,第一次 put 时会触发扩容。后面插入值,当大于扩容阈值时也会触发扩容。“ 167 | 168 | **面试官**:“`HashMap` 如何确定元素放在哪个位置呢?” 169 | 170 | **小龙**:“首先经过扰动函数计算得到hash值;然后通过 **(n - 1) & hash** 判断当前元素存放的位置。” 171 | 172 | **面试官**:”`HashMap` 有什么问题吗?在实际应用场景中。“ 173 | 174 | **小龙**:”因为 HashMap 非线程安全,可能出现并发线程安全问题。在JDK1.7中,当并发执行扩容操作会造成环形链,然后调用 get 方法会死循环。JDK1.8中,并发执行put操作时会发生**数据覆盖**的操作。“ 175 | 176 | **面试官**:”那有什么解决办法吗?“ 177 | 178 | **小龙**:”可以使用 `Hashtable`,因为它的方法加了` synchronized`,可以做到线程安全。“ 179 | 180 | **小龙**:”不过,由于它锁的是整个表,导致效率低下。因此,我们一般使用的是 `ConcurrentHashMap`“ 181 | 182 | **面试官**:“好的,那你能简单介绍一下 `ConcurrentHashMap` 吗?为何它的性能效率更高呢?” 183 | 184 | **小龙**:”ConcurrentHashMap JDK1.7 引入了`分段锁`,数据结构采用Segment数组+HashEntry数组+链表。`Segment` 继承了 ReentrantLock,一个 Segment[i] 就是一把分段锁。比起 Hashtable 锁粒度更细,性能更高。” 185 | 186 | ```Java 187 | 一个Segment中包含一个HashEntry数组,每个HashEntry又是一个链表结构 188 | 189 | static final class Segment extens ReentrantLock implements Serializable{ 190 | 191 | transient volatile HashEntry[] tables; 192 | //..... 193 | } 194 | static final class HashEntry 195 | { 196 | final int hash; 197 | final K key; 198 | volatile V value; 199 | volatile HashEntry next; 200 | } 201 | ``` 202 | 203 | **面试官**:“何为分段锁?” 204 | 205 | ![img](https://xiaolongcoder.oss-cn-beijing.aliyuncs.com/imgs/Java2Top/concurrent202303261219335.webp) 206 | 207 | **小龙**:“一个 `Segment` 就相当于一把锁,它只锁住这个槽位,其他的并不受影响。`ConcurrentHashMap` 将 hash 表分为 16 个桶(默认值),诸如get,put,remove 等常用操作只锁当前需要用到的桶。” 208 | 209 | **小龙**:“试想,原来 只能一个线程进入,现在却能同时16个写线程进入(写线程才需要锁定,而读线程几乎不受限制,之后会提到),并发性的提升是显而易见的。” 210 | 211 | **面试官**:“不过我们现在都基本用 JDK1.8 啦,JDK1.8 它也是使用分段锁吗?” 212 | 213 | **小龙**:“`JDK1.8` 后,它做出了很大改变,**数据结构**采用Node数组+链表+红黑树,抛弃Segment分段锁,采用`CAS+synchronized`,锁粒度更细,只锁住链表头节点(红黑树根结点),不影响其他哈希桶数组元素的读写,提高并发度。” 214 | 215 | **面试官**:“好的,挺不错的。最后一个问题,你知道 `ConcurrentHashMap` 为什么不支持 null value吗?” 216 | 217 | **小龙**:“这个很简单啊,vaule 为 null,有两种情况,可能是存的值为 null 或则是没有映射到值 返回null;” 218 | 219 | **小龙**:”`HashMap` 用于单线程下可以通过 ContainsKey() 区分这两种情况;“ 220 | 221 | **小龙**:“但是 `ConcurrentHashMap`用于多线程场景,本来是没有映射 ContainsKey() 返回fasle,但是可能在你调用 ContainsKey() 检查时新线程插入null值,返回ture,存在二义性” 222 | 223 | **面试官**:“牛逼,基础很好!继续加油。” 224 | 225 | **独白**:“不愧是我,真男人是也!” 226 | 227 | ## 知识总结 228 | 229 | 本期我们通过面试模拟简单介绍了Map相关面试中重点考察的内容,重点剖析了 `HashMap` 与 `ConcurrentHashMap` 相关集合。 230 | 231 | **面试重点** 232 | 233 | Map 常使用实现类使用场景,特性;Hash算法理解;HashMap原理剖析;分段锁理解;ConcurrentHashMap原理,底层数据机构,JDK1.8 与JDK1.7 区别。 234 | 235 | -------------------------------------------------------------------------------- /docs/guide/java/distributed/2-distributed-lock.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 分布式锁详解 4 | --- 5 | 6 | # 分布式锁 7 | 8 | ## 为什么要使用分布式锁 9 | 10 | 在单机环境下,当存在多个线程可以同时改变某个变量(可变共享变量)时,就会出现线程安全问题。这个问题可以通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一些线程安全的类等来避免。 11 | 12 | 而在多机部署环境,需要在多进程下保证线程的安全性,Java提供的这些API仅能保证在单个JVM进程内对多线程访问共享资源的线程安全,已经不满足需求了。这时候就需要使用分布式锁来保证线程安全。通过分布式锁,可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。 13 | 14 | ## 分布式锁应该具备哪些条件 15 | 16 | 在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件: 17 | 18 | 1. 互斥性。在任意时刻,只有一个客户端能持有锁。 19 | 2. 不会死锁。具备锁失效机制,防止死锁。即使有客户端在持有锁的期间崩溃而没有主动解锁,也要保证后续其他客户端能加锁。 20 | 3. 加锁和解锁必须是同一个客户端。客户端a不能将客户端b的锁解开,即不能误解锁。 21 | 4. 高性能、高可用的获取锁与释放锁。 22 | 5. 具备可重入特性。 23 | 6. 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。 24 | 25 | ## 分布式锁的三种实现方式 26 | 27 | 1. 基于数据库实现分布式锁; 28 | 2. 基于缓存(Redis等)实现分布式锁; 29 | 3. 基于Zookeeper实现分布式锁。 30 | 31 | ### 基于数据库的实现方式 32 | 33 | **悲观锁** 34 | 35 | 创建一张锁表,然后通过操作该表中的数据来实现加锁和解锁。当要锁住某个方法或资源时,就向该表插入一条记录,表中设置方法名为唯一键,这样多个请求同时提交数据库时,只有一个操作可以成功,判定操作成功的线程获得该方法的锁,可以执行方法内容;想要释放锁的时候就删除这条记录,其他线程就可以继续往数据库中插入数据获取锁。 36 | 37 | **乐观锁** 38 | 39 | 每次更新操作,都觉得不会存在并发冲突,只有更新失败后,才重试。 40 | 41 | 扣减余额就可以使用这种方案。 42 | 43 | 具体实现:增加个version字段,每次更新修改,都会自增加一,然后去更新余额时,把查出来的那个版本号,带上条件去更新,如果是上次那个版本号,就更新,如果不是,表示别人并发修改过了,就继续重试。 44 | 45 | ### 基于Redis的实现方式 46 | 47 | #### 简单实现 48 | 49 | Redis 2.6.12 之前的版本中采用 setnx + expire 方式实现分布式锁,在 Redis 2.6.12 版本后 setnx 增加了过期时间参数: 50 | 51 | ```java 52 | SET lockKey value NX PX expire-time 53 | ``` 54 | 55 | 所以在Redis 2.6.12 版本后,只需要使用setnx就可以实现分布式锁了。 56 | 57 | 加锁逻辑: 58 | 59 | 1. setnx争抢key的锁,如果已有key存在,则不作操作,过段时间继续重试,保证只有一个客户端能持有锁。 60 | 2. value设置为 requestId(可以使用机器ip拼接当前线程名称),表示这把锁是哪个请求加的,在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。 61 | 3. 再用expire给锁加一个过期时间,防止异常导致锁没有释放。 62 | 63 | 解锁逻辑: 64 | 65 | 首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。使用lua脚本实现原子操作,保证线程安全。 66 | 67 | 下面我们通过Jedis(基于java语言的redis客户端)来演示分布式锁的实现。 68 | 69 | **Jedis实现分布式锁** 70 | 71 | 引入Jedis jar包,在pom.xml文件增加代码: 72 | 73 | ```xml 74 | 75 | redis.clients 76 | jedis 77 | 2.9.0 78 | 79 | ``` 80 | 81 | **加锁** 82 | 83 | 调用jedis的set()实现加锁,加锁代码如下: 84 | 85 | ```java 86 | public class RedisTest { 87 | 88 | private static final String LOCK_SUCCESS = "OK"; 89 | private static final String SET_IF_NOT_EXIST = "NX"; 90 | private static final String SET_EXPIRE_TIME = "PX"; 91 | 92 | @Autowired 93 | private JedisPool jedisPool; 94 | 95 | public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) { 96 | Jedis jedis = jedisPool.getResource(); 97 | String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_EXPIRE_TIME, expireTime); 98 | 99 | if (LOCK_SUCCESS.equals(result)) { 100 | return true; 101 | } 102 | return false; 103 | } 104 | } 105 | ``` 106 | 107 | 各参数说明: 108 | 109 | - lockKey:使用key来当锁,需要保证key是唯一的。可以使用系统号拼接自定义的key。 110 | - requestId:表示这把锁是哪个请求加的,可以使用机器ip拼接当前线程名称。在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。 111 | - NX:意思是SET IF NOT EXIST,保证如果已有key存在,则不作操作,过段时间继续重试。NX参数保证只有一个客户端能持有锁。 112 | - PX:给key加一个过期的设置,具体时间由expireTime决定。 113 | - expireTime:设置key的过期时间,防止异常导致锁没有释放。 114 | 115 | **解锁** 116 | 117 | 首先需要获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。这里使用lua脚本实现原子操作,保证线程安全。 118 | 119 | 使用eval命令执行Lua脚本的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。lua脚本如下: 120 | 121 | ``` 122 | //KEYS[1]是lockKey,ARGV[1]是requestId 123 | String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 124 | Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); 125 | ``` 126 | 127 | Jedis的eval()方法源码如下: 128 | 129 | ```java 130 | public Object eval(String script, List keys, List args) { 131 | return this.eval(script, keys.size(), getParams(keys, args)); 132 | } 133 | ``` 134 | 135 | lua脚本的意思是:调用get获取锁(KEYS[1])对应的value值,检查是否与requestId(ARGV[1])相等,如果相等则调用del删除锁。否则返回0。 136 | 137 | 完整的解锁代码如下: 138 | 139 | ```java 140 | public class RedisTest { 141 | private static final Long RELEASE_SUCCESS = 1L; 142 | 143 | @Autowired 144 | private JedisPool jedisPool; 145 | 146 | public boolean releaseDistributedLock(String lockKey, String requestId) { 147 | Jedis jedis = jedisPool.getResource(); 148 | ////KEYS[1]是lockKey,ARGV[1]是requestId 149 | String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 150 | Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); 151 | 152 | if (RELEASE_SUCCESS.equals(result)) { 153 | return true; 154 | } 155 | return false; 156 | } 157 | } 158 | ``` 159 | 160 | #### RedLock 161 | 162 | 前面的方案是基于**Redis单机版**的分布式锁讨论,还不是很完美。因为Redis一般都是集群部署的。 163 | 164 | 如果线程一在`Redis`的`master`节点上拿到了锁,但是加锁的`key`还没同步到`slave`节点。恰好这时,`master`节点发生故障,一个`slave`节点就会升级为`master`节点。线程二就可以顺理成章获取同个`key`的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。 165 | 166 | 为了解决这个问题,Redis作者antirez提出一种高级的分布式锁算法:**Redlock**。它的核心思想是这样的: 167 | 168 | 部署多个Redis master,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。 169 | 170 | 我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。 171 | 172 | RedLock的实现步骤: 173 | 174 | 1. 获取当前时间,以毫秒为单位。 175 | 2. 按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。 176 | 3. 客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms) 177 | 4. 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。 178 | 5. 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。 179 | 180 | 简化下步骤就是: 181 | 182 | - 按顺序向5个master节点请求加锁 183 | - 根据设置的超时时间来判断,是不是要跳过该master节点。 184 | - 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。 185 | - 如果获取锁失败,解锁! 186 | 187 | ### 基于ZooKeeper的实现方式 188 | 189 | ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下: 190 | 191 | (1)创建一个目录mylock; 192 | (2)线程A想获取锁就在mylock目录下创建临时顺序节点; 193 | (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; 194 | (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; 195 | (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。 196 | 197 | 这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。 198 | 199 | 优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。 200 | 201 | 缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。 202 | 203 | ## 三种实现方式对比 204 | 205 | **数据库分布式锁实现** 206 | 207 | 优点: 208 | 209 | - 简单,使用方便,不需要引入`Redis、zookeeper`等中间件。 210 | 211 | 缺点: 212 | 213 | - 不适合高并发的场景 214 | - db操作性能较差; 215 | 216 | **Redis分布式锁实现** 217 | 218 | 优点: 219 | 220 | - 性能好,适合高并发场景 221 | - 较轻量级 222 | - 有较好的框架支持,如Redisson 223 | 224 | 缺点: 225 | 226 | - 过期时间不好控制 227 | - 需要考虑锁被别的线程误删场景 228 | 229 | **Zookeeper分布式锁实现** 230 | 231 | 缺点: 232 | 233 | - 性能不如redis实现的分布式锁 234 | - 比较重的分布式锁。 235 | 236 | 优点: 237 | 238 | - 有较好的性能和可靠性 239 | - 有封装较好的框架,如Curator 240 | 241 | **对比汇总** 242 | 243 | - 从性能角度(从高到低)Redis > Zookeeper >= 数据库; 244 | - 从理解的难易程度角度(从低到高)数据库 > Redis > Zookeeper; 245 | - 从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库; 246 | - 从可靠性角度(从高到低)Zookeeper > Redis > 数据库。 247 | -------------------------------------------------------------------------------- /docs/guide/advance/excellent-article/17-limit-scheme.md: -------------------------------------------------------------------------------- 1 | # 限流的几种方案 2 | 3 | ### 文章目录 4 | 5 | - 限流基本概念 6 | 7 | - - QPS和连接数控制 8 | - 传输速率 9 | - 黑白名单 10 | - 分布式环境 11 | 12 | - 限流方案常用算法 13 | 14 | - - 令牌桶算法 15 | - 漏桶算法 16 | - 滑动窗口 17 | 18 | - 常用的限流方案 19 | 20 | - - Nginx限流 21 | - 中间件限流 22 | - 限流组件 23 | - 合法性验证限流 24 | - Guawa限流 25 | - 网关层限流 26 | 27 | - 从架构维度考虑限流设计 28 | 29 | - 具体的实现限流的手段: 30 | 31 | - - Tomcat限流 32 | 33 | ### 限流基本概念 34 | 35 | 对一般的限流场景来说它具有两个维度的信息: 36 | 37 | - 时间 限流基于某段时间范围或者某个时间点,也就是我们常说的“时间窗口”,比如对每分钟、每秒钟的时间窗口做限定 38 | - 资源 基于可用资源的限制,比如设定最大访问次数,或最高可用连接数 39 | 40 | 上面两个维度结合起来看,限流就是在某个时间窗口对资源访问做限制,比如设定每秒最多100个访问请求。但在真正的场景里,我们不止设置一种限流规则,而是会设置多个限流规则共同作用,主要的几种限流规则如下: 41 | 42 | ##### QPS和连接数控制 43 | 44 | 对于连接数和QPS)限流来说,我们可设定IP维度的限流,也可以设置基于单个服务器的限流。 45 | 46 | 在真实环境中通常会设置多个维度的限流规则,比如设定同一个IP每秒访问频率小于10,连接数小于5,再设定每台机器QPS最高1000,连接数最大保持200。更进一步,我们可以把某个服务器组或整个机房的服务器当做一个整体,设置更high-level的限流规则,这些所有限流规则都会共同作用于流量控制。 47 | 48 | ##### 传输速率 49 | 50 | 对于“传输速率”大家都不会陌生,比如资源的下载速度。有的网站在这方面的限流逻辑做的更细致,比如普通注册用户下载速度为100k/s,购买会员后是10M/s,这背后就是基于用户组或者用户标签的限流逻辑。 51 | 52 | ##### 黑白名单 53 | 54 | 黑白名单是各个大型企业应用里很常见的限流和放行手段,而且黑白名单往往是动态变化的。举个例子,如果某个IP在一段时间的访问次数过于频繁,被系统识别为机器人用户或流量攻击,那么这个IP就会被加入到黑名单,从而限制其对系统资源的访问,这就是我们俗称的“封IP”。 55 | 56 | 我们平时见到的爬虫程序,比如说爬知乎上的美女图片,或者爬券商系统的股票分时信息,这类爬虫程序都必须实现更换IP的功能,以防被加入黑名单。 57 | 58 | 有时我们还会发现公司的网络无法访问12306这类大型公共网站,这也是因为某些公司的出网IP是同一个地址,因此在访问量过高的情况下,这个IP地址就被对方系统识别,进而被添加到了黑名单。使用家庭宽带的同学们应该知道,大部分网络运营商都会将用户分配到不同出网IP段,或者时不时动态更换用户的IP地址。 59 | 60 | 白名单就更好理解了,相当于御赐金牌在身,可以自由穿梭在各种限流规则里,畅行无阻。比如某些电商公司会将超大卖家的账号加入白名单,因为这类卖家往往有自己的一套运维系统,需要对接公司的IT系统做大量的商品发布、补货等等操作。 61 | 62 | ##### 分布式环境 63 | 64 | 分布式区别于单机限流的场景,它把整个分布式环境中所有服务器当做一个整体来考量。比如说针对IP的限流,我们限制了1个IP每秒最多10个访问,不管来自这个IP的请求落在了哪台机器上,只要是访问了集群中的服务节点,那么都会受到限流规则的制约。 65 | 66 | 我们最好将限流信息保存在一个“中心化”的组件上,这样它就可以获取到集群中所有机器的访问状态,目前有两个比较主流的限流方案: 67 | 68 | - 网关层限流 将限流规则应用在所有流量的入口处 69 | - 中间件限流 将限流信息存储在分布式环境中某个中间件里(比如Redis缓存),每个组件都可以从这里获取到当前时刻的流量统计,从而决定是拒绝服务还是放行流量 70 | - sentinel,springcloud生态圈为微服务量身打造的一款用于分布式限流、熔断降级等组件 71 | 72 | ### 限流方案常用算法 73 | 74 | ##### 令牌桶算法 75 | 76 | Token Bucket令牌桶算法是目前应用最为广泛的限流算法,顾名思义,它有以下两个关键角色: 77 | 78 | - 令牌 获取到令牌的Request才会被处理,其他Requests要么排队要么被直接丢弃 79 | - 桶 用来装令牌的地方,所有Request都从这个桶里面获取令牌 主要涉及到2个过程: 80 | - **令牌生成** 81 | 82 | 这个流程涉及到令牌生成器和令牌桶,前面我们提到过令牌桶是一个装令牌的地方,既然是个桶那么必然有一个容量,也就是说令牌桶所能容纳的令牌数量是一个固定的数值。 83 | 84 | 对于令牌生成器来说,它会根据一个预定的速率向桶中添加令牌,比如我们可以配置让它以每秒100个请求的速率发放令牌,或者每分钟50个。注意这里的发放速度是匀速,也就是说这50个令牌并非是在每个时间窗口刚开始的时候一次性发放,而是会在这个时间窗口内匀速发放。 85 | 86 | 在令牌发放器就是一个水龙头,假如在下面接水的桶子满了,那么自然这个水(令牌)就流到了外面。在令牌发放过程中也一样,令牌桶的容量是有限的,如果当前已经放满了额定容量的令牌,那么新来的令牌就会被丢弃掉。 87 | 88 | - **令牌获取** 89 | 90 | 每个访问请求到来后,必须获取到一个令牌才能执行后面的逻辑。假如令牌的数量少,而访问请求较多的情况下,一部分请求自然无法获取到令牌,那么这个时候我们可以设置一个“缓冲队列”来暂存这些多余的令牌。 91 | 92 | 缓冲队列其实是一个可选的选项,并不是所有应用了令牌桶算法的程序都会实现队列。当有缓存队列存在的情况下,那些暂时没有获取到令牌的请求将被放到这个队列中排队,直到新的令牌产生后,再从队列头部拿出一个请求来匹配令牌。 93 | 94 | 当队列已满的情况下,这部分访问请求将被丢弃。在实际应用中我们还可以给这个队列加一系列的特效,比如设置队列中请求的存活时间,或者将队列改造为PriorityQueue,根据某种优先级排序,而不是先进先出。 95 | 96 | ##### 漏桶算法 97 | 98 | Leaky Bucket,又是个桶,限流算法是跟桶杠上了,那么漏桶和令牌桶有什么不同呢, 99 | 100 | 漏桶算法的前半段和令牌桶类似,但是操作的对象不同,令牌桶是将令牌放入桶里,而漏桶是将访问请求的数据包放到桶里。同样的是,如果桶满了,那么后面新来的数据包将被丢弃。 101 | 102 | 漏桶算法的后半程是有鲜明特色的,它永远只会以一个恒定的速率将数据包从桶内流出。打个比方,如果我设置了漏桶可以存放100个数据包,然后流出速度是1s一个,那么不管数据包以什么速率流入桶里,也不管桶里有多少数据包,漏桶能保证这些数据包永远以1s一个的恒定速度被处理。 103 | 104 | - **漏桶 vs 令牌桶的区别** 105 | 106 | 根据它们各自的特点不难看出来,这两种算法都有一个“恒定”的速率和“不定”的速率。令牌桶是以恒定速率创建令牌,但是访问请求获取令牌的速率“不定”,反正有多少令牌发多少,令牌没了就干等。而漏桶是以“恒定”的速率处理请求,但是这些请求流入桶的速率是“不定”的。 107 | 108 | 从这两个特点来说,漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。而令牌桶则不同,其特性可以“预存”一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,但是导向后台系统的压力也会相应增多。 109 | 110 | ##### 滑动窗口 111 | 112 | 比如说,我们在每一秒内有5个用户访问,第5秒内有10个用户访问,那么在0到5秒这个时间窗口内访问量就是15。如果我们的接口设置了时间窗口内访问上限是20,那么当时间到第六秒的时候,这个时间窗口内的计数总和就变成了10,因为1秒的格子已经退出了时间窗口,因此在第六秒内可以接收的访问量就是20-10=10个。 113 | 114 | 滑动窗口其实也是一种计算器算法,它有一个显著特点,当时间窗口的跨度越长时,限流效果就越平滑。打个比方,如果当前时间窗口只有两秒,而访问请求全部集中在第一秒的时候,当时间向后滑动一秒后,当前窗口的计数量将发生较大的变化,拉长时间窗口可以降低这种情况的发生概率 115 | 116 | ### 常用的限流方案 117 | 118 | ##### 合法性验证限流 119 | 120 | 比如验证码、IP 黑名单等,这些手段可以有效的防止恶意攻击和爬虫采集; 121 | 122 | ##### Guawa限流 123 | 124 | 在限流领域中,Guava在其多线程模块下提供了以`RateLimiter`为首的几个限流支持类,但是作用范围仅限于“当前”这台服务器,也就是说Guawa的限流是单机的限流,跨了机器或者jvm进程就无能为力了 比如说,目前我有2台服务器[`Server 1`,`Server 2`],这两台服务器都部署了一个登陆服务,假如我希望对这两台机器的流量进行控制,比如将两台机器的访问量总和控制在每秒20以内,如果用Guava来做,只能独立控制每台机器的访问量<=10。 125 | 126 | 尽管Guava不是面对分布式系统的解决方案,但是其作为一个简单轻量级的客户端限流组件,非常适合来讲解限流算法 127 | 128 | ##### 网关层限流 129 | 130 | 服务网关,作为整个分布式链路中的第一道关卡,承接了所有用户来访请求,因此在网关层面进行限流是一个很好的切入点 上到下的路径依次是: 131 | 132 | 1. 用户流量从网关层转发到后台服务 133 | 2. 后台服务承接流量,调用缓存获取数据 134 | 3. 缓存中无数据,则访问数据库 135 | 136 | 流量自上而下是逐层递减的,在网关层聚集了最多最密集的用户访问请求,其次是后台服务。 137 | 138 | 然后经过后台服务的验证逻辑之后,刷掉了一部分错误请求,剩下的请求落在缓存上,如果缓存中没有数据才会请求漏斗最下方的数据库,因此数据库层面请求数量最小(相比较其他组件来说数据库往往是并发量能力最差的一环,阿里系的MySQL即便经过了大量改造,单机并发量也无法和Redis、Kafka之类的组件相比) 139 | 140 | 目前主流的网关层有以软件为代表的Nginx,还有Spring Cloud中的Gateway和Zuul这类网关层组件 141 | 142 | **Nginx限流** 143 | 144 | 在系统架构中,Nginx的代理与路由转发是其作为网关层的一个很重要的功能,由于Nginx天生的轻量级和优秀的设计,让它成为众多公司的首选,Nginx从网关这一层面考虑,可以作为最前置的网关,抵挡大部分的网络流量,因此使用Nginx进行限流也是一个很好的选择,在Nginx中,也提供了常用的基于限流相关的策略配置. 145 | 146 | Nginx 提供了两种限流方法:一种是控制速率,另一种是控制并发连接数。 147 | 148 | **控制速率** 149 | 150 | 我们需要使用 `limit_req_zone` 用来限制单位时间内的请求数,即速率限制, 151 | 152 | 因为Nginx的限流统计是基于毫秒的,我们设置的速度是 2r/s,转换一下就是500毫秒内单个IP只允许通过1个请求,从501ms开始才允许通过第2个请求。 153 | 154 | - **控制速率优化版** 155 | 156 | 上面的速率控制虽然很精准但是在生产环境未免太苛刻了,实际情况下我们应该控制一个IP单位总时间内的总访问次数,而不是像上面那样精确到毫秒,我们可以使用 burst 关键字开启此设置 157 | 158 | `burst=4`意思是每个IP最多允许4个突发请求 159 | 160 | **控制并发数** 161 | 162 | 利用 `limit_conn_zone` 和 `limit_conn` 两个指令即可控制并发数 163 | 164 | 其中 `limit_conn perip 10` 表示限制单个 IP 同时最多能持有 10 个连接;`limit_conn perserver 100` 表示 server 同时能处理并发连接的总数为 100 个。 165 | 166 | > 注意:只有当 request header 被后端处理后,这个连接才进行计数。 167 | 168 | **中间件限流** 169 | 170 | 对于分布式环境来说,无非是需要一个类似中心节点的地方存储限流数据。打个比方,如果我希望控制接口的访问速率为每秒100个请求,那么我就需要将当前1s内已经接收到的请求的数量保存在某个地方,并且可以让集群环境中所有节点都能访问。那我们可以用什么技术来存储这个临时数据呢? 171 | 172 | 那么想必大家都能想到,必然是redis了,利用Redis过期时间特性,我们可以轻松设置限流的时间跨度(比如每秒10个请求,或者每10秒10个请求)。同时Redis还有一个特殊技能–脚本编程,我们可以将限流逻辑编写成一段脚本植入到Redis中,这样就将限流的重任从服务层完全剥离出来,同时Redis强大的并发量特性以及高可用集群架构也可以很好的支持庞大集群的限流访问。【reids + lua】 173 | 174 | **限流组件** 175 | 176 | 除了上面介绍的几种方式以外,目前也有一些开源组件提供了类似的功能,比如Sentinel就是一个不错的选择。Sentinel是阿里出品的开源组件,并且包含在了Spring Cloud Alibaba组件库中,Sentinel提供了相当丰富的用于限流的API以及可视化管控台,可以很方便的帮助我们对限流进行治理 177 | 178 | ### 从架构维度考虑限流设计 179 | 180 | 在真实的项目里,不会只使用一种限流手段,往往是几种方式互相搭配使用,让限流策略有一种层次感,达到资源的最大使用率。在这个过程中,限流策略的设计也可以参考前面提到的漏斗模型,上宽下紧,漏斗不同部位的限流方案设计要尽量关注当前组件的高可用。 181 | 182 | 以我参与的实际项目为例,比如说我们研发了一个商品详情页的接口,通过手机淘宝导流,app端的访问请求首先会经过阿里的mtop网关,在网关层我们的限流会做的比较宽松,等到请求通过网关抵达后台的商品详情页服务之后,再利用一系列的中间件+限流组件,对服务进行更加细致的限流控制 183 | 184 | ### 具体的实现限流的手段 185 | 186 | 1)Tomcat 使用 maxThreads来实现限流。 187 | 188 | 2)Nginx的`limit_req_zone`和 burst来实现速率限流。 189 | 190 | 3)Nginx的`limit_conn_zone`和 `limit_conn`两个指令控制并发连接的总数。 191 | 192 | 4)时间窗口算法借助 Redis的有序集合可以实现。 193 | 194 | 5)漏桶算法可以使用Redis-Cell来实现。 195 | 196 | 6)令牌算法可以解决Google的guava包来实现。 197 | 198 | > 需要注意的是借助Redis实现的限流方案可用于分布式系统,而guava实现的限流只能应用于单机环境。如果你觉得服务器端限流麻烦,可以在不改任何代码的情况下直接使用容器限流(Nginx或Tomcat),但前提是能满足项目中的业务需求。 199 | 200 | ##### Tomcat限流 201 | 202 | Tomcat 8.5 版本的最大线程数在 `conf/server.xml` 配置中,maxThreads 就是 Tomcat 的最大线程数,当请求的并发大于此值(maxThreads)时,请求就会排队执行,这样就完成了限流的目的。 203 | 204 | 注意: 205 | 206 | > maxThreads 的值可以适当的调大一些,Tomcat默认为 150(Tomcat 版本 8.5),但这个值也不是越大越好,要看具体的服务器配置,需要注意的是每开启一个线程需要耗用 1MB 的 JVM 内存空间用于作为线程栈之用,并且线程越多 GC 的负担也越重。 207 | 208 | 最后需要注意一下,操作系统对于进程中的线程数有一定的限制,Windows 每个进程中的线程数不允许超过 2000,Linux 每个进程中的线程数不允许超过 1000。 209 | 210 | 211 | 212 | > 参考链接:blog.csdn.net/liuerchong/article/details/118882053 -------------------------------------------------------------------------------- /docs/guide/mass-data/9-sort-500-million-large-files.md: -------------------------------------------------------------------------------- 1 | --- 2 | icon: creative 3 | title: 5亿个数的大文件怎么排序? 4 | --- 5 | 6 | ## 题目描述 7 | 8 | 给你1个文件`bigdata`,大小4663M,5亿个数,文件中的数据随机,一行一个整数: 9 | 10 | ```bash 11 | 6196302 12 | 3557681 13 | 6121580 14 | 2039345 15 | 2095006 16 | 1746773 17 | 7934312 18 | 2016371 19 | 7123302 20 | 8790171 21 | 2966901 22 | ... 23 | 7005375 24 | ``` 25 | 26 | 现在要对这个文件进行排序,怎么做? 27 | 28 | ## 内部排序 29 | 30 | 先尝试内排,选2种排序方式: 31 | 32 | ### 3路快排: 33 | 34 | ```java 35 | private final int cutoff = 8; 36 | 37 | public void perform(Comparable[] a) { 38 | perform(a,0,a.length - 1); 39 | } 40 | 41 | private int median3(Comparable[] a,int x,int y,int z) { 42 | if(lessThan(a[x],a[y])) { 43 | if(lessThan(a[y],a[z])) { 44 | return y; 45 | } 46 | else if(lessThan(a[x],a[z])) { 47 | return z; 48 | }else { 49 | return x; 50 | } 51 | }else { 52 | if(lessThan(a[z],a[y])){ 53 | return y; 54 | }else if(lessThan(a[z],a[x])) { 55 | return z; 56 | }else { 57 | return x; 58 | } 59 | } 60 | } 61 | 62 | private void perform(Comparable[] a,int low,int high) { 63 | int n = high - low + 1; 64 | //当序列非常小,用插入排序 65 | if(n <= cutoff) { 66 | InsertionSort insertionSort = SortFactory.createInsertionSort(); 67 | insertionSort.perform(a,low,high); 68 | //当序列中小时,使用median3 69 | }else if(n <= 100) { 70 | int m = median3(a,low,low + (n >>> 1),high); 71 | exchange(a,m,low); 72 | //当序列比较大时,使用ninther 73 | }else { 74 | int gap = n >>> 3; 75 | int m = low + (n >>> 1); 76 | int m1 = median3(a,low,low + gap,low + (gap << 1)); 77 | int m2 = median3(a,m - gap,m,m + gap); 78 | int m3 = median3(a,high - (gap << 1),high - gap,high); 79 | int ninther = median3(a,m1,m2,m3); 80 | exchange(a,ninther,low); 81 | } 82 | 83 | if(high <= low) 84 | return; 85 | //lessThan 86 | int lt = low; 87 | //greaterThan 88 | int gt = high; 89 | //中心点 90 | Comparable pivot = a[low]; 91 | int i = low + 1; 92 | 93 | /* 94 | * 不变式: 95 | * a[low..lt-1] 小于pivot -> 前部(first) 96 | * a[lt..i-1] 等于 pivot -> 中部(middle) 97 | * a[gt+1..n-1] 大于 pivot -> 后部(final) 98 | * 99 | * a[i..gt] 待考察区域 100 | */ 101 | 102 | while (i <= gt) { 103 | if(lessThan(a[i],pivot)) { 104 | //i-> ,lt -> 105 | exchange(a,lt++,i++); 106 | }else if(lessThan(pivot,a[i])) { 107 | exchange(a,i,gt--); 108 | }else{ 109 | i++; 110 | } 111 | } 112 | 113 | // a[low..lt-1] < v = a[lt..gt] < a[gt+1..high]. 114 | perform(a,low,lt - 1); 115 | perform(a,gt + 1,high); 116 | } 117 | ``` 118 | 119 | ### 归并排序: 120 | 121 | ```java 122 | /** 123 | * 小于等于这个值的时候,交给插入排序 124 | */ 125 | private final int cutoff = 8; 126 | 127 | /** 128 | * 对给定的元素序列进行排序 129 | * 130 | * @param a 给定元素序列 131 | */ 132 | @Override 133 | public void perform(Comparable[] a) { 134 | Comparable[] b = a.clone(); 135 | perform(b, a, 0, a.length - 1); 136 | } 137 | 138 | private void perform(Comparable[] src,Comparable[] dest,int low,int high) { 139 | if(low >= high) 140 | return; 141 | 142 | //小于等于cutoff的时候,交给插入排序 143 | if(high - low <= cutoff) { 144 | SortFactory.createInsertionSort().perform(dest,low,high); 145 | return; 146 | } 147 | 148 | int mid = low + ((high - low) >>> 1); 149 | perform(dest,src,low,mid); 150 | perform(dest,src,mid + 1,high); 151 | 152 | //考虑局部有序 src[mid] <= src[mid+1] 153 | if(lessThanOrEqual(src[mid],src[mid+1])) { 154 | System.arraycopy(src,low,dest,low,high - low + 1); 155 | } 156 | 157 | //src[low .. mid] + src[mid+1 .. high] -> dest[low .. high] 158 | merge(src,dest,low,mid,high); 159 | } 160 | 161 | private void merge(Comparable[] src,Comparable[] dest,int low,int mid,int high) { 162 | 163 | for(int i = low,v = low,w = mid + 1; i <= high; i++) { 164 | if(w > high || v <= mid && lessThanOrEqual(src[v],src[w])) { 165 | dest[i] = src[v++]; 166 | }else { 167 | dest[i] = src[w++]; 168 | } 169 | } 170 | } 171 | ``` 172 | 173 | 数据太多,递归太深,会导致栈溢出。数据太多,数组太长,会导致OOM。 174 | 175 | 可见这两种方式不适用。 176 | 177 | ## 位图法 178 | 179 | BitMap算法的核心思想是用bit数组来记录0-1两种状态,然后再将具体数据映射到这个比特数组的具体位置,这个比特位设置成0表示数据不存在,设置成1表示数据存在。 180 | 181 | BitMap算在在大量数据查询、去重等应用场景中使用的比较多,这个算法具有比较高的空间利用率。 182 | 183 | 实现代码如下: 184 | 185 | ```csharp 186 | private BitSet bits; 187 | 188 | public void perform( 189 | String largeFileName, 190 | int total, 191 | String destLargeFileName, 192 | Castor castor, 193 | int readerBufferSize, 194 | int writerBufferSize, 195 | boolean asc) throws IOException { 196 | 197 | System.out.println("BitmapSort Started."); 198 | long start = System.currentTimeMillis(); 199 | bits = new BitSet(total); 200 | InputPart largeIn = PartFactory.createCharBufferedInputPart(largeFileName, readerBufferSize); 201 | OutputPart largeOut = PartFactory.createCharBufferedOutputPart(destLargeFileName, writerBufferSize); 202 | largeOut.delete(); 203 | 204 | Integer data; 205 | int off = 0; 206 | try { 207 | while (true) { 208 | data = largeIn.read(); 209 | if (data == null) 210 | break; 211 | int v = data; 212 | set(v); 213 | off++; 214 | } 215 | largeIn.close(); 216 | int size = bits.size(); 217 | System.out.println(String.format("lines : %d ,bits : %d", off, size)); 218 | 219 | if(asc) { 220 | for (int i = 0; i < size; i++) { 221 | if (get(i)) { 222 | largeOut.write(i); 223 | } 224 | } 225 | }else { 226 | for (int i = size - 1; i >= 0; i--) { 227 | if (get(i)) { 228 | largeOut.write(i); 229 | } 230 | } 231 | } 232 | 233 | largeOut.close(); 234 | long stop = System.currentTimeMillis(); 235 | long elapsed = stop - start; 236 | System.out.println(String.format("BitmapSort Completed.elapsed : %dms",elapsed)); 237 | }finally { 238 | largeIn.close(); 239 | largeOut.close(); 240 | } 241 | } 242 | 243 | private void set(int i) { 244 | bits.set(i); 245 | } 246 | 247 | private boolean get(int v) { 248 | return bits.get(v); 249 | } 250 | ``` 251 | 252 | ## 外部排序 253 | 254 | 什么是外部排序? 255 | 256 | > 1. 内存极少的情况下,利用分治策略,利用外存保存中间结果,再用多路归并来排序; 257 | 258 | 实现原理如下: 259 | 260 | ![](http://img.topjavaer.cn/img/5亿个数大文件排序2.png) 261 | 262 | **1.分成有序的小文件** 263 | 264 | 内存中维护一个极小的核心缓冲区`memBuffer`,将大文件`bigdata`按行读入,搜集到`memBuffer`满或者大文件读完时,对`memBuffer`中的数据调用内排进行排序,排序后将**有序结果**写入磁盘文件`bigdata.xxx.part.sorted`. 265 | 循环利用`memBuffer`直到大文件处理完毕,得到n个有序的磁盘文件: 266 | 267 | ![](http://img.topjavaer.cn/img/5亿个数大文件排序3.png) 268 | 269 | **2.合并成1个有序的大文件** 270 | 271 | 现在有了n个有序的小文件,怎么合并成1个有序的大文件? 272 | 273 | 利用如下原理进行归并排序: 274 | 275 | ![](http://img.topjavaer.cn/img/5亿个数大文件排序1.png) 276 | 277 | 举个简单的例子: 278 | 279 | > 文件1:**3**,6,9。 280 | > 文件2:**2**,4,8。 281 | > 文件3:**1**,5,7。 282 | > 283 | > 第一回合: 284 | > 文件1的最小值:3 , 排在文件1的第1行。 285 | > 文件2的最小值:2,排在文件2的第1行。 286 | > 文件3的最小值:1,排在文件3的第1行。 287 | > 那么,这3个文件中的最小值是:min(1,2,3) = 1。 288 | > 也就是说,最终大文件的当前最小值,是文件1、2、3的当前最小值的最小值。 289 | > 上面拿出了最小值1,写入大文件。 290 | 291 | > 第二回合: 292 | > 文件1的最小值:3 , 排在文件1的第1行。 293 | > 文件2的最小值:2,排在文件2的第1行。 294 | > 文件3的最小值:5,排在文件3的第2行。 295 | > 那么,这3个文件中的最小值是:min(5,2,3) = 2。 296 | > 将2写入大文件。 297 | > 298 | > 也就是说,最小值属于哪个文件,那么就从哪个文件当中取下一行数据。(因为小文件内部有序,下一行数据代表了它当前的最小值) 299 | 300 | 感兴趣的小伙伴可以自己尝试去实现下~ --------------------------------------------------------------------------------