diff --git a/README.md b/README.md index da7542cae15..e97edcb3989 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -推荐你通过在线阅读网站进行阅读,体验更好,速度更快!地址:[javaguide.cn](https://javaguide.cn/)。 +- 推荐在线阅读(体验更好,速度更快):[javaguide.cn](https://javaguide.cn/) +- 面试突击版本(只保留重点,附带精美 PDF 下载):[interview.javaguide.cn](https://interview.javaguide.cn/)
@@ -16,18 +17,23 @@ > - **求个Star**:如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star,这是对我最大的鼓励,感谢各位一起同行,共勉!Github 地址:[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) 。 > - **转载须知**:以下所有文章如非文首说明为转载皆为 JavaGuide 原创,转载请在文首注明出处。如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境! -
- -
- -## 项目相关 +## 面试突击版本 + +很多同学有“临时突击面试”的需求,所以我专门做了一个 [JavaGuide 面试突击版](https://interview.javaguide.cn/home.html):在 [JavaGuide](https://javaguide.cn/home.html) 原有内容基础上做了大幅精简,只保留高频必考重点,并一直持续更新。 + +在这些“精简后的重点”里,我又额外用 ⭐️ 标出了**重点中的重点**,方便你优先浏览、快速记忆。 -- [项目介绍](https://javaguide.cn/javaguide/intro.html) -- [使用建议](https://javaguide.cn/javaguide/use-suggestion.html) -- [贡献指南](https://javaguide.cn/javaguide/contribution-guideline.html) -- [常见问题](https://javaguide.cn/javaguide/faq.html) +同时提供亮色(白天)和暗色(夜间)PDF,**需要打印的同学记得选亮色版本**,纸质阅读体验会更好。 + +如果你**时间比较充裕**,更推荐直接在 [JavaGuide 官网](https://javaguide.cn/) 上**系统学习**:内容比突击版更全面、更深入,更适合打基础和长期提升。 + +**突击版本网站入口**:[interview.javaguide.cn](https://interview.javaguide.cn/) + +对应的 PDF 版本,可以直接在公众号后台回复“**PDF**”获取: + +JavaGuide 公众号 ## Java @@ -127,6 +133,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. - [Java 21 新特性概览](./docs/java/new-features/java21.md) - [Java 22 & 23 新特性概览](./docs/java/new-features/java22-23.md) - [Java 24 新特性概览](./docs/java/new-features/java24.md) +- [Java 25 新特性概览](./docs/java/new-features/java25.md) ## 计算机基础 @@ -277,7 +284,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. ### 基础 - [RestFul API 简明教程](./docs/system-design/basis/RESTfulAPI.md) -- [软件工程简明教程简明教程](./docs/system-design/basis/software-engineering.md) +- [软件工程简明教程](./docs/system-design/basis/software-engineering.md) - [代码命名指南](./docs/system-design/basis/naming.md) - [代码重构指南](./docs/system-design/basis/refactoring.md) - [单元测试指南](./docs/system-design/basis/unit-test.md) @@ -337,6 +344,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. - [Paxos 算法解读](https://javaguide.cn/distributed-system/protocol/paxos-algorithm.html) - [Raft 算法解读](https://javaguide.cn/distributed-system/protocol/raft-algorithm.html) - [Gossip 协议详解](https://javaguide.cn/distributed-system/protocol/gossip-protocl.html) +- [一致性哈希算法详解](https://javaguide.cn/distributed-system/protocol/consistent-hashing.html) ### RPC @@ -426,7 +434,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. **灾备** = 容灾 + 备份。 -- **备份**:将系统所产生的的所有重要数据多备份几份。 +- **备份**:将系统所产生的所有重要数据多备份几份。 - **容灾**:在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。 **异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。 @@ -439,6 +447,6 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 -![JavaGuide 官方公众号](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) +JavaGuide 公众号 diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 00000000000..ce2a5bc88e7 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,446 @@ +Recommended to read through online reading platforms for better experience and faster speed! Link: [javaguide.cn](https://javaguide.cn/). + +
+ +[![logo](https://oss.javaguide.cn/github/javaguide/csdn/1c00413c65d1995993bf2b0daf7b4f03.png)](https://github.com/Snailclimb/JavaGuide) + +[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide) + +Snailclimb%2FJavaGuide | Trendshift + +
+ +> - **Interview Edition**: Candidates preparing for Java interviews can consider the **[《Java Interview Guide》](./docs/zhuanlan/java-mian-shi-zhi-bei.md)** (high quality, specially designed for interviews, to be used with JavaGuide). +> - **Knowledge Planet**: Exclusive interview mini-books/one-on-one communication/resume modification/exclusive job-seeking guide, welcome to join **[JavaGuide Knowledge Planet](./docs/about-the-author/zhishixingqiu-two-years.md)** (click the link to view the detailed introduction of the planet, make sure you really need it before joining). +> - **Usage Suggestion**: Experienced interviewers always dig into technical issues along the project experience. Definitely do not memorize technical articles! For detailed learning suggestions, please refer to: [JavaGuide Usage Suggestion](./docs/javaguide/use-suggestion.md). +> - **Seek a Star**: If you find the content of JavaGuide helpful, please give a free Star, which is the greatest encouragement to me. Thank you all for walking together and striving together! Github link: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide). +> - **Reprint Notice**: All the following articles are original creations of JavaGuide unless stated otherwise at the beginning. Please indicate the source when reprinting. If malicious plagiarism/copying is discovered, legal weapons will be used to safeguard our rights. Let's together maintain a good technical creation environment! + +
+ +
+ + + +## Project-related + +- [Project Introduction](https://javaguide.cn/javaguide/intro.html) +- [Usage Suggestion](https://javaguide.cn/javaguide/use-suggestion.html) +- [Contribution Guide](https://javaguide.cn/javaguide/contribution-guideline.html) +- [FAQ](https://javaguide.cn/javaguide/faq.html) + +## Java + +### Basics + +**Knowledge Points/Interview Questions Summary** : (Must-see:+1:): + +- [Summary of Common Java Basics Knowledge Points & Interview Questions (Part 1)](./docs/java/basis/java-basic-questions-01.md) +- [Summary of Common Java Basics Knowledge Points & Interview Questions (Part 2)](./docs/java/basis/java-basic-questions-02.md) +- [Summary of Common Java Basics Knowledge Points & Interview Questions (Part 3)](./docs/java/basis/java-basic-questions-03.md) + +**Important Knowledge Points Explanation**: + +- [Why is There Only Pass-by-Value in Java?](./docs/java/basis/why-there-only-value-passing-in-java.md) +- [Serialization in Java Explained](./docs/java/basis/serialization.md) +- [Generics & Wildcards Explained](./docs/java/basis/generics-and-wildcards.md) +- [Java Reflection Mechanism Explained](./docs/java/basis/reflection.md) +- [Java Proxy Pattern Explained](./docs/java/basis/proxy.md) +- [BigDecimal Explained](./docs/java/basis/bigdecimal.md) +- [Java Magic Class Unsafe Explained](./docs/java/basis/unsafe.md) +- [Java SPI Mechanism Explained](./docs/java/basis/spi.md) +- [Java Syntactic Sugar Explained](./docs/java/basis/syntactic-sugar.md) + +### Collections + +**Knowledge Points/Interview Questions Summary**: + +- [Summary of Common Java Collection Knowledge Points & Interview Questions (Part 1)](./docs/java/collection/java-collection-questions-01.md) (Must-see :+1:) +- [Summary of Common Java Collection Knowledge Points & Interview Questions (Part 2)](./docs/java/collection/java-collection-questions-02.md) (Must-see :+1:) +- [Summary of Java Container Usage Precautions](./docs/java/collection/java-collection-precautions-for-use.md) + +**Source Code Analysis**: + +- [ArrayList Core Source Code + Expansion Mechanism Analysis](./docs/java/collection/arraylist-source-code.md) +- [LinkedList Core Source Code Analysis](./docs/java/collection/linkedlist-source-code.md) +- [HashMap Core Source Code + Underlying Data Structure Analysis](./docs/java/collection/hashmap-source-code.md) +# Java Collection & Concurrency Series + +## Collection + +- [ConcurrentHashMap Core Source Code + Underlying Data Structure Analysis](./docs/java/collection/concurrent-hash-map-source-code.md) +- [LinkedHashMap Core Source Code Analysis](./docs/java/collection/linkedhashmap-source-code.md) +- [CopyOnWriteArrayList Core Source Code Analysis](./docs/java/collection/copyonwritearraylist-source-code.md) +- [ArrayBlockingQueue Core Source Code Analysis](./docs/java/collection/arrayblockingqueue-source-code.md) +- [PriorityQueue Core Source Code Analysis](./docs/java/collection/priorityqueue-source-code.md) +- [DelayQueue Core Source Code Analysis](./docs/java/collection/delayqueue-source-code.md) + +### IO + +- [IO Basic Knowledge Summary](./docs/java/io/io-basis.md) +- [IO Design Patterns Summary](./docs/java/io/io-design-patterns.md) +- [IO Model Explanation](./docs/java/io/io-model.md) +- [NIO Core Knowledge Summary](./docs/java/io/nio-basis.md) + +### Concurrency + +**Knowledge Points/Interview Questions Summary** : (Must-read :+1:) + +- [Common Java Concurrency Knowledge Points & Interview Questions Summary (Part 1)](./docs/java/concurrent/java-concurrent-questions-01.md) +- [Common Java Concurrency Knowledge Points & Interview Questions Summary (Part 2)](./docs/java/concurrent/java-concurrent-questions-02.md) +- [Common Java Concurrency Knowledge Points & Interview Questions Summary (Part 3)](./docs/java/concurrent/java-concurrent-questions-03.md) + +**Important Knowledge Points Explanation**: + +- [Optimistic Lock and Pessimistic Lock Explanation](./docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md) +- [CAS Explanation](./docs/java/concurrent/cas.md) +- [JMM (Java Memory Model) Explanation](./docs/java/concurrent/jmm.md) +- **Thread Pool**: [Java Thread Pool Explanation](./docs/java/concurrent/java-thread-pool-summary.md), [Java Thread Pool Best Practices](./docs/java/concurrent/java-thread-pool-best-practices.md) +- [ThreadLocal Explanation](./docs/java/concurrent/threadlocal.md) +- [Java Concurrent Collections Summary](./docs/java/concurrent/java-concurrent-collections.md) +- [Atomic Classes Summary](./docs/java/concurrent/atomic-classes.md) +- [AQS Explanation](./docs/java/concurrent/aqs.md) +- [CompletableFuture Explanation](./docs/java/concurrent/completablefuture-intro.md) + +### JVM (Must-read :+1:) + +The JVM part mainly refers to the [JVM Specification - Java 8](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) and Zhong Zhiming's book [《Deep Understanding of Java Virtual Machine (3rd Edition)》](https://book.douban.com/subject/34907497/) (strongly recommend to read it several times!). + +- **[Java Memory Area](./docs/java/jvm/memory-area.md)** +- **[JVM Garbage Collection](./docs/java/jvm/jvm-garbage-collection.md)** +- [Class File Structure](./docs/java/jvm/class-file-structure.md) +- **[Class Loading Process](./docs/java/jvm/class-loading-process.md)** +- [Class Loader](./docs/java/jvm/classloader.md) +- [【To Be Completed】Most Important JVM Parameters Summary (Half Translated)](./docs/java/jvm/jvm-parameters-intro.md) +- [【Bonus】Understand JVM in Plain Language](./docs/java/jvm/jvm-intro.md) +- [JDK Monitoring and Troubleshooting Tools](./docs/java/jvm/jdk-monitoring-and-troubleshooting-tools.md) + +### New Features + +- **Java 8**: [Java 8 New Features Summary (Translated)](./docs/java/new-features/java8-tutorial-translate.md), [Common Java 8 New Features Summary](./docs/java/new-features/java8-common-new-features.md) +- [Java 9 New Features Overview](./docs/java/new-features/java9.md) +- [Java 10 New Features Overview](./docs/java/new-features/java10.md) +- [Java 11 New Features Overview](./docs/java/new-features/java11.md) +- [Java 12 & 13 New Features Overview](./docs/java/new-features/java12-13.md) +- [Java 14 & 15 New Features Overview](./docs/java/new-features/java14-15.md) +- [Java 16 New Features Overview](./docs/java/new-features/java16.md) +- [Java 17 New Features Overview](./docs/java/new-features/java17.md) +- [Java 18 New Features Overview](./docs/java/new-features/java18.md) +- [Java 19 New Features Overview](./docs/java/new-features/java19.md) +- [Java 20 New Features Overview](./docs/java/new-features/java20.md) +# Overview of Java 21, 22, 23, 24, and 25 New Features + +## Computer Fundamentals + +### Operating Systems + +- [Summary of Common Operating System Knowledge Points & Interview Questions (Part 1)](./docs/cs-basics/operating-system/operating-system-basic-questions-01.md) +- [Summary of Common Operating System Knowledge Points & Interview Questions (Part 2)](./docs/cs-basics/operating-system/operating-system-basic-questions-02.md) +- **Linux**: + - [Summary of Essential Linux Basics for Backend Developers](./docs/cs-basics/operating-system/linux-intro.md) + - [Summary of Shell Scripting Basics](./docs/cs-basics/operating-system/shell-intro.md) + +### Networking + +**Knowledge Points/Interview Questions Summary**: + +- [Summary of Common Computer Network Knowledge Points & Interview Questions (Part 1)](./docs/cs-basics/network/other-network-questions.md) +- [Summary of Common Computer Network Knowledge Points & Interview Questions (Part 2)](./docs/cs-basics/network/other-network-questions2.md) +- [Summary of Professor Xie Xiren's "Computer Network" Content (Supplementary)](./docs/cs-basics/network/computer-network-xiexiren-summary.md) + +**Important Concept Explanations**: + +- [Detailed Explanation of the OSI and TCP/IP Network Layer Models (Basics)](./docs/cs-basics/network/osi-and-tcp-ip-model.md) +- [Summary of Common Application Layer Protocols (Application Layer)](./docs/cs-basics/network/application-layer-protocol.md) +- [HTTP vs HTTPS (Application Layer)](./docs/cs-basics/network/http-vs-https.md) +- [HTTP 1.0 vs HTTP 1.1 (Application Layer)](./docs/cs-basics/network/http1.0-vs-http1.1.md) +- [Common HTTP Status Codes (Application Layer)](./docs/cs-basics/network/http-status-codes.md) +- [Detailed Explanation of the DNS Domain Name System (Application Layer)](./docs/cs-basics/network/dns.md) +- [TCP Three-Way Handshake and Four-Way Termination (Transport Layer)](./docs/cs-basics/network/tcp-connection-and-disconnection.md) +- [TCP Transmission Reliability Guarantee (Transport Layer)](./docs/cs-basics/network/tcp-reliability-guarantee.md) +- [Detailed Explanation of the ARP Protocol (Network Layer)](./docs/cs-basics/network/arp.md) +- [Detailed Explanation of the NAT Protocol (Network Layer)](./docs/cs-basics/network/nat.md) +- [Summary of Common Network Attack Means (Security)](./docs/cs-basics/network/network-attack-means.md) + +### Data Structures + +**Illustrated Data Structures:** + +- [Linear Data Structures: Arrays, Linked Lists, Stacks, Queues](./docs/cs-basics/data-structure/linear-data-structure.md) +- [Graphs](./docs/cs-basics/data-structure/graph.md) +- [Heaps](./docs/cs-basics/data-structure/heap.md) +- [Trees](./docs/cs-basics/data-structure/tree.md): Focus on [Red-Black Trees](./docs/cs-basics/data-structure/red-black-tree.md), B-, B+, B* Trees, and LSM Trees + +Other Commonly Used Data Structures: + +- [Bloom Filters](./docs/cs-basics/data-structure/bloom-filter.md) + +### Algorithms + +The algorithm part is very important. If you don't know how to learn algorithms, you can refer to: + +- [Recommended Algorithm Learning Books and Resources](https://www.zhihu.com/question/323359308/answer/1545320858). +- [How to Solve LeetCode Problems?](https://www.zhihu.com/question/31092580/answer/1534887374) + +**Summary of Common Algorithm Problems**: + +- [Summary of Several Common String Algorithm Problems](./docs/cs-basics/algorithms/string-algorithm-problems.md) +- [Summary of Several Common Linked List Algorithm Problems](./docs/cs-basics/algorithms/linkedlist-algorithm-problems.md) +- [Part of the Coding Questions from the "Sword Refers to Offer"](./docs/cs-basics/algorithms/the-sword-refers-to-offer.md) +- [Ten Classic Sorting Algorithms](./docs/cs-basics/algorithms/10-classical-sorting-algorithms.md) + +Additionally, [GeeksforGeeks](https://www.geeksforgeeks.org/fundamentals-of-algorithms/) has a comprehensive summary of common algorithms. + +## Database + +### Basics + +- [Summary of Database Basics](./docs/database/basis.md) +- [Summary of NoSQL Basics](./docs/database/nosql.md) +- [Explanation of Character Sets](./docs/database/character-set.md) +- SQL: + - [Summary of SQL Syntax Basics](./docs/database/sql/sql-syntax-summary.md) + - [Summary of Common SQL Interview Questions](./docs/database/sql/sql-questions-01.md) + +### MySQL + +**Knowledge Points/Interview Questions Summary:** +# MySQL Common Knowledge Points & Interview Questions Summary (Must-Read :+1:) + +- [MySQL Common Knowledge Points & Interview Questions Summary](./docs/database/mysql/mysql-questions-01.md) +- [MySQL High-Performance Optimization Specification Recommendations](./docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md) + +**Important Knowledge Points:** + +- [MySQL Index Details](./docs/database/mysql/mysql-index.md) +- [Detailed Explanation of MySQL Transaction Isolation Levels (with Pictures)](./docs/database/mysql/transaction-isolation-level.md) +- [Detailed Explanation of MySQL's Three Logs (binlog, redo log, and undo log)](./docs/database/mysql/mysql-logs.md) +- [InnoDB Storage Engine's Implementation of MVCC](./docs/database/mysql/innodb-implementation-of-mvcc.md) +- [How SQL Statements are Executed in MySQL](./docs/database/mysql/how-sql-executed-in-mysql.md) +- [Detailed Explanation of MySQL Query Cache](./docs/database/mysql/mysql-query-cache.md) +- [MySQL Query Execution Plan Analysis](./docs/database/mysql/mysql-query-execution-plan.md) +- [Are MySQL Auto-Increment Primary Keys Always Continuous?](./docs/database/mysql/mysql-auto-increment-primary-key-continuous.md) +- [Suggestions on Storing Time-Related Data in Databases](./docs/database/mysql/some-thoughts-on-database-storage-time.md) +- [Index Invalidation Caused by Implicit Conversion in MySQL](./docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md) + +### Redis + +**Knowledge Points/Interview Questions Summary** (Must-Read :+1:): + +- [Redis Common Knowledge Points & Interview Questions Summary (Part 1)](./docs/database/redis/redis-questions-01.md) +- [Redis Common Knowledge Points & Interview Questions Summary (Part 2)](./docs/database/redis/redis-questions-02.md) + +**Important Knowledge Points:** + +- [Detailed Explanation of 3 Common Cache Read and Write Strategies](./docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md) +- [Detailed Explanation of Redis' 5 Basic Data Structures](./docs/database/redis/redis-data-structures-01.md) +- [Detailed Explanation of Redis' 3 Special Data Structures](./docs/database/redis/redis-data-structures-02.md) +- [Detailed Explanation of Redis Persistence Mechanism](./docs/database/redis/redis-persistence.md) +- [Detailed Explanation of Redis Memory Fragmentation](./docs/database/redis/redis-memory-fragmentation.md) +- [Summary of Common Causes of Redis Blocking](./docs/database/redis/redis-common-blocking-problems-summary.md) +- [Detailed Explanation of Redis Cluster](./docs/database/redis/redis-cluster.md) + +### MongoDB + +- [MongoDB Common Knowledge Points & Interview Questions Summary (Part 1)](./docs/database/mongodb/mongodb-questions-01.md) +- [MongoDB Common Knowledge Points & Interview Questions Summary (Part 2)](./docs/database/mongodb/mongodb-questions-02.md) + +## Search Engines + +[Elasticsearch Common Interview Questions Summary (Paid)](./docs/database/elasticsearch/elasticsearch-questions-01.md) + +![JavaGuide Official Public Account](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) + +## Development Tools + +### Maven + +- [Maven Core Concepts Summary](./docs/tools/maven/maven-core-concepts.md) +- [Maven Best Practices](./docs/tools/maven/maven-best-practices.md) + +### Gradle + +[Gradle Core Concepts Summary](./docs/tools/gradle/gradle-core-concepts.md) (Optional, Maven is still more widely used in China) + +### Docker + +- [Docker Core Concepts Summary](./docs/tools/docker/docker-intro.md) +- [Docker in Action](./docs/tools/docker/docker-in-action.md) + +### Git + +- [Git Core Concepts Summary](./docs/tools/git/git-intro.md) +- [Useful GitHub Tips Summary](./docs/tools/git/github-tips.md) + +## System Design + +- [Common System Design Interview Questions Summary](./docs/system-design/system-design-questions.md) +- [Common Design Pattern Interview Questions Summary](./docs/system-design/design-pattern.md) + +### Basics + +- [A Brief Tutorial on RESTful API](./docs/system-design/basis/RESTfulAPI.md) +- [A Brief Tutorial on Software Engineering](./docs/system-design/basis/software-engineering.md) +- [Code Naming Guide](./docs/system-design/basis/naming.md) +- [Code Refactoring Guide](./docs/system-design/basis/refactoring.md) +- [Unit Testing Guide](./docs/system-design/basis/unit-test.md) + +### Common Frameworks + +#### Spring/SpringBoot (Must-Read :+1:) + +**Knowledge Points/Interview Questions Summary**: +- [Summary of Common Spring Knowledge Points and Interview Questions](./docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md) +- [Summary of Common SpringBoot Knowledge Points and Interview Questions](./docs/system-design/framework/spring/springboot-knowledge-and-questions-summary.md) +- [Summary of Common Spring/SpringBoot Annotations](./docs/system-design/framework/spring/spring-common-annotations.md) +- [SpringBoot Beginner's Guide](https://github.com/Snailclimb/springboot-guide) + +**Detailed Explanation of Important Knowledge Points**: + +- [Detailed Explanation of IoC & AOP (Quick Understanding)](./docs/system-design/framework/spring/ioc-and-aop.md) +- [Detailed Explanation of Spring Transactions](./docs/system-design/framework/spring/spring-transaction.md) +- [Detailed Explanation of Design Patterns in Spring](./docs/system-design/framework/spring/spring-design-patterns-summary.md) +- [Detailed Explanation of SpringBoot Auto-Configuration Principles](./docs/system-design/framework/spring/spring-boot-auto-assembly-principles.md) + +#### MyBatis + +[Summary of Common MyBatis Interview Questions](./docs/system-design/framework/mybatis/mybatis-interview.md) + +### Security + +#### Authentication and Authorization + +- [Detailed Explanation of Authentication and Authorization Fundamentals](./docs/system-design/security/basis-of-authority-certification.md) +- [Detailed Explanation of JWT Basics](./docs/system-design/security/jwt-intro.md) +- [Analysis of Advantages and Disadvantages of JWT and Common Problem Solutions](./docs/system-design/security/advantages-and-disadvantages-of-jwt.md) +- [Detailed Explanation of SSO (Single Sign-On)](./docs/system-design/security/sso-intro.md) +- [Detailed Explanation of Permission System Design](./docs/system-design/security/design-of-authority-system.md) + +#### Data Security + +- [Summary of Common Encryption Algorithms](./docs/system-design/security/encryption-algorithms.md) +- [Summary of Sensitive Word Filtering Solutions](./docs/system-design/security/sentive-words-filter.md) +- [Summary of Data Desensitization Solutions](./docs/system-design/security/data-desensitization.md) +- [Why Both Front-end and Back-end Need to Perform Data Validation](./docs/system-design/security/data-validation.md) + +### Scheduled Tasks + +[Detailed Explanation of Java Scheduled Tasks](./docs/system-design/schedule-task.md) + +### Web Real-time Message Pushing + +[Detailed Explanation of Web Real-time Message Pushing](./docs/system-design/web-real-time-message-push.md) + +## Distributed System + +### Theory, Algorithms, and Protocols + +- [Interpretation of CAP Theory and BASE Theory](https://javaguide.cn/distributed-system/protocol/cap-and-base-theorem.html) +- [Interpretation of Paxos Algorithm](https://javaguide.cn/distributed-system/protocol/paxos-algorithm.html) +- [Interpretation of Raft Algorithm](https://javaguide.cn/distributed-system/protocol/raft-algorithm.html) +- [Detailed Explanation of Gossip Protocol](https://javaguide.cn/distributed-system/protocol/gossip-protocl.html) +- [Detailed Explanation of Consistent Hashing Algorithm](https://javaguide.cn/distributed-system/protocol/consistent-hashing.html) + +### RPC + +- [Summary of RPC Basics](https://javaguide.cn/distributed-system/rpc/rpc-intro.html) +- [Summary of Common Dubbo Knowledge Points and Interview Questions](https://javaguide.cn/distributed-system/rpc/dubbo.html) + +### ZooKeeper + +> These two articles may have some overlapping content, it is recommended to read both. + +- [Summary of ZooKeeper Relevant Concepts (Beginner)](https://javaguide.cn/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.html) +- [Summary of ZooKeeper Relevant Concepts (Advanced)](https://javaguide.cn/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.html) + +### API Gateway + +- [Summary of API Gateway Basics](https://javaguide.cn/distributed-system/api-gateway.html) +- [Summary of Common Spring Cloud Gateway Knowledge Points and Interview Questions](./docs/distributed-system/spring-cloud-gateway-questions.md) + +### Distributed ID + +- [Introduction to Distributed ID and Summary of Implementation Solutions](https://javaguide.cn/distributed-system/distributed-id.html) +- [Design Guide for Distributed ID](https://javaguide.cn/distributed-system/distributed-id-design.html) + +### Distributed Lock +# Distributed Locks + +- [Introduction to Distributed Locks](https://javaguide.cn/distributed-system/distributed-lock.html) +- [Summary of Common Distributed Lock Implementation Solutions](https://javaguide.cn/distributed-system/distributed-lock-implementations.html) + +### Distributed Transactions + +[Summary of Common Distributed Transaction Knowledge Points and Interview Questions](https://javaguide.cn/distributed-system/distributed-transaction.html) + +### Distributed Configuration Center + +[Summary of Common Distributed Configuration Center Knowledge Points and Interview Questions](./docs/distributed-system/distributed-configuration-center.md) + +## High Performance + +### Database Optimization + +- [Database Read-Write Separation and Database Sharding](./docs/high-performance/read-and-write-separation-and-library-subtable.md) +- [Data Separation of Cold and Hot Data](./docs/high-performance/data-cold-hot-separation.md) +- [Summary of Common SQL Optimization Methods](./docs/high-performance/sql-optimization.md) +- [Introduction to Deep Pagination and Optimization Suggestions](./docs/high-performance/deep-pagination-optimization.md) + +### Load Balancing + +[Summary of Common Load Balancing Knowledge Points and Interview Questions](./docs/high-performance/load-balancing.md) + +### CDN + +[Summary of Common CDN (Content Delivery Network) Knowledge Points and Interview Questions](./docs/high-performance/cdn.md) + +### Message Queue + +- [Summary of Message Queue Basic Knowledge](./docs/high-performance/message-queue/message-queue.md) +- [Summary of Common Disruptor Knowledge Points and Interview Questions](./docs/high-performance/message-queue/disruptor-questions.md) +- [Summary of Common RabbitMQ Knowledge Points and Interview Questions](./docs/high-performance/message-queue/rabbitmq-questions.md) +- [Summary of Common RocketMQ Knowledge Points and Interview Questions](./docs/high-performance/message-queue/rocketmq-questions.md) +- [Summary of Common Kafka Knowledge Points and Interview Questions](./docs/high-performance/message-queue/kafka-questions-01.md) + +## High Availability + +[Guide to High Availability System Design](./docs/high-availability/high-availability-system-design.md) + +### Redundancy Design + +[Detailed Explanation of Redundancy Design](./docs/high-availability/redundancy.md) + +### Rate Limiting + +[Detailed Explanation of Service Rate Limiting](./docs/high-availability/limit-request.md) + +### Fallback & Circuit Breaker + +[Detailed Explanation of Fallback & Circuit Breaker](./docs/high-availability/fallback-and-circuit-breaker.md) + +### Timeout & Retry + +[Detailed Explanation of Timeout & Retry](./docs/high-availability/timeout-and-retry.md) + +### Clustering + +Deploying multiple instances of the same service to avoid single point of failure. + +### Disaster Recovery Design and Active-Active Deployment + +**Disaster Recovery** = Disaster Tolerance + Backup. + +- **Backup**: Backing up all important data generated by the system multiple times. +- **Disaster Tolerance**: Establishing two completely identical systems in different locations. When the system in one location suddenly fails, the entire application system can be switched to the other one, so that the system can continue to provide services normally. + +**Active-Active Deployment** describes deploying services in different locations and simultaneously providing services externally. The main difference from traditional disaster recovery design is the "active-active" nature, i.e., all sites are simultaneously providing external services. Active-active deployment is to cope with unexpected situations such as fires, earthquakes and other natural or man-made disasters. + +## Star Trend + +![Stars](https://api.star-history.com/svg?repos=Snailclimb/JavaGuide&type=Date) + +## Official Public Account + +If you want to stay up-to-date with my latest articles and share my valuable content, you can follow my official public account. + +![JavaGuide Official Public Account](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) \ No newline at end of file diff --git a/TRANSLATION_TOOLS.md b/TRANSLATION_TOOLS.md new file mode 100644 index 00000000000..e4ab7acac0d --- /dev/null +++ b/TRANSLATION_TOOLS.md @@ -0,0 +1,172 @@ +# Translation Tools for JavaGuide + +This repository includes automated translation tools to translate all documentation to multiple languages. + +## Available Tools + +### 1. Python Version (`translate_repo.py`) + +**Requirements:** +```bash +pip install deep-translator +``` + +**Usage:** +```bash +python3 translate_repo.py +``` + +**Features:** +- ✅ Uses Google Translate (free, no API key required) +- ✅ Translates all `.md` files in `docs/` folder + `README.md` +- ✅ Preserves directory structure +- ✅ Progress tracking (saves to `.translation_progress.json`) +- ✅ Skips already translated files +- ✅ Rate limiting to avoid API throttling +- ✅ Supports 20 languages + +### 2. Java Version (`TranslateRepo.java`) + +**Requirements:** +```bash +# Requires Gson library +# Download from: https://repo1.maven.org/maven2/com/google/code/gson/gson/2.10.1/gson-2.10.1.jar +``` + +**Compile:** +```bash +javac -cp gson-2.10.1.jar TranslateRepo.java +``` + +**Usage:** +```bash +java -cp .:gson-2.10.1.jar TranslateRepo +``` + +**Features:** +- ✅ Pure Java implementation +- ✅ Uses Google Translate API (free, no key required) +- ✅ Same functionality as Python version +- ✅ Progress tracking with JSON +- ✅ Supports 20 languages + +## Supported Languages + +1. English (en) +2. Chinese Simplified (zh) +3. Spanish (es) +4. French (fr) +5. Portuguese (pt) +6. German (de) +7. Japanese (ja) +8. Korean (ko) +9. Russian (ru) +10. Italian (it) +11. Arabic (ar) +12. Hindi (hi) +13. Turkish (tr) +14. Vietnamese (vi) +15. Polish (pl) +16. Dutch (nl) +17. Indonesian (id) +18. Thai (th) +19. Swedish (sv) +20. Greek (el) + +## Output Structure + +Original: +``` +docs/ +├── java/ +│ └── basics.md +└── ... +README.md +``` + +After translation to English: +``` +docs_en/ +├── java/ +│ └── basics.en.md +└── ... +README.en.md +``` + +## How It Works + +1. **Scans** all `.md` files in `docs/` folder and `README.md` +2. **Splits** large files into chunks (4000 chars) to respect API limits +3. **Translates** each chunk using Google Translate +4. **Preserves** markdown formatting and code blocks +5. **Saves** to `docs_{lang}/` with `.{lang}.md` suffix +6. **Tracks** progress to resume if interrupted + +## Example Workflow + +```bash +# 1. Run translation tool +python3 translate_repo.py + +# 2. Select language (e.g., 1 for English) +Enter choice (1-20): 1 + +# 3. Confirm translation +Translate 292 files to English? (y/n): y + +# 4. Wait for completion (progress shown for each file) +[1/292] docs/java/basics/java-basic-questions-01.md + → docs_en/java/basics/java-basic-questions-01.en.md + Chunk 1/3... ✅ + Chunk 2/3... ✅ + Chunk 3/3... ✅ + ✅ Translated (5234 → 6891 chars) + +# 5. Review and commit +git add docs_en/ README.en.md +git commit -m "Add English translation" +git push +``` + +## Progress Tracking + +The tool saves progress to `.translation_progress.json`: +```json +{ + "completed": [ + "docs/java/basics/file1.md", + "docs/java/basics/file2.md" + ], + "failed": [] +} +``` + +If interrupted, simply run the tool again - it will skip completed files and resume where it left off. + +## Performance + +- **Speed**: ~1 file per 5-10 seconds (depending on file size) +- **For JavaGuide**: 292 files ≈ 2-3 hours total +- **Rate limiting**: 1 second delay between chunks to avoid throttling + +## Notes + +- ✅ Free to use (no API key required) +- ✅ Preserves markdown formatting +- ✅ Handles code blocks correctly +- ✅ Skips existing translations +- ⚠️ Review translations for accuracy (automated translation may have errors) +- ⚠️ Large repos may take several hours + +## Contributing + +After running the translation tool: + +1. Review translated files for accuracy +2. Fix any translation errors manually +3. Test that links and formatting work correctly +4. Create a pull request with your translations + +## License + +These tools are provided as-is for translating JavaGuide documentation. diff --git a/TranslateRepo.java b/TranslateRepo.java new file mode 100644 index 00000000000..626e8345717 --- /dev/null +++ b/TranslateRepo.java @@ -0,0 +1,386 @@ +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.util.*; +import java.util.stream.Collectors; +import com.google.gson.*; + +/** + * Repository Documentation Translation Tool + * + * Translates all markdown files in docs/ folder to target language. + * Preserves directory structure and saves to docs_{lang}/ folder. + * + * Usage: java TranslateRepo + */ +public class TranslateRepo { + + private static final int CHUNK_SIZE = 4000; + private static final String PROGRESS_FILE = ".translation_progress.json"; + private static final Map LANGUAGES = new LinkedHashMap<>(); + + static { + LANGUAGES.put("1", new Language("English", "en", "en")); + LANGUAGES.put("2", new Language("Chinese (Simplified)", "zh-CN", "zh")); + LANGUAGES.put("3", new Language("Spanish", "es", "es")); + LANGUAGES.put("4", new Language("French", "fr", "fr")); + LANGUAGES.put("5", new Language("Portuguese", "pt", "pt")); + LANGUAGES.put("6", new Language("German", "de", "de")); + LANGUAGES.put("7", new Language("Japanese", "ja", "ja")); + LANGUAGES.put("8", new Language("Korean", "ko", "ko")); + LANGUAGES.put("9", new Language("Russian", "ru", "ru")); + LANGUAGES.put("10", new Language("Italian", "it", "it")); + LANGUAGES.put("11", new Language("Arabic", "ar", "ar")); + LANGUAGES.put("12", new Language("Hindi", "hi", "hi")); + LANGUAGES.put("13", new Language("Turkish", "tr", "tr")); + LANGUAGES.put("14", new Language("Vietnamese", "vi", "vi")); + LANGUAGES.put("15", new Language("Polish", "pl", "pl")); + LANGUAGES.put("16", new Language("Dutch", "nl", "nl")); + LANGUAGES.put("17", new Language("Indonesian", "id", "id")); + LANGUAGES.put("18", new Language("Thai", "th", "th")); + LANGUAGES.put("19", new Language("Swedish", "sv", "sv")); + LANGUAGES.put("20", new Language("Greek", "el", "el")); + } + + static class Language { + String name; + String code; + String suffix; + + Language(String name, String code, String suffix) { + this.name = name; + this.code = code; + this.suffix = suffix; + } + } + + static class TranslationProgress { + Set completed = new HashSet<>(); + Set failed = new HashSet<>(); + } + + public static void main(String[] args) { + try { + printHeader(); + + // Get repository path + Scanner scanner = new Scanner(System.in); + System.out.print("Enter repository path (default: current directory): "); + String repoPathStr = scanner.nextLine().trim(); + if (repoPathStr.isEmpty()) { + repoPathStr = "."; + } + + Path repoPath = Paths.get(repoPathStr).toAbsolutePath(); + if (!Files.exists(repoPath)) { + System.out.println("❌ Repository path does not exist: " + repoPath); + return; + } + + System.out.println("📁 Repository: " + repoPath); + System.out.println(); + + // Select language + Language language = selectLanguage(scanner); + System.out.println("\n✨ Selected: " + language.name); + System.out.println(); + + // Find markdown files + System.out.println("🔍 Finding markdown files..."); + List mdFiles = findMarkdownFiles(repoPath); + + if (mdFiles.isEmpty()) { + System.out.println("❌ No markdown files found in docs/ folder or README.md"); + return; + } + + System.out.println("📄 Found " + mdFiles.size() + " markdown files"); + System.out.println(); + + // Load progress + TranslationProgress progress = loadProgress(repoPath); + + // Filter files + List filesToTranslate = new ArrayList<>(); + for (Path file : mdFiles) { + Path outputPath = getOutputPath(file, repoPath, language.suffix); + if (Files.exists(outputPath)) { + System.out.println("⏭️ Skipping (exists): " + repoPath.relativize(file)); + } else if (progress.completed.contains(file.toString())) { + System.out.println("⏭️ Skipping (completed): " + repoPath.relativize(file)); + } else { + filesToTranslate.add(file); + } + } + + if (filesToTranslate.isEmpty()) { + System.out.println("\n✅ All files already translated!"); + return; + } + + System.out.println("\n📝 Files to translate: " + filesToTranslate.size()); + System.out.println(); + + // Confirm + System.out.print("Translate " + filesToTranslate.size() + " files to " + language.name + "? (y/n): "); + String confirm = scanner.nextLine().trim().toLowerCase(); + if (!confirm.equals("y")) { + System.out.println("❌ Translation cancelled"); + return; + } + + System.out.println(); + System.out.println("=".repeat(70)); + System.out.println("Translating to " + language.name + "..."); + System.out.println("=".repeat(70)); + System.out.println(); + + // Translate files + int totalInputChars = 0; + int totalOutputChars = 0; + List failedFiles = new ArrayList<>(); + + for (int i = 0; i < filesToTranslate.size(); i++) { + Path inputPath = filesToTranslate.get(i); + Path relativePath = repoPath.relativize(inputPath); + Path outputPath = getOutputPath(inputPath, repoPath, language.suffix); + + System.out.println("[" + (i + 1) + "/" + filesToTranslate.size() + "] " + relativePath); + System.out.println(" → " + repoPath.relativize(outputPath)); + + try { + int[] chars = translateFile(inputPath, outputPath, language.code); + totalInputChars += chars[0]; + totalOutputChars += chars[1]; + + progress.completed.add(inputPath.toString()); + saveProgress(repoPath, progress); + + System.out.println(" ✅ Translated (" + chars[0] + " → " + chars[1] + " chars)"); + System.out.println(); + + } catch (Exception e) { + System.out.println(" ❌ Failed: " + e.getMessage()); + failedFiles.add(relativePath.toString()); + progress.failed.add(inputPath.toString()); + saveProgress(repoPath, progress); + System.out.println(); + } + } + + // Summary + System.out.println("=".repeat(70)); + System.out.println("Translation Complete!"); + System.out.println("=".repeat(70)); + System.out.println("✅ Translated: " + (filesToTranslate.size() - failedFiles.size()) + " files"); + System.out.println("📊 Input: " + String.format("%,d", totalInputChars) + " characters"); + System.out.println("📊 Output: " + String.format("%,d", totalOutputChars) + " characters"); + + if (!failedFiles.isEmpty()) { + System.out.println("\n❌ Failed: " + failedFiles.size() + " files"); + for (String file : failedFiles) { + System.out.println(" - " + file); + } + } + + System.out.println("\n📁 Output directory: docs_" + language.suffix + "/"); + System.out.println("📁 README: README." + language.suffix + ".md"); + System.out.println(); + System.out.println("💡 Next steps:"); + System.out.println(" 1. Review translated files in docs_" + language.suffix + "/"); + System.out.println(" 2. git add docs_" + language.suffix + "/ README." + language.suffix + ".md"); + System.out.println(" 3. git commit -m 'Add " + language.name + " translation'"); + System.out.println(" 4. Create PR"); + + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + e.printStackTrace(); + } + } + + private static void printHeader() { + System.out.println("=".repeat(70)); + System.out.println("Repository Documentation Translation Tool"); + System.out.println("=".repeat(70)); + System.out.println(); + } + + private static Language selectLanguage(Scanner scanner) { + System.out.println("=".repeat(70)); + System.out.println("Select target language:"); + System.out.println("=".repeat(70)); + + for (Map.Entry entry : LANGUAGES.entrySet()) { + System.out.printf(" %2s. %s%n", entry.getKey(), entry.getValue().name); + } + + System.out.println(); + while (true) { + System.out.print("Enter choice (1-20): "); + String choice = scanner.nextLine().trim(); + if (LANGUAGES.containsKey(choice)) { + return LANGUAGES.get(choice); + } + System.out.println("❌ Invalid choice. Please enter a number between 1-20."); + } + } + + private static List findMarkdownFiles(Path repoPath) throws IOException { + List files = new ArrayList<>(); + + // Add README.md + Path readme = repoPath.resolve("README.md"); + if (Files.exists(readme)) { + files.add(readme); + } + + // Add all .md files in docs/ + Path docsPath = repoPath.resolve("docs"); + if (Files.exists(docsPath)) { + Files.walk(docsPath) + .filter(p -> p.toString().endsWith(".md")) + .forEach(files::add); + } + + Collections.sort(files); + return files; + } + + private static Path getOutputPath(Path inputPath, Path repoPath, String langSuffix) { + String fileName = inputPath.getFileName().toString(); + + // Handle README.md + if (fileName.equals("README.md")) { + return repoPath.resolve("README." + langSuffix + ".md"); + } + + // Handle docs/ files + Path docsPath = repoPath.resolve("docs"); + Path relative = docsPath.relativize(inputPath); + + // Change extension: file.md -> file.{lang}.md + String stem = fileName.substring(0, fileName.length() - 3); + String newName = stem + "." + langSuffix + ".md"; + + return repoPath.resolve("docs_" + langSuffix).resolve(relative.getParent()).resolve(newName); + } + + private static int[] translateFile(Path inputPath, Path outputPath, String targetLang) throws IOException { + // Read input + String content = Files.readString(inputPath, StandardCharsets.UTF_8); + int inputChars = content.length(); + + // Split into chunks + List chunks = splitContent(content, CHUNK_SIZE); + + // Translate chunks + StringBuilder translated = new StringBuilder(); + for (int i = 0; i < chunks.size(); i++) { + System.out.print(" Chunk " + (i + 1) + "/" + chunks.size() + "... "); + String translatedChunk = translateText(chunks.get(i), targetLang); + translated.append(translatedChunk); + System.out.println("✅"); + + try { + Thread.sleep(1000); // Rate limiting + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + String translatedContent = translated.toString(); + int outputChars = translatedContent.length(); + + // Create output directory + Files.createDirectories(outputPath.getParent()); + + // Write output + Files.writeString(outputPath, translatedContent, StandardCharsets.UTF_8); + + return new int[]{inputChars, outputChars}; + } + + private static List splitContent(String content, int chunkSize) { + List chunks = new ArrayList<>(); + StringBuilder currentChunk = new StringBuilder(); + boolean inCodeBlock = false; + + for (String line : content.split("\n")) { + if (line.trim().startsWith("```")) { + inCodeBlock = !inCodeBlock; + } + + if (currentChunk.length() + line.length() > chunkSize && !inCodeBlock && currentChunk.length() > 0) { + chunks.add(currentChunk.toString()); + currentChunk = new StringBuilder(); + } + + currentChunk.append(line).append("\n"); + } + + if (currentChunk.length() > 0) { + chunks.add(currentChunk.toString()); + } + + return chunks; + } + + private static String translateText(String text, String targetLang) throws IOException { + // Use Google Translate API (free, no key required) + String urlStr = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=" + + targetLang + "&dt=t&q=" + URLEncoder.encode(text, StandardCharsets.UTF_8); + + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("User-Agent", "Mozilla/5.0"); + + BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + StringBuilder response = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + response.append(line); + } + in.close(); + + // Parse JSON response + JsonArray jsonArray = JsonParser.parseString(response.toString()).getAsJsonArray(); + StringBuilder translated = new StringBuilder(); + + JsonArray translations = jsonArray.get(0).getAsJsonArray(); + for (int i = 0; i < translations.size(); i++) { + JsonArray translation = translations.get(i).getAsJsonArray(); + translated.append(translation.get(0).getAsString()); + } + + return translated.toString(); + } + + private static TranslationProgress loadProgress(Path repoPath) { + Path progressFile = repoPath.resolve(PROGRESS_FILE); + if (Files.exists(progressFile)) { + try { + String json = Files.readString(progressFile); + Gson gson = new Gson(); + return gson.fromJson(json, TranslationProgress.class); + } catch (Exception e) { + // Ignore errors, return new progress + } + } + return new TranslationProgress(); + } + + private static void saveProgress(Path repoPath, TranslationProgress progress) { + Path progressFile = repoPath.resolve(PROGRESS_FILE); + try { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(progress); + Files.writeString(progressFile, json); + } catch (Exception e) { + System.err.println("Warning: Could not save progress: " + e.getMessage()); + } + } +} diff --git a/docs/.vuepress/client.ts b/docs/.vuepress/client.ts new file mode 100644 index 00000000000..e6fc1f7b6c5 --- /dev/null +++ b/docs/.vuepress/client.ts @@ -0,0 +1,10 @@ +import { defineClientConfig } from "vuepress/client"; +import { h } from "vue"; +import LayoutToggle from "./components/LayoutToggle.vue"; + +export default defineClientConfig({ + rootComponents: [ + // 将切换按钮添加为根组件,会在所有页面显示 + () => h(LayoutToggle), + ], +}); diff --git a/docs/.vuepress/components/LayoutToggle.vue b/docs/.vuepress/components/LayoutToggle.vue new file mode 100644 index 00000000000..74aaa73edaa --- /dev/null +++ b/docs/.vuepress/components/LayoutToggle.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/docs/.vuepress/navbar.ts b/docs/.vuepress/navbar.ts index 88d85c94049..4ab87e5660f 100644 --- a/docs/.vuepress/navbar.ts +++ b/docs/.vuepress/navbar.ts @@ -35,6 +35,11 @@ export default navbar([ icon: "about", children: [ { text: "关于作者", icon: "zuozhe", link: "/about-the-author/" }, + { + text: "面试突击", + icon: "pdf", + link: "https://interview.javaguide.cn/home.html", + }, { text: "更新历史", icon: "history", diff --git a/docs/.vuepress/sidebar/index.ts b/docs/.vuepress/sidebar/index.ts index 6a3c73769d4..85e065d9ec7 100644 --- a/docs/.vuepress/sidebar/index.ts +++ b/docs/.vuepress/sidebar/index.ts @@ -13,6 +13,7 @@ export default sidebar({ "/high-quality-technical-articles/": highQualityTechnicalArticles, "/zhuanlan/": [ "java-mian-shi-zhi-bei", + "interview-guide", "back-end-interview-high-frequency-system-design-and-scenario-questions", "handwritten-rpc-framework", "source-code-reading", @@ -171,6 +172,7 @@ export default sidebar({ "java21", "java22-23", "java24", + "java25", ], }, ], @@ -494,6 +496,7 @@ export default sidebar({ "paxos-algorithm", "raft-algorithm", "gossip-protocl", + "consistent-hashing", ], }, { diff --git a/docs/.vuepress/styles/index.scss b/docs/.vuepress/styles/index.scss index a895ab82939..6eb1c68e6d8 100644 --- a/docs/.vuepress/styles/index.scss +++ b/docs/.vuepress/styles/index.scss @@ -3,3 +3,125 @@ body { font-size: 16px; } } + +// 隐藏布局模式 - 通过 LayoutToggle 组件控制 +html.layout-hidden { + // 隐藏顶部导航栏 + .vp-navbar { + transform: translateY(-100%) !important; + opacity: 0 !important; + pointer-events: none !important; + } + + // 隐藏左侧边栏 + .vp-sidebar { + transform: translateX(-100%) !important; + opacity: 0 !important; + pointer-events: none !important; + width: 0 !important; + } + + // 侧边栏包装器 + .vp-sidebar-wrapper, + .sidebar-wrapper { + width: 0 !important; + min-width: 0 !important; + padding: 0 !important; + margin: 0 !important; + } + + // 隐藏右侧目录 (TOC) + .vp-toc-placeholder, + .toc-wrapper, + .vp-toc, + aside.vp-toc, + .toc { + display: none !important; + width: 0 !important; + } + + // 主容器调整 - 移除左侧 padding/margin + .theme-container { + padding-left: 0 !important; + padding-right: 0 !important; + + .vp-page { + padding-left: 2rem !important; + padding-right: 2rem !important; + padding-top: 1rem !important; + margin-left: 0 !important; + max-width: 100% !important; + width: 100% !important; + transition: all 0.3s ease; + } + } + + // 主题内容区域调整 - 让内容更宽 + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + width: 100% !important; + margin: 0 !important; + padding: 1rem 2rem !important; + } + + // 页面容器调整 + .vp-page-container { + padding-top: 1rem !important; + padding-left: 0 !important; + padding-right: 0 !important; + max-width: 100% !important; + } + + // 确保内容区域居中且宽度适中 + .theme-container > main { + margin-left: 0 !important; + padding-left: 0 !important; + max-width: 100% !important; + } + + // 响应式调整 + @media (min-width: 960px) { + .theme-container .vp-page { + margin-left: 0 !important; + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + padding: 1rem 2rem !important; + } + } + + @media (min-width: 1440px) { + .theme-container .vp-page { + margin-left: 0 !important; + padding-left: 4rem !important; + padding-right: 4rem !important; + } + + .theme-hope-content, + .vp-page-content, + .vp-content { + max-width: 100% !important; + padding: 1rem 3rem !important; + } + } +} + +// 隐藏过渡动画 +.vp-navbar, +.vp-sidebar, +.vp-page, +.theme-container .vp-page { + transition: + transform 0.3s ease, + opacity 0.3s ease, + margin 0.3s ease, + padding 0.3s ease, + width 0.3s ease; +} diff --git a/docs/README.md b/docs/README.md index ed6cbec001c..557f79cf610 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,28 +13,42 @@ actions: link: /about-the-author/zhishixingqiu-two-years.md type: default footer: |- - 鄂ICP备2020015769号-1 | 主题: VuePress Theme Hope + 鄂ICP备2020015769号-1 | 主题: VuePress Theme Hope --- -## 关于网站 +## 🔥必看 -JavaGuide 已经持续维护 6 年多了,累计提交了 **5600+** commit ,共有 **550+** 多位贡献者共同参与维护和完善。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友! +- [Java 面试指南](./home.md)(⭐网站核心):Java 学习&面试指南(Go、Python 后端面试通用,计算机基础面试总结)。 +- [Java 优质开源项目](./open-source-project/):收集整理了 Gitee/Github 上非常棒的 Java 开源项目集合,按实战项目、系统设计、工具类库等维度做了精细分类,持续更新维护! +- [优质技术书籍推荐](./open-source-project/):优质技术书籍推荐合集,涵盖了从计算机基础、数据库、搜索引擎到分布式系统、高可用架构的全方位内容,持续更新维护! -如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star(绝不强制点 Star,觉得内容不错有收获再点赞就好),这是对我最大的鼓励,感谢各位一路同行,共勉!传送门:[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。 +## 💻 实战项目 + +- [⭐AI 智能面试辅助平台 + RAG 知识库](https://javaguide.cn/zhuanlan/interview-guide.html):基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 开发。非常适合作为学习和简历项目,学习门槛低,帮助提升求职竞争力,是主打就业的实战项目。 +- [手写 RPC 框架](https://javaguide.cn/zhuanlan/handwritten-rpc-framework.html):从零开始基于 Netty+Kyro+Zookeeper 实现一个简易的 RPC 框架。麻雀虽小五脏俱全,项目代码注释详细,结构清晰。 + +## 🚀 面试突击版本 + +很多同学有“临时突击面试”的需求,所以我专门做了一个 [JavaGuide 面试突击版](https://interview.javaguide.cn/home.html):在 [JavaGuide](https://javaguide.cn/home.html) 原有内容基础上做了大幅精简,只保留高频必考重点,并一直持续更新。 + +在这些“精简后的重点”里,我又额外用 ⭐️ 标出了**重点中的重点**,方便你优先浏览、快速记忆。 -- [项目介绍](./javaguide/intro.md) -- [贡献指南](./javaguide/contribution-guideline.md) -- [常见问题](./javaguide/faq.md) +同时提供亮色(白天)和暗色(夜间)PDF,**需要打印的同学记得选亮色版本**,纸质阅读体验会更好。 -## 关于作者 +如果你**时间比较充裕**,更推荐直接在 [JavaGuide 官网](https://javaguide.cn/) 上**系统学习**:内容比突击版更全面、更深入,更适合打基础和长期提升。 -- [我曾经也是网瘾少年](./about-the-author/internet-addiction-teenager.md) -- [害,毕业三年了!](./about-the-author/my-college-life.md) -- [我的知识星球快 3 岁了!](./about-the-author/zhishixingqiu-two-years.md) -- [坚持写技术博客六年了](./about-the-author/writing-technology-blog-six-years.md) +**突击版本网站入口**:[interview.javaguide.cn](https://interview.javaguide.cn/) -## 公众号 +对应的 PDF 版本,可以直接在公众号后台回复“**PDF**”获取: -最新更新会第一时间同步在公众号,推荐关注!另外,公众号上有很多干货不会同步在线阅读网站。 +JavaGuide 公众号 + +## 🌐 关于网站 + +JavaGuide 已经持续维护 6 年多了,累计提交了接近 **6000** commit ,共有 **570+** 多位贡献者共同参与维护和完善。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友! + +如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star(绝不强制点 Star,觉得内容不错有收获再点赞就好),这是对我最大的鼓励,感谢各位一路同行,共勉!传送门:[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。 -![JavaGuide 官方公众号](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) +- [项目介绍](./javaguide/intro.md)(JavaGuide 的诞生) +- [贡献指南](./javaguide/contribution-guideline.md)(期待你的贡献,奖励丰富) +- [常见问题](./javaguide/faq.md)(统一回复大家的一些疑问) diff --git a/docs/about-the-author/deprecated-java-technologies.md b/docs/about-the-author/deprecated-java-technologies.md index fa0ed098707..0146d71c4a3 100644 --- a/docs/about-the-author/deprecated-java-technologies.md +++ b/docs/about-the-author/deprecated-java-technologies.md @@ -67,7 +67,7 @@ tag: **知识越贫乏的人,相信的东西就越绝对**,因为他们从未认真了解过与自己观点相对立的角度,也缺乏对技术发展的全局认识。 -举个例子,我刚开始学习 Java 后端开发的时候,完全没什么经验,就随便买了一本书开始看。当时看的是**《Java Web 整合开发王者归来》**这本书(梦开始的地方)。 +举个例子,我刚开始学习 Java 后端开发的时候,完全没什么经验,就随便买了一本书开始看。当时看的是 **《Java Web 整合开发王者归来》** 这本书(梦开始的地方)。 在我上大学那会儿,这本书的很多内容其实已经过时了,比如它花了大量篇幅介绍 JSP、Struts、Hibernate、EJB 和 SVN 等技术。不过,直到现在,我依然非常感谢这本书,带我走进了 Java 后端开发的大门。 diff --git a/docs/about-the-author/zhishixingqiu-two-years.md b/docs/about-the-author/zhishixingqiu-two-years.md index 644478b455a..dd0455a3f13 100644 --- a/docs/about-the-author/zhishixingqiu-two-years.md +++ b/docs/about-the-author/zhishixingqiu-two-years.md @@ -4,9 +4,7 @@ category: 知识星球 star: 2 --- - - -在 **2019 年 12 月 29 号**,经过了大概一年左右的犹豫期,我正式确定要开始做一个自己的星球,帮助学习 Java 和准备 Java 面试的同学。一转眼,已经四年多了。感谢大家一路陪伴,我会信守承诺,继续认真维护这个纯粹的 Java 知识星球,不让信任我的读者失望。 +在 **2019 年 12 月 29 号**,经过了大概一年左右的犹豫期,我正式确定要开始做一个自己的星球,帮助学习 Java 和准备 Java 面试的同学。一转眼,已经六年了。感谢大家一路陪伴,我会信守承诺,继续认真维护这个纯粹的 Java 知识星球,不让信任我的读者失望。 ![](https://oss.javaguide.cn/xingqiu/640-20230727145252757.png) @@ -16,32 +14,56 @@ star: 2 **我有自己的原则,不割韭菜,用心做内容,真心希望帮助到他人!** -## 什么是知识星球? +## 我的知识星球评价如何? + +知识星球是一个私密、长期的知识社群,用来连接创作者和铁杆读者。相比微信群,它更适合沉淀内容、做系统化的学习和信息管理。 + +下面是今年收到了部分好评,每一条都是真实存在的。我看到很多培训班或者机构通过虚构一些不存在的好评来欺骗他人购买高价服务(行业内非常常见),真的很难理解。 + +![球友对星球的真实评价](https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png) + +在这里,不只有理论,更有具体、可落地的求职/转行指导: -简单来说,知识星球就是一个私密交流圈子,主要用途是知识创作者连接铁杆读者/粉丝。相比于微信群,知识星球内容沉淀、信息管理更高效。 +- 有球友入球后,在多次一对一建议下,很快就收到了美国大模型应用开发的面试并通过; +- 有球友在指导下顺利转行,拿到满意的中厂 Offer。 -![](https://oss.javaguide.cn/xingqiu/image-20220211223754566.png) +不少球友评价我是“良心博主”:深夜 11 点多还在帮忙改简历、给建议;对非科班、大龄转行等焦虑问题,也会耐心一一解答,做到有问必回。 + +口碑是最好的证明!这里有连续续费三年的老球友,也有因为信任而把星球推荐给弟弟妹妹的朋友。 + +下面是部分球友今年的求职战绩分享(只是一小部分,有校招,也有社招),同样完全真实。每年面试季之后,星球就有大量的球友询问 offer 如何选择。 + +![部分球友今年的求职战绩](https://oss.javaguide.cn/xingqiu/job-hunting-results-from-members-2025.png) ## 我的知识星球能为你提供什么? -努力做一个最优质的 Java 面试交流星球!加入到我的星球之后,你将获得: +致力于打造最优质的 Java 面试交流星球(后端面试通用)!加入我们,你将获得远超票价的一站式成长服务: + +💎 **核心面试求职服务** + +- **简历深度精修**:提供免费的一对一简历修改服务(已累计帮助 **9000+** 位球友,好评如潮)。 +- **6 大精品专栏**:永久阅读权限,内容涵盖高频面试题、源码解析、实战项目,构建完整知识体系。 +- **独家面试手册**:多本原创 PDF 后端面试手册免费领取,全网独家。 +- **有问必答**:一对一免费提问,提供专属求职指南,拒绝焦虑。 -1. 6 个高质量的专栏永久阅读,内容涵盖面试,源码解析,项目实战等内容! -2. 多本原创 PDF 版本面试手册免费领取。 -3. 免费的简历修改服务(已经累计帮助 7000+ 位球友修改简历)。 -4. 一对一免费提问交流(专属建议,走心回答)。 -5. 专属求职指南和建议,让你少走弯路,效率翻倍! -6. 海量 Java 优质面试资源分享。 -7. 打卡活动,读书交流,学习交流,让学习不再孤单,报团取暖。 -8. 不定期福利:节日抽奖、送书送课、球友线下聚会等等。 -9. …… +🔥 **氛围与福利** -其中的任何一项服务单独拎出来价值都远超星球门票了。 +- **海量资源**:Java 优质面试资源持续更新分享。 +- **抱团成长**:打卡活动、读书交流、线下聚会,让学习之路不再孤单。 +- **惊喜福利**:不定期节日抽奖、送书送课,福利拿到手软。 -这里再送一个 **30** 元的星球专属优惠券吧,数量有限(价格即将上调。老用户续费半价 ,微信扫码即可续费)! +🚀 **拥抱 AI** + +星球目前正在深度分享 **AI 编程** 方法论,并计划推出 **AI 实战项目**。 + +💡 **总结**:这里的任何一项服务(尤其是简历修改和面试资料),单独拎出来的价值都已远超星球门票。 + +这里赠送一个 **30** 元的星球新人专属优惠券(数量有限,价格即将上调)! ![知识星球30元优惠卷](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) +老用户续费可以添加微信(**javaguide1024**)领取一个半价基础基础上的续费优惠卷,记得备注 **“续费”** 。 + ### 专属专栏 星球更新了 **《Java 面试指北》**、**《Java 必读源码系列》**(目前已经整理了 Dubbo 2.6.x、Netty 4.x、SpringBoot2.1 的源码)、 **《从零开始写一个 RPC 框架》**(已更新完)、**《Kafka 常见面试题/知识点总结》** 等多个优质专栏。 @@ -50,7 +72,7 @@ star: 2 《Java 面试指北》内容概览: -![](https://oss.javaguide.cn/xingqiu/image-20220304102536445.png) +![《Java 面试指北》内容概览](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png) 进入星球之后,这些专栏即可免费永久阅读,永久同步更新! @@ -82,7 +104,7 @@ JavaGuide 知识星球优质主题汇总传送门: 本文转自:,JavaGuide 对其做了补充完善。 @@ -357,9 +364,14 @@ public static int[] merge(int[] arr_1, int[] arr_2) { 快速排序使用[分治法](https://zh.wikipedia.org/wiki/分治法)(Divide and conquer)策略来把一个序列分为较小和较大的 2 个子序列,然后递归地排序两个子序列。具体算法描述如下: -1. 从序列中**随机**挑出一个元素,做为 “基准”(`pivot`); -2. 重新排列序列,将所有比基准值小的元素摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个操作结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作; -3. 递归地把小于基准值元素的子序列和大于基准值元素的子序列进行快速排序。 +1. **选择基准(Pivot)** :从数组中选一个元素作为基准。为了避免最坏情况,通常会随机选择。 +2. **分区(Partition)** :重新排列序列,将所有比基准值小的元素摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个操作结束之后,该基准就处于数列的中间位置。 +3. **递归(Recurse)** :递归地把小于基准值元素的子序列和大于基准值元素的子序列进行快速排序。 + +**关于性能,这也是它与归并排序的关键区别:** + +- **平均和最佳情况:** 它的时间复杂度是 $O(nlogn)$。这种情况发生在每次分区都能把数组分成均等的两半。 +- **最坏情况:** 它的时间复杂度会退化到 $O(n^2)$。这发生在每次我们选的基准都是当前数组的最小值或最大值时,比如对一个已经排好序的数组,每次都选第一个元素做基准,这就会导致分区极其不均,算法退化成类似冒泡排序。这就是为什么**随机选择基准**非常重要。 ### 图解算法 diff --git a/docs/cs-basics/algorithms/classical-algorithm-problems-recommendations.md b/docs/cs-basics/algorithms/classical-algorithm-problems-recommendations.md index 3a6a01a210f..3e6adf13f0f 100644 --- a/docs/cs-basics/algorithms/classical-algorithm-problems-recommendations.md +++ b/docs/cs-basics/algorithms/classical-algorithm-problems-recommendations.md @@ -3,6 +3,13 @@ title: 经典算法思想总结(含LeetCode题目推荐) category: 计算机基础 tag: - 算法 +head: + - - meta + - name: keywords + content: 贪心,分治,回溯,动态规划,二分,双指针,算法思想,题目推荐 + - - meta + - name: description + content: 总结常见算法思想与解题模板,配合典型题目推荐,强调思维路径与复杂度权衡,快速构建解题体系。 --- ## 贪心算法 diff --git a/docs/cs-basics/algorithms/common-data-structures-leetcode-recommendations.md b/docs/cs-basics/algorithms/common-data-structures-leetcode-recommendations.md index 51d9225730f..89dd601d52a 100644 --- a/docs/cs-basics/algorithms/common-data-structures-leetcode-recommendations.md +++ b/docs/cs-basics/algorithms/common-data-structures-leetcode-recommendations.md @@ -3,6 +3,13 @@ title: 常见数据结构经典LeetCode题目推荐 category: 计算机基础 tag: - 算法 +head: + - - meta + - name: keywords + content: LeetCode,数组,链表,栈,队列,二叉树,题目推荐,刷题 + - - meta + - name: description + content: 按数据结构类别整理经典 LeetCode 题目清单,聚焦高频与核心考点,助力系统化刷题与巩固。 --- ## 数组 diff --git a/docs/cs-basics/algorithms/linkedlist-algorithm-problems.md b/docs/cs-basics/algorithms/linkedlist-algorithm-problems.md index 1280445409e..cb85d399815 100644 --- a/docs/cs-basics/algorithms/linkedlist-algorithm-problems.md +++ b/docs/cs-basics/algorithms/linkedlist-algorithm-problems.md @@ -3,6 +3,13 @@ title: 几道常见的链表算法题 category: 计算机基础 tag: - 算法 +head: + - - meta + - name: keywords + content: 链表算法,两数相加,反转链表,环检测,合并链表,复杂度分析 + - - meta + - name: description + content: 精选链表高频题的思路与实现,覆盖两数相加、反转、环检测等场景,强调边界处理与复杂度分析。 --- diff --git a/docs/cs-basics/algorithms/string-algorithm-problems.md b/docs/cs-basics/algorithms/string-algorithm-problems.md index 796fe7bf986..ae320ddbbec 100644 --- a/docs/cs-basics/algorithms/string-algorithm-problems.md +++ b/docs/cs-basics/algorithms/string-algorithm-problems.md @@ -3,6 +3,13 @@ title: 几道常见的字符串算法题 category: 计算机基础 tag: - 算法 +head: + - - meta + - name: keywords + content: 字符串算法,KMP,BM,滑动窗口,子串,匹配,复杂度 + - - meta + - name: description + content: 总结字符串高频算法与题型,重点讲解 KMP/BM 原理、滑动窗口等技巧,助力高效匹配与实现。 --- > 作者:wwwxmu diff --git a/docs/cs-basics/algorithms/the-sword-refers-to-offer.md b/docs/cs-basics/algorithms/the-sword-refers-to-offer.md index 73d296d0dc3..84e072a277d 100644 --- a/docs/cs-basics/algorithms/the-sword-refers-to-offer.md +++ b/docs/cs-basics/algorithms/the-sword-refers-to-offer.md @@ -3,6 +3,13 @@ title: 剑指offer部分编程题 category: 计算机基础 tag: - 算法 +head: + - - meta + - name: keywords + content: 剑指Offer,斐波那契,递归,迭代,链表,数组,面试题 + - - meta + - name: description + content: 选编《剑指 Offer》常见编程题,给出递归与迭代等多种思路与示例,实现对高频题型的高效复盘。 --- ## 斐波那契数列 diff --git a/docs/cs-basics/data-structure/bloom-filter.md b/docs/cs-basics/data-structure/bloom-filter.md index be17c1a53aa..6a02b00c566 100644 --- a/docs/cs-basics/data-structure/bloom-filter.md +++ b/docs/cs-basics/data-structure/bloom-filter.md @@ -3,6 +3,13 @@ title: 布隆过滤器 category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 布隆过滤器,Bloom Filter,误判率,哈希函数,位数组,去重,缓存穿透 + - - meta + - name: description + content: 解析 Bloom Filter 的原理与误判特性,结合哈希与位数组实现,适用于海量数据去重与缓存穿透防护。 --- 布隆过滤器相信大家没用过的话,也已经听过了。 diff --git a/docs/cs-basics/data-structure/graph.md b/docs/cs-basics/data-structure/graph.md index e9860c240d5..e3d3d3488f8 100644 --- a/docs/cs-basics/data-structure/graph.md +++ b/docs/cs-basics/data-structure/graph.md @@ -3,6 +3,13 @@ title: 图 category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 图,邻接表,邻接矩阵,DFS,BFS,度,有向图,无向图,连通性 + - - meta + - name: description + content: 介绍图的基本概念与常用表示,结合 DFS/BFS 等核心算法与应用场景,掌握图论入门必备知识。 --- 图是一种较为复杂的非线性结构。 **为啥说其较为复杂呢?** diff --git a/docs/cs-basics/data-structure/heap.md b/docs/cs-basics/data-structure/heap.md index 5de2e5f2ee2..7b3cfc58d06 100644 --- a/docs/cs-basics/data-structure/heap.md +++ b/docs/cs-basics/data-structure/heap.md @@ -2,6 +2,13 @@ category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 堆,最大堆,最小堆,优先队列,堆化,上浮,下沉,堆排序 + - - meta + - name: description + content: 解析堆的性质与操作,理解优先队列实现与堆排序性能优势,掌握插入/删除的复杂度与实践场景。 --- # 堆 diff --git a/docs/cs-basics/data-structure/linear-data-structure.md b/docs/cs-basics/data-structure/linear-data-structure.md index e8ae63a19d5..d1631fe861a 100644 --- a/docs/cs-basics/data-structure/linear-data-structure.md +++ b/docs/cs-basics/data-structure/linear-data-structure.md @@ -3,6 +3,13 @@ title: 线性数据结构 category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 数组,链表,栈,队列,双端队列,复杂度分析,随机访问,插入删除 + - - meta + - name: description + content: 总结数组/链表/栈/队列的特性与操作,配合复杂度分析与典型应用,掌握线性结构的选型与实现。 --- ## 1. 数组 diff --git a/docs/cs-basics/data-structure/red-black-tree.md b/docs/cs-basics/data-structure/red-black-tree.md index 462010e910e..80ca65bdfa4 100644 --- a/docs/cs-basics/data-structure/red-black-tree.md +++ b/docs/cs-basics/data-structure/red-black-tree.md @@ -3,6 +3,13 @@ title: 红黑树 category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 红黑树,自平衡,旋转,插入删除,性质,黑高,时间复杂度 + - - meta + - name: description + content: 深入讲解红黑树的五大性质与旋转调整过程,理解自平衡机制及在标准库与索引结构中的应用。 --- ## 红黑树介绍 diff --git a/docs/cs-basics/data-structure/tree.md b/docs/cs-basics/data-structure/tree.md index de9c6eb6a27..e70f4a75b7d 100644 --- a/docs/cs-basics/data-structure/tree.md +++ b/docs/cs-basics/data-structure/tree.md @@ -3,6 +3,13 @@ title: 树 category: 计算机基础 tag: - 数据结构 +head: + - - meta + - name: keywords + content: 树,二叉树,二叉搜索树,平衡树,遍历,前序,中序,后序,层序,高度,深度 + - - meta + - name: description + content: 系统讲解树与二叉树的核心概念与遍历方法,结合高度/深度等指标,夯实数据结构基础与算法思维。 --- 树就是一种类似现实生活中的树的数据结构(倒置的树)。任何一颗非空树只有一个根节点。 diff --git a/docs/cs-basics/network/application-layer-protocol.md b/docs/cs-basics/network/application-layer-protocol.md index cb809b9157d..f71afbf93e7 100644 --- a/docs/cs-basics/network/application-layer-protocol.md +++ b/docs/cs-basics/network/application-layer-protocol.md @@ -3,6 +3,13 @@ title: 应用层常见协议总结(应用层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 应用层协议,HTTP,WebSocket,DNS,SMTP,FTP,特性,场景 + - - meta + - name: description + content: 汇总应用层常见协议的核心概念与典型场景,重点对比 HTTP 与 WebSocket 的通信模型与能力边界。 --- ## HTTP:超文本传输协议 diff --git a/docs/cs-basics/network/arp.md b/docs/cs-basics/network/arp.md index c4ece76011c..d8364c6f183 100644 --- a/docs/cs-basics/network/arp.md +++ b/docs/cs-basics/network/arp.md @@ -3,6 +3,13 @@ title: ARP 协议详解(网络层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: ARP,地址解析,IP到MAC,广播问询,单播响应,ARP表,欺骗 + - - meta + - name: description + content: 讲解 ARP 的地址解析机制与报文流程,结合 ARP 表与广播/单播详解常见攻击与防御策略。 --- 每当我们学习一个新的网络协议的时候,都要把他结合到 OSI 七层模型中,或者是 TCP/IP 协议栈中来学习,一是要学习该协议在整个网络协议栈中的位置,二是要学习该协议解决了什么问题,地位如何?三是要学习该协议的工作原理,以及一些更深入的细节。 @@ -21,7 +28,7 @@ tag: MAC 地址的全称是 **媒体访问控制地址(Media Access Control Address)**。如果说,互联网中每一个资源都由 IP 地址唯一标识(IP 协议内容),那么一切网络设备都由 MAC 地址唯一标识。 -![路由器的背面就会注明 MAC 位址](./images/arp/2008410143049281.png) +![路由器的背面就会注明 MAC 位址](https://oss.javaguide.cn/github/javaguide/cs-basics/network/router-back-will-indicate-mac-address.png) 可以理解为,MAC 地址是一个网络设备真正的身份证号,IP 地址只是一种不重复的定位方式(比如说住在某省某市某街道的张三,这种逻辑定位是 IP 地址,他的身份证号才是他的 MAC 地址),也可以理解为 MAC 地址是身份证号,IP 地址是邮政地址。MAC 地址也有一些别称,如 LAN 地址、物理地址、以太网地址等。 diff --git a/docs/cs-basics/network/computer-network-xiexiren-summary.md b/docs/cs-basics/network/computer-network-xiexiren-summary.md index fc9f60fd39a..b9254d24ca8 100644 --- a/docs/cs-basics/network/computer-network-xiexiren-summary.md +++ b/docs/cs-basics/network/computer-network-xiexiren-summary.md @@ -3,6 +3,13 @@ title: 《计算机网络》(谢希仁)内容总结 category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 计算机网络,谢希仁,术语,分层模型,链路,主机,教材总结 + - - meta + - name: description + content: 基于《计算机网络》教材的学习笔记,梳理术语与分层模型等核心知识点,便于期末复习与面试巩固。 --- 本文是我在大二学习计算机网络期间整理, 大部分内容都来自于谢希仁老师的[《计算机网络》第七版](https://www.elias.ltd/usr/local/etc/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%88%E7%AC%AC7%E7%89%88%EF%BC%89%E8%B0%A2%E5%B8%8C%E4%BB%81.pdf)这本书。为了内容更容易理解,我对之前的整理进行了一波重构,并配上了一些相关的示意图便于理解。 @@ -145,7 +152,7 @@ tag: 2. **数据链路(data link)**:把实现控制数据运输的协议的硬件和软件加到链路上就构成了数据链路。 3. **循环冗余检验 CRC(Cyclic Redundancy Check)**:为了保证数据传输的可靠性,CRC 是数据链路层广泛使用的一种检错技术。 4. **帧(frame)**:一个数据链路层的传输单元,由一个数据链路层首部和其携带的封包所组成协议数据单元。 -5. **MTU(Maximum Transfer Uint )**:最大传送单元。帧的数据部分的的长度上限。 +5. **MTU(Maximum Transfer Uint )**:最大传送单元。帧的数据部分的长度上限。 6. **误码率 BER(Bit Error Rate )**:在一段时间内,传输错误的比特占所传输比特总数的比率。 7. **PPP(Point-to-Point Protocol )**:点对点协议。即用户计算机和 ISP 进行通信时所使用的数据链路层协议。以下是 PPP 帧的示意图: ![PPP](https://oss.javaguide.cn/p3-juejin/6b0310d3103c4149a725a28aaf001899~tplv-k3u1fbpfcp-zoom-1.jpeg) @@ -191,7 +198,7 @@ tag: 5. **子网掩码(subnet mask )**:它是一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。 6. **CIDR( Classless Inter-Domain Routing )**:无分类域间路由选择 (特点是消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的“网络前缀”(network-prefix)来代替分类地址中的网络号和子网号)。 7. **默认路由(default route)**:当在路由表中查不到能到达目的地址的路由时,路由器选择的路由。默认路由还可以减小路由表所占用的空间和搜索路由表所用的时间。 -8. **路由选择算法(Virtual Circuit)**:路由选择协议的核心部分。因特网采用自适应的,分层次的路由选择协议。 +8. **路由选择算法(Routing Algorithm)**:路由选择协议的核心部分。因特网采用自适应的,分层次的路由选择协议。 ### 4.2. 重要知识点总结 @@ -289,7 +296,7 @@ tag: ![](https://oss.javaguide.cn/p3-juejin/8e3efca026654874bde8be88c96e1783~tplv-k3u1fbpfcp-zoom-1.jpeg) -9. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。 +9. **代理服务器(Proxy Server)**:代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。 10. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。 ![一个电子邮件被发送的过程](https://oss.javaguide.cn/p3-juejin/2bdccb760474435aae52559f2ef9652f~tplv-k3u1fbpfcp-zoom-1.png) diff --git a/docs/cs-basics/network/dns.md b/docs/cs-basics/network/dns.md index 3d3ef0e2254..3fbe6e3c100 100644 --- a/docs/cs-basics/network/dns.md +++ b/docs/cs-basics/network/dns.md @@ -3,6 +3,13 @@ title: DNS 域名系统详解(应用层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: DNS,域名解析,递归查询,迭代查询,缓存,权威DNS,端口53,UDP + - - meta + - name: description + content: 详解 DNS 的层次结构与解析流程,覆盖递归/迭代、缓存与权威服务器,明确应用层端口与性能优化要点。 --- DNS(Domain Name System)域名管理系统,是当用户使用浏览器访问网址之后,使用的第一个重要协议。DNS 要解决的是**域名和 IP 地址的映射问题**。 diff --git a/docs/cs-basics/network/http-status-codes.md b/docs/cs-basics/network/http-status-codes.md index 5550e06d5b8..ca3f9d3379a 100644 --- a/docs/cs-basics/network/http-status-codes.md +++ b/docs/cs-basics/network/http-status-codes.md @@ -3,6 +3,13 @@ title: HTTP 常见状态码总结(应用层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: HTTP 状态码,2xx,3xx,4xx,5xx,重定向,错误码,201 Created,204 No Content + - - meta + - name: description + content: 汇总常见 HTTP 状态码含义与使用场景,强调 201/204 等易混淆点,提升接口设计与调试效率。 --- HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被成功处理。 diff --git a/docs/cs-basics/network/http-vs-https.md b/docs/cs-basics/network/http-vs-https.md index 71c224f1be4..c054b41a20a 100644 --- a/docs/cs-basics/network/http-vs-https.md +++ b/docs/cs-basics/network/http-vs-https.md @@ -3,6 +3,13 @@ title: HTTP vs HTTPS(应用层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: HTTP,HTTPS,SSL,TLS,加密,认证,端口,安全性,握手流程 + - - meta + - name: description + content: 对比 HTTP 与 HTTPS 的协议与安全机制,解析 SSL/TLS 工作原理与握手流程,明确应用层安全落地细节。 --- ## HTTP 协议 diff --git a/docs/cs-basics/network/http1.0-vs-http1.1.md b/docs/cs-basics/network/http1.0-vs-http1.1.md index f0bb9850780..8155ce2df4c 100644 --- a/docs/cs-basics/network/http1.0-vs-http1.1.md +++ b/docs/cs-basics/network/http1.0-vs-http1.1.md @@ -3,6 +3,13 @@ title: HTTP 1.0 vs HTTP 1.1(应用层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: HTTP/1.0,HTTP/1.1,长连接,管道化,缓存,状态码,Host,带宽优化 + - - meta + - name: description + content: 细致对比 HTTP/1.0 与 HTTP/1.1 的协议差异,涵盖长连接、管道化、缓存与状态码增强等关键变更与实践影响。 --- 这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1: diff --git a/docs/cs-basics/network/images/arp/2008410143049281.png b/docs/cs-basics/network/images/arp/2008410143049281.png deleted file mode 100644 index 759fb441f6c..00000000000 Binary files a/docs/cs-basics/network/images/arp/2008410143049281.png and /dev/null differ diff --git a/docs/cs-basics/network/nat.md b/docs/cs-basics/network/nat.md index 4567719b81e..42bf24b0c7e 100644 --- a/docs/cs-basics/network/nat.md +++ b/docs/cs-basics/network/nat.md @@ -3,6 +3,13 @@ title: NAT 协议详解(网络层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: NAT,地址转换,端口映射,LAN,WAN,连接跟踪,DHCP + - - meta + - name: description + content: 解析 NAT 的地址转换与端口映射机制,结合 LAN/WAN 通信与转换表,理解家庭与企业网络的实践细节。 --- ## 应用场景 diff --git a/docs/cs-basics/network/network-attack-means.md b/docs/cs-basics/network/network-attack-means.md index 748999d6eba..4b3aed4efa2 100644 --- a/docs/cs-basics/network/network-attack-means.md +++ b/docs/cs-basics/network/network-attack-means.md @@ -3,6 +3,13 @@ title: 网络攻击常见手段总结 category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 网络攻击,DDoS,IP 欺骗,ARP 欺骗,中间人攻击,扫描,防护 + - - meta + - name: description + content: 总结常见 TCP/IP 攻击与防护思路,覆盖 DDoS、IP/ARP 欺骗、中间人等手段,强调工程防护实践。 --- > 本文整理完善自[TCP/IP 常见攻击手段 - 暖蓝笔记 - 2021](https://mp.weixin.qq.com/s/AZwWrOlLxRSSi-ywBgZ0fA)这篇文章。 diff --git a/docs/cs-basics/network/osi-and-tcp-ip-model.md b/docs/cs-basics/network/osi-and-tcp-ip-model.md index 34092a336b6..bc7b157d841 100644 --- a/docs/cs-basics/network/osi-and-tcp-ip-model.md +++ b/docs/cs-basics/network/osi-and-tcp-ip-model.md @@ -3,6 +3,13 @@ title: OSI 和 TCP/IP 网络分层模型详解(基础) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: OSI 七层,TCP/IP 四层,分层模型,职责划分,协议栈,对比 + - - meta + - name: description + content: 详解 OSI 与 TCP/IP 的分层模型与职责划分,结合历史与实践对比两者差异与工程取舍。 --- ## OSI 七层模型 diff --git a/docs/cs-basics/network/other-network-questions.md b/docs/cs-basics/network/other-network-questions.md index 2f4da42d1a0..b2a98dfe437 100644 --- a/docs/cs-basics/network/other-network-questions.md +++ b/docs/cs-basics/network/other-network-questions.md @@ -3,6 +3,13 @@ title: 计算机网络常见面试题总结(上) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 计算机网络面试题,TCP/IP四层模型,HTTP面试,HTTPS vs HTTP,HTTP/1.1 vs HTTP/2,HTTP/3 QUIC,TCP三次握手,UDP区别,DNS解析,WebSocket vs SSE,GET vs POST,应用层协议,网络分层,队头阻塞,PING命令,ARP协议 + - - meta + - name: description + content: 最新计算机网络高频面试题总结(上):TCP/IP四层模型、HTTP全版本对比、TCP三次握手、DNS解析、WebSocket/SSE实时推送等,附图解+⭐️重点标注,一文搞定应用层&传输层&网络层核心考点,快速备战后端面试! --- @@ -27,7 +34,7 @@ tag: ![osi七层模型2](https://oss.javaguide.cn/github/javaguide/osi七层模型2.png) -#### TCP/IP 四层模型是什么?每一层的作用是什么? +#### ⭐️TCP/IP 四层模型是什么?每一层的作用是什么? **TCP/IP 四层模型** 是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成: @@ -40,7 +47,7 @@ tag: ![TCP/IP 四层模型](https://oss.javaguide.cn/github/javaguide/cs-basics/network/tcp-ip-4-model.png) -关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](./osi-and-tcp-ip-model.md) 这篇文章。 +关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](https://javaguide.cn/cs-basics/network/osi-and-tcp-ip-model.html) 这篇文章。 #### 为什么网络要分层? @@ -64,7 +71,7 @@ tag: ### 常见网络协议 -#### 应用层有哪些常见的协议? +#### ⭐️应用层有哪些常见的协议? ![应用层常见协议](https://oss.javaguide.cn/github/javaguide/cs-basics/network/application-layer-protocol.png) @@ -100,7 +107,7 @@ tag: ## HTTP -### 从输入 URL 到页面展示到底发生了什么?(非常重要) +### ⭐️从输入 URL 到页面展示到底发生了什么?(非常重要) > 类似的问题:打开一个网页,整个过程会使用哪些协议? @@ -120,15 +127,15 @@ tag: 6. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示。 7. 浏览器在不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求。 -详细介绍可以查看这篇文章:[访问网页的全过程(知识串联)](./the-whole-process-of-accessing-web-pages.md)(强烈推荐)。 +详细介绍可以查看这篇文章:[访问网页的全过程(知识串联)](https://javaguide.cn/cs-basics/network/the-whole-process-of-accessing-web-pages.html)(强烈推荐)。 -### HTTP 状态码有哪些? +### ⭐️HTTP 状态码有哪些? HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被成功处理。 ![常见 HTTP 状态码](https://oss.javaguide.cn/github/javaguide/cs-basics/network/http-status-code.png) -关于 HTTP 状态码更详细的总结,可以看我写的这篇文章:[HTTP 常见状态码总结(应用层)](./http-status-codes.md)。 +关于 HTTP 状态码更详细的总结,可以看我写的这篇文章:[HTTP 常见状态码总结(应用层)](https://javaguide.cn/cs-basics/network/http-status-codes.html)。 ### HTTP Header 中常见的字段有哪些? @@ -167,7 +174,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被 | Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) | | Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning | -### HTTP 和 HTTPS 有什么区别?(重要) +### ⭐️HTTP 和 HTTPS 有什么区别?(重要) ![HTTP 和 HTTPS 对比](https://oss.javaguide.cn/github/javaguide/cs-basics/network/http-vs-https.png) @@ -176,7 +183,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被 - **安全性和资源消耗**:HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。 - **SEO(搜索引擎优化)**:搜索引擎通常会更青睐使用 HTTPS 协议的网站,因为 HTTPS 能够提供更高的安全性和用户隐私保护。使用 HTTPS 协议的网站在搜索结果中可能会被优先显示,从而对 SEO 产生影响。 -关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](./http-vs-https.md) 。 +关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](https://javaguide.cn/cs-basics/network/http-vs-https.html) 。 ### HTTP/1.0 和 HTTP/1.1 有什么区别? @@ -188,9 +195,9 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被 - **带宽**:HTTP/1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。 - **Host 头(Host Header)处理** :HTTP/1.1 引入了 Host 头字段,允许在同一 IP 地址上托管多个域名,从而支持虚拟主机的功能。而 HTTP/1.0 没有 Host 头字段,无法实现虚拟主机。 -关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](./http1.0-vs-http1.1.md) 。 +关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](https://javaguide.cn/cs-basics/network/http1.0-vs-http1.1.html) 。 -### HTTP/1.1 和 HTTP/2.0 有什么区别? +### ⭐️HTTP/1.1 和 HTTP/2.0 有什么区别? ![HTTP/1.0 和 HTTP/1.1 对比](https://oss.javaguide.cn/github/javaguide/cs-basics/network/http1.1-vs-http2.0.png) @@ -256,7 +263,7 @@ HTTP/1.1 队头阻塞的主要原因是无法多路复用: | **缓解方法** | 开启多个并行的 TCP 连接 | 减少网络掉包或者使用基于 UDP 的 QUIC 协议 | | **影响场景** | 每次都会发生,尤其是大文件阻塞小文件时。 | 丢包率较高的网络环境下更容易发生。 | -### HTTP 是不保存状态的协议, 如何保存用户状态? +### ⭐️HTTP 是不保存状态的协议, 如何保存用户状态? HTTP 协议本身是 **无状态的 (stateless)** 。这意味着服务器默认情况下无法区分两个连续的请求是否来自同一个用户,或者同一个用户之前的操作是什么。这就像一个“健忘”的服务员,每次你跟他说话,他都不知道你是谁,也不知道你之前点过什么菜。 @@ -326,9 +333,9 @@ URI 的作用像身份证号一样,URL 的作用更像家庭住址一样。URL ### Cookie 和 Session 有什么区别? -准确点来说,这个问题属于认证授权的范畴,你可以在 [认证授权基础概念详解](../../system-design/security/basis-of-authority-certification.md) 这篇文章中找到详细的答案。 +准确点来说,这个问题属于认证授权的范畴,你可以在 [认证授权基础概念详解](https://javaguide.cn/system-design/security/basis-of-authority-certification.html) 这篇文章中找到详细的答案。 -### GET 和 POST 的区别 +### ⭐️GET 和 POST 的区别 这个问题在知乎上被讨论的挺火热的,地址: 。 @@ -365,7 +372,7 @@ WebSocket 协议本质上是应用层的协议,用于弥补 HTTP 协议在持 - 社交聊天 - …… -### WebSocket 和 HTTP 有什么区别? +### ⭐️WebSocket 和 HTTP 有什么区别? WebSocket 和 HTTP 两者都是基于 TCP 的应用层协议,都可以在网络中传输数据。 @@ -387,7 +394,7 @@ WebSocket 的工作过程可以分为以下几个步骤: 另外,建立 WebSocket 连接之后,通过心跳机制来保持 WebSocket 连接的稳定性和活跃性。 -### WebSocket 与短轮询、长轮询的区别 +### ⭐️WebSocket 与短轮询、长轮询的区别 这三种方式,都是为了解决“**客户端如何及时获取服务器最新数据,实现实时更新**”的问题。它们的实现方式和效率、实时性差异较大。 @@ -422,7 +429,7 @@ WebSocket 的工作过程可以分为以下几个步骤: ![Websocket 示意图](https://oss.javaguide.cn/github/javaguide/system-design/web-real-time-message-push/1460000042192394.png) -### SSE 与 WebSocket 有什么区别? +### ⭐️SSE 与 WebSocket 有什么区别? SSE (Server-Sent Events) 和 WebSocket 都是用来实现服务器向浏览器实时推送消息的技术,让网页内容能自动更新,而不需要用户手动刷新。虽然目标相似,但它们在工作方式和适用场景上有几个关键区别: @@ -442,15 +449,15 @@ SSE (Server-Sent Events) 和 WebSocket 都是用来实现服务器向浏览器 - **SSE:** **主要设计用来传输文本** (UTF-8 编码)。如果需要传输二进制数据,需要先进行 Base64 等编码转换成文本。 - **WebSocket:** **原生支持传输文本和二进制数据**,无需额外编码。 -为了提供更好的用户体验和利用其简单、高效、基于标准 HTTP 的特性,**Server-Sent Events (SSE) 是目前大型语言模型 API(如 OpenAI、DeepSeek 等)实现流式响应的常用甚至可以说是标准的技木选择**。 +为了提供更好的用户体验和利用其简单、高效、基于标准 HTTP 的特性,**Server-Sent Events (SSE) 是目前大型语言模型 API(如 OpenAI、DeepSeek 等)实现流式响应的常用甚至可以说是标准的技术选择**。 这里以 DeepSeek 为例,我们发送一个请求并打开浏览器控制台验证一下: -![](https://oss.javaguide.cn/github/javaguide/cs-basics/network/deepseek-sse.png) +![DeepSeek 响应标头](https://oss.javaguide.cn/github/javaguide/cs-basics/network/deepseek-sse.png) ![](https://oss.javaguide.cn/github/javaguide/cs-basics/network/deepseek-sse-eventstream.png) -可以看到,响应头应里包含了 `text/event-stream`,说明使用的确实是SSE。并且,响应数据也确实是持续分块传输。 +可以看到,响应头应里包含了 `text/event-stream`,说明使用的确实是 SSE。并且,响应数据也确实是持续分块传输。 ## PING @@ -521,9 +528,9 @@ DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务 世界上并不是只有 13 台根服务器,这是很多人普遍的误解,网上很多文章也是这么写的。实际上,现在根服务器数量远远超过这个数量。最初确实是为 DNS 根服务器分配了 13 个 IP 地址,每个 IP 地址对应一个不同的根 DNS 服务器。然而,由于互联网的快速发展和增长,这个原始的架构变得不太适应当前的需求。为了提高 DNS 的可靠性、安全性和性能,目前这 13 个 IP 地址中的每一个都有多个服务器,截止到 2023 年底,所有根服务器之和达到了 1700 多台,未来还会继续增加。 -### DNS 解析的过程是什么样的? +### ⭐️DNS 解析的过程是什么样的? -整个过程的步骤比较多,我单独写了一篇文章详细介绍:[DNS 域名系统详解(应用层)](./dns.md) 。 +整个过程的步骤比较多,我单独写了一篇文章详细介绍:[DNS 域名系统详解(应用层)](https://javaguide.cn/cs-basics/network/dns.html) 。 ### DNS 劫持了解吗?如何应对? diff --git a/docs/cs-basics/network/other-network-questions2.md b/docs/cs-basics/network/other-network-questions2.md index 67c731f44c0..99c7dc19f8f 100644 --- a/docs/cs-basics/network/other-network-questions2.md +++ b/docs/cs-basics/network/other-network-questions2.md @@ -3,13 +3,20 @@ title: 计算机网络常见面试题总结(下) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 计算机网络面试题,TCP vs UDP,TCP三次握手,HTTP/3 QUIC,IPv4 vs IPv6,TCP可靠性,IP地址,NAT协议,ARP协议,传输层面试,网络层高频题,基于TCP协议,基于UDP协议,队头阻塞,四次挥手 + - - meta + - name: description + content: 最新计算机网络高频面试题总结(下):TCP/UDP深度对比、三次握手四次挥手、HTTP/3 QUIC优化、IPv6优势、NAT/ARP详解,附表格+⭐️重点标注,一文掌握传输层&网络层核心考点,快速通关后端技术面试! --- 下篇主要是传输层和网络层相关的内容。 ## TCP 与 UDP -### TCP 与 UDP 的区别(重要) +### ⭐️TCP 与 UDP 的区别(重要) 1. **是否面向连接**: - TCP 是面向连接的。在传输数据之前,必须先通过“三次握手”建立连接;数据传输完成后,还需要通过“四次挥手”来释放连接。这保证了双方都准备好通信。 @@ -47,7 +54,7 @@ tag: | **通信模式** | 点对点 (单播) | 单播、多播、广播 | | **常见应用** | HTTP/HTTPS, FTP, SMTP, SSH | DNS, DHCP, SNMP, TFTP, VoIP, 视频流 | -### 什么时候选择 TCP,什么时候选 UDP? +### ⭐️什么时候选择 TCP,什么时候选 UDP? 选择 TCP 还是 UDP,主要取决于你的应用**对数据传输的可靠性要求有多高,以及对实时性和效率的要求有多高**。 @@ -57,7 +64,7 @@ tag: - **文件传输 (FTP, SCP):** 文件内容不允许有任何字节丢失或错序。 - **邮件收发 (SMTP, POP3, IMAP):** 邮件内容需要完整无误地送达。 - **远程登录 (SSH, Telnet):** 命令和响应需要准确传输。 -- ...... +- …… 当**实时性、速度和效率优先,并且应用能容忍少量数据丢失或乱序**时,通常选择 UDP。UDP 开销小、传输快,没有建立连接和保证可靠性的复杂过程。典型应用场景如下: @@ -65,7 +72,7 @@ tag: - **在线游戏:** 需要快速传输玩家位置、状态等信息,对实时性要求极高,旧的数据很快就没用了,丢失少量数据影响通常不大。 - **DHCP (动态主机配置协议):** 客户端在请求 IP 时自身没有 IP 地址,无法满足 TCP 建立连接的前提条件,并且 DHCP 有广播需求、交互模式简单以及自带可靠性机制。 - **物联网 (IoT) 数据上报:** 某些场景下,传感器定期上报数据,丢失个别数据点可能不影响整体趋势分析。 -- ...... +- …… ### HTTP 基于 TCP 还是 UDP? @@ -136,7 +143,7 @@ TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层 - **TCP** 更适合那些对数据**可靠性、完整性和顺序性**要求高的应用,如网页浏览 (HTTP/HTTPS)、文件传输 (FTP/SFTP)、邮件收发 (SMTP/POP3/IMAP)。 - **UDP** 则更适用于那些对**实时性要求高、能容忍少量数据丢失**的应用,如域名解析 (DNS)、实时音视频 (RTP)、在线游戏、网络管理 (SNMP) 等。 -### TCP 三次握手和四次挥手(非常重要) +### ⭐️TCP 三次握手和四次挥手(非常重要) **相关面试题**: @@ -147,11 +154,11 @@ TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层 - 如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样? - 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态? -**参考答案**:[TCP 三次握手和四次挥手(传输层)](./tcp-connection-and-disconnection.md) 。 +**参考答案**:[TCP 三次握手和四次挥手(传输层)](https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html) 。 -### TCP 如何保证传输的可靠性?(重要) +### ⭐️TCP 如何保证传输的可靠性?(重要) -[TCP 传输可靠性保障(传输层)](./tcp-reliability-guarantee.md) +[TCP 传输可靠性保障(传输层)](https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html) ## IP @@ -179,7 +186,7 @@ TCP (传输控制协议) 和 UDP (用户数据报协议) 是互联网传输层 IP 地址过滤是一种简单的网络安全措施,实际应用中一般会结合其他网络安全措施,如认证、授权、加密等一起使用。单独使用 IP 地址过滤并不能完全保证网络的安全。 -### IPv4 和 IPv6 有什么区别? +### ⭐️IPv4 和 IPv6 有什么区别? **IPv4(Internet Protocol version 4)** 是目前广泛使用的 IP 地址版本,其格式是四组由点分隔的数字,例如:123.89.46.72。IPv4 使用 32 位地址作为其 Internet 地址,这意味着共有约 42 亿( 2^32)个可用 IP 地址。 @@ -224,7 +231,7 @@ NAT 不光可以缓解 IPv4 地址资源短缺的问题,还可以隐藏内部 ![NAT 实现 IP地址转换](https://oss.javaguide.cn/github/javaguide/cs-basics/network/network-address-translation.png) -相关阅读:[NAT 协议详解(网络层)](./nat.md)。 +相关阅读:[NAT 协议详解(网络层)](https://javaguide.cn/cs-basics/network/nat.html)。 ## ARP @@ -232,7 +239,7 @@ NAT 不光可以缓解 IPv4 地址资源短缺的问题,还可以隐藏内部 MAC 地址的全称是 **媒体访问控制地址(Media Access Control Address)**。如果说,互联网中每一个资源都由 IP 地址唯一标识(IP 协议内容),那么一切网络设备都由 MAC 地址唯一标识。 -![路由器的背面就会注明 MAC 位址](./images/arp/2008410143049281.png) +![路由器的背面就会注明 MAC 位址](https://oss.javaguide.cn/github/javaguide/cs-basics/network/router-back-will-indicate-mac-address.png) 可以理解为,MAC 地址是一个网络设备真正的身份证号,IP 地址只是一种不重复的定位方式(比如说住在某省某市某街道的张三,这种逻辑定位是 IP 地址,他的身份证号才是他的 MAC 地址),也可以理解为 MAC 地址是身份证号,IP 地址是邮政地址。MAC 地址也有一些别称,如 LAN 地址、物理地址、以太网地址等。 @@ -244,13 +251,13 @@ MAC 地址具有可携带性、永久性,身份证号永久地标识一个人 最后,记住,MAC 地址有一个特殊地址:FF-FF-FF-FF-FF-FF(全 1 地址),该地址表示广播地址。 -### ARP 协议解决了什么问题? +### ⭐️ARP 协议解决了什么问题? ARP 协议,全称 **地址解析协议(Address Resolution Protocol)**,它解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址,ARP 协议解决了 IP 地址转 MAC 地址的一些问题。 ### ARP 协议的工作原理? -[ARP 协议详解(网络层)](./arp.md) +[ARP 协议详解(网络层)](https://javaguide.cn/cs-basics/network/arp.html) ## 复习建议 diff --git a/docs/cs-basics/network/tcp-connection-and-disconnection.md b/docs/cs-basics/network/tcp-connection-and-disconnection.md index 63bc97f82c9..7a8997348a3 100644 --- a/docs/cs-basics/network/tcp-connection-and-disconnection.md +++ b/docs/cs-basics/network/tcp-connection-and-disconnection.md @@ -3,9 +3,16 @@ title: TCP 三次握手和四次挥手(传输层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: TCP,三次握手,四次挥手,状态机,SYN,ACK,FIN,半连接队列,全连接队列 + - - meta + - name: description + content: 详解 TCP 建连与断连过程,结合状态迁移与队列机制解析可靠通信保障与高并发连接处理。 --- -为了准确无误地把数据送达目标处,TCP 协议采用了三次握手策略。 +TCP 是一种面向连接的、可靠的传输层协议。为了在两个不可靠的端点之间建立一个可靠的连接,TCP 采用了三次握手(Three-way Handshake)的策略。 ## 建立连接-TCP 三次握手 @@ -13,32 +20,56 @@ tag: 建立一个 TCP 连接需要“三次握手”,缺一不可: -- **一次握手**:客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 **SYN_SEND** 状态,等待服务端的确认; -- **二次握手**:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 **SYN_RECV** 状态; -- **三次握手**:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务端都进入**ESTABLISHED** 状态,完成 TCP 三次握手。 +1. **第一次握手 (SYN)**: 客户端向服务端发送一个 SYN(Synchronize Sequence Numbers)报文段,其中包含一个由客户端随机生成的初始序列号(Initial Sequence Number, ISN),例如 seq=x。发送后,客户端进入 **SYN_SEND** 状态,等待服务端的确认。 +2. **第二次握手 (SYN+ACK)**: 服务端收到 SYN 报文段后,如果同意建立连接,会向客户端回复一个确认报文段。该报文段包含两个关键信息: + - **SYN**:服务端也需要同步自己的初始序列号,因此报文段中也包含一个由服务端随机生成的初始序列号,例如 seq=y。 + - **ACK** (Acknowledgement):用于确认收到了客户端的请求。其确认号被设置为客户端初始序列号加一,即 ack=x+1。 + - 发送该报文段后,服务端进入 **SYN_RECV** 状态。 +3. **第三次握手 (ACK)**: 客户端收到服务端的 SYN+ACK 报文段后,会向服务端发送一个最终的确认报文段。该报文段包含确认号 ack=y+1。发送后,客户端进入 **ESTABLISHED** 状态。服务端收到这个 ACK 报文段后,也进入 **ESTABLISHED** 状态。 -当建立了 3 次握手之后,客户端和服务端就可以传输数据啦! +至此,双方都确认了连接的建立,TCP 连接成功创建,可以开始进行双向数据传输。 ### 什么是半连接队列和全连接队列? -在 TCP 三次握手过程中,Linux 内核会维护两个队列来管理连接请求: +在 TCP 三次握手过程中,服务端内核会使用两个队列来管理连接请求: -1. **半连接队列**(也称 SYN Queue):当服务端收到客户端的 SYN 请求时,此时双方还没有完全建立连接,它会把半连接状态的连接放在半连接队列。 +1. **半连接队列**(也称 SYN Queue):当服务端收到客户端的 SYN 请求并回复 SYN+ACK 后,连接会处于 SYN_RECV 状态。此时,这个连接信息会被放入半连接队列。这个队列存储的是尚未完成三次握手的连接。 2. **全连接队列**(也称 Accept Queue):当服务端收到客户端对 ACK 响应时,意味着三次握手成功完成,服务端会将该连接从半连接队列移动到全连接队列。如果未收到客户端的 ACK 响应,会进行重传,重传的等待时间通常是指数增长的。如果重传次数超过系统规定的最大重传次数,系统将从半连接队列中删除该连接信息。 -这两个队列的存在是为了处理并发连接请求,确保服务端能够有效地管理新的连接请求。另外,新的连接请求被拒绝或忽略除了和每个队列的大小限制有关系之外,还和很多其他因素有关系,这里就不详细介绍了,整体逻辑比较复杂。 +这两个队列的存在是为了处理并发连接请求,确保服务端能够有效地管理新的连接请求。 + +如果全连接队列满了,新的已完成握手的连接可能会被丢弃,或者触发其他策略。这两个队列的大小都受系统参数控制,它们的容量限制是影响服务器处理高并发连接能力的重要因素,也是 SYN 泛洪攻击(SYN Flood)所针对的目标。 ### 为什么要三次握手? -三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。 +TCP 三次握手的核心目的是为了在客户端和服务器之间建立一个**可靠的**、**全双工的**通信信道。这需要实现两个主要目标: + +**1. 确认双方的收发能力,并同步初始序列号 (ISN)** + +TCP 通信依赖序列号来保证数据的有序和可靠。三次握手是双方交换和确认彼此初始序列号(ISN)的过程,通过这个过程,双方也间接验证了各自的收发能力。 + +- **第一次握手 (客户端 → 服务器)** :客户端发送 SYN 包。 + - 服务器:能确认客户端的发送能力正常,自己的接收能力正常。 + - 客户端:无法确认任何事。 +- **第二次握手 (服务器 → 客户端)** :服务器回复 SYN+ACK 包。 + - 客户端:能确认自己的发送和接收能力正常,服务器的接收和发送能力正常。 + - 服务端:能确认对方发送能力正常,自己接收能力正常 +- **第三次握手 (客户端 → 服务器)** :客户端发送 ACK 包。 + - 客户端:能确认双方发送和接收能力正常。 + - 服务端:能确认双方发送和接收能力正常。 + +经过这三次交互,双方都确认了彼此的收发功能完好,并完成了初始序列号的同步,为后续可靠的数据传输奠定了基础。 + +**2. 防止已失效的连接请求被错误地建立** + +这是“为什么不能是两次握手”的关键原因。 -1. **第一次握手**:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常 -2. **第二次握手**:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常 -3. **第三次握手**:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常 +设想一个场景:客户端发送的第一个连接请求(SYN1)因网络延迟而滞留,于是客户端重发了第二个请求(SYN2)并成功建立了连接,数据传输完毕后连接被释放。此时,延迟的 SYN1 才到达服务端。 -三次握手就能确认双方收发功能都正常,缺一不可。 +- **如果是两次握手**:服务端收到这个失效的 SYN1 后,会误认为是一个新的连接请求,并立即分配资源、建立连接。但这将导致服务端单方面维持一个无效连接,白白浪费系统资源,因为客户端并不会有任何响应。 +- **有了第三次握手**:服务端收到失效的 SYN1 并回复 SYN+ACK 后,会等待客户端的最终确认(ACK)。由于客户端当前并没有发起连接的意图,它会忽略这个 SYN+ACK 或者发送一个 RST (Reset) 报文。这样,服务端就无法收到第三次握手的 ACK,最终会超时关闭这个错误的连接,从而避免了资源浪费。 -更详细的解答可以看这个:[TCP 为什么是三次握手,而不是两次或四次? - 车小胖的回答 - 知乎](https://www.zhihu.com/question/24853633/answer/115173386) 。 +因此,三次握手是确保 TCP 连接可靠性的**最小且必需**的步骤。它不仅确认了双方的通信能力,更重要的是增加了一个最终确认环节,以防止网络中延迟、重复的历史请求对连接建立造成干扰。 ### 第 2 次握手传回了 ACK,为什么还要传回 SYN? @@ -58,10 +89,10 @@ tag: 断开一个 TCP 连接则需要“四次挥手”,缺一不可: -1. **第一次挥手**:客户端发送一个 FIN(SEQ=x) 标志的数据包->服务端,用来关闭客户端到服务端的数据传送。然后客户端进入 **FIN-WAIT-1** 状态。 -2. **第二次挥手**:服务端收到这个 FIN(SEQ=X) 标志的数据包,它发送一个 ACK (ACK=x+1)标志的数据包->客户端 。然后服务端进入 **CLOSE-WAIT** 状态,客户端进入 **FIN-WAIT-2** 状态。 -3. **第三次挥手**:服务端发送一个 FIN (SEQ=y)标志的数据包->客户端,请求关闭连接,然后服务端进入 **LAST-ACK** 状态。 -4. **第四次挥手**:客户端发送 ACK (ACK=y+1)标志的数据包->服务端,然后客户端进入**TIME-WAIT**状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。此时如果客户端等待 **2MSL** 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。 +1. **第一次挥手 (FIN)**:当客户端(或任何一方)决定关闭连接时,它会向服务端发送一个 **FIN**(Finish)标志的报文段,表示自己已经没有数据要发送了。该报文段包含一个序列号 seq=u。发送后,客户端进入 **FIN-WAIT-1** 状态。 +2. **第二次挥手 (ACK)**:服务端收到 FIN 报文段后,会立即回复一个 **ACK** 确认报文段。其确认号为 ack=u+1。发送后,服务端进入 **CLOSE-WAIT** 状态。客户端收到这个 ACK 后,进入 **FIN-WAIT-2** 状态。此时,TCP 连接处于**半关闭(Half-Close)**状态:客户端到服务端的发送通道已关闭,但服务端到客户端的发送通道仍然可以传输数据。 +3. **第三次挥手 (FIN)**:当服务端确认所有待发送的数据都已发送完毕后,它也会向客户端发送一个 **FIN** 报文段,表示自己也准备关闭连接。该报文段同样包含一个序列号 seq=y。发送后,服务端进入 **LAST-ACK** 状态,等待客户端的最终确认。 +4. **第四次挥手**:客户端收到服务端的 FIN 报文段后,会回复一个最终的 **ACK** 确认报文段,确认号为 ack=y+1。发送后,客户端进入 **TIME-WAIT** 状态。服务端在收到这个 ACK 后,立即进入 **CLOSED** 状态,完成连接关闭。客户端则会在 **TIME-WAIT** 状态下等待 **2MSL**(Maximum Segment Lifetime,报文段最大生存时间)后,才最终进入 **CLOSED** 状态。 **只要四次挥手没有结束,客户端和服务端就可以继续传输数据!** @@ -82,7 +113,7 @@ TCP 是全双工通信,可以双向传输数据。任何一方都可以在数 ### 如果第二次挥手时服务端的 ACK 没有送达客户端,会怎样? -客户端没有收到 ACK 确认,会重新发送 FIN 请求。 +客户端在发送 FIN 后会启动一个重传计时器。如果在计时器超时之前没有收到服务端的 ACK,客户端会认为 FIN 报文丢失,并重新发送 FIN 报文。 ### 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态? diff --git a/docs/cs-basics/network/tcp-reliability-guarantee.md b/docs/cs-basics/network/tcp-reliability-guarantee.md index d4c9bea80ed..e55c937af0f 100644 --- a/docs/cs-basics/network/tcp-reliability-guarantee.md +++ b/docs/cs-basics/network/tcp-reliability-guarantee.md @@ -3,6 +3,13 @@ title: TCP 传输可靠性保障(传输层) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: TCP,可靠性,重传,SACK,流量控制,拥塞控制,滑动窗口,校验和 + - - meta + - name: description + content: 系统梳理 TCP 的可靠性保障机制,覆盖重传/选择确认、流量与拥塞控制,明确端到端可靠传输的实现要点。 --- ## TCP 如何保证传输的可靠性? diff --git a/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md b/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md index 906d16fae2e..dc61cbb3dc8 100644 --- a/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md +++ b/docs/cs-basics/network/the-whole-process-of-accessing-web-pages.md @@ -3,6 +3,13 @@ title: 访问网页的全过程(知识串联) category: 计算机基础 tag: - 计算机网络 +head: + - - meta + - name: keywords + content: 访问网页流程,DNS,TCP 建连,HTTP 请求,资源加载,渲染,关闭连接 + - - meta + - name: description + content: 串联从输入 URL 到页面渲染的完整链路,涵盖 DNS、TCP、HTTP 与静态资源加载,助力面试与实践理解。 --- 开发岗中总是会考很多计算机网络的知识点,但如果让面试官只考一道题,便涵盖最多的计网知识点,那可能就是 **网页浏览的全过程** 了。本篇文章将带大家从头到尾过一遍这道被考烂的面试题,必会!!! @@ -71,7 +78,7 @@ TCP 协议保证了数据传输的可靠性,是数据包传输的主力协议 终于,来到网络层,此时我们的主机不再是和另一台主机进行交互了,而是在和中间系统进行交互。也就是说,应用层和传输层都是端到端的协议,而网络层及以下都是中间件的协议了。 -**网络层的的核心功能——转发与路由**,必会!!!如果面试官问到了网络层,而你恰好又什么都不会的话,最最起码要说出这五个字——**转发与路由**。 +**网络层的核心功能——转发与路由**,必会!!!如果面试官问到了网络层,而你恰好又什么都不会的话,最最起码要说出这五个字——**转发与路由**。 - 转发:将分组从路由器的输入端口转移到合适的输出端口。 - 路由:确定分组从源到目的经过的路径。 diff --git a/docs/cs-basics/operating-system/linux-intro.md b/docs/cs-basics/operating-system/linux-intro.md index 1486fe45c90..f13a52d29eb 100644 --- a/docs/cs-basics/operating-system/linux-intro.md +++ b/docs/cs-basics/operating-system/linux-intro.md @@ -8,6 +8,9 @@ head: - - meta - name: description content: 简单介绍一下 Java 程序员必知的 Linux 的一些概念以及常见命令。 + - - meta + - name: keywords + content: Linux,基础命令,发行版,文件系统,权限,进程,网络 --- @@ -285,11 +288,11 @@ Linux 中的打包文件一般是以 `.tar` 结尾的,压缩的命令一般是 需要注意的是:**超级用户可以无视普通用户的权限,即使文件目录权限是 000,依旧可以访问。** -**在 linux 中的每个用户必须属于一个组,不能独立于组外。在 linux 中每个文件有所有者、所在组、其它组的概念。** +**在 Linux 中的每个用户必须属于一个组,不能独立于组外。在 linux 中每个文件有所有者、所在组、其它组的概念。** -- **所有者(u)**:一般为文件的创建者,谁创建了该文件,就天然的成为该文件的所有者,用 `ls ‐ahl` 命令可以看到文件的所有者 也可以使用 chown 用户名 文件名来修改文件的所有者 。 -- **文件所在组(g)**:当某个用户创建了一个文件后,这个文件的所在组就是该用户所在的组用 `ls ‐ahl`命令可以看到文件的所有组也可以使用 chgrp 组名 文件名来修改文件所在的组。 -- **其它组(o)**:除开文件的所有者和所在组的用户外,系统的其它用户都是文件的其它组。 +- **所有者(u)** :一般为文件的创建者,谁创建了该文件,就天然的成为该文件的所有者,用 `ls ‐ahl` 命令可以看到文件的所有者 也可以使用 chown 用户名 文件名来修改文件的所有者 。 +- **文件所在组(g)** :当某个用户创建了一个文件后,这个文件的所在组就是该用户所在的组用 `ls ‐ahl`命令可以看到文件的所有组也可以使用 chgrp 组名 文件名来修改文件所在的组。 +- **其它组(o)** :除开文件的所有者和所在组的用户外,系统的其它用户都是文件的其它组。 > 我们再来看看如何修改文件/目录的权限。 @@ -361,7 +364,7 @@ Linux 系统是一个多用户多任务的分时操作系统,任何一个要 ### 其他 - `sudo + 其他命令`:以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。 -- `grep 要搜索的字符串 要搜索的文件 --color`:搜索命令,--color 代表高亮显示。 +- `grep [选项] "搜索内容" 文件路径`:非常强大且常用的文本搜索命令,它可以根据指定的字符串或正则表达式,在文件或命令输出中进行匹配查找,适用于日志分析、文本过滤、快速定位等多种场景。示例:忽略大小写搜索 syslog 中所有包含 error 的行:`grep -i "error" /var/log/syslog`,查找所有与 java 相关的进程:`ps -ef | grep "java"`。 - `kill -9 进程的pid`:杀死进程(-9 表示强制终止)先用 ps 查找进程,然后用 kill 杀掉。 - `shutdown`:`shutdown -h now`:指定现在立即关机;`shutdown +5 "System will shutdown after 5 minutes"`:指定 5 分钟后关机,同时送出警告信息给登入用户。 - `reboot`:`reboot`:重开机。`reboot -w`:做个重开机的模拟(只有纪录并不会真的重开机)。 diff --git a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md index db7fb7bfa23..1a9036fca42 100644 --- a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md +++ b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md @@ -6,10 +6,10 @@ tag: head: - - meta - name: keywords - content: 操作系统,进程,进程通信方式,死锁,操作系统内存管理,块表,多级页表,虚拟内存,页面置换算法 + content: 操作系统面试题,用户态 vs 内核态,进程 vs 线程,死锁必要条件,系统调用过程,进程调度算法,PCB进程控制块,进程间通信IPC,死锁预防避免,操作系统基础高频题,虚拟内存管理 - - meta - name: description - content: 很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如进程管理、内存管理、虚拟内存等等。 + content: 最新操作系统高频面试题总结(上):用户态/内核态切换、进程线程区别、死锁四条件、系统调用详解、调度算法对比,附图表+⭐️重点标注,一文掌握OS核心考点,快速通关后端技术面试! --- @@ -98,15 +98,17 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win 根据进程访问资源的特点,我们可以把进程在系统上的运行分为两个级别: -- **用户态(User Mode)** : 用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限。当应用程序需要执行某些需要特殊权限的操作,例如读写磁盘、网络通信等,就需要向操作系统发起系统调用请求,进入内核态。 -- **内核态(Kernel Mode)**:内核态运行的进程几乎可以访问计算机的任何资源包括系统的内存空间、设备、驱动程序等,不受限制,拥有非常高的权限。当操作系统接收到进程的系统调用请求时,就会从用户态切换到内核态,执行相应的系统调用,并将结果返回给进程,最后再从内核态切换回用户态。 - ![用户态和内核态](https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/usermode-and-kernelmode.png) +- **用户态(User Mode)** : 用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限。当应用程序需要执行某些需要特殊权限的操作,例如读写磁盘、网络通信等,就需要向操作系统发起系统调用请求,进入内核态。 +- **内核态(Kernel Mode)** :内核态运行的进程几乎可以访问计算机的任何资源包括系统的内存空间、设备、驱动程序等,不受限制,拥有非常高的权限。当操作系统接收到进程的系统调用请求时,就会从用户态切换到内核态,执行相应的系统调用,并将结果返回给进程,最后再从内核态切换回用户态。 + 内核态相比用户态拥有更高的特权级别,因此能够执行更底层、更敏感的操作。不过,由于进入内核态需要付出较高的开销(需要进行一系列的上下文切换和权限检查),应该尽量减少进入内核态的次数,以提高系统的性能和稳定性。 #### 为什么要有用户态和内核态?只有一个内核态不行么? +这样设计主要是为了**安全**和**稳定**。 + - 在 CPU 的所有指令中,有一些指令是比较危险的比如内存分配、设置时钟、IO 处理等,如果所有的程序都能使用这些指令的话,会对系统的正常运行造成灾难性地影响。因此,我们需要限制这些危险指令只能内核态运行。这些只能由操作系统内核态执行的指令也被叫做 **特权指令** 。 - 如果计算机系统中只有一个内核态,那么所有程序或进程都必须共享系统资源,例如内存、CPU、硬盘等,这将导致系统资源的竞争和冲突,从而影响系统性能和效率。并且,这样也会让系统的安全性降低,毕竟所有程序或进程都具有相同的特权级别和访问权限。 @@ -118,12 +120,14 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win 用户态切换到内核态的 3 种方式: -1. **系统调用(Trap)**:用户态进程 **主动** 要求切换到内核态的一种方式,主要是为了使用内核态才能做的事情比如读取磁盘资源。系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现。 -2. **中断(Interrupt)**:当外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。 -3. **异常(Exception)**:当 CPU 在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。 +1. **系统调用(Trap)**:这是最主要的方式,是应用程序**主动**发起的。比如,当我们的程序需要读取一个文件或者发送网络数据时,它无法直接操作磁盘或网卡,就必须调用操作系统提供的接口(如 `read()`,`send()`), 这会触发一次从用户态到内核态的切换。 +2. **中断(Interrupt)**:这是**被动**的,由外部硬件设备触发。比如,当硬盘完成了数据读取,会向 CPU 发送一个中断信号,CPU 会暂停当前用户态的程序,切换到内核态去处理这个中断。 +3. **异常(Exception)**:这也是**被动**的,由程序自身错误引起。比如,我们的代码执行了一个除以零的操作,或者访问了一个非法的内存地址(缺页异常),CPU 会捕获这个异常,并切换到内核态去处理它。 在系统的处理上,中断和异常类似,都是通过中断向量表来找到相应的处理程序进行处理。区别在于,中断来自处理器外部,不是由任何一条专门的指令造成,而异常是执行当前指令的结果。 +最后,需要强调的是,这种**状态切换是有性能开销的**。因为它涉及到保存用户态的上下文(寄存器等)、切换到内核态执行、再恢复用户态的上下文。因此,在高性能编程中,我们常常需要考虑如何减少这种切换次数,比如通过缓冲 I/O 来批量读写文件,就是一个典型的例子。 + ### 系统调用 #### 什么是系统调用? @@ -157,12 +161,17 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win ## 进程和线程 -### 什么是进程和线程? +### 进程和线程的区别是什么? + +进程和线程是操作系统中并发执行的两个核心概念,它们的关系可以理解为 **工厂和工人** 的关系。 -- **进程(Process)** 是指计算机中正在运行的一个程序实例。举例:你打开的微信就是一个进程。 -- **线程(Thread)** 也被称为轻量级进程,更加轻量。多个线程可以在同一个进程中同时执行,并且共享进程的资源比如内存空间、文件句柄、网络连接等。举例:你打开的微信里就有一个线程专门用来拉取别人发你的最新的消息。 +**进程(Process)就像一个工厂**。操作系统在分配资源时,是以进程为基本单位的。比如,当我启动一个微信,操作系统就为它建立了一个独立的工厂,分配给它专属的内存空间、文件句柄等资源。这个工厂与其他工厂(比如我打开的浏览器进程)是严格隔离的。 -### 进程和线程的区别是什么? +**线程(Thread)则像是工厂里的工人**。一个工厂里可以有很多工人,他们共享这个工厂的资源,但每个工人有自己的工具箱和任务清单,让他们可以独立地执行不同的任务。比如微信这个工厂里,可以有一个工人(线程)负责接收消息,一个工人负责渲染界面。 + +这是我用 AI 绘制的一张图片,可以说是非常形象了: + +![](https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/process-and-thread-difference-wechat-factory-as-an-example.png) 下图是 Java 内存区域,我们从 JVM 的角度来说一下线程和进程之间的关系吧! @@ -170,18 +179,17 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win 从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的**堆**和**方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器**、**虚拟机栈** 和 **本地方法栈**。 -**总结:** +这里从 3 个角度总结下线程和进程的核心区别: -- 线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。 -- 线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。 -- 线程执行开销小,但不利于资源的管理和保护;而进程正相反。 +1. **资源所有权:** 进程是资源分配的基本单位,拥有独立的地址空间;而线程是 CPU 调度的基本单位,几乎不拥有系统资源,只保留少量私有数据(PC、栈、寄存器),主要共享其所属进程的资源。 +2. **开销:** 创建或销毁一个工厂(进程)的开销很大,需要分配独立的资源。而雇佣或解雇一个工人(线程)的开销就小得多。同理,进程间的上下文切换开销远大于线程间的切换。 +3. **健壮性:** 工厂之间是隔离的,一个工厂倒闭(进程崩溃)不会影响其他工厂。但一个工厂内的工人之间是共享资源的,一个工人操作失误(比如一个线程访问了非法内存)可能会导致整个工厂停工(整个进程崩溃)。 ### 有了进程为什么还需要线程? -- 进程切换是一个开销很大的操作,线程切换的成本较低。 -- 线程更轻量,一个进程可以创建多个线程。 -- 多个线程可以并发处理不同的任务,更有效地利用了多处理器和多核计算机。而进程只能在一个时间干一件事,如果在执行过程中遇到阻塞问题比如 IO 阻塞就会挂起直到结果返回。 -- 同一进程内的线程共享内存和文件,因此它们之间相互通信无须调用内核。 +核心原因就是**为了在单个应用内实现低开销、高效率的并发**。如果我想让微信同时接收消息和发送文件,如果用两个进程来实现,不仅资源开销巨大,它们之间通信还非常麻烦(需要 IPC)。而使用两个线程,它们不仅切换成本低,还能直接通过共享内存高效通信,从而能更好地利用多核 CPU,提升应用的响应速度和吞吐量。 + +再那我们上面举的工厂和工人为例:线程=同一屋檐下的轻量级工人,切换成本低、共享内存零拷贝;若换成两个独立进程,就得各建一座工厂(独立地址空间),既费砖又费电(资源与 IPC 开销)。 ### 为什么要使用多线程? @@ -250,36 +258,37 @@ PCB 主要包含下面几部分的内容: ![常见进程调度算法](https://oss.javaguide.cn/github/javaguide/cs-basics/network/scheduling-algorithms-of-process.png) -这是一个很重要的知识点!为了确定首先执行哪个进程以及最后执行哪个进程以实现最大 CPU 利用率,计算机科学家已经定义了一些算法,它们是: +进程调度算法的核心目标是决定就绪队列中的哪个进程应该获得 CPU 资源,其设计目标通常是在**吞吐量、周转时间、响应时间**和**公平性**之间做权衡。 -- **先到先服务调度算法(FCFS,First Come, First Served)** : 从就绪队列中选择一个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。 -- **短作业优先的调度算法(SJF,Shortest Job First)** : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。 -- **时间片轮转调度算法(RR,Round-Robin)** : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。 -- **多级反馈队列调度算法(MFQ,Multi-level Feedback Queue)**:前面介绍的几种进程调度的算法都有一定的局限性。如**短进程优先的调度算法,仅照顾了短进程而忽略了长进程** 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成,因而它是目前**被公认的一种较好的进程调度算法**,UNIX 操作系统采取的便是这种调度算法。 -- **优先级调度算法(Priority)**:为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。 +我习惯将这些算法分为两大类:**非抢占式**和**抢占式**。 -### 什么是僵尸进程和孤儿进程? +**第一类:非抢占式调度 (Non-Preemptive)** -在 Unix/Linux 系统中,子进程通常是通过 fork()系统调用创建的,该调用会创建一个新的进程,该进程是原有进程的一个副本。子进程和父进程的运行是相互独立的,它们各自拥有自己的 PCB,即使父进程结束了,子进程仍然可以继续运行。 +这种方式下,一旦 CPU 分配给一个进程,它就会一直运行下去,直到任务完成或主动放弃(比如等待 I/O)。 -当一个进程调用 exit()系统调用结束自己的生命时,内核会释放该进程的所有资源,包括打开的文件、占用的内存等,但是该进程对应的 PCB 依然存在于系统中。这些信息只有在父进程调用 wait()或 waitpid()系统调用时才会被释放,以便让父进程得到子进程的状态信息。 +1. **先到先服务调度算法(FCFS,First Come, First Served)** : 这是最简单的,就像排队,谁先来谁先用。优点是公平、实现简单。但缺点很明显,如果一个很长的任务先到了,后面无数个短任务都得等着,这会导致平均等待时间很长,我们称之为“护航效应”。 +2. **短作业优先的调度算法(SJF,Shortest Job First)** : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源。理论上,它的平均等待时间是最短的,吞吐量很高。但缺点是,它需要预测运行时间,这很难做到,而且可能会导致长作业“饿死”,永远得不到执行。 -这样的设计可以让父进程在子进程结束时得到子进程的状态信息,并且可以防止出现“僵尸进程”(即子进程结束后 PCB 仍然存在但父进程无法得到状态信息的情况)。 +**第二类:抢占式调度 (Preemptive)** -- **僵尸进程**:子进程已经终止,但是其父进程仍在运行,且父进程没有调用 wait()或 waitpid()等系统调用来获取子进程的状态信息,释放子进程占用的资源,导致子进程的 PCB 依然存在于系统中,但无法被进一步使用。这种情况下,子进程被称为“僵尸进程”。避免僵尸进程的产生,父进程需要及时调用 wait()或 waitpid()系统调用来回收子进程。 -- **孤儿进程**:一个进程的父进程已经终止或者不存在,但是该进程仍在运行。这种情况下,该进程就是孤儿进程。孤儿进程通常是由于父进程意外终止或未及时调用 wait()或 waitpid()等系统调用来回收子进程导致的。为了避免孤儿进程占用系统资源,操作系统会将孤儿进程的父进程设置为 init 进程(进程号为 1),由 init 进程来回收孤儿进程的资源。 +操作系统可以强制剥夺当前进程的 CPU 使用权,分配给其他更重要的进程。现代操作系统基本都采用这种方式。 -### 如何查看是否有僵尸进程? +- **时间片轮转调度算法(RR,Round-Robin)** : 这是最经典、最公平的抢占式算法。它给每个进程分配一个固定的时间片,用完了就把它放到队尾,切换到下一个进程。它非常适合分时系统,保证了每个进程都能得到响应,但时间片的设置很关键:太长了退化成 FCFS,太短了则会导致过于频繁的上下文切换,增加系统开销。 +- **优先级调度算法(Priority)**:每个进程都有一个优先级,进程调度器总是选择优先级最高的进程,具有相同优先级的进程以 FCFS 方式执行。这很灵活,可以根据内存要求,时间要求或任何其他资源要求来确定优先级,但同样可能导致低优先级进程“饿死”。 -Linux 下可以使用 Top 命令查找,`zombie` 值表示僵尸进程的数量,为 0 则代表没有僵尸进程。 +前面介绍的几种进程调度的算法都有一定的局限性,如:**短进程优先的调度算法,仅照顾了短进程而忽略了长进程** 。那有没有一种结合了上面这些进程调度算法优点的呢? -![僵尸进程查看](https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/zombie-process-view.jpg) +**多级反馈队列调度算法(MFQ,Multi-level Feedback Queue)** 是现实世界中最常用的一种算法,比如早期的 UNIX。它非常聪明,结合了 RR 和优先级调度。它设置了多个不同优先级的队列,每个队列使用 RR 调度,时间片大小也不同。新进程先进入最高优先级队列;如果在一个时间片内没执行完,就会被降级到下一个队列。这样既照顾了短作业(在高优先级队列中快速完成),也保证了长作业不会饿死(最终会在低优先级队列中得到执行),是一种非常均衡的方案。 -下面这个命令可以定位僵尸进程以及该僵尸进程的父进程: +### 那究竟是谁来调度这个进程呢? -```bash -ps -A -ostat,ppid,pid,cmd |grep -e '^[Zz]' -``` +负责进程调度的核心是操作系统内核中的两个紧密协作的组件:**调度程序(Scheduler)** 和 **分派程序(Dispatcher)**。我们可以把它们理解成一个团队: + +- **调度程序 (Scheduler):** 可以看作是决策者。当需要进行调度时,调度程序会被激活,它会根据预设的调度算法(比如我们前面聊到的多级反馈队列),从就绪队列中挑选出下一个应该占用 CPU 的进程。 +- **分派程序 (Dispatcher):** 可以看作是执行者。它负责完成具体的“交接”工作,也就是**上下文切换**。这个过程非常底层,主要包括: + - 保存当前进程的上下文(CPU 寄存器状态、程序计数器等)到其进程控制块(PCB)中。 + - 加载下一个被选中进程的上下文,从其 PCB 中读取状态,恢复到 CPU 寄存器。 + - 将 CPU 的控制权正式移交给新进程,让它开始运行。 ## 死锁 @@ -287,23 +296,23 @@ ps -A -ostat,ppid,pid,cmd |grep -e '^[Zz]' 死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。 -### 能列举一个操作系统发生死锁的例子吗? +一个最经典的例子就是**“交叉持锁”**。想象有两个线程和两个锁: -假设有两个进程 A 和 B,以及两个资源 X 和 Y,它们的分配情况如下: +- 线程 1 先拿到了锁 A,然后尝试去获取锁 B。 +- 几乎同时,线程 2 拿到了锁 B,然后尝试去获取锁 A。 -| 进程 | 占用资源 | 需求资源 | -| ---- | -------- | -------- | -| A | X | Y | -| B | Y | X | +这时,线程 1 等着线程 2 释放锁 B,而线程 2 等着线程 1 释放锁 A,双方都持有对方需要的资源,并等待对方释放,就形成了一个“死结”。 -此时,进程 A 占用资源 X 并且请求资源 Y,而进程 B 已经占用了资源 Y 并请求资源 X。两个进程都在等待对方释放资源,无法继续执行,陷入了死锁状态。 + ### 产生死锁的四个必要条件是什么? +死锁的发生并不是偶然的,它需要同时满足**四个必要条件**: + 1. **互斥**:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。 2. **占有并等待**:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。 3. **非抢占**:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。 -4. **循环等待**:有一组等待进程 `{P0, P1,..., Pn}`, `P0` 等待的资源被 `P1` 占有,`P1` 等待的资源被 `P2` 占有,……,`Pn-1` 等待的资源被 `Pn` 占有,`Pn` 等待的资源被 `P0` 占有。 +4. **循环等待**:有一组等待进程 {P0, P1,..., Pn}, P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,……,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。 **注意 ⚠️**:这四个条件是产生死锁的 **必要条件** ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。 @@ -371,12 +380,9 @@ Thread[线程 2,5,main]waiting get resource1 解决死锁的方法可以从多个角度去分析,一般的情况下,有**预防,避免,检测和解除四种**。 -- **预防** 是采用某种策略,**限制并发进程对资源的请求**,从而使得死锁的必要条件在系统执行的任何时间上都不满足。 - -- **避免**则是系统在分配资源时,根据资源的使用情况**提前做出预测**,从而**避免死锁的发生** - -- **检测**是指系统设有**专门的机构**,当死锁发生时,该机构能够检测死锁的发生,并精确地确定与死锁有关的进程和资源。 -- **解除** 是与检测相配套的一种措施,用于**将进程从死锁状态下解脱出来**。 +- **死锁预防:** 这是我们程序员最常用的方法。通过编码规范来破坏条件。最经典的就是**破坏循环等待**,比如规定所有线程都必须**按相同的顺序**来获取锁(比如先 A 后 B),这样就不会形成环路。 +- **死锁避免:** 这是一种更动态的方法,比如操作系统的**银行家算法**。它会在分配资源前进行预测,如果这次分配可能导致未来发生死锁,就拒绝分配。但这种方法开销很大,在通用系统中用得比较少。 +- **死锁检测与解除:** 这是一种“事后补救”的策略,就像乐观锁。系统允许死锁发生,但会有一个后台线程(或机制)定期检测是否存在死锁环路(比如通过分析线程等待图)。一旦发现,就会采取措施解除,比如**强制剥夺某个线程的资源或直接终止它**。数据库系统中的死锁处理就常常采用这种方式。 #### 死锁的预防 diff --git a/docs/cs-basics/operating-system/operating-system-basic-questions-02.md b/docs/cs-basics/operating-system/operating-system-basic-questions-02.md index 1d3fc0968bd..d4a33b253a4 100644 --- a/docs/cs-basics/operating-system/operating-system-basic-questions-02.md +++ b/docs/cs-basics/operating-system/operating-system-basic-questions-02.md @@ -6,10 +6,10 @@ tag: head: - - meta - name: keywords - content: 操作系统,进程,进程通信方式,死锁,操作系统内存管理,块表,多级页表,虚拟内存,页面置换算法 + content: 操作系统面试题,虚拟内存详解,分页 vs 分段,页面置换算法,内存碎片,伙伴系统,TLB快表,页缺失,文件系统基础,磁盘调度算法,硬链接 vs 软链接 - - meta - name: description - content: 很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如进程管理、内存管理、虚拟内存等等。 + content: 最新操作系统高频面试题总结(下):虚拟内存映射、内存碎片/伙伴系统、TLB+页缺失处理、分页分段对比、页面置换算法详解、文件系统&磁盘调度,附图表+⭐️重点标注,一文掌握OS内存/文件考点,快速通关后端面试! --- ## 内存管理 @@ -211,7 +211,7 @@ MMU 将虚拟地址翻译为物理地址的主要机制有 3 种: #### 单级页表有什么问题?为什么需要多级页表? -以 32 位的环境为例,虚拟地址空间范围共有 2^32(4G)。假设 一个页的大小是 2^12(4KB),那页表项共有 4G / 4K = 2^20 个。每个页表项为一个地址,占用 4 字节,`2^20 * 2^2 / 1024 * 1024= 4MB`。也就是说一个程序啥都不干,页表大小就得占用 4M。 +以 32 位的环境为例,虚拟地址空间范围共有 2^32(4G)。假设 一个页的大小是 2^12(4KB),那页表项共有 4G / 4K = 2^20 个。每个页表项为一个地址,占用 4 字节,`(2^20 * 2^2) / (1024 * 1024)= 4MB`。也就是说一个程序啥都不干,页表大小就得占用 4M。 系统运行的应用程序多起来的话,页表的开销还是非常大的。而且,绝大部分应用程序可能只能用到页表中的几项,其他的白白浪费了。 @@ -379,7 +379,7 @@ LRU 算法是实际使用中应用的比较多,也被认为是最接近 OPT ### 提高文件系统性能的方式有哪些? -- **优化硬件**:使用高速硬件设备(如 SSD、NVMe)替代传统的机械硬盘,使用 RAID(Redundant Array of Inexpensive Disks)等技术提高磁盘性能。 +- **优化硬件**:使用高速硬件设备(如 SSD、NVMe)替代传统的机械硬盘,使用 RAID(Redundant Array of Independent Disks)等技术提高磁盘性能。 - **选择合适的文件系统选型**:不同的文件系统具有不同的特性,对于不同的应用场景选择合适的文件系统可以提高系统性能。 - **运用缓存**:访问磁盘的效率比较低,可以运用缓存来减少磁盘的访问次数。不过,需要注意缓存命中率,缓存命中率过低的话,效果太差。 - **避免磁盘过度使用**:注意磁盘的使用率,避免将磁盘用满,尽量留一些剩余空间,以免对文件系统的性能产生负面影响。 diff --git a/docs/cs-basics/operating-system/shell-intro.md b/docs/cs-basics/operating-system/shell-intro.md index 48066214c23..366af6ed54a 100644 --- a/docs/cs-basics/operating-system/shell-intro.md +++ b/docs/cs-basics/operating-system/shell-intro.md @@ -8,6 +8,9 @@ head: - - meta - name: description content: Shell 编程在我们的日常开发工作中非常实用,目前 Linux 系统下最流行的运维自动化语言就是 Shell 和 Python 了。这篇文章我会简单总结一下 Shell 编程基础知识,带你入门 Shell 编程! + - - meta + - name: keywords + content: Shell,脚本,命令,自动化,运维,Linux,基础语法 --- Shell 编程在我们的日常开发工作中非常实用,目前 Linux 系统下最流行的运维自动化语言就是 Shell 和 Python 了。 diff --git a/docs/database/basis.md b/docs/database/basis.md index 1df5d538fb8..e12d02a9b49 100644 --- a/docs/database/basis.md +++ b/docs/database/basis.md @@ -11,10 +11,78 @@ tag: ## 什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员? -- **数据库** : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。 -- **数据库管理系统** : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。 -- **数据库系统** : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。 -- **数据库管理员** : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。 +这四个概念描述了从数据本身到管理整个体系的不同层次,我们常用一个图书馆的例子来把它们串联起来理解。 + +- **数据库 (Database - DB):** 它就像是图书馆里,书架上存放的所有书籍和资料。从技术上讲,数据库就是按照一定数据模型组织、描述和储存起来的、可以被各种用户共享的结构化数据的集合。它就是我们最终要存取的核心——信息本身。 +- **数据库管理系统 (Database Management System - DBMS):** 它就像是整个图书馆的管理系统,包括图书的分类编目规则、借阅归还流程、安全检查系统等等。从技术上讲,DBMS 是一种大型软件,比如我们常用的 MySQL、Oracle、PostgreSQL 软件。它的核心职责是科学地组织和存储数据、高效地获取和维护数据;为我们屏蔽了底层文件操作的复杂性,提供了一套标准接口(如 SQL)来操纵数据,并负责并发控制、事务管理、权限控制等复杂问题。 +- **数据库系统 (Database System - DBS):** 它就是整个正常运转的图书馆。这是一个更大的概念,不仅包括书(DB)和管理系统(DBMS),还包括了硬件、应用和使用的人。 +- **数据库管理员 (Database Administrator - DBA ):** 他就是图书馆的馆长,负责整个数据库系统正常运行。他的职责非常广泛,包括数据库的设计、安装、监控、性能调优、备份与恢复、安全管理等等,确保整个系统的稳定、高效和安全。 + +DB 和 DBMS 我们通常会搞混,这里再简单提一下:**通常我们说“用 MySQL 数据库”,其实是用 MySQL(DBMS)来管理一个或多个数据库(DB)。** + +## DBMS 有哪些主要的功能 + +DBMS 通常提供四大核心功能: + +1. **数据定义:** 这是 DBMS 的基础。它提供了一套数据定义语言(Data Definition Language - DDL),让我们能够创建、修改和删除数据库中的各种对象。这不仅仅是定义表的结构(比如字段名、数据类型),还包括定义视图、索引、触发器、存储过程等。 +2. **数据操作:** 这是我们作为开发者日常使用最多的功能。它提供了一套数据操作语言(Data Manipulation Language - DML),核心就是我们熟悉的增、删、改、查(CRUD)操作。它让我们能够方便地对数据库中的数据进行操作和检索。 +3. **数据控制:** 这是保证数据正确、安全、可靠的关键。通常包含并发控制、事务管理、完整性约束、权限控制、安全性限制等功能。 +4. **数据库维护:** 这部分功能是为了保障数据库系统的长期稳定运行。它包括了数据的导入导出、数据库的备份与恢复、性能监控与分析、以及系统日志管理等。 + +## 你知道哪些类型的 DBMS? + +### 关系型数据库 + +除了我们最常用的关系型数据库(RDBMS),比如 MySQL(开源首选)、PostgreSQL(功能最全)、Oracle(企业级),它们基于严格的表结构和 SQL,非常适合结构化数据和需要事务保证的场景,例如银行交易、订单系统。 + +近年来,为了应对互联网应用带来的海量数据、高并发和多样化数据结构的需求,涌现出了一大批 NoSQL 和 NewSQL 数据库。 + +### NoSQL 数据库 + +它们的共同特点是为了极致的性能和水平扩展能力,在某些方面(通常是事务)做了妥协。 + +**1. 键值数据库,代表是 Redis。** + +- **特点:** 数据模型极其简单,就是一个巨大的 Map,通过 Key 来存取 Value。内存操作,性能极高。 +- **适用场景:** 非常适合做缓存、会话存储、计数器等对读写性能要求极高的场景。 + +**2. 文档数据库,代表是 MongoDB。** + +- **特点:** 它存储的是半结构化的文档(比如 JSON/BSON),结构灵活,不需要预先定义表结构。 +- **适用场景:** 特别适合那些数据结构多变、快速迭代的业务,比如用户画像、内容管理系统、日志存储等。 + +**3. 列式数据库,代表是 HBase, Cassandra。** + +- **特点:** 数据是按列族而不是按行来存储的。这使得它在对大量行进行少量列的读取时,性能极高。 +- **适用场景:** 专为海量数据存储和分析设计,非常适合做大数据分析、监控数据存储、推荐系统等需要高吞吐量写入和范围扫描的场景。 + +**4. 图形数据库,代表是 Neo4j。** + +- **特点:** 数据模型是节点(Nodes)和边(Edges),专门用来存储和查询实体之间的复杂关系。 +- **适用场景:** 在社交网络(好友关系)、推荐引擎(用户-商品关系)、知识图谱、欺诈检测(资金流动关系)等场景下,表现远超关系型数据库。 + +### NewSQL 数据库 + +由于 NoSQL 不支持事务,很多对于数据安全要求非常高的系统(比如财务系统、订单系统、交易系统)就不太适合使用了。不过,这类系统往往有存储大量数据的需求。 + +这些系统往往只能通过购买性能更强大的计算机,或者通过数据库中间件来提高存储能力。不过,前者的金钱成本太高,后者的开发成本太高。 + +于是,**NewSQL** 就来了! + +简单来说,NewSQL 就是:**分布式存储+SQL+事务** 。NewSQL 不仅具有 NoSQL 对海量数据的存储管理能力,还保持了传统数据库支持 ACID 和 SQL 等特性。因此,NewSQL 也可以称为 **分布式关系型数据库**。 + +NewSQL 数据库设计的一些目标: + +1. 横向扩展(Scale Out) : 通过增加机器的方式来提高系统的负载能力。与之类似的是 Scale Up(纵向扩展),升级硬件设备的方式来提高系统的负载能力。 +2. 强一致性(Strict Consistency):在任意时刻,所有节点中的数据是一样的。 +3. 高可用(High Availability):系统几乎可以一直提供服务。 +4. 支持标准 SQL(Structured Query Language) :PostgreSQL、MySQL、Oracle 等关系型数据库都支持 SQL 。 +5. 事务(ACID) : 原子性(Atomicity)、一致性(Consistency)、 隔离性(Isolation); 持久性(Durability)。 +6. 兼容主流关系型数据库 : 兼容 MySQL、Oracle、PostgreSQL 等常用关系型数据库。 +7. 云原生 (Cloud Native):可在公有云、私有云、混合云中实现部署工具化、自动化。 +8. HTAP(Hybrid Transactional/Analytical Processing) :支持 OLTP 和 OLAP 混合处理。 + +NewSQL 数据库代表:Google 的 F1/Spanner、阿里的 [OceanBase](https://open.oceanbase.com/)、PingCAP 的 [TiDB](https://pingcap.com/zh/product-community/) 。 ## 什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性? @@ -73,8 +141,20 @@ ER 图由下面 3 个要素组成: ## 主键和外键有什么区别? -- **主键(主码)**:主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。 -- **外键(外码)**:外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。 +从定义和属性上看,它们的区别是: + +- **主键 (Primary Key):** 它的核心作用是唯一标识表中的每一行数据。因此,主键列的值必须是唯一的 (Unique) 且不能为空 (Not Null)。一张表只能有一个主键。主键保证了实体完整性。 +- **外键 (Foreign Key):** 它的核心作用是建立并强制两张表之间的关联关系。一张表中的外键列,其值必须对应另一张表中某行的主键值(或者是一个 NULL 值)。因此,外键的值可以重复,也可以为空。一张表可以有多个外键,分别关联到不同的表。外键保证了引用完整性。 + +用一个简单的电商例子来说明:假设我们有两张表:`users` (用户表) 和 `orders` (订单表)。 + +- 在 `users` 表中,`user_id` 列是**主键**。每个用户的 `user_id` 都是独一无二的,我们用它来区分张三和李四。 +- 在 `orders` 表中,`order_id` 是它自己的**主键**。同时,它会有一个 `user_id` 列,这个列就是一个**外键**,它引用了 `users` 表的 `user_id` 主键。 + +这个外键约束就保证了: + +1. 你不能创建一个不属于任何已知用户的订单( `user_id` 在 `users` 表中不存在)。 +2. 你不能删除一个已经下了订单的用户(除非设置了级联删除等特殊规则)。 ## 为什么不推荐使用外键与级联? @@ -87,7 +167,7 @@ ER 图由下面 3 个要素组成: 为什么不要用外键呢?大部分人可能会这样回答: 1. **增加了复杂性:** a. 每次做 DELETE 或者 UPDATE 都必须考虑外键约束,会导致开发的时候很痛苦, 测试数据极为不方便; b. 外键的主从关系是定的,假如哪天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。 -2. **增加了额外工作**:数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗数据库资源。如果在应用层面去维护的话,可以减小数据库压力; +2. **增加了额外工作**:数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的一致性和正确性,这样会不得不消耗数据库资源。如果在应用层面去维护的话,可以减小数据库压力; 3. **对分库分表不友好**:因为分库分表下外键是无法生效的。 4. …… diff --git a/docs/database/character-set.md b/docs/database/character-set.md index e462a5c97e3..9a0969a2770 100644 --- a/docs/database/character-set.md +++ b/docs/database/character-set.md @@ -3,6 +3,13 @@ title: 字符集详解 category: 数据库 tag: - 数据库基础 +head: + - - meta + - name: keywords + content: 字符集,编码,UTF-8,UTF-16,GBK,utf8mb4,emoji,存储与传输 + - - meta + - name: description + content: 从编码与字符集原理入手,解释 utf8 与 utf8mb4 差异与 emoji 存储问题,指导数据库与应用的正确配置。 --- MySQL 字符编码集中有两套 UTF-8 编码实现:**`utf8`** 和 **`utf8mb4`**。 diff --git a/docs/database/elasticsearch/elasticsearch-questions-01.md b/docs/database/elasticsearch/elasticsearch-questions-01.md index fe6daa6926c..4b1599bea3a 100644 --- a/docs/database/elasticsearch/elasticsearch-questions-01.md +++ b/docs/database/elasticsearch/elasticsearch-questions-01.md @@ -4,6 +4,13 @@ category: 数据库 tag: - NoSQL - Elasticsearch +head: + - - meta + - name: keywords + content: Elasticsearch 面试,索引,分片,倒排,查询,聚合,调优 + - - meta + - name: description + content: 收录 Elasticsearch 高频面试题与实践要点,围绕索引/分片/倒排与聚合查询,形成系统复习清单。 --- **Elasticsearch** 相关的面试题为我的[知识星球](../../about-the-author/zhishixingqiu-two-years.md)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](../../zhuanlan/java-mian-shi-zhi-bei.md)中。 @@ -11,5 +18,3 @@ tag: ![](https://oss.javaguide.cn/javamianshizhibei/elasticsearch-questions.png) - - diff --git a/docs/database/mongodb/mongodb-questions-01.md b/docs/database/mongodb/mongodb-questions-01.md index 81b7db98890..2799ff984f2 100644 --- a/docs/database/mongodb/mongodb-questions-01.md +++ b/docs/database/mongodb/mongodb-questions-01.md @@ -4,6 +4,13 @@ category: 数据库 tag: - NoSQL - MongoDB +head: + - - meta + - name: keywords + content: MongoDB 面试,文档存储,无模式,副本集,分片,索引,一致性 + - - meta + - name: description + content: 汇总 MongoDB 基础与架构高频题,涵盖文档模型、索引、副本集与分片,强调高可用与一致性实践。 --- > 少部分内容参考了 MongoDB 官方文档的描述,在此说明一下。 diff --git a/docs/database/mongodb/mongodb-questions-02.md b/docs/database/mongodb/mongodb-questions-02.md index dcd90d72c4d..f652801fc39 100644 --- a/docs/database/mongodb/mongodb-questions-02.md +++ b/docs/database/mongodb/mongodb-questions-02.md @@ -4,6 +4,13 @@ category: 数据库 tag: - NoSQL - MongoDB +head: + - - meta + - name: keywords + content: MongoDB 索引,复合索引,多键索引,文本索引,地理索引,查询优化 + - - meta + - name: description + content: 讲解 MongoDB 常见索引类型与适用场景,结合查询优化与写入开销权衡,提升检索性能与稳定性。 --- ## MongoDB 索引 diff --git a/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md b/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md index cb30376687b..ec06b4d60e7 100644 --- a/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md +++ b/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md @@ -3,6 +3,13 @@ title: 一千行 MySQL 学习笔记 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: MySQL 笔记,调优,索引,事务,工具,经验总结,实践 + - - meta + - name: description + content: 整理 MySQL 学习与实践的千行笔记,凝练调优思路、索引与事务要点及工具使用,便于快速查阅与复盘。 --- > 原文地址: ,JavaGuide 对本文进行了简答排版,新增了目录。 diff --git a/docs/database/mysql/how-sql-executed-in-mysql.md b/docs/database/mysql/how-sql-executed-in-mysql.md index 0b01d9a4da3..5be1dea1667 100644 --- a/docs/database/mysql/how-sql-executed-in-mysql.md +++ b/docs/database/mysql/how-sql-executed-in-mysql.md @@ -3,6 +3,13 @@ title: SQL语句在MySQL中的执行过程 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: MySQL 执行流程,解析器,优化器,执行器,缓冲池,日志,架构 + - - meta + - name: description + content: 拆解 SQL 在 MySQL 的执行路径,从解析优化到执行与缓存,结合存储引擎交互,构建完整的运行时视角。 --- > 本文来自[木木匠](https://github.com/kinglaw1204)投稿。 diff --git a/docs/database/mysql/images/ACID.drawio b/docs/database/mysql/images/ACID.drawio deleted file mode 100644 index e8805b8d958..00000000000 --- a/docs/database/mysql/images/ACID.drawio +++ /dev/null @@ -1 +0,0 @@ -7Zhdb5swFIZ/jS9b8R24BAJdp1Wamotply6Yj9VgZkyT7tfPBjvAIFK7LcqqNZHAfo99bJ/zxLIDzLA63FDYFHckRRgYWnoA5hYYhq4ZHn8J5XlQPM0chJyWqWw0CrvyB1I9pdqVKWpnDRkhmJXNXExIXaOEzTRIKdnPm2UEz0dtYI4Wwi6BeKl+KVNWDKprbEb9AyrzQo2sO3LBDzB5zCnpajkeMMzYiePYHcwVVL7kQtsCpmQ/kcwImCElhA2l6hAiLGKrwjb0i09Yj/OmqGYv6fD5/ttDl3wtnor7T9S9tRq/C6/swcsTxB1Sy+gny55VgPolIuFEB2awL0qGdg1MhHXPkeBawSoszVmJcUgwoX1f0wp9y9twvWWUPCJlqUmNhKgioonKI2JJISs5hm0ryxmp2cTl8JF6DKsSC/A+IhZQWNYtn/sdqYm070hH+5kWjHGeDNv0+YOHSDxEg/Y6JyTHCDZle52Qqjckbd80zgbvvDj1bxuBHGGZAZmUJ0QZOkwkmZEbRCrEKHepKasp6Xj+pb4fWTRtqRUTDg1LilDynx99jwzwgsTgFUjoK0g4mMmIzthwvndEGa7a/qfN46vpXnMYjbyUi7cPohgEIXBdENnAjYAXi4K/BZ4GIge4GvA3qo2nxgRDnpSTv0pnZovvKTpPcfdyat8+nZs5nYa2pFM3Vuh0zgWncSY4wwmcFghcQWPEnxsQWJeBM4XIzZI/3zrfPISm9a9BaJ4JwtsJhB7wfOBx9jbAd0AQjBCeYm+Q2wbWvz+DNcIHj+fYfrPMSN4JX9lmvUsTbp2J8O2EcM4zL+j9fusB177QGcBN0DuEa9vsxSF0FhD64e12kX2+PjZP8fpxbpJ3KUFc5jWvJjxqiOuBiFbJr4a+NFRlmophVpkaqVMMyMut8Z+dEo8EqDuMvSTHW7vCvB4cXh0vzL1t8q+EGf0E \ No newline at end of file diff --git a/docs/database/mysql/images/AID-C.drawio b/docs/database/mysql/images/AID-C.drawio deleted file mode 100644 index 4ac724c8404..00000000000 --- a/docs/database/mysql/images/AID-C.drawio +++ /dev/null @@ -1 +0,0 @@ -5ZjZcpswFIafRpfJAGK9BBvStM1Fm+mS3skglkYgCvKWp68EwoCNM0k6HmdqZwZL/znazvmkIAM4yzc3FSrTOxphAjQl2gA4B5qmKprDv4SybRVHga2QVFkknXrhPnvCXUupLrMI1yNHRilhWTkWQ1oUOGQjDVUVXY/dYkrGo5YowQfCfYjIofoji1jaqrZm9foHnCVpN7JqygUvUPiYVHRZyPGABgMzCAK7Neeo60sutE5RRNcDCfoAzipKWVvKNzNMRGy7sLXtgiPW3bwrXLAXNSBfy+/et6cCXX26ib/o33+pqyvZS822XTxwxMMjq7RiKU1ogYjfq16zZix6VXit9/lMaclFlYu/MWNbmWu0ZJRLKcuJtOJNxn6K5teGrD0MLPON7LmpbLtKwartoJGoPgxtfbOm1rWrHzELU1mJacEClGdEWD9i5lUoK2q+/jtaUGm/p8sqFPNOGeMIagZ0+YNHVTyEQ32dUJoQjMqsvg5p3hjCunEN4rZ3Xhz2b2jecIQ2LqpYcM0q+rgDjzPhHaa1y1E3sWO51OTuQVWC2TN+eusnEj0YQEJzg2mOeQC5Q4UJYtlqvE+Q3G7Jzq9Hjhckda8gUM56hcgSdxtpD8keOBGydZoxfF+iJhZrfiiN4dpts4PsJwTVdUdCRsiMElo1I0B95uqOtcvHwGI2H5m5gd5+3gVTR4lZ4YrhzbM5llYNykNNHuKaIg+tdX8kQkNq6eA41HTlRFzACS5MwmRER4CYf5a0M1zVze7i8VVUp9z0Rl5KxLcL/AB4M2DbwDeA7QMnEAV3DhwF+CawFeBanY/TjQnaPHWd/Buie/DFhvh7E3wvhP2/QNTWx4hah4iq2gSi5qkI1U9E6GxAqA48WyDp86cFPP08hEYI23E4SWho40V8WSRCY0yioZ6bRONEJN4OSHSA4wKHA2gB1wSe15N4DMBWrktUvH0GU5i3PZ7iII5jLZzEPDIXpmFeGubjdwJonhtz80SYzweYc6h5QW1OXgfYxpleCewQT5O4sA3dUC6LRB2+t3/91gGJ7u38gAC+PLZ3ORmls6AF3su9lBDJkoJXQx40zHVPBCsLEXGlIc+iqLmMT3E1vqAPrpzahb0zQmsPHO0QHGfqVvN6bni1/ymnsQ1+L4P+Xw== \ No newline at end of file diff --git a/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.drawio b/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.drawio deleted file mode 100644 index 6e4e61ba50c..00000000000 --- a/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.drawio +++ /dev/null @@ -1 +0,0 @@ -7Vpbk6I4FP41eWxLQG6P4KV3e2d2ZrZramYfIwZINxIWYqv96zeBIGKi7Uwp2jOWVZqchJPL+c53TiLAGM5X9znM4o9khhKg92crYIyArpuuzr65YF0JbMOsBFGOZ5VIawSP+BUJYV9IF3iGilZHSkhCcdYWBiRNUUBbMpjnZNnuFpKkPWoGIyQJHgOYyNJveEbjSurodiP/A+EorkfWLLdqmcLgOcrJIhXjAd2YWJPJxKma57DWJRZaxHBGllsiYwyMYU4IrUrz1RAlfGvrbauem+xp3cw7Ryk95oE//x4YTj8YYPr11f7y9OmfuWPdibUUdF3vB5qx7RFVktOYRCSFybiR+uWaEdfaZ7WmzwdCMibUmPAJUboWtoYLSpgopvNEtLIJ5+vv4vmy8i+v9My6OlptN47WohaSlAqlminqEzjHCe/wgKifQ5wWbDkfSUrq/mSRB/yJmFKGKN00PPbFNol/8Q5FLyIkShDMcNELyLxsCIqy6ySstLPitn5T98UIsg2EWYp62H0bX0Md5hGiB/oNqn7cKlsDCAvfIzJHbIdYhxwlkOKXNqih8I1o06/BBysIiPwAXITeF5gsUI36XfwkCXNdjpNljCl6zGC5D0tGHm0UwCKr/DnEK44mP8RJMiQJyUtFRhgiKwiYvKA5eUZ1S0pS9L6w8IJyilYHrSdaN1whuFSzRH3ZMFMtirdIqZad3N7Gzd7ntLejXZm96/nc4kHX8WBwZDwwryseuBJBeHWQ2MHQBzhliWObERIcpawcsN1CzNd97jmYpWaeaJjj2ayCGCrwK5yW+rjlM4KZpbhy0wfmSIGFhA/nb1K0LZ4RSdp1wuWgV0rEssmKxda0MksV4dz1ezzKtEmnqh0NGKH8MzdBo9lU6awfJ2FYMFTvom0zv58H4EDGH/vRZRprSEp7O1RJkSnUf7PIZFg7kcmWI5OtiEyDE0SmEcqGeP0AA/8B+/lf3/wv0+fa0N0Gpl3TKrn35/jelPleuW69I3pXDm7K6d/YBK4P3DEYW8BnBe8SZH8kucumO5pB20bdj8iDTGu3SdE4CdFaKp3nJ1oZCpxoNfn+4Ea0P0S05u6Rr0OiVRraVvj8APgecHzu/I4HPE3O8Nhqadu0ahNu2VuIjucFFZbaFP9LIkQzdhFiSghRHRKNcyHEOQIh8j3BDSGdIUR3ukOIMjSqT4Dv5BbhhEle7QZvnuo1s6M07+A0b9d83Vzr6ooY3+01n/OeHfSKMfE2IyjOfeqOtpKHLkYRt/z/TPm/uxu7L5z/a4oDwC0YnO8/n4sHg80S2rc8LIf0hmViPwSuXUomwGeFSSnxHuALvOfvbgDdSnhmP81bILH+W/A3G0qj3BWlFZkl+pqZrcqNrNtZKSp/xzbwR8DVeMHzgetuhnpiQ5WvifSC9ErPFXtupA4AecsL/PJT6qSMy0kqlBbPiAYxaO4i3z349V22G8jg1xwF+rVzHVWMS2RCpzly7L8vPiK/MDrKJg5N8vavjexeihtqCQZ73cuyO7tMZNXm1bXqkrl5PdAY/w8= \ No newline at end of file diff --git a/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.png b/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.png deleted file mode 100644 index db90c6ea22c..00000000000 Binary files a/docs/database/mysql/images/concurrency-consistency-issues-dirty-reading.png and /dev/null differ diff --git a/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.drawio b/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.drawio deleted file mode 100644 index 68c79b9da73..00000000000 --- a/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.drawio +++ /dev/null @@ -1 +0,0 @@ -7VrbcqM4EP0aPcZl7vjR+DI7U3PZ3dTW7j4qWIASgRiQYztfvxJIXAzOkKnx4JqlUuVILalB3UenW10AYxUf32UwjT7RHSJAn++OwFgDXbfnC/4rBKdS4BhaKQgzvCtFDcE9fkFSOJfSPd6hvDWRUUoYTttCnyYJ8llLBrOMHtrTAkraT01hiDqCex+SrvRvvGNRKXV1p5b/hnAYqSdrttzwA/SfwozuE/k8oBtbe7vduuVwDJUuudE8gjt6aIiMDTBWGaWsbMXHFSLCtMps5brthdHqvTOUsCEL3n82DXfum5j99eL88fjlz9i17+RecnZS9kA7bh7ZpRmLaEgTSDa11Cv2jITWOe/Vcz5SmnKhxoWPiLGT9DXcM8pFEYuJHOUvnJ3+keuLzr+iM7NUd31sDq5PshfQhEmlmiX7WxhjIiZ8QMzLIE5yvp1PNKFqPt1nvlgRMcYRpVvGkv9wI4kfMSGfhZSGBMEU5zOfxsWAnxdTt0GpnTeb+i3dk08orSdMdtEpUpSr97jkCYV9mIWIvTLPrKDDTySiMeIW4usyRCDDz+33gPJshNW8Gh+8ISHyBrhIvc+Q7JFC/Tl+COFHV+DkEGGG7lNYbPvAyaONApin5XkO8FGgyQswIStKaFYoMoIA2b7P5TnL6BNSIwlN0A1j4RllDB1fR0PXe3JBxRWSSzVb9g81MylR1CAlJfvh/jYmf1/T37Z7Y/5WQXmKB6PHA3NgPLBGjQeLDkEsVZA4w9BH+MATxzYjEBwmvO1z4yB+1j1xmDBPzZZyIMa7XQkxlOMX+FDoE55PKeaeEsotD1jrHiwQ8TivStEaPCOTtNuBSw+FyGRX7rjOIZtAeuUAXyScu/lMRJk26ZS9wYCRyn8XLqg1W3061XIaBDkH8Tnaqvf7fgCaXfzxf3qXxmqS0r4dqjqRKdD/Z5HJsM8ik9ONTE5PZDKvFpnsKTLdSGSyBkYmY8zIZPUSg9a9707E8CZisM6vKGMTg9O9omxM4C2B64GNBdwlWGrdjIQbgLVd2+/Chr+laHjS0oelNiP9kgjRjHOEWIMuNca1EOIOQAifYBOBiIeMt0JWmWLCzBiY0d2RMdNDGVO6MVJhVB+Yb2jjlkb1Ls1MtbLr1Ub1nsTj59bKjIkiboUihhbLtFGrZQrBU7XsVqplF66odbVMU4Wt7y2PXb8Epk01sOtEHFNZtkpKx66BuVPEuZWIM7QIpjmjJqVTGexKZTD3/MI6Njf01MGm68ePc7hzXqEY+/qhttAqa1nAtcByVdS3VmDhFJIt8HhjW0iWH+AzfCc+uWsWvBogsb/uxQdphVPu8sKL3BNzzUiPhSHVeFUl2zjAW4OFJhpLDywW1aMe+aOKr/tmftLB4m0U0y7kvT1ANmS/cQq84q/QyTiX00QqzZ8Q8yNFlb8C+PXFGfjNLvg1twf92tvRz7v1N5Fljlx/d2ps/gM= \ No newline at end of file diff --git a/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.png b/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.png deleted file mode 100644 index 1718e7ce0dc..00000000000 Binary files a/docs/database/mysql/images/concurrency-consistency-issues-missing-modifications.png and /dev/null differ diff --git a/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.drawio b/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.drawio deleted file mode 100644 index 350470274c5..00000000000 --- a/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.drawio +++ /dev/null @@ -1 +0,0 @@ -7Vptb+I4EP41lvZOKsIJeftIgHRV7UordU/78uXkBudl68TZxBS4X3+2YxNCQkvvSqG7CAnsx/bYmXk8M3YA5iRbXZeoSD7SOSbAGM5XwJwCw4BwaPMfgaxrxDGtGojLdK46NcBt+g9W4FChi3SOq1ZHRilhadEGQ5rnOGQtDJUlXba7RZS0Zy1QjDvAbYhIF/2SzllSo67hNPh7nMaJnhnaXt1yh8L7uKSLXM0HDDOwgyBw6+YMaVnqQasEzelyCzJnwJyUlLK6lK0mmAjdarXV44I9rZt1lzhnhwy4uV0Ff/3tf86+uHH86frm+3v680pJeUBkofQBZjZwA+DxggVcC4wtgXgW8IeyaQrGM/VAbK2ViOdcp6pKS5bQmOaIzBrUl4rCYilDXmv6fKC04CDk4A/M2FoRBC0Y5VDCMqJa+VOW669qvKx8E5WBpavT1XbjdK1qBN1h4m9sNaGElnLR2lqmH9GcqXnhSNUDlKVEyLjBzC9Rmlf8iT/SnOr+dFGGYkTCGGeqYZlj/sWVL75Eh2oQUxoTjIq0GoQ0kw1hJbsGUS2dF7flW4avZqgVLLS619gKqvQ69lnYUHsKlTFmj/QzN5TkWx3TDHMl8nFqn18NB+bIVrJKTBBLH9prQ2ofxpuxG3GfaMpX3XShUVTxtWyRlRe2Zm0gSeFn0Nno0rlDVUK4axGUXCYpw7cFkupbcu/WJhyqitrfROlKENePUkK26BNF2A5DjlespPdYt+Q0xzucss6IUw+4ZHj1OKv2sgBaypcpXw9tVV82nlNDyZbT1FgfR1oUeK69zYu9j2lvxzwze496wtUI+GPg+jJcjcEYwg4FuAJY29b9Jtyyt4IQSeOcV0OuMMxxX6gz5cnDWDVk6XxO9pGrHfN+SYbYOwRxrIMIYh6LINYBBOn6iAtBXosghntigthvKXc9V0a8SGLqHpiYwj0EOzgL/V98cXoyDJsIb1EVKG8xyf65EGc5aYWrSpqNq34IR8VKak6381IsfkNOK/buzz+0QL6+Wmbd3EvUD+I0s5O3HOyCSsxXhe6kPEGvQuTlUmGWD6zp2z4s9XgpdWWgnnhzQG6xdb+TeOwsBA3Xbvu1cz8ZuZdM+TVPRoZz4kzZe2tx7hwo8CJxTR9Ang5s1ikDm75/vUS23ymyeU9GNgeOWp5MXxH/19CmF270Sj1+4IPdK+58kfHC5l6+4WHjAOHTUbAT9CLjNwt65m7Qc7tBz+kJeqNjBT1NskuW8zr3gSfPcmDPBbB6bTWRFz8T4DkSCYDPC4FExjfoAV2L9446QN2VB8U7c0+8AzMH+FPgQVEY+8DzNlP94FPJV5yDMO9w8TzunfaExh4im6q+tQt8+ZEyGY8PNFdCq3vMwkS7yl+B/ObuVdaoS37o9rAfHo39o1Mk+edizJdJ1q1Dk3XjpMl6915bJTHdtx2XJOZZ29rq3FAfLYnh1ebvHnV+2/ynxpz9Cw== \ No newline at end of file diff --git a/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.png b/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.png deleted file mode 100644 index 4bea3c32953..00000000000 Binary files a/docs/database/mysql/images/concurrency-consistency-issues-phantom-read.png and /dev/null differ diff --git a/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.drawio b/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.drawio deleted file mode 100644 index 212d68f7f92..00000000000 --- a/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.drawio +++ /dev/null @@ -1 +0,0 @@ -7Vptc5s4EP41+hiPQYDxR/BLern27nq5m14/3cggQImMKMiOnV9fCYQBQxy3jR3SejyDpZVYIe2jfXYFAE6Wm+sUJdEH5mMK9KG/AXAKdH1kQHGVgq0SQLMQhCnxC5FWCW7JI1bCoZKuiI+zRkfOGOUkaQo9FsfY4w0ZSlP20OwWMNocNUEhbgluPUTb0k/E51EhtfVRJX+HSRiVI2vWuGhZIO8+TNkqVuMBHc6t+XxuF81LVOpSE80i5LOHmgjOAJykjPGitNxMMJVLWy5bcd/8idbdc6c45sfc8NsfBrSHnkH4v4+jj3d//r20rSs1l4xvy/XAvlgeVWUpj1jIYkRnldTN54yl1qGoVX3eM5YIoSaEd5jzrbI1WnEmRBFfUtUqHjjd/qfuzyufZWVgltXppt443apawGKulGqmqs/RklDZ4QZzN0UkzsR0PrCYlf3ZKvXkHRHnAlG6CR1xEYskL7JDNggZCylGCckGHlvmDV6Wd50HhXZRrOs3dVeN0LaBMktWDvvUwpdQR2mI+YF+RtFPWqU2gLLwNWZLLFZIdEgxRZysm6BGam+Eu34VPkRBQeQb4KL0rhFd4RL1+/ihVGxdiZOHiHB8m6B8HR6E82iiAGVJsZ8DspFocgNC6YRRluaKYBBgy/OEPOMpu8dlS8xi/LawsMYpx5uD1lOtO1+hfKlmqfpD5ZlKUVRzSqXsxe0NL/Y+pb1trWf2Lp/nwgfn5gPjSD4w+8UH45aDcEqS2MPQe7QQgWPTI1ASxqLsidXCYq+7cucQEZo5qmFJfL+AGM7II1rk+qTlE0aEpaRy0wXmtAMLVA7n7kK0mp9RQVo/4XJwV7Ycyy4qVkvTiCy7HM7VcCBZpul0itrRgFHK/5ImqDSbXTrL21kQZALV+2jbPd/3A9Bo40/86W03Vjkp7XmqajFToP9izAStPWYatZlp1MFMxgsw05c7///P0EE4SG+tNWf/DBm/0joij/4y05vwPnXgwh8iL/NI8oK9Ii+z03do7ZT44ju+yXeYr+g7Og09amcxMwO4DrBdMDOB7QBHawctYra8adpuE9bsrUTHxzVdWGo6rZ8SIRrcR4h5VN4DT4UQ+wiEtAnogpCzIUS3z4eQKU4mZHuDPPeGuOnvn9yPi/snkpqehh/7kPh+Zi9R/yy1az1LTPX2jr6cXJ3upFLv4PhTnVx15wfGW9qgv1h+oHUkCN1W7FeGoHWnCJfjhZc+XtDtV04RtI4c4cIXp3vTcU6+6DT4bgr1kF9E+iZwJnnsPwHjUS6ZA1cU5rnEuUFrdC2/WAC6RWXwv0gbILG+rOT7/NwoV1luRWGJoQaTTb6QZbsohfn/bATcKRhrsuC4YDzeDXUnhso/jhh4cU9TjydorAPIUNVru8DNf7lOLnw5i5XS7B5zLypd5c8Afn28B36jDX7N7kC/drJoybxES32Ilg4GQc/nXKPXipYOPvflQPWF/Ydlni1aEtXqi7TiJV311R+cfQU= \ No newline at end of file diff --git a/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.png b/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.png deleted file mode 100644 index c734138cf8e..00000000000 Binary files a/docs/database/mysql/images/concurrency-consistency-issues-unrepeatable-read.png and /dev/null differ diff --git "a/docs/database/mysql/images/\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" "b/docs/database/mysql/images/\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" deleted file mode 100644 index 4f649c952cc..00000000000 --- "a/docs/database/mysql/images/\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" +++ /dev/null @@ -1 +0,0 @@ -7XtZd6u4tu6vyRj3PtQedAb8KNEZMGBs09hvmEZgYzCNafzrj5Q4WWsl2VW176m656FO1soApsSUNNtvCuWFla6T1ka33KqTtHxhqGR6YeUXhqEpZokvhDK/UZYU+0ZAbZE8O/0g7IpH+v7mk3ovkrT7pWNf12Vf3H4lxnVVpXH/Cy1q23r8tVtWl7+OeotQ+oWwi6PyKzUokj5/o4qM8IO+SguUv49M888Fn6L4gtr6Xj3He2FYlVdVVXxrvkbvvJ4L7fIoqcefSKzywkptXfdvd9dJSksi23exvb2n/pvWj3m3adX/mRe0zJgy2oFeyjRhNdZm84C/MfwbmyEq7+n7Ol5n28/vEsITv5HbeC6LKknbFxaOedGnu1sUE/qIDQPT8v5a4ica356IVNJkffogfMjKufeYS/qkZ3XVP02CXuDn7pL2MREbRRqLspTqsm5fp8GqqsJLEqZ/XfhTFkPa9un0E+kpCC2tr2nfzrjLs5VfPtX7NFqWFv+1eKOMP4yAZp+qy382gMWzY/Q0PPTB/Yfw8c1T/v+BLoRvVMGXPRHLLap+0Qnf3InZwPhNPAA3tuj0fxZ41Xhs6sf1/77KiyJy/i2LrkU5v/W+pm1bjCm2UazP176YaXQlenyyvvVkxLQtss8tb/3xsltURN+/3BdX7NMMVaUjmVp9JfP/jkvXd3WFnvevg/2Y73O5ZLpV3V6j8q2tTPs+bX/DMokL/OqXdmwC/W/EUIl1kEbqNv3U0rdR1WW4//ubxBhJ61i3ya9cf30xSeO6jfqirj6/mRTdrYyeki2qN/t+XUZZR/3n3j984bdP6mM4/k0SDLGx1xtOfNMg/yE1Hj2vr5ZBBPWtZbxJ8NW1CHt68VzLF0bK4gVSL6L6ovAvS/FFVF4U8QXwLyL9oggvEL4slz/1wRTwAhjSB8ovQHq94Uh/PAZFUcQWcG8R/2ff54id4W2av04dk9/s+p38KewQof9xqPndiPEWjp9h5sn5m7j5H4cPnOR+DR889TV4LL8JHu8B5S+PHe/Z9Cf5pQnOc8/Huu3zGtVVVCo/qLB9C9NP4f3os67r21O8Z+xs8zNCR/e+/h3hd31bXz5yKPN7gZpM7Xfl3KYl9rTh1xT9ndCer27q4tUP3vUjfNLP8lPM7up7G6fPt37OmZ8YceIfMOqjFqX9F0avOvxYz39Drdwfp+cfWqT/2F0+eUiWZUwcfyjvp5aEP/EL/rOO/wLfEahPqVf8JvHS3/jO4m/zncVXIX+NeCTJwY8o932s+tk1fhHoM/r/LP0nKSoLVBFwhSX6Cq2IJAsMT8Gz4Vokyau3fqfZXz34d9HUX6K65TtKeiqPo79RHrX4qjzmb1PeNwD2a0pjqN/+4ap7DxwfgexrxhK/cbq/T2/i/yasn+uRL3nmX38u03xhteD+kNW/yX5/VdL6sM4/UVMW19fy/MN11tEpLTd1V7zhbPlU932NoTosSQP8gM6/5jD884379cQoYNTd3rYNsmIipgNfhwTvVOqdgu+TqI8wXH57ZNTba2FS+NDZjpSpoRrgH3vn5YqH8J1OHiX874Cvsry5rtb4RpW8UnH9LRfemXiIls5u2mvZZGB+INH0XNkXV8q97IrGlmpmU2/2W/6+M8eWVm6Zv9UbrfBNVBtZfM03uW6Aws435GXj6IjobpfsmXrwi4Jets7JW8zHPkjTZdI9Hsub8ChLIGid34HsAK8jKC/8FWetccHJ9rg6IWs4dMPILy+9JLdTH8Pbwcbtlih0sAJa+kjwiuF+7egAjnCNVBlbjnrAvzGcOaWIBIuaLo2CHvlmPu8zjhhIc4ndw21qz6ZGBuO3QEZaqM0X3GZrUwu2W2AjfaM1HqbA/dUS4F02OCQhiUU2ZQOvyC9nRaTk/JpgDkoB1uBI5rXUmSTc7kRXB5vRrmRd4qTtyBY5ozLKGazgIDbrldsDYVT49bhmMVEGKSMZlwgOyPG17tTp2qiBhV30LZkRcJsBbWLXB2v8tEFIrlGytrS1q/qSextRjZRHPgPbFIhjMOoeT4S9GzstKPLo7hzSXPX0oFDWO2hvJrZh1CAfhd3d5lbLc2Az6syZwLiDfGT9fRSW6R3exkWe5HUtudAIK85aK3qnuMYNcy5GK+dPg8S4eh+GkQEv0HTPrXU58Erpz2fHPc/+cdaBuVvcYivmfblm88ttpx+GlVvbA+aRbeid1QAy2eoeHGdP2wuTvV1m80Oc0+0hlW+WWE2aZ+l0MS7AXd/R1Q2AyGTqjkXSqFZSgJYhWG8DWr2IAHvHIWJW5ggjE/OvuwrBsWzlYJw80G59Wu3EM7DuYBXLwRzjLv50a4hyHU6SZ2Iv7NRmJ1qQ03GDQAUOgTwPZoXTkJshs5ILefkadxgYUEsQQGJm614JcKZWI/wbYn+BYRFt8X1vr/DDdWNXzYLCSlhuV8YydSFSMjB0pv64+SIMOGlTHeiWWgERGDgswEU8lO7JiQbxdH+MSw44IIpXpCU+t/W+T6HL2Xxinw5ghLcmZWn5sc0SAdGyt7bAKC3vGWEDqsGlVp6P3V3bJBtZq+7IH4E1k0khGhnoomIXZwfXU3uQHRcJXgDMedGupe2FHwAEIEetly8Oq8bOxn7SKEwwBV1NwQ5HDhjUZFZsrvu67xnLip4aftXODDJWe3MECjIVaRmtSpDWtA9x3DMVEZkrECDFmjwmX7iKBbMjaEfgAv1g0nrGiFA2wHJfMadAw3ljWAfyZQlwBAuk2l3RdXqAvJx5xazmMpaljE6epOlNq1Irjasa5M8nVwE69LCHwzscWTx6+xDUm0xzm1A9wzlFAZm8V8s8tcETzKt6ezTnfKNL+ajWULZCv5jhOd6zrjlhYegKaGAWIu40g42/SBoGC0ltpUla7uNWuXsD3WrSIrwbD0HZHULtUuyw+ofSXoV0Omruno6Vsz8JSJ3NHXYe9UJV/EI8nfnM3fG7yT4y3dJHlSOrLljkO0na9jWZv3zdBki7FgaJwqvh1nrHVqcqYWn6YKrReR0wb2P1/mzTmY/HelAX5YwFswDa1ZokKPRmOay3WdbRI63oYGFYvruze5DUUg/CmugceLDQuxPvC2lwfQse5ilvdA/MUtyiC1m3DkQkv143M449vLRxw4usKO7bYKsLeLtewd2dlGalrh/Yt4bF1UKLRAOFlDUuimZ3b0KrVmb0fsWOAj0KXC0rP7W9oZTDac1Qx9V6p3OsTZ6Pj9IWRA+EwJG3Oy1COE4EaL5IPtrVsu9GjbyZITiW4BpLIrgxrCmx1W7o6XWXjlxwUWenw2t2fvRX3WMjG+4xkk3XCKAy+h5M80e85hTJNLcNgj6IaskHvnVIwdqYwxCFRrehPZI6wc7zna25kA66TjL5Xw96GfoT6F3wX0Gv+BX0vtP+ctD7DoT+kcjILC4fyCg5CoDebjVtNY8jif2g23ruDfqWkdW6oQB/O+nugYAB1Oy2KrQiXdl6W/dhgmI8AMU49sDbKopCKRMqr+No6UCTJX8zzzR0Gj8mScRrwmFYdg9H2JwojCzUdhBS5siH1b3q2bS6C34hURsQHgDOcJ1kjo8g5exRIc6841YbghGgLhyi0heSiNIJgFFlZKXrxZQFsbwdYaiRkeRG8RROCkHOrbw+oaQBPLidcU7s3XihvYt24Z1mgCoXiOJxtg5C2t/qxWDowBnXmVm4ZMce8k0/GjVOyEomnd1bN1hCLJl0pB45vQbGuD6poguMakdQmnbPA4a3phgnwhj69GkWNu02nDAE2uB0aEMTrEeYqnHJzIK1RTK6XK7oxh3l9SgxbbA6FCJFcBUMzPvOZUW7VTOjR/eH4squwna4NoAuQY9pwZ5X94cD/Bw/kqR9AKkpUYdReFQcSK/TPpfPZD9XUh8q8OUuJHFW0M4k3srRI6djHdclGloh3hahgxa7++3AeJvNPDF8nMLQbU+JFt8lhOfOLg7Uuuf7cDyIG0eySyYqkAkqeSBcjRkGkgv6zbiTdiu+HWRGVkUm9S4UWLvG7n50d2XhtLKJhTs4hVsAujle5CJQN91SR1uBK6FLMMaZWASN4QRe12ExRvutoVT7XGhu/UbKHjdDouN53vsYN5m3rRsjVo/8sAcxsvX0FD/SlS5rdvOgEicG5eZYE9xzugJ5xxyioHLGFPVKfOoe8apeK7b5YBPHlcpN0hwG/wA8bTtsGNhylnJ+3VlS84gzB2DF8mZ7W6wFIusTUKQTbhJLwzAJrEKoizxpP90jWqawo9mxsCxTXCXggsK1zS4FGVJjeXq0LMakQH7cU26xcUXAg40Tt9xwTB6Lue5DkjBO12IcuOkKuqk152BgzHUZApWzKmcvbtglk1adbDxiBsMzuJV2F6cTWAEsYol3xymIwZgYK0oUjbtkN/eTWKEVNhLOywKfnipUizBNvEfYpbWNbAaSDbSuS/ZSm46wkuVj9OAUEYSQ5dQ7aKo7szZrHKFheUbrmerG3ALmoF3ZDTb/ZrqoR5pa0xzwdG/YZmNR7EylyHe8ikTQAlVc0VI/UOf5wMAzR+qalNiqB5HpQHoqktsm6YYdCSqPKM+CO1VwagY2l5MsjEkaLd0E498OGJw5m5eAihQ725YiMfdiSlgG6wE60mXdaM7N75VOXXTedpKPzAgGZHdR+eCyg5yBoJPZEaTH9Xwv7Rs9whMQOIVGx7udt+J6Y0W1I5wjbCg1cA75lIeDs3GIp6kLRCCnQ9+SfQqncTwG48ldeco6WjecEDRX9ayFtFGtgUxV1azG6WkALh9RiU9seZ3a9CLZMePDXQV9lBjGofTqXmxzo7KcwlmcnQBItR/fg7z0CRJm16fQe9AbByxg3pW+zK1O61TR005cN/2B2m+Fc2CBWJbHfedYfuTLVEwi3jrSBHHe+g9NanruthsePJ3DcCGukz0pGYtAE6eEiq45jgdnow/pnptbm3OHhJkTjVj8rVR620v5bNEycQB6Xwoav1tqYnjIS526OUtljXg3kun20KfnA0ddpeEsti0yLQsOrurR4nwnlrlGkENhXLhAylnhfJabTlxV19C9cE1RrMSVcN17ezoUDFYOc61I6Sahk+7aDo7LyxjHmleuvaycUO6Ir2mJTGq/lkTkjXymkzmwzQvcu4t9FyWVy59W9Na3B9kajM0o0X1pe7HvHqUabgDbhMJklLR01i+zYY5m4RdN6NlW32chrpEgGphwhBSvVorTqRvTvQ61jFZA4rSNtouG67osFvy1uHOnVL02+nCvMl10xjAn5Wsk5HdSkUIP0RxdJjUwtV1zVi9LaidWd/9KF8U+WN/La7aTkpCU0ycUWwLdMOxq5bGYhe/Qq5QbQ9gBOVwOLqm5djuW8j1uXC6EOVshuK/ykHjipnAkYpkS8V2r80UjkPZwuIbTiZcdNuxjBxuHBDcBLl0hxWl2tLQokVS291C+puTaZ42wjTPncY0jLFO1SC7M0t2d7zEx9htBUj7cTaETHserluuAOC2AChXKqhXsTRB2ubobwBAnekZHqVuQotQMTLfnzMzrKeryhrbhMq205lpuHtgQSKxx4brt+fkibOn9gfJrxerIkIc+EfVGIf6cPuj0vgxbRc/68mEE2K/iWL+iNT+d55UZZudUA5tQa20VrDntpIZrfUszw8iJUbSbmvFx0pC8geuFO21tph5ve5MeE7esZVfyj6qiJ950nBUxjV1Bm4LRRMcOsXSkLPR07VnuaXsar+BarDJfy6q8zvcTf72zYw1cbit6CybRZsNZRWmToQ5EOs/6G18aKnUpRsPx/sAZWT12hzYYIZoNaaAComJP7GwlSh2PYjNnlPnLarvvkyC0TtN4MAOrP2h9ChJ0NuDA5ydK2mrVufRj9mGB8wiJAlYPNXFHIsVKPs/n5hFau+Y4yhqGc9fTRXroAU5WUk6HUnVZH8rdcAxn9rjlYAzlWU5ofdFZStgpHPDPBtVchWH2htxAxHr7CU8ylcIU7YEnIjbxnEiaVAFQYEGwzdJZnuoH7pPEm1ide8YwlyCBVLDyG194LBKvRBuFps5mnU0Zvz+ih+3nzs32FsMdZ4i7lI3Tkm/FwEftweXdZmOzoAPH0RTO90h1kNOyonpPvDnsyA4HBNDnYtUa6+NEbJTpjXznewfLxHKCgOQ/JxXso0x85YTnDkj96T28IallsKID4itX5ZCbj0ggD/EOEeyYlEvjXi2Y/gA9e2svs0t/QXdi4XlcO3m5C1ACtP2FT67BNgTQW16D2uWLINnu/WjLAJ0DrLZndhIHTHe7uK5Ma9EnmJtFQehTHeG2682mrko92lIgYi7Myedw5m/c+np0MWShgXq4Jk6Ii3kZJ98Hnd1q5bbyQKLcDk1gBbPrgxun+WLuv85nPuahFsZXdYS+HVxKJwnrgmw0tb3Lg2gwjjPPXkAj85vGChPlkQappbauPfZUSTbVuo6PkVw3ZX5P1nZmHwYc71WLIJrl7Xqbbnx51SwHKLeNx7NyB3UIbO+YmTDPWR14VhXWYbfXjsfdvvBwIdFGN7WRw6Muo019bIrcD/QLQBv/ESMOCIR7tSy8B0CwWQTm7X6jZGQ28Xra0YLTt+UyWQEHKgeN7AYkrAUc6YqRQ55Lw8LTkYSSgltp5XwCIizWaWe5QnBWkAIIUGRwdIYSFmMkd7EO1FyQ0hh0NmjvBobtKh/vjlsEkUrhqllldoCDOzoJLb2ZFOBgo4Ox7SxceVwfCCA+KDapI6SK8F+mW79YXu6zi1EM56YTzZ5xTlMnAQLZQ95e1U71BY6yb5x4fmbldcaJAK0DLwQc2B9xucaBIy60BEDqsoZeBp6Ve8AOsU89brQ8Ui3B84t+5Ph9EYkdRtp7Q4e7NpROw+PC+SrwwMpSE+qknpqVJ6nN9cYaBOon7YxAYzNd1edrFq2oY7tSebn3UtfYAhvAfYvjs1uCXZ495r73HuzoAYsbVc3W8XTc0qUObcILVUrYeTWWXTSbsXSxLZ7t0rvRjQXG/Py4KmvuKII+TOnVebLj9WWgdwisNLmebyUPaclBuybMjwfMA/FF40mKeXgEVrOPozsljFJMZIqqg5IhVj0khxvNlvIA6BrMqWojgauVuTnRnFn5OEJCgcJ1FgKLKVTRCVHuZbVf0n278iXPhdjX4H4jhQNYG7dNmDfLUywMeD68eU8GVARQAc/ty9t9Gm8bXj4RAMY2bpHOg0FqYACNrbdQ2ouBEHrd3vg7dji4zwcdFsKXHQ7hmx0O4W/b4WD/1OfYf+Q3WIblf1UW+/UbLPf/8xss883pkm8OPvwjlcWJy1+VxfxPK+u746Gfv6BXCSBHoomMy6jrivjT6Z9PQkunog+fbeT+QOT3r/cWeXqK8/Vhfv9q/v/4Cf3to/PvnQjg/uS39p80sPidc0L/zU/y4uLX0MpRnzT7Z8+QLanPMfoTo7/5DBkjfuPlOBK/Ojq+EeUXoLz6PXhZsv9Qdxc+n5pgv34q+M7Y/j53X36jtY+DudwLXL6I4ouyJOdsofyqx+ULEF770C+i9EoRX5Zvx3GxZnFEV1+g9Nr0H/FZvADcjX4/6bv4h1rIx1+E/E5C+KtMhOw+ffxdyFsc+PHHN6zyXw== \ No newline at end of file diff --git "a/docs/database/mysql/images/\346\225\260\346\215\256\345\272\223\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" "b/docs/database/mysql/images/\346\225\260\346\215\256\345\272\223\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" deleted file mode 100644 index 6ab55e05746..00000000000 --- "a/docs/database/mysql/images/\346\225\260\346\215\256\345\272\223\344\272\213\345\212\241\347\244\272\346\204\217\345\233\276.drawio" +++ /dev/null @@ -1 +0,0 @@ -7Zpbc5s4GIZ/jS6TscCAuAQb0sm0s9tmOtvuTUcBGdPIyAtyYu+vX0kIc3TidJOYepLMJNKnE0iP3k8HgDlbba9yvF5+YjGhwJjEW2DOgWHAieGKf9KyKy3uxCwNSZ7GOlNtuEn/JVVJbd2kMSlaGTljlKfrtjFiWUYi3rLhPGcP7WwLRtutrnFCeoabCNO+9a805svSigyntn8gabKsWoa2fuFbHN0lOdtkuj1gmKEdhiEqk1e4qku/aLHEMXtomMwAmLOcMV6GVtsZobJvq24ry4UHUvfPnZOMH1OAJjfh359XhXPNrY9WEH0prr9cyAKymntMN6R6D/W0fFf1kHpHImuBwPQfliknN2scydQHwYSwLfmK6uQFy7geZGiJ+P6tJzJyR3i01JFFSumMUZarVsy5FaD5VGbiObsjVUrGMqKrDfEqpZKya8L9HKdZIZ7zE8tY1Szb5OqplpwLeAzL9MQf0R/yj8xQXCaMJZTgdVpcRmylEqJCZQ0XZe0i2KzfMnzdQr+79Qjck5yTbcOku/+KsBXhuahysq2mRllCzxRHRx8a2CFtWzaQM21txBr1ZF9zPdwioEf8GaNv98aaxGJy6CjL+ZIlLMM0qK1+TYMcxDrPR8bWmoGfhPOdhgBvOGsTIvov333T5VXku4xcWlV0vm0mzncVPAdJKompZq8xblyKqtlDg6Llk+M8IfyRfFaZT47Yo/DlhGKe3rfl7sVRMgd0xKZc93ULMvufDasSLgqFiej5CXTW2zpRhBL1P7CAPwNiXoiAmEBeCIIp8D2AVBLygAerlkA5bvuiPbgpFV6FHCFibWmKMUGL6JA0PaJw46XweNGCVlu19v6sIVvQGJAt57VUC55Eto53bOclRy8uM7ronyxVuqAxc6ZtzGDX6ZV6qEt1CNo/xv+AyjmJL9ym/Fsj3PCEIlY7Qhmp/OA7iK8LYkfvTPM4EL08x7tGtrXMUBxux+4Ab1hWe/n+RP7quep5UD7Bi84K66V3B23HulgsjOgXHGtCcVGck5PdQ9ZVv4aT3e+Ym04Wuq/mZWFvqEflZUc87k9KmXPkEh+Naonv9MTg5vNHENjANQGaq2X5HLizPjdiEvD2MA/v8RvioE2YpkkmopHoYiLsvpxSaYSppxNWaRzTQyv6No1ngdYzJKVz2gDRwHHDgKIYryYofd/xpqcN9QHDd9A8Xxg+bTgLWp4UInSkEFWuZyRKhI5Toj5w70o0AiUyzFMr0eQ4frJ3fsbIjwlPzU//sPNS/fwmvOSMC5Fmshb38ZP18+AHdfRn6OLlTfk54L8sgCzgIRC4AJkqYAPkAs8BAZLH3GimLEieg5dn3274myB35oh1D2fgyV2c20NM+beZBEr6N6SuVFTA87T367ImYBQZoLT4U+BZ76yNgrXuAaV1YtaM/nLqpe7+BJOhUjt15SdofL/7e0vUHLsjaye/+9u/w2vcM7s+cAMJnS8C3tuztkAR+ZXj8PNgzenI2uQNWfPx2vpw95XAmJtft1v7jx8/0osB1PQSbabYmAHXUZYQ+CIQKot3je/xlfymraLlNj+KSltQqXDsgemo3SiUAU+A6e6b+imaUp/PXUZj3apSfEuov/9SbuCrrspz2zremA+++m1vWFrwwxHB3yN9YD4chH8Kp22hHdjiwiGnDp8Pv4jWHx2WF4X1l51m8B8= \ No newline at end of file diff --git a/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md b/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md index 377460c66a6..f08c2204209 100644 --- a/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md +++ b/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md @@ -4,6 +4,13 @@ category: 数据库 tag: - MySQL - 性能优化 +head: + - - meta + - name: keywords + content: 隐式转换,索引失效,类型不匹配,函数计算,优化器,性能退化 + - - meta + - name: description + content: 解析隐式转换导致的索引失效与性能退化,给出类型规范、语句改写与参数配置建议,避免查询退化。 --- > 本次测试使用的 MySQL 版本是 `5.7.26`,随着 MySQL 版本的更新某些特性可能会发生改变,本文不代表所述观点和结论于 MySQL 所有版本均准确无误,版本差异请自行甄别。 diff --git a/docs/database/mysql/innodb-implementation-of-mvcc.md b/docs/database/mysql/innodb-implementation-of-mvcc.md index a2e19998d71..8fa57019f0a 100644 --- a/docs/database/mysql/innodb-implementation-of-mvcc.md +++ b/docs/database/mysql/innodb-implementation-of-mvcc.md @@ -3,6 +3,13 @@ title: InnoDB存储引擎对MVCC的实现 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: InnoDB,MVCC,快照读,当前读,一致性视图,隐藏列,事务版本,间隙锁 + - - meta + - name: description + content: 深入解析 InnoDB 的 MVCC 实现细节与读写隔离,覆盖一致性视图、快照/当前读与隐藏列、间隙锁的配合。 --- ## 多版本并发控制 (Multi-Version Concurrency Control) diff --git a/docs/database/mysql/mysql-auto-increment-primary-key-continuous.md b/docs/database/mysql/mysql-auto-increment-primary-key-continuous.md index ec900188610..345a669cc4c 100644 --- a/docs/database/mysql/mysql-auto-increment-primary-key-continuous.md +++ b/docs/database/mysql/mysql-auto-increment-primary-key-continuous.md @@ -4,6 +4,13 @@ category: 数据库 tag: - MySQL - 大厂面试 +head: + - - meta + - name: keywords + content: 自增主键,不连续,事务回滚,并发插入,计数器,聚簇索引 + - - meta + - name: description + content: 解析自增主键不连续的根因与触发场景,结合事务回滚与并发插入,说明 InnoDB 计数器与聚簇索引的行为。 --- > 作者:飞天小牛肉 diff --git a/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md b/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md index 38c333b3308..339a9a31f25 100644 --- a/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md +++ b/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md @@ -3,6 +3,13 @@ title: MySQL高性能优化规范建议总结 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: MySQL 优化,索引设计,SQL 规范,表结构,慢查询,参数调优,实践清单 + - - meta + - name: description + content: 提炼 MySQL 高性能优化规范,涵盖索引与 SQL、表结构与慢查询、参数与实用清单,提升线上稳定与效率。 --- > 作者: 听风 原文地址: 。 diff --git a/docs/database/mysql/mysql-index.md b/docs/database/mysql/mysql-index.md index a21d133feea..48e31005cef 100644 --- a/docs/database/mysql/mysql-index.md +++ b/docs/database/mysql/mysql-index.md @@ -3,6 +3,13 @@ title: MySQL索引详解 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: MySQL 索引,B+树,覆盖索引,联合索引,选择性,回表,索引下推 + - - meta + - name: description + content: 深入解析 MySQL 索引结构与选型,覆盖 B+ 树、联合与覆盖索引、选择性与回表等关键优化点与实践。 --- > 感谢[WT-AHA](https://github.com/WT-AHA)对本文的完善,相关 PR: 。 @@ -21,19 +28,25 @@ tag: ## 索引的优缺点 -**优点**: +**索引的优点:** -- 使用索引可以大大加快数据的检索速度(大大减少检索的数据量),减少 IO 次数,这也是创建索引的最主要的原因。 -- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 +1. **查询速度起飞 (主要目的)**:通过索引,数据库可以**大幅减少需要扫描的数据量**,直接定位到符合条件的记录,从而显著加快数据检索速度,减少磁盘 I/O 次数。 +2. **保证数据唯一性**:通过创建**唯一索引 (Unique Index)**,可以确保表中的某一列(或几列组合)的值是独一无二的,比如用户 ID、邮箱等。主键本身就是一种唯一索引。 +3. **加速排序和分组**:如果查询中的 ORDER BY 或 GROUP BY 子句涉及的列建有索引,数据库往往可以直接利用索引已经排好序的特性,避免额外的排序操作,从而提升性能。 -**缺点**: +**索引的缺点:** + +1. **创建和维护耗时**:创建索引本身需要时间,特别是对大表操作时。更重要的是,当对表中的数据进行**增、删、改 (DML 操作)** 时,不仅要操作数据本身,相关的索引也必须动态更新和维护,这会**降低这些 DML 操作的执行效率**。 +2. **占用存储空间**:索引本质上也是一种数据结构,需要以物理文件(或内存结构)的形式存储,因此会**额外占用一定的磁盘空间**。索引越多、越大,占用的空间也就越多。 +3. **可能被误用或失效**:如果索引设计不当,或者查询语句写得不好,数据库优化器可能不会选择使用索引(或者选错索引),反而导致性能下降。 -- 创建和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态地修改,这会降低 SQL 执行效率。 -- 索引需要使用物理文件存储,也会耗费一定空间。 +**那么,用了索引就一定能提高查询性能吗?** -但是,**使用索引一定能提高查询性能吗?** +**不一定。** 大多数情况下,合理使用索引确实比全表扫描快得多。但也有例外: -大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。 +- **数据量太小**:如果表里的数据非常少(比如就几百条),全表扫描可能比通过索引查找更快,因为走索引本身也有开销。 +- **查询结果集占比过大**:如果要查询的数据占了整张表的大部分(比如超过 20%-30%),优化器可能会认为全表扫描更划算,因为通过索引多次回表(随机 I/O)的成本可能高于一次顺序的全表扫描。 +- **索引维护不当或统计信息过时**:导致优化器做出错误判断。 ## 索引底层数据结构选型 @@ -386,13 +399,14 @@ EXPLAIN SELECT * FROM student WHERE name = 'Anne Henry' AND class = 'lIrm08RYVk' SELECT * FROM student WHERE class = 'lIrm08RYVk'; ``` -再来看一个常见的面试题:如果有索引 `联合索引(a,b,c)`,查询 `a=1 AND c=1` 会走索引么?`c=1` 呢?`b=1 AND c=1` 呢? +再来看一个常见的面试题:如果有索引 `联合索引(a,b,c)`,查询 `a=1 AND c=1` 会走索引么?`c=1` 呢?`b=1 AND c=1` 呢? `b = 1 AND a = 1 AND c = 1` 呢? 先不要往下看答案,给自己 3 分钟时间想一想。 1. 查询 `a=1 AND c=1`:根据最左前缀匹配原则,查询可以使用索引的前缀部分。因此,该查询仅在 `a=1` 上使用索引,然后对结果进行 `c=1` 的过滤。 2. 查询 `c=1`:由于查询中不包含最左列 `a`,根据最左前缀匹配原则,整个索引都无法被使用。 3. 查询 `b=1 AND c=1`:和第二种一样的情况,整个索引都不会使用。 +4. 查询 `b=1 AND a=1 AND c=1`:这个查询是可以用到索引的。查询优化器分析 SQL 语句时,对于联合索引,会对查询条件进行重排序,以便用到索引。会将 `b=1` 和 `a=1` 的条件进行重排序,变成 `a=1 AND b=1 AND c=1`。 MySQL 8.0.13 版本引入了索引跳跃扫描(Index Skip Scan,简称 ISS),它可以在某些索引查询场景下提高查询效率。在没有 ISS 之前,不满足最左前缀匹配原则的联合索引查询中会执行全表扫描。而 ISS 允许 MySQL 在某些情况下避免全表扫描,即使查询条件不符合最左前缀。不过,这个功能比较鸡肋, 和 Oracle 中的没法比,MySQL 8.0.31 还报告了一个 bug:[Bug #109145 Using index for skip scan cause incorrect result](https://bugs.mysql.com/bug.php?id=109145)(后续版本已经修复)。个人建议知道有这个东西就好,不需要深究,实际项目也不一定能用上。 @@ -474,7 +488,7 @@ MySQL 可以简单分为 Server 层和存储引擎层这两层。Server 层处 索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率。 -因为 MySQL 优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,就会增加 MySQL 优化器生成执行计划的时间,同样会降低查询性能。 +因为 MySQL 优化器在选择如何优化查询时,会根据统计信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,就会增加 MySQL 优化器生成执行计划的时间,同样会降低查询性能。 ### 尽可能的考虑建立联合索引而不是单列索引 diff --git a/docs/database/mysql/mysql-logs.md b/docs/database/mysql/mysql-logs.md index ac7e29db2f3..e0af105ea35 100644 --- a/docs/database/mysql/mysql-logs.md +++ b/docs/database/mysql/mysql-logs.md @@ -3,6 +3,13 @@ title: MySQL三大日志(binlog、redo log和undo log)详解 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: MySQL 日志,binlog,redo log,undo log,两阶段提交,崩溃恢复,复制 + - - meta + - name: description + content: 系统解析 MySQL 的 binlog/redo/undo 三大日志与两阶段提交,理解崩溃恢复与主从复制的实现原理与取舍。 --- > 本文来自公号程序猿阿星投稿,JavaGuide 对其做了补充完善。 @@ -41,16 +48,16 @@ MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一 ### 刷盘时机 -InnoDB 刷新重做日志的时机有几种情况: +在 InnoDB 存储引擎中,**redo log buffer**(重做日志缓冲区)是一块用于暂存 redo log 的内存区域。为了确保事务的持久性和数据的一致性,InnoDB 会在特定时机将这块缓冲区中的日志数据刷新到磁盘上的 redo log 文件中。这些时机可以归纳为以下六种: -InnoDB 将 redo log 刷到磁盘上有几种情况: - -1. 事务提交:当事务提交时,log buffer 里的 redo log 会被刷新到磁盘(可以通过`innodb_flush_log_at_trx_commit`参数控制,后文会提到)。 -2. log buffer 空间不足时:log buffer 中缓存的 redo log 已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。 -3. 事务日志缓冲区满:InnoDB 使用一个事务日志缓冲区(transaction log buffer)来暂时存储事务的重做日志条目。当缓冲区满时,会触发日志的刷新,将日志写入磁盘。 -4. Checkpoint(检查点):InnoDB 定期会执行检查点操作,将内存中的脏数据(已修改但尚未写入磁盘的数据)刷新到磁盘,并且会将相应的重做日志一同刷新,以确保数据的一致性。 -5. 后台刷新线程:InnoDB 启动了一个后台线程,负责周期性(每隔 1 秒)地将脏页(已修改但尚未写入磁盘的数据页)刷新到磁盘,并将相关的重做日志一同刷新。 -6. 正常关闭服务器:MySQL 关闭的时候,redo log 都会刷入到磁盘里去。 +1. **事务提交时(最核心)**:当事务提交时,log buffer 里的 redo log 会被刷新到磁盘(可以通过`innodb_flush_log_at_trx_commit`参数控制,后文会提到)。 +2. **redo log buffer 空间不足时**:这是 InnoDB 的一种主动容量管理策略,旨在避免因缓冲区写满而导致用户线程阻塞。 + - 当 redo log buffer 的已用空间超过其总容量的**一半 (50%)** 时,后台线程会**主动**将这部分日志刷新到磁盘,为后续的日志写入腾出空间,这是一种“未雨绸缪”的优化。 + - 如果因为大事务或 I/O 繁忙导致 buffer 被**完全写满**,那么所有试图写入新日志的用户线程都会被**阻塞**,并强制进行一次同步刷盘,直到有可用空间为止。这种情况会影响数据库性能,应尽量避免。 +3. **触发检查点 (Checkpoint) 时**:Checkpoint 是 InnoDB 为了缩短崩溃恢复时间而设计的核心机制。当 Checkpoint 被触发时,InnoDB 需要将在此检查点之前的所有脏页刷写到磁盘。根据 **Write-Ahead Logging (WAL)** 原则,数据页写入磁盘前,其对应的 redo log 必须先落盘。因此,执行 Checkpoint 操作必然会确保相关的 redo log 也已经被刷新到了磁盘。 +4. **后台线程周期性刷新**:InnoDB 有一个后台的 master thread,它会大约每秒执行一次例行任务,其中就包括将 redo log buffer 中的日志刷新到磁盘。这个机制是 `innodb_flush_log_at_trx_commit` 设置为 0 或 2 时的主要持久化保障。 +5. **正常关闭服务器**:在 MySQL 服务器正常关闭的过程中,为了确保所有已提交事务的数据都被完整保存,InnoDB 会执行一次最终的刷盘操作,将 redo log buffer 中剩余的全部日志都清空并写入磁盘文件。 +6. **binlog 切换时**:当开启 binlog 后,在 MySQL 采用 `innodb_flush_log_at_trx_commit=1` 和 `sync_binlog=1` 的 双一配置下,为了保证 redo log 和 binlog 之间状态的一致性(用于崩溃恢复或主从复制),在 binlog 文件写满或者手动执行 flush logs 进行切换时,会触发 redo log 的刷盘动作。 总之,InnoDB 在多种情况下会刷新重做日志,以保证数据的持久性和一致性。 diff --git a/docs/database/mysql/mysql-query-execution-plan.md b/docs/database/mysql/mysql-query-execution-plan.md index 8866737b934..50c22812457 100644 --- a/docs/database/mysql/mysql-query-execution-plan.md +++ b/docs/database/mysql/mysql-query-execution-plan.md @@ -89,8 +89,8 @@ id 如果相同,从上往下依次执行。id 不同,id 值越大,执行 查询用到的表名,每行都有对应的表名,表名除了正常的表之外,也可能是以下列出的值: - **``** : 本行引用了 id 为 M 和 N 的行的 UNION 结果; -- **``** : 本行引用了 id 为 N 的表所产生的的派生表结果。派生表有可能产生自 FROM 语句中的子查询。 -- **``** : 本行引用了 id 为 N 的表所产生的的物化子查询结果。 +- **``** : 本行引用了 id 为 N 的表所产生的派生表结果。派生表有可能产生自 FROM 语句中的子查询。 +- **``** : 本行引用了 id 为 N 的表所产生的物化子查询结果。 ### type(重要) @@ -111,7 +111,7 @@ system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_su ### possible_keys -possible_keys 列表示 MySQL 执行查询时可能用到的索引。如果这一列为 NULL ,则表示没有可能用到的索引;这种情况下,需要检查 WHERE 语句中所使用的的列,看是否可以通过给这些列中某个或多个添加索引的方法来提高查询性能。 +possible_keys 列表示 MySQL 执行查询时可能用到的索引。如果这一列为 NULL ,则表示没有可能用到的索引;这种情况下,需要检查 WHERE 语句中所使用的列,看是否可以通过给这些列中某个或多个添加索引的方法来提高查询性能。 ### key(重要) diff --git a/docs/database/mysql/mysql-questions-01.md b/docs/database/mysql/mysql-questions-01.md index 7f93eb605e6..2b3ca67f3ab 100644 --- a/docs/database/mysql/mysql-questions-01.md +++ b/docs/database/mysql/mysql-questions-01.md @@ -7,10 +7,10 @@ tag: head: - - meta - name: keywords - content: MySQL基础,MySQL基础架构,MySQL存储引擎,MySQL查询缓存,MySQL事务,MySQL锁等内容。 + content: MySQL面试题,MySQL基础架构,InnoDB存储引擎,MySQL索引,B+树索引,事务隔离级别,redo log,undo log,binlog,MVCC,行级锁,慢查询优化 - - meta - name: description - content: 一篇文章总结MySQL常见的知识点和面试题,涵盖MySQL基础、MySQL基础架构、MySQL存储引擎、MySQL查询缓存、MySQL事务、MySQL锁等内容。 + content: MySQL高频面试题精讲:基础架构、InnoDB引擎、索引原理、B+树、事务ACID、MVCC、redo/undo/binlog日志、行锁/表锁、慢查询优化,一文速通大厂必考点! --- @@ -55,20 +55,30 @@ SQL 可以帮助我们: 由于 MySQL 是开源免费并且比较成熟的数据库,因此,MySQL 被大量使用在各种系统中。任何人都可以在 GPL(General Public License) 的许可下下载并根据个性化的需要对其进行修改。MySQL 的默认端口号是**3306**。 -### MySQL 有什么优点? +### ⭐️MySQL 有什么优点? 这个问题本质上是在问 MySQL 如此流行的原因。 -MySQL 主要具有下面这些优点: +MySQL 成功可以归功于在**生态、功能和运维**这三个层面上的综合优势。 -1. 成熟稳定,功能完善。 -2. 开源免费。 -3. 文档丰富,既有详细的官方文档,又有非常多优质文章可供参考学习。 -4. 开箱即用,操作简单,维护成本低。 -5. 兼容性好,支持常见的操作系统,支持多种开发语言。 -6. 社区活跃,生态完善。 -7. 事务支持优秀, InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失,并且,InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的。 -8. 支持分库分表、读写分离、高可用。 +**第一,从生态和成本角度看,它的护城河非常深。** + +- **开源免费:** 这是它得以广泛普及的基石。任何公司和个人都可以免费使用,极大地降低了技术门槛和初期成本。 +- **社区庞大,生态完善:** 经过几十年的发展,MySQL 拥有极其活跃的社区和丰富的生态系统。这意味着无论你遇到什么问题,几乎都能在网上找到解决方案;同时,市面上所有的主流编程语言、框架、ORM 工具、监控系统都对 MySQL 有完美的支持。它的文档也非常丰富,学习资源唾手可得。 + +**第二,从核心技术功能上看,它非常强大且均衡。** + +- **强大的事务支持:** 这是它作为关系型数据库的立身之本。值得一提的是,InnoDB 默认的可重复读(REPEATABLE-READ)隔离级别,通过 MVCC 和 Next-Key Lock 机制,很大程度上避免了幻读问题,这在很多其他数据库中都需要更高的隔离级别才能做到,兼顾了性能和一致性。详细介绍可以阅读笔者写的这篇文章:[MySQL 事务隔离级别详解](https://javaguide.cn/database/mysql/transaction-isolation-level.html)。 +- **优秀的性能和可扩展性:** MySQL 本身经过了海量互联网业务的严酷考验,单机性能非常出色。更重要的是,它围绕着水平扩展,形成了一套非常成熟的架构方案,比如主从复制、读写分离、以及通过中间件实现的分库分表。这让它能够支撑从初创公司到大型互联网平台的各种规模的业务。 + +**第三,从运维和使用角度看,它非常‘亲民’。** + +- **开箱即用,上手简单:** 相比于 Oracle 等大型商业数据库,MySQL 的安装、配置和日常使用都非常简单直观,学习曲线平缓,对于开发者和初级 DBA 非常友好。 +- **维护成本低:** 由于其简单性和庞大的社区,找到相关的运维人才和解决方案都相对容易,整体的维护成本也更低。 + +值得一提的是最近几年,PostgreSQL 的势头很猛,甚至压过了 MySQL。网上出现了很多抨击诋毁 MySQL 的文章,笔者认为任何无脑抨击其中一方或者吹捧另外一方的行为都是不可取的。 + +笔者也写过一篇文章分享对这两个关系型数据库代表的看法,感兴趣的可以看看:[MySQL 被干成老二了?](https://mp.weixin.qq.com/s/APWD-PzTcTqGUuibAw7GGw)。 ## MySQL 字段类型 @@ -86,7 +96,7 @@ MySQL 字段类型比较多,我这里会挑选一些日常开发使用很频 另外,推荐阅读一下《高性能 MySQL(第三版)》的第四章,有详细介绍 MySQL 字段类型优化。 -### 整数类型的 UNSIGNED 属性有什么用? +### ⭐️整数类型的 UNSIGNED 属性有什么用? MySQL 中的整数类型可以使用可选的 UNSIGNED 属性来表示不允许负值的无符号整数。使用 UNSIGNED 属性可以将正整数的上限提高一倍,因为它不需要存储负数值。 @@ -152,7 +162,7 @@ BLOB 类型主要用于存储二进制大对象,例如图片、音视频等文 - 可能导致表上的 DML 操作变慢。 - …… -### DATETIME 和 TIMESTAMP 的区别是什么? +### ⭐️DATETIME 和 TIMESTAMP 的区别是什么?如何选择? DATETIME 类型没有时区信息,TIMESTAMP 和时区有关。 @@ -161,7 +171,11 @@ TIMESTAMP 只需要使用 4 个字节的存储空间,但是 DATETIME 需要耗 - DATETIME:'1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999' - Timestamp:'1970-01-01 00:00:01.000000' UTC 到 '2038-01-19 03:14:07.999999' UTC -关于两者的详细对比,请参考我写的 [MySQL 时间类型数据存储建议](./some-thoughts-on-database-storage-time.md)。 +`TIMESTAMP` 的核心优势在于其内建的时区处理能力。数据库负责 UTC 存储和基于会话时区的自动转换,简化了需要处理多时区应用的开发。如果应用需要处理多时区,或者希望数据库能自动管理时区转换,`TIMESTAMP` 是自然的选择(注意其时间范围限制,也就是 2038 年问题)。 + +如果应用场景不涉及时区转换,或者希望应用程序完全控制时区逻辑,并且需要表示 2038 年之后的时间,`DATETIME` 是更稳妥的选择。 + +关于两者的详细对比以及日期存储类型选择建议,请参考我写的这篇文章: [MySQL 时间类型数据存储建议](./some-thoughts-on-database-storage-time.md)。 ### NULL 和 '' 的区别是什么? @@ -183,11 +197,11 @@ TIMESTAMP 只需要使用 4 个字节的存储空间,但是 DATETIME 需要耗 看了上面的介绍之后,相信你对另外一个高频面试题:“为什么 MySQL 不建议使用 `NULL` 作为列默认值?”也有了答案。 -### Boolean 类型如何表示? +### ⭐️Boolean 类型如何表示? -MySQL 中没有专门的布尔类型,而是用 TINYINT(1) 类型来表示布尔值。TINYINT(1) 类型可以存储 0 或 1,分别对应 false 或 true。 +MySQL 中没有专门的布尔类型,而是用 `TINYINT(1)` 类型来表示布尔值。`TINYINT(1)` 类型可以存储 0 或 1,分别对应 false 或 true。 -### 手机号存储用 INT 还是 VARCHAR? +### ⭐️手机号存储用 INT 还是 VARCHAR? 存储手机号,**强烈推荐使用 VARCHAR 类型**,而不是 INT 或 BIGINT。主要原因如下: @@ -289,7 +303,7 @@ mysql> SHOW VARIABLES LIKE '%storage_engine%'; MySQL 存储引擎采用的是 **插件式架构** ,支持多种存储引擎,我们甚至可以为不同的数据库表设置不同的存储引擎以适应不同场景的需要。**存储引擎是基于表的,而不是数据库。** -下图展示了具有可插拔存储引擎的 MySQL 架构(): +下图展示了具有可插拔存储引擎的 MySQL 架构: ![MySQL architecture diagram showing connectors, interfaces, pluggable storage engines, the file system with files and logs.](https://oss.javaguide.cn/github/javaguide/mysql/mysql-architecture.png) @@ -297,7 +311,7 @@ MySQL 存储引擎采用的是 **插件式架构** ,支持多种存储引擎 MySQL 官方文档也有介绍到如何编写一个自定义存储引擎,地址: 。 -### MyISAM 和 InnoDB 有什么区别? +### ⭐️MyISAM 和 InnoDB 有什么区别? MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,可谓是风光一时。 @@ -389,9 +403,163 @@ InnoDB 使用缓冲池(Buffer Pool)缓存数据页和索引页,MyISAM 使 因此,对于咱们日常开发的业务系统来说,你几乎找不到什么理由使用 MyISAM 了,老老实实用默认的 InnoDB 就可以了! -## MySQL 索引 +## ⭐️MySQL 索引 + +MySQL 索引相关的问题比较多,也非常重要,更详细的介绍可以阅读笔者写的这篇文章:[MySQL 索引详解](./mysql-index.md) 。 + +### 索引是什么? + +**索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。** + +索引的作用就相当于书的目录。打个比方:我们在查字典的时候,如果没有目录,那我们就只能一页一页地去找我们需要查的那个字,速度很慢;如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。 + +索引底层数据结构存在很多种类型,常见的索引结构有:B 树、 B+ 树 和 Hash、红黑树。在 MySQL 中,无论是 Innodb 还是 MyISAM,都使用了 B+ 树作为索引结构。 + +**索引的优点:** + +1. **查询速度起飞 (主要目的)**:通过索引,数据库可以**大幅减少需要扫描的数据量**,直接定位到符合条件的记录,从而显著加快数据检索速度,减少磁盘 I/O 次数。 +2. **保证数据唯一性**:通过创建**唯一索引 (Unique Index)**,可以确保表中的某一列(或几列组合)的值是独一无二的,比如用户 ID、邮箱等。主键本身就是一种唯一索引。 +3. **加速排序和分组**:如果查询中的 ORDER BY 或 GROUP BY 子句涉及的列建有索引,数据库往往可以直接利用索引已经排好序的特性,避免额外的排序操作,从而提升性能。 + +**索引的缺点:** + +1. **创建和维护耗时**:创建索引本身需要时间,特别是对大表操作时。更重要的是,当对表中的数据进行**增、删、改 (DML 操作)** 时,不仅要操作数据本身,相关的索引也必须动态更新和维护,这会**降低这些 DML 操作的执行效率**。 +2. **占用存储空间**:索引本质上也是一种数据结构,需要以物理文件(或内存结构)的形式存储,因此会**额外占用一定的磁盘空间**。索引越多、越大,占用的空间也就越多。 +3. **可能被误用或失效**:如果索引设计不当,或者查询语句写得不好,数据库优化器可能不会选择使用索引(或者选错索引),反而导致性能下降。 + +**那么,用了索引就一定能提高查询性能吗?** + +**不一定。** 大多数情况下,合理使用索引确实比全表扫描快得多。但也有例外: + +- **数据量太小**:如果表里的数据非常少(比如就几百条),全表扫描可能比通过索引查找更快,因为走索引本身也有开销。 +- **查询结果集占比过大**:如果要查询的数据占了整张表的大部分(比如超过 20%-30%),优化器可能会认为全表扫描更划算,因为通过索引多次回表(随机 I/O)的成本可能高于一次顺序的全表扫描。 +- **索引维护不当或统计信息过时**:导致优化器做出错误判断。 + +### 索引为什么快? + +索引之所以快,核心原因是它**大大减少了磁盘 I/O 的次数**。 + +它的本质是一种**排好序的数据结构**,就像书的目录,让我们不用一页一页地翻(全表扫描)。 + +在 MySQL 中,这个数据结构是**B+树**。B+树结构主要从两方面做了优化: + +1. B+树的特点是“矮胖”,一个千万数据的表,索引树的高度可能只有 3-4 层。这意味着,最多只需要**3-4 次磁盘 I/O**,就能精确定位到我想要的数据,而全表扫描可能需要成千上万次,所以速度极快。 +2. B+树的叶子节点是**用链表连起来的**。找到开头后,就能顺着链表**顺序读**下去,这对磁盘非常友好,还能触发预读。 + +### MySQL 索引底层数据结构是什么? + +在 MySQL 中,MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,详细介绍可以参考笔者写的这篇文章:[MySQL 索引详解](https://javaguide.cn/database/mysql/mysql-index.html)。 + +### 为什么 InnoDB 没有使用哈希作为索引的数据结构? + +> 我发现很多求职者甚至是面试官对这个问题都有误解,他们相当然的认为 MySQL 底层并没有使用哈希或者 B 树作为索引的数据结构。 +> +> 实际上,不论是提问还是回答这个问题都要区分好存储引擎。像 MEMORY 引擎就同时支持哈希和 B 树。 + +哈希索引的底层是哈希表。它的优点是,在进行**精确的等值查询**时,理论上时间复杂度是 **O(1)** ,速度极快。比如 `WHERE id = 123`。 + +但是,它有几个对于通用数据库来说是致命的缺点: + +1. **不支持范围查询:** 这是最主要的原因。哈希函数的一个特点是它会把相邻的输入值(比如 `id=100` 和 `id=101`)映射到哈希表中完全不相邻的位置。这种顺序的破坏,使得我们无法处理像 `WHERE age > 30` 或 `BETWEEN 100 AND 200`这样的范围查询。要完成这种查询,哈希索引只能退化为全表扫描。 +2. **不支持排序:** 同理,因为哈希值是无序的,所以我们无法利用哈希索引来优化 `ORDER BY` 子句。 +3. **不支持部分索引键查询:** 对于联合索引,比如`(col1, col2)`,哈希索引必须使用所有索引列进行查询,它无法单独利用 `col1` 来加速查询。 +4. **哈希冲突问题:** 当不同的键产生相同的哈希值时,需要额外的链表或开放寻址来解决,这会降低性能。 + +鉴于数据库查询中范围查询和排序是极其常见的操作,一个不支持这些功能的索引结构,显然不能作为默认的、通用的索引类型。 + +### 为什么 InnoDB 没有使用 B 树作为索引的数据结构? + +B 树和 B+树都是优秀的多路平衡搜索树,非常适合磁盘存储,因为它们都很“矮胖”,能最大化地利用每一次磁盘 I/O。 + +但 B+树是 B 树的一个增强版,它针对数据库场景做了几个关键优化: + +1. **I/O 效率更高:** 在 B+树中,只有叶子节点才存储数据(或数据指针),而非叶子节点只存储索引键。因为非叶子节点不存数据,所以它们可以容纳更多的索引键。这意味着 B+树的“扇出”更大,在同样的数据量下,B+树通常会比 B 树更矮,也就意味着查找数据所需的磁盘 I/O 次数更少。 +2. **查询性能更稳定:** 在 B+树中,任何一次查询都必须从根节点走到叶子节点才能找到数据,所以查询路径的长度是固定的。而在 B 树中,如果运气好,可能在非叶子节点就找到了数据,但运气不好也得走到叶子,这导致查询性能不稳定。 +3. **对范围查询极其友好:** 这是 B+树最核心的优势。它的所有叶子节点之间通过一个双向链表连接。当我们执行一个范围查询(比如 `WHERE id > 100`)时,只需要通过树形结构找到 `id=100` 的叶子节点,然后就可以沿着链表向后顺序扫描,而无需再回溯到上层节点。这使得范围查询的效率大大提高。 + +### 什么是覆盖索引? + +如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 **覆盖索引(Covering Index)**。 -MySQL 索引相关的问题比较多,对于面试和工作都比较重要,于是,我单独抽了一篇文章专门来总结 MySQL 索引相关的知识点和问题:[MySQL 索引详解](./mysql-index.md) 。 +在 InnoDB 存储引擎中,非主键索引的叶子节点包含的是主键的值。这意味着,当使用非主键索引进行查询时,数据库会先找到对应的主键值,然后再通过主键索引来定位和检索完整的行数据。这个过程被称为“回表”。 + +**覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。** + +### 请解释一下 MySQL 的联合索引及其最左前缀原则 + +使用表中的多个字段创建索引,就是 **联合索引**,也叫 **组合索引** 或 **复合索引**。 + +以 `score` 和 `name` 两个字段建立联合索引: + +```sql +ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name); +``` + +最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据索引中的字段顺序,从左到右依次匹配查询条件中的字段。如果查询条件与索引中的最左侧字段相匹配,那么 MySQL 就会使用索引来过滤数据,这样可以提高查询效率。 + +最左匹配原则会一直向右匹配,直到遇到范围查询(如 >、<)为止。对于 >=、<=、BETWEEN 以及前缀匹配 LIKE 的范围查询,不会停止匹配(相关阅读:[联合索引的最左匹配原则全网都在说的一个错误结论](https://mp.weixin.qq.com/s/8qemhRg5MgXs1So5YCv0fQ))。 + +假设有一个联合索引 `(column1, column2, column3)`,其从左到右的所有前缀为 `(column1)`、`(column1, column2)`、`(column1, column2, column3)`(创建 1 个联合索引相当于创建了 3 个索引),包含这些列的所有查询都会走索引而不会全表扫描。 + +我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。 + +我们这里简单演示一下最左前缀匹配的效果。 + +1、创建一个名为 `student` 的表,这张表只有 `id`、`name`、`class` 这 3 个字段。 + +```sql +CREATE TABLE `student` ( + `id` int NOT NULL, + `name` varchar(100) DEFAULT NULL, + `class` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `name_class_idx` (`name`,`class`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +``` + +2、下面我们分别测试三条不同的 SQL 语句。 + +![](https://oss.javaguide.cn/github/javaguide/database/mysql/leftmost-prefix-matching-rule.png) + +```sql +# 可以命中索引 +SELECT * FROM student WHERE name = 'Anne Henry'; +EXPLAIN SELECT * FROM student WHERE name = 'Anne Henry' AND class = 'lIrm08RYVk'; +# 无法命中索引 +SELECT * FROM student WHERE class = 'lIrm08RYVk'; +``` + +再来看一个常见的面试题:如果有索引 `联合索引(a,b,c)`,查询 `a=1 AND c=1` 会走索引么?`c=1` 呢?`b=1 AND c=1` 呢? `b = 1 AND a = 1 AND c = 1` 呢? + +先不要往下看答案,给自己 3 分钟时间想一想。 + +1. 查询 `a=1 AND c=1`:根据最左前缀匹配原则,查询可以使用索引的前缀部分。因此,该查询仅在 `a=1` 上使用索引,然后对结果进行 `c=1` 的过滤。 +2. 查询 `c=1`:由于查询中不包含最左列 `a`,根据最左前缀匹配原则,整个索引都无法被使用。 +3. 查询 `b=1 AND c=1`:和第二种一样的情况,整个索引都不会使用。 +4. 查询 `b=1 AND a=1 AND c=1`:这个查询是可以用到索引的。查询优化器分析 SQL 语句时,对于联合索引,会对查询条件进行重排序,以便用到索引。会将 `b=1` 和 `a=1` 的条件进行重排序,变成 `a=1 AND b=1 AND c=1`。 + +MySQL 8.0.13 版本引入了索引跳跃扫描(Index Skip Scan,简称 ISS),它可以在某些索引查询场景下提高查询效率。在没有 ISS 之前,不满足最左前缀匹配原则的联合索引查询中会执行全表扫描。而 ISS 允许 MySQL 在某些情况下避免全表扫描,即使查询条件不符合最左前缀。不过,这个功能比较鸡肋, 和 Oracle 中的没法比,MySQL 8.0.31 还报告了一个 bug:[Bug #109145 Using index for skip scan cause incorrect result](https://bugs.mysql.com/bug.php?id=109145)(后续版本已经修复)。个人建议知道有这个东西就好,不需要深究,实际项目也不一定能用上。 + +### SELECT \* 会导致索引失效吗? + +`SELECT *` 不会直接导致索引失效(如果不走索引大概率是因为 where 查询范围过大导致的),但它可能会带来一些其他的性能问题比如造成网络传输和数据处理的浪费、无法使用索引覆盖。 + +### 哪些字段适合创建索引? + +- **不为 NULL 的字段**:索引字段的数据应该尽量不为 NULL,因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL,建议使用 0,1,true,false 这样语义较为清晰的短值或短字符作为替代。 +- **被频繁查询的字段**:我们创建索引的字段应该是查询操作非常频繁的字段。 +- **被作为条件查询的字段**:被作为 WHERE 条件查询的字段,应该被考虑建立索引。 +- **频繁需要排序的字段**:索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。 +- **被经常频繁用于连接的字段**:经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。 + +### 索引失效的原因有哪些? + +1. 创建了组合索引,但查询条件未遵守最左匹配原则; +2. 在索引列上进行计算、函数、类型转换等操作; +3. 以 % 开头的 LIKE 查询比如 `LIKE '%abc';`; +4. 查询条件中使用 OR,且 OR 的前后条件中有一个列没有索引,涉及的索引都不会被使用到; +5. IN 的取值范围较大时会导致索引失效,走全表扫描(NOT IN 和 IN 的失效场景相同); +6. 发生[隐式转换](https://javaguide.cn/database/mysql/index-invalidation-caused-by-implicit-conversion.html "隐式转换"); ## MySQL 查询缓存 @@ -429,26 +597,17 @@ MySQL 5.6 开始,查询缓存已默认禁用。MySQL 8.0 开始,已经不再 ![MySQL 8.0: Retiring Support for the Query Cache](https://oss.javaguide.cn/github/javaguide/mysql/mysql8.0-retiring-support-for-the-query-cache.png) -## MySQL 日志 - -MySQL 日志常见的面试题有: - -- MySQL 中常见的日志有哪些? -- 慢查询日志有什么用? -- binlog 主要记录了什么? -- redo log 如何保证事务的持久性? -- 页修改之后为什么不直接刷盘呢? -- binlog 和 redolog 有什么区别? -- undo log 如何保证事务的原子性? -- …… +## ⭐️MySQL 日志 -上诉问题的答案可以在[《Java 面试指北》(付费)](../../zhuanlan/java-mian-shi-zhi-bei.md) 的 **「技术面试题篇」** 中找到。 +上诉问题的答案可以在[《Java 面试指北》(付费,点击链接领取优惠卷)](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) 的 **「技术面试题篇」** 中找到。 ![《Java 面试指北》技术面试题篇](https://oss.javaguide.cn/javamianshizhibei/technical-interview-questions.png) -## MySQL 事务 +文章地址: (密码获取:)。 + +## ⭐️MySQL 事务 -### 何谓事务? +### 什么是事务? 我们设想一个场景,这个场景中我们需要插入多条相关联的数据到数据库,不幸的是,这个过程可能会遇到下面这些问题: @@ -470,7 +629,7 @@ MySQL 日志常见的面试题有: ![事务示意图](https://oss.javaguide.cn/github/javaguide/mysql/%E4%BA%8B%E5%8A%A1%E7%A4%BA%E6%84%8F%E5%9B%BE.png) -### 何谓数据库事务? +### 什么是数据库事务? 大多数情况下,我们在谈论事务的时候,如果没有特指**分布式事务**,往往指的就是**数据库事务**。 @@ -527,7 +686,7 @@ COMMIT; 例如:事务 1 读取某表中的数据 A=20,事务 1 修改 A=A-1,事务 2 读取到 A = 19,事务 1 回滚导致对 A 的修改并未提交到数据库, A 的值还是 20。 -![脏读](./images/concurrency-consistency-issues-dirty-reading.png) +![脏读](https://oss.javaguide.cn/github/javaguide/database/mysql/concurrency-consistency-issues-dirty-reading.png) #### 丢失修改(Lost to modify) @@ -535,7 +694,7 @@ COMMIT; 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 先修改 A=A-1,事务 2 后来也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。 -![丢失修改](./images/concurrency-consistency-issues-missing-modifications.png) +![丢失修改](https://oss.javaguide.cn/github/javaguide/database/mysql/concurrency-consistency-issues-missing-modifications.png) #### 不可重复读(Unrepeatable read) @@ -543,7 +702,7 @@ COMMIT; 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 再次读取 A =19,此时读取的结果和第一次读取的结果不同。 -![不可重复读](./images/concurrency-consistency-issues-unrepeatable-read.png) +![不可重复读](https://oss.javaguide.cn/github/javaguide/database/mysql/concurrency-consistency-issues-unrepeatable-read.png) #### 幻读(Phantom read) @@ -551,7 +710,7 @@ COMMIT; 例如:事务 2 读取某个范围的数据,事务 1 在这个范围插入了新的数据,事务 2 再次读取这个范围的数据发现相比于第一次读取的结果多了新的数据。 -![幻读](./images/concurrency-consistency-issues-phantom-read.png) +![幻读](https://oss.javaguide.cn/github/javaguide/database/mysql/concurrency-consistency-issues-phantom-read.png) ### 不可重复读和幻读有什么区别? @@ -653,8 +812,6 @@ InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL Inno **在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。** -一些大厂面试中可能会问到 Next-Key Lock 的加锁范围,这里推荐一篇文章:[MySQL next-key lock 加锁范围是什么? - 程序员小航 - 2021](https://segmentfault.com/a/1190000040129107) 。 - ### 共享锁和排他锁呢? 不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类: @@ -782,7 +939,7 @@ CREATE TABLE `sequence_id` ( 最后,再推荐一篇文章:[为什么 MySQL 的自增主键不单调也不连续](https://draveness.me/whys-the-design-mysql-auto-increment/) 。 -## MySQL 性能优化 +## ⭐️MySQL 性能优化 关于 MySQL 性能优化的建议总结,请看这篇文章:[MySQL 高性能优化规范建议总结](./mysql-high-performance-optimization-specification-recommendations.md) 。 @@ -798,8 +955,6 @@ CREATE TABLE `sequence_id` ( **数据库只存储文件地址信息,文件由文件存储服务负责存储。** -相关阅读:[Spring Boot 整合 MinIO 实现分布式文件服务](https://www.51cto.com/article/716978.html) 。 - ### MySQL 如何存储 IP 地址? 可以将 IP 地址转换成整形数据存储,性能更好,占用空间也更小。 @@ -813,10 +968,12 @@ MySQL 提供了两个方法来处理 ip 地址 ### 有哪些常见的 SQL 优化手段? -[《Java 面试指北》(付费)](../../zhuanlan/java-mian-shi-zhi-bei.md) 的 **「技术面试题篇」** 有一篇文章详细介绍了常见的 SQL 优化手段,非常全面,清晰易懂! +[《Java 面试指北》(付费)](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) 的 **「技术面试题篇」** 有一篇文章详细介绍了常见的 SQL 优化手段,非常全面,清晰易懂! ![常见的 SQL 优化手段](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-sql-optimization.png) +文章地址:https://www.yuque.com/snailclimb/mf2z3k/abc2sv (密码获取:)。 + ### 如何分析 SQL 的性能? 我们可以使用 `EXPLAIN` 命令来分析 SQL 的 **执行计划** 。执行计划是指一条 SQL 语句在经过 MySQL 查询优化器的优化会后,具体的执行方式。 diff --git a/docs/database/mysql/transaction-isolation-level.md b/docs/database/mysql/transaction-isolation-level.md index 8b706640ea6..6009d9dbd80 100644 --- a/docs/database/mysql/transaction-isolation-level.md +++ b/docs/database/mysql/transaction-isolation-level.md @@ -3,6 +3,13 @@ title: MySQL事务隔离级别详解 category: 数据库 tag: - MySQL +head: + - - meta + - name: keywords + content: 事务,隔离级别,读未提交,读已提交,可重复读,可串行化,MVCC,锁 + - - meta + - name: description + content: 梳理四大事务隔离级别与并发现象,结合 InnoDB 的 MVCC 与锁机制,明确幻读/不可重复读的应对策略。 --- > 本文由 [SnailClimb](https://github.com/Snailclimb) 和 [guang19](https://github.com/guang19) 共同完成。 diff --git a/docs/database/nosql.md b/docs/database/nosql.md index d5ca59698bd..53c67c32f18 100644 --- a/docs/database/nosql.md +++ b/docs/database/nosql.md @@ -5,6 +5,13 @@ tag: - NoSQL - MongoDB - Redis +head: + - - meta + - name: keywords + content: NoSQL,键值,文档,列族,图数据库,分布式,扩展性,数据模型 + - - meta + - name: description + content: 总结 NoSQL 的分类与特性,对比关系型数据库,结合分布式与扩展性场景,指导模型与选型。 --- ## NoSQL 是什么? diff --git a/docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md b/docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md index 7ad88958704..934762514d2 100644 --- a/docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md +++ b/docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md @@ -3,6 +3,13 @@ title: 3种常用的缓存读写策略详解 category: 数据库 tag: - Redis +head: + - - meta + - name: keywords + content: 缓存读写策略,Cache Aside,Read Through,Write Through,Write Behind,Write Back,缓存一致性,缓存失效,旁路缓存,读写穿透,异步缓存写入,Redis缓存策略,缓存更新策略 + - - meta + - name: description + content: 深入对比 Cache Aside、Read/Write Through、Write Behind 三种缓存读写策略,附详细时序图、一致性问题分析及生产级解决方案,Redis 实战必备! --- 看到很多小伙伴简历上写了“**熟练使用缓存**”,但是被我问到“**缓存常用的 3 种读写策略**”的时候却一脸懵逼。 @@ -15,26 +22,28 @@ tag: ### Cache Aside Pattern(旁路缓存模式) -**Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。** +这是我们日常开发中**最常用、最经典**的一种模式,几乎是互联网应用缓存方案的事实标准,尤其适合**读多写少**的业务场景。 -Cache Aside Pattern 中服务端需要同时维系 db 和 cache,并且是以 db 的结果为准。 +这个模式之所以被称为**“旁路”(Aside)**,是因为应用程序的**写操作完全绕过了缓存,直接操作数据库**。 + +应用程序扮演了数据流转的“指挥官”,需要同时维护 Cache 和 DB 两个数据源。 下面我们来看一下这个策略模式下的缓存读写步骤。 -**写**: +**写操作 :** -- 先更新 db -- 然后直接删除 cache 。 +1. 应用**先更新 DB**。 +2. 然后**直接删除 Cache**中对应的数据。 简单画了一张图帮助大家理解写的步骤。 ![](https://oss.javaguide.cn/github/javaguide/database/redis/cache-aside-write.png) -**读** : +**读操作:** -- 从 cache 中读取数据,读取到就直接返回 -- cache 中读取不到的话,就从 db 中读取数据返回 -- 再把数据放到 cache 中。 +1. 应用先从 Cache 读取数据。 +2. 如果命中(Hit),则直接返回。 +3. 如果未命中(Miss),则从 DB 读取数据,成功读取后,**将数据写回 Cache**,然后返回。 简单画了一张图帮助大家理解读的步骤。 @@ -42,49 +51,69 @@ Cache Aside Pattern 中服务端需要同时维系 db 和 cache,并且是以 d 你仅仅了解了上面这些内容的话是远远不够的,我们还要搞懂其中的原理。 -比如说面试官很可能会追问:“**在写数据的过程中,可以先删除 cache ,后更新 db 么?**” +比如说面试官很可能会追问: + +1. 为什么写操作是“先更新 DB,后删除 Cache”?顺序能反过来吗? +2. 那“先更新 DB,后删除 Cache”就绝对安全吗? +3. 为什么是“删除 Cache”,而不是“更新 Cache”? -**答案:** 那肯定是不行的!因为这样可能会造成 **数据库(db)和缓存(Cache)数据不一致**的问题。 +接下来我会以此分析解答这些问题。 -举例:请求 1 先写数据 A,请求 2 随后读数据 A 的话,就很有可能产生数据不一致性的问题。 +**1. 为什么写操作是“先更新 DB,后删除 Cache”?顺序能反过来吗?** -这个过程可以简单描述为: +**答:** 绝对不能。如果“先删 Cache,后更新 DB”,在高并发下会引入经典的数据不一致问题。 -> 请求 1 先把 cache 中的 A 数据删除 -> 请求 2 从 db 中读取数据->请求 1 再把 db 中的 A 数据更新 +- **时序分析 (请求 A 写, 请求 B 读):** + 1. 请求 A: 先将 Cache 中的数据删除。 + 2. 请求 B: 此时发现 Cache 为空,于是去 DB 读取**旧值**,并准备写入 Cache。 + 3. 请求 A : 将**新值**写入 DB。 + 4. 请求 B: 将之前读到的**旧值**写入了 Cache。 +- **结果:** DB 中是新值,而 Cache 中是旧值,数据不一致。 -当你这样回答之后,面试官可能会紧接着就追问:“**在写数据的过程中,先更新 db,后删除 cache 就没有问题了么?**” +**2. 那“先更新 DB,后删除 Cache”就绝对安全吗?** -**答案:** 理论上来说还是可能会出现数据不一致性的问题,不过概率非常小,因为缓存的写入速度是比数据库的写入速度快很多。 +**答案:** 也不是绝对安全的!因为这样也可能会造成 **数据库和缓存数据不一致**的问题。 -举例:请求 1 先读数据 A,请求 2 随后写数据 A,并且数据 A 在请求 1 请求之前不在缓存中的话,也有可能产生数据不一致性的问题。 +- **时序分析 (请求 A 读, 请求 B 写):** + 1. 请求 A : 缓存未命中,从 DB 读取到**旧值**。 + 2. 请求 B: 迅速完成了 DB 的更新,并将 Cache 删除。 + 3. 请求 A : 将自己之前拿到的**旧值**写入了 Cache。 +- **结果:** DB 中是新值,Cache 中又是旧值。 +- **为什么概率极小?** 这个问题本质上是一个并发时序问题:只要“读 DB → 写 Cache”这段时间窗口内,恰好有写请求完成了 DB 更新,就有可能产生不一致。在大多数业务里,这个窗口时间相对较短,而且还需要与写请求并发“撞车”,所以发生概率不算高,但绝不是不可能。 -这个过程可以简单描述为: +**3. 为什么是“删除 Cache”,而不是“更新 Cache”?** -> 请求 1 从 db 读数据 A-> 请求 2 更新 db 中的数据 A(此时缓存中无数据 A ,故不用执行删除缓存操作 ) -> 请求 1 将数据 A 写入 cache +- **性能开销:** 写操作往往只更新了对象的部分字段,如果为了“更新 Cache”而去重新查询或计算整个缓存对象,开销可能很大。相比之下,“删除”是一个轻量级操作。 +- **懒加载思想:** “删除”操作遵循懒加载原则。只有当数据下一次被真正需要(被读取)时,才触发从 DB 加载并写入缓存,避免了无效的缓存更新。 +- **并发安全:** “更新缓存”在高并发下可能出现更新顺序错乱的问题导致脏数据的概率会更大。 + +当然,这一切都建立在一个重要的前提之上:我们缓存的数据,是可以通过数据库进行确定性重建的,并且业务上可以容忍从‘缓存删除’到‘下一次读取并回填’之间这个极短时间窗口内的数据不一致。 现在我们再来分析一下 **Cache Aside Pattern 的缺陷**。 -**缺陷 1:首次请求数据一定不在 cache 的问题** +**缺陷 1:首次请求数据一定不在 Cache 的问题** -解决办法:可以将热点数据可以提前放入 cache 中。 +解决办法:对于访问量巨大的热点数据,可以在系统启动或低峰期进行缓存预热。 -**缺陷 2:写操作比较频繁的话导致 cache 中的数据会被频繁被删除,这样会影响缓存命中率 。** +**缺陷 2:写操作比较频繁的话导致 Cache 中的数据会被频繁被删除,这样会影响缓存命中率 。** 解决办法: -- 数据库和缓存数据强一致场景:更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。 -- 可以短暂地允许数据库和缓存数据不一致的场景:更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。 +- 数据库和缓存数据强一致场景:更新 DB 的时候同样更新 Cache,不过我们需要加一个锁/分布式锁来保证更新 Cache 的时候不存在线程安全问题。 +- 可以短暂地允许数据库和缓存数据不一致的场景:更新 DB 的时候同样更新 Cache,但是给缓存加一个比较短的过期时间(如 1 分钟),这样的话就可以保证即使数据不一致的话影响也比较小。 ### Read/Write Through Pattern(读写穿透) -Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责。 +在这种模式下,应用程序将**Cache 视为唯一的、主要的存储**。所有的读写请求都直接打向 Cache,而 Cache 服务自身负责与 DB 进行数据同步。 + +对应用程序**透明**,应用开发者无需关心 DB 的存在。 -这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能。 +这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 本身并没有提供 Cache 将数据写入 DB 的功能,需要我们在业务侧或中间件里自己实现。 **写(Write Through):** -- 先查 cache,cache 中不存在,直接更新 db。 -- cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(**同步更新 cache 和 db**)。 +- 先查 Cache,Cache 中不存在,直接更新 DB。 +- Cache 中存在,则先更新 Cache,然后 Cache 服务自己更新 DB。只有当 Cache 和 DB 都写入成功后,才向上层返回成功。 简单画了一张图帮助大家理解写的步骤。 @@ -92,27 +121,38 @@ Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从 **读(Read Through):** -- 从 cache 中读取数据,读取到就直接返回 。 -- 读取不到的话,先从 db 加载,写入到 cache 后返回响应。 +- 应用从 Cache 读取数据。 +- 如果命中,直接返回。 +- 如果未命中,由**Cache 服务自己**负责从 DB 加载数据,加载成功后先写入自身,再返回给应用。 简单画了一张图帮助大家理解读的步骤。 ![](https://oss.javaguide.cn/github/javaguide/database/redis/read-through.png) -Read-Through Pattern 实际只是在 Cache-Aside Pattern 之上进行了封装。在 Cache-Aside Pattern 下,发生读请求的时候,如果 cache 中不存在对应的数据,是由客户端自己负责把数据写入 cache,而 Read Through Pattern 则是 cache 服务自己来写入缓存的,这对客户端是透明的。 +Read-Through 实际只是在 Cache-Aside 之上进行了封装。在 Cache-Aside 下,发生读请求的时候,如果 Cache 中不存在对应的数据,是由客户端自己负责把数据写入 Cache,而 Read Through 则是 Cache 服务自己来写入缓存的,这对客户端是透明的。 -和 Cache Aside Pattern 一样, Read-Through Pattern 也有首次请求数据一定不再 cache 的问题,对于热点数据可以提前放入缓存中。 +从实现角度看,Read-Through 本质上是把 Cache-Aside 中“读 Miss → 读 DB → 回填 Cache”的逻辑,下沉到了缓存服务内部,对客户端透明。 + +和 Cache Aside 一样, Read-Through 也有首次请求数据一定不再 Cache 的问题,对于热点数据可以提前放入缓存中。 ### Write Behind Pattern(异步缓存写入) -Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 db 的读写。 +Write Behind(也常被称为 Write-Back) Pattern 和 Read/Write Through Pattern 很相似,两者都是由 Cache 服务来负责 Cache 和 DB 的读写。 + +但是,两个又有很大的不同:**Read/Write Through 是同步更新 Cache 和 DB,而 Write Behind 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。** + +**写操作 (Write Behind):** -但是,两个又有很大的不同:**Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。** +1. 应用将数据写入 Cache,然后**立即返回**。 +2. Cache 服务将这个写操作放入一个队列中。 +3. 通过一个独立的异步线程/任务,将队列中的写操作**批量地、合并地**写入 DB。 -很明显,这种方式对数据一致性带来了更大的挑战,比如 cache 数据可能还没异步更新 db 的话,cache 服务可能就就挂掉了。 +这种模式对数据一致性带来了挑战(例如:Cache 中的数据还没来得及写回 DB,系统就宕机了),因此不适用于需要强一致性的场景(如交易、库存)。 -这种策略在我们平时开发过程中也非常非常少见,但是不代表它的应用场景少,比如消息队列中消息的异步写入磁盘、MySQL 的 Innodb Buffer Pool 机制都用到了这种策略。 +但是,它的异步和批量特性,带来了**无与伦比的写性能**。它在很多高性能系统中都有广泛应用: -Write Behind Pattern 下 db 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。 +- **MySQL 的 InnoDB Buffer Pool 机制:** 数据修改先在内存 Buffer Pool 中完成,然后由后台线程异步刷写到磁盘。 +- **操作系统的页缓存(Page Cache):** 文件写入也是先写到内存,再由操作系统异步刷盘。 +- **高频计数场景:** 对于文章浏览量、帖子点赞数这类允许短暂数据不一致、但写入极其频繁的场景,可以先在 Redis 中快速累加,再通过定时任务异步同步回数据库。 diff --git a/docs/database/redis/cache-basics.md b/docs/database/redis/cache-basics.md index 391e5bec82d..9f99fdf4ba6 100644 --- a/docs/database/redis/cache-basics.md +++ b/docs/database/redis/cache-basics.md @@ -3,6 +3,13 @@ title: 缓存基础常见面试题总结(付费) category: 数据库 tag: - Redis +head: + - - meta + - name: keywords + content: 缓存面试,一致性,淘汰策略,穿透,雪崩,热点,架构 + - - meta + - name: description + content: 收录缓存基础与架构高频题,涵盖一致性与淘汰策略、穿透/雪崩等问题与治理方案,构建系统复习清单。 --- **缓存基础** 相关的面试题为我的 [知识星球](../../about-the-author/zhishixingqiu-two-years.md)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](../../zhuanlan/java-mian-shi-zhi-bei.md)中。 @@ -10,5 +17,3 @@ tag: ![](https://oss.javaguide.cn/javamianshizhibei/database-questions.png) - - diff --git a/docs/database/redis/images/why-redis-so-fast.png b/docs/database/redis/images/why-redis-so-fast.png deleted file mode 100644 index 279e1955473..00000000000 Binary files a/docs/database/redis/images/why-redis-so-fast.png and /dev/null differ diff --git a/docs/database/redis/redis-cluster.md b/docs/database/redis/redis-cluster.md index e3ef2efd04c..ff55913bca4 100644 --- a/docs/database/redis/redis-cluster.md +++ b/docs/database/redis/redis-cluster.md @@ -10,5 +10,3 @@ tag: ![](https://oss.javaguide.cn/xingqiu/mianshizhibei-database.png) - - diff --git a/docs/database/redis/redis-delayed-task.md b/docs/database/redis/redis-delayed-task.md index 35f9304321f..063bbca8c31 100644 --- a/docs/database/redis/redis-delayed-task.md +++ b/docs/database/redis/redis-delayed-task.md @@ -3,6 +3,13 @@ title: 如何基于Redis实现延时任务 category: 数据库 tag: - Redis +head: + - - meta + - name: keywords + content: Redis,延时任务,过期事件,Redisson,DelayedQueue,可靠性,一致性 + - - meta + - name: description + content: 对比 Redis 过期事件与 Redisson 延时队列两种方案,分析可靠性与一致性权衡,给出工程选型建议。 --- 基于 Redis 实现延时任务的功能无非就下面两种方案: diff --git a/docs/database/redis/redis-memory-fragmentation.md b/docs/database/redis/redis-memory-fragmentation.md index cb2da7476d1..18b915bce1b 100644 --- a/docs/database/redis/redis-memory-fragmentation.md +++ b/docs/database/redis/redis-memory-fragmentation.md @@ -3,6 +3,13 @@ title: Redis内存碎片详解 category: 数据库 tag: - Redis +head: + - - meta + - name: keywords + content: Redis,内存碎片,分配器,内存管理,内存占用,优化 + - - meta + - name: description + content: 解析 Redis 内存碎片的成因与影响,结合分配器与内存管理策略,给出观测与优化方向,降低资源浪费。 --- ## 什么是内存碎片? diff --git a/docs/database/redis/redis-persistence.md b/docs/database/redis/redis-persistence.md index c17fe7db316..2b61a1250ad 100644 --- a/docs/database/redis/redis-persistence.md +++ b/docs/database/redis/redis-persistence.md @@ -71,7 +71,7 @@ AOF 持久化功能的实现可以简单分为 5 步: 1. **命令追加(append)**:所有的写命令会追加到 AOF 缓冲区中。 2. **文件写入(write)**:将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用`write`函数(系统调用),`write`将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。 -3. **文件同步(fsync)**:AOF 缓冲区根据对应的持久化方式( `fsync` 策略)向硬盘做同步操作。这一步需要调用 `fsync` 函数(系统调用), `fsync` 针对单个文件操作,对其进行强制硬盘同步,`fsync` 将阻塞直到写入磁盘完成后返回,保证了数据持久化。 +3. **文件同步(fsync)**:这一步才是持久化的核心!根据你在 `redis.conf` 文件里 `appendfsync` 配置的策略,Redis 会在不同的时机,调用 `fsync` 函数(系统调用)。`fsync` 针对单个文件操作,对其进行强制硬盘同步(文件在内核缓冲区里的数据写到硬盘),`fsync` 将阻塞直到写入磁盘完成后返回,保证了数据持久化。 4. **文件重写(rewrite)**:随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。 5. **重启加载(load)**:当 Redis 重启时,可以加载 AOF 文件进行数据恢复。 @@ -90,9 +90,9 @@ AOF 工作流程图如下: 在 Redis 的配置文件中存在三种不同的 AOF 持久化方式( `fsync`策略),它们分别是: -1. `appendfsync always`:主线程调用 `write` 执行写操作后,后台线程( `aof_fsync` 线程)立即会调用 `fsync` 函数同步 AOF 文件(刷盘),`fsync` 完成后线程返回,这样会严重降低 Redis 的性能(`write` + `fsync`)。 -2. `appendfsync everysec`:主线程调用 `write` 执行写操作后立即返回,由后台线程( `aof_fsync` 线程)每秒钟调用 `fsync` 函数(系统调用)同步一次 AOF 文件(`write`+`fsync`,`fsync`间隔为 1 秒) -3. `appendfsync no`:主线程调用 `write` 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(`write`但不`fsync`,`fsync` 的时机由操作系统决定)。 +1. `appendfsync always`:主线程调用 `write` 执行写操作后,会立刻调用 `fsync` 函数同步 AOF 文件(刷盘)。主线程会阻塞,直到 `fsync` 将数据完全刷到磁盘后才会返回。这种方式数据最安全,理论上不会有任何数据丢失。但因为每个写操作都会同步阻塞主线程,所以性能极差。 +2. `appendfsync everysec`:主线程调用 `write` 执行写操作后立即返回,由后台线程( `aof_fsync` 线程)每秒钟调用 `fsync` 函数(系统调用)同步一次 AOF 文件(`write`+`fsync`,`fsync`间隔为 1 秒)。这种方式主线程的性能基本不受影响。在性能和数据安全之间做出了绝佳的平衡。不过,在 Redis 异常宕机时,最多可能丢失最近 1 秒内的数据。 +3. `appendfsync no`:主线程调用 `write` 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(`write`但不`fsync`,`fsync` 的时机由操作系统决定)。 这种方式性能最好,因为避免了 `fsync` 的阻塞。但数据安全性最差,宕机时丢失的数据量不可控,取决于操作系统上一次同步的时间点。 可以看出:**这 3 种持久化方式的主要区别在于 `fsync` 同步 AOF 文件的时机(刷盘)**。 diff --git a/docs/database/redis/redis-questions-01.md b/docs/database/redis/redis-questions-01.md index 7102985b9a5..9302c744e45 100644 --- a/docs/database/redis/redis-questions-01.md +++ b/docs/database/redis/redis-questions-01.md @@ -6,10 +6,10 @@ tag: head: - - meta - name: keywords - content: Redis基础,Redis常见数据结构,Redis线程模型,Redis内存管理,Redis事务,Redis性能优化 + content: Redis面试题,Redis基础,Redis数据结构,Redis线程模型,Redis持久化,Redis内存管理,Redis性能优化,Redis分布式锁,Redis消息队列,Redis延时队列,Redis缓存策略,Redis单线程,Redis多线程,Redis过期策略,Redis淘汰策略 - - meta - name: description - content: 一篇文章总结Redis常见的知识点和面试题,涵盖Redis基础、Redis常见数据结构、Redis线程模型、Redis内存管理、Redis事务、Redis性能优化等内容。 + content: 最新Redis面试题总结(上):深入讲解Redis基础、五大常用数据结构、单线程模型原理、持久化机制、内存淘汰与过期策略、分布式锁与消息队列实现。适合准备后端面试的开发者! --- @@ -32,7 +32,7 @@ Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两 全世界有非常多的网站使用到了 Redis,[techstacks.io](https://techstacks.io/) 专门维护了一个[使用 Redis 的热门站点列表](https://techstacks.io/tech/redis),感兴趣的话可以看看。 -### Redis 为什么这么快? +### ⭐️Redis 为什么这么快? Redis 内部做了非常多的性能优化,比较重要的有下面 4 点: @@ -43,7 +43,7 @@ Redis 内部做了非常多的性能优化,比较重要的有下面 4 点: > 下面这张图片总结的挺不错的,分享一下,出自 [Why is Redis so fast?](https://twitter.com/alexxubyte/status/1498703822528544770)。 -![why-redis-so-fast](./images/why-redis-so-fast.png) +![why-redis-so-fast](https://oss.javaguide.cn/github/javaguide/database/redis/why-redis-so-fast.png) 那既然都这么快了,为什么不直接用 Redis 当主数据库呢?主要是因为内存成本太高,并且 Redis 提供的数据持久化仍然有数据丢失的风险。 @@ -96,7 +96,7 @@ PS:篇幅问题,我这并没有对上面提到的分布式缓存选型做详 相信看了上面的对比之后,我们已经没有什么理由可以选择使用 Memcached 来作为自己项目的分布式缓存了。 -### 为什么要用 Redis? +### ⭐️为什么要用 Redis? **1、访问速度更快** @@ -114,7 +114,7 @@ PS:篇幅问题,我这并没有对上面提到的分布式缓存选型做详 Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、消息队列、延时队列等场景,功能强大! -### 为什么用 Redis 而不用本地缓存呢? +### ⭐️为什么用 Redis 而不用本地缓存呢? | 特性 | 本地缓存 | Redis | | ------------ | ------------------------------------ | -------------------------------- | @@ -147,7 +147,7 @@ Redis 从 4.0 版本开始,支持通过 Module 来扩展其功能以满足特 关于 Redis 模块的详细介绍,可以查看官方文档:。 -## Redis 应用 +## ⭐️Redis 应用 ### Redis 除了做缓存,还能做什么? @@ -304,7 +304,7 @@ Redisson 内置的延时队列具备下面这些优势: 关于 Redis 实现延时任务的详细介绍,可以看我写的这篇文章:[如何基于 Redis 实现延时任务?](./redis-delayed-task.md)。 -## Redis 数据类型 +## ⭐️Redis 数据类型 关于 Redis 5 种基础数据类型和 3 种特殊数据类型的详细介绍请看下面这两篇文章以及 [Redis 官方文档](https://redis.io/docs/data-types/): @@ -463,7 +463,7 @@ Redis 中有一个叫做 `Sorted Set`(有序集合)的数据类型经常被 - 红黑树 vs 跳表:相比较于红黑树来说,跳表的实现也更简单一些,不需要通过旋转和染色(红黑变换)来保证黑平衡。并且,按照区间来查找数据这个操作,红黑树的效率没有跳表高。 - B+ 树 vs 跳表:B+ 树更适合作为数据库和文件系统中常用的索引结构之一,它的核心思想是通过可能少的 IO 定位到尽可能多的索引来获得查询数据。对于 Redis 这种内存数据库来说,它对这些并不感冒,因为 Redis 作为内存数据库它不可能存储大量的数据,所以对于索引不需要通过 B+ 树这种方式进行维护,只需按照概率进行随机维护即可,节约内存。而且使用跳表实现 zset 时相较前者来说更简单一些,在进行插入时只需通过索引将数据插入到链表中合适的位置再随机维护一定高度的索引即可,也不需要像 B+ 树那样插入时发现失衡时还需要对节点分裂与合并。 -另外,我还单独写了一篇文章从有序集合的基本使用到跳表的源码分析和实现,让你会对 Redis 的有序集合底层实现的跳表有着更深刻的理解和掌握:[Redis 为什么用跳表实现有序集合](./redis-skiplist.md)。 +另外,我还单独写了一篇文章从有序集合的基本使用到跳表的源码分析和实现,让你会对 Redis 的有序集合底层实现的跳表有着更深刻的理解和掌握:[Redis 为什么用跳表实现有序集合](https://javaguide.cn/database/redis/redis-skiplist.html)。 ### Set 的应用场景是什么? @@ -522,6 +522,27 @@ Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap,只 (integer) 2 ``` +### HyperLogLog 适合什么场景? + +HyperLogLog (HLL) 是一种非常巧妙的概率性数据结构,它专门解决一类非常棘手的大数据问题:在海量数据中,用极小的内存,估算一个集合中不重复元素的数量,也就是我们常说的基数(Cardinality) + +HLL 做的最核心的权衡,就是用一点点精确度的损失,来换取巨大的内存空间节省。它给出的不是一个 100%精确的数字,而是一个带有很小标准误差(Redis 中默认是 0.81%)的近似值。 + +**基于这个核心权衡,HyperLogLog 最适合以下特征的场景:** + +1. **数据量巨大,内存敏感:** 这是 HLL 的主战场。比如,要统计一个亿级日活 App 的每日独立访客数。如果用传统的 Set 来存储用户 ID,一个 ID 占几十个字节,上亿个 ID 可能需要几个 GB 甚至几十 GB 的内存,这在很多场景下是不可接受的。而 HLL,在 Redis 中只需要固定的 12KB 内存,就能处理天文数字级别的基数,这是一个颠覆性的优势。 +2. **对结果的精确度要求不是 100%:** 这是使用 HLL 的前提。比如,产品经理想知道一个热门帖子的 UV(独立访客数)是大约 1000 万还是 1010 万,这个细微的差别通常不影响商业决策。但如果场景是统计一个交易系统的准确交易笔数,那 HLL 就完全不适用,因为金融场景要求 100%的精确。 + +**所以,HyperLogLog 具体的应用场景就非常清晰了:** + +- **网站/App 的 UV(Unique Visitor)统计:** 比如统计首页每天有多少个不同的 IP 或用户 ID 访问过。 +- **搜索引擎关键词统计:** 统计每天有多少个不同的用户搜索了某个关键词。 +- **社交网络互动统计:** 比如统计一条微博被多少个不同的用户转发过。 + +在这些场景下,我们关心的是数量级和趋势,而不是个位数的差异。 + +最后,Redis 的实现还非常智能,它内部会根据基数的大小,在**稀疏矩阵**(占用空间更小)和**稠密矩阵**(固定的 12KB)之间自动切换,进一步优化了内存使用。总而言之,当您需要对海量数据进行去重计数,并且可以接受微小误差时,HyperLogLog 就是不二之选。 + ### 使用 HyperLogLog 统计页面 UV 怎么做? 使用 HyperLogLog 统计页面 UV 主要需要用到下面这两个命令: @@ -541,11 +562,23 @@ PFADD PAGE_1:UV USER1 USER2 ...... USERn PFCOUNT PAGE_1:UV ``` -## Redis 持久化机制(重要) +### 如果我想判断一个元素是否不在海量元素集合中,用什么数据类型? + +这是布隆过滤器的经典应用场景。布隆过滤器可以告诉你一个元素一定不存在或者可能存在,它也有极高的空间效率和一定的误判率,但绝不会漏报。也就是说,布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。 + +Bloom Filter 的简单原理图如下: + +![Bloom Filter 的简单原理示意图](https://oss.javaguide.cn/github/javaguide/cs-basics/algorithms/bloom-filter-simple-schematic-diagram.png) + +当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后将对应的位数组的下标设置为 1(当位数组初始化时,所有位置均为 0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。 + +如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。 + +## ⭐️Redis 持久化机制(重要) Redis 持久化机制(RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持久化)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 Redis 持久化机制相关的知识点和问题:[Redis 持久化机制详解](https://javaguide.cn/database/redis/redis-persistence.html)。 -## Redis 线程模型(重要) +## ⭐️Redis 线程模型(重要) 对于读写命令来说,Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作,Redis 6.0 版本之后引入了多线程来处理网络请求(提高网络 IO 读写性能)。 @@ -577,8 +610,6 @@ Redis 通过 **IO 多路复用程序** 来监听来自客户端的大量连接 ![文件事件处理器(file event handler)](https://oss.javaguide.cn/github/javaguide/database/redis/redis-event-handler.png) -相关阅读:[Redis 事件机制详解](http://remcarpediem.net/article/1aa2da89/)。 - ### Redis6.0 之前为什么不使用多线程? 虽然说 Redis 是单线程模型,但实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。** @@ -666,7 +697,7 @@ void bioKillThreads(void); 关于 Redis 后台线程的详细介绍可以查看 [Redis 6.0 后台线程有哪些?](https://juejin.cn/post/7102780434739626014) 这篇就文章。 -## Redis 内存管理 +## ⭐️Redis 内存管理 ### Redis 给缓存数据设置过期时间有什么用? @@ -786,7 +817,7 @@ dynamic-hz yes 因为不太好办到,或者说这种删除方式的成本太高了。假如我们使用延迟队列作为删除策略,这样存在下面这些问题: 1. 队列本身的开销可能很大:key 多的情况下,一个延迟队列可能无法容纳。 -2. 维护延迟队列太麻烦:修改 key 的过期时间就需要调整期在延迟队列中的位置,并且还需要引入并发控制。 +2. 维护延迟队列太麻烦:修改 key 的过期时间就需要调整其在延迟队列中的位置,并且还需要引入并发控制。 ### 大量 key 集中过期怎么办? diff --git a/docs/database/redis/redis-questions-02.md b/docs/database/redis/redis-questions-02.md index 08e5e0a8e43..db3942d84d2 100644 --- a/docs/database/redis/redis-questions-02.md +++ b/docs/database/redis/redis-questions-02.md @@ -6,10 +6,10 @@ tag: head: - - meta - name: keywords - content: Redis基础,Redis常见数据结构,Redis线程模型,Redis内存管理,Redis事务,Redis性能优化 + content: Redis面试题,Redis事务,Redis性能优化,Redis缓存穿透,Redis缓存击穿,Redis缓存雪崩,Redis bigkey,Redis hotkey,Redis慢查询,Redis内存碎片,Redis集群,Redis Sentinel,Redis Cluster,Redis pipeline,Redis Lua脚本 - - meta - name: description - content: 一篇文章总结Redis常见的知识点和面试题,涵盖Redis基础、Redis常见数据结构、Redis线程模型、Redis内存管理、Redis事务、Redis性能优化等内容。 + content: 最新Redis面试题总结(下):深度剖析Redis事务原理、性能优化(pipeline/Lua/bigkey/hotkey)、缓存穿透/击穿/雪崩解决方案、慢查询与内存碎片、Redis Sentinel与Cluster集群详解。助你轻松应对后端技术面试! --- @@ -184,9 +184,9 @@ Redis 从 2.6 版本开始支持执行 Lua 脚本,它的功能和事务非常 如果想要让 Lua 脚本中的命令全部执行,必须保证语句语法和命令都是对的。 -另外,Redis 7.0 新增了 [Redis functions](https://redis.io/docs/manual/programmability/functions-intro/) 特性,你可以将 Redis functions 看作是比 Lua 更强大的脚本。 +另外,Redis 7.0 新增了 [Redis functions](https://redis.io/docs/latest/develop/programmability/functions-intro/) 特性,你可以将 Redis functions 看作是比 Lua 更强大的脚本。 -## Redis 性能优化(重要) +## ⭐️Redis 性能优化(重要) 除了下面介绍的内容之外,再推荐两篇不错的文章: @@ -578,7 +578,7 @@ CONFIG SET slowlog-max-len 128 5. **客户端信息 (Client IP:Port)**: 执行命令的客户端地址和端口。 6. **客户端名称 (Client Name)**: 如果客户端设置了名称 (CLIENT SETNAME)。 -`SLOWLOG GET` 命令默认返回最近 10 条的的慢查询命令,你也自己可以指定返回的慢查询命令的数量 `SLOWLOG GET N`。 +`SLOWLOG GET` 命令默认返回最近 10 条的慢查询命令,你也自己可以指定返回的慢查询命令的数量 `SLOWLOG GET N`。 下面是其他比较常用的慢查询相关的命令: @@ -600,7 +600,7 @@ OK **参考答案**:[Redis 内存碎片详解](https://javaguide.cn/database/redis/redis-memory-fragmentation.html)。 -## Redis 生产问题(重要) +## ⭐️Redis 生产问题(重要) ### 缓存穿透 @@ -760,7 +760,21 @@ Bloom Filter 会使用一个较大的 bit 数组来保存所有的数据,数 ### 哪些情况可能会导致 Redis 阻塞? -单独抽了一篇文章来总结可能会导致 Redis 阻塞的情况:[Redis 常见阻塞原因总结](https://javaguide.cn/database/redis/redis-common-blocking-problems-summary.html)。 +常见的导致 Redis 阻塞原因有: + +- `O(n)` 复杂度命令执行(如 `KEYS *`、`HGETALL`、`LRANGE`、`SMEMBERS` 等),随着数据量增大导致执行时间过长。 +- 执行 `SAVE` 命令生成 RDB 快照时同步阻塞主线程,而 `BGSAVE` 通过 `fork` 子进程避免阻塞。 +- AOF 记录日志在主线程中进行,可能因命令执行后写日志而阻塞后续命令。 +- AOF 刷盘(fsync)时后台线程同步到磁盘,磁盘压力大导致 `fsync` 阻塞,进而阻塞主线程 `write` 操作,尤其在 `appendfsync always` 或 `everysec` 配置下明显。 +- AOF 重写过程中将重写缓冲区内容追加到新 AOF 文件时产生阻塞。 +- 操作大 key(string > 1MB 或复合类型元素 > 5000)导致客户端超时、网络阻塞和工作线程阻塞。 +- 使用 `flushdb` 或 `flushall` 清空数据库时涉及大量键值对删除和内存释放,造成主线程阻塞。 +- 集群扩容缩容时数据迁移为同步操作,大 key 迁移导致两端节点长时间阻塞,可能触发故障转移 +- 内存不足触发 Swap,操作系统将 Redis 内存换出到硬盘,读写性能急剧下降。 +- 其他进程过度占用 CPU 导致 Redis 吞吐量下降。 +- 网络问题如连接拒绝、延迟高、网卡软中断等导致 Redis 阻塞。 + +详细介绍可以阅读这篇文章:[Redis 常见阻塞原因总结](https://javaguide.cn/database/redis/redis-common-blocking-problems-summary.html)。 ## Redis 集群 @@ -798,8 +812,6 @@ Bloom Filter 会使用一个较大的 bit 数组来保存所有的数据,数 6. 控制 key 的生命周期:避免 Redis 中存放了太多不经常被访问的数据。 7. …… -相关文章推荐:[阿里云 Redis 开发规范](https://developer.aliyun.com/article/531067)。 - ## 参考 - 《Redis 开发与运维》 diff --git a/docs/database/redis/redis-skiplist.md b/docs/database/redis/redis-skiplist.md index 11f0c32b665..e33d55da02d 100644 --- a/docs/database/redis/redis-skiplist.md +++ b/docs/database/redis/redis-skiplist.md @@ -3,6 +3,13 @@ title: Redis为什么用跳表实现有序集合 category: 数据库 tag: - Redis +head: + - - meta + - name: keywords + content: Redis,跳表,有序集合,ZSet,时间复杂度,平衡树对比,实现原理 + - - meta + - name: description + content: 深入讲解 Redis 有序集合为何选择跳表实现,结合时间复杂度与平衡树对比,理解工程权衡与源码细节。 --- ## 前言 @@ -241,41 +248,40 @@ private int levelCount = 1; private Node h = new Node(); public void add(int value) { - - //随机生成高度 - int level = randomLevel(); + int level = randomLevel(); // 新节点的随机高度 Node newNode = new Node(); newNode.data = value; newNode.maxLevel = level; - //创建一个node数组,用于记录小于当前value的最大值 - Node[] maxOfMinArr = new Node[level]; - //默认情况下指向头节点 + // 用于记录每层前驱节点的数组 + Node[] update = new Node[level]; for (int i = 0; i < level; i++) { - maxOfMinArr[i] = h; + update[i] = h; } - //基于上述结果拿到当前节点的后继节点 Node p = h; - for (int i = level - 1; i >= 0; i--) { + // 关键修正:从跳表的当前最高层开始查找 + for (int i = levelCount - 1; i >= 0; i--) { while (p.forwards[i] != null && p.forwards[i].data < value) { p = p.forwards[i]; } - maxOfMinArr[i] = p; + // 只记录需要更新的层的前驱节点 + if (i < level) { + update[i] = p; + } } - //更新前驱节点的后继节点为当前节点newNode + // 插入新节点 for (int i = 0; i < level; i++) { - newNode.forwards[i] = maxOfMinArr[i].forwards[i]; - maxOfMinArr[i].forwards[i] = newNode; + newNode.forwards[i] = update[i].forwards[i]; + update[i].forwards[i] = newNode; } - //如果当前newNode高度大于跳表最高高度则更新levelCount + // 更新跳表的总高度 if (levelCount < level) { levelCount = level; } - } ``` @@ -380,7 +386,7 @@ public class SkipList { /** * 每个节点添加一层索引高度的概率为二分之一 */ - private static final float PROB = 0.5 f; + private static final float PROB = 0.5f; /** * 默认情况下的高度为1,即只有自己一个节点 @@ -392,9 +398,11 @@ public class SkipList { */ private Node h = new Node(); - public SkipList() {} + public SkipList() { + } public class Node { + private int data = -1; /** * @@ -404,58 +412,55 @@ public class SkipList { @Override public String toString() { - return "Node{" + - "data=" + data + - ", maxLevel=" + maxLevel + - '}'; + return "Node{" + + "data=" + data + + ", maxLevel=" + maxLevel + + '}'; } } public void add(int value) { - - //随机生成高度 - int level = randomLevel(); + int level = randomLevel(); // 新节点的随机高度 Node newNode = new Node(); newNode.data = value; newNode.maxLevel = level; - //创建一个node数组,用于记录小于当前value的最大值 - Node[] maxOfMinArr = new Node[level]; - //默认情况下指向头节点 + // 用于记录每层前驱节点的数组 + Node[] update = new Node[level]; for (int i = 0; i < level; i++) { - maxOfMinArr[i] = h; + update[i] = h; } - //基于上述结果拿到当前节点的后继节点 Node p = h; - for (int i = level - 1; i >= 0; i--) { + // 关键修正:从跳表的当前最高层开始查找 + for (int i = levelCount - 1; i >= 0; i--) { while (p.forwards[i] != null && p.forwards[i].data < value) { p = p.forwards[i]; } - maxOfMinArr[i] = p; + // 只记录需要更新的层的前驱节点 + if (i < level) { + update[i] = p; + } } - //更新前驱节点的后继节点为当前节点newNode + // 插入新节点 for (int i = 0; i < level; i++) { - newNode.forwards[i] = maxOfMinArr[i].forwards[i]; - maxOfMinArr[i].forwards[i] = newNode; + newNode.forwards[i] = update[i].forwards[i]; + update[i].forwards[i] = newNode; } - //如果当前newNode高度大于跳表最高高度则更新levelCount + // 更新跳表的总高度 if (levelCount < level) { levelCount = level; } - } /** * 理论来讲,一级索引中元素个数应该占原始数据的 50%,二级索引中元素个数占 25%,三级索引12.5% ,一直到最顶层。 - * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。 - * 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且 : - * 50%的概率返回 1 - * 25%的概率返回 2 - * 12.5%的概率返回 3 ... + * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。 该 randomLevel + * 方法会随机生成 1~MAX_LEVEL 之间的数,且 : 50%的概率返回 1 25%的概率返回 2 12.5%的概率返回 3 ... + * * @return */ private int randomLevel() { @@ -523,11 +528,11 @@ public class SkipList { } } - } + ``` -对应测试代码和输出结果如下: +测试代码: ```java public static void main(String[] args) { @@ -550,61 +555,6 @@ public static void main(String[] args) { } ``` -输出结果: - -```bash -**********输出添加结果********** -Node{data=0, maxLevel=2} -Node{data=1, maxLevel=3} -Node{data=2, maxLevel=1} -Node{data=3, maxLevel=1} -Node{data=4, maxLevel=2} -Node{data=5, maxLevel=2} -Node{data=6, maxLevel=2} -Node{data=7, maxLevel=2} -Node{data=8, maxLevel=4} -Node{data=9, maxLevel=1} -Node{data=10, maxLevel=1} -Node{data=11, maxLevel=1} -Node{data=12, maxLevel=1} -Node{data=13, maxLevel=1} -Node{data=14, maxLevel=1} -Node{data=15, maxLevel=3} -Node{data=16, maxLevel=4} -Node{data=17, maxLevel=2} -Node{data=18, maxLevel=1} -Node{data=19, maxLevel=1} -Node{data=20, maxLevel=1} -Node{data=21, maxLevel=3} -Node{data=22, maxLevel=1} -Node{data=23, maxLevel=1} -**********查询结果:Node{data=22, maxLevel=1} ********** -**********删除结果********** -Node{data=0, maxLevel=2} -Node{data=1, maxLevel=3} -Node{data=2, maxLevel=1} -Node{data=3, maxLevel=1} -Node{data=4, maxLevel=2} -Node{data=5, maxLevel=2} -Node{data=6, maxLevel=2} -Node{data=7, maxLevel=2} -Node{data=8, maxLevel=4} -Node{data=9, maxLevel=1} -Node{data=10, maxLevel=1} -Node{data=11, maxLevel=1} -Node{data=12, maxLevel=1} -Node{data=13, maxLevel=1} -Node{data=14, maxLevel=1} -Node{data=15, maxLevel=3} -Node{data=16, maxLevel=4} -Node{data=17, maxLevel=2} -Node{data=18, maxLevel=1} -Node{data=19, maxLevel=1} -Node{data=20, maxLevel=1} -Node{data=21, maxLevel=3} -Node{data=23, maxLevel=1} -``` - **Redis 跳表的特点**: 1. 采用**双向链表**,不同于上面的示例,存在一个回退指针。主要用于简化操作,例如删除某个元素时,还需要找到该元素的前驱节点,使用回退指针会非常方便。 diff --git a/docs/database/sql/sql-questions-01.md b/docs/database/sql/sql-questions-01.md index 4bf08f0fa0b..fe1a7c2f28b 100644 --- a/docs/database/sql/sql-questions-01.md +++ b/docs/database/sql/sql-questions-01.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 面试题,查询,分组,排序,连接,子查询,聚合 + - - meta + - name: description + content: 收录 SQL 基础高频题与解法,涵盖查询/分组/排序/连接等典型场景,强调可读性与性能的兼顾。 --- > 题目来源于:[牛客题霸 - SQL 必知必会](https://www.nowcoder.com/exam/oj?page=1&tab=SQL%E7%AF%87&topicId=298) diff --git a/docs/database/sql/sql-questions-02.md b/docs/database/sql/sql-questions-02.md index 2a4a3e496c6..11d1a1068df 100644 --- a/docs/database/sql/sql-questions-02.md +++ b/docs/database/sql/sql-questions-02.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 面试题,增删改,批量插入,导入,替换插入,约束 + - - meta + - name: description + content: 聚焦增删改等基础操作的题目解析,总结批量插入/导入与替换插入等技巧与注意事项。 --- > 题目来源于:[牛客题霸 - SQL 进阶挑战](https://www.nowcoder.com/exam/oj?page=1&tab=SQL%E7%AF%87&topicId=240) diff --git a/docs/database/sql/sql-questions-03.md b/docs/database/sql/sql-questions-03.md index f5acd8fc5c8..6979bb69146 100644 --- a/docs/database/sql/sql-questions-03.md +++ b/docs/database/sql/sql-questions-03.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 面试题,聚合函数,截断平均,窗口,难题解析,性能 + - - meta + - name: description + content: 围绕聚合函数与复杂统计题型,讲解截断平均等解法与实现要点,兼顾性能与正确性。 --- > 题目来源于:[牛客题霸 - SQL 进阶挑战](https://www.nowcoder.com/exam/oj?page=1&tab=SQL%E7%AF%87&topicId=240) diff --git a/docs/database/sql/sql-questions-04.md b/docs/database/sql/sql-questions-04.md index 84f1a2b3c8c..b9b2ee04543 100644 --- a/docs/database/sql/sql-questions-04.md +++ b/docs/database/sql/sql-questions-04.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 面试题,窗口函数,ROW_NUMBER,排名,分组,MySQL 8 + - - meta + - name: description + content: 总结 MySQL 8 引入的窗口函数用法,包含排序与分组统计场景的高频题与实现技巧。 --- > 题目来源于:[牛客题霸 - SQL 进阶挑战](https://www.nowcoder.com/exam/oj?page=1&tab=SQL%E7%AF%87&topicId=240) @@ -142,20 +149,20 @@ WHERE ranking <= 3 试卷作答记录表 `exam_record`(`uid` 用户 ID, `exam_id` 试卷 ID, `start_time` 开始作答时间, `submit_time` 交卷时间, `score` 得分): -| id | uid | exam_id | start_time | submit_time | score | -| ---- | ---- | ------- | ------------------- | ------------------- | ------ | -| 1 | 1001 | 9001 | 2021-09-01 09:01:01 | 2021-09-01 09:51:01 | 78 | -| 2 | 1001 | 9002 | 2021-09-01 09:01:01 | 2021-09-01 09:31:00 | 81 | -| 3 | 1002 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 81 | -| 4 | 1003 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:59:01 | 86 | -| 5 | 1003 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:51 | 89 | -| 6 | 1004 | 9002 | 2021-09-01 19:01:01 | 2021-09-01 19:30:01 | 85 | -| 7 | 1005 | 9001 | 2021-09-01 12:01:01 | 2021-09-01 12:31:02 | 85 | -| 8 | 1006 | 9001 | 2021-09-07 10:02:01 | 2021-09-07 10:21:01 | 84 | -| 9 | 1003 | 9001 | 2021-09-08 12:01:01 | 2021-09-08 12:11:01 | 40 | -| 10 | 1003 | 9002 | 2021-09-01 14:01:01 | (NULL) | (NULL) | -| 11 | 1005 | 9001 | 2021-09-01 14:01:01 | (NULL) | (NULL) | -| 12 | 1003 | 9003 | 2021-09-08 15:01:01 | (NULL) | (NULL) | +| id | uid | exam_id | start_time | submit_time | score | +| --- | ---- | ------- | ------------------- | ------------------- | ------ | +| 1 | 1001 | 9001 | 2021-09-01 09:01:01 | 2021-09-01 09:51:01 | 78 | +| 2 | 1001 | 9002 | 2021-09-01 09:01:01 | 2021-09-01 09:31:00 | 81 | +| 3 | 1002 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:01 | 81 | +| 4 | 1003 | 9001 | 2021-09-01 19:01:01 | 2021-09-01 19:59:01 | 86 | +| 5 | 1003 | 9002 | 2021-09-01 12:01:01 | 2021-09-01 12:31:51 | 89 | +| 6 | 1004 | 9002 | 2021-09-01 19:01:01 | 2021-09-01 19:30:01 | 85 | +| 7 | 1005 | 9001 | 2021-09-01 12:01:01 | 2021-09-01 12:31:02 | 85 | +| 8 | 1006 | 9001 | 2021-09-07 10:02:01 | 2021-09-07 10:21:01 | 84 | +| 9 | 1003 | 9001 | 2021-09-08 12:01:01 | 2021-09-08 12:11:01 | 40 | +| 10 | 1003 | 9002 | 2021-09-01 14:01:01 | (NULL) | (NULL) | +| 11 | 1005 | 9001 | 2021-09-01 14:01:01 | (NULL) | (NULL) | +| 12 | 1003 | 9003 | 2021-09-08 15:01:01 | (NULL) | (NULL) | 找到第二快和第二慢用时之差大于试卷时长的一半的试卷信息,按试卷 ID 降序排序。由示例数据结果输出如下: diff --git a/docs/database/sql/sql-questions-05.md b/docs/database/sql/sql-questions-05.md index c20af2cad39..e11c14979c5 100644 --- a/docs/database/sql/sql-questions-05.md +++ b/docs/database/sql/sql-questions-05.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 面试题,空值处理,统计,未完成率,CASE,聚合 + - - meta + - name: description + content: 解析空值处理与统计类题目,结合 CASE 与聚合函数给出稳健实现,避免常见陷阱。 --- > 题目来源于:[牛客题霸 - SQL 进阶挑战](https://www.nowcoder.com/exam/oj?page=1&tab=SQL%E7%AF%87&topicId=240) @@ -41,26 +48,53 @@ tag: 写法 1: ```sql -SELECT exam_id, - count(submit_time IS NULL OR NULL) incomplete_cnt, - ROUND(count(submit_time IS NULL OR NULL) / count(*), 3) complete_rate -FROM exam_record -GROUP BY exam_id -HAVING incomplete_cnt <> 0 +SELECT + exam_id, + (COUNT(*) - COUNT(submit_time)) AS incomplete_cnt, + ROUND((COUNT(*) - COUNT(submit_time)) / COUNT(*), 3) AS incomplete_rate +FROM + exam_record +GROUP BY + exam_id +HAVING + (COUNT(*) - COUNT(submit_time)) > 0; ``` +利用 `COUNT(*)`统计分组内的总记录数,`COUNT(submit_time)` 只统计 `submit_time` 字段不为 NULL 的记录数(即已完成数)。两者相减,就是未完成数。 + 写法 2: ```sql -SELECT exam_id, - count(submit_time IS NULL OR NULL) incomplete_cnt, - ROUND(count(submit_time IS NULL OR NULL) / count(*), 3) complete_rate -FROM exam_record -GROUP BY exam_id -HAVING incomplete_cnt <> 0 +SELECT + exam_id, + COUNT(CASE WHEN submit_time IS NULL THEN 1 END) AS incomplete_cnt, + ROUND(COUNT(CASE WHEN submit_time IS NULL THEN 1 END) / COUNT(*), 3) AS incomplete_rate +FROM + exam_record +GROUP BY + exam_id +HAVING + COUNT(CASE WHEN submit_time IS NULL THEN 1 END) > 0; +``` + +使用 `CASE` 表达式,当条件满足时返回一个非 `NULL` 值(例如 1),否则返回 `NULL`。然后用 `COUNT` 函数来统计非 `NULL` 值的数量。 + +写法 3: + +```sql +SELECT + exam_id, + SUM(submit_time IS NULL) AS incomplete_cnt, + ROUND(SUM(submit_time IS NULL) / COUNT(*), 3) AS incomplete_rate +FROM + exam_record +GROUP BY + exam_id +HAVING + incomplete_cnt > 0; ``` -两种写法都可以,只有中间的写法不一样,一个是对符合条件的才`COUNT`,一个是直接上`IF`,后者更为直观,最后这个`having`解释一下, 无论是 `complete_rate` 还是 `incomplete_cnt`,只要不为 0 即可,不为 0 就意味着有未完成的。 +利用 `SUM` 函数对一个表达式求和。当 `submit_time` 为 `NULL` 时,表达式 `(submit_time IS NULL)` 的值为 1 (TRUE),否则为 0 (FALSE)。将这些 1 和 0 加起来,就得到了未完成的数量。 ### 0 级用户高难度试卷的平均用时和平均得分 diff --git a/docs/database/sql/sql-syntax-summary.md b/docs/database/sql/sql-syntax-summary.md index cff0b931495..ef4be0bd88d 100644 --- a/docs/database/sql/sql-syntax-summary.md +++ b/docs/database/sql/sql-syntax-summary.md @@ -4,6 +4,13 @@ category: 数据库 tag: - 数据库基础 - SQL +head: + - - meta + - name: keywords + content: SQL 语法,DDL,DML,DQL,约束,事务,索引,范式 + - - meta + - name: description + content: 系统整理 SQL 基础语法与术语,覆盖 DDL/DML/DQL、约束与事务索引,形成入门到实践的知识路径。 --- > 本文整理完善自下面这两份资料: diff --git a/docs/distributed-system/distributed-configuration-center.md b/docs/distributed-system/distributed-configuration-center.md index 2e00aec70a3..e10ba19d9eb 100644 --- a/docs/distributed-system/distributed-configuration-center.md +++ b/docs/distributed-system/distributed-configuration-center.md @@ -8,5 +8,3 @@ category: 分布式 ![](https://oss.javaguide.cn/javamianshizhibei/distributed-system.png) - - diff --git a/docs/distributed-system/distributed-lock-implementations.md b/docs/distributed-system/distributed-lock-implementations.md index cb4504c4a7a..860d16b74ae 100644 --- a/docs/distributed-system/distributed-lock-implementations.md +++ b/docs/distributed-system/distributed-lock-implementations.md @@ -372,10 +372,4 @@ private static class LockData 为了进一步提高系统的可靠性,建议引入一个兜底机制。例如,可以通过 **版本号(Fencing Token)机制** 来避免并发冲突。 -最后,再分享几篇我觉得写的还不错的文章: - -- [分布式锁实现原理与最佳实践 - 阿里云开发者](https://mp.weixin.qq.com/s/JzCHpIOiFVmBoAko58ZuGw) -- [聊聊分布式锁 - 字节跳动技术团队](https://mp.weixin.qq.com/s/-N4x6EkxwAYDGdJhwvmZLw) -- [Redis、ZooKeeper、Etcd,谁有最好用的分布式锁? - 腾讯云开发者](https://mp.weixin.qq.com/s/yZC6VJGxt1ANZkn0SljZBg) - diff --git a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md index 955c5d2813a..5208dc5e8fc 100644 --- a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md +++ b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md @@ -232,8 +232,8 @@ ZooKeeper 集群中的服务器状态有下面几种: ZooKeeper 集群在宕掉几个 ZooKeeper 服务器之后,如果剩下的 ZooKeeper 服务器个数大于宕掉的个数的话整个 ZooKeeper 才依然可用。假如我们的集群中有 n 台 ZooKeeper 服务器,那么也就是剩下的服务数必须大于 n/2。先说一下结论,2n 和 2n-1 的容忍度是一样的,都是 n-1,大家可以先自己仔细想一想,这应该是一个很简单的数学问题了。 -比如假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台。 -假如我们有 5 台,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的的时候也同样只允许宕掉 2 台。 +比如假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的时候也同样只允许宕掉 1 台。 +假如我们有 5 台,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的时候也同样只允许宕掉 2 台。 综上,何必增加那一个不必要的 ZooKeeper 呢? diff --git a/docs/distributed-system/distributed-transaction.md b/docs/distributed-system/distributed-transaction.md index fa4c83c743c..c0047e16064 100644 --- a/docs/distributed-system/distributed-transaction.md +++ b/docs/distributed-system/distributed-transaction.md @@ -5,8 +5,6 @@ category: 分布式 **分布式事务** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了《Java 面试指北》中。 -![](https://oss.javaguide.cn/javamianshizhibei/distributed-system.png) +![](https://oss.javaguide.cn/javamianshizhibei/distributed-system-config.png) - - diff --git a/docs/distributed-system/protocol/consistent-hashing.md b/docs/distributed-system/protocol/consistent-hashing.md new file mode 100644 index 00000000000..c4de5bac2d2 --- /dev/null +++ b/docs/distributed-system/protocol/consistent-hashing.md @@ -0,0 +1,131 @@ +--- +title: 一致性哈希算法详解 +category: 分布式 +tag: + - 分布式协议&算法 + - 哈希算法 +--- + +开始之前,先说两个常见的场景: + +1. **负载均衡**:由于访问人数太多,我们的网站部署了多台服务器个共同提供相同的服务,但每台服务器上存储的数据不同。为了保证请求的正确响应,相同参数(key)的请求(比如同个 IP 的请求、同一个用户的请求)需要发到同一台服务器处理。 +2. **分布式缓存**:由于缓存数据量太大,我们部署了多台缓存服务器共同提供缓存服务。缓存数据需要尽可能均匀地分布式在这些缓存服务器上,通过 key 可以找到对应的缓存服务器。 + +这两种场景的本质,都是需要建立一个**从 key 到服务器/节点的稳定映射关系**。 + +为了实现这个目标,你首先会想到什么方案呢? + +## 普通哈希算法 + +相信大家很快就能想到 **“哈希+取模”** 这个经典组合。通过哈希函数计算出 key 的哈希值,再对服务器数量取模,从而将 key 映射到固定的服务器上。 + +公式也很简单: + +```java +node_number = hash(key) % N +``` + +- `hash(key)`: 使用哈希函数(建议使用性能较好的非加密哈希函数,例如 SipHash、MurMurHash3、CRC32、DJB)对唯一键进行哈希。 +- `% N`: 对哈希值取模,将哈希值映射到一个介于 0 到 N-1 之间的值,N 为节点数/服务器数。 + +![哈希取模](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/hashqumo.png) + +然而,传统的哈希取模算法有一个比较大的缺陷就是:**无法很好的解决机器/节点动态减少(比如某台机器宕机)或者增加的场景(比如又增加了一台机器)。** + +想象一下,服务器的初始数量为 4 台 (N = 4),如果其中一台服务器宕机,N 就变成了 3。此时,对于同一个 key,`hash(key) % 3` 的结果很可能与 `hash(key) % 4` 完全不同。 + +![哈希取模-移除节点Node2](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/hashqumo-remove-node2.png) + +这意味着几乎所有的数据映射关系都会错乱。在分布式缓存场景下,这会导致**大规模的缓存失效和缓存穿透**,瞬间将压力全部打到后端的数据库上,引发系统雪崩。 + +据估算,当节点数量从 N 变为 N-1 时,平均有 (N-1)/N 比例的数据需要迁移,这个比例 **趋近于 100%** 。这种“牵一发而动全身”的效应,在生产环境中是完全不可接受的。 + +为了更好地解决这个问题,一致性哈希算法诞生了。 + +## 一致性哈希算法 + +一致性哈希算法在 1997 年由麻省理工学院提出(这篇论文的 PDF 在线阅读地址:),是一种特殊的哈希算法,在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了传统哈希算法在分布式[哈希表](https://baike.baidu.com/item/哈希表/5981869)(Distributed Hash Table,DHT)中存在的动态伸缩等问题 。 + +一致性哈希算法的底层原理也很简单,关键在于**哈希环**的引入。 + +### 哈希环 + +一致性哈希算法将哈希空间组织成一个环形结构,将数据和节点都映射到这个环上,然后根据顺时针的规则确定数据或请求应该分配到哪个节点上。通常情况下,哈希环的起点是 0,终点是 2^32 - 1,并且起点与终点连接,故这个环的整数分布范围是 **[0, 2^32-1]** 。 + +传统哈希算法是对服务器数量取模,一致性哈希算法是对哈希环的范围取模,固定值,通常为 2^32: + +```java +node_number = hash(key) % 2^32 +``` + +服务器/节点如何映射到哈希环上呢?也是哈希取模。例如,一般我们会根据服务器的 IP 或者主机名进行哈希,然后再取模。 + +```java +hash(服务器ip)% 2^32 +``` + +如下图所示: + +![哈希环](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle.png) + +我们将数据和节点都映射到哈希环上,环上的每个节点都负责一个区间。对于上图来说,每个节点负责的数据情况如下: + +- **Node1:** 负责 Node4 到 Node1 之间的区域(包含 value6)。 +- **Node2:** 负责 Node1 到 Node2 之间的区域(包含 value1, value2)。 +- **Node3:** 负责 Node2 到 Node3 之间的区域(包含 value3)。 +- **Node4:** 负责 Node3 到 Node4 之间的区域(包含 value4, value5)。 + +### 节点移除/增加 + +新增节点和移除节点的情况下,哈希环的引入可以避免影响范围太大,减少需要迁移的数据。 + +还是用上面分享的哈希环示意图为例,假设 Node2 节点被移除的话,那 Node3 就要负责 Node2 的数据,直接迁移 Node2 的数据到 Node3 即可,其他节点不受影响。 + +![节点移除](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-remove-node2.png) + +同样地,如果我们在 Node1 和 Node2 之间新增一个节点 Node5,那么原本应该由 Node2 负责的一部分数据(即哈希值落在 Node1 和 Node5 之间的数据,如图中的 value1)现在会由 Node5 负责。我们只需要将这部分数据从 Node2 迁移到 Node5 即可,同样只影响了相邻的节点,影响范围非常小。 + +![节点增加](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-add-node5.png) + +### 数据倾斜问题 + +理想情况下,节点在环上是均匀分布的。然而,现实可能并不是这样的,尤其是节点数量比较少的时候。节点可能被映射到附近的区域,这样的话,就会导致绝大部分数据都由其中一个节点负责。 + +![数据倾斜](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-unbalance.png) + +对于上图来说,每个节点负责的数据情况如下: + +- **Node1:** 负责 Node4 到 Node1 之间的区域(包含 value6)。 +- **Node2:** 负责 Node1 到 Node2 之间的区域(包含 value1)。 +- **Node3:** 负责 Node2 到 Node3 之间的区域(包含 value2,value3, value4, value5)。 +- **Node4:** 负责 Node3 到 Node4 之间的区域。 + +除了数据倾斜问题,还有一个隐患。当新增或者删除节点的时候,数据分配不均衡。例如,Node3 被移除的话,Node3 负责的所有数据都要交给 Node4,随后所有的请求都要达到 Node4 上。假设 Node4 的服务器处理能力比较差的话,那可能直接就被干崩了。理想情况下,应该有更多节点来分担压力。 + +如何解决这些问题呢?答案是引入**虚拟节点**。 + +### 虚拟节点 + +虚拟节点就是对真实的物理节点在哈希环上虚拟出几个它的分身节点。数据落到分身节点上实际上就是落到真实的物理节点上,通过将虚拟节点均匀分散在哈希环的各个部分。 + +如下图所示,Node1、Node2、Node3、Node4 这 4 个节点都对应 3 个虚拟节点(下图只是为了演示,实际情况节点分布不会这么有规律)。 + +![](https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-virtual-node.png) + +对于上图来说,每个节点最终负责的数据情况如下: + +- **Node1**:value4 +- **Node2**:value1,value3 +- **Node3**:value5 +- **Node4**:value2,value6 + +**引入虚拟节点的好处是巨大的:** + +1. **数据均衡:** 虚拟节点越多,环上的“服务器点”就越密集,数据分布自然就越均匀,从根本上解决了数据倾斜问题。通常,每个真实节点对应的虚拟节点数在 100 到 200 之间,例如 Nginx 选择为每个权重分配 160 个虚拟节点。这里的权重的是为了区分服务器,例如处理能力更强的服务器权重越高,进而导致对应的虚拟节点越多,被命中的概率越大。 +2. **容错性增强:** 这才是虚拟节点最精妙的地方。当一个物理节点宕机,它相当于在环上的多个虚拟节点同时下线。这些虚拟节点原本负责的数据和流量,会**自然地、均匀地分散**给环上其他**多个不同**的物理节点去接管,而不会将压力集中于某一个邻居节点。这极大地提升了系统的稳定性和容错能力。 + +## 参考 + +- 深入剖析 Nginx 负载均衡算法: +- 读源码学架构系列:一致性哈希: +- 一致性 Hash 算法原理总结: diff --git a/docs/high-availability/fallback-and-circuit-breaker.md b/docs/high-availability/fallback-and-circuit-breaker.md index 59725fa0521..81f5a1917df 100644 --- a/docs/high-availability/fallback-and-circuit-breaker.md +++ b/docs/high-availability/fallback-and-circuit-breaker.md @@ -9,5 +9,3 @@ icon: circuit ![](https://oss.javaguide.cn/xingqiu/mianshizhibei-gaobingfa.png) - - diff --git a/docs/high-availability/idempotency.md b/docs/high-availability/idempotency.md index 41384457ccb..f44786fedab 100644 --- a/docs/high-availability/idempotency.md +++ b/docs/high-availability/idempotency.md @@ -9,5 +9,3 @@ icon: security-fill ![](https://oss.javaguide.cn/xingqiu/mianshizhibei-gaobingfa.png) - - diff --git a/docs/high-performance/deep-pagination-optimization.md b/docs/high-performance/deep-pagination-optimization.md index 0d39e627cef..28826da755a 100644 --- a/docs/high-performance/deep-pagination-optimization.md +++ b/docs/high-performance/deep-pagination-optimization.md @@ -63,8 +63,8 @@ SELECT * FROM t_order WHERE id > 100000 LIMIT 10 > ![](https://oss.javaguide.cn/github/javaguide/mysql/alibaba-java-development-handbook-paging.png) ```sql -# 通过子查询来获取 id 的起始值,把 limit 1000000 的条件转移到子查询 -SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 limit 1) LIMIT 10; +-- 先通过子查询在主键索引上进行偏移,快速找到起始ID +SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order LIMIT 1000000, 1) LIMIT 10; ``` **工作原理**: @@ -84,7 +84,10 @@ SELECT * FROM t_order WHERE id >= (SELECT id FROM t_order where id > 1000000 lim -- 使用 INNER JOIN 进行延迟关联 SELECT t1.* FROM t_order t1 -INNER JOIN (SELECT id FROM t_order where id > 1000000 LIMIT 10) t2 ON t1.id = t2.id; +INNER JOIN ( + -- 这里的子查询可以利用覆盖索引,性能极高 + SELECT id FROM t_order LIMIT 1000000, 10 +) t2 ON t1.id = t2.id; ``` **工作原理**: diff --git a/docs/high-performance/message-queue/disruptor-questions.md b/docs/high-performance/message-queue/disruptor-questions.md index 1881f6c2c79..eaa52b4fd6d 100644 --- a/docs/high-performance/message-queue/disruptor-questions.md +++ b/docs/high-performance/message-queue/disruptor-questions.md @@ -49,7 +49,7 @@ Disruptor 主要解决了 JDK 内置线程安全队列的性能和内存安全 | `LinkedTransferQueue` | 无锁(`CAS`) | 无界 | | `ConcurrentLinkedQueue` | 无锁(`CAS`) | 无界 | -从上表中可以看出:这些队列要不就是加锁有界,要不就是无锁无界。而加锁的的队列势必会影响性能,无界的队列又存在内存溢出的风险。 +从上表中可以看出:这些队列要不就是加锁有界,要不就是无锁无界。而加锁的队列势必会影响性能,无界的队列又存在内存溢出的风险。 因此,一般情况下,我们都是不建议使用 JDK 内置线程安全队列。 diff --git a/docs/high-performance/message-queue/message-queue.md b/docs/high-performance/message-queue/message-queue.md index 5874f290298..f34c8825da4 100644 --- a/docs/high-performance/message-queue/message-queue.md +++ b/docs/high-performance/message-queue/message-queue.md @@ -77,7 +77,7 @@ tag: **消息队列使用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。** 从上图可以看到**消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合**,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。**对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计**。 -例如,我们商城系统分为用户、订单、财务、仓储、消息通知、物流、风控等多个服务。用户在完成下单后,需要调用财务(扣款)、仓储(库存管理)、物流(发货)、消息通知(通知用户发货)、风控(风险评估)等服务。使用消息队列后,下单操作和后续的扣款、发货、通知等操作就解耦了,下单完成发送一个消息到消息队列,需要用到的地方去订阅这个消息进行消息即可。 +例如,我们商城系统分为用户、订单、财务、仓储、消息通知、物流、风控等多个服务。用户在完成下单后,需要调用财务(扣款)、仓储(库存管理)、物流(发货)、消息通知(通知用户发货)、风控(风险评估)等服务。使用消息队列后,下单操作和后续的扣款、发货、通知等操作就解耦了,下单完成发送一个消息到消息队列,需要用到的地方去订阅这个消息进行消费即可。 ![](https://oss.javaguide.cn/github/javaguide/high-performance/message-queue/message-queue-decouple-mall-example.png) diff --git a/docs/high-performance/sql-optimization.md b/docs/high-performance/sql-optimization.md index 9aa94dfd528..ffd444fc3dc 100644 --- a/docs/high-performance/sql-optimization.md +++ b/docs/high-performance/sql-optimization.md @@ -15,5 +15,3 @@ head: ![](https://oss.javaguide.cn/javamianshizhibei/sql-optimization.png) - - diff --git a/docs/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.md b/docs/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.md index 3ae2a5eac42..f756e7a1217 100644 --- a/docs/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.md +++ b/docs/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.md @@ -25,7 +25,7 @@ tag: > > **原文地址**: -在美团的三年多时光,如同一部悠长的交响曲,高高低低,而今离开已有一段时间。闲暇之余,梳理了三年多的收获与感慨,总结成 10 条,既是对过去一段时光的的一个深情回眸,也是对未来之路的一份期许。 +在美团的三年多时光,如同一部悠长的交响曲,高高低低,而今离开已有一段时间。闲暇之余,梳理了三年多的收获与感慨,总结成 10 条,既是对过去一段时光的一个深情回眸,也是对未来之路的一份期许。 倘若一些感悟能为刚步入职场的年轻人,或是刚在职业生涯中崭露头角的后起之秀,带来一点点启示与帮助,也是莫大的荣幸。 diff --git a/docs/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.md b/docs/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.md index 4630bff560e..774ecabc17b 100644 --- a/docs/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.md +++ b/docs/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.md @@ -74,7 +74,7 @@ PS:还好以前有奖杯,不然一点念想都没了。(现在腾讯似乎 但另一方面,后来我负责了团队内很重要的事情,应该是中心内都算很重要的事,我独自负责一个方向,直接向总监汇报,似乎又有点像。 -网上也有其他说法,一针见血,是不是嫡系,就看钱到不到位,这么说也有道理。我在 7 级时,就发了股票,自我感觉,还是不错的。我当时以为不出意外的话,我以后的钱途和发展是不是就会一帆风顺。不出意外就出了意外,第二年,EPC 不达预期,部门总经理和总监都被换了,中心来了一个新的的总监。 +网上也有其他说法,一针见血,是不是嫡系,就看钱到不到位,这么说也有道理。我在 7 级时,就发了股票,自我感觉,还是不错的。我当时以为不出意外的话,我以后的钱途和发展是不是就会一帆风顺。不出意外就出了意外,第二年,EPC 不达预期,部门总经理和总监都被换了,中心来了一个新的总监。 好吧,又要重新建立信任了。再到后来,是不是嫡系已经不重要了,因为大环境不好,又加上裁员,大家主动的被动的差不多都走了。 diff --git a/docs/home.md b/docs/home.md index 047a09fe9ad..a24fdd30e4d 100644 --- a/docs/home.md +++ b/docs/home.md @@ -111,6 +111,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. - [Java 21 新特性概览](./java/new-features/java21.md) - [Java 22 & 23 新特性概览](./java/new-features/java22-23.md) - [Java 24 新特性概览](./java/new-features/java24.md) +- [Java 25 新特性概览](./java/new-features/java25.md) ## 计算机基础 @@ -263,7 +264,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. ### 基础 - [RestFul API 简明教程](./system-design/basis/RESTfulAPI.md) -- [软件工程简明教程简明教程](./system-design/basis/software-engineering.md) +- [软件工程简明教程](./system-design/basis/software-engineering.md) - [代码命名指南](./system-design/basis/naming.md) - [代码重构指南](./system-design/basis/refactoring.md) - [单元测试指南](./system-design/basis/unit-test.md) @@ -323,6 +324,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. - [Paxos 算法解读](./distributed-system/protocol/paxos-algorithm.md) - [Raft 算法解读](./distributed-system/protocol/raft-algorithm.md) - [Gossip 协议详解](./distributed-system/protocol/gossip-protocl.md) +- [一致性哈希算法详解](./distributed-system/protocol/consistent-hashing.md) ### RPC @@ -382,7 +384,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. - [Disruptor 常见知识点&面试题总结](./high-performance/message-queue/disruptor-questions.md) - [RabbitMQ 常见知识点&面试题总结](./high-performance/message-queue/rabbitmq-questions.md) - [RocketMQ 常见知识点&面试题总结](./high-performance/message-queue/rocketmq-questions.md) -- [Kafka 常常见知识点&面试题总结](./high-performance/message-queue/kafka-questions-01.md) +- [Kafka 常见知识点&面试题总结](./high-performance/message-queue/kafka-questions-01.md) ## 高可用 @@ -412,7 +414,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle. **灾备** = 容灾 + 备份。 -- **备份**:将系统所产生的的所有重要数据多备份几份。 +- **备份**:将系统所产生的所有重要数据多备份几份。 - **容灾**:在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。 **异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。 diff --git a/docs/interview-preparation/java-roadmap.md b/docs/interview-preparation/java-roadmap.md index 60dd0fa7fc0..44de032e88c 100644 --- a/docs/interview-preparation/java-roadmap.md +++ b/docs/interview-preparation/java-roadmap.md @@ -12,7 +12,13 @@ icon: path 历时一个月精心打磨,笔者基于当下 Java 后端开发岗位招聘的最新要求,对既有学习路线进行了全面升级。本次升级涵盖技术栈增删、学习路径优化、配套学习资源更新等维度,力争构建出更符合 Java 开发者成长曲线的知识体系。 -![Java 学习路线 PDF 概览](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf.png) +亮色板概览: + +![Java 学习路线 PDF 概览 - 亮色板](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf.png) + +暗色板概览: + +![Java 学习路线 PDF 概览 - 暗色版](https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf-dark.png) 这可能是你见过的最用心、最全面的 Java 后端学习路线。这份学习路线共包含 **4w+** 字,但你完全不用担心内容过多而学不完。我会根据学习难度,划分出适合找小厂工作必学的内容,以及适合逐步提升 Java 后端开发能力的学习路径。 @@ -22,8 +28,8 @@ icon: path 在看这份学习路线的过程中,建议搭配 [Java 面试重点总结(重要)](https://javaguide.cn/interview-preparation/key-points-of-interview.html),可以让你在学习过程中更有目的性。 -由于这份学习路线内容太多,因此我将其整理成了 PDF 版本(共 **61** 页),方便大家阅读。这份 PDF 有黑夜和白天两种阅读版本,满足大家的不同需求。 +由于这份学习路线内容太多,因此我将其整理成了 PDF 版本(共 **55** 页),方便大家阅读。这份 PDF 有黑夜和白天两种阅读版本,满足大家的不同需求。 -这份学习路线的获取方法很简单:直接在公众号「**JavaGuide**」后台回复“**学习路线**”即可获取。 +这份学习路线的获取方法很简单:直接在公众号「**JavaGuide**」后台回复“**路线**”即可获取。 ![JavaGuide 官方公众号](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) diff --git a/docs/interview-preparation/project-experience-guide.md b/docs/interview-preparation/project-experience-guide.md index 0546626f889..1b0992fab84 100644 --- a/docs/interview-preparation/project-experience-guide.md +++ b/docs/interview-preparation/project-experience-guide.md @@ -72,9 +72,9 @@ GitHub 或者码云上面有很多实战类别项目,你可以选择一个来 ## 有没有还不错的项目推荐? -**[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)** 的「面试准备篇」中有一篇文章专门整理了一些比较高质量的实战项目,非常适合用来学习或者作为项目经验。 +**[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)** 的「面试准备篇」中有一篇文章专门整理了一些比较高质量的实战项目,包含业务项目、轮子项目、国外公开课 Lab 和视频类实战项目教程推荐,非常适合用来学习或者作为项目经验。 -![](https://oss.javaguide.cn/javamianshizhibei/project-experience-guide.png) +![优质 Java 实战项目推荐](https://oss.javaguide.cn/javamianshizhibei/project-experience-guide.png) 这篇文章一共推荐了 15+ 个实战项目,有业务类的,也有轮子类的,有开源项目、也有视频教程。对于参加校招的小伙伴,我更建议做一个业务类项目加上一个轮子类的项目。 diff --git a/docs/interview-preparation/self-test-of-common-interview-questions.md b/docs/interview-preparation/self-test-of-common-interview-questions.md index 85b0e236c01..9700ac5b941 100644 --- a/docs/interview-preparation/self-test-of-common-interview-questions.md +++ b/docs/interview-preparation/self-test-of-common-interview-questions.md @@ -8,12 +8,10 @@ icon: security-fill 在 **[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)** 的 **「技术面试题自测篇」** ,我总结了 Java 面试中最重要的知识点的最常见的面试题并按照面试提问的方式展现出来。 -![](https://oss.javaguide.cn/xingqiu/image-20220628102643202.png) +![《Java 面试指北》技术面试题自测篇](https://oss.javaguide.cn/javamianshizhibei/self-test.png) -每一道用于自测的面试题我都会给出重要程度,方便大家在时间比较紧张的时候根据自身情况来选择性自测。并且,我还会给出提示,方便你回忆起对应的知识点。 +每道题我都会给出**提示与思路**,并用 ⭐ 标注重要程度:⭐ 越多,说明面试越爱问,就越值得多花一些时间准备。 -在面试中如果你实在没有头绪的话,一个好的面试官也是会给你提示的。 - -![](https://oss.javaguide.cn/xingqiu/image-20220628102848236.png) +![](https://oss.javaguide.cn/javamianshizhibei/self-test-key-points.png) diff --git a/docs/interview-preparation/teach-you-how-to-prepare-for-the-interview-hand-in-hand.md b/docs/interview-preparation/teach-you-how-to-prepare-for-the-interview-hand-in-hand.md index ae8a4bf6f18..a42f9fa2353 100644 --- a/docs/interview-preparation/teach-you-how-to-prepare-for-the-interview-hand-in-hand.md +++ b/docs/interview-preparation/teach-you-how-to-prepare-for-the-interview-hand-in-hand.md @@ -8,13 +8,11 @@ icon: path 本文节选自 **[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)**。这是一份教你如何更高效地准备面试的专栏,内容和 JavaGuide 互补,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ……)、优质面经等内容。 ::: -你的身边一定有很多编程比你厉害但是找的工作并没有你好的朋友!**技术面试不同于编程,编程厉害不代表技术面试就一定能过。** +你身边是否有这样的朋友:编程能力比你强,求职结果却不如你?其实**技术好≠面试能过** —— 如今的面试早已不是 “会写代码就行”,不做准备就去面,大概率是 “撞枪口”。 -现在你去面个试,不认真准备一下,那简直就是往枪口上撞。我们大部分都只是普通人,没有发过顶级周刊或者获得过顶级大赛奖项。在这样一个技术面试氛围下,我们需要花费很多精力来准备面试,来提高自己的技术能力。“[面试造火箭,工作拧螺丝钉](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247491596&idx=1&sn=36fbf80922f71c200990de11514955f7&chksm=cea1afc7f9d626d1c70d5e54505495ac499ce6eb5e05ba4f4bb079a8563a84e27f17ceff38af&token=353590436&lang=zh_CN&scene=21#wechat_redirect)” 就是目前的一个常态,预计未来很长很长一段时间也还是会是这样。 +我们大多是普通开发者,没有顶会论文或竞赛大奖加持,面对 “面试造火箭,工作拧螺丝钉” 的常态,只能靠扎实准备突围。但准备面试不等于耍小聪明或者死记硬背面试题。 **一定不要对面试抱有侥幸心理。打铁还需自身硬!** 千万不要觉得自己看几篇面经,看几篇面试题解析就能通过面试了。一定要静下心来深入学习! -准备面试不等于耍小聪明或者死记硬背面试题。 **一定不要对面试抱有侥幸心理。打铁还需自身硬!** 千万不要觉得自己看几篇面经,看几篇面试题解析就能通过面试了。一定要静下心来深入学习! - -这篇我会从宏观面出发简单聊聊如何准备 Java 面试,让你少走弯路! +这篇文章就从宏观视角,带你搞懂程序员该如何系统准备面试:从求职导向学习,到简历优化、面试冲刺,帮你少走弯路,高效拿下心仪 offer。 ## 尽早以求职为导向来学习 @@ -138,7 +136,7 @@ Java 后端面试复习的重点请看这篇文章:[Java 面试重点总结( 一定不要抱着一种思想,觉得八股文或者基础问题的考查意义不大。如果你抱着这种思想复习的话,那效果可能不会太好。实际上,个人认为还是很有意义的,八股文或者基础性的知识在日常开发中也会需要经常用到。例如,线程池这块的拒绝策略、核心参数配置什么的,如果你不了解,实际项目中使用线程池可能就用的不是很明白,容易出现问题。而且,其实这种基础性的问题是最容易准备的,像各种底层原理、系统设计、场景题以及深挖你的项目这类才是最难的! -八股文资料首推我的 [《Java 面试指北》](https://t.zsxq.com/11rZ6D7Wk) (配合 JavaGuide 使用,会根据每一年的面试情况对内容进行更新完善)和 [JavaGuide](https://javaguide.cn/) 。里面不仅仅是原创八股文,还有很多对实际开发有帮助的干货。除了我的资料之外,你还可以去网上找一些其他的优质的文章、视频来看。 +八股文资料首推我的 [《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) (配合 JavaGuide 使用,会根据每一年的面试情况对内容进行更新完善)和 [JavaGuide](https://javaguide.cn/) 。里面不仅仅是原创八股文,还有很多对实际开发有帮助的干货。除了我的资料之外,你还可以去网上找一些其他的优质的文章、视频来看。 ![《Java 面试指北》内容概览](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png) diff --git a/docs/java/basis/bigdecimal.md b/docs/java/basis/bigdecimal.md index acedfadc32f..f4cda70a94f 100644 --- a/docs/java/basis/bigdecimal.md +++ b/docs/java/basis/bigdecimal.md @@ -3,6 +3,13 @@ title: BigDecimal 详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: BigDecimal,浮点数精度,小数运算,compareTo,舍入规则,RoundingMode,divide,阿里巴巴规范 + - - meta + - name: description + content: 讲解 BigDecimal 的使用场景与核心 API,解决浮点数精度问题并总结常见舍入规则与最佳实践。 --- 《阿里巴巴 Java 开发手册》中提到:“为了避免精度丢失,可以使用 `BigDecimal` 来进行浮点数的运算”。 @@ -283,7 +290,7 @@ public class BigDecimalUtil { * @return 返回转换结果 */ public static float convertToFloat(double v) { - BigDecimal b = new BigDecimal(v); + BigDecimal b = BigDecimal.valueOf(v); return b.floatValue(); } @@ -294,7 +301,7 @@ public class BigDecimalUtil { * @return 返回转换结果 */ public static int convertsToInt(double v) { - BigDecimal b = new BigDecimal(v); + BigDecimal b = BigDecimal.valueOf(v); return b.intValue(); } @@ -305,7 +312,7 @@ public class BigDecimalUtil { * @return 返回转换结果 */ public static long convertsToLong(double v) { - BigDecimal b = new BigDecimal(v); + BigDecimal b = BigDecimal.valueOf(v); return b.longValue(); } @@ -317,8 +324,8 @@ public class BigDecimalUtil { * @return 返回两个数中大的一个值 */ public static double returnMax(double v1, double v2) { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); return b1.max(b2).doubleValue(); } @@ -330,8 +337,8 @@ public class BigDecimalUtil { * @return 返回两个数中小的一个值 */ public static double returnMin(double v1, double v2) { - BigDecimal b1 = new BigDecimal(v1); - BigDecimal b2 = new BigDecimal(v2); + BigDecimal b1 = BigDecimal.valueOf(v1); + BigDecimal b2 = BigDecimal.valueOf(v2); return b1.min(b2).doubleValue(); } diff --git a/docs/java/basis/generics-and-wildcards.md b/docs/java/basis/generics-and-wildcards.md index bd9cd4b00bf..6904c622f16 100644 --- a/docs/java/basis/generics-and-wildcards.md +++ b/docs/java/basis/generics-and-wildcards.md @@ -3,18 +3,15 @@ title: 泛型&通配符详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: 泛型,通配符,类型擦除,上界通配符,下界通配符,PECS,泛型方法 + - - meta + - name: description + content: 解析 Java 泛型与通配符的语法与原理,涵盖类型擦除、边界与 PECS 原则等高频知识点。 --- **泛型&通配符** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍以及获取方法)中。 -[《Java 面试指北》](hhttps://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) 的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn/#/) 的补充完善,两者可以配合使用。 - -![](https://oss.javaguide.cn/xingqiu/image-20220304102536445.png) - -[《Java 面试指北》](hhttps://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)只是星球内部众多资料中的一个,星球还有很多其他优质资料比如[专属专栏](https://javaguide.cn/zhuanlan/)、Java 编程视频、PDF 资料。 - -![](https://oss.javaguide.cn/xingqiu/image-20220211231206733.png) - - - diff --git a/docs/java/basis/java-basic-questions-01.md b/docs/java/basis/java-basic-questions-01.md index 49e00a73948..fbefddc40c2 100644 --- a/docs/java/basis/java-basic-questions-01.md +++ b/docs/java/basis/java-basic-questions-01.md @@ -44,7 +44,7 @@ head: 除了 Java SE 和 Java EE,还有一个 Java ME(Java Platform,Micro Edition)。Java ME 是 Java 的微型版本,主要用于开发嵌入式消费电子设备的应用程序,例如手机、PDA、机顶盒、冰箱、空调等。Java ME 无需重点关注,知道有这个东西就好了,现在已经用不上了。 -### JVM vs JDK vs JRE +### ⭐️JVM vs JDK vs JRE #### JVM @@ -87,7 +87,7 @@ JRE 是运行已编译 Java 程序所需的环境,主要包含以下两个部 定制的、模块化的 Java 运行时映像有助于简化 Java 应用的部署和节省内存并增强安全性和可维护性。这对于满足现代应用程序架构的需求,如虚拟化、容器化、微服务和云原生开发,是非常重要的。 -### 什么是字节码?采用字节码的好处是什么? +### ⭐️什么是字节码?采用字节码的好处是什么? 在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 `.class` 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以, Java 程序运行时相对来说还是高效的(不过,和 C、 C++,Rust,Go 等语言还是有一定差距的),而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。 @@ -114,7 +114,7 @@ JDK、JRE、JVM、JIT 这四者的关系如下图所示。 ![JVM 的大致结构模型](https://oss.javaguide.cn/github/javaguide/java/basis/jvm-rough-structure-model.png) -### 为什么说 Java 语言“编译与解释并存”? +### ⭐️为什么说 Java 语言“编译与解释并存”? 其实这个问题我们讲字节码的时候已经提到过,因为比较重要,所以我们这里再提一下。 @@ -282,7 +282,7 @@ Java 中的注释有三种: 官方文档:[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/\_keywords.html](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html) -### 自增自减运算符 +### ⭐️自增自减运算符 在写代码的过程中,常见的一种情况是需要某个整数类型变量增加 1 或减少 1。Java 提供了自增运算符 (`++`) 和自减运算符 (`--`) 来简化这种操作。 @@ -305,7 +305,7 @@ int e = --d; 答案:`a = 11` 、`b = 9` 、 `c = 10` 、 `d = 10` 、 `e = 10`。 -### 移位运算符 +### ⭐️移位运算符 移位运算符是最基本的运算符之一,几乎每种编程语言都包含这一运算符。移位操作中,被操作的数据被视为二进制数,移位就是将其向左或向右移动若干位的运算。 @@ -442,7 +442,7 @@ xixi haha ``` -## 基本数据类型 +## ⭐️基本数据类型 ### Java 中的几种基本数据类型了解么? @@ -736,7 +736,7 @@ System.out.println(l + 1 == Long.MIN_VALUE); // true ## 变量 -### 成员变量与局部变量的区别? +### ⭐️成员变量与局部变量的区别? - **语法形式**:从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 `public`,`private`,`static` 等修饰符所修饰,而局部变量不能被访问控制修饰符及 `static` 所修饰;但是,成员变量和局部变量都能被 `final` 所修饰。 - **存储方式**:从变量在内存中的存储方式来看,如果成员变量是使用 `static` 修饰的,那么这个成员变量是属于类的,如果没有使用 `static` 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。 @@ -745,11 +745,16 @@ System.out.println(l + 1 == Long.MIN_VALUE); // true **为什么成员变量有默认值?** -1. 先不考虑变量类型,如果没有默认值会怎样?变量存储的是内存地址对应的任意随机值,程序读取该值运行会出现意外。 +核心原因是为了保证对象状态的安全和可预测性。 -2. 默认值有两种设置方式:手动和自动,根据第一点,没有手动赋值一定要自动赋值。成员变量在运行时可借助反射等方法手动赋值,而局部变量不行。 +成员变量和局部变量在这个规则上不同,主要是因为它们的**生命周期**不一样,导致了编译器对它们的“控制力”也不同。 -3. 对于编译器(javac)来说,局部变量没赋值很好判断,可以直接报错。而成员变量可能是运行时赋值,无法判断,误报“没默认值”又会影响用户体验,所以采用自动赋默认值。 +- **局部变量**只活在一个方法里,编译器能清楚地看到它是否在使用前被赋值,所以编译器会强制你必须手动赋值,否则就报错。 +- **成员变量**是跟着对象走的,它的值可能在构造函数里赋,也可能在后面的某个 `setter` 方法里赋。编译器在编译时**无法预测**它到底什么时候会被赋值。 + +并且,如果一个变量没有被初始化,它的内存里存放的就是“垃圾值”——之前那块内存遗留下的任意数据。如果程序读取并使用了这个垃圾值,就会产生完全不可预测的结果,比如一个数字变成了随机数,一个对象引用变成了非法地址,这会直接导致程序崩溃或出现诡异的 bug。 + +为了避免你拿到一个含有“垃圾值”的危险对象,Java干脆为所有成员变量提供了一个安全的默认值(如 null 或 0),作为一种**安全兜底机制**。 成员变量与局部变量代码示例: @@ -914,7 +919,7 @@ public class Example { } ``` -### 静态方法和实例方法有何不同? +### ⭐️静态方法和实例方法有何不同? **1、调用方式** @@ -947,7 +952,7 @@ public class Person { 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。 -### 重载和重写有什么区别? +### ⭐️重载和重写有什么区别? > 重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理 > @@ -984,14 +989,13 @@ public class Person { 综上:**重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。** -| 区别点 | 重载方法 | 重写方法 | -| :--------- | :------- | :--------------------------------------------------------------- | -| 发生范围 | 同一个类 | 子类 | -| 参数列表 | 必须修改 | 一定不能修改 | -| 返回类型 | 可修改 | 子类方法返回值类型应比父类方法返回值类型更小或相等 | -| 异常 | 可修改 | 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等; | -| 访问修饰符 | 可修改 | 一定不能做更严格的限制(可以降低限制) | -| 发生阶段 | 编译期 | 运行期 | +| 区别点 | 重载 (Overloading) | 重写 (Overriding) | +| -------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | +| **发生范围** | 同一个类中。 | 父类与子类之间(存在继承关系)。 | +| **方法签名** | 方法名**必须相同**,但**参数列表必须不同**(参数的类型、个数或顺序至少有一项不同)。 | 方法名、参数列表**必须完全相同**。 | +| **返回类型** | 与返回值类型**无关**,可以任意修改。 | 子类方法的返回类型必须与父类方法的返回类型**相同**,或者是其**子类**。 | +| **访问修饰符** | 与访问修饰符**无关**,可以任意修改。 | 子类方法的访问权限**不能低于**父类方法的访问权限。(public > protected > default > private) | +| **绑定时期** | 编译时绑定或称静态绑定 | 运行时绑定 (Run-time Binding) 或称动态绑定 | **方法的重写要遵循“两同两小一大”**(以下内容摘录自《疯狂 Java 讲义》,[issue#892](https://github.com/Snailclimb/JavaGuide/issues/892) ): diff --git a/docs/java/basis/java-basic-questions-02.md b/docs/java/basis/java-basic-questions-02.md index 9f8739f291d..42455193f45 100644 --- a/docs/java/basis/java-basic-questions-02.md +++ b/docs/java/basis/java-basic-questions-02.md @@ -16,7 +16,7 @@ head: ## 面向对象基础 -### 面向对象和面向过程的区别 +### ⭐️面向对象和面向过程的区别 面向过程编程(Procedural-Oriented Programming,POP)和面向对象编程(Object-Oriented Programming,OOP)是两种常见的编程范式,两者的主要区别在于解决问题的方式不同: @@ -104,7 +104,7 @@ new 运算符,new 创建对象实例(对象实例在堆内存中),对象 - 一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球); - 一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。 -### 对象的相等和引用相等的区别 +### ⭐️对象的相等和引用相等的区别 - 对象的相等一般比较的是内存中存放的内容是否相等。 - 引用相等一般比较的是他们指向的内存地址是否相等。 @@ -156,7 +156,7 @@ true 构造方法**不能被重写(override)**,但**可以被重载(overload)**。因此,一个类中可以有多个构造方法,这些构造方法可以具有不同的参数列表,以提供不同的对象初始化方式。 -### 面向对象三大特征 +### ⭐️面向对象三大特征 #### 封装 @@ -210,7 +210,7 @@ public class Student { - 多态不能调用“只在子类存在但在父类不存在”的方法; - 如果子类重写了父类的方法,真正执行的是子类重写的方法,如果子类没有重写父类的方法,执行的是父类的方法。 -### 接口和抽象类有什么共同点和区别? +### ⭐️接口和抽象类有什么共同点和区别? #### 接口和抽象类的共同点 @@ -363,7 +363,7 @@ System.out.println(person1.getAddress() == person1Copy.getAddress()); ![shallow&deep-copy](https://oss.javaguide.cn/github/javaguide/java/basis/shallow&deep-copy.png) -## Object +## ⭐️Object ### Object 类的常见方法有哪些? @@ -455,7 +455,7 @@ System.out.println(42 == 42.0);// true `String` 中的 `equals` 方法是被重写过的,因为 `Object` 的 `equals` 方法是比较的对象的内存地址,而 `String` 的 `equals` 方法比较的是对象的值。 -当创建 `String` 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 `String` 对象。 +当使用字符串字面量创建 `String` 类型的对象(如`String aa = "ab"`)时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用;如果没有,就在常量池中创建一个 `String` 对象并赋给当前引用。但当使用`new`关键字创建对象(如`String a = new String("ab")`)时,虚拟机总是会在堆内存中**创建一个新的对象**并使用常量池中的值(如果没有,会先在字符串常量池中创建字符串对象 "ab")进行初始化,然后赋给当前引用。 `String`类`equals()`方法: @@ -504,13 +504,18 @@ public native int hashCode(); ### 为什么要有 hashCode? -我们以“`HashSet` 如何检查重复”为例子来说明为什么要有 `hashCode`? +我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode? -下面这段内容摘自我的 Java 启蒙书《Head First Java》: +当我们把对象加入 HashSet 时,HashSet 会先调用对象的 `hashCode()` 方法,得到一个“哈希值”,并通过内部散列函数对这个哈希值再做一次简单的转换(比如取余),决定这条数据应该放进底层数组的哪一个桶(bucket,对应到底层数组的某个位置): -> 当你把对象加入 `HashSet` 时,`HashSet` 会先计算对象的 `hashCode` 值来判断对象加入的位置,同时也会与其他已经加入的对象的 `hashCode` 值作比较,如果没有相符的 `hashCode`,`HashSet` 会假设对象没有重复出现。但是如果发现有相同 `hashCode` 值的对象,这时会调用 `equals()` 方法来检查 `hashCode` 相等的对象是否真的相同。如果两者相同,`HashSet` 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 `equals` 的次数,相应就大大提高了执行速度。 +1. 如果该桶当前是空的,就直接将对象对应的节点插入到这个桶中。 +2. 如果该桶中已经有其他元素,HashSet 会在这个桶对应的链表或红黑树中逐个比较: + - 对于**哈希值不同**的节点,直接跳过; + - 对于**哈希值相同**的节点,则会进一步调用 equals() 方法来检查这两个对象是否“相等”: + – 如果 `equals()` 返回 true,说明集合中已经存在与当前对象等价的元素,`HashSet` 就不会再次加入它; + – 如果返回 false, 则认为是新元素,会将该对象作为一个新节点加入到**同一个桶**的链表或红黑树中。 -其实, `hashCode()` 和 `equals()`都是用于比较两个对象是否相等。 +通过先利用 `hashCode()` 将候选范围缩小到同一个桶内,再在桶内少量元素上调用 `equals()` 做精确判断,`HashSet` 大大减少了 `equals()` 的调用次数,从而提高了查找和插入的执行效率。 **那为什么 JDK 还要同时提供这两个方法呢?** @@ -551,7 +556,7 @@ public native int hashCode(); ## String -### String、StringBuffer、StringBuilder 的区别? +### ⭐️String、StringBuffer、StringBuilder 的区别? **可变性** @@ -579,6 +584,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { `String` 中的对象是不可变的,也就可以理解为常量,线程安全。`AbstractStringBuilder` 是 `StringBuilder` 与 `StringBuffer` 的公共父类,定义了一些字符串的基本操作,如 `expandCapacity`、`append`、`insert`、`indexOf` 等公共方法。`StringBuffer` 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。`StringBuilder` 并没有对方法进行加同步锁,所以是非线程安全的。 + + **性能** 每次对 `String` 类型进行改变的时候,都会生成一个新的 `String` 对象,然后将指针指向新的 `String` 对象。`StringBuffer` 每次都会对 `StringBuffer` 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 `StringBuilder` 相比使用 `StringBuffer` 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。 @@ -589,7 +596,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { - 单线程操作字符串缓冲区下操作大量数据: 适用 `StringBuilder` - 多线程操作字符串缓冲区下操作大量数据: 适用 `StringBuffer` -### String 为什么是不可变的? +### ⭐️String 为什么是不可变的? `String` 类中使用 `final` 关键字修饰字符数组来保存字符串,~~所以`String` 对象是不可变的。~~ @@ -636,7 +643,7 @@ public final class String implements java.io.Serializable, Comparable, C > > 这是官方的介绍: 。 -### 字符串拼接用“+” 还是 StringBuilder? +### ⭐️字符串拼接用“+” 还是 StringBuilder? Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。 @@ -689,22 +696,22 @@ System.out.println(s); `String` 中的 `equals` 方法是被重写过的,比较的是 String 字符串的值是否相等。 `Object` 的 `equals` 方法是比较的对象的内存地址。 -### 字符串常量池的作用了解吗? +### ⭐️字符串常量池的作用了解吗? **字符串常量池** 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。 ```java -// 在字符串常量池中创建字符串对象 ”ab“ -// 将字符串对象 ”ab“ 的引用赋值给 aa +// 1.在字符串常量池中查询字符串对象 "ab",如果没有则创建"ab"并放入字符串常量池 +// 2.将字符串对象 "ab" 的引用赋值给 aa String aa = "ab"; -// 直接返回字符串常量池中字符串对象 ”ab“,赋值给引用 bb +// 直接返回字符串常量池中字符串对象 "ab",赋值给引用 bb String bb = "ab"; System.out.println(aa==bb); // true ``` 更多关于字符串常量池的介绍可以看一下 [Java 内存区域详解](https://javaguide.cn/java/jvm/memory-area.html) 这篇文章。 -### String s1 = new String("abc");这句话创建了几个字符串对象? +### ⭐️String s1 = new String("abc");这句话创建了几个字符串对象? 先说答案:会创建 1 或 2 个字符串对象。 diff --git a/docs/java/basis/java-basic-questions-03.md b/docs/java/basis/java-basic-questions-03.md index 496e18827da..8f9dcc17073 100644 --- a/docs/java/basis/java-basic-questions-03.md +++ b/docs/java/basis/java-basic-questions-03.md @@ -25,9 +25,14 @@ head: 在 Java 中,所有的异常都有一个共同的祖先 `java.lang` 包中的 `Throwable` 类。`Throwable` 类有两个重要的子类: - **`Exception`** :程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。 -- **`Error`**:`Error` 属于程序无法处理的错误 ,~~我们没办法通过 `catch` 来进行捕获~~不建议通过`catch`捕获 。例如 Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。 +- **`Error`** :`Error` 属于程序无法处理的错误 ,~~我们没办法通过 `catch` 来进行捕获~~不建议通过`catch`捕获 。例如 Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。 -### Checked Exception 和 Unchecked Exception 有什么区别? +### ClassNotFoundException 和 NoClassDefFoundError 的区别 + +- `ClassNotFoundException` 是Exception,发生在使用反射等动态加载时找不到类,是可预期的,可以捕获处理。 +- `NoClassDefFoundError` 是Error,是编译时存在的类,在运行时链接不到了(比如 jar 包缺失),是环境问题,导致 JVM 无法继续。 + +### ⭐️Checked Exception 和 Unchecked Exception 有什么区别? **Checked Exception** 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 `catch`或者`throws` 关键字处理的话,就没办法通过编译。 @@ -53,6 +58,14 @@ head: ![](https://oss.javaguide.cn/github/javaguide/java/basis/unchecked-exception.png) +### 你更倾向于使用 Checked Exception 还是 Unchecked Exception? + +默认使用 Unchecked Exception,只在必要时才用 Checked Exception。 + +我们可以把 Unchecked Exception(比如 `NullPointerException`)看作是代码 Bug。对待 Bug,最好的方式是让它暴露出来然后去修复代码,而不是用 `try-catch` 去掩盖它。 + +一般来说,只在一种情况下使用 Checked Exception:当这个异常是业务逻辑的一部分,并且调用方必须处理它时。比如说,一个余额不足异常。这不是 bug,而是一个正常的业务分支,我需要用 Checked Exception 来强制调用者去处理这种情况,比如提示用户去充值。这样就能在保证关键业务逻辑完整性的同时,让代码尽可能保持简洁。 + ### Throwable 类常用方法有哪些? - `String getMessage()`: 返回异常发生时的详细信息 @@ -205,7 +218,7 @@ catch (IOException e) { } ``` -### 异常使用有哪些需要注意的地方? +### ⭐️异常使用有哪些需要注意的地方? - 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。 - 抛出的异常信息一定要有意义。 @@ -317,9 +330,9 @@ printArray( stringArray ); - 构建集合工具类(参考 `Collections` 中的 `sort`, `binarySearch` 方法)。 - …… -## 反射 +## ⭐️反射 -关于反射的详细解读,请看这篇文章 [Java 反射机制详解](./reflection.md) 。 +关于反射的详细解读,请看这篇文章 [Java 反射机制详解](https://javaguide.cn/java/basis/reflection.html) 。 ### 什么是反射? @@ -384,6 +397,56 @@ public class DebugInvocationHandler implements InvocationHandler { 像 MyBatis、Hibernate 这种框架,能帮你把数据库查出来的一行行数据,自动变成一个个 Java 对象。它是怎么知道数据库字段对应哪个 Java 属性的?还是靠反射。它通过反射获取 Java 类的属性列表,然后把查询结果按名字或配置对应起来,再用反射调用 setter 或直接修改字段值。反过来,保存对象到数据库时,也是用反射读取属性值来拼 SQL。 +## 代理 + +关于 Java 代理的详细介绍,可以看看笔者写的 [Java 代理模式详解](https://javaguide.cn/java/basis/proxy.html "Java 代理模式详解")这篇文章。 + +### 如何实现动态代理? + +动态代理是一种非常强大的设计模式,它允许我们在**不修改源代码**的情况下,对一个类或对象的方法进行**功能增强(Enhancement)**。 + +在 Java 中,实现动态代理最主流的方式有两种:**JDK 动态代理** 和 **CGLIB 动态代理**。 + +**第一种:JDK 动态代理** + +Java 官方提供的,其核心要求是目标类必须实现一个或多个接口。JDK 动态代理在运行时,会利用 `Proxy.newProxyInstance()` 方法,动态地创建一个实现了这些接口的代理类的实例。这个代理类在内存中生成,你看不到它的 `.java` 或 `.class` 文件。 + +当你调用代理对象的任何一个方法时,这个调用都会被转发到我们提供的一个 `InvocationHandler` 接口的 `invoke` 方法中。在 `invoke` 方法里,我们就可以在调用原始方法(目标方法)之前或之后,加入我们自己的增强逻辑。 + +**第二种:CGLIB 动态代理** + +CGLIB 是一个第三方的代码生成库。它的原理与 JDK 完全不同,它不要求被代理的类实现接口。它在运行时,动态生成目标类的子类作为代理类(通过 ASM 字节码操作技术)。然后,它会重写父类(也就是被代理类)中所有非 `final`、`private` 和 `static` 的方法。 + +当你调用代理对象的任何一个方法时,这个调用会被 CGLIB 的 `MethodInterceptor` 接口的 `intercept` 方法拦截。和 `InvocationHandler` 的 `invoke` 方法一样,我们可以在 `intercept` 方法里,在调用原始的父类方法之前或之后,加入我们的增强逻辑。 + +### 静态代理和动态代理有什么区别? + +静态代理和动态代理的核心差异在于 **代理关系的确定时机、实现灵活性及维护成本** 。 + +| 对比维度 | 静态代理 (Static Proxy) | 动态代理 (Dynamic Proxy) | +| ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| 代理关系确定时机 | 编译期(编译后生成固定的 `.class` 字节码文件) | 运行时(动态生成代理类字节码并加载到 JVM) | +| 实现方式 | 手动编写代理类,需与目标类实现同一接口,一对一绑定 | 无需手动编写代理类,通过 `Handler`/`Interceptor` 封装增强逻辑,一对多复用 | +| 接口依赖 | 必须实现接口(代理类与目标类遵循同一接口规范) | 支持代理接口或直接代理实现类 | +| 代码量与维护性 | 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 | 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑 | +| 核心优势 | 实现简单、逻辑直观,无额外框架依赖 | 灵活性强、复用性高,降低重复编码,适配复杂场景 | +| 典型应用场景 | 简单的装饰器模式、少量固定类的增强需求 | Spring AOP、RPC 框架(如 Dubbo)、ORM 框架 | + +### ⭐️JDK 动态代理和 CGLIB 动态代理有什么区别? + +1. JDK 动态代理是官方的,它要求被代理的类必须实现接口。它的原理是动态生成一个接口的实现类来作为代理。CGLIB 是第三方的,它不需要接口。它的原理是动态生成一个被代理类的子类来作为代理。但也正因为是继承,所以它不能代理 `final` 的类,被代理的方法也不能是 `final` 或 `private` 。 +2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。 + +### ⭐️介绍一下动态代理在框架中的实际应用场景 + +动态代理最典型的应用场景就是**Spring AOP**。 + +AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 + +Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: + +![SpringAOPProcess](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/230ae587a322d6e4d09510161987d346.jpeg) + ## 注解 ### 何谓注解? @@ -413,9 +476,9 @@ JDK 提供了很多内置的注解(比如 `@Override`、`@Deprecated`),同 - **编译期直接扫描**:编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用`@Override` 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。 - **运行期通过反射处理**:像框架中自带的注解(比如 Spring 框架的 `@Value`、`@Component`)都是通过反射来进行处理的。 -## SPI +## ⭐️SPI -关于 SPI 的详细解读,请看这篇文章 [Java SPI 机制详解](./spi.md) 。 +关于 SPI 的详细解读,请看这篇文章 [Java SPI 机制详解](https://javaguide.cn/java/basis/spi.html) 。 ### 何谓 SPI? @@ -449,9 +512,9 @@ SPI 将服务接口和具体的服务实现分离开来,将服务调用方和 - 需要遍历加载所有的实现类,不能做到按需加载,这样效率还是相对较低的。 - 当多个 `ServiceLoader` 同时 `load` 时,会有并发问题。 -## 序列化和反序列化 +## ⭐️序列化和反序列化 -关于序列化和反序列化的详细解读,请看这篇文章 [Java 序列化详解](./serialization.md) ,里面涉及到的知识点和面试题更全面。 +关于序列化和反序列化的详细解读,请看这篇文章 [Java 序列化详解](https://javaguide.cn/java/basis/serialization.html) ,里面涉及到的知识点和面试题更全面。 ### 什么是序列化?什么是反序列化? @@ -500,7 +563,7 @@ SPI 将服务接口和具体的服务实现分离开来,将服务调用方和 对于不想进行序列化的变量,使用 `transient` 关键字修饰。 -`transient` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。 +`transient` 关键字的作用是:阻止实例中那些用此关键字修饰的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。 关于 `transient` 还有几点注意: @@ -526,9 +589,9 @@ JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存 关于 I/O 的详细解读,请看下面这几篇文章,里面涉及到的知识点和面试题更全面。 -- [Java IO 基础知识总结](../io/io-basis.md) -- [Java IO 设计模式总结](../io/io-design-patterns.md) -- [Java IO 模型详解](../io/io-model.md) +- [Java IO 基础知识总结](https://javaguide.cn/java/io/io-basis.html) +- [Java IO 设计模式总结](https://javaguide.cn/java/io/io-design-patterns.html) +- [Java IO 模型详解](https://javaguide.cn/java/io/io-model.html) ### Java IO 流了解吗? @@ -550,11 +613,11 @@ Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来 ### Java IO 中的设计模式有哪些? -参考答案:[Java IO 设计模式总结](../io/io-design-patterns.md) +参考答案:[Java IO 设计模式总结](https://javaguide.cn/java/io/io-design-patterns.html) -### BIO、NIO 和 AIO 的区别? +### ⭐️BIO、NIO 和 AIO 的区别? -参考答案:[Java IO 模型详解](../io/io-model.md) +参考答案:[Java IO 模型详解](https://javaguide.cn/java/io/io-model.html) ## 语法糖 diff --git a/docs/java/basis/java-keyword-summary.md b/docs/java/basis/java-keyword-summary.md index 1d21e2467ed..2dee3f100ad 100644 --- a/docs/java/basis/java-keyword-summary.md +++ b/docs/java/basis/java-keyword-summary.md @@ -1,3 +1,17 @@ +--- +title: Java 关键字总结 +category: Java +tag: + - Java基础 +head: + - - meta + - name: keywords + content: Java 关键字,final,static,this,super,abstract,interface,enum,volatile,transient + - - meta + - name: description + content: 梳理常见 Java 关键字的语义与用法差异,便于快速查阅与掌握。 +--- + # final,static,this,super 关键字总结 ## final 关键字 diff --git a/docs/java/basis/proxy.md b/docs/java/basis/proxy.md index 615b0f00e42..dafd1b436aa 100644 --- a/docs/java/basis/proxy.md +++ b/docs/java/basis/proxy.md @@ -3,6 +3,13 @@ title: Java 代理模式详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: 代理模式,静态代理,动态代理,JDK 动态代理,CGLIB,横切增强,设计模式 + - - meta + - name: description + content: 详解 Java 代理模式的静态与动态实现,理解 JDK/CGLIB 动态代理的原理与应用场景。 --- ## 1. 代理模式 @@ -21,7 +28,7 @@ tag: ## 2. 静态代理 -**静态代理中,我们对目标对象的每个方法的增强都是手动完成的(_后面会具体演示代码_),非常不灵活(_比如接口一旦新增加方法,目标对象和代理对象都要进行修改_)且麻烦(_需要对每个目标类都单独写一个代理类_)。** 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。 +静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。 实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。 上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, **静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。** @@ -99,7 +106,7 @@ after method send() ## 3. 动态代理 -相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( _CGLIB 动态代理机制_)。 +相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。 **从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。** @@ -330,10 +337,10 @@ public class DebugMethodInterceptor implements MethodInterceptor { /** - * @param o 被代理的对象(需要增强的对象) + * @param o 代理对象本身(注意不是原始对象,如果使用method.invoke(o, args)会导致循环调用) * @param method 被拦截的方法(需要增强的方法) * @param args 方法入参 - * @param methodProxy 用于调用原始方法 + * @param methodProxy 高性能的方法调用机制,避免反射开销 */ @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { @@ -387,13 +394,21 @@ after method send ### 3.3. JDK 动态代理和 CGLIB 动态代理对比 -1. **JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。** 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。 +1. JDK 动态代理是官方的,它要求被代理的类必须实现接口。它的原理是动态生成一个接口的实现类来作为代理。CGLIB 是第三方的,它不需要接口。它的原理是动态生成一个被代理类的子类来作为代理。但也正因为是继承,所以它不能代理 `final` 的类,被代理的方法也不能是 `final` 或 `private` 。 2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。 ## 4. 静态代理和动态代理的对比 -1. **灵活性**:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的! -2. **JVM 层面**:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。 +静态代理和动态代理的核心差异在于 **代理关系的确定时机、实现灵活性及维护成本** 。 + +| 对比维度 | 静态代理 (Static Proxy) | 动态代理 (Dynamic Proxy) | +| ---------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| 代理关系确定时机 | 编译期(编译后生成固定的 `.class` 字节码文件) | 运行时(动态生成代理类字节码并加载到 JVM) | +| 实现方式 | 手动编写代理类,需与目标类实现同一接口,一对一绑定 | 无需手动编写代理类,通过 `Handler`/`Interceptor` 封装增强逻辑,一对多复用 | +| 接口依赖 | 必须实现接口(代理类与目标类遵循同一接口规范) | 支持代理接口或直接代理实现类 | +| 代码量与维护性 | 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 | 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑 | +| 核心优势 | 实现简单、逻辑直观,无额外框架依赖 | 灵活性强、复用性高,降低重复编码,适配复杂场景 | +| 典型应用场景 | 简单的装饰器模式、少量固定类的增强需求 | Spring AOP、RPC 框架(如 Dubbo)、ORM 框架 | ## 5. 总结 diff --git a/docs/java/basis/reflection.md b/docs/java/basis/reflection.md index 3ce8ccab9a9..a951992c95e 100644 --- a/docs/java/basis/reflection.md +++ b/docs/java/basis/reflection.md @@ -3,6 +3,13 @@ title: Java 反射机制详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: 反射,Class,Method,Field,动态代理,运行时分析,框架原理 + - - meta + - name: description + content: 系统讲解 Java 反射的核心概念与常见用法,结合动态代理与框架底层机制理解运行时能力。 --- ## 何为反射? diff --git a/docs/java/basis/serialization.md b/docs/java/basis/serialization.md index f6ab9071967..a046a06199d 100644 --- a/docs/java/basis/serialization.md +++ b/docs/java/basis/serialization.md @@ -3,6 +3,13 @@ title: Java 序列化详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: 序列化,反序列化,Serializable,transient,serialVersionUID,ObjectInputStream,ObjectOutputStream,协议 + - - meta + - name: description + content: 讲解 Java 对象的序列化/反序列化机制与关键细节,涵盖 transient、版本号与常见应用场景。 --- ## 什么是序列化和反序列化? @@ -101,7 +108,7 @@ public class RpcRequest implements Serializable { 对于不想进行序列化的变量,可以使用 `transient` 关键字修饰。 -`transient` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。 +`transient` 关键字的作用是:阻止实例中那些用此关键字修饰的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。 关于 `transient` 还有几点注意: diff --git a/docs/java/basis/syntactic-sugar.md b/docs/java/basis/syntactic-sugar.md index 3ce9bfc1099..31444ac8385 100644 --- a/docs/java/basis/syntactic-sugar.md +++ b/docs/java/basis/syntactic-sugar.md @@ -6,10 +6,10 @@ tag: head: - - meta - name: keywords - content: Java 语法糖 + content: 语法糖,自动装箱拆箱,泛型,增强 for,可变参数,枚举,内部类,类型推断 - - meta - name: description - content: 这篇文章介绍了 12 种 Java 中常用的语法糖。所谓语法糖就是提供给开发人员便于开发的一种语法而已。但是这种语法只有开发人员认识。要想被执行,需要进行解糖,即转成 JVM 认识的语法。当我们把语法糖解糖之后,你就会发现其实我们日常使用的这些方便的语法,其实都是一些其他更简单的语法构成的。有了这些语法糖,我们在日常开发的时候可以大大提升效率,但是同时也要避免过渡使用。使用之前最好了解下原理,避免掉坑。 + content: 总结 Java 常见语法糖及编译期的“解糖”原理,帮助在提升效率的同时理解底层机制并避免误用。 --- > 作者:Hollis @@ -246,7 +246,7 @@ public static transient void print(String strs[]) } ``` -从反编译后代码可以看出,可变参数在被使用的时候,他首先会创建一个数组,数组的长度就是调用该方法是传递的实参的个数,然后再把参数值全部放到这个数组当中,然后再把这个数组作为参数传递到被调用的方法中。(注:`trasient` 仅在修饰成员变量时有意义,此处 “修饰方法” 是由于在 javassist 中使用相同数值分别表示 `trasient` 以及 `vararg`,见 [此处](https://github.com/jboss-javassist/javassist/blob/7302b8b0a09f04d344a26ebe57f29f3db43f2a3e/src/main/javassist/bytecode/AccessFlag.java#L32)。) +从反编译后代码可以看出,可变参数在被使用的时候,他首先会创建一个数组,数组的长度就是调用该方法是传递的实参的个数,然后再把参数值全部放到这个数组当中,然后再把这个数组作为参数传递到被调用的方法中。(注:`transient` 仅在修饰成员变量时有意义,此处 “修饰方法” 是由于在 javassist 中使用相同数值分别表示 `transient` 以及 `vararg`,见 [此处](https://github.com/jboss-javassist/javassist/blob/7302b8b0a09f04d344a26ebe57f29f3db43f2a3e/src/main/javassist/bytecode/AccessFlag.java#L32)。) ### 枚举 @@ -263,6 +263,7 @@ public enum t { 然后我们使用反编译,看看这段代码到底是怎么实现的,反编译后代码内容如下: ```java +//Java编译器会自动将枚举名处理为合法类名(首字母大写): t -> T public final class T extends Enum { private T(String s, int i) @@ -308,7 +309,7 @@ public final class T extends Enum **内部类之所以也是语法糖,是因为它仅仅是一个编译时的概念,`outer.java`里面定义了一个内部类`inner`,一旦编译成功,就会生成两个完全不同的`.class`文件了,分别是`outer.class`和`outer$inner.class`。所以内部类的名字完全可以和它的外部类名字相同。** ```java -public class OutterClass { +public class OuterClass { private String userName; public String getUserName() { @@ -337,10 +338,10 @@ public class OutterClass { } ``` -以上代码编译后会生成两个 class 文件:`OutterClass$InnerClass.class`、`OutterClass.class` 。当我们尝试对`OutterClass.class`文件进行反编译的时候,命令行会打印以下内容:`Parsing OutterClass.class...Parsing inner class OutterClass$InnerClass.class... Generating OutterClass.jad` 。他会把两个文件全部进行反编译,然后一起生成一个`OutterClass.jad`文件。文件内容如下: +以上代码编译后会生成两个 class 文件:`OuterClass$InnerClass.class`、`OuterClass.class` 。当我们尝试对`OuterClass.class`文件进行反编译的时候,命令行会打印以下内容:`Parsing OuterClass.class...Parsing inner class OuterClass$InnerClass.class... Generating OuterClass.jad` 。他会把两个文件全部进行反编译,然后一起生成一个`OuterClass.jad`文件。文件内容如下: ```java -public class OutterClass +public class OuterClass { class InnerClass { @@ -353,16 +354,16 @@ public class OutterClass this.name = name; } private String name; - final OutterClass this$0; + final OuterClass this$0; InnerClass() { - this.this$0 = OutterClass.this; + this.this$0 = OuterClass.this; super(); } } - public OutterClass() + public OuterClass() { } public String getUserName() @@ -385,37 +386,37 @@ public class OutterClass ```java //省略其他属性 -public class OutterClass { +public class OuterClass { private String userName; ...... class InnerClass{ ...... public void printOut(){ - System.out.println("Username from OutterClass:"+userName); + System.out.println("Username from OuterClass:"+userName); } } } -// 此时,使用javap -p命令对OutterClass反编译结果: -public classOutterClass { +// 此时,使用javap -p命令对OuterClass反编译结果: +public classOuterClass { private String userName; ...... - static String access$000(OutterClass); + static String access$000(OuterClass); } // 此时,InnerClass的反编译结果: -class OutterClass$InnerClass { - final OutterClass this$0; +class OuterClass$InnerClass { + final OuterClass this$0; ...... public void printOut(); } ``` -实际上,在编译完成之后,inner 实例内部会有指向 outer 实例的引用`this$0`,但是简单的`outer.name`是无法访问 private 属性的。从反编译的结果可以看到,outer 中会有一个桥方法`static String access$000(OutterClass)`,恰好返回 String 类型,即 userName 属性。正是通过这个方法实现内部类访问外部类私有属性。所以反编译后的`printOut()`方法大致如下: +实际上,在编译完成之后,inner 实例内部会有指向 outer 实例的引用`this$0`,但是简单的`outer.name`是无法访问 private 属性的。从反编译的结果可以看到,outer 中会有一个桥方法`static String access$000(OuterClass)`,恰好返回 String 类型,即 userName 属性。正是通过这个方法实现内部类访问外部类私有属性。所以反编译后的`printOut()`方法大致如下: ```java public void printOut() { - System.out.println("Username from OutterClass:" + OutterClass.access$000(this.this$0)); + System.out.println("Username from OuterClass:" + OuterClass.access$000(this.this$0)); } ``` @@ -426,7 +427,7 @@ public void printOut() { 3. 匿名内部类、局部内部类通过复制使用局部变量,该变量初始化之后就不能被修改。以下是一个案例: ```java -public class OutterClass { +public class OuterClass { private String userName; public void test(){ @@ -447,10 +448,10 @@ public class OutterClass { ```java //javap命令反编译Inner的结果 //i被复制进内部类,且为final -class OutterClass$1Inner { +class OuterClass$1Inner { final int val$i; - final OutterClass this$0; - OutterClass$1Inner(); + final OuterClass this$0; + OuterClass$1Inner(); public void printName(); } @@ -701,7 +702,7 @@ public static transient void main(String args[]) } else br.close(); - break MISSING_BLOCK_LABEL_113; + break MISSING_BLOCK_LABEL_113; //该标签为反编译工具的生成错误,(不是Java语法本身的内容)属于反编译工具的临时占位符。正常情况下编译器生成的字节码不会包含这种无效标签。 Exception exception; exception; if(br != null) diff --git a/docs/java/basis/unsafe.md b/docs/java/basis/unsafe.md index fff31af808c..078619421c0 100644 --- a/docs/java/basis/unsafe.md +++ b/docs/java/basis/unsafe.md @@ -3,6 +3,13 @@ title: Java 魔法类 Unsafe 详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: Unsafe,低级操作,内存访问,CAS,堆外内存,本地方法,风险 + - - meta + - name: description + content: 介绍 sun.misc.Unsafe 的能力与典型用法,涵盖内存与对象操作、CAS 支持及风险与限制。 --- > 本文整理完善自下面这两篇优秀的文章: @@ -134,20 +141,34 @@ public native void freeMemory(long address); ```java private void memoryTest() { int size = 4; - long addr = unsafe.allocateMemory(size); - long addr3 = unsafe.reallocateMemory(addr, size * 2); - System.out.println("addr: "+addr); - System.out.println("addr3: "+addr3); + // 1. 分配初始内存 + long oldAddr = unsafe.allocateMemory(size); + System.out.println("Initial address: " + oldAddr); + + // 2. 向初始内存写入数据 + unsafe.putInt(oldAddr, 16843009); // 写入 0x01010101 + System.out.println("Value at oldAddr: " + unsafe.getInt(oldAddr)); + + // 3. 重新分配内存 + long newAddr = unsafe.reallocateMemory(oldAddr, size * 2); + System.out.println("New address: " + newAddr); + + // 4. reallocateMemory 已经将数据从 oldAddr 拷贝到 newAddr + // 所以 newAddr 的前4个字节应该和 oldAddr 的内容一样 + System.out.println("Value at newAddr (first 4 bytes): " + unsafe.getInt(newAddr)); + + // 关键:之后所有操作都应该基于 newAddr,oldAddr 已失效! try { - unsafe.setMemory(null,addr ,size,(byte)1); - for (int i = 0; i < 2; i++) { - unsafe.copyMemory(null,addr,null,addr3+size*i,4); - } - System.out.println(unsafe.getInt(addr)); - System.out.println(unsafe.getLong(addr3)); - }finally { - unsafe.freeMemory(addr); - unsafe.freeMemory(addr3); + // 5. 在新内存块的后半部分写入新数据 + unsafe.putInt(newAddr + size, 33686018); // 写入 0x02020202 + + // 6. 读取整个8字节的long值 + System.out.println("Value at newAddr (full 8 bytes): " + unsafe.getLong(newAddr)); + + } finally { + // 7. 只释放最后有效的内存地址 + unsafe.freeMemory(newAddr); + // 如果尝试 freeMemory(oldAddr),将会导致 double free 错误! } } ``` @@ -155,35 +176,56 @@ private void memoryTest() { 先看结果输出: ```plain -addr: 2433733895744 -addr3: 2433733894944 -16843009 -72340172838076673 +Initial address: 140467048086752 +Value at oldAddr: 16843009 +New address: 140467048086752 +Value at newAddr (first 4 bytes): 16843009 +Value at newAddr (full 8 bytes): 144680345659310337 ``` -分析一下运行结果,首先使用`allocateMemory`方法申请 4 字节长度的内存空间,调用`setMemory`方法向每个字节写入内容为`byte`类型的 1,当使用 Unsafe 调用`getInt`方法时,因为一个`int`型变量占 4 个字节,会一次性读取 4 个字节,组成一个`int`的值,对应的十进制结果为 16843009。 +`reallocateMemory` 的行为类似于 C 语言中的 realloc 函数,它会尝试在不移动数据的情况下扩展或收缩内存块。其行为主要有两种情况: -你可以通过下图理解这个过程: +1. **原地扩容**:如果当前内存块后面有足够的连续空闲空间,`reallocateMemory` 会直接在原地址上扩展内存,并返回原始地址。 +2. **异地扩容**:如果当前内存块后面空间不足,它会寻找一个新的、足够大的内存区域,将旧数据拷贝过去,然后释放旧的内存地址,并返回新地址。 -![](https://oss.javaguide.cn/github/javaguide/java/basis/unsafe/image-20220717144344005.png) +**结合本次的运行结果,我们可以进行如下分析:** -在代码中调用`reallocateMemory`方法重新分配了一块 8 字节长度的内存空间,通过比较`addr`和`addr3`可以看到和之前申请的内存地址是不同的。在代码中的第二个 for 循环里,调用`copyMemory`方法进行了两次内存的拷贝,每次拷贝内存地址`addr`开始的 4 个字节,分别拷贝到以`addr3`和`addr3+4`开始的内存空间上: +**第一步:初始分配与写入** -![](https://oss.javaguide.cn/github/javaguide/java/basis/unsafe/image-20220717144354582.png) +- `unsafe.allocateMemory(size)` 分配了 4 字节的堆外内存,地址为 `140467048086752`。 +- `unsafe.putInt(oldAddr, 16843009)` 向该地址写入了 int 值 `16843009`,其十六进制表示为 `0x01010101`。`getInt` 读取正确,证明写入成功。 -拷贝完成后,使用`getLong`方法一次性读取 8 个字节,得到`long`类型的值为 72340172838076673。 +**第二步:原地内存扩容** -需要注意,通过这种方式分配的内存属于 堆外内存 ,是无法进行垃圾回收的,需要我们把这些内存当做一种资源去手动调用`freeMemory`方法进行释放,否则会产生内存泄漏。通用的操作内存方式是在`try`中执行对内存的操作,最终在`finally`块中进行内存的释放。 +- `long newAddr = unsafe.reallocateMemory(oldAddr, size * 2)` 尝试将内存块扩容至 8 字节。 +- 观察输出 New address: `140467048086752`,我们发现 `newAddr` 与 `oldAddr` 的值**完全相同**。 +- 这表明本次操作触发了“原地扩容”。系统在原地址 `140467048086752` 后面找到了足够的空间,直接将内存块扩展到了 8 字节。在这个过程中,旧的地址 `oldAddr` 依然有效,并且就是 `newAddr`,数据也并未发生移动。 -**为什么要使用堆外内存?** +**第三步:验证数据与写入新数据** -- 对垃圾回收停顿的改善。由于堆外内存是直接受操作系统管理而不是 JVM,所以当我们使用堆外内存时,即可保持较小的堆内内存规模。从而在 GC 时减少回收停顿对于应用的影响。 -- 提升程序 I/O 操作的性能。通常在 I/O 通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存。 +- `unsafe.getInt(newAddr)` 再次读取前 4 个字节,结果仍是 `16843009`,验证了原数据完好无损。 +- `unsafe.putInt(newAddr + size, 33686018)` 在扩容出的后 4 个字节(偏移量为 4)写入了新的 int 值 `33686018`(十六进制为 `0x02020202`)。 + +**第四步:读取完整数据** + +- `unsafe.getLong(newAddr)` 从起始地址读取一个 long 值(8 字节)。此时内存中的 8 字节内容为 `0x01010101` (低地址) 和 `0x02020202` (高地址) 的拼接。 +- 在小端字节序(Little-Endian)的机器上,这 8 字节在内存中会被解释为十六进制数 `0x0202020201010101`。 +- 这个十六进制数转换为十进制,结果正是 `144680345659310337`。这完美地解释了最终的输出结果。 + +**第五步:安全的内存释放** + +- `finally` 块中,`unsafe.freeMemory(newAddr)` 安全地释放了整个 8 字节的内存块。 +- 由于本次是原地扩容(`oldAddr == newAddr`),所以即使错误地多写一句 `freeMemory(oldAddr)` 也会导致二次释放的严重错误。 #### 典型应用 `DirectByteBuffer` 是 Java 用于实现堆外内存的一个重要类,通常用在通信过程中做缓冲池,如在 Netty、MINA 等 NIO 框架中应用广泛。`DirectByteBuffer` 对于堆外内存的创建、使用、销毁等逻辑均由 Unsafe 提供的堆外内存 API 来实现。 +**为什么要使用堆外内存?** + +- 对垃圾回收停顿的改善。由于堆外内存是直接受操作系统管理而不是 JVM,所以当我们使用堆外内存时,即可保持较小的堆内内存规模。从而在 GC 时减少回收停顿对于应用的影响。 +- 提升程序 I/O 操作的性能。通常在 I/O 通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存。 + 下图为 `DirectByteBuffer` 构造函数,创建 `DirectByteBuffer` 的时候,通过 `Unsafe.allocateMemory` 分配内存、`Unsafe.setMemory` 进行内存初始化,而后构建 `Cleaner` 对象用于跟踪 `DirectByteBuffer` 对象的垃圾回收,以实现当 `DirectByteBuffer` 被垃圾回收时,分配的堆外内存一起被释放。 ```java diff --git a/docs/java/basis/why-there-only-value-passing-in-java.md b/docs/java/basis/why-there-only-value-passing-in-java.md index dc329cd32b6..e3d5a20c5fb 100644 --- a/docs/java/basis/why-there-only-value-passing-in-java.md +++ b/docs/java/basis/why-there-only-value-passing-in-java.md @@ -3,6 +3,13 @@ title: Java 值传递详解 category: Java tag: - Java基础 +head: + - - meta + - name: keywords + content: 值传递,引用传递,参数传递,对象引用,示例解析,方法调用 + - - meta + - name: description + content: 通过示例解释 Java 参数传递模型,澄清值传递与引用传递的常见误区。 --- 开始之前,我们先来搞懂下面这两个概念: @@ -32,7 +39,7 @@ void sayHello(String str) { 程序设计语言将实参传递给方法(或函数)的方式分为两种: - **值传递**:方法接收的是实参值的拷贝,会创建副本。 -- **引用传递**:方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。 +- **引用传递**:方法接收的直接是实参的地址,而不是实参内的值,这就是指针,此时形参就是实参,对形参的任何修改都会反应到实参,包括重新赋值。 很多程序设计语言(比如 C++、 Pascal)提供了两种参数传递的方式,不过,在 Java 中只有值传递。 diff --git a/docs/java/collection/arrayblockingqueue-source-code.md b/docs/java/collection/arrayblockingqueue-source-code.md index 4c923ef0d29..4a8d473f8d1 100644 --- a/docs/java/collection/arrayblockingqueue-source-code.md +++ b/docs/java/collection/arrayblockingqueue-source-code.md @@ -3,6 +3,13 @@ title: ArrayBlockingQueue 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: ArrayBlockingQueue,阻塞队列,生产者消费者,有界队列,JUC,put,take,线程池,ReentrantLock,Condition + - - meta + - name: description + content: 讲解 ArrayBlockingQueue 的有界阻塞队列实现与典型生产者-消费者使用,结合线程池工作队列分析锁与条件的并发设计。 --- ## 阻塞队列简介 @@ -226,11 +233,11 @@ public class DrainToExample { ![ArrayBlockingQueue 类图](https://oss.javaguide.cn/github/javaguide/java/collection/arrayblockingqueue-class-diagram.png) -从图中我们可以看出,`ArrayBlockingQueue` 继承了阻塞队列 `BlockingQueue` 这个接口,不难猜出通过继承 `BlockingQueue` 这个接口之后,`ArrayBlockingQueue` 就拥有了阻塞队列那些常见的操作行为。 +从图中我们可以看出,`ArrayBlockingQueue` 实现了阻塞队列 `BlockingQueue` 这个接口,不难猜出通过实现 `BlockingQueue` 这个接口之后,`ArrayBlockingQueue` 就拥有了阻塞队列那些常见的操作行为。 同时, `ArrayBlockingQueue` 还继承了 `AbstractQueue` 这个抽象类,这个继承了 `AbstractCollection` 和 `Queue` 的抽象类,从抽象类的特定和语义我们也可以猜出,这个继承关系使得 `ArrayBlockingQueue` 拥有了队列的常见操作。 -所以我们是否可以得出这样一个结论,通过继承 `AbstractQueue` 获得队列所有的操作模板,其实现的入队和出队操作的整体框架。然后 `ArrayBlockingQueue` 通过继承 `BlockingQueue` 获取到阻塞队列的常见操作并将这些操作实现,填充到 `AbstractQueue` 模板方法的细节中,由此 `ArrayBlockingQueue` 成为一个完整的阻塞队列。 +所以我们是否可以得出这样一个结论,通过继承 `AbstractQueue` 获得队列所有的操作模板,其实现的入队和出队操作的整体框架。然后 `ArrayBlockingQueue` 通过实现 `BlockingQueue` 获取到阻塞队列的常见操作并将这些操作实现,填充到 `AbstractQueue` 模板方法的细节中,由此 `ArrayBlockingQueue` 成为一个完整的阻塞队列。 为了印证这一点,我们到源码中一探究竟。首先我们先来看看 `AbstractQueue`,从类的继承关系我们可以大致得出,它通过 `AbstractCollection` 获得了集合的常见操作方法,然后通过 `Queue` 接口获得了队列的特性。 @@ -244,7 +251,7 @@ public abstract class AbstractQueue 对于集合的操作无非是增删改查,所以我们不妨从添加方法入手,从源码中我们可以看到,它实现了 `AbstractCollection` 的 `add` 方法,其内部逻辑如下: -1. 调用继承 `Queue` 接口的来的 `offer` 方法,如果 `offer` 成功则返回 `true`。 +1. 调用继承 `Queue` 接口得来的 `offer` 方法,如果 `offer` 成功则返回 `true`。 2. 如果 `offer` 失败,即代表当前元素入队失败直接抛异常。 ```java @@ -258,7 +265,7 @@ public boolean add(E e) { 而 `AbstractQueue` 中并没有对 `Queue` 的 `offer` 的实现,很明显这样做的目的是定义好了 `add` 的核心逻辑,将 `offer` 的细节交由其子类即我们的 `ArrayBlockingQueue` 实现。 -到此,我们对于抽象类 `AbstractQueue` 的分析就结束了,我们继续看看 `ArrayBlockingQueue` 中另一个重要的继承接口 `BlockingQueue`。 +到此,我们对于抽象类 `AbstractQueue` 的分析就结束了,我们继续看看 `ArrayBlockingQueue` 中实现的另一个重要接口 `BlockingQueue`。 点开 `BlockingQueue` 之后,我们可以看到这个接口同样继承了 `Queue` 接口,这就意味着它也具备了队列所拥有的所有行为。同时,它还定义了自己所需要实现的方法。 @@ -302,11 +309,11 @@ public interface BlockingQueue extends Queue { } ``` -了解了 `BlockingQueue` 的常见操作后,我们就知道了 `ArrayBlockingQueue` 通过继承 `BlockingQueue` 的方法并实现后,填充到 `AbstractQueue` 的方法上,由此我们便知道了上文中 `AbstractQueue` 的 `add` 方法的 `offer` 方法是哪里是实现的了。 +了解了 `BlockingQueue` 的常见操作后,我们就知道了 `ArrayBlockingQueue` 通过实现 `BlockingQueue` 的方法并重写后,填充到 `AbstractQueue` 的方法上,由此我们便知道了上文中 `AbstractQueue` 的 `add` 方法的 `offer` 方法是哪里是实现的了。 ```java public boolean add(E e) { - //AbstractQueue的offer来自下层的ArrayBlockingQueue从BlockingQueue继承并实现的offer方法 + //AbstractQueue的offer来自下层的ArrayBlockingQueue从BlockingQueue实现并重写的offer方法 if (offer(e)) return true; else diff --git a/docs/java/collection/arraylist-source-code.md b/docs/java/collection/arraylist-source-code.md index 5c71801b699..ee9b8b496de 100644 --- a/docs/java/collection/arraylist-source-code.md +++ b/docs/java/collection/arraylist-source-code.md @@ -3,6 +3,13 @@ title: ArrayList 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: ArrayList,动态数组,ensureCapacity,RandomAccess,扩容机制,序列化,add/remove,索引访问,性能,Vector 区别,列表实现 + - - meta + - name: description + content: 系统梳理 ArrayList 的底层原理与常见用法,包含动态数组结构、扩容策略、接口实现以及与 Vector 的差异与性能特点。 --- diff --git a/docs/java/collection/concurrent-hash-map-source-code.md b/docs/java/collection/concurrent-hash-map-source-code.md index d0d210aacdf..af9f978a5f4 100644 --- a/docs/java/collection/concurrent-hash-map-source-code.md +++ b/docs/java/collection/concurrent-hash-map-source-code.md @@ -3,9 +3,16 @@ title: ConcurrentHashMap 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: ConcurrentHashMap,线程安全,分段锁,Segment,CAS,红黑树,链表,并发级别,JDK7,JDK8,并发容器 + - - meta + - name: description + content: 对比 JDK7/8 的 ConcurrentHashMap 实现,解析分段锁、CAS、链表/红黑树等并发设计,理解线程安全 Map 的核心原理。 --- -> 本文来自公众号:末读代码的投稿,原文地址: 。 +> 本文来自末读代码投稿: ,JavaGuide 对原文进行了大篇幅改进优化。 上一篇文章介绍了 HashMap 源码,反响不错,也有很多同学发表了自己的观点,这次又来了,这次是 `ConcurrentHashMap` 了,作为线程安全的 HashMap ,它的使用频率也是很高。那么它的存储结构和实现原理是怎么样的呢? @@ -413,6 +420,8 @@ public V get(Object key) { ## 2. ConcurrentHashMap 1.8 +总的来说 ,`ConcurrentHashMap` 在 Java8 中相对于 Java7 来说变化还是挺大的, + ### 1. 存储结构 ![Java8 ConcurrentHashMap 存储结构(图片来自 javadoop)](https://oss.javaguide.cn/github/javaguide/java/collection/java8_concurrenthashmap.png) @@ -590,9 +599,64 @@ public V get(Object key) { 3. 如果头节点 hash 值小于 0 ,说明正在扩容或者是红黑树,查找之。 4. 如果是链表,遍历查找之。 -总结: +### 5. size 计数 + +`ConcurrentHashMap` 的 `size()` 方法用来获取当前 Map 中元素的总数,但在高并发场景下,如何准确且高效地统计元素数量是一个技术难点。Java8 采用了一套精巧的分段计数机制来解决这个问题。 + +#### 5.1 为什么需要分段计数 + +在并发环境下,如果多个线程同时执行 `put` 操作,它们都需要更新元素总数。如果使用一个共享的计数器变量,就会导致激烈的竞争——所有线程都在争抢同一个变量的修改权,这会严重影响性能。 + +为了解决这个问题,`ConcurrentHashMap` 采用了**分散热点**的设计思想:不使用单一计数器,而是将计数分散到多个变量中。就像银行不会只开一个窗口办业务,而是开多个窗口分流客户一样,这样可以大大减少冲突。 + +#### 5.2 baseCount 和 counterCells 的设计 + +`ConcurrentHashMap` 内部维护了两个关键的计数相关字段: + +- **baseCount**:基础计数器,在没有竞争的情况下,直接通过 CAS 更新这个变量。可以把它理解为"主计数器"。 +- **counterCells**:计数器数组。当多个线程竞争 `baseCount` 失败时,会尝试将计数增量分散到 `counterCells` 数组的不同位置。 + - 每个线程根据自己的 **Probe 值**(可理解为线程 ID 生成的一种哈希码)映射到数组的某个槽位,优先在这个“偏向的格子”里进行累加。 + - **注意**:这个格子并不是严格意义上的“线程私有”,当哈希冲突时,多个线程仍然可能映射到同一个槽位并发更新。 + +**举个例子**:假设有 10 个线程同时往 Map 中添加元素。第一个线程成功通过 CAS 更新了 `baseCount`,但后面 9 个线程在更新 `baseCount` 时发现有竞争,就会转而去 `counterCells` 数组中找一个位置进行累加。这 9 个线程可能分散到数组的不同位置(比如线程 2 在 `counterCells[1]`,线程 3 在 `counterCells[2]`),从而将竞争从一个点分散到了多个点。。 + +#### 5.3 put 元素时如何更新计数 + +在 `putVal` 方法的最后,我们可以看到调用了 `addCount(1L, binCount)` 方法,这个方法就是用来更新元素计数的。 + +`addCount` 的执行逻辑大致可以概括为: + +1. **优先尝试更新 baseCount** + + - 如果当前还没有启用 `counterCells`(`counterCells == null`),线程会先尝试通过 CAS 直接更新 `baseCount`。 + - 如果 CAS 成功,说明竞争不激烈,直接返回即可。 + +2. **竞争出现时,转向 counterCells** + + - 如果 CAS 更新 `baseCount` 失败(说明有其他线程在竞争),或者 `counterCells` 已经存在(说明系统之前已经遇到过竞争),线程就会尝试在 `counterCells` 中更新: + - 根据自己的 probe 值映射到某个槽位; + - 对该槽位对应的 `CounterCell` 做一次 CAS 累加。 + - 如果这个槽位为空或 CAS 仍然冲突,就会进入一个更“重”的路径 `fullAddCount`,在里面负责初始化槽位、重新选择槽位等。 + +3. **动态初始化与扩容 counterCells** + - 当检测到竞争比较激烈(例如:某个 cell 的 CAS 也频繁失败)时,`fullAddCount` 会在一个轻量级的自旋锁 `cellsBusy` 保护下: + - 如果 `counterCells` 还没初始化,就初始化一个较小的数组(比如长度 2); + - 如果已经存在并且长度还没达到上限(通常不超过 CPU 核数),就按 2 倍进行扩容,增加更多的计数槽位,把线程进一步打散。 + +这种设计保证了:在低并发时只使用简单的 `baseCount`,路径非常短;在高并发时则自动切换到分段计数,通过 `counterCells` 和扩容机制摊薄竞争,兼顾了性能和准确性。 + +#### 5.4 sumCount 如何计算元素总数 + +当我们调用 `size()` 方法时,最终会调用 `sumCount()` 方法来计算元素总数。`sumCount()` 的逻辑非常简单直接: + +1. 读取 `baseCount` 的值作为基础值。 +2. 遍历 `counterCells` 数组,将所有非空位置的计数值累加到基础值上。 +3. 返回累加结果。 + +**注意**: -总的来说 `ConcurrentHashMap` 在 Java8 中相对于 Java7 来说变化还是挺大的, +- **弱一致性**:`sumCount()` 全程**不加锁**。在计算期间如果有其他线程插入数据,返回的结果只是一个**近似值**。但在高并发场景下,追求“刹那间的精确总数”代价过大且无意义,近似值通常已足够。 +- **整型溢出**:`size()` 方法返回 `int` 类型。如果元素数量超过 `Integer.MAX_VALUE`,它只会返回 `Integer.MAX_VALUE`。如果需要获取精确的大容量计数,建议使用 Java 8 新增的 **`mappingCount()`** 方法,该方法返回 `long` 类型。 ## 3. 总结 diff --git a/docs/java/collection/copyonwritearraylist-source-code.md b/docs/java/collection/copyonwritearraylist-source-code.md index 9aceb83bc4e..6aec69f4244 100644 --- a/docs/java/collection/copyonwritearraylist-source-code.md +++ b/docs/java/collection/copyonwritearraylist-source-code.md @@ -3,6 +3,13 @@ title: CopyOnWriteArrayList 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: CopyOnWriteArrayList,写时复制,COW,读多写少,线程安全 List,快照,并发性能,内存占用 + - - meta + - name: description + content: 解析 CopyOnWriteArrayList 的写时复制策略,适用读多写少场景的并发优化与权衡,理解其线程安全 List 的实现方式。 --- ## CopyOnWriteArrayList 简介 diff --git a/docs/java/collection/delayqueue-source-code.md b/docs/java/collection/delayqueue-source-code.md index 5fb6f4affad..a1e3af58cdb 100644 --- a/docs/java/collection/delayqueue-source-code.md +++ b/docs/java/collection/delayqueue-source-code.md @@ -3,6 +3,13 @@ title: DelayQueue 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: DelayQueue,延迟队列,Delayed,getDelay,任务调度,PriorityQueue,无界队列,ReentrantLock,Condition + - - meta + - name: description + content: 介绍 DelayQueue 的延时任务队列原理与常见场景,用例包含延时执行与过期删除,解析基于 PriorityQueue 的线程安全实现。 --- ## DelayQueue 简介 diff --git a/docs/java/collection/hashmap-source-code.md b/docs/java/collection/hashmap-source-code.md index 0e9342f0edf..b2e5c231752 100644 --- a/docs/java/collection/hashmap-source-code.md +++ b/docs/java/collection/hashmap-source-code.md @@ -3,6 +3,13 @@ title: HashMap 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: HashMap,哈希表,散列冲突,拉链法,红黑树,JDK1.8,扰动函数,负载因子,扩容,rehash,树化阈值,TREEIFY_THRESHOLD,MIN_TREEIFY_CAPACITY,非线程安全,hashCode,数组+链表 + - - meta + - name: description + content: 深入解析 HashMap 底层实现,涵盖 JDK1.7/1.8 结构差异、hash 计算与扰动函数、负载因子与扩容、链表转红黑树的树化机制等关键细节。 --- @@ -217,7 +224,9 @@ HashMap 中有四个构造方法,它们分别如下: } ``` -> 值得注意的是上述四个构造方法中,都初始化了负载因子 loadFactor,由于 HashMap 中没有 capacity 这样的字段,即使指定了初始化容量 initialCapacity ,也只是通过 tableSizeFor 将其扩容到与 initialCapacity 最接近的 2 的幂次方大小,然后暂时赋值给 threshold ,后续通过 resize 方法将 threshold 赋值给 newCap 进行 table 的初始化。 +> 需要特别注意的是:传入的 `initialCapacity` 并不是最终的数组容量。`HashMap` 会调用 `tableSizeFor()` 将其**向上取整为大于或等于该值的最小 2 的幂次方**,并暂时保存到 `threshold` 字段。真正的 `table` 数组会在第一次扩容(`resize()`)时才初始化为这个大小。 +> +> 例如:`initialCapacity = 9` → `threshold = 16` → `table` 长度最终为 16。 **putMapEntries 方法:** diff --git a/docs/java/collection/java-collection-precautions-for-use.md b/docs/java/collection/java-collection-precautions-for-use.md index 9bd3a4084d5..6d3d0338f64 100644 --- a/docs/java/collection/java-collection-precautions-for-use.md +++ b/docs/java/collection/java-collection-precautions-for-use.md @@ -3,6 +3,13 @@ title: Java集合使用注意事项总结 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: Java集合,使用注意,判空,isEmpty,size,并发容器,最佳实践,ConcurrentLinkedQueue + - - meta + - name: description + content: 总结 Java 集合常见使用注意事项与最佳实践,覆盖判空、并发容器特性等,帮助避免易错点与性能问题。 --- 这篇文章我根据《阿里巴巴 Java 开发手册》总结了关于集合使用常见的注意事项以及其具体原理。 @@ -134,7 +141,8 @@ public static T requireNonNull(T obj) { return obj; } ``` -> `Collectors`也提供了无需mergeFunction的`toMap()`方法,但此时若出现key冲突,则会抛出`duplicateKeyException`异常,因此强烈建议使用`toMap()`方法必填mergeFunction。 + +> `Collectors`也提供了无需 mergeFunction 的`toMap()`方法,但此时若出现 key 冲突,则会抛出`duplicateKeyException`异常,因此强烈建议使用`toMap()`方法必填 mergeFunction。 ## 集合遍历 diff --git a/docs/java/collection/java-collection-questions-01.md b/docs/java/collection/java-collection-questions-01.md index d0a54da58b9..9b1a7b77de1 100644 --- a/docs/java/collection/java-collection-questions-01.md +++ b/docs/java/collection/java-collection-questions-01.md @@ -28,7 +28,7 @@ Java 集合框架如下图所示: 注:图中只列举了主要的继承派生关系,并没有列举所有关系。比方省略了`AbstractList`, `NavigableSet`等抽象类以及其他的一些辅助类,如想深入了解,可自行查看源码。 -### 说说 List, Set, Queue, Map 四者的区别? +### ⭐️说说 List, Set, Queue, Map 四者的区别? - `List`(对付顺序的好帮手): 存储的元素是有序的、可重复的。 - `Set`(注重独一无二的性质): 存储的元素不可重复的。 @@ -79,7 +79,7 @@ Java 集合框架如下图所示: ## List -### ArrayList 和 Array(数组)的区别? +### ⭐️ArrayList 和 Array(数组)的区别? `ArrayList` 内部基于动态数组实现,比 `Array`(静态数组) 使用起来更加灵活: @@ -154,7 +154,7 @@ System.out.println(listOfStrings); [null, java] ``` -### ArrayList 插入和删除元素的时间复杂度? +### ⭐️ArrayList 插入和删除元素的时间复杂度? 对于插入: @@ -188,13 +188,13 @@ System.out.println(listOfStrings); 0 1 2 3 4 5 6 7 8 9 ``` -### LinkedList 插入和删除元素的时间复杂度? +### ⭐️LinkedList 插入和删除元素的时间复杂度? - 头部插入/删除:只需要修改头结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)。 - 尾部插入/删除:只需要修改尾结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)。 - 指定位置插入/删除:需要先移动到指定位置,再修改指定节点的指针完成插入/删除,不过由于有头尾指针,可以从较近的指针出发,因此需要遍历平均 n/4 个元素,时间复杂度为 O(n)。 -这里简单列举一个例子:假如我们要删除节点 9 的话,需要先遍历链表找到该节点。然后,再执行相应节点指针指向的更改,具体的源码可以参考:[LinkedList 源码分析](./linkedlist-source-code.md) 。 +这里简单列举一个例子:假如我们要删除节点 9 的话,需要先遍历链表找到该节点。然后,再执行相应节点指针指向的更改,具体的源码可以参考:[LinkedList 源码分析](https://javaguide.cn/java/collection/linkedlist-source-code.html) 。 ![unlink 方法逻辑](https://oss.javaguide.cn/github/javaguide/java/collection/linkedlist-unlink.jpg) @@ -202,7 +202,7 @@ System.out.println(listOfStrings); `RandomAccess` 是一个标记接口,用来表明实现该接口的类支持随机访问(即可以通过索引快速访问元素)。由于 `LinkedList` 底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 `RandomAccess` 接口。 -### ArrayList 与 LinkedList 区别? +### ⭐️ArrayList 与 LinkedList 区别? - **是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全; - **底层数据结构:** `ArrayList` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) @@ -251,11 +251,13 @@ public interface RandomAccess { `ArrayList` 实现了 `RandomAccess` 接口, 而 `LinkedList` 没有实现。为什么呢?我觉得还是和底层数据结构有关!`ArrayList` 底层是数组,而 `LinkedList` 底层是链表。数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。`ArrayList` 实现了 `RandomAccess` 接口,就表明了他具有快速随机访问功能。 `RandomAccess` 接口只是标识,并不是说 `ArrayList` 实现 `RandomAccess` 接口才具有快速随机访问功能的! -### 说一说 ArrayList 的扩容机制吧 +### ⭐️说一说 ArrayList 的扩容机制吧 -详见笔主的这篇文章: [ArrayList 扩容机制分析](https://javaguide.cn/java/collection/arraylist-source-code.html#_3-1-%E5%85%88%E4%BB%8E-arraylist-%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E8%AF%B4%E8%B5%B7)。 +详见笔主的这篇文章: [ArrayList 扩容机制分析](https://javaguide.cn/java/collection/arraylist-source-code.html#arraylist-扩容机制分析)。 -### 说说集合中的 fail-fast 和 fail-safe 是什么 +### ⭐️集合中的 fail-fast 和 fail-safe 是什么? + +`fail-fast`(快速失败)和 `fail-safe`(安全失败)是Java集合框架在处理并发修改问题时,两种截然不同的设计哲学和容错策略。 关于`fail-fast`引用`medium`中一篇文章关于`fail-fast`和`fail-safe`的说法: @@ -263,43 +265,67 @@ public interface RandomAccess { 快速失败的思想即针对可能发生的异常进行提前表明故障并停止运行,通过尽早的发现和停止错误,降低故障系统级联的风险。 -在`java.util`包下的大部分集合是不支持线程安全的,为了能够提前发现并发操作导致线程安全风险,提出通过维护一个`modCount`记录修改的次数,迭代期间通过比对预期修改次数`expectedModCount`和`modCount`是否一致来判断是否存在并发操作,从而实现快速失败,由此保证在避免在异常时执行非必要的复杂代码。 +在`java.util`包下的大部分集合(如 `ArrayList`, `HashMap`)是不支持线程安全的,为了能够提前发现并发操作导致线程安全风险,提出通过维护一个`modCount`记录修改的次数,迭代期间通过比对预期修改次数`expectedModCount`和`modCount`是否一致来判断是否存在并发操作,从而实现快速失败,由此保证在避免在异常时执行非必要的复杂代码。 -对应的我们给出下面这样一段在示例,我们首先插入`100`个操作元素,一个线程迭代元素,一个线程删除元素,最终输出结果如愿抛出`ConcurrentModificationException`: +**ArrayList (fail-fast) 示例:** ```java -// 使用线程安全的 CopyOnWriteArrayList 避免 ConcurrentModificationException -List list = new CopyOnWriteArrayList<>(); -CountDownLatch countDownLatch = new CountDownLatch(2); - -// 添加元素 -for (int i = 0; i < 100; i++) { - list.add(i); -} - -Thread t1 = new Thread(() -> { - // 迭代元素 (注意:Integer 是不可变的,这里的 i++ 不会修改 list 中的值) - for (Integer i : list) { - i++; // 这行代码实际上没有修改list中的元素 - } - countDownLatch.countDown(); -}); + // 使用线程不安全的 ArrayList,它是一种 fail-fast 集合 + List list = new ArrayList<>(); + CountDownLatch latch = new CountDownLatch(2); + + for (int i = 0; i < 5; i++) { + list.add(i); + } + System.out.println("Initial list: " + list); + + Thread t1 = new Thread(() -> { + try { + for (Integer i : list) { + System.out.println("Iterator Thread (t1) sees: " + i); + Thread.sleep(100); + } + } catch (ConcurrentModificationException e) { + System.err.println("!!! Iterator Thread (t1) caught ConcurrentModificationException as expected."); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + latch.countDown(); + } + }); + + Thread t2 = new Thread(() -> { + try { + Thread.sleep(50); + System.out.println("-> Modifier Thread (t2) is removing element 1..."); + list.remove(Integer.valueOf(1)); + System.out.println("-> Modifier Thread (t2) finished removal."); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + latch.countDown(); + } + }); + + t1.start(); + t2.start(); + latch.await(); + + System.out.println("Final list state: " + list); +``` -Thread t2 = new Thread(() -> { - System.out.println("删除元素1"); - list.remove(Integer.valueOf(1)); // 使用 Integer.valueOf(1) 删除指定值的对象 - countDownLatch.countDown(); -}); +输出: -t1.start(); -t2.start(); -countDownLatch.await(); +``` +Initial list: [0, 1, 2, 3, 4] +Iterator Thread (t1) sees: 0 +-> Modifier Thread (t2) is removing element 1... +-> Modifier Thread (t2) finished removal. +!!! Iterator Thread (t1) caught ConcurrentModificationException as expected. +Final list state: [0, 2, 3, 4] ``` -我们在初始化时插入了`100`个元素,此时对应的修改`modCount`次数为`100`,随后线程 2 在线程 1 迭代期间进行元素删除操作,此时对应的`modCount`就变为`101`。 -线程 1 在随后`foreach`第 2 轮循环发现`modCount` 为`101`,与预期的`expectedModCount(值为100因为初始化插入了元素100个)`不等,判定为并发操作异常,于是便快速失败,抛出`ConcurrentModificationException`: - -![](https://oss.javaguide.cn/github/javaguide/java/collection/fail-fast-and-fail-safe-insert-100-values.png) +程序在线程 t2 修改列表后,线程 t1 的下一次迭代操作立刻就抛出了 `ConcurrentModificationException`。这是因为 ArrayList 的迭代器在每次 `next()` 调用时,都会检查 `modCount` 是否被改变。一旦发现集合在迭代器不知情的情况下被修改,它会立即“快速失败”,以防止在不一致的数据上继续操作导致不可预期的后果。 对此我们也给出`for`循环底层迭代器获取下一个元素时的`next`方法,可以看到其内部的`checkForComodification`具有针对修改次数比对的逻辑: @@ -324,7 +350,7 @@ final void checkForComodification() { > Fail-safe systems take a different approach, aiming to recover and continue even in the face of unexpected conditions. This makes them particularly suited for uncertain or volatile environments. -该思想常运用于并发容器,最经典的实现就是`CopyOnWriteArrayList`的实现,通过写时复制的思想保证在进行修改操作时复制出一份快照,基于这份快照完成添加或者删除操作后,将`CopyOnWriteArrayList`底层的数组引用指向这个新的数组空间,由此避免迭代时被并发修改所干扰所导致并发操作安全问题,当然这种做法也存在缺点,即进行遍历操作时无法获得实时结果: +该思想常运用于并发容器,最经典的实现就是`CopyOnWriteArrayList`的实现,通过写时复制(Copy-On-Write)的思想保证在进行修改操作时复制出一份快照,基于这份快照完成添加或者删除操作后,将`CopyOnWriteArrayList`底层的数组引用指向这个新的数组空间,由此避免迭代时被并发修改所干扰所导致并发操作安全问题,当然这种做法也存在缺点,即进行遍历操作时无法获得实时结果: ![](https://oss.javaguide.cn/github/javaguide/java/collection/fail-fast-and-fail-safe-copyonwritearraylist.png) @@ -579,7 +605,7 @@ Java 中常用的阻塞队列实现类有以下几种: 日常开发中,这些队列使用的其实都不多,了解即可。 -### ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别? +### ⭐️ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别? `ArrayBlockingQueue` 和 `LinkedBlockingQueue` 是 Java 并发包中常用的两种阻塞队列实现,它们都是线程安全的。不过,不过它们之间也存在下面这些区别: diff --git a/docs/java/collection/java-collection-questions-02.md b/docs/java/collection/java-collection-questions-02.md index 94eafcf9825..7ecc35bc613 100644 --- a/docs/java/collection/java-collection-questions-02.md +++ b/docs/java/collection/java-collection-questions-02.md @@ -16,7 +16,7 @@ head: ## Map(重要) -### HashMap 和 Hashtable 的区别 +### ⭐️HashMap 和 Hashtable 的区别 - **线程是否安全:** `HashMap` 是非线程安全的,`Hashtable` 是线程安全的,因为 `Hashtable` 内部的方法基本都经过`synchronized` 修饰。(如果你要保证线程安全的话就使用 `ConcurrentHashMap` 吧!); - **效率:** 因为线程安全的问题,`HashMap` 要比 `Hashtable` 效率高一点。另外,`Hashtable` 基本被淘汰,不要在代码中使用它; @@ -73,7 +73,7 @@ static final int tableSizeFor(int cap) { | 调用 `put()`向 map 中添加元素 | 调用 `add()`方法向 `Set` 中添加元素 | | `HashMap` 使用键(Key)计算 `hashcode` | `HashSet` 使用成员对象来计算 `hashcode` 值,对于两个对象来说 `hashcode` 可能相同,所以`equals()`方法用来判断对象的相等性 | -### HashMap 和 TreeMap 区别 +### ⭐️HashMap 和 TreeMap 区别 `TreeMap` 和`HashMap` 都继承自`AbstractMap` ,但是需要注意的是`TreeMap`它还实现了`NavigableMap`接口和`SortedMap` 接口。 @@ -179,7 +179,7 @@ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 也就是说,在 JDK1.8 中,实际上无论`HashSet`中是否已经存在了某元素,`HashSet`都会直接插入,只是会在`add()`方法的返回值处告诉我们插入前是否存在相同元素。 -### HashMap 的底层实现 +### ⭐️HashMap 的底层实现 #### JDK1.8 之前 @@ -297,7 +297,7 @@ final void treeifyBin(Node[] tab, int hash) { 将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树。 -### HashMap 的长度为什么是 2 的幂次方 +### ⭐️HashMap 的长度为什么是 2 的幂次方 为了让 `HashMap` 存取高效并减少碰撞,我们需要确保数据尽量均匀分布。哈希值在 Java 中通常使用 `int` 表示,其范围是 `-2147483648 ~ 2147483647`前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但是,问题是一个 40 亿长度的数组,内存是放不下的。所以,这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。 @@ -349,7 +349,7 @@ index = 00001100 (12) 2. 可以更好地保证哈希值的均匀分布:扩容之后,在旧数组元素 hash 值比较均匀的情况下,新数组元素也会被分配的比较均匀,最好的情况是会有一半在新数组的前半部分,一半在新数组后半部分。 3. 扩容机制变得简单和高效:扩容后只需检查哈希值高位的变化来决定元素的新位置,要么位置不变(高位为 0),要么就是移动到新位置(高位为 1,原索引位置+原容量)。 -### HashMap 多线程操作导致死循环问题 +### ⭐️HashMap 多线程操作导致死循环问题 JDK1.7 及之前版本的 `HashMap` 在多线程环境下扩容操作可能存在死循环问题,这是由于当一个桶位中有多个元素需要进行扩容时,多个线程同时对链表进行操作,头插法可能会导致链表中的节点指向错误的位置,从而形成一个环形链表,进而使得查询元素的操作陷入死循环无法结束。 @@ -357,9 +357,12 @@ JDK1.7 及之前版本的 `HashMap` 在多线程环境下扩容操作可能存 一般面试中这样介绍就差不多,不需要记各种细节,个人觉得也没必要记。如果想要详细了解 `HashMap` 扩容导致死循环问题,可以看看耗子叔的这篇文章:[Java HashMap 的死循环](https://coolshell.cn/articles/9606.html)。 -### HashMap 为什么线程不安全? +### ⭐️HashMap 为什么线程不安全? -JDK1.7 及之前版本,在多线程环境下,`HashMap` 扩容时会造成死循环和数据丢失的问题。 +`HashMap` 不是线程安全的。在多线程环境下对 `HashMap` 进行并发写操作,可能会导致两种主要问题: + +1. **数据丢失**:并发 `put` 操作可能导致一个线程的写入被另一个线程覆盖。 +2. **无限循环**:在 JDK 7 及以前的版本中,并发扩容时,由于头插法可能导致链表形成环,从而在 `get` 操作时引发无限循环,CPU 飙升至 100%。 数据丢失这个在 JDK1.7 和 JDK 1.8 中都存在,这里以 JDK 1.8 为例进行介绍。 @@ -441,11 +444,11 @@ Test.lambda avgt 5 1551065180.000 ± 19164407.426 ns/op Test.parallelStream avgt 5 186345456.667 ± 3210435.590 ns/op ``` -### ConcurrentHashMap 和 Hashtable 的区别 +### ⭐️ConcurrentHashMap 和 Hashtable 的区别 `ConcurrentHashMap` 和 `Hashtable` 的区别主要体现在实现线程安全的方式上不同。 -- **底层数据结构:** JDK1.7 的 `ConcurrentHashMap` 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟 `HashMap1.8` 的结构一样,数组+链表/红黑二叉树。`Hashtable` 和 JDK1.8 之前的 `HashMap` 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; +- **底层数据结构:** JDK1.7 的 `ConcurrentHashMap` 底层采用 **分段的数组+链表** 实现,在 JDK1.8 中采用的数据结构跟 `HashMap` 的结构一样,数组+链表/红黑二叉树。`Hashtable` 和 JDK1.8 之前的 `HashMap` 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; - **实现线程安全的方式(重要):** - 在 JDK1.7 的时候,`ConcurrentHashMap` 对整个桶数组进行了分割分段(`Segment`,分段锁),每一把锁只锁容器其中一部分数据(下面有示意图),多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 - 到了 JDK1.8 的时候,`ConcurrentHashMap` 已经摒弃了 `Segment` 的概念,而是直接用 `Node` 数组+链表+红黑树的数据结构来实现,并发控制使用 `synchronized` 和 CAS 来操作。(JDK1.6 以后 `synchronized` 锁做了很多优化) 整个看起来就像是优化过且线程安全的 `HashMap`,虽然在 JDK1.8 中还能看到 `Segment` 的数据结构,但是已经简化了属性,只是为了兼容旧版本; @@ -489,7 +492,7 @@ static final class TreeBin extends Node { } ``` -### ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 +### ⭐️ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 #### JDK1.8 之前 @@ -520,7 +523,7 @@ Java 8 几乎完全重写了 `ConcurrentHashMap`,代码量从原来 Java 7 中 Java 8 中,锁粒度更细,`synchronized` 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,就不会影响其他 Node 的读写,效率大幅提升。 -### JDK 1.7 和 JDK 1.8 的 ConcurrentHashMap 实现有什么不同? +### ⭐️JDK 1.7 和 JDK 1.8 的 ConcurrentHashMap 实现有什么不同? - **线程安全实现方式**:JDK 1.7 采用 `Segment` 分段锁来保证安全, `Segment` 是继承自 `ReentrantLock`。JDK1.8 放弃了 `Segment` 分段锁的设计,采用 `Node + CAS + synchronized` 保证线程安全,锁粒度更细,`synchronized` 只锁定当前链表或红黑二叉树的首节点。 - **Hash 碰撞解决方法** : JDK 1.7 采用拉链法,JDK1.8 采用拉链法结合红黑树(链表长度超过一定阈值时,将链表转换为红黑树)。 @@ -557,7 +560,7 @@ public static final Object NULL = new Object(); 翻译过来之后的,大致意思还是单线程下可以容忍歧义,而多线程下无法容忍。 -### ConcurrentHashMap 能保证复合操作的原子性吗? +### ⭐️ConcurrentHashMap 能保证复合操作的原子性吗? `ConcurrentHashMap` 是线程安全的,意味着它可以保证多个线程同时对它进行读写操作时,不会出现数据不一致的情况,也不会导致 JDK1.7 及之前版本的 `HashMap` 多线程操作导致死循环问题。但是,这并不意味着它可以保证所有的复合操作都是原子性的,一定不要搞混了! diff --git a/docs/java/collection/linkedhashmap-source-code.md b/docs/java/collection/linkedhashmap-source-code.md index 08c9a2bcb28..f08d44fc3bd 100644 --- a/docs/java/collection/linkedhashmap-source-code.md +++ b/docs/java/collection/linkedhashmap-source-code.md @@ -3,6 +3,13 @@ title: LinkedHashMap 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: LinkedHashMap,插入顺序,访问顺序,双向链表,LRU,迭代有序,HashMap 扩展,遍历效率 + - - meta + - name: description + content: 解析 LinkedHashMap 在 HashMap 基础上维护双向链表以实现插入/访问有序的机制,及其在 LRU 缓存等场景的应用。 --- ## LinkedHashMap 简介 diff --git a/docs/java/collection/linkedlist-source-code.md b/docs/java/collection/linkedlist-source-code.md index 810ee25cd70..e4858745923 100644 --- a/docs/java/collection/linkedlist-source-code.md +++ b/docs/java/collection/linkedlist-source-code.md @@ -3,6 +3,13 @@ title: LinkedList 源码分析 category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: LinkedList,双向链表,Deque,插入删除复杂度,随机访问,头尾操作,List 接口,链表结构 + - - meta + - name: description + content: 详解 LinkedList 的数据结构与接口实现,分析头尾插入删除的时间复杂度、与 ArrayList 的差异以及不支持随机访问的原因。 --- diff --git a/docs/java/collection/priorityqueue-source-code.md b/docs/java/collection/priorityqueue-source-code.md index b38cae9bcb9..80136ecbc17 100644 --- a/docs/java/collection/priorityqueue-source-code.md +++ b/docs/java/collection/priorityqueue-source-code.md @@ -3,6 +3,13 @@ title: PriorityQueue 源码分析(付费) category: Java tag: - Java集合 +head: + - - meta + - name: keywords + content: PriorityQueue,优先队列,二叉堆,小顶堆,compareTo,offer,poll,扩容,Comparator,堆排序 + - - meta + - name: description + content: 概览 PriorityQueue 的堆结构与核心操作,理解基于二叉堆的优先队列在插入、删除与扩容中的实现细节与性能特征。 --- **PriorityQueue 源码分析** 为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 必读源码系列》](https://javaguide.cn/zhuanlan/source-code-reading.html)中。 diff --git a/docs/java/concurrent/aqs.md b/docs/java/concurrent/aqs.md index c8e079d1a51..3b9fdb881ff 100644 --- a/docs/java/concurrent/aqs.md +++ b/docs/java/concurrent/aqs.md @@ -3,6 +3,13 @@ title: AQS 详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: AQS,AbstractQueuedSynchronizer,同步器,独占锁,共享锁,CLH 队列,acquire,release,阻塞与唤醒,条件队列 + - - meta + - name: description + content: 全面解析 AQS 的队列同步器原理与模板方法,理解其在 ReentrantLock、Semaphore 等同步器中的应用与线程阻塞唤醒机制。 --- @@ -626,7 +633,7 @@ private Node addWaiter(Node mode) { ### AQS 资源获取源码分析(共享模式) -AQS 中以独占模式获取资源的入口方法是 `acquireShared()` ,如下: +AQS 中以共享模式获取资源的入口方法是 `acquireShared()` ,如下: ```JAVA // AQS diff --git a/docs/java/concurrent/atomic-classes.md b/docs/java/concurrent/atomic-classes.md index ec47ba6f66f..4aa7682614e 100644 --- a/docs/java/concurrent/atomic-classes.md +++ b/docs/java/concurrent/atomic-classes.md @@ -3,6 +3,13 @@ title: Atomic 原子类总结 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 原子类,AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,CAS,乐观锁,原子操作,JUC + - - meta + - name: description + content: 概览 JUC 原子类的类型与使用场景,基于 CAS 的原子性保障与并发性能,理解原子类相较于锁的优势与局限。 --- ## Atomic 原子类介绍 diff --git a/docs/java/concurrent/cas.md b/docs/java/concurrent/cas.md index af97f28d0c8..b2b25f19f99 100644 --- a/docs/java/concurrent/cas.md +++ b/docs/java/concurrent/cas.md @@ -3,6 +3,13 @@ title: CAS 详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: CAS,Compare-And-Swap,Unsafe,原子操作,ABA 问题,自旋,乐观锁,原子类 + - - meta + - name: description + content: 解析 Java 中 CAS 的实现与原理,涵盖 Unsafe 提供的原子操作、常见问题如 ABA 以及与锁的对比。 --- 乐观锁和悲观锁的介绍以及乐观锁常见实现方式可以阅读笔者写的这篇文章:[乐观锁和悲观锁详解](https://javaguide.cn/java/concurrent/optimistic-lock-and-pessimistic-lock.html)。 diff --git a/docs/java/concurrent/completablefuture-intro.md b/docs/java/concurrent/completablefuture-intro.md index be21c70e1c7..8452550f754 100644 --- a/docs/java/concurrent/completablefuture-intro.md +++ b/docs/java/concurrent/completablefuture-intro.md @@ -3,6 +3,13 @@ title: CompletableFuture 详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: CompletableFuture,异步编排,并行任务,thenCompose,thenCombine,allOf,anyOf,线程池,Future + - - meta + - name: description + content: 介绍 CompletableFuture 的核心概念与常用 API,涵盖并行执行、任务编排与结果聚合,助力高性能接口设计。 --- 实际项目中,一个接口可能需要同时获取多种不同的数据,然后再汇总返回,这种场景还是挺常见的。举个例子:用户请求获取订单信息,可能需要同时获取用户信息、商品详情、物流信息、商品推荐等数据。 @@ -80,8 +87,6 @@ public class CompletableFuture implements Future, CompletionStage { ![](https://oss.javaguide.cn/github/javaguide/java/concurrent/completablefuture-class-diagram.jpg) -`CompletionStage` 接口描述了一个异步计算的阶段。很多计算可以分成多个阶段或步骤,此时可以通过它将所有步骤组合起来,形成异步计算的流水线。 - `CompletableFuture` 除了提供了更为好用和强大的 `Future` 特性之外,还提供了函数式编程的能力。 ![](https://oss.javaguide.cn/javaguide/image-20210902092441434.png) diff --git a/docs/java/concurrent/java-concurrent-collections.md b/docs/java/concurrent/java-concurrent-collections.md index 45aa258818a..c13320de61b 100644 --- a/docs/java/concurrent/java-concurrent-collections.md +++ b/docs/java/concurrent/java-concurrent-collections.md @@ -3,6 +3,13 @@ title: Java 常见并发容器总结 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 并发容器,ConcurrentHashMap,CopyOnWriteArrayList,ConcurrentLinkedQueue,BlockingQueue,ConcurrentSkipListMap,JUC + - - meta + - name: description + content: 总览 JUC 并发容器及特性,涵盖线程安全 Map、读多写少 List、非阻塞队列与阻塞队列、跳表等常用数据结构。 --- JDK 提供的这些容器大部分在 `java.util.concurrent` 包中。 @@ -142,7 +149,7 @@ private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueu 最低层的链表维护了跳表内所有的元素,每上面一层链表都是下面一层的子集。 -跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素大于当前链表中的取值,就会转入下一层链表继续找。这也就是说在查找过程中,搜索是跳跃式的。如上图所示,在跳表中查找元素 18。 +跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素小于当前访问节点的后继节点(或后继节点为空),就会转入下一层链表继续找。这也就是说在查找过程中,搜索是跳跃式的。如上图所示,在跳表中查找元素 18。 ![在跳表中查找元素18](https://oss.javaguide.cn/github/javaguide/java/32005738.jpg) diff --git a/docs/java/concurrent/java-concurrent-questions-01.md b/docs/java/concurrent/java-concurrent-questions-01.md index e1768d04d45..f8ee3ebf23d 100644 --- a/docs/java/concurrent/java-concurrent-questions-01.md +++ b/docs/java/concurrent/java-concurrent-questions-01.md @@ -92,7 +92,7 @@ JDK 1.2 之前,Java 线程是基于绿色线程(Green Threads)实现的, 从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的**堆**和**方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器**、**虚拟机栈** 和 **本地方法栈**。 -**总结:** **线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。** +**总结:** 线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。 下面是该知识点的扩展内容! @@ -128,8 +128,6 @@ JDK 1.2 之前,Java 线程是基于绿色线程(Green Threads)实现的, 严格来说,Java 就只有一种方式可以创建线程,那就是通过`new Thread().start()`创建。不管是哪种方式,最终还是依赖于`new Thread().start()`。 -关于这个问题的详细分析可以查看这篇文章:[大家都说 Java 有三种创建线程的方式!并发编程中的惊天骗局!](https://mp.weixin.qq.com/s/NspUsyhEmKnJ-4OprRFp9g)。 - ### ⭐️说说线程的生命周期和状态? Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态: @@ -143,7 +141,7 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。 -Java 线程状态变迁图(图源:[挑错 |《Java 并发编程的艺术》中关于线程状态的三处错误](https://mp.weixin.qq.com/s/UOrXql_LhOD8dhTq_EPI0w)): +Java 线程状态变迁图(图源:[挑错 |《Java 并发编程的艺术》中关于线程状态的三处错误](https://mp.weixin.qq.com/s/0UTyrJpRKaKhkhHcQtXAiA)): ![Java 线程状态变迁图](https://oss.javaguide.cn/github/javaguide/java/concurrent/640.png) @@ -160,8 +158,6 @@ Java 线程状态变迁图(图源:[挑错 |《Java 并发编程的艺术》中 - 当线程进入 `synchronized` 方法/块或者调用 `wait` 后(被 `notify`)重新进入 `synchronized` 方法/块,但是锁被其它线程占有,这个时候线程就会进入 **BLOCKED(阻塞)** 状态。 - 线程在执行完了 `run()`方法之后将会进入到 **TERMINATED(终止)** 状态。 -相关阅读:[线程的几种状态你真的了解么?](https://mp.weixin.qq.com/s/R5MrTsWvk9McFSQ7bS0W2w) 。 - ### 什么是线程上下文切换? 线程在执行过程中会有自己的运行条件和状态(也称上下文),比如上文所说到过的程序计数器,栈信息等。当出现如下情况的时候,线程会从占用 CPU 状态中退出。 @@ -198,7 +194,7 @@ Java 线程状态变迁图(图源:[挑错 |《Java 并发编程的艺术》中 这是另一个非常经典的 Java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来! -new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 `start()` 会执行线程的相应准备工作,然后自动执行 `run()` 方法的内容,这是真正的多线程工作。 但是,直接执行 `run()` 方法,会把 `run()` 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。 +new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 `start()` 会执行线程的相应准备工作,然后自动执行 `run()` 方法的内容,这是真正的多线程工作。 但是,直接执行 `run()` 方法,会把 `run()` 方法当成一个普通方法在调用该方法的线程去执行,所以这并不是多线程工作。 **总结:调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。** @@ -405,14 +401,4 @@ Process finished with exit code 0 线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了循环等待条件,因此避免了死锁。 -## 虚拟线程 - -虚拟线程在 Java 21 正式发布,这是一项重量级的更新。我写了一篇文章来总结虚拟线程常见的问题:[虚拟线程常见问题总结](./virtual-thread.md),包含下面这些问题: - -1. 什么是虚拟线程? -2. 虚拟线程和平台线程有什么关系? -3. 虚拟线程有什么优点和缺点? -4. 如何创建虚拟线程? -5. 虚拟线程的底层原理是什么? - diff --git a/docs/java/concurrent/java-concurrent-questions-02.md b/docs/java/concurrent/java-concurrent-questions-02.md index 40c1b140434..91d55b2df18 100644 --- a/docs/java/concurrent/java-concurrent-questions-02.md +++ b/docs/java/concurrent/java-concurrent-questions-02.md @@ -16,7 +16,7 @@ head: ## ⭐️JMM(Java 内存模型) -JMM(Java 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题:[JMM(Java 内存模型)详解](./jmm.md) 。 +JMM(Java 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题:[JMM(Java 内存模型)详解](https://javaguide.cn/java/concurrent/jmm.html) 。 ## ⭐️volatile 关键字 @@ -664,7 +664,7 @@ public class SynchronizedDemo { `synchronized` 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 `synchronized` 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。 -`ReentrantLock` 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。 +`ReentrantLock` 是 JDK 层面实现的(也就是 API 层面,需要 `lock()` 和 `unlock()` 方法配合 `try/finally` 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。 #### ReentrantLock 比 synchronized 增加了一些高级功能 @@ -672,7 +672,7 @@ public class SynchronizedDemo { - **等待可中断** : `ReentrantLock`提供了一种能够中断等待锁的线程的机制,通过 `lock.lockInterruptibly()` 来实现这个机制。也就是说当前线程在等待获取锁的过程中,如果其他线程中断当前线程「 `interrupt()` 」,当前线程就会抛出 `InterruptedException` 异常,可以捕捉该异常进行相应处理。 - **可实现公平锁** : `ReentrantLock`可以指定是公平锁还是非公平锁。而`synchronized`只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。`ReentrantLock`默认情况是非公平的,可以通过 `ReentrantLock`类的`ReentrantLock(boolean fair)`构造方法来指定是否是公平的。 -- **可实现选择性通知(锁可以绑定多个条件)**: `synchronized`关键字与`wait()`和`notify()`/`notifyAll()`方法相结合可以实现等待/通知机制。`ReentrantLock`类当然也可以实现,但是需要借助于`Condition`接口与`newCondition()`方法。 +- **通知机制更强大**:`ReentrantLock` 通过绑定多个 `Condition` 对象,可以实现分组唤醒和选择性通知。这解决了 `synchronized` 只能随机唤醒或全部唤醒的效率问题,为复杂的线程协作场景提供了强大的支持。 - **支持超时** :`ReentrantLock` 提供了 `tryLock(timeout)` 的方法,可以指定等待获取锁的最长等待时间,如果超过了等待时间,就会获取锁失败,不会一直等待。 如果你想使用上述功能,那么选择 `ReentrantLock` 是一个不错的选择。 @@ -762,8 +762,14 @@ public class SynchronizedDemo { ### 可中断锁和不可中断锁有什么区别? -- **可中断锁**:获取锁的过程中可以被中断,不需要一直等到获取锁之后 才能进行其他逻辑处理。`ReentrantLock` 就属于是可中断锁。 -- **不可中断锁**:一旦线程申请了锁,就只能等到拿到锁以后才能进行其他的逻辑处理。 `synchronized` 就属于是不可中断锁。 +它们的区别在于:**线程在获取锁的过程中被阻塞时,是否能够因为中断而提前放弃等待。** + +- **不可中断锁**:线程在等待锁期间即使收到中断信号,也不会退出阻塞状态,而是一直等待直到获得锁。中断状态会被保留,但不会影响锁的获取过程。 + - `synchronized` 属于典型的不可中断锁。 + - `ReentrantLock#lock()` 也是不可中断的。 +- **可中断锁**:线程在等待锁的过程中如果收到中断信号,会立即停止等待并抛出 `InterruptedException`,从而有机会进行取消或错误处理。 + - `ReentrantLock#lockInterruptibly()` 实现了可中断锁。 + - `ReentrantLock#tryLock(long time, TimeUnit unit)` (带超时的尝试获取)也是可中断的。 ## ReentrantReadWriteLock @@ -792,7 +798,7 @@ public interface ReadWriteLock { ![](https://oss.javaguide.cn/github/javaguide/java/concurrent/reentrantreadwritelock-class-diagram.png) -`ReentrantReadWriteLock` 也支持公平锁和非公平锁,默认使用非公平锁,可以通过构造器来显示的指定。 +`ReentrantReadWriteLock` 也支持公平锁和非公平锁,默认使用非公平锁,可以通过构造器来显式地指定。 ```java // 传入一个 boolean 值,true 时为公平锁,false 时为非公平锁 diff --git a/docs/java/concurrent/java-concurrent-questions-03.md b/docs/java/concurrent/java-concurrent-questions-03.md index dc8dc62e979..db154cb915e 100644 --- a/docs/java/concurrent/java-concurrent-questions-03.md +++ b/docs/java/concurrent/java-concurrent-questions-03.md @@ -399,7 +399,7 @@ public void allowCoreThreadTimeOut(boolean value) { 如果「设置了核心线程的存活时间」或者「线程数量超过了核心线程数量」,则将 `timed` 标记为 `true` ,表明获取任务时需要使用 `poll()` 指定超时时间。 -- `timed == true` :使用 `poll()` 来获取任务。使用 `poll()` 方法获取任务超时的话,则当前线程会退出执行( `TERMINATED` ),该线程从线程池中被移除。 +- `timed == true` :使用 `poll(timeout, unit)` 来获取任务。使用 `poll(timeout, unit)` 方法获取任务超时的话,则当前线程会退出执行( `TERMINATED` ),该线程从线程池中被移除。 - `timed == false` :使用 `take()` 来获取任务。使用 `take()` 方法获取任务会让当前线程一直阻塞等待(`WAITING`)。 源码如下: @@ -626,9 +626,9 @@ new RejectedExecutionHandler() { 不同的线程池会选用不同的阻塞队列,我们可以结合内置线程池来分析。 -- 容量为 `Integer.MAX_VALUE` 的 `LinkedBlockingQueue`(有界阻塞队列):`FixedThreadPool` 和 `SingleThreadExecutor` 。`FixedThreadPool`最多只能创建核心线程数的线程(核心线程数和最大线程数相等),`SingleThreadExecutor`只能创建一个线程(核心线程数和最大线程数都是 1),二者的任务队列永远不会被放满。 +- 容量为 `Integer.MAX_VALUE` 的 `LinkedBlockingQueue`(无界阻塞队列):`FixedThreadPool` 和 `SingleThreadExecutor` 。`FixedThreadPool`最多只能创建核心线程数的线程(核心线程数和最大线程数相等),`SingleThreadExecutor`只能创建一个线程(核心线程数和最大线程数都是 1),二者的任务队列永远不会被放满。 - `SynchronousQueue`(同步队列):`CachedThreadPool` 。`SynchronousQueue` 没有容量,不存储元素,目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。也就是说,`CachedThreadPool` 的最大线程数是 `Integer.MAX_VALUE` ,可以理解为线程数是可以无限扩展的,可能会创建大量线程,从而导致 OOM。 -- `DelayedWorkQueue`(延迟队列):`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor` 。`DelayedWorkQueue` 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。`DelayedWorkQueue` 添加元素满了之后会自动扩容,增加原来容量的 50%,即永远不会阻塞,最大扩容可达 `Integer.MAX_VALUE`,所以最多只能创建核心线程数的线程。 +- `DelayedWorkQueue`(延迟队列):`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor` 。`DelayedWorkQueue` 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。`DelayedWorkQueue` 是一个无界队列。其底层虽然是数组,但当数组容量不足时,它会自动进行扩容,因此队列永远不会被填满。当任务不断提交时,它们会全部被添加到队列中。这意味着线程池的线程数量永远不会超过其核心线程数,最大线程数参数对于使用该队列的线程池来说是无效的。 - `ArrayBlockingQueue`(有界阻塞队列):底层由数组实现,容量一旦创建,就不能修改。 ### ⭐️线程池处理任务的流程了解吗? @@ -773,7 +773,7 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内 ![动态配置线程池参数最终效果](https://oss.javaguide.cn/github/javaguide/java/concurrent/meituan-dynamically-configuring-thread-pool-parameters.png) -还没看够?我在[《后端面试高频系统设计&场景题》](https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html#%E4%BB%8B%E7%BB%8D)中详细介绍了如何设计一个动态线程池,这也是面试中常问的一道系统设计题。 +还没看够?我在[《后端面试高频系统设计&场景题》](https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html)中详细介绍了如何设计一个动态线程池,这也是面试中常问的一道系统设计题。 ![《后端面试高频系统设计&场景题》](https://oss.javaguide.cn/xingqiu/back-end-interview-high-frequency-system-design-and-scenario-questions-fengmian.png) @@ -815,7 +815,7 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内 重点是要掌握 `CompletableFuture` 的使用以及常见面试题。 -除了下面的面试题之外,还推荐你看看我写的这篇文章: [CompletableFuture 详解](./completablefuture-intro.md)。 +除了下面的面试题之外,还推荐你看看我写的这篇文章: [CompletableFuture 详解](https://javaguide.cn/java/concurrent/completablefuture-intro.html)。 ### Future 类有什么用? @@ -975,7 +975,7 @@ CompletableFuture.runAsync(() -> { ## AQS -关于 AQS 源码的详细分析,可以看看这一篇文章:[AQS 详解](./aqs.md)。 +关于 AQS 源码的详细分析,可以看看这一篇文章:[AQS 详解](https://javaguide.cn/java/concurrent/aqs.html)。 ### AQS 是什么? @@ -1349,9 +1349,13 @@ public int await() throws InterruptedException, BrokenBarrierException { ## 虚拟线程 -虚拟线程在 Java 21 正式发布,这是一项重量级的更新。 +虚拟线程在 Java 21 正式发布,这是一项重量级的更新。虽然目前面试中问的不多,但还是建议大家去简单了解一下。我写了一篇文章来总结虚拟线程常见的问题:[虚拟线程常见问题总结](https://javaguide.cn/java/concurrent/virtual-thread.html),包含下面这些问题: -虽然目前面试中问的不多,但还是建议大家去简单了解一下,具体可以阅读这篇文章:[虚拟线程极简入门](./virtual-thread.md) 。重点搞清楚虚拟线程和平台线程的关系以及虚拟线程的优势即可。 +1. 什么是虚拟线程? +2. 虚拟线程和平台线程有什么关系? +3. 虚拟线程有什么优点和缺点? +4. 如何创建虚拟线程? +5. 虚拟线程的底层原理是什么? ## 参考 diff --git a/docs/java/concurrent/java-thread-pool-best-practices.md b/docs/java/concurrent/java-thread-pool-best-practices.md index 04154bfa378..36a81fc1db9 100644 --- a/docs/java/concurrent/java-thread-pool-best-practices.md +++ b/docs/java/concurrent/java-thread-pool-best-practices.md @@ -3,6 +3,13 @@ title: Java 线程池最佳实践 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 线程池最佳实践,ThreadPoolExecutor,Executors 风险,有界队列,OOM,拒绝策略,监控,线程命名,参数配置 + - - meta + - name: description + content: 总结线程池使用的关键实践与避坑指南,强调手动配置、避免 Executors OOM 风险、监控与命名等重要事项。 --- 简单总结一下我了解的使用线程池的时候应该注意的东西,网上似乎还没有专门写这方面的文章。 @@ -273,7 +280,7 @@ public class ThreadPoolExecutorConfig { int maxPoolSize = (int) (processNum / (1 - 0.5)); threadPoolExecutor.setCorePoolSize(corePoolSize); // 核心池大小 threadPoolExecutor.setMaxPoolSize(maxPoolSize); // 最大线程数 - threadPoolExecutor.setQueueCapacity(maxPoolSize * 1000); // 队列程度 + threadPoolExecutor.setQueueCapacity(maxPoolSize * 1000); // 队列长度 threadPoolExecutor.setThreadPriority(Thread.MAX_PRIORITY); threadPoolExecutor.setDaemon(false); threadPoolExecutor.setKeepAliveSeconds(300);// 线程空闲时间 diff --git a/docs/java/concurrent/java-thread-pool-summary.md b/docs/java/concurrent/java-thread-pool-summary.md index 47a1f916de2..283871b7988 100644 --- a/docs/java/concurrent/java-thread-pool-summary.md +++ b/docs/java/concurrent/java-thread-pool-summary.md @@ -3,6 +3,13 @@ title: Java 线程池详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 线程池,ThreadPoolExecutor,Executor,核心线程数,最大线程数,任务队列,拒绝策略,池化技术,ScheduledThreadPoolExecutor + - - meta + - name: description + content: 系统梳理 Java 线程池的原理与架构,包含 Executor 框架、关键参数与队列、常见实现及配置要点。 --- @@ -13,15 +20,13 @@ tag: ## 线程池介绍 -顾名思义,线程池就是管理一系列线程的资源池,其提供了一种限制和管理线程资源的方式。每个线程池还维护一些基本统计信息,例如已完成任务的数量。 - -这里借用《Java 并发编程的艺术》书中的部分内容来总结一下使用线程池的好处: +池化技术想必大家已经屡见不鲜了,线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。 -- **降低资源消耗**。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 -- **提高响应速度**。当任务到达时,任务可以不需要等到线程创建就能立即执行。 -- **提高线程的可管理性**。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 +线程池提供了一种限制和管理资源(包括执行一个任务)的方式。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。使用线程池主要带来以下几个好处: -**线程池一般用于执行多个不相关联的耗时任务,没有多线程的情况下,任务顺序执行,使用了线程池的话可让多个不相关联的任务同时执行。** +1. **降低资源消耗**:线程池里的线程是可以重复利用的。一旦线程完成了某个任务,它不会立即销毁,而是回到池子里等待下一个任务。这就避免了频繁创建和销毁线程带来的开销。 +2. **提高响应速度**:因为线程池里通常会维护一定数量的核心线程(或者说“常驻工人”),任务来了之后,可以直接交给这些已经存在的、空闲的线程去执行,省去了创建线程的时间,任务能够更快地得到处理。 +3. **提高线程的可管理性**:线程池允许我们统一管理池中的线程。我们可以配置线程池的大小(核心线程数、最大线程数)、任务队列的类型和大小、拒绝策略等。这样就能控制并发线程的总量,防止资源耗尽,保证系统的稳定性。同时,线程池通常也提供了监控接口,方便我们了解线程池的运行状态(比如有多少活跃线程、多少任务在排队等),便于调优。 ## Executor 框架介绍 @@ -526,7 +531,7 @@ Finished all threads // 任务全部执行完了才会跳出来,因为executo } ``` -更多关于线程池源码分析的内容推荐这篇文章:硬核干货:[4W 字从源码上分析 JUC 线程池 ThreadPoolExecutor 的实现原理](https://www.throwx.cn/2020/08/23/java-concurrency-thread-pool-executor/) +更多关于线程池源码分析的内容推荐这篇文章:硬核干货:[4W 字从源码上分析 JUC 线程池 ThreadPoolExecutor 的实现原理](https://www.cnblogs.com/throwable/p/13574306.html)。 现在,让我们在回到示例代码, 现在应该是不是很容易就可以搞懂它的原理了呢? diff --git a/docs/java/concurrent/jmm.md b/docs/java/concurrent/jmm.md index dbc36a351b9..9afe92b3ff7 100644 --- a/docs/java/concurrent/jmm.md +++ b/docs/java/concurrent/jmm.md @@ -94,7 +94,7 @@ JMM 说白了就是定义了一些规范来解决这些问题,开发者可以 **什么是主内存?什么是本地内存?** - **主内存**:所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量,还是局部变量,类信息、常量、静态变量都是放在主内存中。为了获取更好的运行速度,虚拟机及硬件系统可能会让工作内存优先存储于寄存器和高速缓存中。 -- **本地内存**:每个线程都有一个私有的本地内存,本地内存存储了该线程以读 / 写共享变量的副本。每个线程只能操作自己本地内存中的变量,无法直接访问其他线程的本地内存。如果线程间需要通信,必须通过主内存来进行。本地内存是 JMM 抽象出来的一个概念,并不真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。 +- **本地内存**:每个线程都有一个私有的本地内存,本地内存存储了该线程已读 / 写共享变量的副本。每个线程只能操作自己本地内存中的变量,无法直接访问其他线程的本地内存。如果线程间需要通信,必须通过主内存来进行。本地内存是 JMM 抽象出来的一个概念,并不真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。 Java 内存模型的抽象示意图如下: diff --git a/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md b/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md index ba370690a11..88043a910be 100644 --- a/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md +++ b/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md @@ -3,6 +3,13 @@ title: 乐观锁和悲观锁详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 乐观锁,悲观锁,synchronized,ReentrantLock,CAS,版本号,并发控制,死锁,性能 + - - meta + - name: description + content: 对比乐观锁与悲观锁的思想与实现,结合 synchronized、ReentrantLock 与 CAS 的应用场景与优劣分析。 --- 如果将悲观锁(Pessimistic Lock)和乐观锁(Optimistic Lock)对应到现实生活中来。悲观锁有点像是一位比较悲观(也可以说是未雨绸缪)的人,总是会假设最坏的情况,避免出现问题。乐观锁有点像是一位比较乐观的人,总是会假设最好的情况,在要出现问题之前快速解决问题。 @@ -67,7 +74,7 @@ sum.increment(); 1. 操作员 A 此时将其读出( `version`=1 ),并从其帐户余额中扣除 $50( $100-\$50 )。 2. 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( `version`=1 ),并从其帐户余额中扣除 $20 ( $100-\$20 )。 3. 操作员 A 完成了修改工作,将数据版本号( `version`=1 ),连同帐户扣除后余额( `balance`=\$50 ),提交至数据库更新,此时由于提交数据版本等于数据库记录当前版本,数据被更新,数据库记录 `version` 更新为 2 。 -4. 操作员 B 完成了操作,也将版本号( `version`=1 )试图向数据库提交数据( `balance`=\$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 +4. 操作员 B 完成了操作,也将版本号( `version`=1 )试图向数据库提交数据( `balance`=\$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,而数据库记录当前版本为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 这样就避免了操作员 B 用基于 `version`=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。 diff --git a/docs/java/concurrent/reentrantlock.md b/docs/java/concurrent/reentrantlock.md index ef1cd38625c..0bc97de2d6a 100644 --- a/docs/java/concurrent/reentrantlock.md +++ b/docs/java/concurrent/reentrantlock.md @@ -3,6 +3,13 @@ title: 从ReentrantLock的实现看AQS的原理及应用 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: ReentrantLock,AQS,公平锁,非公平锁,可重入,lock/unlock,Sync Queue,独占锁,compareAndSetState,acquire + - - meta + - name: description + content: 结合 ReentrantLock 的实现剖析 AQS 工作原理,比较公平与非公平锁、与 synchronized 的差异以及独占锁的加解锁流程。 --- > 本文转载自: @@ -503,9 +510,9 @@ private void setHead(Node node) { // 靠前驱节点判断当前线程是否应该被阻塞 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { - // 获取头结点的节点状态 + // 获取前驱结点的节点状态 int ws = pred.waitStatus; - // 说明头结点处于唤醒状态 + // 说明前驱结点处于唤醒状态 if (ws == Node.SIGNAL) return true; // 通过枚举值我们知道waitStatus>0是取消状态 diff --git a/docs/java/concurrent/threadlocal.md b/docs/java/concurrent/threadlocal.md index 0cdaf0adfd6..b560ad85258 100644 --- a/docs/java/concurrent/threadlocal.md +++ b/docs/java/concurrent/threadlocal.md @@ -3,6 +3,13 @@ title: ThreadLocal 详解 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: ThreadLocal,线程变量副本,ThreadLocalMap,弱引用,哈希冲突,扩容,清理机制,内存泄漏 + - - meta + - name: description + content: 深入解析 ThreadLocal 的设计与实现,涵盖 ThreadLocalMap 的结构、弱引用与清理机制,以及常见使用坑位与规避方式。 --- > 本文来自一枝花算不算浪漫投稿, 原文地址:[https://juejin.cn/post/6844904151567040519](https://juejin.cn/post/6844904151567040519)。 diff --git a/docs/java/concurrent/virtual-thread.md b/docs/java/concurrent/virtual-thread.md index f7f889fb81f..73659bc296e 100644 --- a/docs/java/concurrent/virtual-thread.md +++ b/docs/java/concurrent/virtual-thread.md @@ -3,6 +3,13 @@ title: 虚拟线程常见问题总结 category: Java tag: - Java并发 +head: + - - meta + - name: keywords + content: 虚拟线程,Virtual Threads,Project Loom,Java 21,平台线程,轻量级线程,并发,I/O 密集型,兼容性 + - - meta + - name: description + content: 总结 Java 21 虚拟线程的概念与实践,解析与平台线程关系、适用场景、优势与限制以及常见问题。 --- > 本文部分内容来自 [Lorin](https://github.com/Lorin-github) 的[PR](https://github.com/Snailclimb/JavaGuide/pull/2190)。 diff --git a/docs/java/io/io-basis.md b/docs/java/io/io-basis.md index 1ea1bcd3f86..dd2bbf4e47b 100755 --- a/docs/java/io/io-basis.md +++ b/docs/java/io/io-basis.md @@ -4,6 +4,13 @@ category: Java tag: - Java IO - Java基础 +head: + - - meta + - name: keywords + content: IO 基础,字节流,字符流,缓冲,文件操作,InputStream,Reader,OutputStream,Writer + - - meta + - name: description + content: 概述 Java IO 的基础概念与核心类,理解字节/字符流、缓冲与文件读写。 --- @@ -20,7 +27,7 @@ Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来 ## 字节流 ### InputStream(字节输入流) - + `InputStream`用于从源头(通常是文件)读取数据(字节信息)到内存中,`java.io.InputStream`抽象类是所有字节输入流的父类。 `InputStream` 常用方法: diff --git a/docs/java/io/io-design-patterns.md b/docs/java/io/io-design-patterns.md index f005a18ece4..c09fcdd382f 100644 --- a/docs/java/io/io-design-patterns.md +++ b/docs/java/io/io-design-patterns.md @@ -4,6 +4,13 @@ category: Java tag: - Java IO - Java基础 +head: + - - meta + - name: keywords + content: IO 设计模式,装饰器,适配器,职责链,流式处理,FilterInputStream + - - meta + - name: description + content: 结合设计模式理解 Java IO 的类结构与扩展方式,掌握流式处理的典型用法。 --- 这篇文章我们简单来看看我们从 IO 中能够学习到哪些设计模式的应用。 diff --git a/docs/java/io/io-model.md b/docs/java/io/io-model.md index e6d48bc0439..27309174e76 100644 --- a/docs/java/io/io-model.md +++ b/docs/java/io/io-model.md @@ -4,6 +4,13 @@ category: Java tag: - Java IO - Java基础 +head: + - - meta + - name: keywords + content: IO 模型,阻塞IO,非阻塞IO,同步异步,多路复用,Reactor,Proactor + - - meta + - name: description + content: 总结常见 IO 模型与并发处理方式,理解阻塞/非阻塞与同步/异步差异。 --- IO 模型这块确实挺难理解的,需要太多计算机底层知识。写这篇文章用了挺久,就非常希望能把我所知道的讲出来吧!希望朋友们能有收获!为了写这篇文章,还翻看了一下《UNIX 网络编程》这本书,太难了,我滴乖乖!心痛~ @@ -87,6 +94,9 @@ Java 中的 NIO 可以看作是 **I/O 多路复用模型**。也有很多人认 相比于同步阻塞 IO 模型,同步非阻塞 IO 模型确实有了很大改进。通过轮询操作,避免了一直阻塞。 +> 同步非阻塞 IO,发起一个 read 调用,如果数据没有准备好,这个时候应用程序可以不阻塞等待,而是切换去做一些小的计算任务,然后很快回来继续发起 read 调用,也就是轮询。这个 +> 轮询不是持续不断发起的,会有间隙, 这个间隙的利用就是同步非阻塞 IO 比同步阻塞 IO 高效的地方。 + 但是,这种 IO 模型同样存在问题:**应用程序不断进行 I/O 系统调用轮询数据是否已经准备好的过程是十分消耗 CPU 资源的。** 这个时候,**I/O 多路复用模型** 就上场了。 diff --git a/docs/java/io/nio-basis.md b/docs/java/io/nio-basis.md index 4cf9723ba37..0c22198be5e 100644 --- a/docs/java/io/nio-basis.md +++ b/docs/java/io/nio-basis.md @@ -4,6 +4,13 @@ category: Java tag: - Java IO - Java基础 +head: + - - meta + - name: keywords + content: NIO,Channel,Buffer,Selector,非阻塞IO,零拷贝,文件与网络 + - - meta + - name: description + content: 介绍 Java NIO 的核心组件与使用方式,理解 Channel/Buffer/Selector 的协作与性能优势。 --- 在学习 NIO 之前,需要先了解一下计算机 I/O 模型的基础理论知识。还不了解的话,可以参考我写的这篇文章:[Java IO 模型详解](https://javaguide.cn/java/io/io-model.html)。 diff --git a/docs/java/jvm/class-file-structure.md b/docs/java/jvm/class-file-structure.md index 31cc64e30fb..3691a7bb65b 100644 --- a/docs/java/jvm/class-file-structure.md +++ b/docs/java/jvm/class-file-structure.md @@ -3,6 +3,13 @@ title: 类文件结构详解 category: Java tag: - JVM +head: + - - meta + - name: keywords + content: Class 文件,常量池,魔数,版本,字段,方法,属性 + - - meta + - name: description + content: 介绍 Java 字节码 Class 文件结构与常量池等核心组成,辅助理解编译产物。 --- ## 回顾一下字节码 @@ -73,7 +80,7 @@ ClassFile { 每当 Java 发布大版本(比如 Java 8,Java9)的时候,主版本号都会加 1。你可以使用 `javap -v` 命令来快速查看 Class 文件的版本号信息。 -高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。所以,我们在实际开发的时候要确保开发的的 JDK 版本和生产环境的 JDK 版本保持一致。 +高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。所以,我们在实际开发的时候要确保开发的 JDK 版本和生产环境的 JDK 版本保持一致。 ### 常量池(Constant Pool) @@ -84,7 +91,7 @@ ClassFile { 紧接着主次版本号之后的是常量池,常量池的数量是 `constant_pool_count-1`(**常量池计数器是从 1 开始计数的,将第 0 项常量空出来是有特殊考虑的,索引值为 0 代表“不引用任何一个常量池项”**)。 -常量池主要存放两大常量:字面量和符号引用。字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量: +常量池主要存放两大常量:字面量和符号引用。字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量: - 类和接口的全限定名 - 字段的名称和描述符 @@ -174,7 +181,7 @@ Java 类的继承关系由类索引、父类索引和接口索引集合三项确 **字段的 access_flag 的取值:** -![字段的 access_flag 的取值](https://oss.javaguide.cn/JVM/image-20201031084342859.png) +![字段的 access_flag 的取值](https://oss.javaguide.cn/github/javaguide/java/jvm/class-file-fields-access_flag.png) ### 方法表集合(Methods) @@ -193,7 +200,7 @@ Class 文件存储格式中对方法的描述与对字段的描述几乎采用 **方法表的 access_flag 取值:** -![方法表的 access_flag 取值](https://oss.javaguide.cn/JVM/image-20201031084248965.png) +![方法表的 access_flag 取值](https://oss.javaguide.cn/github/javaguide/java/jvm/class-file-methods-access_flag.png) 注意:因为`volatile`修饰符和`transient`修饰符不可以修饰方法,所以方法表的访问标志中没有这两个对应的标志,但是增加了`synchronized`、`native`、`abstract`等关键字修饰方法,所以也就多了这些关键字对应的标志。 diff --git a/docs/java/jvm/class-loading-process.md b/docs/java/jvm/class-loading-process.md index 6d6bcd2ea54..2d587cb5278 100644 --- a/docs/java/jvm/class-loading-process.md +++ b/docs/java/jvm/class-loading-process.md @@ -3,6 +3,13 @@ title: 类加载过程详解 category: Java tag: - JVM +head: + - - meta + - name: keywords + content: 类加载,加载,验证,准备,解析,初始化,clinit,常量池 + - - meta + - name: description + content: 拆解 JVM 类加载的各阶段与关键细节,理解验证、准备、解析与初始化的具体行为。 --- ## 类的生命周期 @@ -33,7 +40,7 @@ tag: 虚拟机规范上面这 3 点并不具体,因此是非常灵活的。比如:"通过全类名获取定义此类的二进制字节流" 并没有指明具体从哪里获取( `ZIP`、 `JAR`、`EAR`、`WAR`、网络、动态代理技术运行时动态生成、其他文件生成比如 `JSP`...)、怎样获取。 -加载这一步主要是通过我们后面要讲到的 **类加载器** 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 **双亲委派模型** 决定(不过,我们也能打破由双亲委派模型)。 +加载这一步主要是通过我们后面要讲到的 **类加载器** 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 **双亲委派模型** 决定(不过,我们也能打破双亲委派模型)。 > 类加载器、双亲委派模型也是非常重要的知识点,这部分内容在[类加载器详解](https://javaguide.cn/java/jvm/classloader.html "类加载器详解")这篇文章中有详细介绍到。阅读本篇文章的时候,大家知道有这么个东西就可以了。 diff --git a/docs/java/jvm/classloader.md b/docs/java/jvm/classloader.md index 35a8bfd0eda..1ffed7a29ae 100644 --- a/docs/java/jvm/classloader.md +++ b/docs/java/jvm/classloader.md @@ -3,6 +3,13 @@ title: 类加载器详解(重点) category: Java tag: - JVM +head: + - - meta + - name: keywords + content: 类加载器,双亲委派,加载链接初始化,自定义 ClassLoader,ClassPath + - - meta + - name: description + content: 深入讲解 JVM 类加载机制与双亲委派模型,包含加载流程与常见实践。 --- ## 回顾一下类加载过程 @@ -101,7 +108,7 @@ JVM 中内置了三个重要的 `ClassLoader`: 除了 `BootstrapClassLoader` 是 JVM 自身的一部分之外,其他所有的类加载器都是在 JVM 外部实现的,并且全都继承自 `ClassLoader`抽象类。这样做的好处是用户可以自定义类加载器,以便让应用程序自己决定如何去获取所需的类。 -每个 `ClassLoader` 可以通过`getParent()`获取其父 `ClassLoader`,如果获取到 `ClassLoader` 为`null`的话,那么该类是通过 `BootstrapClassLoader` 加载的。 +每个 `ClassLoader` 可以通过`getParent()`获取其父 `ClassLoader`,如果获取到 `ClassLoader` 为`null`的话,那么该类加载器的父类加载器是 `BootstrapClassLoader` 。 ```java public abstract class ClassLoader { @@ -354,13 +361,29 @@ Tomcat 这四个自定义的类加载器对应的目录如下: 从图中的委派关系中可以看出: - `CommonClassLoader`作为 `CatalinaClassLoader` 和 `SharedClassLoader` 的父加载器。`CommonClassLoader` 能加载的类都可以被 `CatalinaClassLoader` 和 `SharedClassLoader` 使用。因此,`CommonClassLoader` 是为了实现公共类库(可以被所有 Web 应用和 Tomcat 内部组件使用的类库)的共享和隔离。 -- `CatalinaClassLoader` 和 `SharedClassLoader` 能加载的类则与对方相互隔离。`CatalinaClassLoader` 用于加载 Tomcat 自身的类,为了隔离 Tomcat 本身的类和 Web 应用的类。`SharedClassLoader` 作为 `WebAppClassLoader` 的父加载器,专门来加载 Web 应用之间共享的类比如 Spring、Mybatis。 +- `CatalinaClassLoader` 和 `SharedClassLoader` 能加载的类则与对方相互隔离。`CatalinaClassLoader` 用于加载 Tomcat 自身的类,为了隔离 Tomcat 本身的类和 Web 应用的类。`SharedClassLoader` 作为 `WebAppClassLoader` 的父加载器,专门来加载 Web 应用之间共享的类,但是在Tomcat的默认配置下`catalina.properties`配置文件的`shared.loader= `值为空,所以`SharedClassLoader` 并不生效,`SharedClassLoader` 实际上会退化为 `CommonClassLoader`,`SharedClassLoader`比较合适用来加载多个web应用间共享的类库,比如整个公司级别的监控、日志等。 - 每个 Web 应用都会创建一个单独的 `WebAppClassLoader`,并在启动 Web 应用的线程里设置线程线程上下文类加载器为 `WebAppClassLoader`。各个 `WebAppClassLoader` 实例之间相互隔离,进而实现 Web 应用之间的类隔。 单纯依靠自定义类加载器没办法满足某些场景的要求,例如,有些情况下,高层的类加载器需要加载低层的加载器才能加载的类。 比如,SPI 中,SPI 的接口(如 `java.sql.Driver`)是由 Java 核心库提供的,由`BootstrapClassLoader` 加载。而 SPI 的实现(如`com.mysql.cj.jdbc.Driver`)是由第三方供应商提供的,它们是由应用程序类加载器或者自定义类加载器来加载的。默认情况下,一个类及其依赖类由同一个类加载器加载。所以,加载 SPI 的接口的类加载器(`BootstrapClassLoader`)也会用来加载 SPI 的实现。按照双亲委派模型,`BootstrapClassLoader` 是无法找到 SPI 的实现类的,因为它无法委托给子类加载器去尝试加载。 +这里需要注意:JDK 9+ 之后引入模块化,JDBC API 被拆分到 `java.sql` 模块中,不再是 `BootstrapClassLoader` 直接加载,而是由 `PlatformClassLoader` 加载。 + +```java +public class ClassLoaderTest { + public static void main(String[] args) throws ClassNotFoundException { + Class clazz = Class.forName("java.sql.Driver"); + ClassLoader loader = clazz.getClassLoader(); + System.out.println("Loader for java.sql.Driver: " + loader); + + // .jdks/corretto-1.8.0_442/bin/java 环境下为 Loader for java.sql.Driver: null + + // .jdks/jbr-17.0.12/bin/java 环境下为 Loader for java.sql.Driver: jdk.internal.loader.ClassLoaders$PlatformClassLoader@30f39991 + } +} +``` + 再比如,假设我们的项目中有 Spring 的 jar 包,由于其是 Web 应用之间共享的,因此会由 `SharedClassLoader` 加载(Web 服务器是 Tomcat)。我们项目中有一些用到了 Spring 的业务类,比如实现了 Spring 提供的接口、用到了 Spring 提供的注解。所以,加载 Spring 的类加载器(也就是 `SharedClassLoader`)也会用来加载这些业务类。但是业务类在 Web 应用目录下,不在 `SharedClassLoader` 的加载路径下,所以 `SharedClassLoader` 无法找到业务类,也就无法加载它们。 如何解决这个问题呢? 这个时候就需要用到 **线程上下文类加载器(`ThreadContextClassLoader`)** 了。 diff --git a/docs/java/jvm/jdk-monitoring-and-troubleshooting-tools.md b/docs/java/jvm/jdk-monitoring-and-troubleshooting-tools.md index 33fc2d8767b..011dfaa8a07 100644 --- a/docs/java/jvm/jdk-monitoring-and-troubleshooting-tools.md +++ b/docs/java/jvm/jdk-monitoring-and-troubleshooting-tools.md @@ -3,6 +3,13 @@ title: JDK监控和故障处理工具总结 category: Java tag: - JVM +head: + - - meta + - name: keywords + content: JDK 工具,jps,jstat,jmap,jstack,jvisualvm,诊断,监控 + - - meta + - name: description + content: 汇总 JDK 常用监控与排错工具及使用示例,辅助定位与分析 JVM 问题。 --- ## JDK 命令行工具 @@ -276,7 +283,7 @@ JConsole 可以显示当前内存的详细信息。不仅包括堆内存/非堆 点击右边的“执行 GC(G)”按钮可以强制应用程序执行一个 Full GC。 -> - **新生代 GC(Minor GC)**:指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。 +> - **新生代 GC(Minor GC)**:指发生新生代的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。 > - **老年代 GC(Major GC/Full GC)**:指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。 ![内存监控 ](./pictures/jdk监控和故障处理工具总结/3内存监控.png) diff --git a/docs/java/jvm/jvm-garbage-collection.md b/docs/java/jvm/jvm-garbage-collection.md index 970933ee5ce..b5e837a5ac6 100644 --- a/docs/java/jvm/jvm-garbage-collection.md +++ b/docs/java/jvm/jvm-garbage-collection.md @@ -3,6 +3,13 @@ title: JVM垃圾回收详解(重点) category: Java tag: - JVM +head: + - - meta + - name: keywords + content: 垃圾回收,GC 算法,分代回收,标记清除,复制,整理,G1,ZGC + - - meta + - name: description + content: 总结 JVM 垃圾回收的算法与回收器,解析内存管理与调优要点。 --- > 如果没有特殊说明,都是针对的是 HotSpot 虚拟机。 @@ -521,7 +528,7 @@ G1 收集器的运作大致分为以下几个步骤: ### ZGC 收集器 -与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。 +与 CMS、ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。 ZGC 可以将暂停时间控制在几毫秒以内,且暂停时间不受堆内存大小的影响,出现 Stop The World 的情况会更少,但代价是牺牲了一些吞吐量。ZGC 最大支持 16TB 的堆内存。 diff --git a/docs/java/jvm/jvm-in-action.md b/docs/java/jvm/jvm-in-action.md index 99b6fc6041d..032f870f6c2 100644 --- a/docs/java/jvm/jvm-in-action.md +++ b/docs/java/jvm/jvm-in-action.md @@ -3,6 +3,13 @@ title: JVM线上问题排查和性能调优案例 category: Java tag: - JVM +head: + - - meta + - name: keywords + content: JVM 实战,线上排查,性能调优,内存分析,GC 优化,工具 + - - meta + - name: description + content: 汇集 JVM 在生产中的问题排查与优化案例,涵盖内存与 GC、工具使用等。 --- JVM 线上问题排查和性能调优也是面试常问的一个问题,尤其是社招中大厂的面试。 diff --git a/docs/java/jvm/jvm-intro.md b/docs/java/jvm/jvm-intro.md index 2fdb9b3e055..faef7db1e22 100644 --- a/docs/java/jvm/jvm-intro.md +++ b/docs/java/jvm/jvm-intro.md @@ -3,6 +3,13 @@ title: 大白话带你认识 JVM category: Java tag: - JVM +head: + - - meta + - name: keywords + content: JVM 基础,类加载,方法区,堆栈,程序计数器,运行时数据区 + - - meta + - name: description + content: 用通俗方式介绍 JVM 的基本组成与类加载执行流程,帮助快速入门虚拟机原理。 --- > 来自[说出你的愿望吧丷](https://juejin.im/user/5c2400afe51d45451758aa96)投稿,原文地址:。 diff --git a/docs/java/jvm/jvm-parameters-intro.md b/docs/java/jvm/jvm-parameters-intro.md index b97fc66d923..1204e3f60f6 100644 --- a/docs/java/jvm/jvm-parameters-intro.md +++ b/docs/java/jvm/jvm-parameters-intro.md @@ -3,6 +3,13 @@ title: 最重要的JVM参数总结 category: Java tag: - JVM +head: + - - meta + - name: keywords + content: JVM 参数,堆大小,栈大小,GC 设置,性能调优,XX 参数 + - - meta + - name: description + content: 总结常用 JVM 参数与配置方法,结合内存与 GC 调优的实践建议。 --- > 本文由 JavaGuide 翻译自 [https://www.baeldung.com/jvm-parameters](https://www.baeldung.com/jvm-parameters),并对文章进行了大量的完善补充。 diff --git a/docs/java/jvm/memory-area.md b/docs/java/jvm/memory-area.md index c841024a452..dacac25216c 100644 --- a/docs/java/jvm/memory-area.md +++ b/docs/java/jvm/memory-area.md @@ -3,6 +3,13 @@ title: Java内存区域详解(重点) category: Java tag: - JVM +head: + - - meta + - name: keywords + content: 运行时数据区,堆,方法区,虚拟机栈,本地方法栈,程序计数器,对象创建 + - - meta + - name: description + content: 详解 JVM 运行时数据区的组成与作用,覆盖对象创建与访问定位等核心机制。 --- @@ -80,7 +87,7 @@ Java 虚拟机规范对于运行时数据区域的规定是相当宽松的。以 **操作数栈** 主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。 -**动态链接** 主要服务一个方法需要调用其他方法的场景。Class 文件的常量池里保存有大量的符号引用比如方法引用的符号引用。当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用,这个过程也被称为 **动态连接** 。 +**动态链接**是 Java 虚拟机实现方法调用的关键机制之一。在 Class 文件中,方法调用以**符号引用**的形式存在于常量池。为了执行调用,这些符号引用必须被转换为内存中的**直接引用**。这个转换过程分为两种情况:对于静态方法、私有方法等在编译期就能确定版本的方法,这个转换在**类加载的解析阶段**就完成了,这称为**静态解析**。而对于需要根据对象实际类型才能确定具体实现的**虚方法**(这是实现多态的基础),这个转换过程则被推迟到**程序运行期间**,由**动态链接**来完成。因此,**动态链接**的核心作用是**在运行时解析虚方法的调用点,将其链接到正确的方法版本上**。 ![](https://oss.javaguide.cn/github/javaguide/jvmimage-20220331175738692.png) @@ -177,7 +184,19 @@ MaxTenuringThreshold of 20 is invalid; must be between 0 and 15 《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,方法区到底要如何实现那就是虚拟机自己要考虑的事情了。也就是说,在不同的虚拟机实现上,方法区的实现是不同的。 -当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 **类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据**。 +当虚拟机加载一个类时,它会从 Class 文件中解析出相应的信息,并将这些**元数据**存入方法区。具体来说,方法区主要存储以下核心数据: + +1. **类的元数据**:包括类的完整结构,如类名、父类、实现的接口、访问修饰符,以及字段和方法的详细信息(名称、类型、修饰符等)。 +2. **方法的字节码**:每个方法的原始指令序列。 +3. **运行时常量池**:每个类独有的,由 Class 文件中的常量池转换而来,用于存放编译期生成的各种字面量和对类型、字段、方法的符号引用。 + +需要特别注意的是,以下几类数据虽然在逻辑上与类相关,但在 HotSpot 虚拟机中,它们并不存储在方法区内: + +- **静态变量(Static Variables)**:自 JDK 7 起,静态变量已从方法区(永久代)**移至 Java 堆(Heap)中**,与该类的 `java.lang.Class` 对象一起存放。 +- **字符串常量池(String Pool)**:同样自 JDK 7 起,字符串常量池也**移至 Java 堆中**。 +- **即时编译器编译后的代码缓存(JIT Code Cache)**:JIT 编译器将热点方法的字节码编译成的本地机器码,存放在一个**独立的、名为“Code Cache”的内存区域**,而不是方法区本身。这样做是为了实现更高效的执行和内存管理。 + +![method-area-jdk1.7](https://oss.javaguide.cn/github/javaguide/java/jvm/method-area-jdk1.7.png) **方法区和永久代以及元空间是什么关系呢?** 方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。 @@ -242,10 +261,10 @@ Class 文件中除了有类的版本、字段、方法、接口等描述信息 **字符串常量池** 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。 ```java -// 在字符串常量池中创建字符串对象 ”ab“ -// 将字符串对象 ”ab“ 的引用赋值给给 aa +// 1.在字符串常量池中查询字符串对象 "ab",如果没有则创建"ab"并放入字符串常量池 +// 2.将字符串对象 "ab" 的引用赋值给 aa String aa = "ab"; -// 直接返回字符串常量池中字符串对象 ”ab“,赋值给引用 bb +// 直接返回字符串常量池中字符串对象 "ab",赋值给引用 bb String bb = "ab"; System.out.println(aa==bb); // true ``` diff --git a/docs/java/new-features/java10.md b/docs/java/new-features/java10.md index ee5fbb18187..f2681bc6f8a 100644 --- a/docs/java/new-features/java10.md +++ b/docs/java/new-features/java10.md @@ -3,6 +3,13 @@ title: Java 10 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 10,JDK10,var 局部变量类型推断,垃圾回收改进,性能 + - - meta + - name: description + content: 概览 JDK 10 的主要更新,重点介绍 var 类型推断与其他平台改进。 --- **Java 10** 发布于 2018 年 3 月 20 日,最知名的特性应该是 `var` 关键字(局部变量类型推断)的引入了,其他还有垃圾收集器改善、GC 改进、性能提升、线程管控等一批新特性。 @@ -81,11 +88,11 @@ list.stream().collect(Collectors.toUnmodifiableSet()); ## Optional 增强 -`Optional` 新增了`orElseThrow()`方法来在没有值时抛出指定的异常。 +`Optional` 新增了一个无参的 `orElseThrow()` 方法,作为带参数的 `orElseThrow(Supplier exceptionSupplier)` 的简化版本,在没有值时默认抛出一个 NoSuchElementException 异常。 ```java -Optional.ofNullable(cache.getIfPresent(key)) - .orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key)); +Optional optional = Optional.empty(); +String result = optional.orElseThrow(); ``` ## 应用程序类数据共享(扩展 CDS 功能) diff --git a/docs/java/new-features/java11.md b/docs/java/new-features/java11.md index 0f114047434..a05d1a91b83 100644 --- a/docs/java/new-features/java11.md +++ b/docs/java/new-features/java11.md @@ -3,6 +3,13 @@ title: Java 11 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 11,JDK11,LTS,HTTP 客户端,字符串 API,移除特性 + - - meta + - name: description + content: 总结 JDK 11 的更新,关注新 HTTP 客户端与字符串增强等实用特性。 --- **Java 11** 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本!Java 11 和 2017 年 9 月份发布的 Java 9 以及 2018 年 3 月份发布的 Java 10 相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,**Oracle 表示会对 Java 11 提供大力支持,这一支持将会持续至 2026 年 9 月。这是据 Java 8 以后支持的首个长期版本。** diff --git a/docs/java/new-features/java12-13.md b/docs/java/new-features/java12-13.md index 8b3207ab4d5..f616d96c993 100644 --- a/docs/java/new-features/java12-13.md +++ b/docs/java/new-features/java12-13.md @@ -3,6 +3,13 @@ title: Java 12 & 13 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 12,Java 13,字符串增强,切换表达式,垃圾回收,JEP + - - meta + - name: description + content: 归纳 JDK 12/13 的特性更新,包含字符串增强、switch 改进与 GC 调整等。 --- ## Java12 diff --git a/docs/java/new-features/java14-15.md b/docs/java/new-features/java14-15.md index 415ca543e4b..fff1891aa15 100644 --- a/docs/java/new-features/java14-15.md +++ b/docs/java/new-features/java14-15.md @@ -3,6 +3,13 @@ title: Java 14 & 15 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 14,Java 15,record,文本块,NullPointerException 细节,模式匹配,JEP + - - meta + - name: description + content: 概览 JDK 14/15 的关键特性,如 record、文本块与空指针精准提示等语言增强。 --- ## Java14 diff --git a/docs/java/new-features/java16.md b/docs/java/new-features/java16.md index 60906c40020..3d35f133644 100644 --- a/docs/java/new-features/java16.md +++ b/docs/java/new-features/java16.md @@ -3,6 +3,13 @@ title: Java 16 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 16,JDK16,记录类改进,新 API,JEP,性能 + - - meta + - name: description + content: 介绍 JDK 16 的语言与平台更新,包含记录类与其他 JEP 改动。 --- Java 16 在 2021 年 3 月 16 日正式发布,非长期支持(LTS)版本。 diff --git a/docs/java/new-features/java17.md b/docs/java/new-features/java17.md index e478f1f5c43..95d9bb50c57 100644 --- a/docs/java/new-features/java17.md +++ b/docs/java/new-features/java17.md @@ -3,6 +3,13 @@ title: Java 17 新特性概览(重要) category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 17,JDK17,LTS,密封类,记录类,模式匹配,API 更新,JEP + - - meta + - name: description + content: 总结 JDK 17 的重要更新与 JEP,涵盖密封类、记录类与模式匹配等特性。 --- Java 17 在 2021 年 9 月 14 日正式发布,是一个长期支持(LTS)版本。 diff --git a/docs/java/new-features/java18.md b/docs/java/new-features/java18.md index 40fa7bb61df..dbfdd225e3d 100644 --- a/docs/java/new-features/java18.md +++ b/docs/java/new-features/java18.md @@ -3,6 +3,13 @@ title: Java 18 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 18,JDK18,预览特性,API 更新,JEP + - - meta + - name: description + content: 概览 JDK 18 的更新与预览特性,理解新 API 带来的改进。 --- Java 18 在 2022 年 3 月 22 日正式发布,非长期支持版本。 diff --git a/docs/java/new-features/java19.md b/docs/java/new-features/java19.md index a207bc6830a..2c4a4839efd 100644 --- a/docs/java/new-features/java19.md +++ b/docs/java/new-features/java19.md @@ -3,6 +3,13 @@ title: Java 19 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 19,JDK19,虚拟线程预览,结构化并发,外部函数 API,JEP + - - meta + - name: description + content: 介绍 JDK 19 的预览特性与并发相关更新,为后续虚拟线程铺垫。 --- JDK 19 定于 2022 年 9 月 20 日正式发布以供生产使用,非长期支持版本。不过,JDK 19 中有一些比较重要的新特性值得关注。 diff --git a/docs/java/new-features/java20.md b/docs/java/new-features/java20.md index 9dd86a71c70..4dc09646ae8 100644 --- a/docs/java/new-features/java20.md +++ b/docs/java/new-features/java20.md @@ -3,6 +3,13 @@ title: Java 20 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 20,JDK20,记录模式预览,虚拟线程改进,语言增强,JEP + - - meta + - name: description + content: 总结 JDK 20 的语言与并发改动,延续虚拟线程与模式匹配相关增强。 --- JDK 20 于 2023 年 3 月 21 日发布,非长期支持版本。 diff --git a/docs/java/new-features/java21.md b/docs/java/new-features/java21.md index 5f145c23cc5..4d58ecdd075 100644 --- a/docs/java/new-features/java21.md +++ b/docs/java/new-features/java21.md @@ -3,6 +3,13 @@ title: Java 21 新特性概览(重要) category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 21,JDK21,LTS,字符串模板,Sequenced Collections,分代 ZGC,记录模式,switch 模式匹配,虚拟线程,外部函数与内存 API + - - meta + - name: description + content: 概览 JDK 21 的关键新特性与实践影响,重点介绍字符串模板、Sequenced Collections、分代 ZGC、虚拟线程等。 --- JDK 21 于 2023 年 9 月 19 日 发布,这是一个非常重要的版本,里程碑式。 @@ -346,7 +353,7 @@ switch (b) { ## JEP 445:未命名类和实例 main 方法 (预览) -这个特性主要简化了 `main` 方法的的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 +这个特性主要简化了 `main` 方法的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 没有使用该特性之前定义一个 `main` 方法: diff --git a/docs/java/new-features/java22-23.md b/docs/java/new-features/java22-23.md index 223c2b7a72c..183595b447d 100644 --- a/docs/java/new-features/java22-23.md +++ b/docs/java/new-features/java22-23.md @@ -3,6 +3,13 @@ title: Java 22 & 23 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 22,Java 23,JEP,Markdown 文档注释,类文件 API,向量 API,结构化并发,作用域值 + - - meta + - name: description + content: 概览 JDK 22/23 的关键 JEP 与语言/平台增强,持续追踪性能与并发相关改动。 --- JDK 23 和 JDK 22 一样,这也是一个非 LTS(长期支持)版本,Oracle 仅提供六个月的支持。下一个长期支持版是 JDK 25,预计明年 9 月份发布。 @@ -25,7 +32,7 @@ JDK 23 一共有 12 个新特性: - [JEP 476:模块导入声明 (预览)](https://openjdk.org/jeps/476) - [JEP 477:未命名类和实例 main 方法 (第三次预览)](https://openjdk.org/jeps/477) - [JEP 480:结构化并发 (第三次预览)](https://openjdk.org/jeps/480) -- [JEP 481: 作用域值 (第三次预览)](https://openjdk.org/jeps/481) +- [JEP 481:作用域值 (第三次预览)](https://openjdk.org/jeps/481) - [JEP 482:灵活的构造函数体(第二次预览)](https://openjdk.org/jeps/482) JDK 22 的新特性如下: @@ -257,7 +264,7 @@ public class Example { ### JEP 477:未命名类和实例 main 方法 (第三次预览) -这个特性主要简化了 `main` 方法的的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 +这个特性主要简化了 `main` 方法的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 没有使用该特性之前定义一个 `main` 方法: diff --git a/docs/java/new-features/java24.md b/docs/java/new-features/java24.md index 4b8df7e2317..67b207062c9 100644 --- a/docs/java/new-features/java24.md +++ b/docs/java/new-features/java24.md @@ -3,6 +3,13 @@ title: Java 24 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强 + - - meta + - name: description + content: 总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。 --- [JDK 24](https://openjdk.org/projects/jdk/24/) 是自 JDK 21 以来的第三个非长期支持版本,和 [JDK 22](https://javaguide.cn/java/new-features/java22-23.html)、[JDK 23](https://javaguide.cn/java/new-features/java22-23.html)一样。下一个长期支持版是 **JDK 25**,预计今年 9 月份发布。 @@ -124,7 +131,7 @@ ScopedValue.where(V, ) 现有的使用 `synchronized` 的 Java 代码无需修改即可受益于虚拟线程的扩展能力。 例如,一个 I/O 密集型的应用程序,如果使用传统的平台线程,可能会因为线程阻塞而导致并发能力下降。 而使用虚拟线程,即使在 `synchronized` 块中发生阻塞,也不会固定平台线程,从而允许平台线程继续服务于其他虚拟线程,提高整体的并发性能。 -## JEP 493:在没有 JMOD 文件的情况下链接运行时镜像 +## JEP 493: 在没有 JMOD 文件的情况下链接运行时镜像 默认情况下,JDK 同时包含运行时镜像(运行时所需的模块)和 JMOD 文件。这个特性使得 jlink 工具无需使用 JDK 的 JMOD 文件就可以创建自定义运行时镜像,减少了 JDK 的安装体积(约 25%)。 @@ -135,7 +142,7 @@ ScopedValue.where(V, ) ## JEP 495: 简化的源文件和实例主方法(第四次预览) -这个特性主要简化了 `main` 方法的的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 +这个特性主要简化了 `main` 方法的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 没有使用该特性之前定义一个 `main` 方法: diff --git a/docs/java/new-features/java25.md b/docs/java/new-features/java25.md new file mode 100644 index 00000000000..ef0fa58564f --- /dev/null +++ b/docs/java/new-features/java25.md @@ -0,0 +1,45 @@ +--- +title: Java 25 新特性概览 +category: Java +tag: + - Java新特性 +head: + - - meta + - name: keywords + content: Java 25,JDK25,LTS,作用域值,紧凑对象头,分代 Shenandoah,模块导入,结构化并发 + - - meta + - name: description + content: 概览 JDK 25 的关键新特性与预览改动,关注并发、GC 与语言/平台增强。 +--- + +JDK 25 于 2025 年 9 月 16 日 发布,这是一个非常重要的版本,里程碑式。 + +JDK 25 是 LTS(长期支持版),至此为止,目前有 JDK8、JDK11、JDK17、JDK21 和 JDK 25 这四个长期支持版了。 + +JDK 21 共有 18 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍: + +- [JEP 506: Scoped Values (作用域值)](https://openjdk.org/projects/jdk/25/) +- [JEP 512: Compact Source Files and Instance Main Methods (紧凑源文件与实例主方法)](https://openjdk.org/jeps/512) +- [JEP 519: Compact Object Headers (紧凑对象头)](https://openjdk.org/jeps/519) +- [JEP 521: Generational Shenandoah (分代 Shenandoah GC)](https://openjdk.org/jeps/521) +- [JEP 507: Primitive Types in Patterns, instanceof, and switch (模式匹配支持基本类型, 第三次预览)](https://openjdk.org/jeps/507) +- [JEP 505: Structured Concurrency (结构化并发, 第五次预览)](https://openjdk.org/jeps/505) +- [JEP 511: Module Import Declarations (模块导入声明)](https://openjdk.org/jeps/511) +- [JEP 513: Flexible Constructor Bodies (灵活的构造函数体)](https://openjdk.org/jeps/513) +- [JEP 508: Vector API (向量 API, 第十次孵化)](https://openjdk.org/jeps/508) + +下图是从 JDK 8 到 JDK 24 每个版本的更新带来的新特性数量和更新时间: + +![](https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png) + +## JEP 506: 作用域值 + +作用域值(Scoped Values)可以在线程内和线程间共享不可变的数据,优于线程局部变量 `ThreadLocal` ,尤其是在使用大量虚拟线程时。 + +```java +final static ScopedValue<...> V = new ScopedValue<>(); + +// In some method +ScopedValue.where(V, ) + .run(() -> { ... V.get() ... call methods ... }); +``` diff --git a/docs/java/new-features/java8-common-new-features.md b/docs/java/new-features/java8-common-new-features.md index e402ba5a882..233b7d5dfdd 100644 --- a/docs/java/new-features/java8-common-new-features.md +++ b/docs/java/new-features/java8-common-new-features.md @@ -3,6 +3,13 @@ title: Java8 新特性实战 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 8,Lambda,Stream API,Optional,Date/Time API,默认方法,函数式接口 + - - meta + - name: description + content: 实战讲解 Java 8 的核心新特性,包括 Lambda、Stream、Optional、日期时间 API 与接口默认方法等。 --- > 本文来自[cowbi](https://github.com/cowbi)的投稿~ @@ -359,7 +366,7 @@ Stream limit(long maxSize); Stream sorted(Comparator comparator); /** -* 在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。 +* 丢弃此流中的前 n 个元素,返回由剩余元素组成的新流。 */ Stream skip(long n); @@ -857,7 +864,7 @@ LocalDate date = LocalDate.of(2021, 1, 26); LocalDate.parse("2021-01-26"); LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22); -LocalDateTime.parse("2021-01-26 12:12:22"); +LocalDateTime.parse("2021-01-26T12:12:22"); LocalTime time = LocalTime.of(12, 12, 22); LocalTime.parse("12:12:22"); diff --git a/docs/java/new-features/java8-tutorial-translate.md b/docs/java/new-features/java8-tutorial-translate.md index 9e0fd04ec70..9cc4552660a 100644 --- a/docs/java/new-features/java8-tutorial-translate.md +++ b/docs/java/new-features/java8-tutorial-translate.md @@ -1,3 +1,17 @@ +--- +title: 《Java8 指南》中文翻译 +category: Java +tag: + - Java新特性 +head: + - - meta + - name: keywords + content: Java 8,指南,Lambda,方法引用,默认方法,Stream API,函数式接口,Date/Time API + - - meta + - name: description + content: 翻译与整理 Java 8 教程,涵盖 Lambda、方法引用、接口默认方法、Stream 等新特性与示例代码。 +--- + # 《Java8 指南》中文翻译 随着 Java 8 的普及度越来越高,很多人都提到面试中关于 Java 8 也是非常常问的知识点。应各位要求和需要,我打算对这部分知识做一个总结。本来准备自己总结的,后面看到 GitHub 上有一个相关的仓库,地址: diff --git a/docs/java/new-features/java9.md b/docs/java/new-features/java9.md index 8fbce002f9d..456d7e44f63 100644 --- a/docs/java/new-features/java9.md +++ b/docs/java/new-features/java9.md @@ -3,6 +3,13 @@ title: Java 9 新特性概览 category: Java tag: - Java新特性 +head: + - - meta + - name: keywords + content: Java 9,JDK9,模块化,JPMS,jlink,集合工厂方法,新 API + - - meta + - name: description + content: 解析 Java 9 的模块化系统与 jlink 等更新,理解对运行时镜像与库使用的影响。 --- **Java 9** 发布于 2017 年 9 月 21 日 。作为 Java 8 之后 3 年半才发布的新版本,Java 9 带来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、`Stream` 流……。 diff --git a/docs/javaguide/contribution-guideline.md b/docs/javaguide/contribution-guideline.md index 0c9e8df0ef1..1f330582b8b 100644 --- a/docs/javaguide/contribution-guideline.md +++ b/docs/javaguide/contribution-guideline.md @@ -4,29 +4,91 @@ category: 走近项目 icon: guide --- -欢迎参与 JavaGuide 的维护工作,这是一件非常有意义的事情。详细信息请看:[JavaGuide 贡献指南](https://zhuanlan.zhihu.com/p/464832264) 。 +你好,我是 Guide!欢迎来到 JavaGuide 的“开源实验室”。 -你可以从下面几个方向来做贡献: +参与开源项目的维护,不仅是一次技术实战,更是一场**“技术反哺”的修行**。 -- 修改错别字,毕竟内容基本都是手敲,难免会有笔误。 -- 对原有内容进行修改完善,例如对某个面试问题的答案进行完善、对某篇文章的内容进行完善。 -- 新增内容,例如新增面试常问的问题、添加重要知识点的详解。 +在这里,你的每一行文字和代码,都会被全球几十万的开发者看到。 -目前的贡献奖励也比较丰富和完善,对于多次贡献的用户,有耳机、键盘等实物奖励以及现金奖励! +## 为什么要参与 JavaGuide 的维护? -一定一定一定要注意 **排版规范**: +很多小伙伴觉得开源社区门槛高,其实不然。参与 JavaGuide 维护的收益非常务实: -- [中文文案排版指北 - GitHub](https://github.com/sparanoid/chinese-copywriting-guidelines) -- [写给大家看的中文排版指南 - 知乎](https://zhuanlan.zhihu.com/p/20506092) -- [中文文案排版细则 - Dawner](https://dawner.top/posts/chinese-copywriting-rules/) +1. **深度对齐知识点**:在纠错或完善内容的过程中,你会强迫自己进行“穿透式学习”,这种记忆远比死记硬背八股文要深刻。 +2. **影响力背书**:JavaGuide 已经接近 160k Star 了。如果你的 `PR` 被采纳,你的名字将永久留在 `Contributor` 列表中。这在求职面试时,是一份非常有说服力的**“开源实战证明”**。 +3. **实物奖励**:我会不定期给高频贡献的小伙伴寄送耳机、机械键盘等硬核周边,甚至还有直接的现金激励。 + +## 可以从哪些方向进行贡献? + +你可以根据自己的精力,选择以下三个维度的贡献: + +- **纠错(初级)**:发现文档中的错别字、标点误用或代码格式混乱。这类贡献最简单,但最有温度。 +- **完善(进阶)**:对现有的面试题答案进行重构。比如某篇文章的逻辑有断层,或者缺少了最新的技术特性分析。 +- **新增(专家)**:根据大厂最新的面试动向,新增高频面试题详解或硬核知识点的深度剖析。 + +## 如何丝滑地提交贡献? + +### 极简模式:点击“编辑此页”(3 分钟上手) + +本站每个页面的**左下角**都有一个 **「编辑此页」** 按钮。 + +1. **点击跳转**:直接进入 GitHub 在线编辑界面。 +2. **在线修改**:在浏览器里直接改内容,省去 `git clone` 的麻烦。 +3. **提交申请**:填写提交信息(Commit Message),点击提交即可自动触发 `Pull Request`。 + +这种方式最适合修正笔误或小范围的内容优化。 + +![](https://oss.javaguide.cn/github/javaguide/about/javaguide-contribution-edit-page.png) + +### 进阶模式:Fork + PR(标准开源流程) + +如果你想进行大篇幅的重构或新增内容,建议走标准的 GitHub 工作流: + +1. **Fork 仓库**:点击[原仓库](https://github.com/Snailclimb/JavaGuide)右上角的 `Fork`,将 JavaGuide 复制一份副本到你的账户名下。 +2. **本地开发**:你可以将项目克隆到本地,在本地自由修改,编写内容。内容修改或者编写完成之后,直接提交到副本仓库即可。 +3. **发起 PR**:提交完成后,点击 `New Pull Request`,将你的修改请求合并到 JavaGuide 的主分支。 + +![](https://oss.javaguide.cn/github/javaguide/about/javaguide-contribution-pr.png) + +Git 相关的技能非常重要,建议在正式工作之前一定要熟练掌握。 + +我写过两篇相关的文章,推荐看看: + +- [Git 核心概念总结](https://javaguide.cn/tools/git/git-intro.html) +- [Github 实用小技巧总结](https://javaguide.cn/tools/git/github-tips.html) + +### 提交 Issue 开启讨论 + +如果你发现某些地方需要改进,但暂时没空写代码,或者想提议新增某个专题,请直接通过 **Issue** 发起讨论。 + +推荐的模板如下: + +> **标题**:建议新增 Redis 与数据库双写一致性方案的对比与选型指南 +> +> **内容描述**:缓存一致性是面试和实战中的重难点,目前文档中缺乏系统性的方案对比。建议补充: +> +> 1. **方案对比**:详细对比“先更新数据库再删缓存”、“延迟双删”、“订阅 binlog 异步删除”等方案的优缺点。 +> 2. **极端场景分析**:分析在主从延迟或网络抖动下,如何最大程度保障最终一致性。 +> +> **认领意向**:我对该领域有深入研究,并整理了一份对比表格和流程图,希望能将其贡献到 JavaGuide。 + +## 贡献要求 + +### 排版是第一生产力 + +中英文之间要加空格,标点符号要规范。请参考(任选一篇阅读即可): + +- [中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines) - [中文技术文档写作风格指南](https://github.com/yikeke/zh-style-guide/) -- [中文排版需求](https://www.w3.org/TR/clreq/) +- [中文文案排版细则 - Dawner](https://dawner.top/posts/chinese-copywriting-rules/) +- [写给大家看的中文排版指南 - 知乎](https://zhuanlan.zhihu.com/p/20506092) + +### 内容原创 + +你可以参考学习别人的文章,但**一定、一定、一定不要复制粘贴**! -如果要提 issue/question 的话,强烈推荐阅读下面这些资料: +你要做的不是“信息的搬运工”,而是“知识的过滤器”。用你自己的话讲出来,努力写得比别人更通俗易懂,突出核心重点。**这种“穿透式”的表达,才是对读者最大的负责。** -- [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way) -- [《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) -- [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html) -- [《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393)。 +## 写在最后 -另外,你可以参考学习别人的文章,但一定一定一定不能复制粘贴别人的内容,努力比别人写的更容易理解,用自己的话讲出来,适当简化表达,突出重点! +开源不是一个人的单打独斗,而是一群人的砥砺前行。 **准备 Java 面试,首选 JavaGuide!** 期待在 Contributor 列表中看到你的名字。 diff --git a/docs/open-source-project/practical-project.md b/docs/open-source-project/practical-project.md index 5c39243fa28..c450f4e18c4 100644 --- a/docs/open-source-project/practical-project.md +++ b/docs/open-source-project/practical-project.md @@ -4,6 +4,10 @@ category: 开源项目 icon: project --- +## AI + +- [interview-guide](https://github.com/Snailclimb/interview-guide):基于 Spring Boot 4.0 + Java 21 + Spring AI + PostgreSQL + pgvector + RustFS + Redis,实现简历智能分析、AI 模拟面试、知识库 RAG 检索等核心功能。非常适合作为学习和简历项目,学习门槛低。 + ## 快速开发平台 - [Snowy](https://gitee.com/xiaonuobase/snowy):国内首个国密前后端分离快速开发平台。详细介绍:[5.1k!这是我见过最强的前后端分离快速开发脚手架!!](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247534316&idx=1&sn=69938397674fc33ecda43c8c9d0a4039&chksm=cea10927f9d68031bc862485c6be984ade5af233d4d871d498c38f22164a84314678c0c67cd7&token=1464380539&lang=zh_CN#rd)。 @@ -67,13 +71,7 @@ icon: project ## 售票系统 - [12306](https://gitee.com/nageoffer/12306) :基于 JDK17 + SpringBoot3 + SpringCloud 微服务架构的高并发 12306 购票服务。 - -## 权限管理系统 - -权限管理系统在企业级的项目中一般都是非常重要的,如果你需求去实际了解一个不错的权限系统是如何设计的话,推荐你可以参考下面这些开源项目。 - -- [SpringBoot-Shiro-Vue](https://github.com/Heeexy/SpringBoot-Shiro-Vue):基于 Spring Boot-Shiro-Vue 的权限管理思路,前后端都加以控制,可以做到按钮/接口级别的权限。 -- [renren-security](https://gitee.com/renrenio/renren-security):一套灵活的权限控制系统,可控制到页面或按钮,满足绝大部分的权限需求 +- [大麦](https://gitee.com/java-up-up/damai):提供热门演唱会的购票功能,并且对如何解决高并发下的抢票而产生的各种问题,从而设计出了实际落地的解决方案。 ## 造轮子 diff --git a/docs/snippets/article-footer.snippet.md b/docs/snippets/article-footer.snippet.md index 5ec368caefb..973986b80f2 100644 --- a/docs/snippets/article-footer.snippet.md +++ b/docs/snippets/article-footer.snippet.md @@ -1 +1,9 @@ -![JavaGuide 官方公众号](https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png) +## 写在最后 + +感谢你能看到这里,也希望这篇文章对你有点用。 + +JavaGuide 坚持更新 6 年多,近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助,非常欢迎点个免费的 Star 支持下(完全自愿,觉得有收获再点就好):[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。 + +如果你想要付费支持/面试辅导(比如实战项目、简历优化、一对一提问、高频考点突击资料等)的话,欢迎了解我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。已经坚持维护六年,内容持续更新,虽白菜价(0.4元/天)但质量很高,主打一个良心! + +JavaGuide 公众号 diff --git a/docs/snippets/article-header.snippet.md b/docs/snippets/article-header.snippet.md index 1bac94f1cb5..80097335d7d 100644 --- a/docs/snippets/article-header.snippet.md +++ b/docs/snippets/article-header.snippet.md @@ -1 +1,5 @@ -[![JavaGuide官方知识星球](https://oss.javaguide.cn/xingqiu/xingqiu.png)](../about-the-author/zhishixingqiu-two-years.md) +::: tip 实战项目推荐 + +[基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 开发的 AI 智能面试辅助平台 + RAG 知识库已开源,附带系统学习教程!非常适合作为学习和简历项目,学习门槛低,帮助提升求职竞争力,是主打就业的实战项目。](https://javaguide.cn/zhuanlan/interview-guide.html) + +::: diff --git a/docs/snippets/planet.snippet.md b/docs/snippets/planet.snippet.md index b9f08320cde..d8f2ff7e6e8 100644 --- a/docs/snippets/planet.snippet.md +++ b/docs/snippets/planet.snippet.md @@ -1,24 +1,37 @@ +这本[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)(后端面试通用)的内容经过反复打磨,质量极高,旨在帮助每一位 Java/后端求职者从容应对面试挑战。 + +**用数据说话:** 截至目前,专栏累计阅读量已突破 **477.1W**,收获点赞 **5,118** 个,评论互动 **1,657** 条。值得一提的是,评论区不仅仅是留言板,更是答疑区——几乎每一条提问,我都会用心回复,确保无疑问遗留。 + +![](https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics-2025.png) + [《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn/#/) 的补充完善,两者可以配合使用。 -![《Java 面试指北》内容概览](https://oss.javaguide.cn/xingqiu/image-20220304102536445.png) +![《Java 面试指北》内容概览](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png) -为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的[Java 面试知识星球](../about-the-author/zhishixingqiu-two-years.md)。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面,非常适合准备 Java 面试和学习 Java 的同学。 +下面是[《Java 面试指北》](../zhuanlan/java-mian-shi-zhi-bei.md)收到的部分球友的真实反馈: -**欢迎准备 Java 面试以及学习 Java 的同学加入我的 [知识星球](../about-the-author/zhishixingqiu-two-years.md),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。** +![《Java 面试指北》 收到的部分球友的真实反馈](https://oss.javaguide.cn/xingqiu/praise-that-the-mianshizhibei-received.png) + +如果需要面试辅导(比如简历优化、一对一模拟问答、高频考点突击资料等),欢迎了解我的[知识星球](../about-the-author/zhishixingqiu-two-years.md)。已经坚持维护六年,内容持续更新,虽然是白菜价(0.4 元/天),但质量很高、服务也很全面,主打一个良心! 下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍): [![星球服务](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](../about-the-author/zhishixingqiu-two-years.md) -**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!** +下面是今年收到了部分好评,每一条都是真实存在的。我看到很多培训班或者机构通过虚构一些不存在的好评来欺骗他人购买高价服务(行业内非常常见),真的很难理解。 + +![球友对星球的真实评价](https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png) -如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍:[JavaGuide 知识星球详细介绍](../about-the-author/zhishixingqiu-two-years.md) 。 +**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!** 如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍:[JavaGuide 知识星球详细介绍](../about-the-author/zhishixingqiu-two-years.md) 。 这里再送一张 **30** 元的星球专属优惠券,数量有限(价格即将上调。老用户续费半价 ,微信扫码即可续费)! ![知识星球30元优惠卷](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) -进入星球之后,记得查看 **[星球使用指南](https://t.zsxq.com/0d18KSarv)** (一定要看!!!) 和 **[星球优质主题汇总](https://t.zsxq.com/12uSKgTIm)** ,干货多多! +🚀 **入圈必做**(干货满满,一定要看!): + +1. [星球使用指南](https://t.zsxq.com/0d18KSarv) +2. [优质主题汇总](https://t.zsxq.com/12uSKgTIm) **无任何套路,无任何潜在收费项。用心做内容,不割韭菜!** diff --git a/docs/snippets/planet2.snippet.md b/docs/snippets/planet2.snippet.md index 891d58c8923..aeeef4aee8c 100644 --- a/docs/snippets/planet2.snippet.md +++ b/docs/snippets/planet2.snippet.md @@ -10,9 +10,11 @@ [![星球服务](https://oss.javaguide.cn/xingqiu/xingqiufuwu.png)](../about-the-author/zhishixingqiu-two-years.md) -**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!** +下面是今年收到了部分好评,每一条都是真实存在的。我看到很多培训班或者机构通过虚构一些不存在的好评来欺骗他人购买高价服务(行业内非常常见),真的很难理解。 -如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍:[JavaGuide 知识星球详细介绍](../about-the-author/zhishixingqiu-two-years.md)。 +![球友对星球的真实评价](https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png) + +**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!** 如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍:[JavaGuide 知识星球详细介绍](../about-the-author/zhishixingqiu-two-years.md) 。 ## 星球限时优惠 @@ -20,7 +22,10 @@ ![知识星球30元优惠卷](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) -进入星球之后,记得查看 **[星球使用指南](https://t.zsxq.com/0d18KSarv)** (一定要看!!!) 和 **[星球优质主题汇总](https://www.yuque.com/snailclimb/rpkqw1/ncxpnfmlng08wlf1)** 。 +🚀 **入圈必做**(干货满满,一定要看!): + +1. [星球使用指南](https://t.zsxq.com/0d18KSarv) +2. [优质主题汇总](https://t.zsxq.com/12uSKgTIm) **无任何套路,无任何潜在收费项。用心做内容,不割韭菜!** diff --git a/docs/snippets/small-advertisement.snippet.md b/docs/snippets/small-advertisement.snippet.md index 03b14a8738c..70231c10fc5 100644 --- a/docs/snippets/small-advertisement.snippet.md +++ b/docs/snippets/small-advertisement.snippet.md @@ -1,6 +1,5 @@ ::: tip 这是一则或许对你有用的小广告 -- **面试专版**:准备 Java 面试的小伙伴可以考虑面试专版:**[《Java 面试指北 》](../zhuanlan/java-mian-shi-zhi-bei.md)** (质量非常高,专为面试打造,配合 JavaGuide 食用效果最佳)。 -- **知识星球**:技术专栏/一对一提问/简历修改/求职指南/面试打卡/不定时福利,欢迎加入 **[JavaGuide 官方知识星球](../about-the-author/zhishixingqiu-two-years.md)**。 +如果你想要付费支持/面试辅导(比如实战项目、简历优化、一对一提问、高频考点突击资料等)的话,欢迎了解我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。已经坚持维护六年,内容持续更新,虽白菜价(0.4元/天)但质量很高,主打一个良心! ::: diff --git a/docs/system-design/basis/RESTfulAPI.md b/docs/system-design/basis/RESTfulAPI.md index 15671201961..4554fefea14 100644 --- a/docs/system-design/basis/RESTfulAPI.md +++ b/docs/system-design/basis/RESTfulAPI.md @@ -3,21 +3,17 @@ title: RestFul API 简明教程 category: 代码质量 --- -![](https://oss.javaguide.cn/system-design/basis/2021050713553862.png) - 这篇文章简单聊聊后端程序员必备的 RESTful API 相关的知识。 开始正式介绍 RESTful API 之前,我们需要首先搞清:**API 到底是什么?** ## 何为 API? -![](https://oss.javaguide.cn/system-design/basis/20210507153833945.png) - **API(Application Programming Interface)** 翻译过来是应用程序编程接口的意思。 我们在进行后端开发的时候,主要的工作就是为前端或者其他后端服务提供 API 比如查询用户数据的 API 。 -![](https://oss.javaguide.cn/system-design/basis/20210507130629538.png) +![](https://oss.javaguide.cn/github/javaguide/system-design/basis/20210507130629538.png) 但是, API 不仅仅代表后端系统暴露的接口,像框架中提供的方法也属于 API 的范畴。 @@ -66,7 +62,7 @@ POST /classes:新建一个班级 ## RESTful API 规范 -![](https://oss.javaguide.cn/system-design/basis/20210507154007779.png) +![](https://oss.javaguide.cn/github/javaguide/system-design/basis/20210507154007779.png) ### 动作 diff --git a/docs/system-design/basis/software-engineering.md b/docs/system-design/basis/software-engineering.md index c6cd4fa3188..598243efa7a 100644 --- a/docs/system-design/basis/software-engineering.md +++ b/docs/system-design/basis/software-engineering.md @@ -15,7 +15,7 @@ category: 系统设计 简单来说,软件危机描述了当时软件开发的一个痛点:我们很难高效地开发出质量高的软件。 -Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也提高过软件危机,他是这样说的:“导致软件危机的主要原因是机器变得功能强大了几个数量级!坦率地说:只要没有机器,编程就完全没有问题。当我们有一些弱小的计算机时,编程成为一个温和的问题,而现在我们有了庞大的计算机,编程也同样成为一个巨大的问题”。 +Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也提到过软件危机,他是这样说的:“导致软件危机的主要原因是机器变得功能强大了几个数量级!坦率地说:只要没有机器,编程就完全没有问题。当我们有一些弱小的计算机时,编程成为一个温和的问题,而现在我们有了庞大的计算机,编程也同样成为一个巨大的问题”。 **说了这么多,到底什么是软件工程呢?** @@ -38,7 +38,7 @@ Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也 - 交付:将做好的软件交付给客户。 - 维护:对软件进行维护比如解决 bug,完善功能。 -软件开发过程只是比较笼统的层面上,一定义了一个软件开发可能涉及到的一些流程。 +软件开发过程只是比较笼统的层面上,定义了一个软件开发可能涉及到的一些流程。 软件开发模型更具体地定义了软件开发过程,对开发过程提供了强有力的理论支持。 @@ -46,7 +46,7 @@ Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也 软件开发模型有很多种,比如瀑布模型(Waterfall Model)、快速原型模型(Rapid Prototype Model)、V 模型(V-model)、W 模型(W-model)、敏捷开发模型。其中最具有代表性的还是 **瀑布模型** 和 **敏捷开发** 。 -**瀑布模型** 定义了一套完成的软件开发周期,完整地展示了一个软件的的生命周期。 +**瀑布模型** 定义了一套完整的软件开发周期,完整地展示了一个软件的生命周期。 ![](https://oss.javaguide.cn/github/javaguide/system-design/schedule-task/up-264f2750a3d30366e36c375ec3a30ec2775.png) diff --git a/docs/system-design/framework/mybatis/mybatis-interview.md b/docs/system-design/framework/mybatis/mybatis-interview.md index 988111d777a..33e36dc6fdf 100644 --- a/docs/system-design/framework/mybatis/mybatis-interview.md +++ b/docs/system-design/framework/mybatis/mybatis-interview.md @@ -82,7 +82,7 @@ public interface StuMapper { 能正常运行,并能得到相应的结果,这样就实现了在 Dao 接口中写重载方法。 -**Mybatis 的 Dao 接口可以有多个重载方法,但是多个接口对应的映射必须只有一个,否则启动会报错。** +**Mybatis 的 Dao 接口可以有多个重载方法,但是多个方法对应的映射必须只有一个,否则启动会报错。** 相关 issue:[更正:Dao 接口里的方法可以重载,但是 Mybatis 的 xml 里面的 ID 不允许重复!](https://github.com/Snailclimb/JavaGuide/issues/1122)。 diff --git a/docs/system-design/framework/netty.md b/docs/system-design/framework/netty.md index 1a0833f86e1..a9ff56c906c 100644 --- a/docs/system-design/framework/netty.md +++ b/docs/system-design/framework/netty.md @@ -6,6 +6,6 @@ icon: "network" **Netty** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)中。 - +![](https://oss.javaguide.cn/javamianshizhibei/netty-questisons.png) - + diff --git a/docs/system-design/framework/spring/ioc-and-aop.md b/docs/system-design/framework/spring/ioc-and-aop.md index e58f40f81af..a7ee8ea38e7 100644 --- a/docs/system-design/framework/spring/ioc-and-aop.md +++ b/docs/system-design/framework/spring/ioc-and-aop.md @@ -114,7 +114,7 @@ AOP 可以将横切关注点(如日志记录、事务管理、权限控制、 ![](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/crosscut-logic-and-businesslogic-separation%20%20%20%20%20%20.png) -以日志记录为例进行介绍,假如我们需要对某些方法进行统一格式的日志记录,没有使用 AOP 技术之前,我们需要挨个写日志记录的逻辑代码,全是重复的的逻辑。 +以日志记录为例进行介绍,假如我们需要对某些方法进行统一格式的日志记录,没有使用 AOP 技术之前,我们需要挨个写日志记录的逻辑代码,全是重复的逻辑。 ```java public CommonResponse method1() { diff --git a/docs/system-design/framework/spring/spring-common-annotations.md b/docs/system-design/framework/spring/spring-common-annotations.md index e51fb4b9603..c6d16fa7821 100644 --- a/docs/system-design/framework/spring/spring-common-annotations.md +++ b/docs/system-design/framework/spring/spring-common-annotations.md @@ -486,7 +486,7 @@ Bean Validation 规范及其实现(如 Hibernate Validator)提供了丰富 - `@Email`: 检查被注解的 `CharSequence`(如 `String`)是否符合 Email 格式(内置了一个相对宽松的正则表达式)。 - `@Past` / `@Future`: 检查被注解的日期或时间类型(`java.util.Date`、`java.util.Calendar`、JSR 310 `java.time` 包下的类型)是否在当前时间之前 / 之后。 - `@PastOrPresent` / `@FutureOrPresent`: 类似 `@Past` / `@Future`,但允许等于当前时间。 -- ...... +- …… ### 验证请求体(RequestBody) @@ -885,7 +885,7 @@ public class User { } ``` -`@JsonIgnore`作用于字段或` getter/setter` 方法级别,用于指定在序列化或反序列化时忽略该特定属性。 +`@JsonIgnore`作用于字段或`getter/setter` 方法级别,用于指定在序列化或反序列化时忽略该特定属性。 ```java public class User { diff --git a/docs/system-design/framework/spring/spring-design-patterns-summary.md b/docs/system-design/framework/spring/spring-design-patterns-summary.md index e4499b00f2e..a384db519bd 100644 --- a/docs/system-design/framework/spring/spring-design-patterns-summary.md +++ b/docs/system-design/framework/spring/spring-design-patterns-summary.md @@ -130,7 +130,7 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { **AOP(Aspect-Oriented Programming,面向切面编程)** 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 -**Spring AOP 就是基于动态代理的**,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy** 去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: +Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: ![SpringAOPProcess](https://oss.javaguide.cn/github/javaguide/SpringAOPProcess.jpg) diff --git a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md index eab9117ad90..c5e8bcb5244 100644 --- a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md +++ b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md @@ -88,7 +88,7 @@ Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮 Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。 -### Spring,Spring MVC,Spring Boot 之间什么关系? +### ⭐️Spring,Spring MVC,Spring Boot 之间什么关系? 很多人对 Spring,Spring MVC,Spring Boot 这三者傻傻分不清楚!这里简单介绍一下这三者,其实很简单,没有什么高深的东西。 @@ -110,29 +110,44 @@ Spring Boot 只是简化了配置,如果你需要构建 MVC 架构的 Web 程 ## Spring IoC -### 谈谈自己对于 Spring IoC 的了解 +### ⭐️什么是 IoC? -**IoC(Inversion of Control:控制反转)** 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。 +IoC (Inversion of Control )即控制反转/反转控制。它是一种思想不是一个技术实现。描述的是:Java 开发领域对象的创建以及管理的问题。 -**为什么叫控制反转?** +例如:现有类 A 依赖于类 B -- **控制**:指的是对象创建(实例化、管理)的权力 -- **反转**:控制权交给外部环境(Spring 框架、IoC 容器) +- **传统的开发方式** :往往是在类 A 中手动通过 new 关键字来 new 一个 B 的对象出来 +- **使用 IoC 思想的开发方式** :不通过 new 关键字来创建对象,而是通过 IoC 容器(Spring 框架) 来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面去取即可。 -![IoC 图解](https://oss.javaguide.cn/java-guide-blog/frc-365faceb5697f04f31399937c059c162.png) +从以上两种开发方式的对比来看:我们 “丧失了一个权力” (创建、管理对象的权力),从而也得到了一个好处(不用再考虑对象的创建、管理等一系列的事情) -将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 +**为什么叫控制反转?** -在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。 +- **控制** :指的是对象创建(实例化、管理)的权力 +- **反转** :控制权交给外部环境(IoC 容器) -在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。 +![IoC 图解](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&Aop-ioc-illustration.png) -Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。 +### ⭐️IoC 解决了什么问题? -相关阅读: +IoC 的思想就是两方之间不互相依赖,由第三方容器来管理相关资源。这样有什么好处呢? -- [IoC 源码阅读](https://javadoop.com/post/spring-ioc) -- [IoC & AOP 详解(快速搞懂)](./ioc-and-aop.md) +1. 对象之间的耦合度或者说依赖程度降低; +2. 资源变的容易管理;比如你用 Spring 容器提供的话很容易就可以实现一个单例。 + +例如:现有一个针对 User 的操作,利用 Service 和 Dao 两层结构进行开发 + +在没有使用 IoC 思想的情况下,Service 层想要使用 Dao 层的具体实现的话,需要通过 new 关键字在`UserServiceImpl` 中手动 new 出 `IUserDao` 的具体实现类 `UserDaoImpl`(不能直接 new 接口类)。 + +很完美,这种方式也是可以实现的,但是我们想象一下如下场景: + +开发过程中突然接到一个新的需求,针对`IUserDao` 接口开发出另一个具体实现类。因为 Server 层依赖了`IUserDao`的具体实现,所以我们需要修改`UserServiceImpl`中 new 的对象。如果只有一个类引用了`IUserDao`的具体实现,可能觉得还好,修改起来也不是很费力气,但是如果有许许多多的地方都引用了`IUserDao`的具体实现的话,一旦需要更换`IUserDao` 的实现方式,那修改起来将会非常的头疼。 + +![IoC&Aop-ioc-illustration-dao-service](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&Aop-ioc-illustration-dao-service.png) + +使用 IoC 的思想,我们将对象的控制权(创建、管理)交由 IoC 容器去管理,我们在使用的时候直接向 IoC 容器 “要” 就可以了 + +![](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&Aop-ioc-illustration-dao.png) ### 什么是 Spring Bean? @@ -215,39 +230,50 @@ Spring 内置的 `@Autowired` 以及 JDK 内置的 `@Resource` 和 `@Inject` 都 `@Autowired` 和`@Resource`使用的比较多一些。 -### @Autowired 和 @Resource 的区别是什么? +### ⭐️@Autowired 和 @Resource 的区别是什么? -`Autowired` 属于 Spring 内置的注解,默认的注入方式为`byType`(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。 +`@Autowired` 是 Spring 内置的注解,默认注入逻辑为**先按类型(byType)匹配,若存在多个同类型 Bean,则再尝试按名称(byName)筛选**。 -**这会有什么问题呢?** 当一个接口存在多个实现类的话,`byType`这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。 +具体来说: -这种情况下,注入方式会变为 `byName`(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 `smsService` 就是我这里所说的名称,这样应该比较好理解了吧。 +1. 优先根据接口 / 类的类型在 Spring 容器中查找匹配的 Bean。若只找到一个符合类型的 Bean,直接注入,无需考虑名称; +2. 若找到多个同类型的 Bean(例如一个接口有多个实现类),则会尝试通过**属性名或参数名**与 Bean 的名称进行匹配(默认 Bean 名称为类名首字母小写,除非通过 `@Bean(name = "...")` 或 `@Component("...")` 显式指定)。 -```java -// smsService 就是我们上面所说的名称 -@Autowired -private SmsService smsService; -``` +当一个接口存在多个实现类时: + +- 若属性名与某个 Bean 的名称一致,则注入该 Bean; +- 若属性名与所有 Bean 名称都不匹配,会抛出 `NoUniqueBeanDefinitionException`,此时需要通过 `@Qualifier` 显式指定要注入的 Bean 名称。 -举个例子,`SmsService` 接口有两个实现类: `SmsServiceImpl1`和 `SmsServiceImpl2`,且它们都已经被 Spring 容器所管理。 +举例说明: ```java -// 报错,byName 和 byType 都无法匹配到 bean +// SmsService 接口有两个实现类:SmsServiceImpl1、SmsServiceImpl2(均被 Spring 管理) + +// 报错:byType 匹配到多个 Bean,且属性名 "smsService" 与两个实现类的默认名称(smsServiceImpl1、smsServiceImpl2)都不匹配 @Autowired private SmsService smsService; -// 正确注入 SmsServiceImpl1 对象对应的 bean + +// 正确:属性名 "smsServiceImpl1" 与实现类 SmsServiceImpl1 的默认名称匹配 @Autowired private SmsService smsServiceImpl1; -// 正确注入 SmsServiceImpl1 对象对应的 bean -// smsServiceImpl1 就是我们上面所说的名称 + +// 正确:通过 @Qualifier 显式指定 Bean 名称 "smsServiceImpl1" @Autowired @Qualifier(value = "smsServiceImpl1") private SmsService smsService; ``` -我们还是建议通过 `@Qualifier` 注解来显式指定名称而不是依赖变量的名称。 +实际开发实践中,我们还是建议通过 `@Qualifier` 注解来显式指定名称而不是依赖变量的名称。 + +`@Resource` 源自 **JSR-250** 规范(标准 Java 规范),在 JDK 6 到 JDK 10 中,它确实存在于 JDK 提供的包中。不过,从 JDK 11 开始,它不再默认存在于 JDK 内部,你需要引入额外的依赖 `javax.annotation-api`才能使用。 -`@Resource`属于 JDK 提供的注解,默认注入方式为 `byName`。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为`byType`。 +Spring 对 `@Resource`(无参数情况)的处理逻辑如下: + +1. **按名称(byName)匹配:**默认取字段名(Field Name)作为 bean 的名称去容器中查找。如果找到了该名称的 Bean,则直接注入。 +2. **回退到按类型(byType)匹配:**如果**没有**找到同名的 Bean,Spring 会退而求其次,尝试根据字段的**类型**去查找。**按类型匹配的结果判定** + - **找到 1 个 Bean**:注入成功。 + - **找到 0 个 Bean**:抛出异常 (`NoSuchBeanDefinitionException`)。 + - **找到 >1 个 Bean**:抛出异常 (`NoUniqueBeanDefinitionException`)。 `@Resource` 有两个比较重要且日常开发常用的属性:`name`(名称)、`type`(类型)。 @@ -272,13 +298,15 @@ private SmsService smsServiceImpl1; private SmsService smsService; ``` -简单总结一下: +**简单总结一下**: - `@Autowired` 是 Spring 提供的注解,`@Resource` 是 JDK 提供的注解。 - `Autowired` 默认的注入方式为`byType`(根据类型进行匹配),`@Resource`默认注入方式为 `byName`(根据名称进行匹配)。 - 当一个接口存在多个实现类的情况下,`@Autowired` 和`@Resource`都需要通过名称才能正确匹配到对应的 Bean。`Autowired` 可以通过 `@Qualifier` 注解来显式指定名称,`@Resource`可以通过 `name` 属性来显式指定名称。 - `@Autowired` 支持在构造函数、方法、字段和参数上使用。`@Resource` 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。 +考虑到 `@Resource` 的语义更清晰(名称优先),并且是 Java 标准,能减少对 Spring 框架的强耦合,我们通常**更推荐使用 `@Resource`**,尤其是在需要按名称注入的场景下。而 `@Autowired` 配合构造器注入,在实现依赖注入的不可变性和强制性方面有优势,也是一种非常好的实践。 + ### 注入 Bean 的方式有哪些? 依赖注入 (Dependency Injection, DI) 的常见方式: @@ -334,7 +362,7 @@ public class UserService { } ``` -### 构造函数注入还是 Setter 注入? +### ⭐️构造函数注入还是 Setter 注入? Spring 官方有对这个问题的回答:。 @@ -351,7 +379,7 @@ Spring 官方有对这个问题的回答: diff --git a/docs/system-design/schedule-task.md b/docs/system-design/schedule-task.md index 99b3d574056..cdedcc56942 100644 --- a/docs/system-design/schedule-task.md +++ b/docs/system-design/schedule-task.md @@ -293,7 +293,7 @@ public class TestJob implements SimpleJob { > Quartz 作为开源作业调度中的佼佼者,是作业调度的首选。但是集群环境中 Quartz 采用 API 的方式对任务进行管理,从而可以避免上述问题,但是同样存在以下问题: > -> - 问题一:调用 API 的的方式操作任务,不人性化; +> - 问题一:调用 API 的方式操作任务,不人性化; > - 问题二:需要持久化业务 QuartzJobBean 到底层数据表中,系统侵入性相当严重。 > - 问题三:调度逻辑和 QuartzJobBean 耦合在同一个项目中,这将导致一个问题,在调度任务数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务; > - 问题四:quartz 底层以“抢占式”获取 DB 锁并由抢占成功节点负责运行任务,会导致节点负载悬殊非常大;而 XXL-JOB 通过执行器实现“协同分配式”运行任务,充分发挥集群优势,负载各节点均衡。 diff --git a/docs/system-design/security/advantages-and-disadvantages-of-jwt.md b/docs/system-design/security/advantages-and-disadvantages-of-jwt.md index bbb6d19778f..5a14f1c53bb 100644 --- a/docs/system-design/security/advantages-and-disadvantages-of-jwt.md +++ b/docs/system-design/security/advantages-and-disadvantages-of-jwt.md @@ -147,7 +147,7 @@ JWT 认证的话,我们应该如何解决续签问题呢?查阅了很多资 **2、每次请求都返回新 JWT(不推荐)** -这种方案的的思路很简单,但是,开销会比较大,尤其是在服务端要存储维护 JWT 的情况下。 +这种方案的思路很简单,但是,开销会比较大,尤其是在服务端要存储维护 JWT 的情况下。 **3、JWT 有效期设置到半夜(不推荐)** diff --git a/docs/system-design/security/data-validation.md b/docs/system-design/security/data-validation.md index fdeb6fb95cc..2e5f0c96cdb 100644 --- a/docs/system-design/security/data-validation.md +++ b/docs/system-design/security/data-validation.md @@ -79,7 +79,7 @@ Bean Validation 规范及其实现(如 Hibernate Validator)提供了丰富 - `@Email`: 检查被注解的 `CharSequence`(如 `String`)是否符合 Email 格式(内置了一个相对宽松的正则表达式)。 - `@Past` / `@Future`: 检查被注解的日期或时间类型(`java.util.Date`、`java.util.Calendar`、JSR 310 `java.time` 包下的类型)是否在当前时间之前 / 之后。 - `@PastOrPresent` / `@FutureOrPresent`: 类似 `@Past` / `@Future`,但允许等于当前时间。 -- ...... +- …… 当 Controller 方法使用 `@RequestBody` 注解来接收请求体并将其绑定到一个对象时,可以在该参数前添加 `@Valid` 注解来触发对该对象的校验。如果验证失败,它将抛出`MethodArgumentNotValidException`。 @@ -161,7 +161,7 @@ Bean Validation 主要解决的是**数据格式、语法层面**的校验。但 - 游客能访问管理员后台接口吗?(不行) - 游客能管理其他用户的信息吗?(不行) - VIP 用户能使用专属的优惠券吗?(可以) -- ...... +- …… 权限校验发生在**数据校验之后**,它关心的是“**谁 (Who)** 能对 **什么资源 (What)** 执行 **什么操作 (Action)**”。 diff --git a/docs/system-design/security/encryption-algorithms.md b/docs/system-design/security/encryption-algorithms.md index cb75b10a415..fb9ffe4a03c 100644 --- a/docs/system-design/security/encryption-algorithms.md +++ b/docs/system-design/security/encryption-algorithms.md @@ -3,6 +3,7 @@ title: 常见加密算法总结 category: 系统设计 tag: - 安全 + - 哈希算法 --- 加密算法是一种用数学方法对数据进行变换的技术,目的是保护数据的安全,防止被未经授权的人读取或修改。加密算法可以分为三大类:对称加密算法、非对称加密算法和哈希算法(也叫摘要算法)。 diff --git a/docs/system-design/security/jwt-intro.md b/docs/system-design/security/jwt-intro.md index 6b8213e9e91..fd9f3f5a474 100644 --- a/docs/system-design/security/jwt-intro.md +++ b/docs/system-design/security/jwt-intro.md @@ -127,7 +127,7 @@ HMACSHA256( ## 如何基于 JWT 进行身份验证? -在基于 JWT 进行身份验证的的应用程序中,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。 +在基于 JWT 进行身份验证的应用程序中,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。 ![ JWT 身份验证示意图](https://oss.javaguide.cn/github/javaguide/system-design/jwt/jwt-authentication%20process.png) diff --git a/docs/system-design/system-design-questions.md b/docs/system-design/system-design-questions.md index e34d5cc479c..bf1b8c93000 100644 --- a/docs/system-design/system-design-questions.md +++ b/docs/system-design/system-design-questions.md @@ -4,10 +4,18 @@ category: Java面试指北 icon: "design" --- -**系统设计** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)中。 +**系统设计** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《后端面试高频系统设计&场景题》](https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html)中。 -![](https://oss.javaguide.cn/javamianshizhibei/system-design-questions.png) +**《后端面试高频系统设计&场景题》** 包含了常见的系统设计案例比如短链系统、秒杀系统以及高频的场景题比如海量数据去重、第三方授权登录。 - +![《后端面试高频系统设计&场景题》](https://oss.javaguide.cn/xingqiu/back-end-interview-high-frequency-system-design-and-scenario-questions-fengmian.png) + +近年来,随着国内的技术面试越来越卷,越来越多的公司开始在面试中考察系统设计和场景问题,以此来更全面的考察求职者,不论是校招还是社招。不过,正常面试全是场景题的情况还是极少的,面试官一般会在面试中穿插一两个系统设计和场景题来考察你。 + +于是,我总结了这份《后端面试高频系统设计&场景题》,包含了常见的系统设计案例比如短链系统、秒杀系统以及高频的场景题比如海量数据去重、第三方授权登录。 - +即使不是准备面试,我也强烈推荐你认真阅读这一系列文章,这对于提升自己系统设计思维和解决实际问题的能力还是非常有帮助的。并且,涉及到的很多案例都可以用到自己的项目上比如抽奖系统设计、第三方授权登录、Redis 实现延时任务的正确方式。 + +《后端面试高频系统设计&场景题》本身是属于[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)的一部分,后面由于内容篇幅较多,因此被单独提了出来。 + + diff --git a/docs/tools/docker/docker-in-action.md b/docs/tools/docker/docker-in-action.md index 3c7198cf96b..f192a0b9a43 100644 --- a/docs/tools/docker/docker-in-action.md +++ b/docs/tools/docker/docker-in-action.md @@ -3,6 +3,13 @@ title: Docker实战 category: 开发工具 tag: - Docker +head: + - - meta + - name: keywords + content: Docker 实战,镜像构建,容器管理,环境一致性,部署,性能 + - - meta + - name: description + content: 通过实战理解 Docker 的镜像与容器管理,解决环境一致性与交付效率问题,提升开发测试部署的协同效率。 --- ## Docker 介绍 diff --git a/docs/tools/docker/docker-intro.md b/docs/tools/docker/docker-intro.md index 5db4f557784..b0cf2ea1f94 100644 --- a/docs/tools/docker/docker-intro.md +++ b/docs/tools/docker/docker-intro.md @@ -3,6 +3,13 @@ title: Docker核心概念总结 category: 开发工具 tag: - Docker +head: + - - meta + - name: keywords + content: Docker,容器,镜像,仓库,引擎,隔离,虚拟机对比,部署 + - - meta + - name: description + content: 梳理 Docker 的核心概念与容器/虚拟机差异,掌握镜像、容器与仓库的关系及在交付部署中的实际价值。 --- 本文只是对 Docker 的概念做了较为详细的介绍,并不涉及一些像 Docker 环境的安装以及 Docker 的一些常见操作和命令。 diff --git a/docs/tools/git/git-intro.md b/docs/tools/git/git-intro.md index c2cf8000570..d6af521a228 100644 --- a/docs/tools/git/git-intro.md +++ b/docs/tools/git/git-intro.md @@ -3,6 +3,13 @@ title: Git核心概念总结 category: 开发工具 tag: - Git +head: + - - meta + - name: keywords + content: Git,版本控制,分布式,分支,提交,合并,冲突解决,工作流 + - - meta + - name: description + content: 总结 Git 的核心概念与工作流,涵盖分支与合并、提交管理与冲突解决,助力团队协作与代码质量提升。 --- ## 版本控制 diff --git a/docs/tools/git/github-tips.md b/docs/tools/git/github-tips.md index 25df8592c71..11a84d1e6ec 100644 --- a/docs/tools/git/github-tips.md +++ b/docs/tools/git/github-tips.md @@ -3,6 +3,13 @@ title: Github实用小技巧总结 category: 开发工具 tag: - Git +head: + - - meta + - name: keywords + content: Github 技巧,个人主页,README,统计信息,开源贡献,简历 + - - meta + - name: description + content: 汇总 Github 的高效使用技巧,包括个性化主页、自动简历与统计展示,提升个人品牌与开源协作体验。 --- 我使用 Github 已经有 6 年多了,今天毫无保留地把自己觉得比较有用的 Github 小技巧送给关注 JavaGuide 的各位小伙伴。 diff --git a/docs/zhuanlan/README.md b/docs/zhuanlan/README.md index 9175b71ff3c..09e6401790e 100644 --- a/docs/zhuanlan/README.md +++ b/docs/zhuanlan/README.md @@ -5,10 +5,11 @@ category: 知识星球 这部分的内容为我的[知识星球](../about-the-author/zhishixingqiu-two-years.md)专属,目前已经更新了下面这些专栏: -- **[《Java 面试指北》](./java-mian-shi-zhi-bei.md)** : 与 JavaGuide 开源版的内容互补! -- **[《后端面试高频系统设计&场景题》](./back-end-interview-high-frequency-system-design-and-scenario-questions.md)** : 包含了常见的系统设计案例比如短链系统、秒杀系统以及高频的场景题比如海量数据去重、第三方授权登录。 -- **[《手写 RPC 框架》](./java-mian-shi-zhi-bei.md)** : 从零开始基于 Netty+Kyro+Zookeeper 实现一个简易的 RPC 框架。 -- **[《Java 必读源码系列》](./source-code-reading.md)**:目前已经整理了 Dubbo 2.6.x、Netty 4.x、SpringBoot 2.1 等框架/中间件的源码 +- [《Java 面试指北》](./java-mian-shi-zhi-bei.md) : 与 JavaGuide 开源版的内容互补! +- [⭐AI 智能面试辅助平台 + RAG 知识库](./interview-guide.md):基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 开发。非常适合作为学习和简历项目,学习门槛低,帮助提升求职竞争力,是主打就业的实战项目。 +- [《后端面试高频系统设计&场景题》](./back-end-interview-high-frequency-system-design-and-scenario-questions.md) : 包含了常见的系统设计案例比如短链系统、秒杀系统以及高频的场景题比如海量数据去重、第三方授权登录。 +- [《手写 RPC 框架》](./java-mian-shi-zhi-bei.md) : 从零开始基于 Netty+Kyro+Zookeeper 实现一个简易的 RPC 框架。 +- [《Java 必读源码系列》](./source-code-reading.md):目前已经整理了 Dubbo 2.6.x、Netty 4.x、SpringBoot 2.1 等框架/中间件的源码 - …… 欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](../about-the-author/zhishixingqiu-two-years.md),干货非常多!收费虽然是白菜价,但星球里的内容比你参加几万的培训班质量还要高。 diff --git a/docs/zhuanlan/interview-guide.md b/docs/zhuanlan/interview-guide.md new file mode 100644 index 00000000000..1d2e70aadef --- /dev/null +++ b/docs/zhuanlan/interview-guide.md @@ -0,0 +1,548 @@ +--- +title: 《SpringAI 智能面试平台+RAG 知识库》 +category: 知识星球 +star: 5 +--- + +## 项目介绍 + +这是一个基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 的 AI 智能面试辅助平台 + RAG 知识库。系统提供三大核心功能: + +1. **智能简历分析**:上传简历后,AI 自动进行多维度评分并给出改进建议 +2. **模拟面试系统**:基于简历内容生成个性化面试题,支持实时问答和答案评估 +3. **RAG 知识库问答**:上传技术文档构建私有知识库,支持向量检索增强的智能问答 + +**项目地址**: + +- Github: +- Gitee: + +完整代码完全免费开源,没有 Pro 版本或者付费版! + +## 配套教程内容安排 + +本项目专为求职者和开发者设计,旨在解决当前 AI 应用开发中的核心痛点。通过“保姆级”的保姆级教程,我们将从零构建一个融合了 **大模型集成、RAG(检索增强生成)、高性能对象存储与向量数据库**的完整系统。 + +无论你是想学习 **Spring AI** 的前沿应用,还是需要一个**高含金量的简历项目**,本项目都将为你提供从基建搭建、业务攻坚到面试话术复盘的全方位指导。 + +**内容安排如下(正在持续更新中)**: + +### 环境搭建 + +1. 本地搭建 PostgreSQL + PGvector 向量数据库 +2. Spring Boot + RustFS 构建高性能 S3 兼容的对象存储服务 +3. 大模型 API 申请和 Ollama 部署本地模型 + +### 核心功能开发 + +1. 简历上传、多格式内容提取与解析 +2. Spring AI 与大模型集成 +3. Prompt 工程:从模糊指令到结构化设计 +4. AI 模拟面试功能 +5. PDF 报告导出功能 +6. 知识库 RAG 问答 +7. 基于 SSE(Server-Sent Events)的打字机效果输出 +8. Docker Compose 一键部署 + +### 进阶优化 + +1. 统一异常处理与业务错误码设计 +2. MapStruct 实体映射最佳实践 +3. 基于 Redis Stream 的异步任务处理实现 +4. Spring Boot 4.0 升级指南 +5. Docker Compose 一键部署 + +### 面试 + +1. 面试官问“这个项目哪里来的”时,如何回答? +2. Redis 面试问题挖掘 +3. Spring AI 面试问题挖掘 +4. 文件上传和 PDF 到处面试问题挖掘 +5. 知识库 RAG 面试问题挖掘 + +### 内容获取 + +**本项目为 [JavaGuide 知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) 内部专属实战项目,通过语雀文档在线阅读学习,不单独对外开放。** + +之所以选择在星球内部发布,是为了确保每一位学习者都能获得**深度的技术答疑**和**完整的求职配套服务**。 + +整个项目教程预计在 **1-2** 个月内更完。我坚持“慢工出细活”,每一篇文章(不提供视频,浪费时间且不利于学习能力提高)都经过反复推敲,确保**高质量、零门槛**,即便是基础薄弱的同学也能跟着文档从零跑通。 + +这只是开始。后续星球还会持续推出更多贴合企业真实业务场景的 **Java 实战项目**,带你始终站在技术前沿。 + +并且,我的星球还有很多其他服务(比如简历优化、一对一提问、高频考点突击资料等),欢迎详细了解我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。 + +已经坚持维护六年,内容持续更新,虽白菜价(**0.4 元/天**)但质量很高,主打一个良心! + +仅需 **149**(价格即将上调,老用户续费半价 ,微信扫码即可续费),两本书的价格,就能让你拥有上万培训班的服务! + +![知识星球30元优惠卷](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) + +用心做事,坚持本心,共勉! + +## 系统架构 + +**提示**:架构图采用 draw.io 绘制,导出为 svg 格式,在 Dark 模式下的显示效果会有问题。 + +系统采用前后端分离架构,整体分为三层:前端展示层、后端服务层、数据存储层。 + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/interview-guide-architecture-diagram.svg) + +**后端层**: + +- REST Controllers:统一的 API 入口,处理 HTTP 请求 +- 业务服务层: + - Resume Service:简历上传、解析、AI 分析 + - Interview Service:面试会话管理、问题生成、答案评估 + - Knowledge Service:知识库上传、文本分块、向量化 + - RAG Chat Service:检索增强生成,流式问答 +- 异步处理层:基于 Redis Stream 的消费者,异步处理耗时的 AI 任务(如简历分析、向量化、面试评估) +- AI 集成层:Spring AI + DashScope(通义千问)。统一的 LLM 调用接口,支持对话生成和文本向量化。 + +**数据存储层**: + +- PostgreSQL + pgvector: + - 关系数据:简历、面试记录、知识库元数据 + - 向量检索:存储文档向量,支持相似度搜索 +- Redis: + + - 会话缓存:面试会话状态 + - 消息队列:Redis Stream 实现异步任务队列 + +- RustFS/MinIO (S3):原始文件(简历 PDF、知识库文档) + +**异步处理流程**: + +简历分析、知识库向量化和面试报告生成采用 Redis Stream 异步处理,这里以简历分析和知识库向量化为例介绍一下整体流程: + +``` +上传请求 → 保存文件 → 发送消息到 Stream → 立即返回 + ↓ + Consumer 消费消息 + ↓ + 执行分析/向量化任务 + ↓ + 更新数据库状态 + ↓ + 前端轮询获取最新状态 +``` + +状态流转: `PENDING` → `PROCESSING` → `COMPLETED` / `FAILED` + +**知识库问答处理流程**: + +``` +知识库问答 → 问题向量化 → pgvector 相似度搜索 → 检索相关文档 + ↓ + 构建 Prompt → LLM 生成回答 → SSE 流式返回 +``` + +## 技术栈概览 + +### 后端技术 + +| 技术 | 版本 | 说明 | +| --------------------- | ----- | ------------------------- | +| Spring Boot | 4.0 | 应用框架 | +| Java | 21 | 开发语言 | +| Spring AI | 2.0 | AI 集成框架 | +| PostgreSQL + pgvector | 14+ | 关系数据库 + 向量存储 | +| Redis | 6+ | 缓存 + 消息队列(Stream) | +| Apache Tika | 2.9.2 | 文档解析 | +| iText 8 | 8.0.5 | PDF 导出 | +| MapStruct | 1.6.3 | 对象映射 | +| Gradle | 8.14 | 构建工具 | + +### 前端技术 + +| 技术 | 版本 | 说明 | +| ------------- | ----- | -------- | +| React | 18.3 | UI 框架 | +| TypeScript | 5.6 | 开发语言 | +| Vite | 5.4 | 构建工具 | +| Tailwind CSS | 4.1 | 样式框架 | +| React Router | 7.11 | 路由管理 | +| Framer Motion | 12.23 | 动画库 | +| Recharts | 3.6 | 图表库 | +| Lucide React | 0.468 | 图标库 | + +## 技术选型常见问题解答 + +这里只是简单介绍,后续我会分享文章详细拷打技术选型。 + +### 为什么选择 Spring AI? + +Spring AI 是 Spring 官方推出的 AI 集成框架,提供了统一的 LLM 调用抽象。选择它的原因: + +1. 统一抽象:一套代码支持多种 LLM 提供商(OpenAI、阿里云 DashScope、Ollama 等),切换模型只需修改配置 +2. Spring 生态集成:与 Spring Boot 无缝集成,支持自动配置、依赖注入、声明式调用 +3. 内置向量存储支持:原生支持 pgvector、Milvus、Pinecone 等向量数据库,简化 RAG 开发 +4. 结构化输出:通过 `BeanOutputConverter` 将 LLM 输出直接映射为 Java 对象,无需手动解析 JSON + +```java +// 示例:Spring AI 结构化输出 +var converter = new BeanOutputConverter<>(ResumeAnalysisDTO.class); +String result = chatClient.prompt() + .system(systemPrompt) + .user(userPrompt + converter.getFormat()) + .call() + .content(); +return converter.convert(result); // 直接得到 Java 对象 +``` + +### 数据存储为什么选择 PostgreSQL + pgvector? + +本项目需要同时存储结构化数据(简历、面试记录)和向量数据(文档 Embedding)。方案对比: + +| 方案 | 优点 | 缺点 | +| --------------------- | ------------------------ | -------------------------- | +| PostgreSQL + pgvector | 一套数据库搞定,运维简单 | 向量检索性能不如专业向量库 | +| PostgreSQL + Milvus | 向量检索性能更好 | 多一个组件,运维复杂度增加 | +| PostgreSQL + Pinecone | 云托管,无需运维 | 成本高,数据在第三方 | + +**选择 pgvector 的理由**: + +- 架构简单:不引入额外组件,降低部署和运维复杂度 +- 性能够用:HNSW 索引支持毫秒级检索,万级文档场景完全够用 +- 事务一致性:向量数据和业务数据在同一数据库,天然支持事务 +- SQL 查询:可以结合 WHERE 条件过滤,比如"只在某个分类的知识库中检索" + +```sql +-- pgvector 相似度搜索示例 +SELECT content, 1 - (embedding <=> $1) as similarity +FROM vector_store +WHERE metadata->>'category' = 'Java' +ORDER BY embedding <=> $1 +LIMIT 5; +``` + +**为什么不选择 MySQL 搭配向量数据库呢?** + +PostgreSQL 最大的优势,也是它在 AI 时代甩开对手的“王牌”,就是其强大的可扩展性。开发者可以在不修改内核的情况下,像“即插即用”一样为数据库安装各种功能强大的插件,这让 PostgreSQL 变成了一个无所不能的“数据瑞士军刀”。 + +- **AI 向量检索?** 有官方推荐的 **pgvector** 扩展,性能强大,生态成熟,足以媲美专业的向量数据库。 +- **全文搜索?** 内置支持(能满足基础需求),或使用 **pg_bm25** 等扩展。 +- **时序数据?** 有顶级的 **TimescaleDB** 扩展。 +- **地理信息?** 有行业标准的 **PostGIS** 扩展。 + +这种“一站式”解决能力,正是其魅力所在。它意味着许多项目不再需要依赖 Elasticsearch、Milvus 等大量外部中间件,仅凭一个增强版的 PostgreSQL 即可满足多样化需求,从而极大地简化了技术栈,降低了开发和运维的复杂度与成本。 + +关于 MySQL 和 PostgreSQL 的详细对比,可以参考我写的这篇文章:[MySQL vs PostgreSQL,如何选择?](https://mp.weixin.qq.com/s/APWD-PzTcTqGUuibAw7GGw)。 + +### 为什么引入 Redis? + +本项目主要有两个场景用到了 Redis: + +1. Redis 替代 `ConcurrentHashMap` 实现会话的缓存。 +2. 基于 Redis Stream 实现简历分析、知识库向量化等场景的异步(还能解耦,分析和向量化可以使用其他编程语言来做)。 + +**为什么引入 Redis Stream?为何不选择 Kafka、RabbitMQ 等更成熟的消息队列?** + +简历分析、知识库向量化等 AI 任务耗时较长(10-60 秒),不适合同步处理。需要消息队列实现异步解耦。 + +| 维度 | Redis Stream | RabbitMQ | Kafka | 内存队列 | +| :--------------- | :-------------------------------- | :----------------------------- | :--------------------------- | :--------------------------------- | +| **吞吐量** | 高(十万级 QPS) | 中(万级 QPS) | 极高(百万级,水平扩展) | 极高(千万级/秒,受限于 CPU/内存) | +| **延迟** | 极低(亚毫秒级) | 低(毫秒级) | 中(毫秒到十毫秒级) | 极低(纳秒/微秒级) | +| **持久化** | 支持(RDB/AOF) | 支持(Mnesia/磁盘) | 强支持(原生分段日志) | 无(进程终止即失) | +| **消息堆积能力** | 一般(受限于内存) | 中(磁盘堆积,性能下降明显) | 极强(TB 级磁盘存储) | 差(受限于堆内存) | +| **消费模式** | 发布订阅 / 消费者组 | 灵活路由 / 多种交换机模式 | 发布订阅 / 消费者组 | 点对点 / 多消费者(取决于实现) | +| **消息回溯** | 支持(按 ID / 时间范围) | 不支持 | 强支持(按 Offset / 时间戳) | 不支持 | +| **消息顺序性** | 单 Stream 有序 | 单队列有序 | 单 Partition 有序 | 有序(单队列) | +| **可靠性** | 中(异步复制可能丢失) | 高(Publisher Confirm / 事务) | 极高(多副本 ISR + acks) | 低(无持久化、无确认) | +| **运维复杂度** | 低 | 中 | 高(KRaft 模式已简化) | 极低 | +| **适用场景** | 轻量级流处理、已有 Redis 基础设施 | 复杂路由、企业级集成 | 大数据流、事件溯源、日志聚合 | 进程内解耦、极致性能场景 | + +选择 Redis Stream 的理由: + +- 复用现有组件:Redis 已用于会话缓存,无需引入新中间件 +- 功能满足需求:支持消费者组、消息确认(ACK)、持久化 +- 运维简单:对于中小型项目,Redis Stream 完全够用 + +### 构建工具为什么选择 Gradle? + +SpringBoot 官方现在用的就是 Gradle,加上国内现在都是 Maven 更多,换个 Gradle 还更新颖一些。 + +个人也更喜欢用 Gradle,也写过相关的文章:[Gradle 核心概念总结](https://javaguide.cn/tools/gradle/gradle-core-concepts.html)。 + +### 为什么使用 MapStruct? + +项目中有大量 Entity ↔ DTO 转换需求,MapStruct 是编译时代码生成的对象映射框架: + +| 方案 | 性能 | 类型安全 | 使用复杂度 | +| ----------- | ------------ | ---------- | ------------ | +| MapStruct | 零反射,最快 | 编译时检查 | 定义接口即可 | +| BeanUtils | 反射,慢 | 运行时报错 | 一行代码 | +| ModelMapper | 反射,较慢 | 运行时报错 | 配置复杂 | +| 手写转换 | 最快 | 编译时检查 | 重复代码多 | + +### 为什么使用 Apache Tika? + +系统需要解析多种格式的文档(PDF、Word、TXT),Apache Tika 是 Apache 基金会的文档解析库: + +- 格式支持全:PDF、DOCX、DOC、TXT、HTML、Markdown 等上百种格式 +- 自动识别:根据文件内容自动检测格式,无需依赖文件扩展名 +- 文本提取:统一的 API 提取纯文本,屏蔽格式差异 + +```java +// Tika 解析示例 +Tika tika = new Tika(); +String content = tika.parseToString(inputStream); // 自动识别格式并提取文本 +``` + +### 为什么使用 SSE 而不是 WebSocket? + +知识库问答需要流式输出(像 ChatGPT 那样逐字显示),有两种技术选择: + +| 方案 | 优点 | 缺点 | +| --------- | ------------------------- | -------------------------- | +| SSE | 简单,基于 HTTP,单向推送 | 仅支持服务端 → 客户端 | +| WebSocket | 双向通信,功能强大 | 协议复杂,需要维护连接状态 | + +选择 SSE 的理由: + +- 场景匹配:LLM 流式输出是单向的(服务端 → 客户端),不需要双向通信 +- 实现简单:基于 HTTP,天然支持重连、跨域 +- Spring 支持好:`Flux>` 一行代码搞定 + +### 前端为什么选择 React + TypeScript + Tailwind CSS? + +| 技术 | 选择理由 | +| ------------ | ------------------------------------------ | +| React | 生态最成熟,组件化开发,社区资源丰富 | +| TypeScript | 类型安全,IDE 智能提示,减少运行时错误 | +| Vite | 开发服务器启动快(秒级),HMR 热更新体验好 | +| Tailwind CSS | 原子化 CSS,快速开发,无需写 CSS 文件 | + +## 效果展示 + +### 简历与面试 + +简历库: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-history.png) + +简历上传分析: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-upload-analysis.png) + +简历分析详情: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-analysis-detail.png) + +面试记录: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-interview-history.png) + +面试详情: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-interview-detail.png) + +模拟面试: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-mock-interview.png) + +### 知识库 + +知识库管理: + +![](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-knowledge-base-management.png) + +问答助手: + +![page-qa-assistant](https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-qa-assistant.png) + +## 项目结构 + +**这是项目前后端的结构说明**: + +``` + interview-guide/ + ├── app/ # 后端 Spring Boot 应用 + │ ├── src/main/java/interview/guide/ + │ │ ├── App.java # 启动类 + │ │ │ + │ │ ├── common/ # 公共模块 + │ │ │ ├── config/ # 配置类(CORS、S3存储等) + │ │ │ ├── constant/ # 常量定义 + │ │ │ ├── exception/ # 全局异常处理 + │ │ │ ├── model/ # 公共模型(如异步任务状态) + │ │ │ └── result/ # 统一响应封装 Result + │ │ │ + │ │ ├── infrastructure/ # 基础设施层 + │ │ │ ├── file/ # 文件处理(解析、存储、校验) + │ │ │ │ ├── DocumentParseService # Apache Tika 文档解析 + │ │ │ │ ├── FileStorageService # S3 文件存储 + │ │ │ │ ├── FileHashService # SHA-256 哈希计算 + │ │ │ │ └── FileValidationService # 文件类型/大小校验 + │ │ │ ├── redis/ # Redis 操作封装 + │ │ │ │ ├── RedisService # 通用 Redis 操作 + Stream 消费 + │ │ │ │ └── InterviewSessionCache # 面试会话缓存 + │ │ │ ├── mapper/ # MapStruct 对象映射 + │ │ │ └── export/ # PDF 导出服务 + │ │ │ + │ │ └── modules/ # 业务模块 + │ │ ├── resume/ # 简历模块 + │ │ │ ├── ResumeController # REST API + │ │ │ ├── model/ # Entity + DTO + │ │ │ ├── repository/ # JPA Repository + │ │ │ ├── service/ # 业务逻辑 + │ │ │ │ ├── ResumeUploadService # 上传处理 + │ │ │ │ ├── ResumeGradingService # AI 分析评分 + │ │ │ │ └── ResumePersistenceService # 持久化 + │ │ │ └── listener/ # 异步消费者 + │ │ │ ├── AnalyzeStreamProducer # 发送分析任务 + │ │ │ └── AnalyzeStreamConsumer # 消费并执行分析 + │ │ │ + │ │ ├── interview/ # 面试模块 + │ │ │ ├── InterviewController + │ │ │ ├── model/ + │ │ │ ├── repository/ + │ │ │ ├── service/ + │ │ │ │ ├── InterviewSessionService # 会话管理 + │ │ │ │ ├── InterviewQuestionService # 问题生成 + │ │ │ │ └── AnswerEvaluationService # 答案评估 + │ │ │ └── listener/ + │ │ │ └── EvaluateStreamConsumer # 异步评估 + │ │ │ + │ │ └── knowledgebase/ # 知识库模块 + │ │ ├── KnowledgeBaseController # 知识库管理 API + │ │ ├── RagChatController # RAG 问答 API(支持 SSE) + │ │ ├── model/ + │ │ ├── repository/ + │ │ │ └── VectorRepository # 向量数据操作 + │ │ ├── service/ + │ │ │ ├── KnowledgeBaseUploadService # 上传处理 + │ │ │ ├── KnowledgeBaseVectorService # 向量化 + │ │ │ ├── KnowledgeBaseQueryService # RAG 检索 + 生成 + │ │ │ └── RagChatSessionService # 聊天会话管理 + │ │ └── listener/ + │ │ └── VectorizeStreamConsumer # 异步向量化 + │ │ + │ └── src/main/resources/ + │ ├── application.yml # 主配置文件 + │ └── prompts/ # AI 提示词模板 + │ ├── resume-analysis-*.st # 简历分析提示词 + │ ├── interview-question-*.st # 面试问题生成提示词 + │ ├── interview-evaluation-*.st # 面试评估提示词 + │ └── knowledgebase-query-*.st # RAG 问答提示词 + │ + ├── frontend/ # 前端 React 应用 + │ ├── src/ + │ │ ├── main.tsx # 入口文件 + │ │ ├── App.tsx # 根组件 + 路由配置 + │ │ │ + │ │ ├── api/ # API 请求层 + │ │ │ ├── request.ts # Axios 封装 + 拦截器 + │ │ │ ├── resume.ts # 简历 API + │ │ │ ├── interview.ts # 面试 API + │ │ │ ├── knowledgebase.ts # 知识库 API + │ │ │ └── ragChat.ts # RAG 聊天 API(含 SSE) + │ │ │ + │ │ ├── pages/ # 页面组件 + │ │ │ ├── UploadPage.tsx # 简历上传 + │ │ │ ├── HistoryPage.tsx # 简历列表 + │ │ │ ├── ResumeDetailPage.tsx # 简历详情 + 分析结果 + │ │ │ ├── InterviewPage.tsx # 模拟面试 + │ │ │ ├── InterviewHistoryPage.tsx # 面试记录 + │ │ │ ├── KnowledgeBaseUploadPage.tsx # 知识库上传 + │ │ │ ├── KnowledgeBaseManagePage.tsx # 知识库管理 + │ │ │ └── KnowledgeBaseQueryPage.tsx # 知识库问答 + │ │ │ + │ │ ├── components/ # 通用组件 + │ │ │ ├── Layout.tsx # 页面布局(侧边栏 + 内容区) + │ │ │ ├── FileUploadCard.tsx # 文件上传卡片 + │ │ │ ├── AnalysisPanel.tsx # 分析结果展示 + │ │ │ ├── InterviewChatPanel.tsx # 面试问答交互 + │ │ │ ├── RadarChart.tsx # 雷达图(Recharts) + │ │ │ ├── CodeBlock.tsx # 代码块高亮 + │ │ │ └── ConfirmDialog.tsx # 确认弹窗 + │ │ │ + │ │ ├── types/ # TypeScript 类型定义 + │ │ └── utils/ # 工具函数 + │ │ + │ ├── package.json + │ └── vite.config.ts + │ + ├── docker/ # Docker 相关 + │ └── postgres/init.sql # 数据库初始化(pgvector 扩展) + │ + ├── docker-compose.yml # 一键部署编排 + ├── .env.example # 环境变量模板 + └── README.md +``` + +**后端分层详细介绍**: + +| 层级 | 目录 | 职责 | +| -------------- | ----------------------- | ------------------------------------- | +| Controller | `modules/*/Controller` | REST API 入口,参数校验,调用 Service | +| Service | `modules/*/service/` | 业务逻辑,事务管理 | +| Repository | `modules/*/repository/` | 数据访问,JPA 查询 | +| Model | `modules/*/model/` | Entity 实体 + DTO 传输对象 | +| Listener | `modules/*/listener/` | Redis Stream 异步消费者 | +| Infrastructure | `infrastructure/` | 通用基础设施(文件、缓存、导出) | +| Common | `common/` | 公共配置、异常、常量 | + +## 学习本项目你将获得什么? + +本项目采用最新主流的 Java 技术栈,是市面上第一个基于 SpringBoot4.x+SpringAI2.x 的实战项目以及教程。 + +项目整体难易程度一般,即使你的编程基础一般,按照项目教程也能顺利走完。 + +通过学习这个项目,你不仅能掌握最新的 Java AI 生态工具,还能学会如何构建一个生产级别的大模型应用。 + +### 深度掌握 AI 应用开发核心范式 + +本项目是学习 **LLM 应用开发**的绝佳实践案例: + +- **Spring AI 2.0 实战**:掌握如何通过统一抽象接口对接多种大模型(如通义千问、OpenAI),实现“零成本”模型切换。 +- **Prompt Engineering(提示词工程)**:学习如何编写结构化的 System/User Prompt,并利用 `BeanOutputConverter` 实现 **LLM 输出到 Java 对象的自动化映射**,告别繁琐的 JSON 手动解析。 +- **RAG 全链路实现**:深度理解“文档解析 -> 文本分块 -> 向量化 (Embedding) -> 向量数据库存储 -> 相似度检索 -> 上下文增强生成”的完整闭环。 + +### 现代化的 Java 后端架构思维 + +本项目采用了目前 Java 社区最前沿的技术组合: + +- **拥抱 Java 21 & Spring Boot 4.0**:提前布局最新版本的特性(如 record、虚拟线程等),让你的技术栈领先市场。 +- **模块化单体架构**:学习如何通过清晰的分层逻辑(Modules + Infrastructure + Common)组织代码,使系统既具备微服务的解耦特性,又保留单体应用的部署便捷性。 +- **高性能对象转换**:通过 **MapStruct** 替代传统的反射拷贝,学习在追求极致性能的场景下如何优雅地处理 Entity 与 DTO 的映射。 + +### 高级数据存储与中间件应用 + +避开盲目引入复杂中间件的坑,学习“最合适”的选型逻辑: + +- **PostgreSQL + pgvector 的“一站式”方案**:学习如何使用一套数据库同时处理关系型业务数据和 AI 向量数据,掌握 **HNSW 索引**在万级文档场景下的优化实践。 +- **Redis Stream 异步任务处理**:深入理解为什么在 AI 耗时任务(10-60s)场景下选择 Redis Stream 而非 Kafka。掌握基于 **消息队列** 实现系统解耦和流量削峰的真实手段。 +- **文件处理专家系统**:利用 **Apache Tika** 构建通用的文档解析引擎,处理 PDF、Word、TXT 等多种格式,提升处理非结构化数据的能力。 + +### 工程化与部署能力 + +- **标准化交付**:学习使用 **Gradle** 进行项目构建,掌握其相比 Maven 的灵活性优势。 +- **容器化部署**:通过 **Docker Compose** 实现数据库(含扩展)、缓存、后端应用的一键式环境搭建,理解生产环境下的基础设施配置。 + +### 前端工程化与交互设计 + +对于后端同学,这也是一次绝佳的“全栈能力”升级机会: + +- **SSE (Server-Sent Events) 流式渲染**:掌握像 ChatGPT 一样逐字输出回答的实现技术,理解其相比 WebSocket 在单向推送场景下的优势。 +- **响应式与动画设计**:使用 **Tailwind CSS** 快速构建美观的 UI,结合 **Framer Motion** 提升用户交互体验。 +- **数据可视化**:通过 **Recharts** 将 AI 分析后的简历评分、维度对比以直观的雷达图等形式展现。 + +## 总结 + +很多 AI 项目只停留在调用一个 API。而本项目带你解决的是**真实工程问题**: + +- 如何处理大模型响应慢的问题?(**异步处理 + Redis Stream**) +- 如何让大模型输出格式固定的数据?(**结构化 Prompt + MapStruct**) +- 如何让大模型基于私有文档回答?(**RAG + pgvector**) + +**本项目为 [JavaGuide 知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html) 内部专属实战项目,通过语雀文档在线阅读学习,不单独对外开放。** + +除了实战项目之外,我的星球还有很多其他服务(比如简历优化、一对一提问、高频考点突击资料等),欢迎详细了解我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。 + +已经坚持维护六年,内容持续更新,虽白菜价(**0.4 元/天**)但质量很高,主打一个良心! + +仅需 **149**(价格即将上调,老用户续费半价 ,微信扫码即可续费),两本书的价格,就能让你拥有上万培训班的服务! + +![知识星球30元优惠卷](https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg) diff --git a/docs/zhuanlan/java-mian-shi-zhi-bei.md b/docs/zhuanlan/java-mian-shi-zhi-bei.md index 752a6bcea71..ccdc08192f9 100644 --- a/docs/zhuanlan/java-mian-shi-zhi-bei.md +++ b/docs/zhuanlan/java-mian-shi-zhi-bei.md @@ -4,11 +4,17 @@ category: 知识星球 star: 5 --- -我花费了三年的时间,写了一本针对 Java 面试的《Java 面试指北》,内容质量非常高,非常适合准备 Java 面试的朋友使用! +**四年磨一剑,只为打造最优质的 Java 面试指南。** -目前的成绩:累计阅读 **270w+** ,点赞 **3550+** ,评论 **1130+** (几乎每一条提问类型的评论我看到后都会用心回复)。 +这本《Java 面试指北》(后端面试通用)的内容经过反复打磨,质量极高,旨在帮助每一位 Java/后端求职者从容应对面试挑战。 -![《Java 面试指北》统计](https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics.png) +**用数据说话:** 截至目前,专栏累计阅读量已突破 **477.1W**,收获点赞 **5,118** 个,评论互动 **1,657** 条。值得一提的是,评论区不仅仅是留言板,更是答疑区——几乎每一条提问,我都会用心回复,确保无疑问遗留。 + +![](https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics-2025.png) + +📅 **增长见证:** 下图记录了 2024 年时的成绩。对比当下,你会发现其增长速度可以用“惊人”来形容,这不仅是数据的攀升,更是无数读者认可的证明! + +![](https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics.png) ## 介绍 @@ -22,6 +28,10 @@ star: 5 《Java 面试指北》 会根据每一年的面试情况对内容进行更新完善,保证内容质量的时效性。并且,只需要加入[知识星球](../about-the-author/zhishixingqiu-two-years.md)一次,即可永久获取《Java 面试指北》的访问权限,持续同步更新完善。 +下面是《Java 面试指北》 收到的部分球友的真实反馈: + +![《Java 面试指北》 收到的部分球友的真实反馈](https://oss.javaguide.cn/xingqiu/praise-that-the-mianshizhibei-received.png) + ## 内容概览 ![《Java 面试指北》内容概览](https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png) @@ -32,7 +42,11 @@ star: 5 ![《Java 面试指北》面试准备篇](https://oss.javaguide.cn/javamianshizhibei/preparation-for-interview.png) -另外,考虑到很多小伙伴缺少项目经历,我还推荐了很多小众但优质的实战项目,有视频也有开源项目,有业务系统,也有各种含金量比较高的轮子类项目。 +其中的 **「⭐Java 面试准备常见问题解答(补充)」** 和 **「⭐ 项目经验常见问题解答(补充)」** 强烈建议必看,信息密度非常高! + +![](https://oss.javaguide.cn/javamianshizhibei/java-project-experience-and-interview-faq.png) + +另外,考虑到很多同学项目经历不足,我还专门整理了一批**小众但优质的实战项目**:既有配套视频,也有高质量开源仓库,既包含完整业务系统,也有技术含量很高的轮子类项目,方便你快速补齐项目短板。 ![《Java面试指北》-实战项目推荐](https://oss.javaguide.cn/javamianshizhibei/practical-project-recommendation.png) @@ -46,23 +60,30 @@ star: 5 古人云:“**他山之石,可以攻玉**” 。善于学习借鉴别人的面试的成功经验或者失败的教训,可以让自己少走许多弯路。 -**「面经篇」** 主要会分享一些高质量的 Java 后端面经,有校招的,也有社招的,有大厂的,也有中小厂的。 +**「面经篇」** 主打高质量 Java 后端真实面经:校招 / 社招全覆盖,大厂、中小厂、央国企、外企,连大厂内包都有,不管你是哪种求职方向,都能找到适配的面经参考。 + +![《Java 面试指北》面经篇](https://oss.javaguide.cn/javamianshizhibei/real-interview-experience.png) -如果你是非科班的同学,也能在这些文章中找到对应的非科班的同学写的面经。 +**为何选择《Java 面试指北》的面经?** -![《Java 面试指北》面经篇](https://oss.javaguide.cn/javamianshizhibei/thinkimage-20220612185810480.png) +相比于网络上海量但杂乱的面经信息,《Java 面试指北》中提供的面经在质量筛选和价值挖掘上投入了更多精力。每一份收录的面经均力求做到: -相比于牛客网或者其他网站的面经,《Java 面试指北》中整理的面经质量更高,并且,我会提供优质的参考资料。 +- **内容真实、有启发性**: 优先选择那些能反映实际面试场景、考察重点和面试官思路的经验。 +- **提供深度学习资源**: 拒绝“只有问题没有答案”的焦虑。针对面经中的高频/核心难题,我精心关联了高质量的参考资料(通常是我撰写的深度解析文章)或直接提供核心参考答案,助你知其然更知其所以然。 -另外,[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)还有专门分享面经和面试题的专题,里面会分享很多优质的面经和面试题。 +另外,[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)还有专门分享面经和面试题的专题,持续更新优质的面经和面试题。 -![星球面经专题](https://oss.javaguide.cn/javamianshizhibei/image-20220304120018731.png) +![](https://oss.javaguide.cn/javamianshizhibei/xingqiu-real-interview-experience.png) ### 技术面试题自测篇 -为了让小伙伴们自测以检查自己的掌握情况,我还推出了 **「技术面试题自测」** 系列。不过,目前只更新了 Java 和数据库的自测,正在持续更新中。 +为了让小伙伴们自测以检查自己的掌握情况,我还推出了 **「技术面试题自测」** 系列。目前已经覆盖 Java 后端的核心高频考点,并在持续迭代更新中。 + +![《Java 面试指北》技术面试题自测篇](https://oss.javaguide.cn/javamianshizhibei/self-test.png) + +每道题我都会给出**提示与思路**,并用 ⭐ 标注重要程度:⭐ 越多,说明面试越爱问,就越值得多花一些时间准备。 -![《Java 面试指北》技术面试题自测篇](https://oss.javaguide.cn/javamianshizhibei/image-20220621095641897.png) +![](https://oss.javaguide.cn/javamianshizhibei/self-test-key-points.png) 高效准备技术八股文的技巧之一在于多多自测,查漏补缺。 diff --git a/package.json b/package.json index fa13424e049..1e230152c67 100644 --- a/package.json +++ b/package.json @@ -20,18 +20,18 @@ ".md": "markdownlint-cli2" }, "dependencies": { - "@vuepress/bundler-vite": "2.0.0-rc.19", - "@vuepress/plugin-feed": "2.0.0-rc.70", - "@vuepress/plugin-search": "2.0.0-rc.70", + "@vuepress/bundler-vite": "2.0.0-rc.24", + "@vuepress/plugin-feed": "2.0.0-rc.112", + "@vuepress/plugin-search": "2.0.0-rc.112", "husky": "9.1.7", "markdownlint-cli2": "0.17.1", "mathjax-full": "3.2.2", "nano-staged": "0.8.0", "prettier": "3.4.2", - "sass-embedded": "1.83.1", - "vue": "^3.5.13", - "vuepress": "2.0.0-rc.19", - "vuepress-theme-hope": "2.0.0-rc.68" + "sass-embedded": "1.89.2", + "vue": "^3.5.18", + "vuepress": "2.0.0-rc.24", + "vuepress-theme-hope": "2.0.0-rc.94" }, "packageManager": "pnpm@10.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d174a9236f9..d839c7d6de2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: dependencies: '@vuepress/bundler-vite': - specifier: 2.0.0-rc.19 - version: 2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1) + specifier: 2.0.0-rc.24 + version: 2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2) '@vuepress/plugin-feed': - specifier: 2.0.0-rc.70 - version: 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + specifier: 2.0.0-rc.112 + version: 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) '@vuepress/plugin-search': - specifier: 2.0.0-rc.70 - version: 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + specifier: 2.0.0-rc.112 + version: 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) husky: specifier: 9.1.7 version: 9.1.7 @@ -33,324 +33,192 @@ importers: specifier: 3.4.2 version: 3.4.2 sass-embedded: - specifier: 1.83.1 - version: 1.83.1 + specifier: 1.89.2 + version: 1.89.2 vue: - specifier: ^3.5.13 - version: 3.5.13 + specifier: ^3.5.18 + version: 3.5.18 vuepress: - specifier: 2.0.0-rc.19 - version: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + specifier: 2.0.0-rc.24 + version: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) vuepress-theme-hope: - specifier: 2.0.0-rc.68 - version: 2.0.0-rc.68(@vuepress/plugin-feed@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)))(@vuepress/plugin-search@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)))(katex@0.16.20)(markdown-it@14.1.0)(mathjax-full@3.2.2)(nodejs-jieba@0.2.1(encoding@0.1.13))(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + specifier: 2.0.0-rc.94 + version: 2.0.0-rc.94(@vuepress/plugin-feed@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)))(@vuepress/plugin-search@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)))(katex@0.16.22)(markdown-it@14.1.0)(mathjax-full@3.2.2)(nodejs-jieba@0.2.1(encoding@0.1.13))(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) packages: - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.5': - resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/types@7.26.5': - resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@bufbuild/protobuf@2.2.3': - resolution: {integrity: sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==} + '@bufbuild/protobuf@2.6.3': + resolution: {integrity: sha512-w/gJKME9mYN7ZoUAmSMAWXk4hkVpxRKvEJCb3dV5g9wwWdxTJJ0ayOJAVcNxtdqaxDyFuC0uz4RSGVacJ030PQ==} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -359,45 +227,45 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@lit-labs/ssr-dom-shim@1.3.0': - resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} + '@lit-labs/ssr-dom-shim@1.4.0': + resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} - '@lit/reactive-element@2.0.4': - resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} + '@lit/reactive-element@2.1.1': + resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} '@mapbox/node-pre-gyp@1.0.11': resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true - '@mdit-vue/plugin-component@2.1.3': - resolution: {integrity: sha512-9AG17beCgpEw/4ldo/M6Y/1Rh4E1bqMmr/rCkWKmCAxy9tJz3lzY7HQJanyHMJufwsb3WL5Lp7Om/aPcQTZ9SA==} + '@mdit-vue/plugin-component@2.1.4': + resolution: {integrity: sha512-fiLbwcaE6gZE4c8Mkdkc4X38ltXh/EdnuPE1hepFT2dLiW6I4X8ho2Wq7nhYuT8RmV4OKlCFENwCuXlKcpV/sw==} - '@mdit-vue/plugin-frontmatter@2.1.3': - resolution: {integrity: sha512-KxsSCUVBEmn6sJcchSTiI5v9bWaoRxe68RBYRDGcSEY1GTnfQ5gQPMIsM48P4q1luLEIWurVGGrRu7u93//LDQ==} + '@mdit-vue/plugin-frontmatter@2.1.4': + resolution: {integrity: sha512-mOlavV176njnozIf0UZGFYymmQ2LK5S1rjrbJ1uGz4Df59tu0DQntdE7YZXqmJJA9MiSx7ViCTUQCNPKg7R8Ow==} - '@mdit-vue/plugin-headers@2.1.3': - resolution: {integrity: sha512-AcL7a7LHQR3ISINhfjGJNE/bHyM0dcl6MYm1Sr//zF7ZgokPGwD/HhD7TzwmrKA9YNYCcO9P3QmF/RN9XyA6CA==} + '@mdit-vue/plugin-headers@2.1.4': + resolution: {integrity: sha512-tyZwGZu2mYkNSqigFP1CK3aZYxuYwrqcrIh8ljd8tfD1UDPJkAbQeayq62U572po2IuWVB1BqIG8JIXp5POOTA==} - '@mdit-vue/plugin-sfc@2.1.3': - resolution: {integrity: sha512-Ezl0dNvQNS639Yl4siXm+cnWtQvlqHrg+u+lnau/OHpj9Xh3LVap/BSQVugKIV37eR13jXXYf3VaAOP1fXPN+w==} + '@mdit-vue/plugin-sfc@2.1.4': + resolution: {integrity: sha512-oqAlMulkz280xUJIkormzp6Ps0x5WULZrwRivylWJWDEyVAFCj5VgR3Dx6CP2jdgyuPXwW3+gh2Kzw+Xe+kEIQ==} - '@mdit-vue/plugin-title@2.1.3': - resolution: {integrity: sha512-XWVOQoZqczoN97xCDrnQicmXKoqwOjIymIm9HQnRXhHnYKOgJPW1CxSGhkcOGzvDU1v0mD/adojVyyj/s6ggWw==} + '@mdit-vue/plugin-title@2.1.4': + resolution: {integrity: sha512-uuF24gJvvLVIWG/VBtCDRqMndfd5JzOXoBoHPdKKLk3PA4P84dsB0u0NnnBUEl/YBOumdCotasn7OfFMmco9uQ==} - '@mdit-vue/plugin-toc@2.1.3': - resolution: {integrity: sha512-41Q+iXpLHZt0zJdApVwoVt7WF6za/xUjtjEPf90Z3KLzQO01TXsv48Xp9BsrFHPcPcm8tiZ0+O1/ICJO80V/MQ==} + '@mdit-vue/plugin-toc@2.1.4': + resolution: {integrity: sha512-vvOU7u6aNmvPwKXzmoHion1sv4zChBp20LDpSHlRlXc3btLwdYIA0DR+UiO5YeyLUAO0XSHQKBpsIWi57K9/3w==} - '@mdit-vue/shared@2.1.3': - resolution: {integrity: sha512-27YI8b0VVZsAlNwaWoaOCWbr4eL8B04HxiYk/y2ktblO/nMcOEOLt4p0RjuobvdyUyjHvGOS09RKhq7qHm1CHQ==} + '@mdit-vue/shared@2.1.4': + resolution: {integrity: sha512-Axd8g2iKQTMuHcPXZH5JY3hbSMeLyoeu0ftdgMrjuPzHpJnWiPSAnA0dAx5NQFQqZkXHhyIrAssLSrOWjFmPKg==} - '@mdit-vue/types@2.1.0': - resolution: {integrity: sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==} + '@mdit-vue/types@2.1.4': + resolution: {integrity: sha512-QiGNZslz+zXUs2X8D11UQhB4KAMZ0DZghvYxa7+1B+VMLcDtz//XHpWbcuexjzE3kBXSxIUTPH3eSQCa0puZHA==} - '@mdit/helper@0.16.0': - resolution: {integrity: sha512-vUmLSZp+7UXJIYxOya9BkD0OgjgQ+6gpX+htEnc4SKaDPx4S1E7h5TE6Wy4E9Gm/JhkMHoD6TdeoQwrN/I9cLQ==} + '@mdit/helper@0.22.1': + resolution: {integrity: sha512-lDpajcdAk84aYCNAM/Mi3djw38DJq7ocLw5VOSMu/u2YKX3/OD37a6Qb59in8Uyp4SiAbQoSHa8px6hgHEpB5g==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -405,16 +273,16 @@ packages: markdown-it: optional: true - '@mdit/plugin-alert@0.16.0': - resolution: {integrity: sha512-T+0BUVhKjp+Azp6sNdDbiZwydDIcZP6/NAg9uivPvcsDnI9u4lMRCdXI090xNJOdhHO3l/lOsoO//s+++MJNtA==} + '@mdit/plugin-alert@0.22.2': + resolution: {integrity: sha512-n2oVSeg3yeZBCjqfAqbnJxeu4PGq+CXwUWsiwrrARj39z23QZ62FbgL5WGNyP/WFnDAeHMedLDYtipC9OgIOgA==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-align@0.16.0': - resolution: {integrity: sha512-BJhOjX4Zobs+ZKEpDtxGrUCnppkFCTGIBLjXkCPmxeLf4Tsh7dqv5vVhbRueSOz/EIzc2RJzR0dlMLofsaCFeA==} + '@mdit/plugin-align@0.22.1': + resolution: {integrity: sha512-KCI9Sa1TW25Th1QvEZUp1OnI5qOE82OeduWKeQ5CHsVIbW2WTyRZjLgxPO0kPWPw15gbSrLvWj4RC7cv+C5p6Q==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -422,8 +290,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-attrs@0.16.2': - resolution: {integrity: sha512-ftzyOo6mDquRfpwcrSYPu9DIUhIRvC9ZTjUq1lGUd/ts93PKF9v6YCio/L376CEKLMVibHdNYBQAkGTQFwAgnA==} + '@mdit/plugin-attrs@0.23.1': + resolution: {integrity: sha512-KY05v0DIBMItOxoniyDxxtyYIiT+0JTQ2Ke0mzyCyvPplqCv4Avus7/uAZ3+IGcaI2oOTlYEHdU288VBFgXjAw==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -431,8 +299,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-container@0.16.0': - resolution: {integrity: sha512-NCsyEiOmoJvXSEVJSY6vaEcvbE11sciRSx5qXBvQQZxUYGYsB+ObYSFVZDFPezsEN35X3b07rurLx8P2Mi9DgQ==} + '@mdit/plugin-container@0.22.1': + resolution: {integrity: sha512-UY1NRRb/Su9YxQerkCF8bWG0fY/V24b9f/jVWh5DhD+Dw4MifVbV6p5TlaeQ854Xz9prkhyXSugiWbjhju6BgQ==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -440,16 +308,16 @@ packages: markdown-it: optional: true - '@mdit/plugin-demo@0.16.0': - resolution: {integrity: sha512-EoSpHz8ViLk5HLBCSzQZGOa36JXGHM4q5zOJ0ppgZymxnzRr6vUo+GX022uLivxyNMW1+l30IiF+jbse+JtBGw==} + '@mdit/plugin-demo@0.22.2': + resolution: {integrity: sha512-2V7C2ioftTz8mbUp+JEc8uQL0ffbopA4CihXobyQTctL/qrvL7/goqHBCXdC1Xy64KfWEhukHcuSdWARCv1Muw==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-figure@0.16.0': - resolution: {integrity: sha512-0lYZX3cCUNaygtQXXZH2fHXzmF7sMZ5Jbk5MXDxEDIk1Nkxj8ADo/SctvXN5exwyGpJyw8nTbm7CGgMqifDpmQ==} + '@mdit/plugin-figure@0.22.1': + resolution: {integrity: sha512-z7uqtKsQ/ILkdM4pLrfuvz2eAhtwNzRPT9xnixFosrMgF7CEHbBtFTF6nc2ht1mOqCTRqoIL+FWg8InYMiBPhQ==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -457,22 +325,22 @@ packages: markdown-it: optional: true - '@mdit/plugin-footnote@0.16.0': - resolution: {integrity: sha512-vaJWhOsya7bYfplLlMHYBxGTbME0e46/eTVKBROemWtAf873DTkV4IhkAq7MzGqeYrw0L9gxQPgGDFphGfySMA==} + '@mdit/plugin-footnote@0.22.2': + resolution: {integrity: sha512-lHB6AV61QruvrWXIu/oWncltH2ED8cBUuvX4IO+5TvtWSyyc6wOm3ErPqqTFJqy1SJ1p21oLNcqRGdPF+S3N4w==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 - '@mdit/plugin-icon@0.16.5': - resolution: {integrity: sha512-9T34gnNrjCMdqNLnC1oi+kZT1iCnwlHAtH3D7sjVkcP8Cw4GoDoAGy50oyryivDlczrKubOFtF05lYAfXZauuA==} + '@mdit/plugin-icon@0.22.1': + resolution: {integrity: sha512-Ipjh5Lc1tXn57Pag2GUh0nfwf+sBR4SCZsWAp807E9wncT4/yecznlXotDdXWxDIisloEpu0n+LYHatABmgscA==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-img-lazyload@0.16.0': - resolution: {integrity: sha512-Ilf3e5SKG7hd+RAoYQalpjoz8LMCxCe3BBHFYerv8u4wLnKe/L0Gqc8kXSpR37flzv3Ncw/NMqmD4ZZ0QQnK9A==} + '@mdit/plugin-img-lazyload@0.22.1': + resolution: {integrity: sha512-ombpBQqR1zYjtr4/7s8EvIVx/ymtiflWksXropYz81o0I9Bm9Os1UPuNgjwfT/DEhIit4HMaJhjpKhGkYrOKgA==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -480,8 +348,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-img-mark@0.16.0': - resolution: {integrity: sha512-BUYqQRWUxNKB0BbMb8SZtlTeDZNXxuJ9AuiuB54RIWlbx3iRlQkbQI3B/AxTT5/EbRMDhxOq0R8PumBuA1gNFA==} + '@mdit/plugin-img-mark@0.22.1': + resolution: {integrity: sha512-C6i9Tl39pKetoH83XBkj5/hfN+uK6N8Fw8ltyERNki916vzUCci/09NfrT92MF/AfJPoDJQYALy7qdgOVjnT9Q==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -489,8 +357,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-img-size@0.16.0': - resolution: {integrity: sha512-4FBvIHYWT22bjU+kO1I00xLtnCi7aXdZ7QD3CJnK4Xl6gN8/WB9IkfqYnBPv8yDiaZrabduQo8Dh8Dm8hPOm2A==} + '@mdit/plugin-img-size@0.22.2': + resolution: {integrity: sha512-+2+HpV5wZ3ZvFAs2alOiftDO635UbbOTr9uRQ0LZi/1lIZzKa0GE8sxYmtAZXRkdbGCj1uN6puoT7Bc7fdBs7Q==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -498,16 +366,16 @@ packages: markdown-it: optional: true - '@mdit/plugin-include@0.16.0': - resolution: {integrity: sha512-9ESwsc+/jYkS0hIzpWqMQ9bHgHG//35datnfp0KUOql/DSuLVhufPtNkKNe/SVNO/+AOBTTlRYzej9Jl7JjD7g==} + '@mdit/plugin-include@0.22.1': + resolution: {integrity: sha512-ylP4euox7PDH+Vg9XXuLwDIWpy/HHzeHaO+V8GEnu/QS8PgBEJ0981wLtIik53Fq8FdHgQ2rKRRhBaJ04GNUjQ==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-katex-slim@0.16.2': - resolution: {integrity: sha512-NVq2fL6Zlbd/se8a69qGoOJ43wfQ+WJ33oIPDuh/n+pBtOnXS2P8oq01k/peen40wdPQBo62rQDxTgd+sMCOsA==} + '@mdit/plugin-katex-slim@0.23.1': + resolution: {integrity: sha512-oNao/gmUrtNSCFffGhCPWxZ9UHR2jpbB+GRXB7UQabl9ijIV6LZgUM3vjSda1c47s7c7ac+9P0J/GYaxC1GHFA==} engines: {node: '>= 18'} peerDependencies: katex: ^0.16.9 @@ -518,8 +386,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-mark@0.16.0': - resolution: {integrity: sha512-VY8HhLaNw6iO6E1pSZr3bG6MzyxcAdQmQ+S0r/l87S0EKHCBrUJusaUjxa9aTVHiBcgGUjg9aumribGrWfuitA==} + '@mdit/plugin-mark@0.22.1': + resolution: {integrity: sha512-2blMM/gGyqPARvaal44mt0pOi+8phmFpj7D4suG4qMd1j8aGDZl9R7p8inbr3BePOady1eloh0SWSCdskmutZg==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -527,8 +395,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-mathjax-slim@0.16.0': - resolution: {integrity: sha512-bbo6HtNOFdNMGZH/pxc3X1vZOvOW1FF9RMiAW2pkmyk7sPnMziB8uwxm0Ra1RajEC/NDxJ3wcF7xynkLmS6PfA==} + '@mdit/plugin-mathjax-slim@0.23.1': + resolution: {integrity: sha512-32FkYqLrL6YXbtXUU8tJFRTVwu+bZJo50mCFcVt+b5UA1AWSc7UY3qsyG7iY/4dho7qU/NdB2ABTadGOR9EgsA==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -539,16 +407,16 @@ packages: mathjax-full: optional: true - '@mdit/plugin-plantuml@0.16.0': - resolution: {integrity: sha512-ZjGOWYxPcGFq/TAJ2wOU6vCYH82685ERFQAC+xUsd/f6G41oGmk5i2aNqfNYYPmoQvcPvimGUPky9L6k2IXKXw==} + '@mdit/plugin-plantuml@0.22.2': + resolution: {integrity: sha512-PjfYAKaPhnip2f51lYSiKz9cJWvMw+JfZZp/Yzdmmdtfi/la5uzilZfxVRDboJJ6qZ1qnp0pxNTVIcDb65s6DA==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-spoiler@0.16.0': - resolution: {integrity: sha512-lm2lLx5H6649igzmbEe7KGsYfS6EOHn3Ps1ZdOHIFo0AY9eEh//gbjPOuJNJU58vtMnzLYzQHQKp/JqViYTIQQ==} + '@mdit/plugin-spoiler@0.22.1': + resolution: {integrity: sha512-sk+timpOVDRlC1ShjsZ5f48eqXzJajZK1rMhtSe/ON+9ttxaXsvTPQzK1xhAE+fUrN9CzfFcDUgMAhOkTl9deg==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -556,8 +424,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-stylize@0.16.0': - resolution: {integrity: sha512-uxM9aFdgS5YCXOSNSdYyC+uXyCnmqv1VUPRNAv0g/iOts0pUp63ZEUEO2sNlbXj1rGGEWylXyXqh3OU9rRngzg==} + '@mdit/plugin-stylize@0.22.1': + resolution: {integrity: sha512-JEfLd9sVcoDZ8sI4iH+t8iOKA6QkQKYgaGIbNrjoc7j65bsAEFKu+Sh9VQy6il3xIwsDJcah+O57rzxEeDsscQ==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -565,8 +433,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-sub@0.16.0': - resolution: {integrity: sha512-XpGcZW11SAWuiWtx9aYugM67OLtQJSfN87Q/aZbEfm6ahgdbO5lAe/vBFTBmL9aDc2EVatytGeZL3kA7pfHlOA==} + '@mdit/plugin-sub@0.22.1': + resolution: {integrity: sha512-ZEEcxk2cB0mRHwBijxCwG8xf3LH/ax2WH+0yMMVaQ4fZuszZzAnHGOlEn/ijLVl2gmSF0lwlJXCz6q7rzi3r0w==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -574,8 +442,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-sup@0.16.0': - resolution: {integrity: sha512-45Sws9TC9h9ZRB/IcXAae+uYXb+FkVr/rkr9eMYKMFKksjMBddN+WY3Gpl9O7LhaGPipqTkm68QZnRSS1jvFkw==} + '@mdit/plugin-sup@0.22.1': + resolution: {integrity: sha512-B0ez+dt1tjX2gxcS6ShF+ddXU6X7wDwVnz1rB4aXo5PhvCRkBWpuXbFJT2gy5TIAG7/B4AHQww2KeEYhd56NUw==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -583,16 +451,16 @@ packages: markdown-it: optional: true - '@mdit/plugin-tab@0.16.0': - resolution: {integrity: sha512-c+/oT319DIWaMHyx5chueW8cy4pjC7E09QOg3qp86abTCdG2ljGLOlMAQbst5i/iH684QG/i8EJpB4oUeQdhkw==} + '@mdit/plugin-tab@0.22.2': + resolution: {integrity: sha512-3BbC3GTCiws2HsFG+BsXhuss6O90OLIvnBRrKP4IQtMIWlcEaxDf1nNvYYFt3sWipSGI4JuO3S7BxQ1dZkabKg==} peerDependencies: markdown-it: ^14.1.0 peerDependenciesMeta: markdown-it: optional: true - '@mdit/plugin-tasklist@0.16.0': - resolution: {integrity: sha512-pxVxartDd8LYxhdYxyrh4c7JEAq+4cEMLI1HNCHTMK9cfO+SoVd/YpibfrDUg+LHvffc8Pf2Yc8pWXNoW34B1g==} + '@mdit/plugin-tasklist@0.22.1': + resolution: {integrity: sha512-mn09Sm0fMV6ql3wb6TuoAai4gmnybvq09KeHa2ckBKKO/fwqVqCvOUI2yvZc3IrYMR+4B2WlBtyCBk5v11H9Uw==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -600,8 +468,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-tex@0.16.0': - resolution: {integrity: sha512-VWb5rJYP0eBRRjYhcaRE3r8UQkUaBXzu0l42ck7DOp+MSPsgXfS+bmk8/tyHG6/X/Mig9H92Lh1jzTqp3f5yKg==} + '@mdit/plugin-tex@0.22.1': + resolution: {integrity: sha512-sCoOHznJjECeWCd0SggYpiZfwDfGGZ5mN3sKQA9PCHVRRXHh0dEl3wwNNvp/L8f6jZ4SpG5mxtPqBvxlPbE5nw==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -609,8 +477,8 @@ packages: markdown-it: optional: true - '@mdit/plugin-uml@0.16.0': - resolution: {integrity: sha512-BIsq6PpmRgoThtVR2j4BGiRGis6jrcxxqQW3RICacrG52Ps2RWEGwu7B/IvXs+KJZJLJsrKFQ2Pqaxttbjx3kw==} + '@mdit/plugin-uml@0.22.1': + resolution: {integrity: sha512-ioSQ1HKfbBgf/euOtJjVCHlxgvx6UStuy6J4ftLEUHT4S1Jl22d1UrhEf0yZ/tMlYpWKgjh9pGUL68T4ze+VSA==} engines: {node: '>= 18'} peerDependencies: markdown-it: ^14.1.0 @@ -642,154 +510,160 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.30.1': - resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rolldown/pluginutils@1.0.0-beta.29': + resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.30.1': - resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.30.1': - resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.30.1': - resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.30.1': - resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.30.1': - resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': - resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.30.1': - resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.30.1': - resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.30.1': - resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': - resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': - resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.30.1': - resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.30.1': - resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.30.1': - resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.30.1': - resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-win32-arm64-msvc@4.30.1': - resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.30.1': - resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.30.1': - resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} cpu: [x64] os: [win32] - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@shikijs/core@1.26.2': - resolution: {integrity: sha512-ORyu3MrY7dCC7FDLDsFSkBM9b/AT9/Y8rH+UQ07Rtek48pp0ZhQOMPTKolqszP4bBCas6FqTZQYt18BBamVl/g==} + '@shikijs/core@3.9.2': + resolution: {integrity: sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA==} - '@shikijs/engine-javascript@1.26.2': - resolution: {integrity: sha512-ngkIu9swLVo9Zt5QBtz5Sk08vmPcwuj01r7pPK/Zjmo2U2WyKMK4WMUMmkdQiUacdcLth0zt8u1onp4zhkFXKQ==} + '@shikijs/engine-javascript@3.9.2': + resolution: {integrity: sha512-kUTRVKPsB/28H5Ko6qEsyudBiWEDLst+Sfi+hwr59E0GLHV0h8RfgbQU7fdN5Lt9A8R1ulRiZyTvAizkROjwDA==} - '@shikijs/engine-oniguruma@1.26.2': - resolution: {integrity: sha512-mlN7Qrs+w60nKrd7at7XkXSwz6728Pe34taDmHrG6LRHjzCqQ+ysg+/AT6/D2LMk0s2lsr71DjpI73430QP4/w==} + '@shikijs/engine-oniguruma@3.9.2': + resolution: {integrity: sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA==} - '@shikijs/langs@1.26.2': - resolution: {integrity: sha512-o5cdPycB2Kw3IgncHxWopWPiTkjAj7dG01fLkkUyj3glb5ftxL/Opecq9F54opMlrgXy7ZIqDERvFLlUzsCOuA==} + '@shikijs/langs@3.9.2': + resolution: {integrity: sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w==} - '@shikijs/themes@1.26.2': - resolution: {integrity: sha512-y4Pn6PM5mODz/e3yF6jAUG7WLKJzqL2tJ5qMJCUkMUB1VRgtQVvoa1cHh7NScryGXyrYGJ8nPnRDhdv2rw0xpA==} + '@shikijs/themes@3.9.2': + resolution: {integrity: sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA==} - '@shikijs/transformers@1.26.2': - resolution: {integrity: sha512-nAwivOhYDKudYsX9xOmA9ekkqYv+Q/IadX5ca0nV7qPTN+wf/tXHrjxVmJJlsEVtakCEuMR0a0AVL+V9QZxi7w==} + '@shikijs/transformers@3.9.2': + resolution: {integrity: sha512-MW5hT4TyUp6bNAgTExRYLk1NNasVQMTCw1kgbxHcEC0O5cbepPWaB+1k+JzW9r3SP2/R8kiens8/3E6hGKfgsA==} - '@shikijs/types@1.26.2': - resolution: {integrity: sha512-PO2jucx2FIdlLBPYbIUlMtWSLs5ulcRcuV93cR3T65lkK5SJP4MGBRt9kmWGXiQc0f7+FHj/0BEawditZcI/fQ==} + '@shikijs/types@3.9.2': + resolution: {integrity: sha512-/M5L0Uc2ljyn2jKvj4Yiah7ow/W+DJSglVafvWAJ/b8AZDeeRAdMu3c2riDzB7N42VD+jSnWxeP9AKtd4TfYVw==} - '@shikijs/vscode-textmate@10.0.1': - resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - '@stackblitz/sdk@1.11.0': resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/fs-extra@11.0.4': resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -821,14 +695,14 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@22.10.5': - resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/node@24.2.1': + resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} @@ -842,120 +716,120 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/web-bluetooth@0.0.20': - resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} - '@ungap/structured-clone@1.2.1': - resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitejs/plugin-vue@5.2.1': - resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} - engines: {node: ^18.0.0 || >=20.0.0} + '@vitejs/plugin-vue@6.0.1': + resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 - '@vue/compiler-core@3.5.13': - resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + '@vue/compiler-core@3.5.18': + resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==} - '@vue/compiler-dom@3.5.13': - resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + '@vue/compiler-dom@3.5.18': + resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==} - '@vue/compiler-sfc@3.5.13': - resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + '@vue/compiler-sfc@3.5.18': + resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==} - '@vue/compiler-ssr@3.5.13': - resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + '@vue/compiler-ssr@3.5.18': + resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/devtools-api@7.7.0': - resolution: {integrity: sha512-bHEv6kT85BHtyGgDhE07bAUMAy7zpv6nnR004nSTd0wWMrAOtcrYoXO5iyr20Hkf5jR8obQOfS3byW+I3l2CCA==} + '@vue/devtools-api@7.7.7': + resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} - '@vue/devtools-kit@7.7.0': - resolution: {integrity: sha512-5cvZ+6SA88zKC8XiuxUfqpdTwVjJbvYnQZY5NReh7qlSGPvVDjjzyEtW+gdzLXNSd8tStgOjAdMCpvDQamUXtA==} + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} - '@vue/devtools-shared@7.7.0': - resolution: {integrity: sha512-jtlQY26R5thQxW9YQTpXbI0HoK0Wf9Rd4ekidOkRvSy7ChfK0kIU6vvcBtjj87/EcpeOSK49fZAicaFNJcoTcQ==} + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} - '@vue/reactivity@3.5.13': - resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + '@vue/reactivity@3.5.18': + resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==} - '@vue/runtime-core@3.5.13': - resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + '@vue/runtime-core@3.5.18': + resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==} - '@vue/runtime-dom@3.5.13': - resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + '@vue/runtime-dom@3.5.18': + resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==} - '@vue/server-renderer@3.5.13': - resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + '@vue/server-renderer@3.5.18': + resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==} peerDependencies: - vue: 3.5.13 + vue: 3.5.18 - '@vue/shared@3.5.13': - resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@vue/shared@3.5.18': + resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} - '@vuepress/bundler-vite@2.0.0-rc.19': - resolution: {integrity: sha512-Vn0wEVRcdAld+8NJeELSwrj5JEPObRn0xpRWtAau/UwVWHmMLo16RRkTvXdjSiwpDWeP/9ztC5buyTXVoeb7Dw==} + '@vuepress/bundler-vite@2.0.0-rc.24': + resolution: {integrity: sha512-prgT3f6xOBC43rhfvzlfXY0wJKsI+oV5RC4s0YyVPZ0s5VQKI3RRD1aY+euiVFPks3Mjx+DxEtKBOLsJ7I6crA==} - '@vuepress/bundlerutils@2.0.0-rc.19': - resolution: {integrity: sha512-ln5htptK14OMJV3yeGRxAwYhSkVxrTwEHEaifeWrFvjuNxj2kLmkCl7MDdzr232jSOWwkCcmbOyafbxMsaRDkQ==} + '@vuepress/bundlerutils@2.0.0-rc.24': + resolution: {integrity: sha512-gtO0zhb57SyDotgdSI+TMAwJKg7KC75/G4UoWRwkyAHREsbWUInHQfXzzaFMnKmkdcB9YeXXbOnWGwZjRn74ew==} - '@vuepress/cli@2.0.0-rc.19': - resolution: {integrity: sha512-QFicPNIj3RZAJbHoLbeYlPJsPchnQLGuw0n8xv0eeUi9ejEXO1huWA8sLoPbTGdiDW+PHr1MHnaVMkyUfwaKcQ==} + '@vuepress/cli@2.0.0-rc.24': + resolution: {integrity: sha512-3IJtADHg67U6q3i1n3klbBtm5TZZI3uO+MkEDq8efgK7kk27LAt+7GhxqxZCq5xJ+GPNZqElc+t3+eG9biDNFA==} hasBin: true - '@vuepress/client@2.0.0-rc.19': - resolution: {integrity: sha512-vUAU6n4qmtXqthxkb4LHq0D+VWSDenwBDf0jUs7RaBLuOVrbPtmH/hs4k1vLIlGdwC3Zs/G6tlB4UmuZiiwR8Q==} + '@vuepress/client@2.0.0-rc.24': + resolution: {integrity: sha512-7W1FbrtsNDdWqkNoLfZKpZl8hv+j6sGCdmKtq90bRwzbaM+P2FJ6WYQ4Px4o/N0pqvr70k1zQe3A42QIeH0Ybw==} - '@vuepress/core@2.0.0-rc.19': - resolution: {integrity: sha512-rvmBPMIWS2dey/2QjxZoO0OcrUU46NE3mSLk3oU7JOP0cG7xvRxf6U1OXiwYLC3fPO4g6XbHiKe6gihkmL6VDA==} + '@vuepress/core@2.0.0-rc.24': + resolution: {integrity: sha512-NfNg6+vo5BJHBsLpoiXO8pU0zKaYCZxQinidW9r4KclNfZzC8PMkeBMeCT0uxcrb+XCaiHOrW19pF0/6NYNs0Q==} - '@vuepress/helper@2.0.0-rc.70': - resolution: {integrity: sha512-3v8m0x9GyPY3TC+GFBJ8eNQ0Pa3qYLXfT5wK4HtZw+ti4dff6fNufqUtH63a2CgTKMI0BHrdUddw/lmI1LobPw==} + '@vuepress/helper@2.0.0-rc.112': + resolution: {integrity: sha512-gj19xHyYbG0wygcoJ6YypCNS+nybVt2AEJFyHTFvl+KiB2BfBhKWuCpWufp4c4Od1xkru4y56I+pSU2b8CGIBQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/highlighter-helper@2.0.0-rc.70': - resolution: {integrity: sha512-YNSY22RqLTvpp8ZJ6UQtJPwpqytWBj9EkxUcBX3zf+7p4+QgMg8gdjvKAS/UKC3n2eNFBEH1y+ZRytQBVMW/9g==} + '@vuepress/highlighter-helper@2.0.0-rc.112': + resolution: {integrity: sha512-gDNGSOFR6yXS567ObWqn7vc8O8ZqCl1kn5wDdBfa0qe011CQgsJKQbGH6tFxfbi0JznZ1bjpKZmEaUKxsFRbtg==} peerDependencies: - '@vueuse/core': ^12.2.0 - vuepress: 2.0.0-rc.19 + '@vueuse/core': ^13.5.0 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: '@vueuse/core': optional: true - '@vuepress/markdown@2.0.0-rc.19': - resolution: {integrity: sha512-6jgUXhpEK55PEEGtPhz7Hq/JqTbLU8n9w2D7emXiK2FYcbeKpjoRIbVRzmzB/dXeK3NzHChANu2IIqpOT6Ba1w==} + '@vuepress/markdown@2.0.0-rc.24': + resolution: {integrity: sha512-yYSo89cFbti2F/JWX3Odx9jbPje20PuVO+0SLkZX9AP5wuOv79Mx5QeRVEUS1YfD3faM98ya5LoIyuYWjPjJHw==} - '@vuepress/plugin-active-header-links@2.0.0-rc.70': - resolution: {integrity: sha512-t20HQsVTzkVH+nGyaaBtllV/xR4UKU/+yRSnUOo7jpbdHIpKAppke6JwOTVQAnSTDbTLqX7sD6LmI7WrVBmCVw==} + '@vuepress/plugin-active-header-links@2.0.0-rc.112': + resolution: {integrity: sha512-D20vh2A/nPslD1fQdJMQh5BmViLCynJ41YcqaM3YEc9duI0rj6oVAFRALs9H2QipPtwPtibXkHERrR0WQxDsdA==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-back-to-top@2.0.0-rc.70': - resolution: {integrity: sha512-zcHN13tTSl2lK+OwStrYpp553I41GvWFf0Havr2DHJ4LlyZBEvzLzcqwJ4kZhyGdU1u0nstkyzqkEyi5PsjlJw==} + '@vuepress/plugin-back-to-top@2.0.0-rc.112': + resolution: {integrity: sha512-R/JrM0jwMTzJxjzz+eCJB475sqAq/6p5SJYioRi7FMeuJ3pLheWVIh4gVV5TuJ71v6XyIJMeBr4Z9/sX+Lb3Bw==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-blog@2.0.0-rc.70': - resolution: {integrity: sha512-2+lprMHSRbUb2GHhV4mqMmoEZjHdYyX08Q2Tp9A+v4EJ0SNcIbic2IVgpbyysRz9DXjMaTvojkensFZwHWiNvA==} + '@vuepress/plugin-blog@2.0.0-rc.112': + resolution: {integrity: sha512-VZQG997jTAXx1E5UeLvf9spqH3UkHvwR8HtRMt/bQITHzAMDtoEFw3RDZd4rSdO41S4jksIsOhuqfz4zX+EQ3A==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-catalog@2.0.0-rc.70': - resolution: {integrity: sha512-mvdA4iTL6sPXjcKFIFGC4aYTye30R5xNJrezkodbmrc/bWfBSqc5NPbOXxBY4hSTYgiVOWx+dVVACIi8O6dBWg==} + '@vuepress/plugin-catalog@2.0.0-rc.112': + resolution: {integrity: sha512-l4BbbwQ1t4jvJc9RurHIp42mQBo5H7H3MOo2bZj6qC3965mRihMztXjmFL8bb0A6pLthimmyYT9bJLvEDBy7Vg==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-comment@2.0.0-rc.70': - resolution: {integrity: sha512-AweFgxY75t2TzNKD3d+9ytBPHLT6i2OpPsJWkrwCnfx6IjKT0SExl9uzRDYa7Y9YDVKBZhDtmEMhs7BQEghq4Q==} + '@vuepress/plugin-comment@2.0.0-rc.112': + resolution: {integrity: sha512-Ty7HE6oUI5Inlth4ykAWf7sug8kY7LD5t77p9zKLpITffRN6eIRipgAEyWRnogmwYYu6lj8THjrAj6Jc7+ACJw==} peerDependencies: - '@waline/client': ^3.4.3 - artalk: ^2.9.0 - twikoo: ^1.6.39 - vuepress: 2.0.0-rc.19 + '@waline/client': ^3.5.5 + artalk: ^2.9.1 + twikoo: ^1.6.41 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: '@waline/client': optional: true @@ -964,116 +838,148 @@ packages: twikoo: optional: true - '@vuepress/plugin-copy-code@2.0.0-rc.70': - resolution: {integrity: sha512-hTYP2/SJ7qoD7NCEt9a9evZvaw+gfuHh60YISHlY/LmwJoicQyGMSjetraubWMRJFvaL77nGw4JtFxmzvKMqDA==} + '@vuepress/plugin-copy-code@2.0.0-rc.112': + resolution: {integrity: sha512-P0wrNU5O95/1s8LgXHNoMka66VhaJ9K9xiqVI8afJxJKtKOaanQ15pXqlJlhYIjnxMfV9Rh3YvM5qwiB9WSEyg==} + peerDependencies: + vuepress: 2.0.0-rc.24 + + '@vuepress/plugin-copyright@2.0.0-rc.112': + resolution: {integrity: sha512-kpsIB8ntPufNO9Sbrr1YRdPLiWOUQuYWpey4L2Uiod5010gp79yOv9o3clKJdpKVPP6b5dfcuSYuekPJBbPE8Q==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-copyright@2.0.0-rc.70': - resolution: {integrity: sha512-5W+ymDFKaGMw49R/lL73tr7x5ibhHP3YxvjdC8S1ftbP0sAvb5xekE2CXVxTxdJYTHMv1QlF4JLYgUq3G2Powg==} + '@vuepress/plugin-feed@2.0.0-rc.112': + resolution: {integrity: sha512-K/7kvBxTilLDarqQne6lmmi41mP+PCrVCqMXAyaZR5VXcxUqE5cvNs/6N1AH8HXhRRtyAfsjlVYI3W0Yx5vYFA==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-feed@2.0.0-rc.70': - resolution: {integrity: sha512-hzAaxh8xi+X8bz/60tmzgT86jc4koSOIPRh7V5iwVrqNcBswk76OxGO+/3bEd8Tt45RXUatwaN3O5RTrjpoU5A==} + '@vuepress/plugin-git@2.0.0-rc.112': + resolution: {integrity: sha512-OKnw1wSgJuKFE6z2aFoqg+ldjUSRuTahzW8DVC9jOy32Uss0LDo0zXiL4UCk+XAkJXfERUOc2pXYOMs5seGDmQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-git@2.0.0-rc.68': - resolution: {integrity: sha512-k/tXBSIyQM26UrmDK/mN1/q6gw8PmF2uLyIaso+B39qCOFQKUBq4uJF2a0oYTq9tpjM5AHwwBpytPE5cdV/BPQ==} + '@vuepress/plugin-icon@2.0.0-rc.112': + resolution: {integrity: sha512-aufvjiIS9zHuTz2fQXZLCR6zSVtOifnCdnj+sQ8LYsT53OHikI1rNS8o0Dk68IyPP3eiFjdQ423+sKz17UPBYg==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-icon@2.0.0-rc.70': - resolution: {integrity: sha512-nQLr5LnfO9pTynqmHIWPXhUZpqzKaxP0JjFW1lbIDOn5F3gLZ04vxcAPvtrJYis0erwmlCR6/yx20u8bGvakIg==} + '@vuepress/plugin-links-check@2.0.0-rc.112': + resolution: {integrity: sha512-UyxFAhJSXnxdeeoAToGPUbOzWLupAlIInLFBV6ZlQkyaOLEusAdxrfRxR+xJc7DhCVbzstP87PJC8VvO36unSA==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-links-check@2.0.0-rc.70': - resolution: {integrity: sha512-ZqqU9pjtr7O0lvOOk6bxtWj5EvktL8PMegRpRv2a10uN0kQGXljGg5XihmC2EW3AlVBl3je9gw40+DVHocMDVw==} + '@vuepress/plugin-markdown-chart@2.0.0-rc.112': + resolution: {integrity: sha512-mvmtYKSwD9m5B0ElrLHhqlwudkJbKtz9NstS5CmZ2exFOBkOGQBDeE9kbZGf2vUxHYbCZQQzjqAJB2bIIb+VZA==} peerDependencies: - vuepress: 2.0.0-rc.19 + chart.js: ^4.4.7 + echarts: ^5.6.0 + flowchart.ts: ^3.0.1 + markmap-lib: ^0.18.11 + markmap-toolbar: ^0.18.10 + markmap-view: ^0.18.10 + mermaid: ^11.8.0 + vuepress: 2.0.0-rc.24 + peerDependenciesMeta: + chart.js: + optional: true + echarts: + optional: true + flowchart.ts: + optional: true + markmap-lib: + optional: true + markmap-toolbar: + optional: true + markmap-view: + optional: true + mermaid: + optional: true - '@vuepress/plugin-markdown-ext@2.0.0-rc.70': - resolution: {integrity: sha512-NwB2cAmATP8Nd3mBLPvLDANKXAFj2VxB9HZUOb9/Vrpt3+otSRQ17mBjUwnBllbnE9Yjz1zcAU6HjKuMdkJfEw==} + '@vuepress/plugin-markdown-ext@2.0.0-rc.112': + resolution: {integrity: sha512-fMaBKLmg/ux6s/PNDuIdBEogZOYys7sajZLnr7Xfp1gtQV/GnXAabBoBAINWbdy4Un0RRaMgLcqokR2AeS2poQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-markdown-hint@2.0.0-rc.70': - resolution: {integrity: sha512-yd9HAKRw9hcG8jsatSpA5H6CVFFm9c8eXBq1OGKsm/1kCCLcznKdc6bdnL6c24ieOrS5se0IL+daCoaklruscQ==} + '@vuepress/plugin-markdown-hint@2.0.0-rc.112': + resolution: {integrity: sha512-H4QCUIF3gvTh+/Etz0g3MBGCk48MLm9Dep/hJl2//Ke56lNSmldMac059itL8rzPQ4ntl0HoI55060e4zOprxw==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-markdown-image@2.0.0-rc.70': - resolution: {integrity: sha512-Ao0WnZ0tR3HRClDNhGIFi3+/lhgqrhoU1HWRsfnWx1ONcvTTNZfuR2TrLzKvkPiDzg1D05HZaFH3PggCun9V8Q==} + '@vuepress/plugin-markdown-image@2.0.0-rc.112': + resolution: {integrity: sha512-E2Qju3SKtCLvRkBM1ZvtBWvOZW+eoIr2n1ZBawxcj9k1Zt74vvEy0BP7pKOSP5Qu9bwY6W1MAnT3H+R3QaDP+g==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-markdown-include@2.0.0-rc.70': - resolution: {integrity: sha512-4C4HOieqdIThAF/zi6h5bWkw7l22qUn9vmR5PGPNmnQSZ2anr5L0+ulIYOOElGZxlXL35Io0FD749fXrV2alVg==} + '@vuepress/plugin-markdown-include@2.0.0-rc.112': + resolution: {integrity: sha512-zea8MlrUKbgAJm35Aqf/lDLz5Nu4LhVFV1C/IY0OlcvLwEbdyifPi/l1ZB+b2kfrW81GiuEb24a5Nr1JpDx2Gg==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-markdown-math@2.0.0-rc.70': - resolution: {integrity: sha512-PSqMqA7C5d6z5qfhIYbDgCZC3AL+P0644dIEgOMDG4Twg9S6LtDawkbILOshT0nb6RVLlA9gy/0ME5yIc/+zjw==} + '@vuepress/plugin-markdown-math@2.0.0-rc.112': + resolution: {integrity: sha512-ZsIT3UKokslL+NUrdV5xTaOfuqEn41ZIlIL4PfCCgCpvUap/ziHbpQizU3sVgciq88mDsYYteVqgBqXcQzNiig==} peerDependencies: - katex: ^0.16.10 + katex: ^0.16.21 mathjax-full: ^3.2.2 - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: katex: optional: true mathjax-full: optional: true - '@vuepress/plugin-markdown-stylize@2.0.0-rc.70': - resolution: {integrity: sha512-Z0PTI+ePEXpA41llpOpXiBgCJsJvkU9HAJ0cjfeWODMHJ0wa6/Y9aA3w2aXPzg6qBGaHxSAXymRJz51a4H53EA==} + '@vuepress/plugin-markdown-preview@2.0.0-rc.112': + resolution: {integrity: sha512-R4Hl0JwapFZbzYPl3kC90w+cN/uecBXhpFER2xkX4oz7fPVYfF4I252JgzIyF1LofSsQMob7EUxbSmReVeliIA==} + peerDependencies: + vuepress: 2.0.0-rc.24 + + '@vuepress/plugin-markdown-stylize@2.0.0-rc.112': + resolution: {integrity: sha512-M9wYDM1F/Qvo8jJgQcuhQbgrpZLLPe+KhkwBSKvSFOFD5QluEXBrd8S51eXSMlvLRJVE8VIj9Rh7TP9Q8wly/A==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-markdown-tab@2.0.0-rc.70': - resolution: {integrity: sha512-W/tRQ8dgPiOj94GUz9apWxJHWsLC88RNS10hRX9wBlSaqZ/S9YUcI4D8nWy0uf4KYo6PbOnucJL/y66uuhlrLw==} + '@vuepress/plugin-markdown-tab@2.0.0-rc.112': + resolution: {integrity: sha512-Dnyn6ezrbl8KP7XD+8duPVAQL/E0TZTb3O4bRO/SLJSnbrbwSlNfm/ra5Vv2SgYQV9CnpFo6I+y7dETNK49t7A==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-notice@2.0.0-rc.70': - resolution: {integrity: sha512-a+Z/xJopfm8OZ0SRon78D94RIpkhi8CNbFDUhQYPciS71b/pqN28OFdFPFWNU/PACcRoe3IMBcXWsq1wKZ9/Rg==} + '@vuepress/plugin-notice@2.0.0-rc.112': + resolution: {integrity: sha512-v6QRqWuH/42WNufosxu0FBUvGXh34j81Wiuio37DqSbMcgATkrPPEdXhMI27bg+zbXhms9UTukKJ4X8JJsN9Rg==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-nprogress@2.0.0-rc.70': - resolution: {integrity: sha512-WYhCs5X1vPbzQ5PhYTWzh5cCRjsCnE52qBlzIzftzM9SNG2Fm8tdMS3V7bmCzpg1IgALcIfwX/9r7uUVcby0OQ==} + '@vuepress/plugin-nprogress@2.0.0-rc.112': + resolution: {integrity: sha512-kNz7SvVx7Z09aQFf4iwQ3C9h1WZBuefa7cKyYpSrWYFciFU2do98SUg3C5Wi8ttJ7oPcM+NmSiGbjJrjwpncig==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-photo-swipe@2.0.0-rc.70': - resolution: {integrity: sha512-z3BlKQttCMycl3u/cBVO2XVdLGEI/1qtW8Rp9u4U5G6Utv3HU5/UL3GIZmz2mNP7l3k4mOE8wnnKaxDVbvIRug==} + '@vuepress/plugin-photo-swipe@2.0.0-rc.112': + resolution: {integrity: sha512-WkkPC9rjwAQCMuVwUqCl14hO8z2Odv5k1yF2pWH2XGBja5VyBJK5t+XUmS1ak7zcjTz40+AYmauglbXo06RUSQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-reading-time@2.0.0-rc.70': - resolution: {integrity: sha512-AemYL6ogRsstEnLiOEDcaMULJecpeftH0RpAqdUTcAPiy3gsWdn4kMgMHlvpgm1J99aE5w6d8G511Kx+i6JxAQ==} + '@vuepress/plugin-reading-time@2.0.0-rc.112': + resolution: {integrity: sha512-76t64Uvr+1ADAq1z/DbU9ftAXKhVOBjxGKplRkbffobyTQ0mrDjDBM2rArytQiK+8utDgGPTjblCt+oJkxovzg==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-redirect@2.0.0-rc.70': - resolution: {integrity: sha512-je16tvDeuphNz21hqdxQoKHCn/LVfwd6YtB3xNb5t/cEBUUAdB+byrwfUlpUVY3HWqFjr5Ot0RJuQkHFVmBJUA==} + '@vuepress/plugin-redirect@2.0.0-rc.112': + resolution: {integrity: sha512-IOSgVM3nUxO3zpQ7i4FY1kKM4A2I8iM9LCrCFALPrnvt1wfQ4SoTuCxqG3Z1BRgi30DzfMzoXsuVbMZkwk7n2g==} hasBin: true peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-rtl@2.0.0-rc.70': - resolution: {integrity: sha512-2odC4M3uRGZEKYFvDLRDwyQOwAJ8YFtRI0ZrDh0paNROqssWsK9JGpYlRSDCLedwtZuQYLp8L/NN44F0kyx+fw==} + '@vuepress/plugin-rtl@2.0.0-rc.112': + resolution: {integrity: sha512-wZwf1wE+FemynTECgXGOr7ly6p6hl3a2r39EQZLY7hIEp+MJIE8JKvP1EB2IuW0LCsEhnoSLX7wMC6EncUlnCQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-sass-palette@2.0.0-rc.70': - resolution: {integrity: sha512-wy17yNlmc0liQFJSoqc9XEu96Nk3daSof2ncXNuRGJnCsfXbwd0QZr6bNdZos0Zmk5+KolzIffeEBhOGnRw+VA==} + '@vuepress/plugin-sass-palette@2.0.0-rc.112': + resolution: {integrity: sha512-luqYhX2AlGRBwABpR/JgnVuAm+5yxGdxoXNe7+cNF2dSRZq47WVT2alHvyWqECpDHxgMjVyUQN5PmD1zDs01sg==} peerDependencies: - sass: ^1.80.3 - sass-embedded: ^1.80.3 - sass-loader: ^16.0.2 - vuepress: 2.0.0-rc.19 + sass: ^1.89.2 + sass-embedded: ^1.89.2 + sass-loader: ^16.0.5 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: sass: optional: true @@ -1082,45 +988,57 @@ packages: sass-loader: optional: true - '@vuepress/plugin-search@2.0.0-rc.70': - resolution: {integrity: sha512-Av8yFH4xeC4OUhit6NWMOJ50KwMd2J/LQzGT/e70o/oDn5wV7nomKU972mdnHILIpzin/HrJP0LhvSeXZrLrNQ==} + '@vuepress/plugin-search@2.0.0-rc.112': + resolution: {integrity: sha512-liQxClnwXRn3V8I3OORvS2/OwHSx2pi0c3F/V/ji++Zy4DVpSEzhMJAfHkHmo1KKzokqakSBiJz8bQudp5ZMFw==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-seo@2.0.0-rc.70': - resolution: {integrity: sha512-8l4BYboPcCQ96h53Wzmh7eCXOL3x9XzvNjude4pJHsuU4tSl/T0rsCyZI2fPOybKJQSPhahKMiM4sU1+e+/wQg==} + '@vuepress/plugin-seo@2.0.0-rc.112': + resolution: {integrity: sha512-WWZ0Dx1MxF9Mj6UVdB8TP5GozTNv51ZQQP6EAKYzprKCw0RVQYg5/tXWlg7IWcSw72go5iFiMBj5wZQigN+t4g==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-shiki@2.0.0-rc.70': - resolution: {integrity: sha512-N4E9mnpjUwcC2MRWdTFSGE3q3X7cQeG9HlBK5/Ts6M/qd15pn1q88cOdkemMGmUYGKBJv0PMlm2g2x8OTvjs6A==} + '@vuepress/plugin-shiki@2.0.0-rc.112': + resolution: {integrity: sha512-jXPJuAl9zNrYqdMgLRdAakrYCJcHJJCoIJ/73ODtejfU1+78s7PL6HheFEyakWC8MGyReGw+e0vJs+9NisXxIQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + '@vuepress/shiki-twoslash': 2.0.0-rc.112 + vuepress: 2.0.0-rc.24 + peerDependenciesMeta: + '@vuepress/shiki-twoslash': + optional: true - '@vuepress/plugin-sitemap@2.0.0-rc.70': - resolution: {integrity: sha512-TL9Oblicr1O9WnJnBqwMuC7VZHad6Z4pOHpEEYQKD2O9vRCnlEeP6f7xYF+xWjXXNjQasq6mYGPzTBboqL/PDA==} + '@vuepress/plugin-sitemap@2.0.0-rc.112': + resolution: {integrity: sha512-64a/Kpu+2zY8r7o5AqFbZ1M3VKp44Z3RR6mGcr/747BEzVSl7ULk5ctx7Smtqm6Z2sSLEEU1aC6ZAtV5I+jqeQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/plugin-theme-data@2.0.0-rc.70': - resolution: {integrity: sha512-xIxEvWZb+rZkUNUy4T2h1vA/YXMMflvvXRu3VHjozlnzjDqdICxFGBgqHMDdQ/9cSXKLGJNNLi4MPm4lzMuCRw==} + '@vuepress/plugin-theme-data@2.0.0-rc.112': + resolution: {integrity: sha512-QrCzB/wLxWmy76iEN140pZ1ZaigsFRimfGp1A65UOWAytEmkeRecEGBqZua4PDwiYOZQz/gf80xu5/SFsa8BAQ==} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - '@vuepress/shared@2.0.0-rc.19': - resolution: {integrity: sha512-xaDeZxX0Qetc2Y6/lrzO6M/40i3LmMm7Fk85bOftBBOaNehZ24RdsmIHBJDDv+bTUv+DBF++1/mOtbt6DBRzEA==} + '@vuepress/shared@2.0.0-rc.24': + resolution: {integrity: sha512-CAmJGMcDV5DnFEJ74f7IdCms2CBl8Md62uWbgAW8wEYiYanjRM8Rr1oIrz+cWoBSnWPf1HyPR3JoKYgw7OW4bw==} - '@vuepress/utils@2.0.0-rc.19': - resolution: {integrity: sha512-cgzk8/aJquZKgFMNTuqdjbU5NrCzrPmdTyhYBcmliL/6N/He1OTWn3PD9QWUGJNODb1sPRJpklZnCpU07waLmg==} + '@vuepress/utils@2.0.0-rc.24': + resolution: {integrity: sha512-7D6o12Y64efevSdp+k84ivMZ3dSkZjQwbn79ywbHVbYtoZikvnpTE5GuG7lFOLcF3qZWQVqi7sRJVJdZnH9DuA==} - '@vueuse/core@12.4.0': - resolution: {integrity: sha512-XnjQYcJwCsyXyIafyA6SvyN/OBtfPnjvJmbxNxQjCcyWD198urwm5TYvIUUyAxEAN0K7HJggOgT15cOlWFyLeA==} + '@vueuse/core@13.6.0': + resolution: {integrity: sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==} + peerDependencies: + vue: ^3.5.0 - '@vueuse/metadata@12.4.0': - resolution: {integrity: sha512-AhPuHs/qtYrKHUlEoNO6zCXufu8OgbR8S/n2oMw1OQuBQJ3+HOLQ+EpvXs+feOlZMa0p8QVvDWNlmcJJY8rW2g==} + '@vueuse/metadata@13.6.0': + resolution: {integrity: sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==} - '@vueuse/shared@12.4.0': - resolution: {integrity: sha512-9yLgbHVIF12OSCojnjTIoZL1+UA10+O4E1aD6Hpfo/DKVm5o3SZIwz6CupqGy3+IcKI8d6Jnl26EQj/YucnW0Q==} + '@vueuse/shared@13.6.0': + resolution: {integrity: sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==} + peerDependencies: + vue: ^3.5.0 + + '@xmldom/xmldom@0.9.8': + resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} + engines: {node: '>=14.6'} abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1133,8 +1051,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} aggregate-error@3.1.0: @@ -1161,8 +1079,8 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} @@ -1178,45 +1096,48 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} balloon-css@1.2.0: resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==} - bcrypt-ts@5.0.3: - resolution: {integrity: sha512-2FcgD12xPbwCoe5i9/HK0jJ1xA1m+QfC1e6htG9Bl/hNOnLyaFmQSlqLKcfe3QdnoMPKpKEGFCbESBTg+SJNOw==} - engines: {node: '>=18'} + bcrypt-ts@7.1.0: + resolution: {integrity: sha512-t/Dqr9YzYmn/+oPQBgotBPUuezpZD5CPBwapM5Ep1p3zsLmEycMdXOfZpWbztSBWJ41DlB7EluJBUDsAGSiUeQ==} + engines: {node: '>=20'} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - birpc@0.2.19: - resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==} + birpc@2.5.0: + resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + browserslist@4.25.2: + resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1235,14 +1156,14 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001692: - resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} + caniuse-lite@1.0.30001733: + resolution: {integrity: sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + chalk@5.5.0: + resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} character-entities-html4@2.1.0: @@ -1260,9 +1181,9 @@ packages: cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - cheerio@1.0.0: - resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} - engines: {node: '>=18.17'} + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -1308,18 +1229,18 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@13.0.0: - resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - commander@9.2.0: - resolution: {integrity: sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==} - engines: {node: ^12.20.0 || >=14} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1342,21 +1263,18 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1368,8 +1286,8 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -1378,8 +1296,8 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} devlop@1.1.0: @@ -1404,11 +1322,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.80: - resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} - - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + electron-to-chromium@1.5.199: + resolution: {integrity: sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1419,8 +1334,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encoding-sniffer@0.2.0: - resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} @@ -1429,6 +1344,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -1441,13 +1360,8 @@ packages: err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} engines: {node: '>=18'} hasBin: true @@ -1467,31 +1381,34 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - execa@9.5.2: - resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} - engines: {node: ^18.19.0 || >=20.5.0} - - exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fastq@1.18.0: - resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1500,15 +1417,15 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + fs-extra@11.3.1: + resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} engines: {node: '>=14.14'} fs-minipass@2.1.0: @@ -1540,10 +1457,6 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - giscus@1.6.0: resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==} @@ -1563,6 +1476,10 @@ packages: resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1580,23 +1497,38 @@ packages: hash-sum@2.0.0: resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} - hast-util-to-html@9.0.4: - resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==} + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - htmlparser2@9.1.0: - resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} @@ -1610,10 +1542,6 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-signals@8.0.0: - resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} - engines: {node: '>=18.18.0'} - husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -1627,8 +1555,12 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - immutable@5.0.3: - resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -1696,10 +1628,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -1739,8 +1667,8 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - katex@0.16.20: - resolution: {integrity: sha512-jjuLaMGD/7P8jUTpdKhA9IoqnH+yMFB3sdAFtq5QdAqeP2PjiSbnC3EaguKPNtv6dXXanHxp1ckwvF4a86LBig==} + katex@0.16.22: + resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} hasBin: true kind-of@6.0.3: @@ -1754,14 +1682,14 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - lit-element@4.1.1: - resolution: {integrity: sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==} + lit-element@4.2.1: + resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} - lit-html@3.2.1: - resolution: {integrity: sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==} + lit-html@3.3.1: + resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} - lit@3.2.1: - resolution: {integrity: sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==} + lit@3.3.1: + resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} @@ -1891,8 +1819,8 @@ packages: micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.3: - resolution: {integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==} + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} @@ -1973,13 +1901,13 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.0.9: - resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} engines: {node: ^18 || >=20} hasBin: true @@ -1987,8 +1915,8 @@ packages: resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} engines: {node: '>= 0.6'} - node-addon-api@8.3.0: - resolution: {integrity: sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==} + node-addon-api@8.5.0: + resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} node-fetch@2.7.0: @@ -2030,10 +1958,6 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. @@ -2052,11 +1976,14 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - oniguruma-to-es@1.0.0: - resolution: {integrity: sha512-kihvp0O4lFwf5tZMkfanwQLIZ9ORe9OeOFgZonH0BQeThgwfJiaZFeOfvvJVnJIM9TiVmx0RDD35hUJDR0++rQ==} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} - ora@8.1.1: - resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} p-limit@2.3.0: @@ -2081,18 +2008,14 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} parse5-parser-stream@7.1.2: resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - parse5@7.2.1: - resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -2106,10 +2029,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -2118,6 +2037,10 @@ packages: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -2132,6 +2055,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -2157,8 +2084,8 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.0: - resolution: {integrity: sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} prettier@3.4.2: @@ -2166,10 +2093,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-ms@9.2.0: - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} - engines: {node: '>=18'} - proc-log@4.2.0: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -2178,8 +2101,8 @@ packages: resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} engines: {node: '>=10'} - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} @@ -2201,18 +2124,27 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.1: - resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - regex-recursion@5.1.1: - resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - regex@5.1.1: - resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -2229,8 +2161,8 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: @@ -2241,16 +2173,16 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rollup@4.30.1: - resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2258,128 +2190,104 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass-embedded-android-arm64@1.83.1: - resolution: {integrity: sha512-S63rlLPGCA9FCqYYOobDJrwcuBX0zbSOl7y0jT9DlfqeqNOkC6NIT1id6RpMFCs3uhd4gbBS2E/5WPv5J5qwbw==} + sass-embedded-android-arm64@1.89.2: + resolution: {integrity: sha512-+pq7a7AUpItNyPu61sRlP6G2A8pSPpyazASb+8AK2pVlFayCSPAEgpwpCE9A2/Xj86xJZeMizzKUHxM2CBCUxA==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [android] - sass-embedded-android-arm@1.83.1: - resolution: {integrity: sha512-FKfrmwDG84L5cfn8fmIew47qnCFFUdcoOTCzOw8ROItkRhLLH0hnIm6gEpG5T6OFf6kxzUxvE9D0FvYQUznZrw==} + sass-embedded-android-arm@1.89.2: + resolution: {integrity: sha512-oHAPTboBHRZlDBhyRB6dvDKh4KvFs+DZibDHXbkSI6dBZxMTT+Yb2ivocHnctVGucKTLQeT7+OM5DjWHyynL/A==} engines: {node: '>=14.0.0'} cpu: [arm] os: [android] - sass-embedded-android-ia32@1.83.1: - resolution: {integrity: sha512-AGlY2vFLJhF2hN0qOz12f4eDs6x0b5BUapOpgfRrqQLHIfJhxkvi39bInsiBgQ57U0jb4I7AaS2e2e+sj7+Rqw==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [android] - - sass-embedded-android-riscv64@1.83.1: - resolution: {integrity: sha512-OyU4AnfAUVd/wBaT60XvHidmQdaEsVUnxvI71oyPM/id1v97aWTZX3SmGkwGb7uA/q6Soo2uNalgvOSNJn7PwA==} + sass-embedded-android-riscv64@1.89.2: + resolution: {integrity: sha512-HfJJWp/S6XSYvlGAqNdakeEMPOdhBkj2s2lN6SHnON54rahKem+z9pUbCriUJfM65Z90lakdGuOfidY61R9TYg==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [android] - sass-embedded-android-x64@1.83.1: - resolution: {integrity: sha512-NY5rwffhF4TnhXVErZnfFIjHqU3MNoWxCuSHumRN3dDI8hp8+IF59W5+Qw9AARlTXvyb+D0u5653aLSea5F40w==} + sass-embedded-android-x64@1.89.2: + resolution: {integrity: sha512-BGPzq53VH5z5HN8de6jfMqJjnRe1E6sfnCWFd4pK+CAiuM7iw5Fx6BQZu3ikfI1l2GY0y6pRXzsVLdp/j4EKEA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [android] - sass-embedded-darwin-arm64@1.83.1: - resolution: {integrity: sha512-w1SBcSkIgIWgUfB7IKcPoTbSwnS3Kag5PVv3e3xfW6ZCsDweYZLQntUd2WGgaoekdm1uIbVuvPxnDH2t880iGQ==} + sass-embedded-darwin-arm64@1.89.2: + resolution: {integrity: sha512-UCm3RL/tzMpG7DsubARsvGUNXC5pgfQvP+RRFJo9XPIi6elopY5B6H4m9dRYDpHA+scjVthdiDwkPYr9+S/KGw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [darwin] - sass-embedded-darwin-x64@1.83.1: - resolution: {integrity: sha512-RWrmLtUhEP5kvcGOAFdr99/ebZ/eW9z3FAktLldvgl2k96WSTC1Zr2ctL0E+Y+H3uLahEZsshIFk6RkVIRKIsA==} + sass-embedded-darwin-x64@1.89.2: + resolution: {integrity: sha512-D9WxtDY5VYtMApXRuhQK9VkPHB8R79NIIR6xxVlN2MIdEid/TZWi1MHNweieETXhWGrKhRKglwnHxxyKdJYMnA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [darwin] - sass-embedded-linux-arm64@1.83.1: - resolution: {integrity: sha512-HVIytzj8OO18fmBY6SVRIYErcJ+Nd9a5RNF6uArav/CqvwPLATlUV8dwqSyWQIzSsQUhDF/vFIlJIoNLKKzD3A==} + sass-embedded-linux-arm64@1.89.2: + resolution: {integrity: sha512-2N4WW5LLsbtrWUJ7iTpjvhajGIbmDR18ZzYRywHdMLpfdPApuHPMDF5CYzHbS+LLx2UAx7CFKBnj5LLjY6eFgQ==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - sass-embedded-linux-arm@1.83.1: - resolution: {integrity: sha512-y7rHuRgjg2YM284rin068PsEdthPljSGb653Slut5Wba4A2IP11UNVraSl6Je2AYTuoPRjQX0g7XdsrjXlzC3g==} + sass-embedded-linux-arm@1.89.2: + resolution: {integrity: sha512-leP0t5U4r95dc90o8TCWfxNXwMAsQhpWxTkdtySDpngoqtTy3miMd7EYNYd1znI0FN1CBaUvbdCMbnbPwygDlA==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - sass-embedded-linux-ia32@1.83.1: - resolution: {integrity: sha512-/pc+jHllyvfaYYLTRCoXseRc4+V3Z7IDPqsviTcfVdICAoR9mgK2RtIuIZanhm1NP/lDylDOgvj1NtjcA2dNvg==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [linux] - - sass-embedded-linux-musl-arm64@1.83.1: - resolution: {integrity: sha512-wjSIYYqdIQp3DjliSTYNFg04TVqQf/3Up/Stahol0Qf/TTjLkjHHtT2jnDaZI5GclHi2PVJqQF3wEGB8bGJMzQ==} + sass-embedded-linux-musl-arm64@1.89.2: + resolution: {integrity: sha512-nTyuaBX6U1A/cG7WJh0pKD1gY8hbg1m2SnzsyoFG+exQ0lBX/lwTLHq3nyhF+0atv7YYhYKbmfz+sjPP8CZ9lw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - sass-embedded-linux-musl-arm@1.83.1: - resolution: {integrity: sha512-sFM8GXOVoeR91j9MiwNRcFXRpTA7u4185SaGuvUjcRMb84mHvtWOJPGDvgZqbWdVClBRJp6J7+CShliWngy/og==} + sass-embedded-linux-musl-arm@1.89.2: + resolution: {integrity: sha512-Z6gG2FiVEEdxYHRi2sS5VIYBmp17351bWtOCUZ/thBM66+e70yiN6Eyqjz80DjL8haRUegNQgy9ZJqsLAAmr9g==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - sass-embedded-linux-musl-ia32@1.83.1: - resolution: {integrity: sha512-iwhTH5gwmoGt3VH6dn4WV8N6eWvthKAvUX5XPURq7e9KEsc7QP8YNHagwaAJh7TAPopb32buyEg6oaUmzxUI+Q==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [linux] - - sass-embedded-linux-musl-riscv64@1.83.1: - resolution: {integrity: sha512-FjFNWHU1n0Q6GpK1lAHQL5WmzlPjL8DTVLkYW2A/dq8EsutAdi3GfpeyWZk9bte8kyWdmPUWG3BHlnQl22xdoA==} + sass-embedded-linux-musl-riscv64@1.89.2: + resolution: {integrity: sha512-N6oul+qALO0SwGY8JW7H/Vs0oZIMrRMBM4GqX3AjM/6y8JsJRxkAwnfd0fDyK+aICMFarDqQonQNIx99gdTZqw==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - sass-embedded-linux-musl-x64@1.83.1: - resolution: {integrity: sha512-BUfYR5TIDvgGHWhxSIKwTJocXU88ECZ0BW89RJqtvr7m83fKdf5ylTFCOieU7BwcA7SORUeZzcQzVFIdPUM3BQ==} + sass-embedded-linux-musl-x64@1.89.2: + resolution: {integrity: sha512-K+FmWcdj/uyP8GiG9foxOCPfb5OAZG0uSVq80DKgVSC0U44AdGjvAvVZkrgFEcZ6cCqlNC2JfYmslB5iqdL7tg==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - sass-embedded-linux-riscv64@1.83.1: - resolution: {integrity: sha512-KOBGSpMrJi8y+H+za3vAAVQImPUvQa5eUrvTbbOl+wkU7WAGhOu8xrxgmYYiz3pZVBBcfRjz4I2jBcDFKJmWSw==} + sass-embedded-linux-riscv64@1.89.2: + resolution: {integrity: sha512-g9nTbnD/3yhOaskeqeBQETbtfDQWRgsjHok6bn7DdAuwBsyrR3JlSFyqKc46pn9Xxd9SQQZU8AzM4IR+sY0A0w==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - sass-embedded-linux-x64@1.83.1: - resolution: {integrity: sha512-swUsMHKqlEU9dZQ/I5WADDaXz+QkmJS27x/Oeh+oz41YgZ0ppKd0l4Vwjn0LgOQn+rxH1zLFv6xXDycvj68F/w==} + sass-embedded-linux-x64@1.89.2: + resolution: {integrity: sha512-Ax7dKvzncyQzIl4r7012KCMBvJzOz4uwSNoyoM5IV6y5I1f5hEwI25+U4WfuTqdkv42taCMgpjZbh9ERr6JVMQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - sass-embedded-win32-arm64@1.83.1: - resolution: {integrity: sha512-6lONEBN5TaFD5L/y68zUugryXqm4RAFuLdaOPeZQRu+7ay/AmfhtFYfE5gRssnIcIx1nlcoq7zA3UX+SN2jo1Q==} + sass-embedded-win32-arm64@1.89.2: + resolution: {integrity: sha512-j96iJni50ZUsfD6tRxDQE2QSYQ2WrfHxeiyAXf41Kw0V4w5KYR/Sf6rCZQLMTUOHnD16qTMVpQi20LQSqf4WGg==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [win32] - sass-embedded-win32-ia32@1.83.1: - resolution: {integrity: sha512-HxZDkAE9n6Gb8Rz6xd67VHuo5FkUSQ4xPb7cHKa4pE0ndwH5Oc0uEhbqjJobpgmnuTm1rQYNU2nof1sFhy2MFA==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [win32] - - sass-embedded-win32-x64@1.83.1: - resolution: {integrity: sha512-5Q0aPfUaqRek8Ee1AqTUIC0o6yQSA8QwyhCgh7upsnHG3Ltm8pkJOYjzm+UgYPJeoMNppDjdDlRGQISE7qzd4g==} + sass-embedded-win32-x64@1.89.2: + resolution: {integrity: sha512-cS2j5ljdkQsb4PaORiClaVYynE9OAPZG/XjbOMxpQmjRIf7UroY4PEIH+Waf+y47PfXFX9SyxhYuw2NIKGbEng==} engines: {node: '>=14.0.0'} cpu: [x64] os: [win32] - sass-embedded@1.83.1: - resolution: {integrity: sha512-LdKG6nxLEzpXbMUt0if12PhUNonGvy91n7IWHOZRZjvA6AWm9oVdhpO+KEXN/Sc+jjGvQeQcav9+Z8DwmII/pA==} + sass-embedded@1.89.2: + resolution: {integrity: sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA==} engines: {node: '>=16.0.0'} hasBin: true @@ -2394,8 +2302,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true @@ -2410,8 +2318,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.26.2: - resolution: {integrity: sha512-iP7u2NA9A6JwRRCkIUREEX2cMhlYV5EBmbbSlfSRvPThwca8HBRbVkWuNWW+kw9+i6BSUZqqG6YeUs5dC2SjZw==} + shiki@3.9.2: + resolution: {integrity: sha512-t6NKl5e/zGTvw/IyftLcumolgOczhuroqwXngDeMqJ3h3EQiTY/7wmfgPlsmloD8oYfqkEDqxiaH37Pjm1zUhQ==} signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -2437,8 +2345,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + socks@2.8.6: + resolution: {integrity: sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -2452,8 +2360,8 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} - speech-rule-engine@4.0.7: - resolution: {integrity: sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==} + speech-rule-engine@4.1.2: + resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==} hasBin: true sprintf-js@1.0.3: @@ -2500,10 +2408,6 @@ packages: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - superjson@2.2.2: resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} engines: {node: '>=16'} @@ -2520,10 +2424,18 @@ packages: resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==} engines: {node: '>=16.0.0'} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2534,18 +2446,21 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@6.21.0: - resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} - engines: {node: '>=18.17'} + undici@7.13.0: + resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==} + engines: {node: '>=20.18.1'} unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} @@ -2555,6 +2470,9 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -2586,8 +2504,8 @@ packages: resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} engines: {node: '>=4'} - update-browserslist-db@1.1.2: - resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2598,25 +2516,28 @@ packages: varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@6.0.7: - resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@7.0.6: + resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 jiti: '>=1.21.0' - less: '*' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -2644,32 +2565,32 @@ packages: yaml: optional: true - vue-router@4.5.0: - resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} peerDependencies: vue: ^3.2.0 - vue@3.5.13: - resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + vue@3.5.18: + resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - vuepress-plugin-components@2.0.0-rc.68: - resolution: {integrity: sha512-JGll1AC40jMSvOlWNQUUN+ZTBFqgoOmuf2QAVWMPY3+D0a9xlBMxSsrK7Rvn0JrE+0txHyOlGJMNUT+f3/OMZQ==} - engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + vuepress-plugin-components@2.0.0-rc.94: + resolution: {integrity: sha512-U6s7qWG1ETm7yvshD+gWe1SrTezjaFvW8gUvmmAZEoLTV5Pd+FC7BR7W8syPieOzUzOVjF2UeO5zVsZ/M9jp4A==} + engines: {node: '>= 20.6.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: artplayer: ^5.0.0 dashjs: 4.7.4 hls.js: ^1.4.12 mpegts.js: ^1.7.3 - sass: ^1.81.0 - sass-embedded: ^1.81.0 - sass-loader: ^16.0.2 + sass: ^1.89.2 + sass-embedded: ^1.89.2 + sass-loader: ^16.0.5 vidstack: ^1.12.9 - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: artplayer: optional: true @@ -2688,43 +2609,22 @@ packages: vidstack: optional: true - vuepress-plugin-md-enhance@2.0.0-rc.68: - resolution: {integrity: sha512-fi0bkKIEAFihOqBDAYmrQRPImXhfRdx5mi2blX/lvSl7vCun+FZV6NHz8mvGQNh0DcT6vn5ksCNjyih7lkrtEw==} - engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + vuepress-plugin-md-enhance@2.0.0-rc.94: + resolution: {integrity: sha512-oI9e3JvdcpQeK3w1nIowl+Tn49euLxicrIg1uKf0mUd7JB1ofo1XDuxBLtRASgRoqCRiiQsq1trYnyO9CiPGpQ==} + engines: {node: '>= 20.6.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: '@vue/repl': ^4.1.1 - chart.js: ^4.0.0 - echarts: ^5.0.0 - flowchart.ts: ^3.0.0 kotlin-playground: ^1.23.0 - markmap-lib: ^0.18.5 - markmap-toolbar: ^0.18.5 - markmap-view: ^0.18.5 - mermaid: ^11.2.0 sandpack-vue3: ^3.0.0 - sass: ^1.81.0 - sass-embedded: ^1.81.0 - sass-loader: ^16.0.2 - vuepress: 2.0.0-rc.19 + sass: ^1.89.2 + sass-embedded: ^1.89.2 + sass-loader: ^16.0.5 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: '@vue/repl': optional: true - chart.js: - optional: true - echarts: - optional: true - flowchart.ts: - optional: true kotlin-playground: optional: true - markmap-lib: - optional: true - markmap-toolbar: - optional: true - markmap-view: - optional: true - mermaid: - optional: true sandpack-vue3: optional: true sass: @@ -2734,34 +2634,38 @@ packages: sass-loader: optional: true - vuepress-shared@2.0.0-rc.68: - resolution: {integrity: sha512-wqKktaUUvEC6qMWNXuYo7uh5oEzYLhd7sY3ACvj+nu5JgXgQdk0sVoTN3vMp6PmVFmINjJfJB1zzn778hcOFGA==} - engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + vuepress-shared@2.0.0-rc.94: + resolution: {integrity: sha512-ZlVIeRkCY7jt8QpELr3i5PGFkWk7VkTG1emn6BuOE2Hd+tI8zZH4a6lCGqtkhpu093tpM+tSANiR83RRNQCCCw==} + engines: {node: '>= 20.6.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: - vuepress: 2.0.0-rc.19 + vuepress: 2.0.0-rc.24 - vuepress-theme-hope@2.0.0-rc.68: - resolution: {integrity: sha512-4mMq/VACqkFZx/5gGp+5QnH9p3GYroj75rU7LGIuTIc1hCrsQMfdM/HTvfvoRnBoWUedT+KEAy17Z9ThNOSyDQ==} - engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + vuepress-theme-hope@2.0.0-rc.94: + resolution: {integrity: sha512-FA35vxdUY3tk1ORDSCTTozttoTNSmdCTms3v7871vUFeKmQ+MY+iCFGDVMeoCEcuCMGJ7F0+bcCUkH3ohFcdgQ==} + engines: {node: '>= 20.6.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: - '@vuepress/plugin-docsearch': 2.0.0-rc.70 - '@vuepress/plugin-feed': 2.0.0-rc.70 - '@vuepress/plugin-prismjs': 2.0.0-rc.70 - '@vuepress/plugin-pwa': 2.0.0-rc.70 - '@vuepress/plugin-revealjs': 2.0.0-rc.70 - '@vuepress/plugin-search': 2.0.0-rc.70 - '@vuepress/plugin-slimsearch': 2.0.0-rc.70 - '@vuepress/plugin-watermark': 2.0.0-rc.70 - nodejs-jieba: ^0.2.1 - sass: ^1.81.0 - sass-embedded: ^1.81.0 - sass-loader: ^16.0.2 - vuepress: 2.0.0-rc.19 + '@vuepress/plugin-docsearch': 2.0.0-rc.112 + '@vuepress/plugin-feed': 2.0.0-rc.112 + '@vuepress/plugin-meilisearch': 2.0.0-rc.112 + '@vuepress/plugin-prismjs': 2.0.0-rc.112 + '@vuepress/plugin-pwa': 2.0.0-rc.112 + '@vuepress/plugin-revealjs': 2.0.0-rc.112 + '@vuepress/plugin-search': 2.0.0-rc.112 + '@vuepress/plugin-slimsearch': 2.0.0-rc.112 + '@vuepress/plugin-watermark': 2.0.0-rc.112 + '@vuepress/shiki-twoslash': 2.0.0-rc.112 + nodejs-jieba: ^0.2.1 || ^0.3.0 + sass: ^1.89.2 + sass-embedded: ^1.89.2 + sass-loader: ^16.0.5 + vuepress: 2.0.0-rc.24 peerDependenciesMeta: '@vuepress/plugin-docsearch': optional: true '@vuepress/plugin-feed': optional: true + '@vuepress/plugin-meilisearch': + optional: true '@vuepress/plugin-prismjs': optional: true '@vuepress/plugin-pwa': @@ -2774,6 +2678,8 @@ packages: optional: true '@vuepress/plugin-watermark': optional: true + '@vuepress/shiki-twoslash': + optional: true nodejs-jieba: optional: true sass: @@ -2783,20 +2689,23 @@ packages: sass-loader: optional: true - vuepress@2.0.0-rc.19: - resolution: {integrity: sha512-JDeuPTu14Kprdqx2geAryjFJvUzVaMnOLewlAgwVuZTygDWb8cgXhu9/p6rqzzdHETtIrvjbASBhH7JPyqmxmA==} - engines: {node: ^18.19.0 || >=20.4.0} + vuepress@2.0.0-rc.24: + resolution: {integrity: sha512-56O9fAj3Fr1ezngeHDGyp5I1fWxBnP6gaGerjYjPNtr2RteSZtnqL/fQDzmiw5rFpuMVlfOTXESvQjQUlio8PQ==} + engines: {node: ^20.9.0 || >=22.0.0} hasBin: true peerDependencies: - '@vuepress/bundler-vite': 2.0.0-rc.19 - '@vuepress/bundler-webpack': 2.0.0-rc.19 - vue: ^3.5.0 + '@vuepress/bundler-vite': 2.0.0-rc.24 + '@vuepress/bundler-webpack': 2.0.0-rc.24 + vue: ^3.5.17 peerDependenciesMeta: '@vuepress/bundler-vite': optional: true '@vuepress/bundler-webpack': optional: true + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -2849,10 +2758,6 @@ packages: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true - xmldom-sre@0.1.31: - resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==} - engines: {node: '>=0.1'} - y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -2867,172 +2772,102 @@ packages: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} - yoctocolors@2.1.1: - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} - engines: {node: '>=18'} - zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/parser@7.26.5': + '@babel/parser@7.28.0': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.28.2 - '@babel/types@7.26.5': + '@babel/types@7.28.2': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - - '@bufbuild/protobuf@2.2.3': {} - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.24.2': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 - '@esbuild/android-arm64@0.24.2': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.24.2': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.24.2': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.24.2': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.24.2': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.24.2': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.24.2': - optional: true + '@bufbuild/protobuf@2.6.3': {} - '@esbuild/linux-arm64@0.21.5': + '@esbuild/aix-ppc64@0.25.8': optional: true - '@esbuild/linux-arm64@0.24.2': + '@esbuild/android-arm64@0.25.8': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/android-arm@0.25.8': optional: true - '@esbuild/linux-arm@0.24.2': + '@esbuild/android-x64@0.25.8': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/darwin-arm64@0.25.8': optional: true - '@esbuild/linux-ia32@0.24.2': + '@esbuild/darwin-x64@0.25.8': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/freebsd-arm64@0.25.8': optional: true - '@esbuild/linux-loong64@0.24.2': + '@esbuild/freebsd-x64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-arm64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.24.2': + '@esbuild/linux-arm@0.25.8': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ia32@0.25.8': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/linux-loong64@0.25.8': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-mips64el@0.25.8': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/linux-ppc64@0.25.8': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-riscv64@0.25.8': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/linux-s390x@0.25.8': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.25.8': optional: true - '@esbuild/linux-x64@0.24.2': + '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-arm64@0.24.2': + '@esbuild/netbsd-x64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.24.2': + '@esbuild/openbsd-x64@0.25.8': optional: true - '@esbuild/openbsd-arm64@0.24.2': + '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.24.2': + '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/win32-ia32@0.25.8': optional: true - '@esbuild/sunos-x64@0.24.2': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.24.2': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.24.2': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.24.2': + '@esbuild/win32-x64@0.25.8': optional: true '@isaacs/cliui@8.0.2': @@ -3045,238 +2880,238 @@ snapshots: wrap-ansi-cjs: wrap-ansi@7.0.0 optional: true - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.4': {} - '@lit-labs/ssr-dom-shim@1.3.0': {} + '@lit-labs/ssr-dom-shim@1.4.0': {} - '@lit/reactive-element@2.0.4': + '@lit/reactive-element@2.1.1': dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 + '@lit-labs/ssr-dom-shim': 1.4.0 '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': dependencies: - detect-libc: 2.0.3 + detect-libc: 2.0.4 https-proxy-agent: 5.0.1 make-dir: 3.1.0 node-fetch: 2.7.0(encoding@0.1.13) nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.6.3 + semver: 7.7.2 tar: 6.2.1 transitivePeerDependencies: - encoding - supports-color optional: true - '@mdit-vue/plugin-component@2.1.3': + '@mdit-vue/plugin-component@2.1.4': dependencies: '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/plugin-frontmatter@2.1.3': + '@mdit-vue/plugin-frontmatter@2.1.4': dependencies: - '@mdit-vue/types': 2.1.0 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 gray-matter: 4.0.3 markdown-it: 14.1.0 - '@mdit-vue/plugin-headers@2.1.3': + '@mdit-vue/plugin-headers@2.1.4': dependencies: - '@mdit-vue/shared': 2.1.3 - '@mdit-vue/types': 2.1.0 + '@mdit-vue/shared': 2.1.4 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/plugin-sfc@2.1.3': + '@mdit-vue/plugin-sfc@2.1.4': dependencies: - '@mdit-vue/types': 2.1.0 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/plugin-title@2.1.3': + '@mdit-vue/plugin-title@2.1.4': dependencies: - '@mdit-vue/shared': 2.1.3 - '@mdit-vue/types': 2.1.0 + '@mdit-vue/shared': 2.1.4 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/plugin-toc@2.1.3': + '@mdit-vue/plugin-toc@2.1.4': dependencies: - '@mdit-vue/shared': 2.1.3 - '@mdit-vue/types': 2.1.0 + '@mdit-vue/shared': 2.1.4 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/shared@2.1.3': + '@mdit-vue/shared@2.1.4': dependencies: - '@mdit-vue/types': 2.1.0 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit-vue/types@2.1.0': {} + '@mdit-vue/types@2.1.4': {} - '@mdit/helper@0.16.0(markdown-it@14.1.0)': + '@mdit/helper@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-alert@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-alert@0.22.2(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-align@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-align@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/plugin-container': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-container': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-attrs@0.16.2(markdown-it@14.1.0)': + '@mdit/plugin-attrs@0.23.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-container@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-container@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-demo@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-demo@0.22.2(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-figure@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-figure@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-footnote@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-footnote@0.22.2(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 markdown-it: 14.1.0 - '@mdit/plugin-icon@0.16.5(markdown-it@14.1.0)': + '@mdit/plugin-icon@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-img-lazyload@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-img-lazyload@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-img-mark@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-img-mark@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-img-size@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-img-size@0.22.2(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-include@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-include@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 upath: 2.0.1 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-katex-slim@0.16.2(katex@0.16.20)(markdown-it@14.1.0)': + '@mdit/plugin-katex-slim@0.23.1(katex@0.16.22)(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-tex': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-tex': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: - katex: 0.16.20 + katex: 0.16.22 markdown-it: 14.1.0 - '@mdit/plugin-mark@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-mark@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-mathjax-slim@0.16.0(markdown-it@14.1.0)(mathjax-full@3.2.2)': + '@mdit/plugin-mathjax-slim@0.23.1(markdown-it@14.1.0)(mathjax-full@3.2.2)': dependencies: - '@mdit/plugin-tex': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-tex': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 upath: 2.0.1 optionalDependencies: markdown-it: 14.1.0 mathjax-full: 3.2.2 - '@mdit/plugin-plantuml@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-plantuml@0.22.2(markdown-it@14.1.0)': dependencies: - '@mdit/plugin-uml': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-uml': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-spoiler@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-spoiler@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-stylize@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-stylize@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-sub@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-sub@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-sup@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-sup@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-tab@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-tab@0.22.2(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-tasklist@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-tasklist@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-tex@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-tex@0.22.1(markdown-it@14.1.0)': dependencies: '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 - '@mdit/plugin-uml@0.16.0(markdown-it@14.1.0)': + '@mdit/plugin-uml@0.22.1(markdown-it@14.1.0)': dependencies: - '@mdit/helper': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 optionalDependencies: markdown-it: 14.1.0 @@ -3291,11 +3126,11 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.18.0 + fastq: 1.19.1 '@npmcli/agent@2.2.2': dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 10.4.3 @@ -3306,126 +3141,128 @@ snapshots: '@npmcli/fs@3.1.1': dependencies: - semver: 7.6.3 + semver: 7.7.2 optional: true '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.30.1': + '@pkgr/core@0.2.9': {} + + '@rolldown/pluginutils@1.0.0-beta.29': {} + + '@rollup/rollup-android-arm-eabi@4.46.2': optional: true - '@rollup/rollup-android-arm64@4.30.1': + '@rollup/rollup-android-arm64@4.46.2': optional: true - '@rollup/rollup-darwin-arm64@4.30.1': + '@rollup/rollup-darwin-arm64@4.46.2': optional: true - '@rollup/rollup-darwin-x64@4.30.1': + '@rollup/rollup-darwin-x64@4.46.2': optional: true - '@rollup/rollup-freebsd-arm64@4.30.1': + '@rollup/rollup-freebsd-arm64@4.46.2': optional: true - '@rollup/rollup-freebsd-x64@4.30.1': + '@rollup/rollup-freebsd-x64@4.46.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.1': + '@rollup/rollup-linux-arm-musleabihf@4.46.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.1': + '@rollup/rollup-linux-arm64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.30.1': + '@rollup/rollup-linux-arm64-musl@4.46.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + '@rollup/rollup-linux-ppc64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.30.1': + '@rollup/rollup-linux-riscv64-gnu@4.46.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.30.1': + '@rollup/rollup-linux-riscv64-musl@4.46.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.30.1': + '@rollup/rollup-linux-s390x-gnu@4.46.2': optional: true - '@rollup/rollup-linux-x64-musl@4.30.1': + '@rollup/rollup-linux-x64-gnu@4.46.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.30.1': + '@rollup/rollup-linux-x64-musl@4.46.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.30.1': + '@rollup/rollup-win32-arm64-msvc@4.46.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.30.1': + '@rollup/rollup-win32-ia32-msvc@4.46.2': optional: true - '@sec-ant/readable-stream@0.4.1': {} + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true - '@shikijs/core@1.26.2': + '@shikijs/core@3.9.2': dependencies: - '@shikijs/engine-javascript': 1.26.2 - '@shikijs/engine-oniguruma': 1.26.2 - '@shikijs/types': 1.26.2 - '@shikijs/vscode-textmate': 10.0.1 + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - hast-util-to-html: 9.0.4 + hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@1.26.2': + '@shikijs/engine-javascript@3.9.2': dependencies: - '@shikijs/types': 1.26.2 - '@shikijs/vscode-textmate': 10.0.1 - oniguruma-to-es: 1.0.0 + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 - '@shikijs/engine-oniguruma@1.26.2': + '@shikijs/engine-oniguruma@3.9.2': dependencies: - '@shikijs/types': 1.26.2 - '@shikijs/vscode-textmate': 10.0.1 + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@1.26.2': + '@shikijs/langs@3.9.2': dependencies: - '@shikijs/types': 1.26.2 + '@shikijs/types': 3.9.2 - '@shikijs/themes@1.26.2': + '@shikijs/themes@3.9.2': dependencies: - '@shikijs/types': 1.26.2 + '@shikijs/types': 3.9.2 - '@shikijs/transformers@1.26.2': + '@shikijs/transformers@3.9.2': dependencies: - shiki: 1.26.2 + '@shikijs/core': 3.9.2 + '@shikijs/types': 3.9.2 - '@shikijs/types@1.26.2': + '@shikijs/types@3.9.2': dependencies: - '@shikijs/vscode-textmate': 10.0.1 + '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - '@shikijs/vscode-textmate@10.0.1': {} + '@shikijs/vscode-textmate@10.0.2': {} '@sindresorhus/merge-streams@2.3.0': {} - '@sindresorhus/merge-streams@4.0.0': {} - '@stackblitz/sdk@1.11.0': {} '@types/debug@4.1.12': dependencies: - '@types/ms': 0.7.34 + '@types/ms': 2.1.0 - '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 22.10.5 + '@types/node': 24.2.1 '@types/hash-sum@1.0.2': {} @@ -3435,7 +3272,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.10.5 + '@types/node': 24.2.1 '@types/katex@0.16.7': {} @@ -3456,13 +3293,13 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/ms@0.7.34': {} + '@types/ms@2.1.0': {} '@types/node@17.0.45': {} - '@types/node@22.10.5': + '@types/node@24.2.1': dependencies: - undici-types: 6.20.0 + undici-types: 7.10.0 '@types/sax@1.2.7': dependencies: @@ -3474,105 +3311,106 @@ snapshots: '@types/unist@3.0.3': {} - '@types/web-bluetooth@0.0.20': {} + '@types/web-bluetooth@0.0.21': {} - '@ungap/structured-clone@1.2.1': {} + '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-vue@5.2.1(vite@6.0.7(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)': + '@vitejs/plugin-vue@6.0.1(vite@7.0.6(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)': dependencies: - vite: 6.0.7(@types/node@22.10.5)(sass-embedded@1.83.1) - vue: 3.5.13 + '@rolldown/pluginutils': 1.0.0-beta.29 + vite: 7.0.6(@types/node@24.2.1)(sass-embedded@1.89.2) + vue: 3.5.18 - '@vue/compiler-core@3.5.13': + '@vue/compiler-core@3.5.18': dependencies: - '@babel/parser': 7.26.5 - '@vue/shared': 3.5.13 + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.18 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.13': + '@vue/compiler-dom@3.5.18': dependencies: - '@vue/compiler-core': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/compiler-core': 3.5.18 + '@vue/shared': 3.5.18 - '@vue/compiler-sfc@3.5.13': + '@vue/compiler-sfc@3.5.18': dependencies: - '@babel/parser': 7.26.5 - '@vue/compiler-core': 3.5.13 - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.18 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 estree-walker: 2.0.2 magic-string: 0.30.17 - postcss: 8.5.0 + postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.13': + '@vue/compiler-ssr@3.5.18': dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/compiler-dom': 3.5.18 + '@vue/shared': 3.5.18 '@vue/devtools-api@6.6.4': {} - '@vue/devtools-api@7.7.0': + '@vue/devtools-api@7.7.7': dependencies: - '@vue/devtools-kit': 7.7.0 + '@vue/devtools-kit': 7.7.7 - '@vue/devtools-kit@7.7.0': + '@vue/devtools-kit@7.7.7': dependencies: - '@vue/devtools-shared': 7.7.0 - birpc: 0.2.19 + '@vue/devtools-shared': 7.7.7 + birpc: 2.5.0 hookable: 5.5.3 mitt: 3.0.1 perfect-debounce: 1.0.0 speakingurl: 14.0.1 superjson: 2.2.2 - '@vue/devtools-shared@7.7.0': + '@vue/devtools-shared@7.7.7': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.5.13': + '@vue/reactivity@3.5.18': dependencies: - '@vue/shared': 3.5.13 + '@vue/shared': 3.5.18 - '@vue/runtime-core@3.5.13': + '@vue/runtime-core@3.5.18': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/reactivity': 3.5.18 + '@vue/shared': 3.5.18 - '@vue/runtime-dom@3.5.13': + '@vue/runtime-dom@3.5.18': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/runtime-core': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/reactivity': 3.5.18 + '@vue/runtime-core': 3.5.18 + '@vue/shared': 3.5.18 csstype: 3.1.3 - '@vue/server-renderer@3.5.13(vue@3.5.13)': + '@vue/server-renderer@3.5.18(vue@3.5.18)': dependencies: - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 - vue: 3.5.13 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + vue: 3.5.18 - '@vue/shared@3.5.13': {} + '@vue/shared@3.5.18': {} - '@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1)': + '@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2)': dependencies: - '@vitejs/plugin-vue': 5.2.1(vite@6.0.7(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) - '@vuepress/bundlerutils': 2.0.0-rc.19 - '@vuepress/client': 2.0.0-rc.19 - '@vuepress/core': 2.0.0-rc.19 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 - autoprefixer: 10.4.20(postcss@8.5.0) + '@vitejs/plugin-vue': 6.0.1(vite@7.0.6(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + '@vuepress/bundlerutils': 2.0.0-rc.24 + '@vuepress/client': 2.0.0-rc.24 + '@vuepress/core': 2.0.0-rc.24 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 + autoprefixer: 10.4.21(postcss@8.5.6) connect-history-api-fallback: 2.0.0 - postcss: 8.5.0 - postcss-load-config: 6.0.1(postcss@8.5.0) - rollup: 4.30.1 - vite: 6.0.7(@types/node@22.10.5)(sass-embedded@1.83.1) - vue: 3.5.13 - vue-router: 4.5.0(vue@3.5.13) + postcss: 8.5.6 + postcss-load-config: 6.0.1(postcss@8.5.6) + rollup: 4.46.2 + vite: 7.0.6(@types/node@24.2.1)(sass-embedded@1.89.2) + vue: 3.5.18 + vue-router: 4.5.1(vue@3.5.18) transitivePeerDependencies: - '@types/node' - jiti @@ -3588,83 +3426,84 @@ snapshots: - typescript - yaml - '@vuepress/bundlerutils@2.0.0-rc.19': + '@vuepress/bundlerutils@2.0.0-rc.24': dependencies: - '@vuepress/client': 2.0.0-rc.19 - '@vuepress/core': 2.0.0-rc.19 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 - vue: 3.5.13 - vue-router: 4.5.0(vue@3.5.13) + '@vuepress/client': 2.0.0-rc.24 + '@vuepress/core': 2.0.0-rc.24 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 + vue: 3.5.18 + vue-router: 4.5.1(vue@3.5.18) transitivePeerDependencies: - supports-color - typescript - '@vuepress/cli@2.0.0-rc.19': + '@vuepress/cli@2.0.0-rc.24': dependencies: - '@vuepress/core': 2.0.0-rc.19 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 + '@vuepress/core': 2.0.0-rc.24 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 cac: 6.7.14 chokidar: 3.6.0 envinfo: 7.14.0 - esbuild: 0.21.5 + esbuild: 0.25.8 transitivePeerDependencies: - supports-color - typescript - '@vuepress/client@2.0.0-rc.19': + '@vuepress/client@2.0.0-rc.24': dependencies: - '@vue/devtools-api': 7.7.0 - '@vuepress/shared': 2.0.0-rc.19 - vue: 3.5.13 - vue-router: 4.5.0(vue@3.5.13) + '@vue/devtools-api': 7.7.7 + '@vue/devtools-kit': 7.7.7 + '@vuepress/shared': 2.0.0-rc.24 + vue: 3.5.18 + vue-router: 4.5.1(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/core@2.0.0-rc.19': + '@vuepress/core@2.0.0-rc.24': dependencies: - '@vuepress/client': 2.0.0-rc.19 - '@vuepress/markdown': 2.0.0-rc.19 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 - vue: 3.5.13 + '@vuepress/client': 2.0.0-rc.24 + '@vuepress/markdown': 2.0.0-rc.24 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 + vue: 3.5.18 transitivePeerDependencies: - supports-color - typescript - '@vuepress/helper@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/helper@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vue/shared': 3.5.13 - '@vueuse/core': 12.4.0 - cheerio: 1.0.0 + '@vue/shared': 3.5.18 + '@vueuse/core': 13.6.0(vue@3.5.18) + cheerio: 1.1.2 fflate: 0.8.2 gray-matter: 4.0.3 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/highlighter-helper@2.0.0-rc.70(@vueuse/core@12.4.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/highlighter-helper@2.0.0-rc.112(@vueuse/core@13.6.0(vue@3.5.18))(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) optionalDependencies: - '@vueuse/core': 12.4.0 - - '@vuepress/markdown@2.0.0-rc.19': - dependencies: - '@mdit-vue/plugin-component': 2.1.3 - '@mdit-vue/plugin-frontmatter': 2.1.3 - '@mdit-vue/plugin-headers': 2.1.3 - '@mdit-vue/plugin-sfc': 2.1.3 - '@mdit-vue/plugin-title': 2.1.3 - '@mdit-vue/plugin-toc': 2.1.3 - '@mdit-vue/shared': 2.1.3 - '@mdit-vue/types': 2.1.0 + '@vueuse/core': 13.6.0(vue@3.5.18) + + '@vuepress/markdown@2.0.0-rc.24': + dependencies: + '@mdit-vue/plugin-component': 2.1.4 + '@mdit-vue/plugin-frontmatter': 2.1.4 + '@mdit-vue/plugin-headers': 2.1.4 + '@mdit-vue/plugin-sfc': 2.1.4 + '@mdit-vue/plugin-title': 2.1.4 + '@mdit-vue/plugin-toc': 2.1.4 + '@mdit-vue/shared': 2.1.4 + '@mdit-vue/types': 2.1.4 '@types/markdown-it': 14.1.2 '@types/markdown-it-emoji': 3.0.1 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 markdown-it: 14.1.0 markdown-it-anchor: 9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0) markdown-it-emoji: 3.0.0 @@ -3672,334 +3511,368 @@ snapshots: transitivePeerDependencies: - supports-color - '@vuepress/plugin-active-header-links@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-active-header-links@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-back-to-top@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-back-to-top@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-blog@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-blog@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - chokidar: 3.6.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + chokidar: 4.0.3 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-catalog@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-catalog@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-comment@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-comment@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) giscus: 1.6.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-copy-code@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-copy-code@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-copyright@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-copyright@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-feed@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-feed@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) xml-js: 1.6.11 transitivePeerDependencies: - typescript - '@vuepress/plugin-git@2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-git@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - execa: 9.5.2 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + rehype-parse: 9.0.1 + rehype-sanitize: 6.0.0 + rehype-stringify: 10.0.1 + unified: 11.0.5 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + transitivePeerDependencies: + - typescript - '@vuepress/plugin-icon@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-icon@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-icon': 0.16.5(markdown-it@14.1.0) - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@mdit/plugin-icon': 0.22.1(markdown-it@14.1.0) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-links-check@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-links-check@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': + dependencies: + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + transitivePeerDependencies: + - typescript + + '@vuepress/plugin-markdown-chart@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@mdit/plugin-container': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-plantuml': 0.22.2(markdown-it@14.1.0) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: + - markdown-it - typescript - '@vuepress/plugin-markdown-ext@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-ext@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-container': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-footnote': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-tasklist': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-container': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-footnote': 0.22.2(markdown-it@14.1.0) + '@mdit/plugin-tasklist': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) js-yaml: 4.1.0 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-markdown-hint@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-hint@2.0.0-rc.112(markdown-it@14.1.0)(vue@3.5.18)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-alert': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-container': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-alert': 0.22.2(markdown-it@14.1.0) + '@mdit/plugin-container': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript + - vue - '@vuepress/plugin-markdown-image@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-image@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-figure': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-img-lazyload': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-img-mark': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-img-size': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-figure': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-img-lazyload': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-img-mark': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-img-size': 0.22.2(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-markdown-include@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-include@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-include': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-include': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-markdown-math@2.0.0-rc.70(katex@0.16.20)(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-math@2.0.0-rc.112(katex@0.16.22)(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-katex-slim': 0.16.2(katex@0.16.20)(markdown-it@14.1.0) - '@mdit/plugin-mathjax-slim': 0.16.0(markdown-it@14.1.0)(mathjax-full@3.2.2) + '@mdit/plugin-katex-slim': 0.23.1(katex@0.16.22)(markdown-it@14.1.0) + '@mdit/plugin-mathjax-slim': 0.23.1(markdown-it@14.1.0)(mathjax-full@3.2.2) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) optionalDependencies: - katex: 0.16.20 + katex: 0.16.22 mathjax-full: 3.2.2 transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-markdown-stylize@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-preview@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-align': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-attrs': 0.16.2(markdown-it@14.1.0) - '@mdit/plugin-mark': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-spoiler': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-stylize': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-sub': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-sup': 0.16.0(markdown-it@14.1.0) + '@mdit/helper': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-demo': 0.22.2(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-markdown-tab@2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-stylize@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@mdit/plugin-tab': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-align': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-attrs': 0.23.1(markdown-it@14.1.0) + '@mdit/plugin-mark': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-spoiler': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-stylize': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-sub': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-sup': 0.22.1(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - markdown-it - typescript - '@vuepress/plugin-notice@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-markdown-tab@2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@mdit/plugin-tab': 0.22.2(markdown-it@14.1.0) + '@types/markdown-it': 14.1.2 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + transitivePeerDependencies: + - markdown-it + - typescript + + '@vuepress/plugin-notice@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': + dependencies: + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + chokidar: 4.0.3 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-nprogress@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-nprogress@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-photo-swipe@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-photo-swipe@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) photoswipe: 5.4.4 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-reading-time@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-reading-time@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-redirect@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-redirect@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - commander: 13.0.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + commander: 14.0.0 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-rtl@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-rtl@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-sass-palette@2.0.0-rc.70(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-sass-palette@2.0.0-rc.112(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) chokidar: 4.0.3 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) optionalDependencies: - sass-embedded: 1.83.1 + sass-embedded: 1.89.2 transitivePeerDependencies: - typescript - '@vuepress/plugin-search@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-search@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - chokidar: 3.6.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + chokidar: 4.0.3 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-seo@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-seo@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-shiki@2.0.0-rc.70(@vueuse/core@12.4.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-shiki@2.0.0-rc.112(@vueuse/core@13.6.0(vue@3.5.18))(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@shikijs/transformers': 1.26.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/highlighter-helper': 2.0.0-rc.70(@vueuse/core@12.4.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - nanoid: 5.0.9 - shiki: 1.26.2 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@shikijs/transformers': 3.9.2 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/highlighter-helper': 2.0.0-rc.112(@vueuse/core@13.6.0(vue@3.5.18))(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + nanoid: 5.1.5 + shiki: 3.9.2 + synckit: 0.11.11 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - '@vueuse/core' - typescript - '@vuepress/plugin-sitemap@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-sitemap@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) sitemap: 8.0.0 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/plugin-theme-data@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13))': + '@vuepress/plugin-theme-data@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18))': dependencies: - '@vue/devtools-api': 7.7.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vue/devtools-api': 7.7.7 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - '@vuepress/shared@2.0.0-rc.19': + '@vuepress/shared@2.0.0-rc.24': dependencies: - '@mdit-vue/types': 2.1.0 + '@mdit-vue/types': 2.1.4 - '@vuepress/utils@2.0.0-rc.19': + '@vuepress/utils@2.0.0-rc.24': dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 - '@vuepress/shared': 2.0.0-rc.19 - debug: 4.4.0 - fs-extra: 11.2.0 - globby: 14.0.2 + '@vuepress/shared': 2.0.0-rc.24 + debug: 4.4.1 + fs-extra: 11.3.1 + globby: 14.1.0 hash-sum: 2.0.0 - ora: 8.1.1 + ora: 8.2.0 picocolors: 1.1.1 upath: 2.0.1 transitivePeerDependencies: - supports-color - '@vueuse/core@12.4.0': + '@vueuse/core@13.6.0(vue@3.5.18)': dependencies: - '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 12.4.0 - '@vueuse/shared': 12.4.0 - vue: 3.5.13 - transitivePeerDependencies: - - typescript + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 13.6.0 + '@vueuse/shared': 13.6.0(vue@3.5.18) + vue: 3.5.18 - '@vueuse/metadata@12.4.0': {} + '@vueuse/metadata@13.6.0': {} - '@vueuse/shared@12.4.0': + '@vueuse/shared@13.6.0(vue@3.5.18)': dependencies: - vue: 3.5.13 - transitivePeerDependencies: - - typescript + vue: 3.5.18 + + '@xmldom/xmldom@0.9.8': {} abbrev@1.1.1: optional: true @@ -4009,12 +3882,12 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color optional: true - agent-base@7.1.3: + agent-base@7.1.4: optional: true aggregate-error@3.1.0: @@ -4039,7 +3912,7 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - aproba@2.0.0: + aproba@2.1.0: optional: true are-we-there-yet@2.0.0: @@ -4056,36 +3929,38 @@ snapshots: argparse@2.0.1: {} - autoprefixer@10.4.20(postcss@8.5.0): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.24.4 - caniuse-lite: 1.0.30001692 + browserslist: 4.25.2 + caniuse-lite: 1.0.30001733 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.0 + postcss: 8.5.6 postcss-value-parser: 4.2.0 + bail@2.0.2: {} + balanced-match@1.0.2: optional: true balloon-css@1.2.0: {} - bcrypt-ts@5.0.3: {} + bcrypt-ts@7.1.0: {} binary-extensions@2.3.0: {} - birpc@0.2.19: {} + birpc@2.5.0: {} boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 optional: true - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 optional: true @@ -4094,12 +3969,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: + browserslist@4.25.2: dependencies: - caniuse-lite: 1.0.30001692 - electron-to-chromium: 1.5.80 + caniuse-lite: 1.0.30001733 + electron-to-chromium: 1.5.199 node-releases: 2.0.19 - update-browserslist-db: 1.1.2(browserslist@4.24.4) + update-browserslist-db: 1.1.3(browserslist@4.25.2) buffer-builder@0.2.0: {} @@ -4123,11 +3998,11 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001692: {} + caniuse-lite@1.0.30001733: {} ccount@2.0.1: {} - chalk@5.4.1: {} + chalk@5.5.0: {} character-entities-html4@2.1.0: {} @@ -4140,24 +4015,24 @@ snapshots: cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 + css-select: 5.2.2 + css-what: 6.2.2 domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - cheerio@1.0.0: + cheerio@1.1.2: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 domhandler: 5.0.3 domutils: 3.2.2 - encoding-sniffer: 0.2.0 - htmlparser2: 9.1.0 - parse5: 7.2.1 + encoding-sniffer: 0.2.1 + htmlparser2: 10.0.0 + parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 6.21.0 + undici: 7.13.0 whatwg-mimetype: 4.0.0 chokidar@3.6.0: @@ -4174,7 +4049,7 @@ snapshots: chokidar@4.0.3: dependencies: - readdirp: 4.1.1 + readdirp: 4.1.2 chownr@2.0.0: optional: true @@ -4207,11 +4082,11 @@ snapshots: comma-separated-tokens@2.0.3: {} - commander@13.0.0: {} + commander@13.1.0: {} - commander@8.3.0: {} + commander@14.0.0: {} - commander@9.2.0: {} + commander@8.3.0: {} concat-map@0.0.1: optional: true @@ -4232,28 +4107,27 @@ snapshots: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + optional: true - css-select@5.1.0: + css-select@5.2.2: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 - css-what@6.1.0: {} + css-what@6.2.2: {} csstype@3.1.3: {} - dayjs@1.11.13: {} - - debug@4.4.0: + debug@4.4.1: dependencies: ms: 2.1.3 decamelize@1.2.0: {} - decode-named-character-reference@1.0.2: + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -4262,7 +4136,7 @@ snapshots: dequal@2.0.3: {} - detect-libc@2.0.3: + detect-libc@2.0.4: optional: true devlop@1.1.0: @@ -4292,9 +4166,7 @@ snapshots: eastasianwidth@0.2.0: optional: true - electron-to-chromium@1.5.80: {} - - emoji-regex-xs@1.0.0: {} + electron-to-chromium@1.5.199: {} emoji-regex@10.4.0: {} @@ -4303,7 +4175,7 @@ snapshots: emoji-regex@9.2.2: optional: true - encoding-sniffer@0.2.0: + encoding-sniffer@0.2.1: dependencies: iconv-lite: 0.6.3 whatwg-encoding: 3.1.1 @@ -4315,6 +4187,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.1: {} + env-paths@2.2.1: optional: true @@ -4323,59 +4197,34 @@ snapshots: err-code@2.0.3: optional: true - esbuild@0.21.5: + esbuild@0.25.8: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 escalade@3.2.0: {} @@ -4385,28 +4234,15 @@ snapshots: estree-walker@2.0.2: {} - execa@9.5.2: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.0 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.2.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 - - exponential-backoff@3.1.1: + exponential-backoff@3.1.2: optional: true extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 + extend@3.0.2: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4415,15 +4251,15 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fastq@1.18.0: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 - fflate@0.8.2: {} + fdir@6.4.6(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 + fflate@0.8.2: {} fill-range@7.1.1: dependencies: @@ -4434,7 +4270,7 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 @@ -4442,7 +4278,7 @@ snapshots: fraction.js@4.3.7: {} - fs-extra@11.2.0: + fs-extra@11.3.1: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 @@ -4466,7 +4302,7 @@ snapshots: gauge@3.0.2: dependencies: - aproba: 2.0.0 + aproba: 2.1.0 color-support: 1.1.3 console-control-strings: 1.1.0 has-unicode: 2.0.1 @@ -4481,14 +4317,9 @@ snapshots: get-east-asian-width@1.3.0: {} - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - giscus@1.6.0: dependencies: - lit: 3.2.1 + lit: 3.3.1 glob-parent@5.1.2: dependencies: @@ -4496,7 +4327,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -4523,6 +4354,15 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.1.0 + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + graceful-fs@4.2.11: {} gray-matter@4.0.3: @@ -4539,7 +4379,37 @@ snapshots: hash-sum@2.0.0: {} - hast-util-to-html@9.0.4: + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.0 + unist-util-position: 5.0.0 + + hast-util-to-html@9.0.5: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.3 @@ -4548,7 +4418,7 @@ snapshots: hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 mdast-util-to-hast: 13.2.0 - property-information: 6.5.0 + property-information: 7.1.0 space-separated-tokens: 2.0.2 stringify-entities: 4.0.4 zwitch: 2.0.4 @@ -4557,24 +4427,32 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + hookable@5.5.3: {} html-void-elements@3.0.0: {} - htmlparser2@9.1.0: + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - entities: 4.5.0 + entities: 6.0.1 - http-cache-semantics@4.1.1: + http-cache-semantics@4.2.0: optional: true http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color optional: true @@ -4582,21 +4460,19 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color optional: true https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.1 transitivePeerDependencies: - supports-color optional: true - human-signals@8.0.0: {} - husky@9.1.7: {} iconv-lite@0.6.3: @@ -4605,7 +4481,9 @@ snapshots: ignore@5.3.2: {} - immutable@5.0.3: {} + ignore@7.0.5: {} + + immutable@5.1.3: {} imurmurhash@0.1.4: optional: true @@ -4662,15 +4540,14 @@ snapshots: is-plain-obj@4.1.0: {} - is-stream@4.0.1: {} - is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} is-what@4.1.16: {} - isexe@2.0.0: {} + isexe@2.0.0: + optional: true isexe@3.1.1: optional: true @@ -4702,7 +4579,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - katex@0.16.20: + katex@0.16.22: dependencies: commander: 8.3.0 @@ -4714,21 +4591,21 @@ snapshots: dependencies: uc.micro: 2.1.0 - lit-element@4.1.1: + lit-element@4.2.1: dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 - '@lit/reactive-element': 2.0.4 - lit-html: 3.2.1 + '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit/reactive-element': 2.1.1 + lit-html: 3.3.1 - lit-html@3.2.1: + lit-html@3.3.1: dependencies: '@types/trusted-types': 2.0.7 - lit@3.2.1: + lit@3.3.1: dependencies: - '@lit/reactive-element': 2.0.4 - lit-element: 4.1.1 - lit-html: 3.2.1 + '@lit/reactive-element': 2.1.1 + lit-element: 4.2.1 + lit-html: 3.3.1 locate-path@5.0.0: dependencies: @@ -4736,7 +4613,7 @@ snapshots: log-symbols@6.0.0: dependencies: - chalk: 5.4.1 + chalk: 5.5.0 is-unicode-supported: 1.3.0 lru-cache@10.4.3: @@ -4744,7 +4621,7 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 make-dir@3.1.0: dependencies: @@ -4755,7 +4632,7 @@ snapshots: dependencies: '@npmcli/agent': 2.2.2 cacache: 18.0.4 - http-cache-semantics: 4.1.1 + http-cache-semantics: 4.2.0 is-lambda: 1.0.1 minipass: 7.1.2 minipass-fetch: 3.0.5 @@ -4819,13 +4696,13 @@ snapshots: esm: 3.2.25 mhchemparser: 4.2.1 mj-context-menu: 0.6.1 - speech-rule-engine: 4.0.7 + speech-rule-engine: 4.1.2 mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.2.1 + '@ungap/structured-clone': 1.3.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -4841,7 +4718,7 @@ snapshots: micromark-core-commonmark@2.0.2: dependencies: - decode-named-character-reference: 1.0.2 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-factory-destination: 2.0.1 micromark-factory-label: 2.0.1 @@ -4854,7 +4731,7 @@ snapshots: micromark-util-html-tag-name: 2.0.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.0.3 + micromark-util-subtokenize: 2.1.0 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 @@ -4898,7 +4775,7 @@ snapshots: dependencies: '@types/katex': 0.16.7 devlop: 1.1.0 - katex: 0.16.20 + katex: 0.16.22 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 @@ -4978,7 +4855,7 @@ snapshots: micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.3: + micromark-util-subtokenize@2.1.0: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.1 @@ -4992,8 +4869,8 @@ snapshots: micromark@4.0.1: dependencies: '@types/debug': 4.1.12 - debug: 4.4.0 - decode-named-character-reference: 1.0.2 + debug: 4.4.1 + decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.2 micromark-factory-space: 2.0.1 @@ -5005,7 +4882,7 @@ snapshots: micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.0.3 + micromark-util-subtokenize: 2.1.0 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 transitivePeerDependencies: @@ -5020,12 +4897,12 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 optional: true minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 optional: true minipass-collect@2.0.1: @@ -5087,14 +4964,14 @@ snapshots: dependencies: picocolors: 1.1.1 - nanoid@3.3.8: {} + nanoid@3.3.11: {} - nanoid@5.0.9: {} + nanoid@5.1.5: {} negotiator@0.6.4: optional: true - node-addon-api@8.3.0: + node-addon-api@8.5.0: optional: true node-fetch@2.7.0(encoding@0.1.13): @@ -5107,13 +4984,13 @@ snapshots: node-gyp@10.3.1: dependencies: env-paths: 2.2.1 - exponential-backoff: 3.1.1 + exponential-backoff: 3.1.2 glob: 10.4.5 graceful-fs: 4.2.11 make-fetch-happen: 13.0.1 nopt: 7.2.1 proc-log: 4.2.0 - semver: 7.6.3 + semver: 7.7.2 tar: 6.2.1 which: 4.0.0 transitivePeerDependencies: @@ -5125,7 +5002,7 @@ snapshots: nodejs-jieba@0.2.1(encoding@0.1.13): dependencies: '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) - node-addon-api: 8.3.0 + node-addon-api: 8.5.0 node-gyp: 10.3.1 transitivePeerDependencies: - encoding @@ -5146,11 +5023,6 @@ snapshots: normalize-range@0.1.2: {} - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - npmlog@5.0.1: dependencies: are-we-there-yet: 2.0.0 @@ -5175,15 +5047,17 @@ snapshots: dependencies: mimic-function: 5.0.1 - oniguruma-to-es@1.0.0: + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: dependencies: - emoji-regex-xs: 1.0.0 - regex: 5.1.1 - regex-recursion: 5.1.1 + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 - ora@8.1.1: + ora@8.2.0: dependencies: - chalk: 5.4.1 + chalk: 5.5.0 cli-cursor: 5.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 @@ -5216,34 +5090,31 @@ snapshots: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.0.2 + decode-named-character-reference: 1.2.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 - parse-ms@4.0.0: {} - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 - parse5: 7.2.1 + parse5: 7.3.0 parse5-parser-stream@7.1.2: dependencies: - parse5: 7.2.1 + parse5: 7.3.0 - parse5@7.2.1: + parse5@7.3.0: dependencies: - entities: 4.5.0 + entities: 6.0.1 path-exists@4.0.0: {} path-is-absolute@1.0.1: optional: true - path-key@3.1.1: {} - - path-key@4.0.0: {} + path-key@3.1.1: + optional: true path-scurry@1.11.1: dependencies: @@ -5253,6 +5124,8 @@ snapshots: path-type@5.0.0: {} + path-type@6.0.0: {} + perfect-debounce@1.0.0: {} photoswipe@5.4.4: {} @@ -5261,28 +5134,26 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.3: {} + pngjs@5.0.0: {} - postcss-load-config@6.0.1(postcss@8.5.0): + postcss-load-config@6.0.1(postcss@8.5.6): dependencies: lilconfig: 3.1.3 optionalDependencies: - postcss: 8.5.0 + postcss: 8.5.6 postcss-value-parser@4.2.0: {} - postcss@8.5.0: + postcss@8.5.6: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 prettier@3.4.2: {} - pretty-ms@9.2.0: - dependencies: - parse-ms: 4.0.0 - proc-log@4.2.0: optional: true @@ -5292,7 +5163,7 @@ snapshots: retry: 0.12.0 optional: true - property-information@6.5.0: {} + property-information@7.1.0: {} punycode.js@2.3.1: {} @@ -5315,19 +5186,35 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.1.1: {} + readdirp@4.1.2: {} - regex-recursion@5.1.1: + regex-recursion@6.0.2: dependencies: - regex: 5.1.1 regex-utilities: 2.3.0 regex-utilities@2.3.0: {} - regex@5.1.1: + regex@6.0.1: dependencies: regex-utilities: 2.3.0 + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + require-directory@2.1.1: {} require-main-filename@2.0.0: {} @@ -5340,7 +5227,7 @@ snapshots: retry@0.12.0: optional: true - reusify@1.0.4: {} + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -5349,36 +5236,37 @@ snapshots: glob: 7.2.3 optional: true - rollup@4.30.1: + rollup@4.46.2: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.30.1 - '@rollup/rollup-android-arm64': 4.30.1 - '@rollup/rollup-darwin-arm64': 4.30.1 - '@rollup/rollup-darwin-x64': 4.30.1 - '@rollup/rollup-freebsd-arm64': 4.30.1 - '@rollup/rollup-freebsd-x64': 4.30.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 - '@rollup/rollup-linux-arm-musleabihf': 4.30.1 - '@rollup/rollup-linux-arm64-gnu': 4.30.1 - '@rollup/rollup-linux-arm64-musl': 4.30.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 - '@rollup/rollup-linux-riscv64-gnu': 4.30.1 - '@rollup/rollup-linux-s390x-gnu': 4.30.1 - '@rollup/rollup-linux-x64-gnu': 4.30.1 - '@rollup/rollup-linux-x64-musl': 4.30.1 - '@rollup/rollup-win32-arm64-msvc': 4.30.1 - '@rollup/rollup-win32-ia32-msvc': 4.30.1 - '@rollup/rollup-win32-x64-msvc': 4.30.1 + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 fsevents: 2.3.3 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.1: + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -5387,97 +5275,81 @@ snapshots: safer-buffer@2.1.2: {} - sass-embedded-android-arm64@1.83.1: - optional: true - - sass-embedded-android-arm@1.83.1: - optional: true - - sass-embedded-android-ia32@1.83.1: - optional: true - - sass-embedded-android-riscv64@1.83.1: - optional: true - - sass-embedded-android-x64@1.83.1: + sass-embedded-android-arm64@1.89.2: optional: true - sass-embedded-darwin-arm64@1.83.1: + sass-embedded-android-arm@1.89.2: optional: true - sass-embedded-darwin-x64@1.83.1: + sass-embedded-android-riscv64@1.89.2: optional: true - sass-embedded-linux-arm64@1.83.1: + sass-embedded-android-x64@1.89.2: optional: true - sass-embedded-linux-arm@1.83.1: + sass-embedded-darwin-arm64@1.89.2: optional: true - sass-embedded-linux-ia32@1.83.1: + sass-embedded-darwin-x64@1.89.2: optional: true - sass-embedded-linux-musl-arm64@1.83.1: + sass-embedded-linux-arm64@1.89.2: optional: true - sass-embedded-linux-musl-arm@1.83.1: + sass-embedded-linux-arm@1.89.2: optional: true - sass-embedded-linux-musl-ia32@1.83.1: + sass-embedded-linux-musl-arm64@1.89.2: optional: true - sass-embedded-linux-musl-riscv64@1.83.1: + sass-embedded-linux-musl-arm@1.89.2: optional: true - sass-embedded-linux-musl-x64@1.83.1: + sass-embedded-linux-musl-riscv64@1.89.2: optional: true - sass-embedded-linux-riscv64@1.83.1: + sass-embedded-linux-musl-x64@1.89.2: optional: true - sass-embedded-linux-x64@1.83.1: + sass-embedded-linux-riscv64@1.89.2: optional: true - sass-embedded-win32-arm64@1.83.1: + sass-embedded-linux-x64@1.89.2: optional: true - sass-embedded-win32-ia32@1.83.1: + sass-embedded-win32-arm64@1.89.2: optional: true - sass-embedded-win32-x64@1.83.1: + sass-embedded-win32-x64@1.89.2: optional: true - sass-embedded@1.83.1: + sass-embedded@1.89.2: dependencies: - '@bufbuild/protobuf': 2.2.3 + '@bufbuild/protobuf': 2.6.3 buffer-builder: 0.2.0 colorjs.io: 0.5.2 - immutable: 5.0.3 - rxjs: 7.8.1 + immutable: 5.1.3 + rxjs: 7.8.2 supports-color: 8.1.1 sync-child-process: 1.0.2 varint: 6.0.0 optionalDependencies: - sass-embedded-android-arm: 1.83.1 - sass-embedded-android-arm64: 1.83.1 - sass-embedded-android-ia32: 1.83.1 - sass-embedded-android-riscv64: 1.83.1 - sass-embedded-android-x64: 1.83.1 - sass-embedded-darwin-arm64: 1.83.1 - sass-embedded-darwin-x64: 1.83.1 - sass-embedded-linux-arm: 1.83.1 - sass-embedded-linux-arm64: 1.83.1 - sass-embedded-linux-ia32: 1.83.1 - sass-embedded-linux-musl-arm: 1.83.1 - sass-embedded-linux-musl-arm64: 1.83.1 - sass-embedded-linux-musl-ia32: 1.83.1 - sass-embedded-linux-musl-riscv64: 1.83.1 - sass-embedded-linux-musl-x64: 1.83.1 - sass-embedded-linux-riscv64: 1.83.1 - sass-embedded-linux-x64: 1.83.1 - sass-embedded-win32-arm64: 1.83.1 - sass-embedded-win32-ia32: 1.83.1 - sass-embedded-win32-x64: 1.83.1 + sass-embedded-android-arm: 1.89.2 + sass-embedded-android-arm64: 1.89.2 + sass-embedded-android-riscv64: 1.89.2 + sass-embedded-android-x64: 1.89.2 + sass-embedded-darwin-arm64: 1.89.2 + sass-embedded-darwin-x64: 1.89.2 + sass-embedded-linux-arm: 1.89.2 + sass-embedded-linux-arm64: 1.89.2 + sass-embedded-linux-musl-arm: 1.89.2 + sass-embedded-linux-musl-arm64: 1.89.2 + sass-embedded-linux-musl-riscv64: 1.89.2 + sass-embedded-linux-musl-x64: 1.89.2 + sass-embedded-linux-riscv64: 1.89.2 + sass-embedded-linux-x64: 1.89.2 + sass-embedded-win32-arm64: 1.89.2 + sass-embedded-win32-x64: 1.89.2 sax@1.4.1: {} @@ -5489,7 +5361,7 @@ snapshots: semver@6.3.1: optional: true - semver@7.6.3: + semver@7.7.2: optional: true set-blocking@2.0.0: {} @@ -5497,18 +5369,20 @@ snapshots: shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 + optional: true - shebang-regex@3.0.0: {} + shebang-regex@3.0.0: + optional: true - shiki@1.26.2: + shiki@3.9.2: dependencies: - '@shikijs/core': 1.26.2 - '@shikijs/engine-javascript': 1.26.2 - '@shikijs/engine-oniguruma': 1.26.2 - '@shikijs/langs': 1.26.2 - '@shikijs/themes': 1.26.2 - '@shikijs/types': 1.26.2 - '@shikijs/vscode-textmate': 10.0.1 + '@shikijs/core': 3.9.2 + '@shikijs/engine-javascript': 3.9.2 + '@shikijs/engine-oniguruma': 3.9.2 + '@shikijs/langs': 3.9.2 + '@shikijs/themes': 3.9.2 + '@shikijs/types': 3.9.2 + '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 signal-exit@3.0.7: @@ -5530,14 +5404,14 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 - socks: 2.8.3 + agent-base: 7.1.4 + debug: 4.4.1 + socks: 2.8.6 transitivePeerDependencies: - supports-color optional: true - socks@2.8.3: + socks@2.8.6: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 @@ -5549,11 +5423,11 @@ snapshots: speakingurl@14.0.1: {} - speech-rule-engine@4.0.7: + speech-rule-engine@4.1.2: dependencies: - commander: 9.2.0 + '@xmldom/xmldom': 0.9.8 + commander: 13.1.0 wicked-good-xpath: 1.3.0 - xmldom-sre: 0.1.31 sprintf-js@1.0.3: {} @@ -5606,8 +5480,6 @@ snapshots: strip-bom-string@1.0.0: {} - strip-final-newline@4.0.0: {} - superjson@2.2.2: dependencies: copy-anything: 3.0.5 @@ -5622,6 +5494,10 @@ snapshots: sync-message-port@1.1.3: {} + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -5632,6 +5508,11 @@ snapshots: yallist: 4.0.0 optional: true + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -5641,18 +5522,30 @@ snapshots: trim-lines@3.0.1: {} + trough@2.2.0: {} + tslib@2.8.1: {} uc.micro@2.1.0: {} - undici-types@6.20.0: {} + undici-types@7.10.0: {} - undici@6.21.0: {} + undici@7.13.0: {} unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 @@ -5690,9 +5583,9 @@ snapshots: upath@2.0.1: {} - update-browserslist-db@1.1.2(browserslist@4.24.4): + update-browserslist-db@1.1.3(browserslist@4.25.2): dependencies: - browserslist: 4.24.4 + browserslist: 4.25.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -5701,7 +5594,12 @@ snapshots: varint@6.0.0: {} - vfile-message@4.0.2: + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 @@ -5709,124 +5607,126 @@ snapshots: vfile@6.0.3: dependencies: '@types/unist': 3.0.3 - vfile-message: 4.0.2 + vfile-message: 4.0.3 - vite@6.0.7(@types/node@22.10.5)(sass-embedded@1.83.1): + vite@7.0.6(@types/node@24.2.1)(sass-embedded@1.89.2): dependencies: - esbuild: 0.24.2 - postcss: 8.5.0 - rollup: 4.30.1 + esbuild: 0.25.8 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.46.2 + tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 22.10.5 + '@types/node': 24.2.1 fsevents: 2.3.3 - sass-embedded: 1.83.1 + sass-embedded: 1.89.2 - vue-router@4.5.0(vue@3.5.13): + vue-router@4.5.1(vue@3.5.18): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.13 + vue: 3.5.18 - vue@3.5.13: + vue@3.5.18: dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-sfc': 3.5.13 - '@vue/runtime-dom': 3.5.13 - '@vue/server-renderer': 3.5.13(vue@3.5.13) - '@vue/shared': 3.5.13 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-sfc': 3.5.18 + '@vue/runtime-dom': 3.5.18 + '@vue/server-renderer': 3.5.18(vue@3.5.18) + '@vue/shared': 3.5.18 - vuepress-plugin-components@2.0.0-rc.68(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)): + vuepress-plugin-components@2.0.0-rc.94(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)): dependencies: '@stackblitz/sdk': 1.11.0 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-sass-palette': 2.0.0-rc.70(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-sass-palette': 2.0.0-rc.112(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) balloon-css: 1.2.0 create-codepen: 2.0.0 qrcode: 1.5.4 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) - vuepress-shared: 2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + vuepress-shared: 2.0.0-rc.94(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) optionalDependencies: - sass-embedded: 1.83.1 + sass-embedded: 1.89.2 transitivePeerDependencies: - typescript - vuepress-plugin-md-enhance@2.0.0-rc.68(markdown-it@14.1.0)(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)): + vuepress-plugin-md-enhance@2.0.0-rc.94(markdown-it@14.1.0)(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)): dependencies: - '@mdit/plugin-container': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-demo': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-plantuml': 0.16.0(markdown-it@14.1.0) - '@mdit/plugin-uml': 0.16.0(markdown-it@14.1.0) + '@mdit/plugin-container': 0.22.1(markdown-it@14.1.0) + '@mdit/plugin-demo': 0.22.2(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-sass-palette': 2.0.0-rc.70(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-sass-palette': 2.0.0-rc.112(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) balloon-css: 1.2.0 js-yaml: 4.1.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) - vuepress-shared: 2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + vuepress-shared: 2.0.0-rc.94(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) optionalDependencies: - sass-embedded: 1.83.1 + sass-embedded: 1.89.2 transitivePeerDependencies: - markdown-it - typescript - vuepress-shared@2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)): + vuepress-shared@2.0.0-rc.94(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)): dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 - dayjs: 1.11.13 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) transitivePeerDependencies: - typescript - vuepress-theme-hope@2.0.0-rc.68(@vuepress/plugin-feed@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)))(@vuepress/plugin-search@2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)))(katex@0.16.20)(markdown-it@14.1.0)(mathjax-full@3.2.2)(nodejs-jieba@0.2.1(encoding@0.1.13))(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)): - dependencies: - '@vuepress/helper': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-active-header-links': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-back-to-top': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-blog': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-catalog': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-comment': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-copy-code': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-copyright': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-git': 2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-icon': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-links-check': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-ext': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-hint': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-image': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-include': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-math': 2.0.0-rc.70(katex@0.16.20)(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-stylize': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-markdown-tab': 2.0.0-rc.70(markdown-it@14.1.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-notice': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-nprogress': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-photo-swipe': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-reading-time': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-redirect': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-rtl': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-sass-palette': 2.0.0-rc.70(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-seo': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-shiki': 2.0.0-rc.70(@vueuse/core@12.4.0)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-sitemap': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-theme-data': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vueuse/core': 12.4.0 + vuepress-theme-hope@2.0.0-rc.94(@vuepress/plugin-feed@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)))(@vuepress/plugin-search@2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)))(katex@0.16.22)(markdown-it@14.1.0)(mathjax-full@3.2.2)(nodejs-jieba@0.2.1(encoding@0.1.13))(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)): + dependencies: + '@vuepress/helper': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-active-header-links': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-back-to-top': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-blog': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-catalog': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-comment': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-copy-code': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-copyright': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-git': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-icon': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-links-check': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-chart': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-ext': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-hint': 2.0.0-rc.112(markdown-it@14.1.0)(vue@3.5.18)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-image': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-include': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-math': 2.0.0-rc.112(katex@0.16.22)(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-preview': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-stylize': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-markdown-tab': 2.0.0-rc.112(markdown-it@14.1.0)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-notice': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-nprogress': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-photo-swipe': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-reading-time': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-redirect': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-rtl': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-sass-palette': 2.0.0-rc.112(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-seo': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-shiki': 2.0.0-rc.112(@vueuse/core@13.6.0(vue@3.5.18))(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-sitemap': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-theme-data': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vueuse/core': 13.6.0(vue@3.5.18) balloon-css: 1.2.0 - bcrypt-ts: 5.0.3 - chokidar: 3.6.0 - vue: 3.5.13 - vuepress: 2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13) - vuepress-plugin-components: 2.0.0-rc.68(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress-plugin-md-enhance: 2.0.0-rc.68(markdown-it@14.1.0)(sass-embedded@1.83.1)(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - vuepress-shared: 2.0.0-rc.68(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + bcrypt-ts: 7.1.0 + chokidar: 4.0.3 + vue: 3.5.18 + vuepress: 2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18) + vuepress-plugin-components: 2.0.0-rc.94(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress-plugin-md-enhance: 2.0.0-rc.94(markdown-it@14.1.0)(sass-embedded@1.89.2)(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + vuepress-shared: 2.0.0-rc.94(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) optionalDependencies: - '@vuepress/plugin-feed': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) - '@vuepress/plugin-search': 2.0.0-rc.70(vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13)) + '@vuepress/plugin-feed': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) + '@vuepress/plugin-search': 2.0.0-rc.112(vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18)) nodejs-jieba: 0.2.1(encoding@0.1.13) - sass-embedded: 1.83.1 + sass-embedded: 1.89.2 transitivePeerDependencies: - '@vue/repl' - '@waline/client' @@ -5851,21 +5751,23 @@ snapshots: - typescript - vidstack - vuepress@2.0.0-rc.19(@vuepress/bundler-vite@2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1))(vue@3.5.13): + vuepress@2.0.0-rc.24(@vuepress/bundler-vite@2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2))(vue@3.5.18): dependencies: - '@vuepress/cli': 2.0.0-rc.19 - '@vuepress/client': 2.0.0-rc.19 - '@vuepress/core': 2.0.0-rc.19 - '@vuepress/markdown': 2.0.0-rc.19 - '@vuepress/shared': 2.0.0-rc.19 - '@vuepress/utils': 2.0.0-rc.19 - vue: 3.5.13 + '@vuepress/cli': 2.0.0-rc.24 + '@vuepress/client': 2.0.0-rc.24 + '@vuepress/core': 2.0.0-rc.24 + '@vuepress/markdown': 2.0.0-rc.24 + '@vuepress/shared': 2.0.0-rc.24 + '@vuepress/utils': 2.0.0-rc.24 + vue: 3.5.18 optionalDependencies: - '@vuepress/bundler-vite': 2.0.0-rc.19(@types/node@22.10.5)(sass-embedded@1.83.1) + '@vuepress/bundler-vite': 2.0.0-rc.24(@types/node@24.2.1)(sass-embedded@1.89.2) transitivePeerDependencies: - supports-color - typescript + web-namespaces@2.0.1: {} + webidl-conversions@3.0.1: optional: true @@ -5886,6 +5788,7 @@ snapshots: which@2.0.2: dependencies: isexe: 2.0.0 + optional: true which@4.0.0: dependencies: @@ -5926,8 +5829,6 @@ snapshots: dependencies: sax: 1.4.1 - xmldom-sre@0.1.31: {} - y18n@4.0.3: {} yallist@4.0.0: @@ -5952,6 +5853,4 @@ snapshots: y18n: 4.0.3 yargs-parser: 18.1.3 - yoctocolors@2.1.1: {} - zwitch@2.0.4: {} diff --git a/translate_repo.py b/translate_repo.py new file mode 100755 index 00000000000..41828334976 --- /dev/null +++ b/translate_repo.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python3 +""" +Batch Translation Tool for Repository Documentation + +Translates all markdown files in docs/ folder to target language. +Preserves directory structure and saves to docs_{lang}/ folder. +""" + +import os +import sys +import time +import json +from pathlib import Path +from deep_translator import GoogleTranslator + +# Language configurations +LANGUAGES = { + '1': {'name': 'English', 'code': 'en', 'suffix': 'en'}, + '2': {'name': 'Chinese (Simplified)', 'code': 'zh-CN', 'suffix': 'zh'}, + '3': {'name': 'Spanish', 'code': 'es', 'suffix': 'es'}, + '4': {'name': 'French', 'code': 'fr', 'suffix': 'fr'}, + '5': {'name': 'Portuguese', 'code': 'pt', 'suffix': 'pt'}, + '6': {'name': 'German', 'code': 'de', 'suffix': 'de'}, + '7': {'name': 'Japanese', 'code': 'ja', 'suffix': 'ja'}, + '8': {'name': 'Korean', 'code': 'ko', 'suffix': 'ko'}, + '9': {'name': 'Russian', 'code': 'ru', 'suffix': 'ru'}, + '10': {'name': 'Italian', 'code': 'it', 'suffix': 'it'}, + '11': {'name': 'Arabic', 'code': 'ar', 'suffix': 'ar'}, + '12': {'name': 'Hindi', 'code': 'hi', 'suffix': 'hi'}, + '13': {'name': 'Turkish', 'code': 'tr', 'suffix': 'tr'}, + '14': {'name': 'Vietnamese', 'code': 'vi', 'suffix': 'vi'}, + '15': {'name': 'Polish', 'code': 'pl', 'suffix': 'pl'}, + '16': {'name': 'Dutch', 'code': 'nl', 'suffix': 'nl'}, + '17': {'name': 'Indonesian', 'code': 'id', 'suffix': 'id'}, + '18': {'name': 'Thai', 'code': 'th', 'suffix': 'th'}, + '19': {'name': 'Swedish', 'code': 'sv', 'suffix': 'sv'}, + '20': {'name': 'Greek', 'code': 'el', 'suffix': 'el'}, +} + +CHUNK_SIZE = 4000 # Characters per chunk +PROGRESS_FILE = '.translation_progress.json' + + +def print_header(): + print("=" * 70) + print("Repository Documentation Translation Tool") + print("=" * 70) + print() + + +def select_language(): + """Let user select target language""" + print("=" * 70) + print("Select target language:") + print("=" * 70) + + for num, lang in LANGUAGES.items(): + print(f" {num:>2}. {lang['name']}") + + print() + while True: + choice = input("Enter choice (1-20): ").strip() + if choice in LANGUAGES: + return LANGUAGES[choice] + print("❌ Invalid choice. Please enter a number between 1-20.") + + +def find_markdown_files(repo_path): + """Find all markdown files in docs/ folder and README.md""" + repo_path = Path(repo_path) + docs_path = repo_path / 'docs' + + files = [] + + # Add README.md if exists + readme = repo_path / 'README.md' + if readme.exists(): + files.append(readme) + + # Add all .md files in docs/ + if docs_path.exists(): + for md_file in docs_path.rglob('*.md'): + files.append(md_file) + + return sorted(files) + + +def get_output_path(input_path, repo_path, lang_suffix): + """ + Convert input path to output path. + docs/java/basics.md -> docs_en/java/basics.en.md + README.md -> README.en.md + """ + repo_path = Path(repo_path) + input_path = Path(input_path) + + # Handle README.md + if input_path.name == 'README.md': + return repo_path / f'README.{lang_suffix}.md' + + # Handle docs/ files + relative = input_path.relative_to(repo_path / 'docs') + + # Change extension: file.md -> file.{lang}.md + stem = relative.stem + new_name = f'{stem}.{lang_suffix}.md' + + output_path = repo_path / f'docs_{lang_suffix}' / relative.parent / new_name + return output_path + + +def split_content(content, chunk_size=CHUNK_SIZE): + """Split content into chunks, preserving code blocks""" + chunks = [] + current_chunk = "" + in_code_block = False + + lines = content.split('\n') + + for line in lines: + # Track code blocks + if line.strip().startswith('```'): + in_code_block = not in_code_block + + # If adding this line exceeds chunk size and we're not in a code block + if len(current_chunk) + len(line) > chunk_size and not in_code_block and current_chunk: + chunks.append(current_chunk) + current_chunk = line + '\n' + else: + current_chunk += line + '\n' + + if current_chunk: + chunks.append(current_chunk) + + return chunks + + +def translate_text(text, target_lang): + """Translate text using Google Translate""" + try: + translator = GoogleTranslator(source='auto', target=target_lang) + translated = translator.translate(text) + return translated + except Exception as e: + print(f"\n⚠️ Translation error: {e}") + return text # Return original on error + + +def translate_file(input_path, output_path, lang_code): + """Translate a single markdown file""" + # Read input + with open(input_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Split into chunks + chunks = split_content(content) + + # Translate each chunk + translated_chunks = [] + for i, chunk in enumerate(chunks, 1): + print(f" Chunk {i}/{len(chunks)}... ", end='', flush=True) + translated = translate_text(chunk, lang_code) + translated_chunks.append(translated) + print("✅") + time.sleep(1) # Rate limiting + + # Combine translated chunks + translated_content = ''.join(translated_chunks) + + # Create output directory + output_path.parent.mkdir(parents=True, exist_ok=True) + + # Write output + with open(output_path, 'w', encoding='utf-8') as f: + f.write(translated_content) + + return len(content), len(translated_content) + + +def load_progress(repo_path): + """Load translation progress""" + progress_file = Path(repo_path) / PROGRESS_FILE + if progress_file.exists(): + with open(progress_file, 'r') as f: + return json.load(f) + return {'completed': [], 'failed': []} + + +def save_progress(repo_path, progress): + """Save translation progress""" + progress_file = Path(repo_path) / PROGRESS_FILE + with open(progress_file, 'w') as f: + json.dump(progress, f, indent=2) + + +def main(): + print_header() + + # Get repository path + repo_path = input("Enter repository path (default: current directory): ").strip() + if not repo_path: + repo_path = '.' + + repo_path = Path(repo_path).resolve() + + if not repo_path.exists(): + print(f"❌ Repository path does not exist: {repo_path}") + sys.exit(1) + + print(f"📁 Repository: {repo_path}") + print() + + # Select language + lang_config = select_language() + print(f"\n✨ Selected: {lang_config['name']}") + print() + + # Find all markdown files + print("🔍 Finding markdown files...") + md_files = find_markdown_files(repo_path) + + if not md_files: + print("❌ No markdown files found in docs/ folder or README.md") + sys.exit(1) + + print(f"📄 Found {len(md_files)} markdown files") + print() + + # Load progress + progress = load_progress(repo_path) + + # Filter out already completed files + files_to_translate = [] + for f in md_files: + output_path = get_output_path(f, repo_path, lang_config['suffix']) + if output_path.exists(): + print(f"⏭️ Skipping (exists): {f.relative_to(repo_path)}") + elif str(f) in progress['completed']: + print(f"⏭️ Skipping (completed): {f.relative_to(repo_path)}") + else: + files_to_translate.append(f) + + if not files_to_translate: + print("\n✅ All files already translated!") + sys.exit(0) + + print(f"\n📝 Files to translate: {len(files_to_translate)}") + print() + + # Confirm + confirm = input(f"Translate {len(files_to_translate)} files to {lang_config['name']}? (y/n): ").strip().lower() + if confirm != 'y': + print("❌ Translation cancelled") + sys.exit(0) + + print() + print("=" * 70) + print(f"Translating to {lang_config['name']}...") + print("=" * 70) + print() + + # Translate files + total_input_chars = 0 + total_output_chars = 0 + failed_files = [] + + for idx, input_path in enumerate(files_to_translate, 1): + relative_path = input_path.relative_to(repo_path) + output_path = get_output_path(input_path, repo_path, lang_config['suffix']) + + print(f"[{idx}/{len(files_to_translate)}] {relative_path}") + print(f" → {output_path.relative_to(repo_path)}") + + try: + input_chars, output_chars = translate_file(input_path, output_path, lang_config['code']) + total_input_chars += input_chars + total_output_chars += output_chars + + # Mark as completed + progress['completed'].append(str(input_path)) + save_progress(repo_path, progress) + + print(f" ✅ Translated ({input_chars} → {output_chars} chars)") + print() + + except Exception as e: + print(f" ❌ Failed: {e}") + failed_files.append((str(relative_path), str(e))) + progress['failed'].append(str(input_path)) + save_progress(repo_path, progress) + print() + + # Summary + print("=" * 70) + print("Translation Complete!") + print("=" * 70) + print(f"✅ Translated: {len(files_to_translate) - len(failed_files)} files") + print(f"📊 Input: {total_input_chars:,} characters") + print(f"📊 Output: {total_output_chars:,} characters") + + if failed_files: + print(f"\n❌ Failed: {len(failed_files)} files") + for file, error in failed_files: + print(f" - {file}: {error}") + + print(f"\n📁 Output directory: docs_{lang_config['suffix']}/") + print(f"📁 README: README.{lang_config['suffix']}.md") + print() + print("💡 Next steps:") + print(f" 1. Review translated files in docs_{lang_config['suffix']}/") + print(f" 2. git add docs_{lang_config['suffix']}/ README.{lang_config['suffix']}.md") + print(f" 3. git commit -m 'Add {lang_config['name']} translation'") + print(" 4. Create PR") + print() + + +if __name__ == "__main__": + main()