diff --git a/404.html b/404.html index 5bf371bd..32deeeb8 100644 --- a/404.html +++ b/404.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容

404

页面不存在

我们是怎么来到这儿的?

- +
跳至主要內容

404

页面不存在

这里什么也没有

+ diff --git a/about/friend.html b/about/friend.html index b2c49b6f..f4cec4eb 100644 --- a/about/friend.html +++ b/about/friend.html @@ -31,10 +31,10 @@ } - +
跳至主要內容
我的好友

我的好友


我的好友

- + diff --git a/about/hobby.html b/about/hobby.html index 43b86324..a93aaa10 100644 --- a/about/hobby.html +++ b/about/hobby.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容

Yaien Blog小于 1 分钟

上次编辑于:
贡献者: yanggl
- +
跳至主要內容

Yaien Blog小于 1 分钟

上次编辑于:
贡献者: yanggl
+ diff --git a/about/index.html b/about/index.html index ed027ffd..be316364 100644 --- a/about/index.html +++ b/about/index.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

About

Yaien Blog小于 1 分钟

- + diff --git a/about/me.html b/about/me.html index 72aba862..47480e89 100644 --- a/about/me.html +++ b/about/me.html @@ -31,10 +31,10 @@ } - +
跳至主要內容
个人简介

个人简介


个人简介

- + diff --git a/about/update.html b/about/update.html index dead5cab..7ea4602f 100644 --- a/about/update.html +++ b/about/update.html @@ -31,10 +31,10 @@ } - +
跳至主要內容
更新内容

更新内容


更新内容

- + diff --git a/article/index.html b/article/index.html index ee772268..454284b0 100644 --- a/article/index.html +++ b/article/index.html @@ -31,21 +31,23 @@ } - +
跳至主要內容
多边形等距离外扩

多边形等距离外扩

实现多边形形成的多边形进行等距外扩或收缩的算法实现

-

多边形原创


Mysql事务原理

Mysql事务原理

+

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

+

MySQL事务原创
锁机制

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+

MySQL锁机制原创
垃圾收集器及原理

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器

JVMGC原创
对象创建与内存分配

对象创建与内存分配

当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。

-

JVM原创
Redis分布式锁实战

Redis分布式锁实战

-

记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

-

Redis原创
2
3
4
5
...
8
- +
JVM原创
2
3
4
5
...
9
+ diff --git a/assets/1910011300.html-b91fce53.js b/assets/1910011300.html-a1fde421.js similarity index 97% rename from assets/1910011300.html-b91fce53.js rename to assets/1910011300.html-a1fde421.js index e5a9c6db..07cc8212 100644 --- a/assets/1910011300.html-b91fce53.js +++ b/assets/1910011300.html-a1fde421.js @@ -1,4 +1,4 @@ -import{_ as e,o as s,c as n,d as t,a,b as l,e as c}from"./app-1efcbe9f.js";const o={},i=a("h1",{id:"mysql-基础-一",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql-基础-一","aria-hidden":"true"},"#"),l(" MySQL 基础(一)")],-1),r=a("p",null,"MySQL 基础笔记系列",-1),d=c(`

简介

什么是DBMS: DataBaseManagementSystem,数据库管理系统(数据库管理软件),作用就是负责对数据进行增删改查的软件,常见的DBMS: MySQL、Oracle、DB2、SQLServer、SQLite等

数据库分类

  1. 关系型数据库: 以表为单位保存数据,经过数学理论验证可以保存现实生活中存在的任何关系
  2. 非关系型数据库: 以键值对形式保存数据,一般用于解决特殊场景,如数据缓存。

开源和闭源

  1. 开源:公开源代码,免费。盈利方式:靠卖服务,开源有大拿无偿维护升级
  2. 闭源:不公开源代码,收费。盈利方式:靠卖产品+卖服务,闭源有大拿攻击,但是公司会花钱养一群人维护升级

主流数据库

  1. MySQL:Oracle公司产品, 08年被Sun公司收购,09Sun被Oracle 拉里.埃里森 MariaDB 市场占有率第一
  2. Oracle:Oracle公司产品市场占有率第二,性能最高 价格最贵的数据库
  3. SQLServer:微软公司产品排第三,主要应用在微软整套解决方案中
  4. DB2:IBM公司产品 主要应用在IBM整套解决方案中
  5. sqlite:轻量级数据库,只具备基础的增删改查操作

SQL

Structured Query Language:结构化查询语言,用户程序员和数据库软件进行交流的语言

数据库相关

  1. 连接数据库
mysql -uroot -p
+import{_ as e,o as s,c as n,d as t,a,b as l,e as c}from"./app-6a63891c.js";const o={},i=a("h1",{id:"mysql-基础-一",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql-基础-一","aria-hidden":"true"},"#"),l(" MySQL 基础(一)")],-1),r=a("p",null,"MySQL 基础笔记系列",-1),d=c(`

简介

什么是DBMS: DataBaseManagementSystem,数据库管理系统(数据库管理软件),作用就是负责对数据进行增删改查的软件,常见的DBMS: MySQL、Oracle、DB2、SQLServer、SQLite等

数据库分类

  1. 关系型数据库: 以表为单位保存数据,经过数学理论验证可以保存现实生活中存在的任何关系
  2. 非关系型数据库: 以键值对形式保存数据,一般用于解决特殊场景,如数据缓存。

开源和闭源

  1. 开源:公开源代码,免费。盈利方式:靠卖服务,开源有大拿无偿维护升级
  2. 闭源:不公开源代码,收费。盈利方式:靠卖产品+卖服务,闭源有大拿攻击,但是公司会花钱养一群人维护升级

主流数据库

  1. MySQL:Oracle公司产品, 08年被Sun公司收购,09Sun被Oracle 拉里.埃里森 MariaDB 市场占有率第一
  2. Oracle:Oracle公司产品市场占有率第二,性能最高 价格最贵的数据库
  3. SQLServer:微软公司产品排第三,主要应用在微软整套解决方案中
  4. DB2:IBM公司产品 主要应用在IBM整套解决方案中
  5. sqlite:轻量级数据库,只具备基础的增删改查操作

SQL

Structured Query Language:结构化查询语言,用户程序员和数据库软件进行交流的语言

数据库相关

  1. 连接数据库
mysql -uroot -p
 
  1. 查看所有数据库
 show databases;
 
  1. 创建数据库
 # 格式:create database 数据库名;
 create database db1;
diff --git a/assets/1910011300.html-4fb37a7b.js b/assets/1910011300.html-b19ba2e7.js
similarity index 90%
rename from assets/1910011300.html-4fb37a7b.js
rename to assets/1910011300.html-b19ba2e7.js
index 5ccf488a..35a52a8d 100644
--- a/assets/1910011300.html-4fb37a7b.js
+++ b/assets/1910011300.html-b19ba2e7.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-01034d18","path":"/note/db/mysql/basic/1910011300.html","title":"MySQL 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":1,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"简介","slug":"简介","link":"#简介","children":[{"level":3,"title":"数据库分类","slug":"数据库分类","link":"#数据库分类","children":[]},{"level":3,"title":"开源和闭源","slug":"开源和闭源","link":"#开源和闭源","children":[]},{"level":3,"title":"主流数据库","slug":"主流数据库","link":"#主流数据库","children":[]},{"level":3,"title":"SQL","slug":"sql","link":"#sql","children":[]}]},{"level":2,"title":"数据库相关","slug":"数据库相关","link":"#数据库相关","children":[]},{"level":2,"title":"表相关","slug":"表相关","link":"#表相关","children":[]},{"level":2,"title":"引擎","slug":"引擎","link":"#引擎","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.18,"words":654},"filePathRelative":"note/db/mysql/basic/1910011300.md","localizedDate":"2023年5月7日","excerpt":"

MySQL 基础(一)

\\n

MySQL 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-01034d18","path":"/note/db/mysql/basic/1910011300.html","title":"MySQL 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":1,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"简介","slug":"简介","link":"#简介","children":[{"level":3,"title":"数据库分类","slug":"数据库分类","link":"#数据库分类","children":[]},{"level":3,"title":"开源和闭源","slug":"开源和闭源","link":"#开源和闭源","children":[]},{"level":3,"title":"主流数据库","slug":"主流数据库","link":"#主流数据库","children":[]},{"level":3,"title":"SQL","slug":"sql","link":"#sql","children":[]}]},{"level":2,"title":"数据库相关","slug":"数据库相关","link":"#数据库相关","children":[]},{"level":2,"title":"表相关","slug":"表相关","link":"#表相关","children":[]},{"level":2,"title":"引擎","slug":"引擎","link":"#引擎","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.18,"words":654},"filePathRelative":"note/db/mysql/basic/1910011300.md","localizedDate":"2023年5月7日","excerpt":"

MySQL 基础(一)

\\n

MySQL 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/1910011301.html-2a29a21a.js b/assets/1910011301.html-64cc386a.js similarity index 99% rename from assets/1910011301.html-2a29a21a.js rename to assets/1910011301.html-64cc386a.js index 82af7bb2..826b087d 100644 --- a/assets/1910011301.html-2a29a21a.js +++ b/assets/1910011301.html-64cc386a.js @@ -1,4 +1,4 @@ -import{_ as n,o as s,c as e,d as t,a,b as l,e as o}from"./app-1efcbe9f.js";const p={},c=a("h1",{id:"mysql基础-二",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql基础-二","aria-hidden":"true"},"#"),l(" MySQL基础(二)")],-1),i=a("p",null,"MySQL基础笔记系列",-1),u=o(`

主键约束

  • 主键:表示数据唯一性的字段称为主键
  • 约束: 创建表时给表字段添加的限制条件
  • 主键约束: 限制值唯一且非空
# 格式: create table t1(id int primary key,name varchar(10));
+import{_ as n,o as s,c as e,d as t,a,b as l,e as o}from"./app-6a63891c.js";const p={},c=a("h1",{id:"mysql基础-二",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql基础-二","aria-hidden":"true"},"#"),l(" MySQL基础(二)")],-1),i=a("p",null,"MySQL基础笔记系列",-1),u=o(`

主键约束

  • 主键:表示数据唯一性的字段称为主键
  • 约束: 创建表时给表字段添加的限制条件
  • 主键约束: 限制值唯一且非空
# 格式: create table t1(id int primary key,name varchar(10));
 insert into t1 values(1,'AAA');
 insert into t1 values(1,'BBB'); # 报错 不能重复
 insert into t1 values(null,'CCC'); # 报错 不能为null
diff --git a/assets/1910011301.html-df2c9fce.js b/assets/1910011301.html-aa1860e5.js
similarity index 97%
rename from assets/1910011301.html-df2c9fce.js
rename to assets/1910011301.html-aa1860e5.js
index 917f710d..bf45e4ba 100644
--- a/assets/1910011301.html-df2c9fce.js
+++ b/assets/1910011301.html-aa1860e5.js
@@ -1 +1 @@
-const l=JSON.parse('{"key":"v-01333213","path":"/note/db/mysql/basic/1910011301.html","title":"MySQL基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"主键约束","slug":"主键约束","link":"#主键约束","children":[]},{"level":2,"title":"主键约束+自增","slug":"主键约束-自增","link":"#主键约束-自增","children":[]},{"level":2,"title":"注释 comment","slug":"注释-comment","link":"#注释-comment","children":[]},{"level":2,"title":"`的作用","slug":"的作用","link":"#的作用","children":[]},{"level":2,"title":"SQL分类","slug":"sql分类","link":"#sql分类","children":[{"level":3,"title":"DDL Data Definition Language 数据定义语言","slug":"ddl-data-definition-language-数据定义语言","link":"#ddl-data-definition-language-数据定义语言","children":[]},{"level":3,"title":"DML Data Manipulation Language 数据操作语言","slug":"dml-data-manipulation-language-数据操作语言","link":"#dml-data-manipulation-language-数据操作语言","children":[]},{"level":3,"title":"DQL Data Query Language 数据查询语言","slug":"dql-data-query-language-数据查询语言","link":"#dql-data-query-language-数据查询语言","children":[]},{"level":3,"title":"TCL Transaction Control Language 事务控制语言","slug":"tcl-transaction-control-language-事务控制语言","link":"#tcl-transaction-control-language-事务控制语言","children":[]},{"level":3,"title":"DCL Data Control Language 数据控制语言","slug":"dcl-data-control-language-数据控制语言","link":"#dcl-data-control-language-数据控制语言","children":[]}]},{"level":2,"title":"数据类型","slug":"数据类型","link":"#数据类型","children":[{"level":3,"title":"整数","slug":"整数","link":"#整数","children":[]},{"level":3,"title":"浮点数","slug":"浮点数","link":"#浮点数","children":[]},{"level":3,"title":"字符串:","slug":"字符串","link":"#字符串","children":[]},{"level":3,"title":"日期","slug":"日期","link":"#日期","children":[]}]},{"level":2,"title":"sql文件导入","slug":"sql文件导入","link":"#sql文件导入","children":[]},{"level":2,"title":"is null  和 is not null","slug":"is-null-和-is-not-null","link":"#is-null-和-is-not-null","children":[]},{"level":2,"title":"别名","slug":"别名","link":"#别名","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.55,"words":765},"filePathRelative":"note/db/mysql/basic/1910011301.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(二)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; +const l=JSON.parse('{"key":"v-01333213","path":"/note/db/mysql/basic/1910011301.html","title":"MySQL基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"主键约束","slug":"主键约束","link":"#主键约束","children":[]},{"level":2,"title":"主键约束+自增","slug":"主键约束-自增","link":"#主键约束-自增","children":[]},{"level":2,"title":"注释 comment","slug":"注释-comment","link":"#注释-comment","children":[]},{"level":2,"title":"`的作用","slug":"的作用","link":"#的作用","children":[]},{"level":2,"title":"SQL分类","slug":"sql分类","link":"#sql分类","children":[{"level":3,"title":"DDL Data Definition Language 数据定义语言","slug":"ddl-data-definition-language-数据定义语言","link":"#ddl-data-definition-language-数据定义语言","children":[]},{"level":3,"title":"DML Data Manipulation Language 数据操作语言","slug":"dml-data-manipulation-language-数据操作语言","link":"#dml-data-manipulation-language-数据操作语言","children":[]},{"level":3,"title":"DQL Data Query Language 数据查询语言","slug":"dql-data-query-language-数据查询语言","link":"#dql-data-query-language-数据查询语言","children":[]},{"level":3,"title":"TCL Transaction Control Language 事务控制语言","slug":"tcl-transaction-control-language-事务控制语言","link":"#tcl-transaction-control-language-事务控制语言","children":[]},{"level":3,"title":"DCL Data Control Language 数据控制语言","slug":"dcl-data-control-language-数据控制语言","link":"#dcl-data-control-language-数据控制语言","children":[]}]},{"level":2,"title":"数据类型","slug":"数据类型","link":"#数据类型","children":[{"level":3,"title":"整数","slug":"整数","link":"#整数","children":[]},{"level":3,"title":"浮点数","slug":"浮点数","link":"#浮点数","children":[]},{"level":3,"title":"字符串:","slug":"字符串","link":"#字符串","children":[]},{"level":3,"title":"日期","slug":"日期","link":"#日期","children":[]}]},{"level":2,"title":"sql文件导入","slug":"sql文件导入","link":"#sql文件导入","children":[]},{"level":2,"title":"is null 和 is not null","slug":"is-null-和-is-not-null","link":"#is-null-和-is-not-null","children":[]},{"level":2,"title":"别名","slug":"别名","link":"#别名","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.55,"words":765},"filePathRelative":"note/db/mysql/basic/1910011301.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(二)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/1910011302.html-13fdb17f.js b/assets/1910011302.html-26c408a0.js similarity index 99% rename from assets/1910011302.html-13fdb17f.js rename to assets/1910011302.html-26c408a0.js index ee4beb30..ececf2e1 100644 --- a/assets/1910011302.html-13fdb17f.js +++ b/assets/1910011302.html-26c408a0.js @@ -1,4 +1,4 @@ -import{_ as a,o as n,c as e,d as t,a as s,b as p,e as o}from"./app-1efcbe9f.js";const l={},c=s("h1",{id:"mysql基础-三",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#mysql基础-三","aria-hidden":"true"},"#"),p(" MySQL基础(三)")],-1),r=s("p",null,"MySQL基础笔记系列",-1),k=o(`

比较运算符 > < >= <= = !=和<>

  1. 查询工资在2000以内的员工姓名和工资
select ename,sal from emp where sal<2000;
+import{_ as a,o as n,c as e,d as t,a as s,b as p,e as o}from"./app-6a63891c.js";const l={},c=s("h1",{id:"mysql基础-三",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#mysql基础-三","aria-hidden":"true"},"#"),p(" MySQL基础(三)")],-1),r=s("p",null,"MySQL基础笔记系列",-1),k=o(`

比较运算符 > < >= <= = !=和<>

  1. 查询工资在2000以内的员工姓名和工资
select ename,sal from emp where sal<2000;
 
  1. 查询工作job是manager的员工姓名、工资和工作
select ename,sal,job from emp where job='manager';
 
  1. 查询员工工资小于等于1600的员工姓名,职位和工资
select ename,job,sal from emp where sal<=1600;
 
  1. 查询不是10号部门的员工姓名,部门编号(两种写法)
select ename,deptno from emp where deptno!=10;
diff --git a/assets/1910011302.html-67fec82f.js b/assets/1910011302.html-32d1a4b4.js
similarity index 96%
rename from assets/1910011302.html-67fec82f.js
rename to assets/1910011302.html-32d1a4b4.js
index f9c2ac98..89511cc5 100644
--- a/assets/1910011302.html-67fec82f.js
+++ b/assets/1910011302.html-32d1a4b4.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-02e80ab2","path":"/note/db/mysql/basic/1910011302.html","title":"MySQL基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"比较运算符 > <  >=  <= = !=和<>","slug":"比较运算符-和","link":"#比较运算符-和","children":[]},{"level":2,"title":"去重 distinct","slug":"去重-distinct","link":"#去重-distinct","children":[]},{"level":2,"title":"and 和 or","slug":"and-和-or","link":"#and-和-or","children":[]},{"level":2,"title":"模糊查询 like","slug":"模糊查询-like","link":"#模糊查询-like","children":[]},{"level":2,"title":"between x and y  包含x和y","slug":"between-x-and-y-包含x和y","link":"#between-x-and-y-包含x和y","children":[]},{"level":2,"title":"in","slug":"in","link":"#in","children":[]},{"level":2,"title":"排序 order by 字段名","slug":"排序-order-by-字段名","link":"#排序-order-by-字段名","children":[]},{"level":2,"title":"分页查询","slug":"分页查询","link":"#分页查询","children":[]},{"level":2,"title":"数值计算 + - * / % 7%2 = mod(7,2)","slug":"数值计算-7-2-mod-7-2","link":"#数值计算-7-2-mod-7-2","children":[]},{"level":2,"title":"日期相关","slug":"日期相关","link":"#日期相关","children":[]},{"level":2,"title":"ifnull","slug":"ifnull","link":"#ifnull","children":[]},{"level":2,"title":"聚合函数","slug":"聚合函数","link":"#聚合函数","children":[]},{"level":2,"title":"字符串相关","slug":"字符串相关","link":"#字符串相关","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.13,"words":1840},"filePathRelative":"note/db/mysql/basic/1910011302.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(三)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-02e80ab2","path":"/note/db/mysql/basic/1910011302.html","title":"MySQL基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"比较运算符 > < >= <= = !=和<>","slug":"比较运算符-和","link":"#比较运算符-和","children":[]},{"level":2,"title":"去重 distinct","slug":"去重-distinct","link":"#去重-distinct","children":[]},{"level":2,"title":"and 和 or","slug":"and-和-or","link":"#and-和-or","children":[]},{"level":2,"title":"模糊查询 like","slug":"模糊查询-like","link":"#模糊查询-like","children":[]},{"level":2,"title":"between x and y 包含x和y","slug":"between-x-and-y-包含x和y","link":"#between-x-and-y-包含x和y","children":[]},{"level":2,"title":"in","slug":"in","link":"#in","children":[]},{"level":2,"title":"排序 order by 字段名","slug":"排序-order-by-字段名","link":"#排序-order-by-字段名","children":[]},{"level":2,"title":"分页查询","slug":"分页查询","link":"#分页查询","children":[]},{"level":2,"title":"数值计算 + - * / % 7%2 = mod(7,2)","slug":"数值计算-7-2-mod-7-2","link":"#数值计算-7-2-mod-7-2","children":[]},{"level":2,"title":"日期相关","slug":"日期相关","link":"#日期相关","children":[]},{"level":2,"title":"ifnull","slug":"ifnull","link":"#ifnull","children":[]},{"level":2,"title":"聚合函数","slug":"聚合函数","link":"#聚合函数","children":[]},{"level":2,"title":"字符串相关","slug":"字符串相关","link":"#字符串相关","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.13,"words":1840},"filePathRelative":"note/db/mysql/basic/1910011302.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(三)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/1910011303.html-4155e70d.js b/assets/1910011303.html-41984d55.js similarity index 89% rename from assets/1910011303.html-4155e70d.js rename to assets/1910011303.html-41984d55.js index 1d77d604..a801c439 100644 --- a/assets/1910011303.html-4155e70d.js +++ b/assets/1910011303.html-41984d55.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-049ce351","path":"/note/db/mysql/basic/1910011303.html","title":"MySQL基础(四)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"数学相关","slug":"数学相关","link":"#数学相关","children":[]},{"level":2,"title":"分组查询 group by","slug":"分组查询-group-by","link":"#分组查询-group-by","children":[]},{"level":2,"title":"having","slug":"having","link":"#having","children":[]},{"level":2,"title":"子查询可以写的位置","slug":"子查询可以写的位置","link":"#子查询可以写的位置","children":[]},{"level":2,"title":"关联查询","slug":"关联查询","link":"#关联查询","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.62,"words":485},"filePathRelative":"note/db/mysql/basic/1910011303.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(四)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-049ce351","path":"/note/db/mysql/basic/1910011303.html","title":"MySQL基础(四)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"数学相关","slug":"数学相关","link":"#数学相关","children":[]},{"level":2,"title":"分组查询 group by","slug":"分组查询-group-by","link":"#分组查询-group-by","children":[]},{"level":2,"title":"having","slug":"having","link":"#having","children":[]},{"level":2,"title":"子查询可以写的位置","slug":"子查询可以写的位置","link":"#子查询可以写的位置","children":[]},{"level":2,"title":"关联查询","slug":"关联查询","link":"#关联查询","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.62,"words":485},"filePathRelative":"note/db/mysql/basic/1910011303.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(四)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/1910011303.html-5556c3cb.js b/assets/1910011303.html-df8db159.js similarity index 99% rename from assets/1910011303.html-5556c3cb.js rename to assets/1910011303.html-df8db159.js index bc0638e7..fd9a05f1 100644 --- a/assets/1910011303.html-5556c3cb.js +++ b/assets/1910011303.html-df8db159.js @@ -1,4 +1,4 @@ -import{_ as a,o as n,c as e,d as o,a as s,b as p,e as t}from"./app-1efcbe9f.js";const l={},c=s("h1",{id:"mysql基础-四",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#mysql基础-四","aria-hidden":"true"},"#"),p(" MySQL基础(四)")],-1),r=s("p",null,"MySQL基础笔记系列",-1),k=t(`

数学相关

  1. 向下取整 floor(num)
select floor(3.85);
+import{_ as a,o as n,c as e,d as o,a as s,b as p,e as t}from"./app-6a63891c.js";const l={},c=s("h1",{id:"mysql基础-四",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#mysql基础-四","aria-hidden":"true"},"#"),p(" MySQL基础(四)")],-1),r=s("p",null,"MySQL基础笔记系列",-1),k=t(`

数学相关

  1. 向下取整 floor(num)
select floor(3.85);
 
  1. 四舍五入 round(num)
select round(3.8);
 
  1. 四舍五入 round(num,m) m代表小数位数
select round(23.879,2);
 
  1. 非四舍五入 truncate(num,m) m代表小数位数
select truncate(23.879,2);
diff --git a/assets/1910011304.html-14b3f0b3.js b/assets/1910011304.html-f19eccf5.js
similarity index 97%
rename from assets/1910011304.html-14b3f0b3.js
rename to assets/1910011304.html-f19eccf5.js
index da91a75d..1cf63bc3 100644
--- a/assets/1910011304.html-14b3f0b3.js
+++ b/assets/1910011304.html-f19eccf5.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as e,d as t,a,b as p,e as o}from"./app-1efcbe9f.js";const l={},c=a("h1",{id:"mysql基础-五",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql基础-五","aria-hidden":"true"},"#"),p(" MySQL基础(五)")],-1),i=a("p",null,"MySQL基础笔记系列",-1),r=o(`

关联关系

外键:用于建立关系的字段称为外键

一对一

  • 什么是一对一: 有AB两张表,A表中一条数据对应B表中的一条数据,同时B表中一条也对应A表中的一条

  • 应用场景: 为了提高查询效率 把原有一张表的数据拆成两张表如:商品表和商品详情表 、 用户表和用户信息扩展表

一对多

  • 什么是一对多:有AB两张表,A表中一条数据对应B表多条数据,同时B表一条对应A表一条,称为一对多。
  • 应用场景: 员工表和部门表 商品表和商品分类表 用户表和地址表
  • 如何建立关系: 在多的表中添加外键指向另外一张表的主键

多对多

  • 什么是多对多: 有AB两张表,A表中一条数据对应B表中的多条数据,同时B表中一体数据对应A表中的多条。
  • 应用场景: 用户表和权限表 老师表和学生表
  • 如何建立关系: 创建关系表,在关系表中添加两个外键指向另外两个表的主键

视图(view)

  • 什么是视图:数据库中表和视图都是其内部的对象,视图本质其实是取代了一段SQL查询语句,视图没有自己独立的数据,数据来自于原表
  • 视图的作用: 1. 重用SQL,提高开发效率 2. 隐藏敏感字段
 create view 视图名 as (子查询);
+import{_ as s,o as n,c as e,d as t,a,b as p,e as o}from"./app-6a63891c.js";const l={},c=a("h1",{id:"mysql基础-五",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#mysql基础-五","aria-hidden":"true"},"#"),p(" MySQL基础(五)")],-1),i=a("p",null,"MySQL基础笔记系列",-1),r=o(`

关联关系

外键:用于建立关系的字段称为外键

一对一

  • 什么是一对一: 有AB两张表,A表中一条数据对应B表中的一条数据,同时B表中一条也对应A表中的一条

  • 应用场景: 为了提高查询效率 把原有一张表的数据拆成两张表如:商品表和商品详情表 、 用户表和用户信息扩展表

一对多

  • 什么是一对多:有AB两张表,A表中一条数据对应B表多条数据,同时B表一条对应A表一条,称为一对多。
  • 应用场景: 员工表和部门表 商品表和商品分类表 用户表和地址表
  • 如何建立关系: 在多的表中添加外键指向另外一张表的主键

多对多

  • 什么是多对多: 有AB两张表,A表中一条数据对应B表中的多条数据,同时B表中一体数据对应A表中的多条。
  • 应用场景: 用户表和权限表 老师表和学生表
  • 如何建立关系: 创建关系表,在关系表中添加两个外键指向另外两个表的主键

视图(view)

  • 什么是视图:数据库中表和视图都是其内部的对象,视图本质其实是取代了一段SQL查询语句,视图没有自己独立的数据,数据来自于原表
  • 视图的作用: 1. 重用SQL,提高开发效率 2. 隐藏敏感字段
 create view 视图名 as (子查询);
 

分类

  • 简单视图: 创建视图的子查询中不包含去重,分组,关联查询,聚合函数的视图称为简单视图,可以对视图中的数据进行增删改查操作
  • 复杂视图: 和简单视图相反,只能对视图中的数据进行查询操作

使用

  1. 创建查询每个部门平均工资,最高工资,最低工资,工资总和,部门人数的视图
create view v_emp_info as (select deptno,avg(sal),max(sal),min(sal),sum(sal),count(*) from emp group by deptno);
 
  1. 创建一个10号部门的视图
create view v_emp_10 as(select * from emp where deptno=10);
 
  1. 创建一个没有工资的员工表视图
create view v_emp_nosal as(select empno,ename,deptno from emp);
diff --git a/assets/1910011304.html-3e17f12f.js b/assets/1910011304.html-f8bda883.js
similarity index 95%
rename from assets/1910011304.html-3e17f12f.js
rename to assets/1910011304.html-f8bda883.js
index faad25cd..de9ac380 100644
--- a/assets/1910011304.html-3e17f12f.js
+++ b/assets/1910011304.html-f8bda883.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0651bbf0","path":"/note/db/mysql/basic/1910011304.html","title":"MySQL基础(五)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"关联关系","slug":"关联关系","link":"#关联关系","children":[{"level":3,"title":"一对一","slug":"一对一","link":"#一对一","children":[]},{"level":3,"title":"一对多","slug":"一对多","link":"#一对多","children":[]},{"level":3,"title":"多对多","slug":"多对多","link":"#多对多","children":[]}]},{"level":2,"title":"视图(view)","slug":"视图-view","link":"#视图-view","children":[{"level":3,"title":"分类","slug":"分类","link":"#分类","children":[]},{"level":3,"title":"使用","slug":"使用","link":"#使用","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.05,"words":614},"filePathRelative":"note/db/mysql/basic/1910011304.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(五)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0651bbf0","path":"/note/db/mysql/basic/1910011304.html","title":"MySQL基础(五)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["MySQL","基础"]},"headers":[{"level":2,"title":"关联关系","slug":"关联关系","link":"#关联关系","children":[{"level":3,"title":"一对一","slug":"一对一","link":"#一对一","children":[]},{"level":3,"title":"一对多","slug":"一对多","link":"#一对多","children":[]},{"level":3,"title":"多对多","slug":"多对多","link":"#多对多","children":[]}]},{"level":2,"title":"视图(view)","slug":"视图-view","link":"#视图-view","children":[{"level":3,"title":"分类","slug":"分类","link":"#分类","children":[]},{"level":3,"title":"使用","slug":"使用","link":"#使用","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.05,"words":614},"filePathRelative":"note/db/mysql/basic/1910011304.md","localizedDate":"2023年5月7日","excerpt":"

MySQL基础(五)

\\n

MySQL基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/1911011300.html-0f56ebf0.js b/assets/1911011300.html-9196bca3.js similarity index 99% rename from assets/1911011300.html-0f56ebf0.js rename to assets/1911011300.html-9196bca3.js index 9cb4c038..155b2f3d 100644 --- a/assets/1911011300.html-0f56ebf0.js +++ b/assets/1911011300.html-9196bca3.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"mybatis-基础",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#mybatis-基础","aria-hidden":"true"},"#"),e(" Mybatis 基础")],-1),u=n("p",null,"Mybatis 基础笔记",-1),i=o(`

作用

简化持久层开发,当需要开发某个增删改查功能时,程序员只需要定义好该功能对应的抽象方法,及该抽象方法的功能对应的SQL语句即可。

创建

与创建SpringMVC项目的步骤相同,另外,增加添加依赖:


+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"mybatis-基础",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#mybatis-基础","aria-hidden":"true"},"#"),e(" Mybatis 基础")],-1),u=n("p",null,"Mybatis 基础笔记",-1),i=o(`

作用

简化持久层开发,当需要开发某个增删改查功能时,程序员只需要定义好该功能对应的抽象方法,及该抽象方法的功能对应的SQL语句即可。

创建

与创建SpringMVC项目的步骤相同,另外,增加添加依赖:


 <dependencys>
     <!-- MyBatis -->
     <dependency>
diff --git a/assets/1911011300.html-f9e364ef.js b/assets/1911011300.html-ba390a45.js
similarity index 96%
rename from assets/1911011300.html-f9e364ef.js
rename to assets/1911011300.html-ba390a45.js
index f57fb051..f4bde552 100644
--- a/assets/1911011300.html-f9e364ef.js
+++ b/assets/1911011300.html-ba390a45.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0b555a5d","path":"/note/framework/mybatis/1911011300.html","title":"Mybatis 基础","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["MyBatis"],"tag":["基础"]},"headers":[{"level":2,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":2,"title":"创建","slug":"创建","link":"#创建","children":[]},{"level":2,"title":"配置数据库连接","slug":"配置数据库连接","link":"#配置数据库连接","children":[]},{"level":2,"title":"接口与抽象方法","slug":"接口与抽象方法","link":"#接口与抽象方法","children":[]},{"level":2,"title":"配置SQL","slug":"配置sql","link":"#配置sql","children":[]},{"level":2,"title":"查询时,需要查询结果中的列名与返回值类型中的属性名保持一致","slug":"查询时-需要查询结果中的列名与返回值类型中的属性名保持一致","link":"#查询时-需要查询结果中的列名与返回值类型中的属性名保持一致","children":[]},{"level":2,"title":"简单的关联表查询数据","slug":"简单的关联表查询数据","link":"#简单的关联表查询数据","children":[]},{"level":2,"title":"使用resultMap处理1对多的关联查询","slug":"使用resultmap处理1对多的关联查询","link":"#使用resultmap处理1对多的关联查询","children":[]},{"level":2,"title":"动态SQL","slug":"动态sql","link":"#动态sql","children":[]},{"level":2,"title":"#{}与${}占位符","slug":"与-占位符","link":"#与-占位符","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.12,"words":3037},"filePathRelative":"note/framework/mybatis/1911011300.md","localizedDate":"2023年5月7日","excerpt":"

Mybatis 基础

\\n

Mybatis 基础笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0b555a5d","path":"/note/framework/mybatis/1911011300.html","title":"Mybatis 基础","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["MyBatis"],"tag":["基础"]},"headers":[{"level":2,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":2,"title":"创建","slug":"创建","link":"#创建","children":[]},{"level":2,"title":"配置数据库连接","slug":"配置数据库连接","link":"#配置数据库连接","children":[]},{"level":2,"title":"接口与抽象方法","slug":"接口与抽象方法","link":"#接口与抽象方法","children":[]},{"level":2,"title":"配置SQL","slug":"配置sql","link":"#配置sql","children":[]},{"level":2,"title":"查询时,需要查询结果中的列名与返回值类型中的属性名保持一致","slug":"查询时-需要查询结果中的列名与返回值类型中的属性名保持一致","link":"#查询时-需要查询结果中的列名与返回值类型中的属性名保持一致","children":[]},{"level":2,"title":"简单的关联表查询数据","slug":"简单的关联表查询数据","link":"#简单的关联表查询数据","children":[]},{"level":2,"title":"使用resultMap处理1对多的关联查询","slug":"使用resultmap处理1对多的关联查询","link":"#使用resultmap处理1对多的关联查询","children":[]},{"level":2,"title":"动态SQL","slug":"动态sql","link":"#动态sql","children":[]},{"level":2,"title":"#{}与${}占位符","slug":"与-占位符","link":"#与-占位符","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.12,"words":3037},"filePathRelative":"note/framework/mybatis/1911011300.md","localizedDate":"2023年5月7日","excerpt":"

Mybatis 基础

\\n

Mybatis 基础笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2101202010.html-bb29e7a7.js b/assets/2101202010.html-419c8446.js similarity index 92% rename from assets/2101202010.html-bb29e7a7.js rename to assets/2101202010.html-419c8446.js index 393e3395..070424d2 100644 --- a/assets/2101202010.html-bb29e7a7.js +++ b/assets/2101202010.html-419c8446.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-06496550","path":"/note/framework/spring/basic/2101202010.html","title":"Spring 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-09T00:00:00.000Z","index":true,"order":1,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"框架","slug":"框架","link":"#框架","children":[]},{"level":2,"title":"解决的问题","slug":"解决的问题","link":"#解决的问题","children":[]},{"level":2,"title":"准备Spring环境","slug":"准备spring环境","link":"#准备spring环境","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过无参数构造方法","slug":"通过spring创建并获取对象-通过无参数构造方法","link":"#通过spring创建并获取对象-通过无参数构造方法","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过静态工厂方法(不常用)","slug":"通过spring创建并获取对象-通过静态工厂方法-不常用","link":"#通过spring创建并获取对象-通过静态工厂方法-不常用","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过实例工厂方法(不常用)","slug":"通过spring创建并获取对象-通过实例工厂方法-不常用","link":"#通过spring创建并获取对象-通过实例工厂方法-不常用","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.11,"words":1532},"filePathRelative":"note/framework/spring/basic/2101202010.md","localizedDate":"2019年5月9日","excerpt":"

Spring 基础(一)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-06496550","path":"/note/framework/spring/basic/2101202010.html","title":"Spring 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-09T00:00:00.000Z","index":true,"order":1,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"框架","slug":"框架","link":"#框架","children":[]},{"level":2,"title":"解决的问题","slug":"解决的问题","link":"#解决的问题","children":[]},{"level":2,"title":"准备Spring环境","slug":"准备spring环境","link":"#准备spring环境","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过无参数构造方法","slug":"通过spring创建并获取对象-通过无参数构造方法","link":"#通过spring创建并获取对象-通过无参数构造方法","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过静态工厂方法(不常用)","slug":"通过spring创建并获取对象-通过静态工厂方法-不常用","link":"#通过spring创建并获取对象-通过静态工厂方法-不常用","children":[]},{"level":2,"title":"通过Spring创建并获取对象-通过实例工厂方法(不常用)","slug":"通过spring创建并获取对象-通过实例工厂方法-不常用","link":"#通过spring创建并获取对象-通过实例工厂方法-不常用","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.11,"words":1532},"filePathRelative":"note/framework/spring/basic/2101202010.md","localizedDate":"2019年5月9日","excerpt":"

Spring 基础(一)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2101202010.html-67358898.js b/assets/2101202010.html-7c28455a.js similarity index 99% rename from assets/2101202010.html-67358898.js rename to assets/2101202010.html-7c28455a.js index 3066d666..b507e828 100644 --- a/assets/2101202010.html-67358898.js +++ b/assets/2101202010.html-7c28455a.js @@ -1,4 +1,4 @@ -import{_ as a,o as s,c as t,d as p,a as n,b as e,e as c}from"./app-1efcbe9f.js";const o={},l=n("h1",{id:"spring-基础-一",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-一","aria-hidden":"true"},"#"),e(" Spring 基础(一)")],-1),u=n("p",null,"Spring 基础笔记系列",-1),i=c(`

框架

开发人员可以在项目开发过程中,引用某些框架,从而,在开发过程中,就可以不必关心某些功能的开发,而是由框架直接完成!

解决的问题

Spring框架主要解决了创建对象和管理对象的问题!

传统的创建对象的方法例如:

	User user = new User();
+import{_ as a,o as s,c as t,d as p,a as n,b as e,e as c}from"./app-6a63891c.js";const o={},l=n("h1",{id:"spring-基础-一",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-一","aria-hidden":"true"},"#"),e(" Spring 基础(一)")],-1),u=n("p",null,"Spring 基础笔记系列",-1),i=c(`

框架

开发人员可以在项目开发过程中,引用某些框架,从而,在开发过程中,就可以不必关心某些功能的开发,而是由框架直接完成!

解决的问题

Spring框架主要解决了创建对象和管理对象的问题!

传统的创建对象的方法例如:

	User user = new User();
 

使用Spring框架之后,可以改为:

	User user = 从框架中获取;
 

这样的做法的好处主要在于解耦,即解除耦合度,表现为降低对象与对象之间的依赖关系。

例如,在以下代码中,UserServlet就是依赖于UserDao的,因为功能的实现是通过UserDao中的reg()方法来实现的,如果没有UserDao类,则UserServlet无法完成用户注册功能:

	public class UserDao {
 		public void reg() {
diff --git a/assets/2101202011.html-03acbdf1.js b/assets/2101202011.html-4bd3357e.js
similarity index 99%
rename from assets/2101202011.html-03acbdf1.js
rename to assets/2101202011.html-4bd3357e.js
index 56d8fb95..6302f1a0 100644
--- a/assets/2101202011.html-03acbdf1.js
+++ b/assets/2101202011.html-4bd3357e.js
@@ -1,4 +1,4 @@
-import{_ as a,o as t,c as s,d as e,a as n,b as o,e as p}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"spring-基础-二",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-二","aria-hidden":"true"},"#"),o(" Spring 基础(二)")],-1),i=n("p",null,"Spring 基础笔记系列",-1),u=p(`

对象的作用域与生命周期(不常用)

由Spring管理的对象,默认都是单例的!并且,都是饿汉式的单例模式。

在配置<bean>节点时,可以添加scope属性其是否单例,当取值为singleton时表示单例,该值也是默认值,当取值为prototype时表示非单例:

<bean id="user" class="cn.tedu.spring.User" scope="prototype"/>
+import{_ as a,o as t,c as s,d as e,a as n,b as o,e as p}from"./app-6a63891c.js";const c={},l=n("h1",{id:"spring-基础-二",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-二","aria-hidden":"true"},"#"),o(" Spring 基础(二)")],-1),i=n("p",null,"Spring 基础笔记系列",-1),u=p(`

对象的作用域与生命周期(不常用)

由Spring管理的对象,默认都是单例的!并且,都是饿汉式的单例模式。

在配置<bean>节点时,可以添加scope属性其是否单例,当取值为singleton时表示单例,该值也是默认值,当取值为prototype时表示非单例:

<bean id="user" class="cn.tedu.spring.User" scope="prototype"/>
 

在单例模式的基础之上,还可以通过lazy-init属性配置它是否为懒汉式的单例模式,默认值为false,即非懒汉式,也就是饿汉式的单例模式,当取值为true时,表示懒汉式的单例模式:

<bean id="user"
       class="cn.tedu.spring.User"
       scope="singleton"
diff --git a/assets/2101202011.html-d98fdd85.js b/assets/2101202011.html-6519750e.js
similarity index 95%
rename from assets/2101202011.html-d98fdd85.js
rename to assets/2101202011.html-6519750e.js
index 6111e10a..79c050db 100644
--- a/assets/2101202011.html-d98fdd85.js
+++ b/assets/2101202011.html-6519750e.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-07fe3def","path":"/note/framework/spring/basic/2101202011.html","title":"Spring 基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-10T00:00:00.000Z","index":true,"order":2,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"对象的作用域与生命周期(不常用)","slug":"对象的作用域与生命周期-不常用","link":"#对象的作用域与生命周期-不常用","children":[]},{"level":2,"title":"Spring的IoC","slug":"spring的ioc","link":"#spring的ioc","children":[{"level":3,"title":"什么是IoC","slug":"什么是ioc","link":"#什么是ioc","children":[]},{"level":3,"title":"通过SET方式注入属性的值","slug":"通过set方式注入属性的值","link":"#通过set方式注入属性的值","children":[]},{"level":3,"title":"通过构造方法注入属性的值(不常用)","slug":"通过构造方法注入属性的值-不常用","link":"#通过构造方法注入属性的值-不常用","children":[]},{"level":3,"title":"注入集合类型的值","slug":"注入集合类型的值","link":"#注入集合类型的值","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.11,"words":1532},"filePathRelative":"note/framework/spring/basic/2101202011.md","localizedDate":"2019年5月10日","excerpt":"

Spring 基础(二)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-07fe3def","path":"/note/framework/spring/basic/2101202011.html","title":"Spring 基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-10T00:00:00.000Z","index":true,"order":2,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"对象的作用域与生命周期(不常用)","slug":"对象的作用域与生命周期-不常用","link":"#对象的作用域与生命周期-不常用","children":[]},{"level":2,"title":"Spring的IoC","slug":"spring的ioc","link":"#spring的ioc","children":[{"level":3,"title":"什么是IoC","slug":"什么是ioc","link":"#什么是ioc","children":[]},{"level":3,"title":"通过SET方式注入属性的值","slug":"通过set方式注入属性的值","link":"#通过set方式注入属性的值","children":[]},{"level":3,"title":"通过构造方法注入属性的值(不常用)","slug":"通过构造方法注入属性的值-不常用","link":"#通过构造方法注入属性的值-不常用","children":[]},{"level":3,"title":"注入集合类型的值","slug":"注入集合类型的值","link":"#注入集合类型的值","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.11,"words":1532},"filePathRelative":"note/framework/spring/basic/2101202011.md","localizedDate":"2019年5月10日","excerpt":"

Spring 基础(二)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2101202012.html-7b6182e5.js b/assets/2101202012.html-2a241835.js similarity index 95% rename from assets/2101202012.html-7b6182e5.js rename to assets/2101202012.html-2a241835.js index 34a8dc82..34b234d5 100644 --- a/assets/2101202012.html-7b6182e5.js +++ b/assets/2101202012.html-2a241835.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-09b3168e","path":"/note/framework/spring/basic/2101202012.html","title":"Spring 基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-10T00:00:00.000Z","index":true,"order":3,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"Spring表达式","slug":"spring表达式","link":"#spring表达式","children":[]},{"level":2,"title":"Spring自动装配(不推荐)","slug":"spring自动装配-不推荐","link":"#spring自动装配-不推荐","children":[]},{"level":2,"title":"Spring注解","slug":"spring注解","link":"#spring注解","children":[{"level":3,"title":"通用注解","slug":"通用注解","link":"#通用注解","children":[]},{"level":3,"title":"关于作用域和生命周期的注解","slug":"关于作用域和生命周期的注解","link":"#关于作用域和生命周期的注解","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1177},"filePathRelative":"note/framework/spring/basic/2101202012.md","localizedDate":"2019年5月10日","excerpt":"

Spring 基础(三)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-09b3168e","path":"/note/framework/spring/basic/2101202012.html","title":"Spring 基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-10T00:00:00.000Z","index":true,"order":3,"category":["Spring"],"tag":["基础"]},"headers":[{"level":2,"title":"Spring表达式","slug":"spring表达式","link":"#spring表达式","children":[]},{"level":2,"title":"Spring自动装配(不推荐)","slug":"spring自动装配-不推荐","link":"#spring自动装配-不推荐","children":[]},{"level":2,"title":"Spring注解","slug":"spring注解","link":"#spring注解","children":[{"level":3,"title":"通用注解","slug":"通用注解","link":"#通用注解","children":[]},{"level":3,"title":"关于作用域和生命周期的注解","slug":"关于作用域和生命周期的注解","link":"#关于作用域和生命周期的注解","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1177},"filePathRelative":"note/framework/spring/basic/2101202012.md","localizedDate":"2019年5月10日","excerpt":"

Spring 基础(三)

\\n

Spring 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2101202012.html-bff452d5.js b/assets/2101202012.html-d6ea40fa.js similarity index 99% rename from assets/2101202012.html-bff452d5.js rename to assets/2101202012.html-d6ea40fa.js index fa8caf2e..71ac595f 100644 --- a/assets/2101202012.html-bff452d5.js +++ b/assets/2101202012.html-d6ea40fa.js @@ -1,4 +1,4 @@ -import{_ as a,o as s,c as t,d as e,a as n,b as p,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"spring-基础-三",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-三","aria-hidden":"true"},"#"),p(" Spring 基础(三)")],-1),i=n("p",null,"Spring 基础笔记系列",-1),u=o(`

Spring表达式

当某个Bean的某些属性值来自于另一个Bean的某些属性,则可以使用Spring表达式,例如:

	public class ValueBean {
+import{_ as a,o as s,c as t,d as e,a as n,b as p,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"spring-基础-三",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-基础-三","aria-hidden":"true"},"#"),p(" Spring 基础(三)")],-1),i=n("p",null,"Spring 基础笔记系列",-1),u=o(`

Spring表达式

当某个Bean的某些属性值来自于另一个Bean的某些属性,则可以使用Spring表达式,例如:

	public class ValueBean {
 	
 		// SampleBean中names的第3个值
 		public String name;
diff --git a/assets/2102202010.html-5e81c37e.js b/assets/2102202010.html-a632e59c.js
similarity index 92%
rename from assets/2102202010.html-5e81c37e.js
rename to assets/2102202010.html-a632e59c.js
index 4597dfa8..1039886c 100644
--- a/assets/2102202010.html-5e81c37e.js
+++ b/assets/2102202010.html-a632e59c.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-ed30c3f6","path":"/note/framework/springmvc/basic/2102202010.html","title":"SpringMVC 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-12T00:00:00.000Z","index":true,"order":1,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"SpringMVC框架","slug":"springmvc框架","link":"#springmvc框架","children":[{"level":3,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":3,"title":"SpringMVC中的核心组件","slug":"springmvc中的核心组件","link":"#springmvc中的核心组件","children":[]}]},{"level":2,"title":"SpringMVC HelloWorld","slug":"springmvc-helloworld","link":"#springmvc-helloworld","children":[{"level":3,"title":"目标","slug":"目标","link":"#目标","children":[]},{"level":3,"title":"创建","slug":"创建","link":"#创建","children":[]},{"level":3,"title":"配置DispatcherServlet","slug":"配置dispatcherservlet","link":"#配置dispatcherservlet","children":[]},{"level":3,"title":"通过控制器接收请求","slug":"通过控制器接收请求","link":"#通过控制器接收请求","children":[]},{"level":3,"title":"显示页面","slug":"显示页面","link":"#显示页面","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.74,"words":1423},"filePathRelative":"note/framework/springmvc/basic/2102202010.md","localizedDate":"2019年5月12日","excerpt":"

SpringMVC 基础(一)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-ed30c3f6","path":"/note/framework/springmvc/basic/2102202010.html","title":"SpringMVC 基础(一)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-12T00:00:00.000Z","index":true,"order":1,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"SpringMVC框架","slug":"springmvc框架","link":"#springmvc框架","children":[{"level":3,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":3,"title":"SpringMVC中的核心组件","slug":"springmvc中的核心组件","link":"#springmvc中的核心组件","children":[]}]},{"level":2,"title":"SpringMVC HelloWorld","slug":"springmvc-helloworld","link":"#springmvc-helloworld","children":[{"level":3,"title":"目标","slug":"目标","link":"#目标","children":[]},{"level":3,"title":"创建","slug":"创建","link":"#创建","children":[]},{"level":3,"title":"配置DispatcherServlet","slug":"配置dispatcherservlet","link":"#配置dispatcherservlet","children":[]},{"level":3,"title":"通过控制器接收请求","slug":"通过控制器接收请求","link":"#通过控制器接收请求","children":[]},{"level":3,"title":"显示页面","slug":"显示页面","link":"#显示页面","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.74,"words":1423},"filePathRelative":"note/framework/springmvc/basic/2102202010.md","localizedDate":"2019年5月12日","excerpt":"

SpringMVC 基础(一)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2102202010.html-63e2a662.js b/assets/2102202010.html-ceaf693a.js similarity index 99% rename from assets/2102202010.html-63e2a662.js rename to assets/2102202010.html-ceaf693a.js index d84efea1..d9daf025 100644 --- a/assets/2102202010.html-63e2a662.js +++ b/assets/2102202010.html-ceaf693a.js @@ -1,4 +1,4 @@ -import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"springmvc-基础-一",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-一","aria-hidden":"true"},"#"),e(" SpringMVC 基础(一)")],-1),i=n("p",null,"SpringMVC 基础笔记系列",-1),u=o(`

SpringMVC框架

作用

SpringMVC框架解决了V与C的交互问题。

原生的Servlet就是控制器,使用Servlet主要存在的问题是实例太多,配置麻烦,管理难度大等一系列的问题。例如项目中有用户注册功能,则可能需要开发UserRegisterServlet
,如果还有登录功能,则可能需要开发UserLoginServlet,几乎是每个功能需要有1个对应的Servlet,如果一个项目中有200个不同的功能,则需要200个Servlet,在更大的系统中,Servlet
的数量就非常多,在实际运行时,在内存中的Servlet对象就会占据大量的内存空间!由于Servlet的数量很多,进而导致配置文件的配置信息会非常多,配置信息多了以后,就会引发管理难度大的问题。

SpringMVC中的核心组件

  • DispatcherServlet:前端控制器,用于接收所有请求,并负责分发;

  • HandlerMapping:根据请求路径映射控制器或控制器的方法,确定请求路径与控制器或控制器中的方法的对应关系;

  • Controller:实际处理请求的组件;

  • ModelAndView:控制器的返回结果,包括处理完成后的数据,及最终应该响应给客户端的视图名称;

  • ViewResolver:根据视图名称得到具体的视图组件。

具体的执行流程图:

SpringMVC HelloWorld

目标

在浏览器中通过http://localhost:8080/项目名称/hello.do可以访问某个JSP显示的页面,页面中显示**Hello, SpringMVC!!!**字样。

创建

创建Maven ProjectArtifact Idcn.tedu.springArtifact IdSPRINGMVC01Packaing必须选择war

创建完成后,首先生成web.xml文件。

然后,在pom.xml中添加spring-webmvc的依赖。

然后,将Spring的配置文件复制到src/main/resources下。

另外,还需要添加Tomcat运行环境。

配置DispatcherServlet

打开web.xml,在配置文件中对DispatcherServlet进行配置,使之可以处理所有以.do结尾的请求:

<node>
+import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"springmvc-基础-一",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-一","aria-hidden":"true"},"#"),e(" SpringMVC 基础(一)")],-1),i=n("p",null,"SpringMVC 基础笔记系列",-1),u=o(`

SpringMVC框架

作用

SpringMVC框架解决了V与C的交互问题。

原生的Servlet就是控制器,使用Servlet主要存在的问题是实例太多,配置麻烦,管理难度大等一系列的问题。例如项目中有用户注册功能,则可能需要开发UserRegisterServlet
,如果还有登录功能,则可能需要开发UserLoginServlet,几乎是每个功能需要有1个对应的Servlet,如果一个项目中有200个不同的功能,则需要200个Servlet,在更大的系统中,Servlet
的数量就非常多,在实际运行时,在内存中的Servlet对象就会占据大量的内存空间!由于Servlet的数量很多,进而导致配置文件的配置信息会非常多,配置信息多了以后,就会引发管理难度大的问题。

SpringMVC中的核心组件

  • DispatcherServlet:前端控制器,用于接收所有请求,并负责分发;

  • HandlerMapping:根据请求路径映射控制器或控制器的方法,确定请求路径与控制器或控制器中的方法的对应关系;

  • Controller:实际处理请求的组件;

  • ModelAndView:控制器的返回结果,包括处理完成后的数据,及最终应该响应给客户端的视图名称;

  • ViewResolver:根据视图名称得到具体的视图组件。

具体的执行流程图:

SpringMVC HelloWorld

目标

在浏览器中通过http://localhost:8080/项目名称/hello.do可以访问某个JSP显示的页面,页面中显示**Hello, SpringMVC!!!**字样。

创建

创建Maven ProjectArtifact Idcn.tedu.springArtifact IdSPRINGMVC01Packaing必须选择war

创建完成后,首先生成web.xml文件。

然后,在pom.xml中添加spring-webmvc的依赖。

然后,将Spring的配置文件复制到src/main/resources下。

另外,还需要添加Tomcat运行环境。

配置DispatcherServlet

打开web.xml,在配置文件中对DispatcherServlet进行配置,使之可以处理所有以.do结尾的请求:

<node>
     <servlet>
         <servlet-name>SpringMVC</servlet-name>
         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
diff --git a/assets/2102202011.html-cfac6552.js b/assets/2102202011.html-a488edf1.js
similarity index 97%
rename from assets/2102202011.html-cfac6552.js
rename to assets/2102202011.html-a488edf1.js
index 8e6ffa34..642be578 100644
--- a/assets/2102202011.html-cfac6552.js
+++ b/assets/2102202011.html-a488edf1.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-e9c712b8","path":"/note/framework/springmvc/basic/2102202011.html","title":"SpringMVC 基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-13T00:00:00.000Z","index":true,"order":2,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"接收请求参数","slug":"接收请求参数","link":"#接收请求参数","children":[{"level":3,"title":"(不推荐) 使用HttpServletRequest","slug":"不推荐-使用httpservletrequest","link":"#不推荐-使用httpservletrequest","children":[]},{"level":3,"title":"(推荐) 将请求参数设计为处理请求的方法的参数","slug":"推荐-将请求参数设计为处理请求的方法的参数","link":"#推荐-将请求参数设计为处理请求的方法的参数","children":[]},{"level":3,"title":"(推荐) 使用封装的类型作为处理请求的方法的参数","slug":"推荐-使用封装的类型作为处理请求的方法的参数","link":"#推荐-使用封装的类型作为处理请求的方法的参数","children":[]},{"level":3,"title":"如何选取各种获取请求参数的方式","slug":"如何选取各种获取请求参数的方式","link":"#如何选取各种获取请求参数的方式","children":[]}]},{"level":2,"title":"重定向","slug":"重定向","link":"#重定向","children":[]},{"level":2,"title":"转发数据","slug":"转发数据","link":"#转发数据","children":[{"level":3,"title":"(不推荐) 通过HttpServletRequest参数封装转发的数据","slug":"不推荐-通过httpservletrequest参数封装转发的数据","link":"#不推荐-通过httpservletrequest参数封装转发的数据","children":[]},{"level":3,"title":"(更不推荐) 使用ModelAndView","slug":"更不推荐-使用modelandview","link":"#更不推荐-使用modelandview","children":[]},{"level":3,"title":"(推荐) 使用ModelMap封装转发的数据","slug":"推荐-使用modelmap封装转发的数据","link":"#推荐-使用modelmap封装转发的数据","children":[]}]},{"level":2,"title":"关于@RequestMapping注解","slug":"关于-requestmapping注解","link":"#关于-requestmapping注解","children":[]},{"level":2,"title":"关于@RequestParam注解","slug":"关于-requestparam注解","link":"#关于-requestparam注解","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.71,"words":2314},"filePathRelative":"note/framework/springmvc/basic/2102202011.md","localizedDate":"2019年5月13日","excerpt":"

SpringMVC 基础(二)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-e9c712b8","path":"/note/framework/springmvc/basic/2102202011.html","title":"SpringMVC 基础(二)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-13T00:00:00.000Z","index":true,"order":2,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"接收请求参数","slug":"接收请求参数","link":"#接收请求参数","children":[{"level":3,"title":"(不推荐) 使用HttpServletRequest","slug":"不推荐-使用httpservletrequest","link":"#不推荐-使用httpservletrequest","children":[]},{"level":3,"title":"(推荐) 将请求参数设计为处理请求的方法的参数","slug":"推荐-将请求参数设计为处理请求的方法的参数","link":"#推荐-将请求参数设计为处理请求的方法的参数","children":[]},{"level":3,"title":"(推荐) 使用封装的类型作为处理请求的方法的参数","slug":"推荐-使用封装的类型作为处理请求的方法的参数","link":"#推荐-使用封装的类型作为处理请求的方法的参数","children":[]},{"level":3,"title":"如何选取各种获取请求参数的方式","slug":"如何选取各种获取请求参数的方式","link":"#如何选取各种获取请求参数的方式","children":[]}]},{"level":2,"title":"重定向","slug":"重定向","link":"#重定向","children":[]},{"level":2,"title":"转发数据","slug":"转发数据","link":"#转发数据","children":[{"level":3,"title":"(不推荐) 通过HttpServletRequest参数封装转发的数据","slug":"不推荐-通过httpservletrequest参数封装转发的数据","link":"#不推荐-通过httpservletrequest参数封装转发的数据","children":[]},{"level":3,"title":"(更不推荐) 使用ModelAndView","slug":"更不推荐-使用modelandview","link":"#更不推荐-使用modelandview","children":[]},{"level":3,"title":"(推荐) 使用ModelMap封装转发的数据","slug":"推荐-使用modelmap封装转发的数据","link":"#推荐-使用modelmap封装转发的数据","children":[]}]},{"level":2,"title":"关于@RequestMapping注解","slug":"关于-requestmapping注解","link":"#关于-requestmapping注解","children":[]},{"level":2,"title":"关于@RequestParam注解","slug":"关于-requestparam注解","link":"#关于-requestparam注解","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.71,"words":2314},"filePathRelative":"note/framework/springmvc/basic/2102202011.md","localizedDate":"2019年5月13日","excerpt":"

SpringMVC 基础(二)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2102202011.html-edc165f3.js b/assets/2102202011.html-d7d74653.js similarity index 99% rename from assets/2102202011.html-edc165f3.js rename to assets/2102202011.html-d7d74653.js index 43fd58dd..cb7b8eeb 100644 --- a/assets/2102202011.html-edc165f3.js +++ b/assets/2102202011.html-d7d74653.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"springmvc-基础-二",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-二","aria-hidden":"true"},"#"),e(" SpringMVC 基础(二)")],-1),u=n("p",null,"SpringMVC 基础笔记系列",-1),i=o(`

接收请求参数

(不推荐) 使用HttpServletRequest

在处理请求的方法的参数列表中添加HttpServletRequest参数,然后,在处理过程中,调用requestgetParameter()方法即可获取各请求参数的值:

	@RequestMapping("handle_register.do")
+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"springmvc-基础-二",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-二","aria-hidden":"true"},"#"),e(" SpringMVC 基础(二)")],-1),u=n("p",null,"SpringMVC 基础笔记系列",-1),i=o(`

接收请求参数

(不推荐) 使用HttpServletRequest

在处理请求的方法的参数列表中添加HttpServletRequest参数,然后,在处理过程中,调用requestgetParameter()方法即可获取各请求参数的值:

	@RequestMapping("handle_register.do")
 	public String handleRegister(HttpServletRequest request) {
 		System.out.println("UserController.handleRegister()");
 		String username = request.getParameter("username");
diff --git a/assets/2102202012.html-b2c89c59.js b/assets/2102202012.html-2374338c.js
similarity index 90%
rename from assets/2102202012.html-b2c89c59.js
rename to assets/2102202012.html-2374338c.js
index 3678de85..5aa42e97 100644
--- a/assets/2102202012.html-b2c89c59.js
+++ b/assets/2102202012.html-2374338c.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-e65d617a","path":"/note/framework/springmvc/basic/2102202012.html","title":"SpringMVC 基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-14T00:00:00.000Z","index":true,"order":3,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"SpringMVC中的拦截器(Interceptor)","slug":"springmvc中的拦截器-interceptor","link":"#springmvc中的拦截器-interceptor","children":[{"level":3,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[]},{"level":3,"title":"开发拦截器","slug":"开发拦截器","link":"#开发拦截器","children":[]}]},{"level":2,"title":"请求参数的乱码解决方案","slug":"请求参数的乱码解决方案","link":"#请求参数的乱码解决方案","children":[]},{"level":2,"title":"在SpringMVC中统一处理异常","slug":"在springmvc中统一处理异常","link":"#在springmvc中统一处理异常","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.29,"words":1588},"filePathRelative":"note/framework/springmvc/basic/2102202012.md","localizedDate":"2019年5月14日","excerpt":"

SpringMVC 基础(三)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-e65d617a","path":"/note/framework/springmvc/basic/2102202012.html","title":"SpringMVC 基础(三)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-14T00:00:00.000Z","index":true,"order":3,"category":["SpringMVC"],"tag":["基础"]},"headers":[{"level":2,"title":"SpringMVC中的拦截器(Interceptor)","slug":"springmvc中的拦截器-interceptor","link":"#springmvc中的拦截器-interceptor","children":[{"level":3,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[]},{"level":3,"title":"开发拦截器","slug":"开发拦截器","link":"#开发拦截器","children":[]}]},{"level":2,"title":"请求参数的乱码解决方案","slug":"请求参数的乱码解决方案","link":"#请求参数的乱码解决方案","children":[]},{"level":2,"title":"在SpringMVC中统一处理异常","slug":"在springmvc中统一处理异常","link":"#在springmvc中统一处理异常","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.29,"words":1588},"filePathRelative":"note/framework/springmvc/basic/2102202012.md","localizedDate":"2019年5月14日","excerpt":"

SpringMVC 基础(三)

\\n

SpringMVC 基础笔记系列

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2102202012.html-5b6457df.js b/assets/2102202012.html-c2e13622.js similarity index 99% rename from assets/2102202012.html-5b6457df.js rename to assets/2102202012.html-c2e13622.js index 37796064..ee0c87fc 100644 --- a/assets/2102202012.html-5b6457df.js +++ b/assets/2102202012.html-c2e13622.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as c}from"./app-1efcbe9f.js";const o={},l=n("h1",{id:"springmvc-基础-三",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-三","aria-hidden":"true"},"#"),e(" SpringMVC 基础(三)")],-1),i=n("p",null,"SpringMVC 基础笔记系列",-1),u=c(`

SpringMVC中的拦截器(Interceptor)

基本概念

在SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。

注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。

开发拦截器

首先,所有的拦截器类都必须实现HandlerInterceptor接口,可以自定义LoginInterceptor

	public class LoginInterceptor implements HandlerInterceptor {
+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as c}from"./app-6a63891c.js";const o={},l=n("h1",{id:"springmvc-基础-三",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#springmvc-基础-三","aria-hidden":"true"},"#"),e(" SpringMVC 基础(三)")],-1),i=n("p",null,"SpringMVC 基础笔记系列",-1),u=c(`

SpringMVC中的拦截器(Interceptor)

基本概念

在SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。

注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。

开发拦截器

首先,所有的拦截器类都必须实现HandlerInterceptor接口,可以自定义LoginInterceptor

	public class LoginInterceptor implements HandlerInterceptor {
 	
 		public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
 				throws Exception {
diff --git a/assets/2301101200.html-9fa665b1.js b/assets/2301101200.html-3dfbc147.js
similarity index 89%
rename from assets/2301101200.html-9fa665b1.js
rename to assets/2301101200.html-3dfbc147.js
index 00b823e1..853ce28b 100644
--- a/assets/2301101200.html-9fa665b1.js
+++ b/assets/2301101200.html-3dfbc147.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-249319d7","path":"/note/structure/2301101200.html","title":"栈(Stack)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["数据结构"],"tag":["栈"]},"headers":[{"level":2,"title":"图形分析","slug":"图形分析","link":"#图形分析","children":[]},{"level":2,"title":"思路分析","slug":"思路分析","link":"#思路分析","children":[]},{"level":2,"title":"实现代码","slug":"实现代码","link":"#实现代码","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.55,"words":465},"filePathRelative":"note/structure/2301101200.md","localizedDate":"2023年5月7日","excerpt":"

栈(Stack)

\\n

栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-249319d7","path":"/note/structure/2301101200.html","title":"栈(Stack)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["数据结构"],"tag":["栈"]},"headers":[{"level":2,"title":"图形分析","slug":"图形分析","link":"#图形分析","children":[]},{"level":2,"title":"思路分析","slug":"思路分析","link":"#思路分析","children":[]},{"level":2,"title":"实现代码","slug":"实现代码","link":"#实现代码","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.55,"words":465},"filePathRelative":"note/structure/2301101200.md","localizedDate":"2023年5月7日","excerpt":"

栈(Stack)

\\n

栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2301101200.html-564885be.js b/assets/2301101200.html-794943d8.js similarity index 99% rename from assets/2301101200.html-564885be.js rename to assets/2301101200.html-794943d8.js index b2e6f14f..38a9db76 100644 --- a/assets/2301101200.html-564885be.js +++ b/assets/2301101200.html-794943d8.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},i=n("h1",{id:"栈-stack",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#栈-stack","aria-hidden":"true"},"#"),e(" 栈(Stack)")],-1),l=n("p",null,"栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。",-1),u=o(`

图形分析

思路分析

  1. 定义一个top来表示入栈的数量(栈顶),当添加一个数据时top会指向新添加的数据;
  2. 设置top = -1;(初始值)默认栈为空,当 top == stack.size() 时表示栈满;
  3. 定义一个数组 stack 模拟栈,保存需要入栈的值;
  4. 入栈:接收一个值,将值保存到 stack 中,并将 top 的位置上移 ;
  5. 出栈:将栈顶top的值取出,并将top下移,重新标记栈顶的位置;

实现代码

//定义一个 ArrayStack 表示栈结构
+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},i=n("h1",{id:"栈-stack",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#栈-stack","aria-hidden":"true"},"#"),e(" 栈(Stack)")],-1),l=n("p",null,"栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。",-1),u=o(`

图形分析

思路分析

  1. 定义一个top来表示入栈的数量(栈顶),当添加一个数据时top会指向新添加的数据;
  2. 设置top = -1;(初始值)默认栈为空,当 top == stack.size() 时表示栈满;
  3. 定义一个数组 stack 模拟栈,保存需要入栈的值;
  4. 入栈:接收一个值,将值保存到 stack 中,并将 top 的位置上移 ;
  5. 出栈:将栈顶top的值取出,并将top下移,重新标记栈顶的位置;

实现代码

//定义一个 ArrayStack 表示栈结构
 class ArrayStack {
 	private int maxSize; // 栈的大小
 	private int[] stack; // 数组,模拟一个栈,用于存放
diff --git a/assets/2301101203.html-c687ba0e.js b/assets/2301101203.html-12235a8e.js
similarity index 90%
rename from assets/2301101203.html-c687ba0e.js
rename to assets/2301101203.html-12235a8e.js
index a3d5ea61..53bdc9da 100644
--- a/assets/2301101203.html-c687ba0e.js
+++ b/assets/2301101203.html-12235a8e.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-79efb2b0","path":"/note/algorithm/2301101203.html","title":"基数(桶)排序","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-02-03T00:00:00.000Z","category":["算法"],"tag":["排序"]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":2,"title":"图解","slug":"图解","link":"#图解","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.04,"words":912},"filePathRelative":"note/algorithm/2301101203.md","localizedDate":"2023年2月3日","excerpt":"

基数(桶)排序

\\n

基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-79efb2b0","path":"/note/algorithm/2301101203.html","title":"基数(桶)排序","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-02-03T00:00:00.000Z","category":["算法"],"tag":["排序"]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":2,"title":"图解","slug":"图解","link":"#图解","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.04,"words":912},"filePathRelative":"note/algorithm/2301101203.md","localizedDate":"2023年2月3日","excerpt":"

基数(桶)排序

\\n

基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2301101203.html-fd6bbc11.js b/assets/2301101203.html-bffe4d93.js similarity index 99% rename from assets/2301101203.html-fd6bbc11.js rename to assets/2301101203.html-bffe4d93.js index 1e12f32b..cca6d4be 100644 --- a/assets/2301101203.html-fd6bbc11.js +++ b/assets/2301101203.html-bffe4d93.js @@ -1,4 +1,4 @@ -import{_ as t,r as p,o as e,c as o,d as c,a as n,b as s,f as l,e as i}from"./app-1efcbe9f.js";const u={},r=n("h1",{id:"基数-桶-排序",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#基数-桶-排序","aria-hidden":"true"},"#"),s(" 基数(桶)排序")],-1),k=n("p",null,"基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。",-1),d=i(`

介绍

  1. 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

  2. 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

  3. 基数排序(Radix Sort)是桶排序的扩展

  4. 基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

基本思想

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

图解

注意:排序的次数取决于最大数值元素的位数

代码实现

package com.ygl.sort;
+import{_ as t,r as p,o as e,c as o,d as c,a as n,b as s,f as l,e as i}from"./app-6a63891c.js";const u={},r=n("h1",{id:"基数-桶-排序",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#基数-桶-排序","aria-hidden":"true"},"#"),s(" 基数(桶)排序")],-1),k=n("p",null,"基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。",-1),d=i(`

介绍

  1. 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

  2. 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

  3. 基数排序(Radix Sort)是桶排序的扩展

  4. 基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

基本思想

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

图解

注意:排序的次数取决于最大数值元素的位数

代码实现

package com.ygl.sort;
 
 import java.util.Arrays;
 
diff --git a/assets/2301101204.html-e47dc170.js b/assets/2301101204.html-12a956c7.js
similarity index 94%
rename from assets/2301101204.html-e47dc170.js
rename to assets/2301101204.html-12a956c7.js
index d1c3fc31..cb8811f7 100644
--- a/assets/2301101204.html-e47dc170.js
+++ b/assets/2301101204.html-12a956c7.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-7ba48b4f","path":"/note/algorithm/2301101204.html","title":"归并排序","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-03-01T00:00:00.000Z","category":["算法"],"tag":["排序"]},"headers":[{"level":2,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":2,"title":"合并的实现图解如下","slug":"合并的实现图解如下","link":"#合并的实现图解如下","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.71,"words":814},"filePathRelative":"note/algorithm/2301101204.md","localizedDate":"2023年3月1日","excerpt":"

归并排序

\\n

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-7ba48b4f","path":"/note/algorithm/2301101204.html","title":"归并排序","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-03-01T00:00:00.000Z","category":["算法"],"tag":["排序"]},"headers":[{"level":2,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":2,"title":"合并的实现图解如下","slug":"合并的实现图解如下","link":"#合并的实现图解如下","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.71,"words":814},"filePathRelative":"note/algorithm/2301101204.md","localizedDate":"2023年3月1日","excerpt":"

归并排序

\\n

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2301101204.html-d8c173f0.js b/assets/2301101204.html-e1f3ef94.js similarity index 99% rename from assets/2301101204.html-d8c173f0.js rename to assets/2301101204.html-e1f3ef94.js index ea0e688e..bb840065 100644 --- a/assets/2301101204.html-d8c173f0.js +++ b/assets/2301101204.html-e1f3ef94.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},i=n("h1",{id:"归并排序",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#归并排序","aria-hidden":"true"},"#"),e(" 归并排序")],-1),l=n("p",null,"归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。",-1),u=o(`

基本思想

将一个需要排序的数组通过递归进行拆分,当每一个元素都是一个个体的时候,再进行合并,合并时将合并的数据保存到一个临时开辟的空间中,这意味着需要额外的空间来保存数据;这个算法主要分为三步: (一):拆分
将一个初始的数组递归拆分,将每一个元素拆分至单个个体独立存在(这里并不对数据进行操作) (二):合并(核心) 将拆分的当个个体元素进行合并,合并的过程中进行排序,并保存到临时创建的空间内(需要对数据进行排序操作) (三):拷贝
将临时空间内的数据拷贝到原数组中

合并的实现图解如下


代码实现

package com.ygl.sort;
+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},i=n("h1",{id:"归并排序",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#归并排序","aria-hidden":"true"},"#"),e(" 归并排序")],-1),l=n("p",null,"归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。",-1),u=o(`

基本思想

将一个需要排序的数组通过递归进行拆分,当每一个元素都是一个个体的时候,再进行合并,合并时将合并的数据保存到一个临时开辟的空间中,这意味着需要额外的空间来保存数据;这个算法主要分为三步: (一):拆分
将一个初始的数组递归拆分,将每一个元素拆分至单个个体独立存在(这里并不对数据进行操作) (二):合并(核心) 将拆分的当个个体元素进行合并,合并的过程中进行排序,并保存到临时创建的空间内(需要对数据进行排序操作) (三):拷贝
将临时空间内的数据拷贝到原数组中

合并的实现图解如下


代码实现

package com.ygl.sort;
 
 import java.util.Arrays;
 import java.util.Date;
diff --git a/assets/2302201400.html-1488f35d.js b/assets/2302201400.html-3f58b59f.js
similarity index 99%
rename from assets/2302201400.html-1488f35d.js
rename to assets/2302201400.html-3f58b59f.js
index 341352f2..23634a4a 100644
--- a/assets/2302201400.html-1488f35d.js
+++ b/assets/2302201400.html-3f58b59f.js
@@ -1,4 +1,4 @@
-import{_ as s,o as a,c as t,d as p,a as n,b as o,e}from"./app-1efcbe9f.js";const c={},i=n("h1",{id:"多边形等距离外扩",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#多边形等距离外扩","aria-hidden":"true"},"#"),o(" 多边形等距离外扩")],-1),l=n("p",null,"实现多边形形成的多边形进行等距外扩或收缩的算法实现",-1),u=e(`

涉及技术

  • 腾讯地图
  • jdk1.8

问题描述

开发一款应用,前端在地图上标记经纬度坐标点集合形成一个多边形,现需要在后端将多边形做一个等距外扩或者收缩。

在网上查了好多的算法,经过尝试验证,在小经度多边形进行收缩外扩都或多或少存在一些问题,因此自己参考写出了

一个多边形外扩收缩的算法。

JAVA代码实现

坐标点实体(Point)

/**
+import{_ as s,o as a,c as t,d as p,a as n,b as o,e}from"./app-6a63891c.js";const c={},i=n("h1",{id:"多边形等距离外扩",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#多边形等距离外扩","aria-hidden":"true"},"#"),o(" 多边形等距离外扩")],-1),l=n("p",null,"实现多边形形成的多边形进行等距外扩或收缩的算法实现",-1),u=e(`

涉及技术

  • 腾讯地图
  • jdk1.8

问题描述

开发一款应用,前端在地图上标记经纬度坐标点集合形成一个多边形,现需要在后端将多边形做一个等距外扩或者收缩。

在网上查了好多的算法,经过尝试验证,在小经度多边形进行收缩外扩都或多或少存在一些问题,因此自己参考写出了

一个多边形外扩收缩的算法。

JAVA代码实现

坐标点实体(Point)

/**
  * 地图上一个点的经纬度
  *
  * @author yanggl
diff --git a/assets/2302201400.html-e38b2bce.js b/assets/2302201400.html-94557db3.js
similarity index 95%
rename from assets/2302201400.html-e38b2bce.js
rename to assets/2302201400.html-94557db3.js
index d8567a84..982679c9 100644
--- a/assets/2302201400.html-e38b2bce.js
+++ b/assets/2302201400.html-94557db3.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-1dbb5152","path":"/note/other/2302201400.html","title":"多边形等距离外扩","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"sticky":1,"star":1,"cover":"/assets/images/cover3.jpg","category":["算法","地图"],"tag":["多边形"]},"headers":[{"level":2,"title":"涉及技术","slug":"涉及技术","link":"#涉及技术","children":[]},{"level":2,"title":"问题描述","slug":"问题描述","link":"#问题描述","children":[]},{"level":2,"title":"JAVA代码实现","slug":"java代码实现","link":"#java代码实现","children":[{"level":3,"title":"坐标点实体(Point)","slug":"坐标点实体-point","link":"#坐标点实体-point","children":[]},{"level":3,"title":"向量工具类 (VectorUtil)","slug":"向量工具类-vectorutil","link":"#向量工具类-vectorutil","children":[]},{"level":3,"title":"核心工具类 (DistanceUtil)","slug":"核心工具类-distanceutil","link":"#核心工具类-distanceutil","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.96,"words":887},"filePathRelative":"note/other/2302201400.md","localizedDate":"2023年5月7日","excerpt":"

多边形等距离外扩

\\n

实现多边形形成的多边形进行等距外扩或收缩的算法实现

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1dbb5152","path":"/note/other/2302201400.html","title":"多边形等距离外扩","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"sticky":1,"star":1,"cover":"/assets/images/cover3.jpg","category":["算法","地图"],"tag":["多边形"]},"headers":[{"level":2,"title":"涉及技术","slug":"涉及技术","link":"#涉及技术","children":[]},{"level":2,"title":"问题描述","slug":"问题描述","link":"#问题描述","children":[]},{"level":2,"title":"JAVA代码实现","slug":"java代码实现","link":"#java代码实现","children":[{"level":3,"title":"坐标点实体(Point)","slug":"坐标点实体-point","link":"#坐标点实体-point","children":[]},{"level":3,"title":"向量工具类 (VectorUtil)","slug":"向量工具类-vectorutil","link":"#向量工具类-vectorutil","children":[]},{"level":3,"title":"核心工具类 (DistanceUtil)","slug":"核心工具类-distanceutil","link":"#核心工具类-distanceutil","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.96,"words":887},"filePathRelative":"note/other/2302201400.md","localizedDate":"2023年5月7日","excerpt":"

多边形等距离外扩

\\n

实现多边形形成的多边形进行等距外扩或收缩的算法实现

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2302201401.html-ef86aa7a.js b/assets/2302201401.html-08f31c5e.js similarity index 99% rename from assets/2302201401.html-ef86aa7a.js rename to assets/2302201401.html-08f31c5e.js index 70fc0adb..deee65d5 100644 --- a/assets/2302201401.html-ef86aa7a.js +++ b/assets/2302201401.html-08f31c5e.js @@ -1,4 +1,4 @@ -import{_ as o,r as e,o as c,c as u,d as l,a as n,b as s,f as t,e as p}from"./app-1efcbe9f.js";const i={},k=n("h1",{id:"微信服务号推送服务模板消息",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#微信服务号推送服务模板消息","aria-hidden":"true"},"#"),s(" 微信服务号推送服务模板消息")],-1),r=n("p",null,"记录通过微信服务号推送服务模版消息的实现",-1),d=n("h2",{id:"业务需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#业务需求","aria-hidden":"true"},"#"),s(" 业务需求")],-1),m={href:"https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html",target:"_blank",rel:"noopener noreferrer"},v=n("br",null,null,-1),b={href:"https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Operation_Specifications.html",target:"_blank",rel:"noopener noreferrer"},g=n("br",null,null,-1),q=p('

认证的服务号

要使用模板功能,该服务号必须是认证的,且接收的对象必须关注此服务号,否则无法推送

服务号中添加模板

在添加模板之前,需要开通模板消息接口服务,可使用的接口以及限制可在最地下的接口权限查看。

获取access_token

',6),h={href:"https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html",target:"_blank",rel:"noopener noreferrer"},_=n("br",null,null,-1),f={href:"https://mp.weixin.qq.com/debug?token=2007817736&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"},w=n("br",null,null,-1),O=n("img",{src:"https://qiniu.yanggl.cn/image/2302201401_2.png",alt:"",loading:"lazy"},null,-1),y=n("br",null,null,-1),x=p(`

调用后台代码,发送模板消息

    @Test
+import{_ as o,r as e,o as c,c as u,d as l,a as n,b as s,f as t,e as p}from"./app-6a63891c.js";const i={},k=n("h1",{id:"微信服务号推送服务模板消息",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#微信服务号推送服务模板消息","aria-hidden":"true"},"#"),s(" 微信服务号推送服务模板消息")],-1),r=n("p",null,"记录通过微信服务号推送服务模版消息的实现",-1),d=n("h2",{id:"业务需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#业务需求","aria-hidden":"true"},"#"),s(" 业务需求")],-1),m={href:"https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html",target:"_blank",rel:"noopener noreferrer"},v=n("br",null,null,-1),b={href:"https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Operation_Specifications.html",target:"_blank",rel:"noopener noreferrer"},g=n("br",null,null,-1),q=p('

认证的服务号

要使用模板功能,该服务号必须是认证的,且接收的对象必须关注此服务号,否则无法推送

服务号中添加模板

在添加模板之前,需要开通模板消息接口服务,可使用的接口以及限制可在最地下的接口权限查看。

获取access_token

',6),h={href:"https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html",target:"_blank",rel:"noopener noreferrer"},_=n("br",null,null,-1),f={href:"https://mp.weixin.qq.com/debug?token=2007817736&lang=zh_CN",target:"_blank",rel:"noopener noreferrer"},w=n("br",null,null,-1),O=n("img",{src:"https://qiniu.yanggl.cn/image/2302201401_2.png",alt:"",loading:"lazy"},null,-1),y=n("br",null,null,-1),x=p(`

调用后台代码,发送模板消息

    @Test
     public void httpTest2() {
         RestTemplate restTemplate = new RestTemplate();
         String ACCESS_TOKEN = "ACCESS_TOKEN ";
diff --git a/assets/2302201401.html-a4498f7c.js b/assets/2302201401.html-81f9bb22.js
similarity index 90%
rename from assets/2302201401.html-a4498f7c.js
rename to assets/2302201401.html-81f9bb22.js
index d18e7d37..88805fe7 100644
--- a/assets/2302201401.html-a4498f7c.js
+++ b/assets/2302201401.html-81f9bb22.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-1f7029f1","path":"/note/other/2302201401.html","title":"微信服务号推送服务模板消息","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["微信"],"tag":["微信服务号"]},"headers":[{"level":2,"title":"业务需求","slug":"业务需求","link":"#业务需求","children":[]},{"level":2,"title":"认证的服务号","slug":"认证的服务号","link":"#认证的服务号","children":[]},{"level":2,"title":"服务号中添加模板","slug":"服务号中添加模板","link":"#服务号中添加模板","children":[]},{"level":2,"title":"获取access_token","slug":"获取access-token","link":"#获取access-token","children":[]},{"level":2,"title":"调用后台代码,发送模板消息","slug":"调用后台代码-发送模板消息","link":"#调用后台代码-发送模板消息","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.92,"words":576},"filePathRelative":"note/other/2302201401.md","localizedDate":"2023年5月7日","excerpt":"

微信服务号推送服务模板消息

\\n

记录通过微信服务号推送服务模版消息的实现

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1f7029f1","path":"/note/other/2302201401.html","title":"微信服务号推送服务模板消息","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["微信"],"tag":["微信服务号"]},"headers":[{"level":2,"title":"业务需求","slug":"业务需求","link":"#业务需求","children":[]},{"level":2,"title":"认证的服务号","slug":"认证的服务号","link":"#认证的服务号","children":[]},{"level":2,"title":"服务号中添加模板","slug":"服务号中添加模板","link":"#服务号中添加模板","children":[]},{"level":2,"title":"获取access_token","slug":"获取access-token","link":"#获取access-token","children":[]},{"level":2,"title":"调用后台代码,发送模板消息","slug":"调用后台代码-发送模板消息","link":"#调用后台代码-发送模板消息","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.92,"words":576},"filePathRelative":"note/other/2302201401.md","localizedDate":"2023年5月7日","excerpt":"

微信服务号推送服务模板消息

\\n

记录通过微信服务号推送服务模版消息的实现

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2302201404.html-7d4e0f66.js b/assets/2302201404.html-1b00791e.js similarity index 99% rename from assets/2302201404.html-7d4e0f66.js rename to assets/2302201404.html-1b00791e.js index 6035824d..8fe370fc 100644 --- a/assets/2302201404.html-7d4e0f66.js +++ b/assets/2302201404.html-1b00791e.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as o,e}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"layui实现根据条件改变表头",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#layui实现根据条件改变表头","aria-hidden":"true"},"#"),o(" LayUI实现根据条件改变表头")],-1),r=n("p",null,"使用LayUI框架,请求后台数据并将数据渲染到表格中。",-1),u=e(`

需求

要求:根据 收支科目 调整表头展示项。

数据源(cols.js)

/**
+import{_ as s,o as a,c as t,d as p,a as n,b as o,e}from"./app-6a63891c.js";const c={},l=n("h1",{id:"layui实现根据条件改变表头",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#layui实现根据条件改变表头","aria-hidden":"true"},"#"),o(" LayUI实现根据条件改变表头")],-1),r=n("p",null,"使用LayUI框架,请求后台数据并将数据渲染到表格中。",-1),u=e(`

需求

要求:根据 收支科目 调整表头展示项。

数据源(cols.js)

/**
  * 表格头部数据
  * @type {{title: *[]}}
  */
diff --git a/assets/2302201404.html-18c11cc6.js b/assets/2302201404.html-31e7a726.js
similarity index 89%
rename from assets/2302201404.html-18c11cc6.js
rename to assets/2302201404.html-31e7a726.js
index ab3805ad..118ac8e9 100644
--- a/assets/2302201404.html-18c11cc6.js
+++ b/assets/2302201404.html-31e7a726.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-248eb3ce","path":"/note/other/2302201404.html","title":"LayUI实现根据条件改变表头","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["前端"],"tag":["LayUI"]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"数据源(cols.js)","slug":"数据源-cols-js","link":"#数据源-cols-js","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"效果展示","slug":"效果展示","link":"#效果展示","children":[]},{"level":2,"title":"存在问题","slug":"存在问题","link":"#存在问题","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1516},"filePathRelative":"note/other/2302201404.md","localizedDate":"2023年5月7日","excerpt":"

LayUI实现根据条件改变表头

\\n

使用LayUI框架,请求后台数据并将数据渲染到表格中。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-248eb3ce","path":"/note/other/2302201404.html","title":"LayUI实现根据条件改变表头","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["前端"],"tag":["LayUI"]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"数据源(cols.js)","slug":"数据源-cols-js","link":"#数据源-cols-js","children":[]},{"level":2,"title":"代码实现","slug":"代码实现","link":"#代码实现","children":[]},{"level":2,"title":"效果展示","slug":"效果展示","link":"#效果展示","children":[]},{"level":2,"title":"存在问题","slug":"存在问题","link":"#存在问题","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1516},"filePathRelative":"note/other/2302201404.md","localizedDate":"2023年5月7日","excerpt":"

LayUI实现根据条件改变表头

\\n

使用LayUI框架,请求后台数据并将数据渲染到表格中。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2304010120.html-10394f60.js b/assets/2304010120.html-2b707481.js similarity index 99% rename from assets/2304010120.html-10394f60.js rename to assets/2304010120.html-2b707481.js index 5a517591..74650b07 100644 --- a/assets/2304010120.html-10394f60.js +++ b/assets/2304010120.html-2b707481.js @@ -1 +1 @@ -import{_ as n,r as i,o as a,c as d,d as s,a as t,b as e,f as l,e as c}from"./app-1efcbe9f.js";const h={},o=t("h1",{id:"redis核心数据结构",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#redis核心数据结构","aria-hidden":"true"},"#"),e(" Redis核心数据结构")],-1),y=t("p",null,"Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。",-1),g=t("p",null,"Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。",-1),x=t("h2",{id:"地址",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#地址","aria-hidden":"true"},"#"),e(" 地址")],-1),k={href:"https://redis.io/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/redis/redis",target:"_blank",rel:"noopener noreferrer"},b={href:"https://redis.io/topics/data-types-intro",target:"_blank",rel:"noopener noreferrer"},p={href:"https://redis.io/commands",target:"_blank",rel:"noopener noreferrer"},f={href:"https://redis.io/documentation",target:"_blank",rel:"noopener noreferrer"},S=c('

基本特性

  • 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值;
  • 数据是存储在内存中的;
  • 键值对中键的类型可以是字符串、整形、浮点型等,且键是唯一的,相同的键会覆盖值;
  • 键值对中值类型可以是:String、Hash、List、Set、ZSet等 ;
  • 内置了复制、磁盘持久化、LUA脚本、事务、SSL、ACLS、客户端缓存、客户端代理等功能;
  • 提供了Redis哨兵、Redis cluster等高可用性的模式;

核心数据结构

String

String类型的结构对应JAVA的Map,key-val的存储形式

常用操作
命令描述
SET key value存入字符串
MSET [key val ...]批量存入字符串
GET key获取值
MGET [key ...]批量获取值
DEL [key ...]删除一个或多个键
EXPIRE key seconds设置一个键过期时间(s)
SETNX key valkey不存在且保存成功返回1,失败返回0,可用做分布式锁
incrby key val批量生产序列号
SET key val EX 10 NX防止程序意外终止导致死锁
应用场景
  1. 单值缓存
  2. 对象缓存
  3. 分布式锁
  4. 计数器
  5. Web集群session共享
  6. 分布式系统全局系列号等

Hash

Hash类型的结构,存储一个类型的key-val

常用操作
命令简述
HASH key field value存储一个哈希表key的键值
HASHNX key field value存储一个不存在的哈希表key的键值
HMSET key field获取哈希表key对应的field键值
HMGET key [field ...]批量获取field键值
HDEL key [field ...]批量删除
HLEN key返回哈希表key中field的数量
HGETALL key返回哈希表key中所有的键值
HINCRBY key field increment为哈希表key中field键的值加上增量
应用场景
  1. 对象缓存
  2. 电商购物车等
优缺点
  • 优点
    1. 同类数据归类整合储存,方便数据管理
    2. 相比String操作小号的内存与cpu更小
    3. 相比Stirng更节约存储空间
  • 缺点
    1. 过期功能不能使用在field上,只能使用在key上
    2. Redis集群架构下不适合大规模使用

List

List,列表结构,跟JAVA中的List基本相似

命令描述
LPUSH key [value ...]将一个或多个value值插入到key列表的表头
RPUSH key [value ...]将一个或多value值插入多key列表表尾
LPOP key返回并移除key列表的头元素
RPOP key返回并移除key列表的尾元素
LRANGE key start stop返回列表key中制定区域内的元素,区间则以start和stop指定
BLPOP [key...] timeot从key列表表头弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
BRPOP [key...] timeout从key列表尾部弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
应用场景
  1. stack(栈) LPUSH + LPOP
  2. Queue (队列) LPUSH + RPOP
  3. Blocking MQ(阻塞队列)LPUSH + BRPOP

Set

Set集合,val中存储多个不重复的元素

常用命令
命令描述
SADD key [momber ...]往集合key中存入元素,元素存在则忽略
SREM key [momber ...]从集合key中删除元素
SMEMBERS key获取集合key中所有元素
SCARD key获取集合key的元素个数
SISMEMBER key momber判断val元素是否存在于key集合中
SRANDMENMBER key [count]从集合key中选出count个元素,元素不移除
SPOP key [count]从集合key中选出count个元素并移除元素
SINTER [key ..]交集运算
SINTERSTORE newSet [key ...]将交集结果存入新集合newSet中
SUNION [key ...]并集运算
SUNIONSTORE newSet [key ...]将并集结果存入新集合newSet中
SDIFF [key ... ]差集运算
SDIFFSTORE newSet [key ...]将差集结果存入新集合newSet中
应用场景
  1. 抽奖
  2. 去重
  3. 点赞/取消点赞
  4. 集合操作实现关注模型等
  5. 集合操作实现商品赛选等

ZSet

与Set集合类似,ZSet集合是有序的

命令描述
ZADD key [[score member] ...]往有序集合key中加入带分值元素
ZREM key [member ...]从有序集合key中删除元素
ZSCORE key member返回有序集合key中member元素的分值
ZINCRBY key increment member为key中元素member的分值加上increment
ZCARD key返回key中的元素个数
ZRANGE key start stop [withscores]正序获取key中start到stop的元素
ZREVRANGE key start stop [withscores]倒序获取key中start到stop的元素
应用场景
  1. 微博排行榜
  2. 七日排行榜单等

Redis的单线程和高性能

  1. Redis的单线程并非是真正意义上的单线程。单线程主要是指网络IO键值对读写是由一个线程来完成,而这也是Redis对外提供键值存储服务的主要流程。Redis内部的其他功能,例如持久化、 异步删除、数据同步等等是会有额外的线程去执行的。
  2. 单个线程访问之所以还会那么快,是因为数据都存在内存中,所有的运算都是内存级别的运算,单线程同时避免了多线程所带来的上下文切换问题。
  3. 单线程的任务我们需要注意访问的命令,对于那些耗时的命令以及当访问一个bigkey的时候可能会导致Redis卡顿。
  4. 对于客户端的并发连接,Redis底层利用epoll模型来实现IO多路复用,将链接信息和时间放到队列中,依次放到文件事件分派器,事件分派器将事件分发给对应的事件处理器。
IO多路复用
IO多路复用

其他高级命令

keys

全局遍历所有的key,用来列出所有满足特定正则字符串规则的key,当Redis数据量较大时,性能会有所下降,应避免使用

scan

SCAN cursor [MATCH pattern] [COUNT count] 渐进式便利所有的键,相对于keys性能消耗更小,安全性更低

scan 参数提供了三个参数,第一个是 cursor 整数值(hash桶的索引值),第二个是 key 的正则模式,第三个是一次遍历的key的数量(参考值,底层遍历的数量不一定),并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。

⚠️注意:scan并非完美无瑕,如果在scan的过程中如果有键的变化(增加、 删除、 修改),那么遍历效果可能会碰到如下问题:
新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是我们在开发时需要考虑的。

info

查看Redis服务运行信息,分为以下9大块:

  • Server: 服务器运行的环境参数
  • Clients: 客户端相关信息
  • Memory: 服务器运行内存统计数据
  • Persistence: 持久化信息
  • Stats: 通用统计数据
  • Replication: 主从复制相关信息
  • CPU: CPU使用情况
  • Cluster: 集群情况
  • KeySpace: 键值对统计数量信息
',46);function m(R,_){const r=i("ExternalLinkIcon");return a(),d("div",null,[o,y,g,s(" more "),x,t("p",null,[e("官网:"),t("a",k,[e("https://redis.io/"),l(r)])]),t("p",null,[e("GitHub:"),t("a",u,[e("https://github.com/redis/redis"),l(r)])]),t("p",null,[e("Redis数据类型简介:"),t("a",b,[e("https://redis.io/topics/data-types-intro"),l(r)])]),t("p",null,[e("命令完整列表:"),t("a",p,[e("https://redis.io/commands"),l(r)])]),t("p",null,[e("完整文档地址:"),t("a",f,[e("https://redis.io/documentation"),l(r)])]),S])}const P=n(h,[["render",m],["__file","2304010120.html.vue"]]);export{P as default}; +import{_ as n,r as i,o as a,c as d,d as s,a as t,b as e,f as l,e as c}from"./app-6a63891c.js";const h={},o=t("h1",{id:"redis核心数据结构",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#redis核心数据结构","aria-hidden":"true"},"#"),e(" Redis核心数据结构")],-1),y=t("p",null,"Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。",-1),g=t("p",null,"Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。",-1),x=t("h2",{id:"地址",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#地址","aria-hidden":"true"},"#"),e(" 地址")],-1),k={href:"https://redis.io/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/redis/redis",target:"_blank",rel:"noopener noreferrer"},b={href:"https://redis.io/topics/data-types-intro",target:"_blank",rel:"noopener noreferrer"},p={href:"https://redis.io/commands",target:"_blank",rel:"noopener noreferrer"},f={href:"https://redis.io/documentation",target:"_blank",rel:"noopener noreferrer"},S=c('

基本特性

  • 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值;
  • 数据是存储在内存中的;
  • 键值对中键的类型可以是字符串、整形、浮点型等,且键是唯一的,相同的键会覆盖值;
  • 键值对中值类型可以是:String、Hash、List、Set、ZSet等 ;
  • 内置了复制、磁盘持久化、LUA脚本、事务、SSL、ACLS、客户端缓存、客户端代理等功能;
  • 提供了Redis哨兵、Redis cluster等高可用性的模式;

核心数据结构

String

String类型的结构对应JAVA的Map,key-val的存储形式

常用操作
命令描述
SET key value存入字符串
MSET [key val ...]批量存入字符串
GET key获取值
MGET [key ...]批量获取值
DEL [key ...]删除一个或多个键
EXPIRE key seconds设置一个键过期时间(s)
SETNX key valkey不存在且保存成功返回1,失败返回0,可用做分布式锁
incrby key val批量生产序列号
SET key val EX 10 NX防止程序意外终止导致死锁
应用场景
  1. 单值缓存
  2. 对象缓存
  3. 分布式锁
  4. 计数器
  5. Web集群session共享
  6. 分布式系统全局系列号等

Hash

Hash类型的结构,存储一个类型的key-val

常用操作
命令简述
HASH key field value存储一个哈希表key的键值
HASHNX key field value存储一个不存在的哈希表key的键值
HMSET key field获取哈希表key对应的field键值
HMGET key [field ...]批量获取field键值
HDEL key [field ...]批量删除
HLEN key返回哈希表key中field的数量
HGETALL key返回哈希表key中所有的键值
HINCRBY key field increment为哈希表key中field键的值加上增量
应用场景
  1. 对象缓存
  2. 电商购物车等
优缺点
  • 优点
    1. 同类数据归类整合储存,方便数据管理
    2. 相比String操作小号的内存与cpu更小
    3. 相比Stirng更节约存储空间
  • 缺点
    1. 过期功能不能使用在field上,只能使用在key上
    2. Redis集群架构下不适合大规模使用

List

List,列表结构,跟JAVA中的List基本相似

命令描述
LPUSH key [value ...]将一个或多个value值插入到key列表的表头
RPUSH key [value ...]将一个或多value值插入多key列表表尾
LPOP key返回并移除key列表的头元素
RPOP key返回并移除key列表的尾元素
LRANGE key start stop返回列表key中制定区域内的元素,区间则以start和stop指定
BLPOP [key...] timeot从key列表表头弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
BRPOP [key...] timeout从key列表尾部弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
应用场景
  1. stack(栈) LPUSH + LPOP
  2. Queue (队列) LPUSH + RPOP
  3. Blocking MQ(阻塞队列)LPUSH + BRPOP

Set

Set集合,val中存储多个不重复的元素

常用命令
命令描述
SADD key [momber ...]往集合key中存入元素,元素存在则忽略
SREM key [momber ...]从集合key中删除元素
SMEMBERS key获取集合key中所有元素
SCARD key获取集合key的元素个数
SISMEMBER key momber判断val元素是否存在于key集合中
SRANDMENMBER key [count]从集合key中选出count个元素,元素不移除
SPOP key [count]从集合key中选出count个元素并移除元素
SINTER [key ..]交集运算
SINTERSTORE newSet [key ...]将交集结果存入新集合newSet中
SUNION [key ...]并集运算
SUNIONSTORE newSet [key ...]将并集结果存入新集合newSet中
SDIFF [key ... ]差集运算
SDIFFSTORE newSet [key ...]将差集结果存入新集合newSet中
应用场景
  1. 抽奖
  2. 去重
  3. 点赞/取消点赞
  4. 集合操作实现关注模型等
  5. 集合操作实现商品赛选等

ZSet

与Set集合类似,ZSet集合是有序的

命令描述
ZADD key [[score member] ...]往有序集合key中加入带分值元素
ZREM key [member ...]从有序集合key中删除元素
ZSCORE key member返回有序集合key中member元素的分值
ZINCRBY key increment member为key中元素member的分值加上increment
ZCARD key返回key中的元素个数
ZRANGE key start stop [withscores]正序获取key中start到stop的元素
ZREVRANGE key start stop [withscores]倒序获取key中start到stop的元素
应用场景
  1. 微博排行榜
  2. 七日排行榜单等

Redis的单线程和高性能

  1. Redis的单线程并非是真正意义上的单线程。单线程主要是指网络IO键值对读写是由一个线程来完成,而这也是Redis对外提供键值存储服务的主要流程。Redis内部的其他功能,例如持久化、 异步删除、数据同步等等是会有额外的线程去执行的。
  2. 单个线程访问之所以还会那么快,是因为数据都存在内存中,所有的运算都是内存级别的运算,单线程同时避免了多线程所带来的上下文切换问题。
  3. 单线程的任务我们需要注意访问的命令,对于那些耗时的命令以及当访问一个bigkey的时候可能会导致Redis卡顿。
  4. 对于客户端的并发连接,Redis底层利用epoll模型来实现IO多路复用,将链接信息和时间放到队列中,依次放到文件事件分派器,事件分派器将事件分发给对应的事件处理器。
IO多路复用
IO多路复用

其他高级命令

keys

全局遍历所有的key,用来列出所有满足特定正则字符串规则的key,当Redis数据量较大时,性能会有所下降,应避免使用

scan

SCAN cursor [MATCH pattern] [COUNT count] 渐进式便利所有的键,相对于keys性能消耗更小,安全性更低

scan 参数提供了三个参数,第一个是 cursor 整数值(hash桶的索引值),第二个是 key 的正则模式,第三个是一次遍历的key的数量(参考值,底层遍历的数量不一定),并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。

⚠️注意:scan并非完美无瑕,如果在scan的过程中如果有键的变化(增加、 删除、 修改),那么遍历效果可能会碰到如下问题:
新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是我们在开发时需要考虑的。

info

查看Redis服务运行信息,分为以下9大块:

  • Server: 服务器运行的环境参数
  • Clients: 客户端相关信息
  • Memory: 服务器运行内存统计数据
  • Persistence: 持久化信息
  • Stats: 通用统计数据
  • Replication: 主从复制相关信息
  • CPU: CPU使用情况
  • Cluster: 集群情况
  • KeySpace: 键值对统计数量信息
',46);function m(R,_){const r=i("ExternalLinkIcon");return a(),d("div",null,[o,y,g,s(" more "),x,t("p",null,[e("官网:"),t("a",k,[e("https://redis.io/"),l(r)])]),t("p",null,[e("GitHub:"),t("a",u,[e("https://github.com/redis/redis"),l(r)])]),t("p",null,[e("Redis数据类型简介:"),t("a",b,[e("https://redis.io/topics/data-types-intro"),l(r)])]),t("p",null,[e("命令完整列表:"),t("a",p,[e("https://redis.io/commands"),l(r)])]),t("p",null,[e("完整文档地址:"),t("a",f,[e("https://redis.io/documentation"),l(r)])]),S])}const P=n(h,[["render",m],["__file","2304010120.html.vue"]]);export{P as default}; diff --git a/assets/2304010120.html-e922b0a2.js b/assets/2304010120.html-46b62f31.js similarity index 96% rename from assets/2304010120.html-e922b0a2.js rename to assets/2304010120.html-46b62f31.js index a92a6845..658285a1 100644 --- a/assets/2304010120.html-e922b0a2.js +++ b/assets/2304010120.html-46b62f31.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1452e9f3","path":"/note/db/redis/2304010120.html","title":"Redis核心数据结构","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-03T00:00:00.000Z","index":true,"order":1,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"地址","slug":"地址","link":"#地址","children":[]},{"level":2,"title":"基本特性","slug":"基本特性","link":"#基本特性","children":[]},{"level":2,"title":"核心数据结构","slug":"核心数据结构","link":"#核心数据结构","children":[{"level":3,"title":"String","slug":"string","link":"#string","children":[]},{"level":3,"title":"Hash","slug":"hash","link":"#hash","children":[]},{"level":3,"title":"List","slug":"list","link":"#list","children":[]},{"level":3,"title":"Set","slug":"set","link":"#set","children":[]},{"level":3,"title":"ZSet","slug":"zset","link":"#zset","children":[]},{"level":3,"title":"Redis的单线程和高性能","slug":"redis的单线程和高性能","link":"#redis的单线程和高性能","children":[]},{"level":3,"title":"其他高级命令","slug":"其他高级命令","link":"#其他高级命令","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.79,"words":2036},"filePathRelative":"note/db/redis/2304010120.md","localizedDate":"2021年1月3日","excerpt":"

Redis核心数据结构

\\n

Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。

\\n

Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1452e9f3","path":"/note/db/redis/2304010120.html","title":"Redis核心数据结构","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-03T00:00:00.000Z","index":true,"order":1,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"地址","slug":"地址","link":"#地址","children":[]},{"level":2,"title":"基本特性","slug":"基本特性","link":"#基本特性","children":[]},{"level":2,"title":"核心数据结构","slug":"核心数据结构","link":"#核心数据结构","children":[{"level":3,"title":"String","slug":"string","link":"#string","children":[]},{"level":3,"title":"Hash","slug":"hash","link":"#hash","children":[]},{"level":3,"title":"List","slug":"list","link":"#list","children":[]},{"level":3,"title":"Set","slug":"set","link":"#set","children":[]},{"level":3,"title":"ZSet","slug":"zset","link":"#zset","children":[]},{"level":3,"title":"Redis的单线程和高性能","slug":"redis的单线程和高性能","link":"#redis的单线程和高性能","children":[]},{"level":3,"title":"其他高级命令","slug":"其他高级命令","link":"#其他高级命令","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.79,"words":2036},"filePathRelative":"note/db/redis/2304010120.md","localizedDate":"2021年1月3日","excerpt":"

Redis核心数据结构

\\n

Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。

\\n

Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2304021002.html-21c638b0.js b/assets/2304021002.html-6c07516c.js similarity index 97% rename from assets/2304021002.html-21c638b0.js rename to assets/2304021002.html-6c07516c.js index cd3a61d1..80b0d1bc 100644 --- a/assets/2304021002.html-21c638b0.js +++ b/assets/2304021002.html-6c07516c.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-4f779f12","path":"/note/framework/spring/sourcecode/2304021002.html","title":"Spring 构建与内置功能","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-09T00:00:00.000Z","index":true,"order":1,"category":["Spring"],"tag":["源码"]},"headers":[{"level":2,"title":"创建方式","slug":"创建方式","link":"#创建方式","children":[{"level":3,"title":"XML","slug":"xml","link":"#xml","children":[]},{"level":3,"title":"配置类","slug":"配置类","link":"#配置类","children":[]}]},{"level":2,"title":"前置知识(概念)","slug":"前置知识-概念","link":"#前置知识-概念","children":[{"level":3,"title":"BeanDefinition","slug":"beandefinition","link":"#beandefinition","children":[]},{"level":3,"title":"BeanDefinitionReader","slug":"beandefinitionreader","link":"#beandefinitionreader","children":[]},{"level":3,"title":"AnnotatedBeanDefinitionReader","slug":"annotatedbeandefinitionreader","link":"#annotatedbeandefinitionreader","children":[]},{"level":3,"title":"XmlBeanDefinitionReader","slug":"xmlbeandefinitionreader","link":"#xmlbeandefinitionreader","children":[]},{"level":3,"title":"ClassPathBeanDefinitionScanner","slug":"classpathbeandefinitionscanner","link":"#classpathbeandefinitionscanner","children":[]},{"level":3,"title":"BeanFactory","slug":"beanfactory","link":"#beanfactory","children":[]},{"level":3,"title":"ApplicationContext","slug":"applicationcontext","link":"#applicationcontext","children":[]},{"level":3,"title":"Resource","slug":"resource","link":"#resource","children":[]},{"level":3,"title":"Environment","slug":"environment","link":"#environment","children":[]},{"level":3,"title":"ApplicationListener","slug":"applicationlistener","link":"#applicationlistener","children":[]},{"level":3,"title":"类型转换器","slug":"类型转换器","link":"#类型转换器","children":[]},{"level":3,"title":"OrderComparator","slug":"ordercomparator","link":"#ordercomparator","children":[]},{"level":3,"title":"BeanPostProcessor","slug":"beanpostprocessor","link":"#beanpostprocessor","children":[]},{"level":3,"title":"BeanFactoryPostProcessor","slug":"beanfactorypostprocessor","link":"#beanfactorypostprocessor","children":[]},{"level":3,"title":"FactoryBean","slug":"factorybean","link":"#factorybean","children":[]},{"level":3,"title":"Filter","slug":"filter","link":"#filter","children":[]},{"level":3,"title":"Metadata","slug":"metadata","link":"#metadata","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":8.97,"words":2690},"filePathRelative":"note/framework/spring/sourcecode/2304021002.md","localizedDate":"2019年5月9日","excerpt":"

Spring 构建与内置功能

\\n

记录spring的简单内置功能以及使用

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-4f779f12","path":"/note/framework/spring/sourcecode/2304021002.html","title":"Spring 构建与内置功能","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2019-05-09T00:00:00.000Z","index":true,"order":1,"category":["Spring"],"tag":["源码"]},"headers":[{"level":2,"title":"创建方式","slug":"创建方式","link":"#创建方式","children":[{"level":3,"title":"XML","slug":"xml","link":"#xml","children":[]},{"level":3,"title":"配置类","slug":"配置类","link":"#配置类","children":[]}]},{"level":2,"title":"前置知识(概念)","slug":"前置知识-概念","link":"#前置知识-概念","children":[{"level":3,"title":"BeanDefinition","slug":"beandefinition","link":"#beandefinition","children":[]},{"level":3,"title":"BeanDefinitionReader","slug":"beandefinitionreader","link":"#beandefinitionreader","children":[]},{"level":3,"title":"AnnotatedBeanDefinitionReader","slug":"annotatedbeandefinitionreader","link":"#annotatedbeandefinitionreader","children":[]},{"level":3,"title":"XmlBeanDefinitionReader","slug":"xmlbeandefinitionreader","link":"#xmlbeandefinitionreader","children":[]},{"level":3,"title":"ClassPathBeanDefinitionScanner","slug":"classpathbeandefinitionscanner","link":"#classpathbeandefinitionscanner","children":[]},{"level":3,"title":"BeanFactory","slug":"beanfactory","link":"#beanfactory","children":[]},{"level":3,"title":"ApplicationContext","slug":"applicationcontext","link":"#applicationcontext","children":[]},{"level":3,"title":"Resource","slug":"resource","link":"#resource","children":[]},{"level":3,"title":"Environment","slug":"environment","link":"#environment","children":[]},{"level":3,"title":"ApplicationListener","slug":"applicationlistener","link":"#applicationlistener","children":[]},{"level":3,"title":"类型转换器","slug":"类型转换器","link":"#类型转换器","children":[]},{"level":3,"title":"OrderComparator","slug":"ordercomparator","link":"#ordercomparator","children":[]},{"level":3,"title":"BeanPostProcessor","slug":"beanpostprocessor","link":"#beanpostprocessor","children":[]},{"level":3,"title":"BeanFactoryPostProcessor","slug":"beanfactorypostprocessor","link":"#beanfactorypostprocessor","children":[]},{"level":3,"title":"FactoryBean","slug":"factorybean","link":"#factorybean","children":[]},{"level":3,"title":"Filter","slug":"filter","link":"#filter","children":[]},{"level":3,"title":"Metadata","slug":"metadata","link":"#metadata","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":8.97,"words":2690},"filePathRelative":"note/framework/spring/sourcecode/2304021002.md","localizedDate":"2019年5月9日","excerpt":"

Spring 构建与内置功能

\\n

记录spring的简单内置功能以及使用

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2304021002.html-836cddaf.js b/assets/2304021002.html-da517711.js similarity index 99% rename from assets/2304021002.html-836cddaf.js rename to assets/2304021002.html-da517711.js index aae98174..711c17ae 100644 --- a/assets/2304021002.html-836cddaf.js +++ b/assets/2304021002.html-da517711.js @@ -1,4 +1,4 @@ -import{_ as e,r as p,o,c,d as i,a as n,b as s,f as l,e as a}from"./app-1efcbe9f.js";const u={},r=n("h1",{id:"spring-构建与内置功能",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-构建与内置功能","aria-hidden":"true"},"#"),s(" Spring 构建与内置功能")],-1),k=n("p",null,"记录spring的简单内置功能以及使用",-1),d=a(`

创建方式

XML

通过加载xml配置文件,获取配置文件里相关的配置项,例如定义的scan标签、bean标签等可以配置扫描的路径以及进行bean的注入。

   ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
+import{_ as e,r as p,o,c,d as i,a as n,b as s,f as l,e as a}from"./app-6a63891c.js";const u={},r=n("h1",{id:"spring-构建与内置功能",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-构建与内置功能","aria-hidden":"true"},"#"),s(" Spring 构建与内置功能")],-1),k=n("p",null,"记录spring的简单内置功能以及使用",-1),d=a(`

创建方式

XML

通过加载xml配置文件,获取配置文件里相关的配置项,例如定义的scan标签、bean标签等可以配置扫描的路径以及进行bean的注入。

   ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    OrderService orderService = (OrderService) context.getBean("orderService");
    orderService.test();
 

值得一提的是,这种方式需要手动配置bean,所以目前已经很少用了

配置类

通过配置类方式创建spring,其实就是通过注解会自动的解析,与xml类似,只不过不再需要程序员手动写bean。

   AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
diff --git a/assets/2305011002.html-8fc17830.js b/assets/2305011002.html-8fc17830.js
deleted file mode 100644
index cebd9dce..00000000
--- a/assets/2305011002.html-8fc17830.js
+++ /dev/null
@@ -1 +0,0 @@
-import{_ as a,o,c as d,d as i,a as e,b as l,e as r}from"./app-1efcbe9f.js";const c={},h=e("h1",{id:"深入理解mysql索引底层数据结构与算法",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#深入理解mysql索引底层数据结构与算法","aria-hidden":"true"},"#"),l(" 深入理解mysql索引底层数据结构与算法")],-1),t=e("p",null,"mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。",-1),n=r('

索引

索引 是帮助mysql高效获取数据的排好序数据结构

索引的数据结构

二叉树

二叉树的右子节点比父节点大,左子节点比父节点小。通过这一特点进行查询时,通过比对可以快速定位节点位置。

缺点:数据形成的是满二叉树或者完全二叉树的时候去查找才能很好的发挥索引的优势。在极端情况下,
例如形成的是一颗线型的二叉树,其实跟没有构建索引是一样的。

红黑树

红黑树又称二叉平衡树,在新增节点时可以自动调整节点的位置。

缺点:当数据的数据量大时,树的高度会变得很高,而且树的高度不可控。如果查询的数据恰好在叶子节点,而树的查询都是从
父节点开始的,执行的IO次数也会很多。

HASH

构建一个HASH桶,将计算好HASH的元素以及地址放到桶的指定位置,再有相同的则追加在元素后面。

查询则先计算好在桶的哪个位置,再遍历链表。在某种情况下要比B+树更高效。

缺点:对范围IN的查询不支持、hash冲突等问题

B树

B树的结构是在一个节点里面放很多的小节点元素,通过节点的横向扩展来解决树的高度问题。小节点元素是从左到右递增排列的。

B树相比B+树,在存储上有所不足。相比B+树,一个h=3可以存两千多万,而B数要存两千多万需要的h=6。使用B+树的结构,
将数据全部记录在叶子结点,非叶子结点就可以存储更多的索引,形成的树高度更小。

B+树(变种)

B+树与B树的结构类似。
B+树只有叶子节点存储数据,其他节点只存储苏索引,且叶子节点包含所有的索引字段和数据。
B+树节点内的索引元素都是排好序的,同时,非叶子节点的子索引元素都是子节点的第一个索引元素(冗余)。
B+树两个叶子节点之间用指针相连,通过指针可以快速定位其他节点的元素,很好的支持范围查找。

查询过程:

  1. 将节点加载到内存,在内存里比对待查询数据与索引字段,比对结果所指向的是叶子节点,则获取索引元素数据或数据地址;
  2. 不是指向叶子节点,再次进行操作1;

一个节点为一页,mysql默认设置的大小为16384(16KB)
按照mysql存储数据的大小,以bigint为例,占8B;指向子结点的索引节点为6B,则每一页可以存储
16384/(8+6)=1170个节点。以一个高度为3的B+树来算,叶子结点由于存储的是索引和数据,假设每一个节点存1K的数据,一页就是16K,
则总共可以记录 1170117016=21902400.两千万个。

存储引擎

存储引擎描述的是数据表而不是数据库,真实起作用的是在表上。

MyISAM

myisam生成三个数据文件:

  1. .frm结尾的是记录数据表相关信息的文件
  2. .MYD结尾的是记录真实数据信息的数据文件
  3. .MYI结尾的是记录索引信息的文件

myisam构建的索引,叶子结点存的是数据在文件中的地址,再通过地址去数据文件获取数据,又称非聚集索引

InnoDB

innodb生成二个数据文件:

  1. .frm结尾的是记录数据表相关信息的文件
  2. .idb结尾的是记录数据和索引信息的文件

innodb构建的索引,叶子结点存的是完整的数据记录,又称聚集索引

  1. 建议建主键,且推荐使用整形自增主键的原因:

    • 如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,
      而不用mysql帮我们去创建
    • 相比UUID等作为主键,推荐整形是比对方便,且节约空间
    • 使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素
  2. 主键索引与非主键索引的区别:

    • 主键索引叶子节点存储完整的数据,非主键索引叶子节点存储的是主键值
  3. 非主键索引叶子节点存主键值的原因:

    • 节约存储空间
    • 一致性

非主键索引查询数据时,需要先获取叶子节点存储的主键,再去主键索引里查完整的记录,这一操作又称为回表

联合索引

联合索引是根据构建索引的字段从左到右的顺序,通过比对字段数据进行排序,如果字段相同,则比对下一个字段的数据,以此来构成了联合索引。

最左前缀原则:在使用联合索引查询数据时,需要按照索引字段从左到右的顺序去添加条件,否则可能会不走索引;

',36);function s(p,b){return o(),d("div",null,[h,t,i(" more "),n])}const m=a(c,[["render",s],["__file","2305011002.html.vue"]]);export{m as default}; diff --git a/assets/2305011002.html-ffdcf33e.js b/assets/2305011002.html-ffdcf33e.js deleted file mode 100644 index 9966d6a3..00000000 --- a/assets/2305011002.html-ffdcf33e.js +++ /dev/null @@ -1 +0,0 @@ -const l=JSON.parse('{"key":"v-113cc96c","path":"/note/db/mysql/further/2305011002.html","title":"深入理解mysql索引底层数据结构与算法","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-04T00:00:00.000Z","index":true,"order":1,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"索引","slug":"索引","link":"#索引","children":[]},{"level":2,"title":"索引的数据结构","slug":"索引的数据结构","link":"#索引的数据结构","children":[{"level":3,"title":"二叉树","slug":"二叉树","link":"#二叉树","children":[]},{"level":3,"title":"红黑树","slug":"红黑树","link":"#红黑树","children":[]},{"level":3,"title":"HASH","slug":"hash","link":"#hash","children":[]},{"level":3,"title":"B树","slug":"b树","link":"#b树","children":[]},{"level":3,"title":"B+树(变种)","slug":"b-树-变种","link":"#b-树-变种","children":[]}]},{"level":2,"title":"存储引擎","slug":"存储引擎","link":"#存储引擎","children":[{"level":3,"title":"MyISAM","slug":"myisam","link":"#myisam","children":[]},{"level":3,"title":"InnoDB","slug":"innodb","link":"#innodb","children":[]}]},{"level":2,"title":"联合索引","slug":"联合索引","link":"#联合索引","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.15,"words":1545},"filePathRelative":"note/db/mysql/further/2305011002.md","localizedDate":"2023年5月4日","excerpt":"

深入理解mysql索引底层数据结构与算法

\\n

mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2305012001.html-f4132e9f.js b/assets/2305012001.html-1c65085b.js similarity index 54% rename from assets/2305012001.html-f4132e9f.js rename to assets/2305012001.html-1c65085b.js index cb64e5db..c142f1be 100644 --- a/assets/2305012001.html-f4132e9f.js +++ b/assets/2305012001.html-1c65085b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-b8674064","path":"/note/db/mysql/further/2305012001.html","title":"MySQL索引","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-05T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"处理问题","slug":"处理问题","link":"#处理问题","children":[]},{"level":2,"title":"数据结构对比","slug":"数据结构对比","link":"#数据结构对比","children":[{"level":3,"title":"二叉树","slug":"二叉树","link":"#二叉树","children":[]},{"level":3,"title":"红黑树","slug":"红黑树","link":"#红黑树","children":[]},{"level":3,"title":"B树","slug":"b树","link":"#b树","children":[]},{"level":3,"title":"B+树","slug":"b-树","link":"#b-树","children":[]},{"level":3,"title":"HASH","slug":"hash","link":"#hash","children":[]}]},{"level":2,"title":"存储引擎索引实现","slug":"存储引擎索引实现","link":"#存储引擎索引实现","children":[{"level":3,"title":"MyISAM","slug":"myisam","link":"#myisam","children":[]},{"level":3,"title":"InnoDB","slug":"innodb","link":"#innodb","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":9.24,"words":2771},"filePathRelative":"note/db/mysql/further/2305012001.md","localizedDate":"2023年5月5日","excerpt":"

MySQL索引

\\n

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

\\n

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-b8674064","path":"/note/db/mysql/further/2305012001.html","title":"MySQL索引","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-05T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"处理问题","slug":"处理问题","link":"#处理问题","children":[]},{"level":2,"title":"数据结构对比","slug":"数据结构对比","link":"#数据结构对比","children":[{"level":3,"title":"二叉树","slug":"二叉树","link":"#二叉树","children":[]},{"level":3,"title":"红黑树","slug":"红黑树","link":"#红黑树","children":[]},{"level":3,"title":"B树","slug":"b树","link":"#b树","children":[]},{"level":3,"title":"B+树","slug":"b-树","link":"#b-树","children":[]},{"level":3,"title":"HASH","slug":"hash","link":"#hash","children":[]}]},{"level":2,"title":"存储引擎索引实现","slug":"存储引擎索引实现","link":"#存储引擎索引实现","children":[{"level":3,"title":"MyISAM","slug":"myisam","link":"#myisam","children":[]},{"level":3,"title":"InnoDB","slug":"innodb","link":"#innodb","children":[]}]},{"level":2,"title":"联合索引","slug":"联合索引","link":"#联合索引","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":9.61,"words":2882},"filePathRelative":"note/db/mysql/further/2305012001.md","localizedDate":"2023年5月5日","excerpt":"

MySQL索引

\\n

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

\\n

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305012001.html-f8c1b846.js b/assets/2305012001.html-9305c46c.js similarity index 94% rename from assets/2305012001.html-f8c1b846.js rename to assets/2305012001.html-9305c46c.js index 22291f4e..5df296c8 100644 --- a/assets/2305012001.html-f8c1b846.js +++ b/assets/2305012001.html-9305c46c.js @@ -1,3 +1,3 @@ -import{_ as e,o as n,c as l,d as t,a as i,b as a,e as r}from"./app-1efcbe9f.js";const o={},g=i("h1",{id:"mysql索引",tabindex:"-1"},[i("a",{class:"header-anchor",href:"#mysql索引","aria-hidden":"true"},"#"),a(" MySQL索引")],-1),h=i("p",null,[a("索引"),i("strong",null,"是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构"),a(",简单讲就是"),i("strong",null,"一种排好序的数据结构"),a("。")],-1),d=i("p",null,"在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。",-1),p=r(`

处理问题

MySQL存储数据是存储在磁盘上的,而且两个相邻数据之间所处的磁盘位置不一定连续,让一条查询sql执行的时候,是需要去磁盘中一条条数据进行加载比较,每一次的加载都是一次磁盘IO,这是非常消耗性能的。处理这个问题的解决方案就是减少磁盘IO,尽可能控制读取次数。索引就是帮我们去整理这些数据,通过建立合适的数据结构,方便查询的时候减少对磁盘的IO。

数据结构对比

二叉树

索引
索引

如果现在要查询一条语句,条件是Col2 = 25,如果没有使用索引,顺序一条条的去查找比对,需要进行5次磁盘IO才能找到,而通过二叉树构成的索引,只需要两次就可以定位到数据,效率会高一些。

如果现在要查询一条Col1 = 6的语句,此时构成的二叉树就有可能是线性的,虽然都是查询6,但其实查找都是执行了六次,索引并没有对查询有任何帮助,索引mysql并不使用二叉树作为底层数据结构。

二叉树
二叉树

红黑树

红黑树其实也是一棵二叉数,又叫二叉平衡树。红黑树可以对新增的元素做平衡处理,比如上面1-6的元素,如果用红黑树实现就会是这样的情况。

红黑树
红黑树

虽然红黑树解决了二叉数线性树的弊端,但是在某些场景下对sql的查询仍然不是很友好,比如:当插入几百万行数据时,树的高度已经达到十几,如果刚好查找的数据在叶子节点,那至少需要遍历十几次才可以查找到。

我们要解决的本质是减少查询次数,如果我们控制了树的高度,就可以很好的解决查询的问题。

B树

B树的特点就是一个节点不在只存一个数据,而是一页数据,一页数据里面存放多个数据节点以及子节点地址,子节点又存储一页数据,这就可以实现将数据横向存储,控制了树的高度,减少了IO次数。

B树
B树

B树做索引的特点:

  • 叶节点具有相同的深度,叶节点的指针为空
  • 所有索引元素不重复
  • 节点中的数据索引从左到右递增排序
  • data里面存储的是索引数据所在行所处磁盘文件的地址

虽然B树解决接红黑树高度的问题,但其实mysql也不是用b树结构做索引,而是使用变种的B+树。

B+树

B+树可以算是B树的进阶版,其数据结构类似,节点都是存储一页数据,并且节点中的索引也是从左到右递增排好序。

B+树与B树的差异:

  1. 非叶子节点不存储data数据,只存储索引,而且是冗余存储,这样处理可以放更多的索引
  2. 所有的data数据都放在叶子节点,并且叶子节点包含所有的索引字段
  3. 叶子节点之间用指针连接,mysql变种后的是实现双指针,这样可以提高区间的访问性能
B+树
B+树

假设现在要通过索引树查找Col = 35的数据,步骤如下:

  • 从根节点出发
  • 将节点整页的数据加载到内存里
  • 如果是非叶子节点,通过查找算法进行数据比对找到35所在的子节点数据页磁盘地址,再次执行操作2
  • 如果是叶子节点,通过匹配到的索引记录的磁盘文件地址去加载所需数据

树高度问题如何解决?

mysql一页在磁盘上默认分配的大小为16kb,通过语句**SHOW GLOBAL STATUS LIKE 'Innodb_page_size'**可以查到这个值。

对于非叶子节点,假设我们使用bigint做主键,占用的是8Byte,记录叶子节点的元素指针在mysql设置的是6Byte,那一页就可以存放 16x1024/(8+6)个元素,计算后为1170个元素。

对于叶子节点,不仅仅存索引还存储data数据,这个data可能存储的是索引所在行的磁盘文件的地址,也有可能存储的是索引所在行的所有列数据。现假设叶子节点存放data为所有列数据,一个元素记录了1K数据,那一页就可以16个元素。

此时如果构建一个 h=3 的B+树,那叶子节点就可以存放 1170x1170x16 个元素,计算后为21902400个元素。

两千多万的数据,高度仅仅是3,查找一个元素的IO次数只用了3次,而如果没有走索引,那有可能扫几百万甚至千万次才可以找到元素,查找效率差的是好几个数量级,这也是索引为什么那么高效的原因。

对于Mysql来说,有可能根节点或者所有非叶子节点常驻内存,对于千万数据级别的表进行查找,直接在内存进行匹配获取索引行数据或者磁盘文件地址,只需要一次IO去获取数据就可以获取到需要的数据,查找的效率更高了。

如果使用B树存储,因为data是跟随索引元素下的,假设还是1kb,那一页数据就存16个元素,如果存储两千万的数据,树的高度大约为7,跟B+树没得比。

B Three 和 B+ Three的区别

  1. B Three的数据是放在节点元素上的,而且所有索引元素都不重复;B+ Three是将数据存放在叶子节点的元素上,并且非叶子节点冗余子节点的首个索引元素,叶子节点记录所有索引元素记录。
  2. B Three 叶子节点之间没有指针相连,当进行范围查询时,获取下一个叶子节点的数据又要从根节点再次检索;B+ Three叶子节点之间记录了双向指针,当进行范围查找时就可以通过这个双向指针快速定位下一个节点的数据。

HASH

mysql还有一种索引结构就是Hash索引,通过一个Hash桶(数组)还有链表形成,通过计算索引行的Hash值确定是在表的哪一个位置,再形成链表去存储数据,当查询的时候只需要对数据进行一次hash计算就可以定位出数据存储的位置,在很多时候Hash索引要比B+树索引更高效。

HASH
HASH

Hash虽然很高效,但是更多时候依旧选择B+树作为索引结构,原因主要有:

  1. Hash冲突问题
  2. 只能满足 = 的条件,对于范围查询 IN、大于、小于 等不支持,B+树通过叶子节点的双向指针可以很好的支持范围查询

存储引擎索引实现

Mysql中有很多的存储引擎实现,存储引擎描述的是数据库表的,真实作用是在表上而不是数据库。早期使用的是MyISAM,现在流行的是InnoDB,现在看看这两个的的实现。

MyISAM

MyISAM存储引擎生成的数据文件是三个

MyISAM数据文件
MyISAM数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.MYD结尾的是记录真实数据信息的数据文件
  3. 以.MYI结尾的是记录索引信息的文件

这就意味着MyISAM存储引擎的主键索引实现是讲数据与索引树分开的,通过两个文件分别存储索引树和数据信息,又叫非聚集索引,MyISAM主键、非主键索引都是非聚集索引

MyISAM
MyISAM

MyISAM如果要查找数据,通过MYI索引树文件,先定位到查找的数据,再通过叶子节点记录的行磁盘文件地址,再去MYD文件获取具体的数据信息。

InnoDB

InnoDB存储引擎生成的数据文件是两个

InnoDB数据文件
InnoDB数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.idb结尾的是记录数据和索引信息的文件

与MyISAM不同,InnoDB主键索引是将索引树和数据写在同一个同一个文件,叶子节点包含了完整列数据,又叫聚集索引,而对于非主键索引,叶子节点记录的是主键索引的主键值而不是完整的列数据,又称非聚集索引

InnoDB
InnoDB

问题:

  1. 为什么建议InnoDB表要建主键?

    答:如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,主键索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,而不用mysql帮我们去创建。

  2. 为什么推荐使用整形的自增主键?

 答:相比UUID等作为主键,推荐整形是比对方便,且节约空间。使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素。
+import{_ as e,o as n,c as l,d as t,a as i,b as a,e as r}from"./app-6a63891c.js";const o={},h=i("h1",{id:"mysql索引",tabindex:"-1"},[i("a",{class:"header-anchor",href:"#mysql索引","aria-hidden":"true"},"#"),a(" MySQL索引")],-1),g=i("p",null,[a("索引"),i("strong",null,"是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构"),a(",简单讲就是"),i("strong",null,"一种排好序的数据结构"),a("。")],-1),d=i("p",null,"在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。",-1),p=r(`

处理问题

MySQL存储数据是存储在磁盘上的,而且两个相邻数据之间所处的磁盘位置不一定连续,让一条查询sql执行的时候,是需要去磁盘中一条条数据进行加载比较,每一次的加载都是一次磁盘IO,这是非常消耗性能的。处理这个问题的解决方案就是减少磁盘IO,尽可能控制读取次数。索引就是帮我们去整理这些数据,通过建立合适的数据结构,方便查询的时候减少对磁盘的IO。

数据结构对比

二叉树

索引
索引

如果现在要查询一条语句,条件是Col2 = 25,如果没有使用索引,顺序一条条的去查找比对,需要进行5次磁盘IO才能找到,而通过二叉树构成的索引,只需要两次就可以定位到数据,效率会高一些。

如果现在要查询一条Col1 = 6的语句,此时构成的二叉树就有可能是线性的,虽然都是查询6,但其实查找都是执行了六次,索引并没有对查询有任何帮助,索引mysql并不使用二叉树作为底层数据结构。

二叉树
二叉树

红黑树

红黑树其实也是一棵二叉数,又叫二叉平衡树。红黑树可以对新增的元素做平衡处理,比如上面1-6的元素,如果用红黑树实现就会是这样的情况。

红黑树
红黑树

虽然红黑树解决了二叉数线性树的弊端,但是在某些场景下对sql的查询仍然不是很友好,比如:当插入几百万行数据时,树的高度已经达到十几,如果刚好查找的数据在叶子节点,那至少需要遍历十几次才可以查找到。

我们要解决的本质是减少查询次数,如果我们控制了树的高度,就可以很好的解决查询的问题。

B树

B树的特点就是一个节点不在只存一个数据,而是一页数据,一页数据里面存放多个数据节点以及子节点地址,子节点又存储一页数据,这就可以实现将数据横向存储,控制了树的高度,减少了IO次数。

B树
B树

B树做索引的特点:

  • 叶节点具有相同的深度,叶节点的指针为空
  • 所有索引元素不重复
  • 节点中的数据索引从左到右递增排序
  • data里面存储的是索引数据所在行所处磁盘文件的地址

虽然B树解决接红黑树高度的问题,但其实mysql也不是用b树结构做索引,而是使用变种的B+树。

B+树

B+树可以算是B树的进阶版,其数据结构类似,节点都是存储一页数据,并且节点中的索引也是从左到右递增排好序。

B+树与B树的差异:

  1. 非叶子节点不存储data数据,只存储索引,而且是冗余存储,这样处理可以放更多的索引
  2. 所有的data数据都放在叶子节点,并且叶子节点包含所有的索引字段
  3. 叶子节点之间用指针连接,mysql变种后的是实现双指针,这样可以提高区间的访问性能
B+树
B+树

假设现在要通过索引树查找Col = 35的数据,步骤如下:

  • 从根节点出发
  • 将节点整页的数据加载到内存里
  • 如果是非叶子节点,通过查找算法进行数据比对找到35所在的子节点数据页磁盘地址,再次执行操作2
  • 如果是叶子节点,通过匹配到的索引记录的磁盘文件地址去加载所需数据

树高度问题如何解决?

mysql一页在磁盘上默认分配的大小为16kb,通过语句**SHOW GLOBAL STATUS LIKE 'Innodb_page_size'**可以查到这个值。

对于非叶子节点,假设我们使用bigint做主键,占用的是8Byte,记录叶子节点的元素指针在mysql设置的是6Byte,那一页就可以存放 16x1024/(8+6)个元素,计算后为1170个元素。

对于叶子节点,不仅仅存索引还存储data数据,这个data可能存储的是索引所在行的磁盘文件的地址,也有可能存储的是索引所在行的所有列数据。现假设叶子节点存放data为所有列数据,一个元素记录了1K数据,那一页就可以16个元素。

此时如果构建一个 h=3 的B+树,那叶子节点就可以存放 1170x1170x16 个元素,计算后为21902400个元素。

两千多万的数据,高度仅仅是3,查找一个元素的IO次数只用了3次,而如果没有走索引,那有可能扫几百万甚至千万次才可以找到元素,查找效率差的是好几个数量级,这也是索引为什么那么高效的原因。

对于Mysql来说,有可能根节点或者所有非叶子节点常驻内存,对于千万数据级别的表进行查找,直接在内存进行匹配获取索引行数据或者磁盘文件地址,只需要一次IO去获取数据就可以获取到需要的数据,查找的效率更高了。

如果使用B树存储,因为data是跟随索引元素下的,假设还是1kb,那一页数据就存16个元素,如果存储两千万的数据,树的高度大约为7,跟B+树没得比。

B Three 和 B+ Three的区别

  1. B Three的数据是放在节点元素上的,而且所有索引元素都不重复;B+ Three是将数据存放在叶子节点的元素上,并且非叶子节点冗余子节点的首个索引元素,叶子节点记录所有索引元素记录。
  2. B Three 叶子节点之间没有指针相连,当进行范围查询时,获取下一个叶子节点的数据又要从根节点再次检索;B+ Three叶子节点之间记录了双向指针,当进行范围查找时就可以通过这个双向指针快速定位下一个节点的数据。

HASH

mysql还有一种索引结构就是Hash索引,通过一个Hash桶(数组)还有链表形成,通过计算索引行的Hash值确定是在表的哪一个位置,再形成链表去存储数据,当查询的时候只需要对数据进行一次hash计算就可以定位出数据存储的位置,在很多时候Hash索引要比B+树索引更高效。

HASH
HASH

Hash虽然很高效,但是更多时候依旧选择B+树作为索引结构,原因主要有:

  1. Hash冲突问题
  2. 只能满足 = 的条件,对于范围查询 IN、大于、小于 等不支持,B+树通过叶子节点的双向指针可以很好的支持范围查询

存储引擎索引实现

Mysql中有很多的存储引擎实现,存储引擎描述的是数据库表的,真实作用是在表上而不是数据库。早期使用的是MyISAM,现在流行的是InnoDB,现在看看这两个的的实现。

MyISAM

MyISAM存储引擎生成的数据文件是三个

MyISAM数据文件
MyISAM数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.MYD结尾的是记录真实数据信息的数据文件
  3. 以.MYI结尾的是记录索引信息的文件

这就意味着MyISAM存储引擎的主键索引实现是讲数据与索引树分开的,通过两个文件分别存储索引树和数据信息,又叫非聚集索引,MyISAM主键、非主键索引都是非聚集索引

MyISAM
MyISAM

MyISAM如果要查找数据,通过MYI索引树文件,先定位到查找的数据,再通过叶子节点记录的行磁盘文件地址,再去MYD文件获取具体的数据信息。

InnoDB

InnoDB存储引擎生成的数据文件是两个

InnoDB数据文件
InnoDB数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.idb结尾的是记录数据和索引信息的文件

与MyISAM不同,InnoDB主键索引是将索引树和数据写在同一个同一个文件,叶子节点包含了完整列数据,又叫聚集索引,而对于非主键索引,叶子节点记录的是主键索引的主键值而不是完整的列数据,又称非聚集索引

InnoDB
InnoDB

问题:

  1. 为什么建议InnoDB表要建主键?

    答:如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,主键索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,而不用mysql帮我们去创建。

  2. 为什么推荐使用整形的自增主键?

 答:相比UUID等作为主键,推荐整形是比对方便,且节约空间。使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素。
 
  1. 为什么非主键索引叶子节点存储的是主键值?
 答:InnoDB引擎每一张表都会有一个主键索引,如果我们建表时有指定列为主键,则使用主键去构建主键索引,如果没有指定主键,那会选择一列数据不重复的列或者建一列隐藏列作为主键再去构建主键索引。而非主键索引之所以只记录主键索引的主键值,主要是为了节约存储空间,还有一个就是为了保证插入数据时非主键索引与主键索引的一致性。
-
`,60);function s(c,f){return n(),l("div",null,[g,h,d,t(" more "),p])}const y=e(o,[["render",s],["__file","2305012001.html.vue"]]);export{y as default}; +

联合索引

联合索引是根据构建索引的字段从左到右的顺序,通过比对字段数据进行排序,如果字段相同,则比对下一个字段的数据,以此来构成了联合索引。

最左前缀原则:在使用联合索引查询数据时,需要按照索引字段从左到右的顺序去添加条件,否则可能会不走索引;

`,63);function s(c,u){return n(),l("div",null,[h,g,d,t(" more "),p])}const b=e(o,[["render",s],["__file","2305012001.html.vue"]]);export{b as default}; diff --git a/assets/2305020500.html-e661241b.js b/assets/2305020500.html-3b17715a.js similarity index 99% rename from assets/2305020500.html-e661241b.js rename to assets/2305020500.html-3b17715a.js index 76155441..83875545 100644 --- a/assets/2305020500.html-e661241b.js +++ b/assets/2305020500.html-3b17715a.js @@ -1 +1 @@ -import{_ as a,r as t,o as r,c as n,a as e,b as l,f as o,d,e as s}from"./app-1efcbe9f.js";const c={},h=e("h1",{id:"explain详解与索引最佳实践",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#explain详解与索引最佳实践","aria-hidden":"true"},"#"),l(" Explain详解与索引最佳实践")],-1),u=e("p",null,[l("Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置"),e("br"),l(" 一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。")],-1),p=e("p",null,"如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。",-1),b={href:"https://dev.mysql.com/doc/refman/5.7/en/explain-output.html",target:"_blank",rel:"noopener noreferrer"},f=s('

字段详解

Explain字段
Explain字段

id

属于select的编号,有几个select就会有几个id,与select出现的顺序一致。

id值越大,执行优先级越高;相同则从上往下执行;为null最后执行。

select_type

标识对应行使简单查询还是复杂查询。

  1. simple:简单查询,查询中不含有子查询和union
  2. primary:复杂查询中最外层select
  3. subquery:包含在select中的子查询(不在from子句中)
  4. derived:包含在from子句中的子查询。Mysql会讲结果存在临时表中。

table

表示行正在访问的表。

当from子句中有子查询,table展示<derivenN>,表示当前查询依赖id = N的查询

当有union时,table展示<union1,2>,1和2表示参与union的行id

type

表示关联类型访问类型,即mysql决定如何查找表中的行,查找数据行记录的大概范围

从优到劣依次为:system > const > eq_ref > ref > range > index > All

  • null:能够在优化阶段分解查询语句,在执行阶段不需要再访问表或索引
  • system:是const的特例,表里只有一条元组匹配时为system
  • const:mysql能对查询的某部分进行优化并转化为一个常量,用于主键索引或唯一索引
    的所有列与常数比较时,所以表最多有一个匹配行
  • eq_ref:主键索引或唯一索引的所有部分都被连接使用,最多只会返回一条符合的记录。
  • ref:与eq_ref类型,使用的是普通索引或唯一索引部分前缀,索引要和某个值比较,可能会找到多个符合条件的行
  • range:通常出现在 IN、between、>、<等操作中。使用一个索引来检索给定范围
  • index:扫描全索引就能拿到结果,一般扫描某个二级索引,这种扫描是直接扫叶子节点,速度较慢,称为覆盖索引
  • all:全表扫描,扫描聚集索引所有叶子节点,数据大,比index更慢

一般得保证查询达到range,最好能达到ref

partitions

如果查询是基于分区表的话,会显示查询将访问的分区

此字段与在5.7以前还需要再加上 partitions 关键字才会展示

possible_keys

显示本次查询可能会使用哪些索引来完成。

key

显示实际采用的索引

key_len

显示索引里使用的字节数,通过这个值可以算出具体使用了索引中的那些列

计算规则如下:

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字或字母占1个字节,
    一个汉字占3个字节
    • char(n):如果存汉字长度就是3n字节
  • varchar(n):如果存汉字则长度是3n+2字节,加的2字节用来存储字符串长度,因为varchar是变长字符串
  • 数值类型
    • tinyint:1字节
  • smallint:1字节
  • int:4字节
    • bigint:8字节
  • 时间类型
    • data:3字节
  • timestamp:4字节
  • datetime:8字节
  • 如果字段允许为null,需要1字节记录是否为null

ref

显示在key列记录的索引中,表查找所用到的列或常量

rows

显示本行查询估计要读取并检测的行数,不是结果集里的行数

filtered

filtered是一个半分比的值.rows*filtered/100可以估算出将要和explain中前一个表进行连接的行数(id值比当前表id值小的表)

此字段与在5.7以前还需要再加上 extended 关键字才会展示

Extra

展示额外信息,重要值如下:

  1. Using index:使用了覆盖索引
  2. Using where:使用where语句来处理结果,并且查询的列未被索引覆盖
  3. Using index condition:查询的列不完全被覆盖,where条件中是一个前导列的范围
  4. Using temporary:mysql建了一张临时表来处理查询
  5. Using filesort:使用外部排序而不是索引排序,数据较小时从内存排,否则需要在磁盘完成
  6. Select tables optimized way:使用了某些聚合函数(min()、max()等)来访问存在索引的某个字段

索引最佳实践

  • 全值匹配
  • 最左前缀原则
  • 在索引列上做操作(计算、函数、自动或手动类型转换)会导致索引失效
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引,减少select * 的语句
  • 使用不等于(!=或<>)、not in、not exists会导致索引失效
  • 大于、小于、大于等于、小于等于会根据检索比例、表大小等因素整体评估是否使用索引
  • is null 、 is not null 一般情况下也会导致索引失效
  • like 以通配符开头('%xa')也会导致索引失效
',46);function x(m,k){const i=t("ExternalLinkIcon");return r(),n("div",null,[h,u,p,e("blockquote",null,[e("p",null,[l("官方地址:"),e("a",b,[l("https://dev.mysql.com/doc/refman/5.7/en/explain-output.html"),o(i)])])]),d(" more "),f])}const _=a(c,[["render",x],["__file","2305020500.html.vue"]]);export{_ as default}; +import{_ as a,r as t,o as r,c as n,a as e,b as l,f as o,d,e as s}from"./app-6a63891c.js";const c={},h=e("h1",{id:"explain详解与索引最佳实践",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#explain详解与索引最佳实践","aria-hidden":"true"},"#"),l(" Explain详解与索引最佳实践")],-1),u=e("p",null,[l("Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置"),e("br"),l(" 一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。")],-1),p=e("p",null,"如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。",-1),b={href:"https://dev.mysql.com/doc/refman/5.7/en/explain-output.html",target:"_blank",rel:"noopener noreferrer"},f=s('

字段详解

Explain字段
Explain字段

id

属于select的编号,有几个select就会有几个id,与select出现的顺序一致。

id值越大,执行优先级越高;相同则从上往下执行;为null最后执行。

select_type

标识对应行使简单查询还是复杂查询。

  1. simple:简单查询,查询中不含有子查询和union
  2. primary:复杂查询中最外层select
  3. subquery:包含在select中的子查询(不在from子句中)
  4. derived:包含在from子句中的子查询。Mysql会讲结果存在临时表中。

table

表示行正在访问的表。

当from子句中有子查询,table展示<derivenN>,表示当前查询依赖id = N的查询

当有union时,table展示<union1,2>,1和2表示参与union的行id

type

表示关联类型访问类型,即mysql决定如何查找表中的行,查找数据行记录的大概范围

从优到劣依次为:system > const > eq_ref > ref > range > index > All

  • null:能够在优化阶段分解查询语句,在执行阶段不需要再访问表或索引
  • system:是const的特例,表里只有一条元组匹配时为system
  • const:mysql能对查询的某部分进行优化并转化为一个常量,用于主键索引或唯一索引
    的所有列与常数比较时,所以表最多有一个匹配行
  • eq_ref:主键索引或唯一索引的所有部分都被连接使用,最多只会返回一条符合的记录。
  • ref:与eq_ref类型,使用的是普通索引或唯一索引部分前缀,索引要和某个值比较,可能会找到多个符合条件的行
  • range:通常出现在 IN、between、>、<等操作中。使用一个索引来检索给定范围
  • index:扫描全索引就能拿到结果,一般扫描某个二级索引,这种扫描是直接扫叶子节点,速度较慢,称为覆盖索引
  • all:全表扫描,扫描聚集索引所有叶子节点,数据大,比index更慢

一般得保证查询达到range,最好能达到ref

partitions

如果查询是基于分区表的话,会显示查询将访问的分区

此字段与在5.7以前还需要再加上 partitions 关键字才会展示

possible_keys

显示本次查询可能会使用哪些索引来完成。

key

显示实际采用的索引

key_len

显示索引里使用的字节数,通过这个值可以算出具体使用了索引中的那些列

计算规则如下:

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字或字母占1个字节,
    一个汉字占3个字节
    • char(n):如果存汉字长度就是3n字节
  • varchar(n):如果存汉字则长度是3n+2字节,加的2字节用来存储字符串长度,因为varchar是变长字符串
  • 数值类型
    • tinyint:1字节
  • smallint:1字节
  • int:4字节
    • bigint:8字节
  • 时间类型
    • data:3字节
  • timestamp:4字节
  • datetime:8字节
  • 如果字段允许为null,需要1字节记录是否为null

ref

显示在key列记录的索引中,表查找所用到的列或常量

rows

显示本行查询估计要读取并检测的行数,不是结果集里的行数

filtered

filtered是一个半分比的值.rows*filtered/100可以估算出将要和explain中前一个表进行连接的行数(id值比当前表id值小的表)

此字段与在5.7以前还需要再加上 extended 关键字才会展示

Extra

展示额外信息,重要值如下:

  1. Using index:使用了覆盖索引
  2. Using where:使用where语句来处理结果,并且查询的列未被索引覆盖
  3. Using index condition:查询的列不完全被覆盖,where条件中是一个前导列的范围
  4. Using temporary:mysql建了一张临时表来处理查询
  5. Using filesort:使用外部排序而不是索引排序,数据较小时从内存排,否则需要在磁盘完成
  6. Select tables optimized way:使用了某些聚合函数(min()、max()等)来访问存在索引的某个字段

索引最佳实践

  • 全值匹配
  • 最左前缀原则
  • 在索引列上做操作(计算、函数、自动或手动类型转换)会导致索引失效
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引,减少select * 的语句
  • 使用不等于(!=或<>)、not in、not exists会导致索引失效
  • 大于、小于、大于等于、小于等于会根据检索比例、表大小等因素整体评估是否使用索引
  • is null 、 is not null 一般情况下也会导致索引失效
  • like 以通配符开头('%xa')也会导致索引失效
',46);function x(m,k){const i=t("ExternalLinkIcon");return r(),n("div",null,[h,u,p,e("blockquote",null,[e("p",null,[l("官方地址:"),e("a",b,[l("https://dev.mysql.com/doc/refman/5.7/en/explain-output.html"),o(i)])])]),d(" more "),f])}const _=a(c,[["render",x],["__file","2305020500.html.vue"]]);export{_ as default}; diff --git a/assets/2305020500.html-3e7b5938.js b/assets/2305020500.html-a0beaa1e.js similarity index 96% rename from assets/2305020500.html-3e7b5938.js rename to assets/2305020500.html-a0beaa1e.js index 8e655d72..7c8fd6e4 100644 --- a/assets/2305020500.html-3e7b5938.js +++ b/assets/2305020500.html-a0beaa1e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-753dfaa7","path":"/note/db/mysql/further/2305020500.html","title":"Explain详解与索引最佳实践","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":3,"title":"字段详解","slug":"字段详解","link":"#字段详解","children":[]},{"level":3,"title":"索引最佳实践","slug":"索引最佳实践","link":"#索引最佳实践","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.85,"words":1455},"filePathRelative":"note/db/mysql/further/2305020500.md","localizedDate":"2023年5月6日","excerpt":"

Explain详解与索引最佳实践

\\n

Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置
\\n一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。

\\n

如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。

\\n
\\n

官方地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

\\n
\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-753dfaa7","path":"/note/db/mysql/further/2305020500.html","title":"Explain详解与索引最佳实践","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":3,"title":"字段详解","slug":"字段详解","link":"#字段详解","children":[]},{"level":3,"title":"索引最佳实践","slug":"索引最佳实践","link":"#索引最佳实践","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.85,"words":1455},"filePathRelative":"note/db/mysql/further/2305020500.md","localizedDate":"2023年5月6日","excerpt":"

Explain详解与索引最佳实践

\\n

Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置
\\n一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。

\\n

如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。

\\n
\\n

官方地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

\\n
\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305020501.html-ec79daf8.js b/assets/2305020501.html-199e39c0.js similarity index 99% rename from assets/2305020501.html-ec79daf8.js rename to assets/2305020501.html-199e39c0.js index 6aea313b..0ace4fae 100644 --- a/assets/2305020501.html-ec79daf8.js +++ b/assets/2305020501.html-199e39c0.js @@ -1,4 +1,4 @@ -import{_ as a,o as r,c as l,d as t,a as e,b as o,e as i}from"./app-1efcbe9f.js";const s={},d=e("h1",{id:"sql底层执行原理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sql底层执行原理","aria-hidden":"true"},"#"),o(" SQL底层执行原理")],-1),n=e("p",null,"介绍SQL底层执行原理以及流程",-1),c=i(`

Mysql结构

大体来说,可以分为Server层和引擎层两部分

server层

server层主要包括连接器、查询缓存、分析器、优化器、执行器等。这些基本涵盖了mysql的大多数核心
服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

连接器

连接器是用来建立Mysql客户端与服务端之间通信的,客户端要想发起通信都必须先跟服务端建立通信连接

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证身份,这个时候用的就是你输入的用户名和密码来进行认证。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。文本中这个图是 show
processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接,关闭连接 kill <id>

客户端如果长时间不发送command到Server端,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。

-- 查看wait_timeout
+import{_ as a,o as r,c as l,d as t,a as e,b as o,e as i}from"./app-6a63891c.js";const s={},d=e("h1",{id:"sql底层执行原理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sql底层执行原理","aria-hidden":"true"},"#"),o(" SQL底层执行原理")],-1),n=e("p",null,"介绍SQL底层执行原理以及流程",-1),c=i(`

Mysql结构

大体来说,可以分为Server层和引擎层两部分

server层

server层主要包括连接器、查询缓存、分析器、优化器、执行器等。这些基本涵盖了mysql的大多数核心
服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

连接器

连接器是用来建立Mysql客户端与服务端之间通信的,客户端要想发起通信都必须先跟服务端建立通信连接

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证身份,这个时候用的就是你输入的用户名和密码来进行认证。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。文本中这个图是 show
processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接,关闭连接 kill <id>

客户端如果长时间不发送command到Server端,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。

-- 查看wait_timeout
 show global variables like 'wait_timeout';
 
 -- 设置全局服务器关闭非交互连接之前等待活动的秒数
diff --git a/assets/2305020501.html-18e5137b.js b/assets/2305020501.html-f66fe47a.js
similarity index 80%
rename from assets/2305020501.html-18e5137b.js
rename to assets/2305020501.html-f66fe47a.js
index d7abcd54..291628c4 100644
--- a/assets/2305020501.html-18e5137b.js
+++ b/assets/2305020501.html-f66fe47a.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-76f2d346","path":"/note/db/mysql/further/2305020501.html","title":"SQL底层执行原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"Mysql结构","slug":"mysql结构","link":"#mysql结构","children":[{"level":3,"title":"server层","slug":"server层","link":"#server层","children":[]},{"level":3,"title":"引擎层","slug":"引擎层","link":"#引擎层","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.84,"words":1751},"filePathRelative":"note/db/mysql/further/2305020501.md","localizedDate":"2023年5月7日","excerpt":"

SQL底层执行原理

\\n

介绍SQL底层执行原理以及流程

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-76f2d346","path":"/note/db/mysql/further/2305020501.html","title":"SQL底层执行原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"Mysql结构","slug":"mysql结构","link":"#mysql结构","children":[{"level":3,"title":"server层","slug":"server层","link":"#server层","children":[]},{"level":3,"title":"引擎层","slug":"引擎层","link":"#引擎层","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.84,"words":1751},"filePathRelative":"note/db/mysql/further/2305020501.md","localizedDate":"2023年5月7日","excerpt":"

SQL底层执行原理

\\n

介绍SQL底层执行原理以及流程

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305032100.html-d828cb0c.js b/assets/2305032100.html-3e380eec.js similarity index 92% rename from assets/2305032100.html-d828cb0c.js rename to assets/2305032100.html-3e380eec.js index 3abbbce9..7a6b5cf5 100644 --- a/assets/2305032100.html-d828cb0c.js +++ b/assets/2305032100.html-3e380eec.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1ae8d368","path":"/note/db/mysql/further/2305032100.html","title":"索引优化实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-08T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"优化实战","slug":"优化实战","link":"#优化实战","children":[{"level":3,"title":"联合索引第一个字段用范围不会走索引","slug":"联合索引第一个字段用范围不会走索引","link":"#联合索引第一个字段用范围不会走索引","children":[]},{"level":3,"title":"强制走索引","slug":"强制走索引","link":"#强制走索引","children":[]},{"level":3,"title":"覆盖索引优化","slug":"覆盖索引优化","link":"#覆盖索引优化","children":[]},{"level":3,"title":"in和or在表数据大会走索引,反之不会","slug":"in和or在表数据大会走索引-反之不会","link":"#in和or在表数据大会走索引-反之不会","children":[]},{"level":3,"title":"like语句一般情况下都会走索引","slug":"like语句一般情况下都会走索引","link":"#like语句一般情况下都会走索引","children":[]}]},{"level":2,"title":"索引下推","slug":"索引下推","link":"#索引下推","children":[]},{"level":2,"title":"trace工具","slug":"trace工具","link":"#trace工具","children":[]},{"level":2,"title":"ORDER BY 与 GROUP BY 优化","slug":"order-by-与-group-by-优化","link":"#order-by-与-group-by-优化","children":[{"level":3,"title":"总结","slug":"总结","link":"#总结","children":[]}]},{"level":2,"title":"文件排序原理","slug":"文件排序原理","link":"#文件排序原理","children":[{"level":3,"title":"排序方式","slug":"排序方式","link":"#排序方式","children":[]},{"level":3,"title":"排序过程","slug":"排序过程","link":"#排序过程","children":[]}]},{"level":2,"title":"设计原则","slug":"设计原则","link":"#设计原则","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.76,"words":3229},"filePathRelative":"note/db/mysql/further/2305032100.md","localizedDate":"2023年5月8日","excerpt":"

索引优化实战

\\n

MYSQL 索引优化实战笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1ae8d368","path":"/note/db/mysql/further/2305032100.html","title":"索引优化实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-08T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["MySQL","索引"]},"headers":[{"level":2,"title":"优化实战","slug":"优化实战","link":"#优化实战","children":[{"level":3,"title":"联合索引第一个字段用范围不会走索引","slug":"联合索引第一个字段用范围不会走索引","link":"#联合索引第一个字段用范围不会走索引","children":[]},{"level":3,"title":"强制走索引","slug":"强制走索引","link":"#强制走索引","children":[]},{"level":3,"title":"覆盖索引优化","slug":"覆盖索引优化","link":"#覆盖索引优化","children":[]},{"level":3,"title":"in和or在表数据大会走索引,反之不会","slug":"in和or在表数据大会走索引-反之不会","link":"#in和or在表数据大会走索引-反之不会","children":[]},{"level":3,"title":"like语句一般情况下都会走索引","slug":"like语句一般情况下都会走索引","link":"#like语句一般情况下都会走索引","children":[]}]},{"level":2,"title":"索引下推","slug":"索引下推","link":"#索引下推","children":[]},{"level":2,"title":"trace工具","slug":"trace工具","link":"#trace工具","children":[]},{"level":2,"title":"ORDER BY 与 GROUP BY 优化","slug":"order-by-与-group-by-优化","link":"#order-by-与-group-by-优化","children":[{"level":3,"title":"总结","slug":"总结","link":"#总结","children":[]}]},{"level":2,"title":"文件排序原理","slug":"文件排序原理","link":"#文件排序原理","children":[{"level":3,"title":"排序方式","slug":"排序方式","link":"#排序方式","children":[]},{"level":3,"title":"排序过程","slug":"排序过程","link":"#排序过程","children":[]}]},{"level":2,"title":"设计原则","slug":"设计原则","link":"#设计原则","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.76,"words":3227},"filePathRelative":"note/db/mysql/further/2305032100.md","localizedDate":"2023年5月8日","excerpt":"

索引优化实战

\\n

MYSQL 索引优化实战笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305032100.html-89e390c8.js b/assets/2305032100.html-89e390c8.js new file mode 100644 index 00000000..a30a8adb --- /dev/null +++ b/assets/2305032100.html-89e390c8.js @@ -0,0 +1,302 @@ +import{_ as e,r as t,o as i,c as o,d as l,a as n,b as s,f as p,e as c}from"./app-6a63891c.js";const u={},r=n("h1",{id:"索引优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#索引优化实战","aria-hidden":"true"},"#"),s(" 索引优化实战")],-1),d=n("p",null,"MYSQL 索引优化实战笔记",-1),v=c(`

优化实战

联合索引第一个字段用范围不会走索引

mysql内部优化规则可能会觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不知直接全表扫

强制走索引

-- 语句
+EXPLAIN SELECT * FROM user force index(idx)
+

添加强制索引可以让语句走索引,但是最总查找的效率不一定会比扫全表高,因为回表效率不高

覆盖索引优化

对于不走索引的语句,可以尝试使用覆盖索引来进行优化

in和or在表数据大会走索引,反之不会

like语句一般情况下都会走索引

like语句看着其实跟大于小于号差不多,之所以会走索引是因为用到了索引下推

索引下推

联合索引是按照最左前缀原则来进行匹配,正常情况下联合索引首个字段就使用like,那只会匹配到这个字段, 后面的因为不能确保是有序的就无法再利用索引。

5.6版本以前,首个字段就停止索引匹配,那就会拿这些对应索引的主键逐个回表找数据,再比对后序字段是否符合。5.6对此进行优化,在索引便利过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。这就是索引下推。

索引下推回减少回表次数,对于innodb引擎只能适用于二级索引,对主键索引并不适用

trace工具

trace

-- 开启trace
+set session optimizer_trace = "enabled=on",end_markers_in_json=on;
+
+-- 一起执行查询
+select * from employees where name > 'a' order by position;
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+-- 关闭 trace
+set session optimizer_trace = "enabled=off";
+

结果集

{
+  "steps": [
+    {
+      "join_preparation": {
+        /* 第一阶段:准备阶段,格式化SQL */
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select \`t_menu\`.\`menu_id\` AS \`menu_id\`,\`t_menu\`.\`menu_name\` AS \`menu_name\`,\`t_menu\`.\`menu_url\` AS \`menu_url\`,\`t_menu\`.\`parent_id\` AS \`parent_id\`,\`t_menu\`.\`level\` AS \`level\`,\`t_menu\`.\`icon\` AS \`icon\`,\`t_menu\`.\`order_by\` AS \`order_by\`,\`t_menu\`.\`hidden\` AS \`hidden\`,\`t_menu\`.\`remark\` AS \`remark\` from \`t_menu\` where (\`t_menu\`.\`menu_name\` > 'a') order by \`t_menu\`.\`parent_id\`"
+          }
+        ]
+        /* steps */
+      }
+      /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        /* 第二阶段:SQL优化阶段 */
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              /* 条件处理 */
+              "condition": "WHERE",
+              "original_condition": "(\`t_menu\`.\`menu_name\` > 'a')",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
+                }
+              ]
+              /* steps */
+            }
+            /* condition_processing */
+          },
+          {
+            "substitute_generated_columns": {
+            }
+            /* substitute_generated_columns */
+          },
+          {
+            "table_dependencies": [
+              /* 表依赖详情 */
+              {
+                "table": "\`t_menu\`",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ]
+                /* depends_on_map_bits */
+              }
+            ]
+            /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ]
+            /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              /* 预估表的访问成本 */
+              {
+                "table": "\`t_menu\`",
+                "range_analysis": {
+                  "table_scan": {
+                    /* 全表扫描情况 */
+                    "rows": 15,
+                    /* 扫描行数 */
+                    "cost": 6.1
+                    /* 扫描成本 */
+                  }
+                  /* table_scan */,
+                  "potential_range_indexes": [
+                    /* 查询可能使用的索引 */
+                    {
+                      "index": "PRIMARY",
+                      /* 主键索引 */
+                      "usable": false,
+                      "cause": "not_applicable"
+                    },
+                    {
+                      "index": "idx_test",
+                      /* 二级索引 */
+                      "usable": true,
+                      "key_parts": [
+                        "menu_name",
+                        "menu_url",
+                        "parent_id",
+                        "menu_id"
+                      ]
+                      /* key_parts */
+                    }
+                  ]
+                  /* potential_range_indexes */,
+                  "setup_range_conditions": [
+                  ]
+                  /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  }
+                  /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    /* 分析各个索引使用成本 */
+                    "range_scan_alternatives": [
+                      {
+                        "index": "idx_test",
+                        "ranges": [
+                          "a < menu_name"
+                          /* 索引使用范围 */
+                        ]
+                        /* ranges */,
+                        "index_dives_for_eq_ranges": true,
+                        "rowid_ordered": false,
+                        /* 使用该索引获取的记录是否按照主键排序 */
+                        "using_mrr": false,
+                        "index_only": false,
+                        /* 是否使用覆盖索引 */
+                        "rows": 15,
+                        /* 扫描行数 */
+                        "cost": 19.01,
+                        /* 使用成本 */
+                        "chosen": false,
+                        /* 是否选择 */
+                        "cause": "cost"
+                      }
+                    ]
+                    /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    }
+                    /* analyzing_roworder_intersect */
+                  }
+                  /* analyzing_range_alternatives */
+                }
+                /* range_analysis */
+              }
+            ]
+            /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "plan_prefix": [
+                ]
+                /* plan_prefix */,
+                "table": "\`t_menu\`",
+                "best_access_path": {
+                  /* 最有访问路径 */
+                  "considered_access_paths": [
+                    /* 最终选择的访问路径 */
+                    {
+                      "rows_to_scan": 15,
+                      "access_type": "scan",
+                      /* 访问类型:scan为全表扫描 */
+                      "resulting_rows": 15,
+                      "cost": 4,
+                      "chosen": true,
+                      "use_tmp_table": true
+                    }
+                  ]
+                  /* considered_access_paths */
+                }
+                /* best_access_path */,
+                "condition_filtering_pct": 100,
+                "rows_for_plan": 15,
+                "cost_for_plan": 4,
+                "sort_cost": 15,
+                "new_cost_for_plan": 19,
+                "chosen": true
+              }
+            ]
+            /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(\`t_menu\`.\`menu_name\` > 'a')",
+              "attached_conditions_computation": [
+              ]
+              /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "table": "\`t_menu\`",
+                  "attached": "(\`t_menu\`.\`menu_name\` > 'a')"
+                }
+              ]
+              /* attached_conditions_summary */
+            }
+            /* attaching_conditions_to_tables */
+          },
+          {
+            "clause_processing": {
+              "clause": "ORDER BY",
+              "original_clause": "\`t_menu\`.\`parent_id\`",
+              "items": [
+                {
+                  "item": "\`t_menu\`.\`parent_id\`"
+                }
+              ]
+              /* items */,
+              "resulting_clause_is_simple": true,
+              "resulting_clause": "\`t_menu\`.\`parent_id\`"
+            }
+            /* clause_processing */
+          },
+          {
+            "reconsidering_access_paths_for_index_ordering": {
+              "clause": "ORDER BY",
+              "steps": [
+              ]
+              /* steps */,
+              "index_order_summary": {
+                "table": "\`t_menu\`",
+                "index_provides_order": false,
+                "order_direction": "undefined",
+                "index": "unknown",
+                "plan_changed": false
+              }
+              /* index_order_summary */
+            }
+            /* reconsidering_access_paths_for_index_ordering */
+          },
+          {
+            "refine_plan": [
+              {
+                "table": "\`t_menu\`"
+              }
+            ]
+            /* refine_plan */
+          }
+        ]
+        /* steps */
+      }
+      /* join_optimization */
+    },
+    {
+      "join_execution": {
+        /* 第三阶段:执行阶段 */
+        "select#": 1,
+        "steps": [
+          {
+            "filesort_information": [
+              {
+                "direction": "asc",
+                "table": "\`t_menu\`",
+                "field": "parent_id"
+              }
+            ]
+            /* filesort_information */,
+            "filesort_priority_queue_optimization": {
+              "usable": false,
+              "cause": "not applicable (no LIMIT)"
+            }
+            /* filesort_priority_queue_optimization */,
+            "filesort_execution": [
+            ]
+            /* filesort_execution */,
+            "filesort_summary": {
+              /* 文件排序信息 */
+              "rows": 15,
+              /* 预计扫描行数 */
+              "examined_rows": 15,
+              /* 参与排序行 */
+              "number_of_tmp_files": 0,
+              /* 使用临时文件个数,如果为0表示全部用sort_puffer内存排序 */
+              "sort_buffer_size": 14656,
+              /* 排序缓存大小 */
+              "sort_mode": "<sort_key, rowid>"
+              /* 排序方式,双路 <sort_key, packed_additional_fields>:单路排序*/
+            }
+            /* filesort_summary */
+          }
+        ]
+        /* steps */
+      }
+      /* join_execution */
+    }
+  ]
+  /* steps */
+}
+

ORDER BY 与 GROUP BY 优化

order by排序的优化主要是利用索引已经排好序的规律来优化,如果没有使用到会通过文件进行排序消耗性能

  • 使用order by 联合索引,查询条件与排序条件中间字段不能断
  • order by 多个字段时,顺序要与索引字段一致
  • 使用降序排序不会走索引排序
  • in 不会走索引排序,因为结果集不确定是否有序
  • 使用 > 条件的不会走索引排序,可能是数据量太大

总结

  1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2. order by满足两种情况会使用Using index。
    1. order by语句使用索引最左前列。
    2. 使用where子句与order by子句条件列组合满足索引最左前列。
  3. 尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  4. 如果order by的条件不在索引列上,就会产生Using filesort。
  5. 能用覆盖索引尽量用覆盖索引
  6. group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order
    by
    null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。

文件排序原理

排序方式

单路排序

一次性取满足条件的数据的所有字段,然后在sort_buffer中进行排序。

trace工具的sort_mode显示<sort_key, additional_fields>或<sort_key, packed_additional_fields>

双路排序

双路排序又称回表排序。首先根据条件查询出相应的字段,然后取排序字段和主键在sort_buffer中排序,拍完序再去主键索引取信息

trace工具的sort_mode显示<sort_key, rowid>.

MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来 判断使用哪种排序模式。

  • 如果字段的总长度小于max_length_for_sort_data,那么使用单路排序模式;
  • 如果字段的总长度大于max_length_for_sort_data,那么使用双路排序模式;

排序过程

假设条件为 where name = 'a'

单路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出所有字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 返回结果

双路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出ID和排序字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 按照排序好的ID值回到原表取出所欲字段值然后返回

其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer 中进行排序,然后再通过主键回到原表查询需要的字段。

如果 MySQL 排序内存 sort_buffer 配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data
配置小点,让优化器选择使用双路排序算法,可以在sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据。

如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择全字段排序(单路排序)
,把需要的字段放到 sort_buffer
中,这样排序后就会直接从内存里返回查询结果了。

所以,MySQL通过 max_length_for_sort_data 这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率。

注意:如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。

设计原则

1. 代码先行,索引后上

不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗? 这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

2. 联合索引尽量覆盖条件

比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

3. 不要在小基数字段上建立索引

索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。
如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。
一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4. 长字符串我们可以采用前缀索引

尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。
当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的。

对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于
KEY index(name(20),age,position)。

此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来完整的name字段值进行比对。
但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排序是没法用上索引的, group by也是同理。所以这里要对前缀索引有一个了解。

5. where与order by冲突时优先where

在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?

一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。 因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。

6. 基于慢sql查询做优化

可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。

`,61),m={href:"http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7B",target:"_blank",rel:"noopener noreferrer"};function k(b,q){const a=t("ExternalLinkIcon");return i(),o("div",null,[r,d,l(" more "),v,n("blockquote",null,[n("p",null,[s("慢sql查询查阅:"),n("a",m,[s("http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7B"),p(a)])])])])}const g=e(u,[["render",k],["__file","2305032100.html.vue"]]);export{g as default}; diff --git a/assets/2305032100.html-eda76f6c.js b/assets/2305032100.html-eda76f6c.js deleted file mode 100644 index 053c3132..00000000 --- a/assets/2305032100.html-eda76f6c.js +++ /dev/null @@ -1,309 +0,0 @@ -import{_ as e,r as t,o,c as p,d as i,a as n,b as s,f as l,e as c}from"./app-1efcbe9f.js";const r={},u=n("h1",{id:"索引优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#索引优化实战","aria-hidden":"true"},"#"),s(" 索引优化实战")],-1),d=n("p",null,"MYSQL 索引优化实战笔记",-1),k=c(`

优化实战

联合索引第一个字段用范围不会走索引

mysql内部优化规则可能会觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不知直接全表扫

强制走索引

-- 语句
-EXPLAIN
-SELECT *
-FROM user force index(idx)
-

添加强制索引可以让语句走索引,但是最总查找的效率不一定会比扫全表高,因为回表效率不高

覆盖索引优化

对于不走索引的语句,可以尝试使用覆盖索引来进行优化

in和or在表数据大会走索引,反之不会

like语句一般情况下都会走索引

like语句看着其实跟大于小于号差不多,之所以会走索引是因为用到了索引下推

索引下推

联合索引是按照最左前缀原则来进行匹配,正常情况下联合索引首个字段就使用like,那只会匹配到这个字段, 后面的因为不能确保是有序的就无法再利用索引。

5.6版本以前,首个字段就停止索引匹配,那就会拿这些对应索引的主键逐个回表找数据,再比对后序字段是否符合

5.6对此进行优化,在索引便利过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。这就是索引下推。

索引下推回减少回表次数,对于innodb引擎只能适用于二级索引,对主键索引并不适用

trace工具

trace


--- 开启trace
-set session optimizer_trace = "enabled=on",end_markers_in_json=on;
-
--- 一起执行查询
-select *
-from employees
-where name > 'a'
-order by position;
-SELECT *
-FROM information_schema.OPTIMIZER_TRACE;
-
--- 关闭 trace
-set session optimizer_trace = "enabled=off";
-

结果集

{
-  "steps": [
-    {
-      "join_preparation": {
-        /* 第一阶段:准备阶段,格式化SQL */
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select \`t_menu\`.\`menu_id\` AS \`menu_id\`,\`t_menu\`.\`menu_name\` AS \`menu_name\`,\`t_menu\`.\`menu_url\` AS \`menu_url\`,\`t_menu\`.\`parent_id\` AS \`parent_id\`,\`t_menu\`.\`level\` AS \`level\`,\`t_menu\`.\`icon\` AS \`icon\`,\`t_menu\`.\`order_by\` AS \`order_by\`,\`t_menu\`.\`hidden\` AS \`hidden\`,\`t_menu\`.\`remark\` AS \`remark\` from \`t_menu\` where (\`t_menu\`.\`menu_name\` > 'a') order by \`t_menu\`.\`parent_id\`"
-          }
-        ]
-        /* steps */
-      }
-      /* join_preparation */
-    },
-    {
-      "join_optimization": {
-        /* 第二阶段:SQL优化阶段 */
-        "select#": 1,
-        "steps": [
-          {
-            "condition_processing": {
-              /* 条件处理 */
-              "condition": "WHERE",
-              "original_condition": "(\`t_menu\`.\`menu_name\` > 'a')",
-              "steps": [
-                {
-                  "transformation": "equality_propagation",
-                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
-                },
-                {
-                  "transformation": "constant_propagation",
-                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
-                },
-                {
-                  "transformation": "trivial_condition_removal",
-                  "resulting_condition": "(\`t_menu\`.\`menu_name\` > 'a')"
-                }
-              ]
-              /* steps */
-            }
-            /* condition_processing */
-          },
-          {
-            "substitute_generated_columns": {
-            }
-            /* substitute_generated_columns */
-          },
-          {
-            "table_dependencies": [
-              /* 表依赖详情 */
-              {
-                "table": "\`t_menu\`",
-                "row_may_be_null": false,
-                "map_bit": 0,
-                "depends_on_map_bits": [
-                ]
-                /* depends_on_map_bits */
-              }
-            ]
-            /* table_dependencies */
-          },
-          {
-            "ref_optimizer_key_uses": [
-            ]
-            /* ref_optimizer_key_uses */
-          },
-          {
-            "rows_estimation": [
-              /* 预估表的访问成本 */
-              {
-                "table": "\`t_menu\`",
-                "range_analysis": {
-                  "table_scan": {
-                    /* 全表扫描情况 */
-                    "rows": 15,
-                    /* 扫描行数 */
-                    "cost": 6.1
-                    /* 扫描成本 */
-                  }
-                  /* table_scan */,
-                  "potential_range_indexes": [
-                    /* 查询可能使用的索引 */
-                    {
-                      "index": "PRIMARY",
-                      /* 主键索引 */
-                      "usable": false,
-                      "cause": "not_applicable"
-                    },
-                    {
-                      "index": "idx_test",
-                      /* 二级索引 */
-                      "usable": true,
-                      "key_parts": [
-                        "menu_name",
-                        "menu_url",
-                        "parent_id",
-                        "menu_id"
-                      ]
-                      /* key_parts */
-                    }
-                  ]
-                  /* potential_range_indexes */,
-                  "setup_range_conditions": [
-                  ]
-                  /* setup_range_conditions */,
-                  "group_index_range": {
-                    "chosen": false,
-                    "cause": "not_group_by_or_distinct"
-                  }
-                  /* group_index_range */,
-                  "analyzing_range_alternatives": {
-                    /* 分析各个索引使用成本 */
-                    "range_scan_alternatives": [
-                      {
-                        "index": "idx_test",
-                        "ranges": [
-                          "a < menu_name"
-                          /* 索引使用范围 */
-                        ]
-                        /* ranges */,
-                        "index_dives_for_eq_ranges": true,
-                        "rowid_ordered": false,
-                        /* 使用该索引获取的记录是否按照主键排序 */
-                        "using_mrr": false,
-                        "index_only": false,
-                        /* 是否使用覆盖索引 */
-                        "rows": 15,
-                        /* 扫描行数 */
-                        "cost": 19.01,
-                        /* 使用成本 */
-                        "chosen": false,
-                        /* 是否选择 */
-                        "cause": "cost"
-                      }
-                    ]
-                    /* range_scan_alternatives */,
-                    "analyzing_roworder_intersect": {
-                      "usable": false,
-                      "cause": "too_few_roworder_scans"
-                    }
-                    /* analyzing_roworder_intersect */
-                  }
-                  /* analyzing_range_alternatives */
-                }
-                /* range_analysis */
-              }
-            ]
-            /* rows_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "plan_prefix": [
-                ]
-                /* plan_prefix */,
-                "table": "\`t_menu\`",
-                "best_access_path": {
-                  /* 最有访问路径 */
-                  "considered_access_paths": [
-                    /* 最终选择的访问路径 */
-                    {
-                      "rows_to_scan": 15,
-                      "access_type": "scan",
-                      /* 访问类型:scan为全表扫描 */
-                      "resulting_rows": 15,
-                      "cost": 4,
-                      "chosen": true,
-                      "use_tmp_table": true
-                    }
-                  ]
-                  /* considered_access_paths */
-                }
-                /* best_access_path */,
-                "condition_filtering_pct": 100,
-                "rows_for_plan": 15,
-                "cost_for_plan": 4,
-                "sort_cost": 15,
-                "new_cost_for_plan": 19,
-                "chosen": true
-              }
-            ]
-            /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": "(\`t_menu\`.\`menu_name\` > 'a')",
-              "attached_conditions_computation": [
-              ]
-              /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "table": "\`t_menu\`",
-                  "attached": "(\`t_menu\`.\`menu_name\` > 'a')"
-                }
-              ]
-              /* attached_conditions_summary */
-            }
-            /* attaching_conditions_to_tables */
-          },
-          {
-            "clause_processing": {
-              "clause": "ORDER BY",
-              "original_clause": "\`t_menu\`.\`parent_id\`",
-              "items": [
-                {
-                  "item": "\`t_menu\`.\`parent_id\`"
-                }
-              ]
-              /* items */,
-              "resulting_clause_is_simple": true,
-              "resulting_clause": "\`t_menu\`.\`parent_id\`"
-            }
-            /* clause_processing */
-          },
-          {
-            "reconsidering_access_paths_for_index_ordering": {
-              "clause": "ORDER BY",
-              "steps": [
-              ]
-              /* steps */,
-              "index_order_summary": {
-                "table": "\`t_menu\`",
-                "index_provides_order": false,
-                "order_direction": "undefined",
-                "index": "unknown",
-                "plan_changed": false
-              }
-              /* index_order_summary */
-            }
-            /* reconsidering_access_paths_for_index_ordering */
-          },
-          {
-            "refine_plan": [
-              {
-                "table": "\`t_menu\`"
-              }
-            ]
-            /* refine_plan */
-          }
-        ]
-        /* steps */
-      }
-      /* join_optimization */
-    },
-    {
-      "join_execution": {
-        /* 第三阶段:执行阶段 */
-        "select#": 1,
-        "steps": [
-          {
-            "filesort_information": [
-              {
-                "direction": "asc",
-                "table": "\`t_menu\`",
-                "field": "parent_id"
-              }
-            ]
-            /* filesort_information */,
-            "filesort_priority_queue_optimization": {
-              "usable": false,
-              "cause": "not applicable (no LIMIT)"
-            }
-            /* filesort_priority_queue_optimization */,
-            "filesort_execution": [
-            ]
-            /* filesort_execution */,
-            "filesort_summary": {
-              /* 文件排序信息 */
-              "rows": 15,
-              /* 预计扫描行数 */
-              "examined_rows": 15,
-              /* 参与排序行 */
-              "number_of_tmp_files": 0,
-              /* 使用临时文件个数,如果为0表示全部用sort_puffer内存排序 */
-              "sort_buffer_size": 14656,
-              /* 排序缓存大小 */
-              "sort_mode": "<sort_key, rowid>"
-              /* 排序方式,双路 <sort_key, packed_additional_fields>:单路排序*/
-            }
-            /* filesort_summary */
-          }
-        ]
-        /* steps */
-      }
-      /* join_execution */
-    }
-  ]
-  /* steps */
-}
-

ORDER BY 与 GROUP BY 优化

order by排序的优化主要是利用索引已经排好序的规律来优化,如果没有使用到会通过文件进行排序消耗性能

  • 使用order by 联合索引,查询条件与排序条件中间字段不能断
  • order by 多个字段时,顺序要与索引字段一致
  • 使用降序排序不会走索引排序
  • in 不会走索引排序,因为结果集不确定是否有序
  • 使用 > 条件的不会走索引排序,可能是数据量太大

总结

  1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2. order by满足两种情况会使用Using index。
    1. order by语句使用索引最左前列。
    2. 使用where子句与order by子句条件列组合满足索引最左前列。
  3. 尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  4. 如果order by的条件不在索引列上,就会产生Using filesort。
  5. 能用覆盖索引尽量用覆盖索引
  6. group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by
    null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。

文件排序原理

排序方式

单路排序

一次性取满足条件的数据的所有字段,然后在sort_buffer中进行排序。

trace工具的sort_mode显示<sort_key, additional_fields>或<sort_key, packed_additional_fields>

双路排序

双路排序又称回表排序。首先根据条件查询出相应的字段,然后取排序字段和主键在sort_buffer中排序,拍完序再去主键索引取信息

trace工具的sort_mode显示<sort_key, rowid>.

MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来 判断使用哪种排序模式。

  • 如果字段的总长度小于max_length_for_sort_data,那么使用单路排序模式;
  • 如果字段的总长度大于max_length_for_sort_data,那么使用双路排序模式;

排序过程

假设条件为 where name = 'a'

单路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出所有字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 返回结果

双路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出ID和排序字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 按照排序好的ID值回到原表取出所欲字段值然后返回

其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer
中进行排序,然后再通过主键回到原表查询需要的字段。

如果 MySQL 排序内存 sort_buffer 配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data
配置小点,让优化器选择使用双路排序算法,可以在sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据。

如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择全字段排序(单路排序),把需要的字段放到 sort_buffer
中,这样排序后就会直接从内存里返回查询结果了。

所以,MySQL通过 max_length_for_sort_data 这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率。

注意:如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。

设计原则

1. 代码先行,索引后上

不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗? 这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

2. 联合索引尽量覆盖条件

比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group
by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

3. 不要在小基数字段上建立索引

索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。
如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。
一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4. 长字符串我们可以采用前缀索引

尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。
当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的。 对于这种varchar(255)
的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY index(name(20)
,age,position)。
此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来完整的name字段值进行比对。
但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排序是没法用上索引的, group by也是同理。所以这里大家要对前缀索引有一个了解。

5. where与order by冲突时优先where

在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?
一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。
因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。

6. 基于慢sql查询做优化

可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。

`,59),v={href:"http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7B",target:"_blank",rel:"noopener noreferrer"};function m(b,q){const a=t("ExternalLinkIcon");return o(),p("div",null,[u,d,i(" more "),k,n("blockquote",null,[n("p",null,[s("慢sql查询查阅:"),n("a",v,[s("http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7B"),l(a)])])])])}const h=e(r,[["render",m],["__file","2305032100.html.vue"]]);export{h as default}; diff --git a/assets/2305052159.html-dafcd578.js b/assets/2305052159.html-29576ffe.js similarity index 91% rename from assets/2305052159.html-dafcd578.js rename to assets/2305052159.html-29576ffe.js index f51ca99f..24daf8a2 100644 --- a/assets/2305052159.html-dafcd578.js +++ b/assets/2305052159.html-29576ffe.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-305fe098","path":"/note/java/jvm/2305052159.html","title":"JVM简介","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":1,"date":"2023-05-05T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"类加载器(ClassLoad)","slug":"类加载器-classload","link":"#类加载器-classload","children":[]},{"level":2,"title":"运行时数据区(Runtime Data Area)","slug":"运行时数据区-runtime-data-area","link":"#运行时数据区-runtime-data-area","children":[]},{"level":2,"title":"执行引擎(Execution Engine)","slug":"执行引擎-execution-engine","link":"#执行引擎-execution-engine","children":[]},{"level":2,"title":"本地库接口(Native Interface)","slug":"本地库接口-native-interface","link":"#本地库接口-native-interface","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.41,"words":424},"filePathRelative":"note/java/jvm/2305052159.md","localizedDate":"2023年5月5日","excerpt":"

JVM简介

\\n

Java虚拟机(JVM)是Java程序运行行的关键组件,他负责将Java源代码转换为可执行的机器码。主要由:类加载器、运行时数据区、执行引擎、本地库接口组成。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-305fe098","path":"/note/java/jvm/2305052159.html","title":"JVM简介","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":1,"date":"2023-05-05T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"类加载器(ClassLoad)","slug":"类加载器-classload","link":"#类加载器-classload","children":[]},{"level":2,"title":"运行时数据区(Runtime Data Area)","slug":"运行时数据区-runtime-data-area","link":"#运行时数据区-runtime-data-area","children":[]},{"level":2,"title":"执行引擎(Execution Engine)","slug":"执行引擎-execution-engine","link":"#执行引擎-execution-engine","children":[]},{"level":2,"title":"本地库接口(Native Interface)","slug":"本地库接口-native-interface","link":"#本地库接口-native-interface","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.41,"words":424},"filePathRelative":"note/java/jvm/2305052159.md","localizedDate":"2023年5月5日","excerpt":"

JVM简介

\\n

Java虚拟机(JVM)是Java程序运行行的关键组件,他负责将Java源代码转换为可执行的机器码。主要由:类加载器、运行时数据区、执行引擎、本地库接口组成。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305052159.html-6918d3f2.js b/assets/2305052159.html-de6983c6.js similarity index 95% rename from assets/2305052159.html-6918d3f2.js rename to assets/2305052159.html-de6983c6.js index 74b02a14..3064f3f9 100644 --- a/assets/2305052159.html-6918d3f2.js +++ b/assets/2305052159.html-de6983c6.js @@ -1 +1 @@ -import{_ as e,o as t,c as i,d as n,a,b as r,e as o}from"./app-1efcbe9f.js";const s={},c=a("h1",{id:"jvm简介",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#jvm简介","aria-hidden":"true"},"#"),r(" JVM简介")],-1),d=a("p",null,"Java虚拟机(JVM)是Java程序运行行的关键组件,他负责将Java源代码转换为可执行的机器码。主要由:类加载器、运行时数据区、执行引擎、本地库接口组成。",-1),h=o('
JVM组成
JVM组成

类加载器(ClassLoad)

类加载器主要负责将Java字节码文件加载到内存中,以便程序运行时使用。

运行时数据区(Runtime Data Area)

运行时数据区是JVM内存的一部分,用于存储程序运行时的数据。它包括以下几个区域:

  • 程序计数器(Program Counter Register): 记录当前线程执行的字节码行号,用于指示下一条需要执行的指令。
  • Java虚拟机栈(Java Virtual Machine Stacks): 描述Java方法执行的内存模型,包括局部变量表、操作数栈、动态链接等信息。
  • 本地方法栈(Native Method Stack): 为虚拟机调用Native方法服务。
  • Java堆(Java Heap): 存放对象实例,是Java虚拟机中内存最大的一块,被所有线程共享。
  • 方法区(Method Area): 存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

执行引擎(Execution Engine)

执行引擎将字节码翻译成底层系统指令,再交由CPU执行

本地库接口(Native Interface)

本地库接口用于扩展Java平台的功能,融合不同编程语言的功能为Java使用。通过本地库接口,Java程序可以调用本地代码库中的功能,实现对特定平台资源的访问和操作。

',10);function l(g,u){return t(),i("div",null,[c,d,n(" more "),h])}const _=e(s,[["render",l],["__file","2305052159.html.vue"]]);export{_ as default}; +import{_ as e,o as t,c as i,d as n,a,b as r,e as o}from"./app-6a63891c.js";const s={},c=a("h1",{id:"jvm简介",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#jvm简介","aria-hidden":"true"},"#"),r(" JVM简介")],-1),d=a("p",null,"Java虚拟机(JVM)是Java程序运行行的关键组件,他负责将Java源代码转换为可执行的机器码。主要由:类加载器、运行时数据区、执行引擎、本地库接口组成。",-1),h=o('
JVM组成
JVM组成

类加载器(ClassLoad)

类加载器主要负责将Java字节码文件加载到内存中,以便程序运行时使用。

运行时数据区(Runtime Data Area)

运行时数据区是JVM内存的一部分,用于存储程序运行时的数据。它包括以下几个区域:

  • 程序计数器(Program Counter Register): 记录当前线程执行的字节码行号,用于指示下一条需要执行的指令。
  • Java虚拟机栈(Java Virtual Machine Stacks): 描述Java方法执行的内存模型,包括局部变量表、操作数栈、动态链接等信息。
  • 本地方法栈(Native Method Stack): 为虚拟机调用Native方法服务。
  • Java堆(Java Heap): 存放对象实例,是Java虚拟机中内存最大的一块,被所有线程共享。
  • 方法区(Method Area): 存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

执行引擎(Execution Engine)

执行引擎将字节码翻译成底层系统指令,再交由CPU执行

本地库接口(Native Interface)

本地库接口用于扩展Java平台的功能,融合不同编程语言的功能为Java使用。通过本地库接口,Java程序可以调用本地代码库中的功能,实现对特定平台资源的访问和操作。

',10);function l(g,u){return t(),i("div",null,[c,d,n(" more "),h])}const _=e(s,[["render",l],["__file","2305052159.html.vue"]]);export{_ as default}; diff --git a/assets/2305081011.html-fcf35e80.js b/assets/2305081011.html-4b54635e.js similarity index 95% rename from assets/2305081011.html-fcf35e80.js rename to assets/2305081011.html-4b54635e.js index 66a34410..9ad009d6 100644 --- a/assets/2305081011.html-fcf35e80.js +++ b/assets/2305081011.html-4b54635e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3fc52a21","path":"/en/tutorial/docker/2305081011.html","title":"Docker安装","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"Docker的自动化安装","slug":"docker的自动化安装","link":"#docker的自动化安装","children":[]},{"level":2,"title":"Docker手动安装","slug":"docker手动安装","link":"#docker手动安装","children":[{"level":3,"title":"卸载Docker(可选)","slug":"卸载docker-可选","link":"#卸载docker-可选","children":[]},{"level":3,"title":"设置源仓库","slug":"设置源仓库","link":"#设置源仓库","children":[]},{"level":3,"title":"Docker安装","slug":"docker安装-1","link":"#docker安装-1","children":[]}]},{"level":2,"title":"Docker启动","slug":"docker启动","link":"#docker启动","children":[]},{"level":2,"title":"删除Docker","slug":"删除docker","link":"#删除docker","children":[]},{"level":2,"title":"Docker其他常见命令","slug":"docker其他常见命令","link":"#docker其他常见命令","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.7,"words":1110},"filePathRelative":"en/tutorial/docker/2305081011.md","localizedDate":"May 6, 2023","excerpt":"

Docker安装

\\n

本文记录Docker容器安装的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-3fc52a21","path":"/en/tutorial/docker/2305081011.html","title":"Docker安装","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"Docker的自动化安装","slug":"docker的自动化安装","link":"#docker的自动化安装","children":[]},{"level":2,"title":"Docker手动安装","slug":"docker手动安装","link":"#docker手动安装","children":[{"level":3,"title":"卸载Docker(可选)","slug":"卸载docker-可选","link":"#卸载docker-可选","children":[]},{"level":3,"title":"设置源仓库","slug":"设置源仓库","link":"#设置源仓库","children":[]},{"level":3,"title":"Docker安装","slug":"docker安装-1","link":"#docker安装-1","children":[]}]},{"level":2,"title":"Docker启动","slug":"docker启动","link":"#docker启动","children":[]},{"level":2,"title":"删除Docker","slug":"删除docker","link":"#删除docker","children":[]},{"level":2,"title":"Docker其他常见命令","slug":"docker其他常见命令","link":"#docker其他常见命令","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.7,"words":1110},"filePathRelative":"en/tutorial/docker/2305081011.md","localizedDate":"May 6, 2023","excerpt":"

Docker安装

\\n

本文记录Docker容器安装的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081011.html-652250d9.js b/assets/2305081011.html-667135b8.js similarity index 99% rename from assets/2305081011.html-652250d9.js rename to assets/2305081011.html-667135b8.js index 78cd9bea..bf70cb0e 100644 --- a/assets/2305081011.html-652250d9.js +++ b/assets/2305081011.html-667135b8.js @@ -1,4 +1,4 @@ -import{_ as c,r as t,o as r,c as l,d as p,a as e,b as a,f as o,e as n}from"./app-1efcbe9f.js";const i={},d=e("h1",{id:"docker安装",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#docker安装","aria-hidden":"true"},"#"),a(" Docker安装")],-1),u=e("p",null,"本文记录Docker容器安装的详细步骤",-1),k=n(`

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
+import{_ as c,r as t,o as r,c as l,d as p,a as e,b as a,f as o,e as n}from"./app-6a63891c.js";const i={},d=e("h1",{id:"docker安装",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#docker安装","aria-hidden":"true"},"#"),a(" Docker安装")],-1),u=e("p",null,"本文记录Docker容器安装的详细步骤",-1),k=n(`

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
 LSB Version:	:core-4.1-amd64:core-4.1-noarch
 Distributor ID:	CentOS
 Description:	CentOS Linux release 7.6.1810 (Core)
diff --git a/assets/2305081011.html-1e891ec3.js b/assets/2305081011.html-699d2aef.js
similarity index 95%
rename from assets/2305081011.html-1e891ec3.js
rename to assets/2305081011.html-699d2aef.js
index 3e3941ff..287e70e1 100644
--- a/assets/2305081011.html-1e891ec3.js
+++ b/assets/2305081011.html-699d2aef.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-4468c549","path":"/note/other/2305081011.html","title":"Docker安装","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"Docker的自动化安装","slug":"docker的自动化安装","link":"#docker的自动化安装","children":[]},{"level":2,"title":"Docker手动安装","slug":"docker手动安装","link":"#docker手动安装","children":[{"level":3,"title":"卸载Docker(可选)","slug":"卸载docker-可选","link":"#卸载docker-可选","children":[]},{"level":3,"title":"设置源仓库","slug":"设置源仓库","link":"#设置源仓库","children":[]},{"level":3,"title":"Docker安装","slug":"docker安装-1","link":"#docker安装-1","children":[]}]},{"level":2,"title":"Docker启动","slug":"docker启动","link":"#docker启动","children":[]},{"level":2,"title":"删除Docker","slug":"删除docker","link":"#删除docker","children":[]},{"level":2,"title":"Docker其他常见命令","slug":"docker其他常见命令","link":"#docker其他常见命令","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.7,"words":1110},"filePathRelative":"note/other/2305081011.md","localizedDate":"2023年5月6日","excerpt":"

Docker安装

\\n

本文记录Docker容器安装的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-4468c549","path":"/note/other/2305081011.html","title":"Docker安装","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"Docker的自动化安装","slug":"docker的自动化安装","link":"#docker的自动化安装","children":[]},{"level":2,"title":"Docker手动安装","slug":"docker手动安装","link":"#docker手动安装","children":[{"level":3,"title":"卸载Docker(可选)","slug":"卸载docker-可选","link":"#卸载docker-可选","children":[]},{"level":3,"title":"设置源仓库","slug":"设置源仓库","link":"#设置源仓库","children":[]},{"level":3,"title":"Docker安装","slug":"docker安装-1","link":"#docker安装-1","children":[]}]},{"level":2,"title":"Docker启动","slug":"docker启动","link":"#docker启动","children":[]},{"level":2,"title":"删除Docker","slug":"删除docker","link":"#删除docker","children":[]},{"level":2,"title":"Docker其他常见命令","slug":"docker其他常见命令","link":"#docker其他常见命令","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.7,"words":1110},"filePathRelative":"note/other/2305081011.md","localizedDate":"2023年5月6日","excerpt":"

Docker安装

\\n

本文记录Docker容器安装的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081011.html-c5f5302c.js b/assets/2305081011.html-902da75d.js similarity index 99% rename from assets/2305081011.html-c5f5302c.js rename to assets/2305081011.html-902da75d.js index 78cd9bea..bf70cb0e 100644 --- a/assets/2305081011.html-c5f5302c.js +++ b/assets/2305081011.html-902da75d.js @@ -1,4 +1,4 @@ -import{_ as c,r as t,o as r,c as l,d as p,a as e,b as a,f as o,e as n}from"./app-1efcbe9f.js";const i={},d=e("h1",{id:"docker安装",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#docker安装","aria-hidden":"true"},"#"),a(" Docker安装")],-1),u=e("p",null,"本文记录Docker容器安装的详细步骤",-1),k=n(`

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
+import{_ as c,r as t,o as r,c as l,d as p,a as e,b as a,f as o,e as n}from"./app-6a63891c.js";const i={},d=e("h1",{id:"docker安装",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#docker安装","aria-hidden":"true"},"#"),a(" Docker安装")],-1),u=e("p",null,"本文记录Docker容器安装的详细步骤",-1),k=n(`

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
 LSB Version:	:core-4.1-amd64:core-4.1-noarch
 Distributor ID:	CentOS
 Description:	CentOS Linux release 7.6.1810 (Core)
diff --git a/assets/2305081101.html-17d24cf1.js b/assets/2305081101.html-1759f57e.js
similarity index 96%
rename from assets/2305081101.html-17d24cf1.js
rename to assets/2305081101.html-1759f57e.js
index 51ebc78e..436fcd21 100644
--- a/assets/2305081101.html-17d24cf1.js
+++ b/assets/2305081101.html-1759f57e.js
@@ -1 +1 @@
-const l=JSON.parse('{"key":"v-7763b6e7","path":"/note/other/2305081101.html","title":"Docker 安装 Mysql5.7","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"创建并启动容器","slug":"创建并启动容器","link":"#创建并启动容器","children":[{"level":3,"title":"下载Mysql镜像","slug":"下载mysql镜像","link":"#下载mysql镜像","children":[]},{"level":3,"title":"查看已下载镜像","slug":"查看已下载镜像","link":"#查看已下载镜像","children":[]},{"level":3,"title":"创建mysql容器","slug":"创建mysql容器","link":"#创建mysql容器","children":[]},{"level":3,"title":"查看容器是否启动成功","slug":"查看容器是否启动成功","link":"#查看容器是否启动成功","children":[]},{"level":3,"title":"进入容器","slug":"进入容器","link":"#进入容器","children":[]},{"level":3,"title":"修改密码、创建远程账号","slug":"修改密码、创建远程账号","link":"#修改密码、创建远程账号","children":[]},{"level":3,"title":"退出,重启mysql容器","slug":"退出-重启mysql容器","link":"#退出-重启mysql容器","children":[]}]},{"level":2,"title":"Navicat远程登录","slug":"navicat远程登录","link":"#navicat远程登录","children":[{"level":3,"title":"检查防火墙","slug":"检查防火墙","link":"#检查防火墙","children":[]},{"level":3,"title":"使用创建远程连接的账号登录","slug":"使用创建远程连接的账号登录","link":"#使用创建远程连接的账号登录","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.99,"words":597},"filePathRelative":"note/other/2305081101.md","localizedDate":"2023年5月6日","excerpt":"

Docker 安装 Mysql5.7

\\n

本文记录Docker容器安装Mysql5.7的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; +const l=JSON.parse('{"key":"v-7763b6e7","path":"/note/other/2305081101.html","title":"Docker 安装 Mysql5.7","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"创建并启动容器","slug":"创建并启动容器","link":"#创建并启动容器","children":[{"level":3,"title":"下载Mysql镜像","slug":"下载mysql镜像","link":"#下载mysql镜像","children":[]},{"level":3,"title":"查看已下载镜像","slug":"查看已下载镜像","link":"#查看已下载镜像","children":[]},{"level":3,"title":"创建mysql容器","slug":"创建mysql容器","link":"#创建mysql容器","children":[]},{"level":3,"title":"查看容器是否启动成功","slug":"查看容器是否启动成功","link":"#查看容器是否启动成功","children":[]},{"level":3,"title":"进入容器","slug":"进入容器","link":"#进入容器","children":[]},{"level":3,"title":"修改密码、创建远程账号","slug":"修改密码、创建远程账号","link":"#修改密码、创建远程账号","children":[]},{"level":3,"title":"退出,重启mysql容器","slug":"退出-重启mysql容器","link":"#退出-重启mysql容器","children":[]}]},{"level":2,"title":"Navicat远程登录","slug":"navicat远程登录","link":"#navicat远程登录","children":[{"level":3,"title":"检查防火墙","slug":"检查防火墙","link":"#检查防火墙","children":[]},{"level":3,"title":"使用创建远程连接的账号登录","slug":"使用创建远程连接的账号登录","link":"#使用创建远程连接的账号登录","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.99,"words":597},"filePathRelative":"note/other/2305081101.md","localizedDate":"2023年5月6日","excerpt":"

Docker 安装 Mysql5.7

\\n

本文记录Docker容器安装Mysql5.7的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2305081101.html-46a00fec.js b/assets/2305081101.html-73b58578.js similarity index 99% rename from assets/2305081101.html-46a00fec.js rename to assets/2305081101.html-73b58578.js index 80b0447d..6becb0cd 100644 --- a/assets/2305081101.html-46a00fec.js +++ b/assets/2305081101.html-73b58578.js @@ -1,4 +1,4 @@ -import{_ as s,o as n,c as e,d as t,a,b as o,e as p}from"./app-1efcbe9f.js";const r={},l=a("h1",{id:"docker-安装-mysql5-7",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker-安装-mysql5-7","aria-hidden":"true"},"#"),o(" Docker 安装 Mysql5.7")],-1),c=a("p",null,"本文记录Docker容器安装Mysql5.7的详细步骤",-1),d=p(`

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
+import{_ as s,o as n,c as e,d as t,a,b as o,e as p}from"./app-6a63891c.js";const r={},l=a("h1",{id:"docker-安装-mysql5-7",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker-安装-mysql5-7","aria-hidden":"true"},"#"),o(" Docker 安装 Mysql5.7")],-1),c=a("p",null,"本文记录Docker容器安装Mysql5.7的详细步骤",-1),d=p(`

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
 docker pull mysql:5.7
 

查看已下载镜像

docker images
 

创建mysql容器

快捷添加

# 创建一个名为mysql的容器,密码123
diff --git a/assets/2305081101.html-cf7208d0.js b/assets/2305081101.html-880cd6a8.js
similarity index 96%
rename from assets/2305081101.html-cf7208d0.js
rename to assets/2305081101.html-880cd6a8.js
index 83055691..2eedbf40 100644
--- a/assets/2305081101.html-cf7208d0.js
+++ b/assets/2305081101.html-880cd6a8.js
@@ -1 +1 @@
-const l=JSON.parse('{"key":"v-72c01bbf","path":"/en/tutorial/docker/2305081101.html","title":"Docker 安装 Mysql5.7","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"创建并启动容器","slug":"创建并启动容器","link":"#创建并启动容器","children":[{"level":3,"title":"下载Mysql镜像","slug":"下载mysql镜像","link":"#下载mysql镜像","children":[]},{"level":3,"title":"查看已下载镜像","slug":"查看已下载镜像","link":"#查看已下载镜像","children":[]},{"level":3,"title":"创建mysql容器","slug":"创建mysql容器","link":"#创建mysql容器","children":[]},{"level":3,"title":"查看容器是否启动成功","slug":"查看容器是否启动成功","link":"#查看容器是否启动成功","children":[]},{"level":3,"title":"进入容器","slug":"进入容器","link":"#进入容器","children":[]},{"level":3,"title":"修改密码、创建远程账号","slug":"修改密码、创建远程账号","link":"#修改密码、创建远程账号","children":[]},{"level":3,"title":"退出,重启mysql容器","slug":"退出-重启mysql容器","link":"#退出-重启mysql容器","children":[]}]},{"level":2,"title":"Navicat远程登录","slug":"navicat远程登录","link":"#navicat远程登录","children":[{"level":3,"title":"检查防火墙","slug":"检查防火墙","link":"#检查防火墙","children":[]},{"level":3,"title":"使用创建远程连接的账号登录","slug":"使用创建远程连接的账号登录","link":"#使用创建远程连接的账号登录","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.99,"words":597},"filePathRelative":"en/tutorial/docker/2305081101.md","localizedDate":"May 6, 2023","excerpt":"

Docker 安装 Mysql5.7

\\n

本文记录Docker容器安装Mysql5.7的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; +const l=JSON.parse('{"key":"v-72c01bbf","path":"/en/tutorial/docker/2305081101.html","title":"Docker 安装 Mysql5.7","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"创建并启动容器","slug":"创建并启动容器","link":"#创建并启动容器","children":[{"level":3,"title":"下载Mysql镜像","slug":"下载mysql镜像","link":"#下载mysql镜像","children":[]},{"level":3,"title":"查看已下载镜像","slug":"查看已下载镜像","link":"#查看已下载镜像","children":[]},{"level":3,"title":"创建mysql容器","slug":"创建mysql容器","link":"#创建mysql容器","children":[]},{"level":3,"title":"查看容器是否启动成功","slug":"查看容器是否启动成功","link":"#查看容器是否启动成功","children":[]},{"level":3,"title":"进入容器","slug":"进入容器","link":"#进入容器","children":[]},{"level":3,"title":"修改密码、创建远程账号","slug":"修改密码、创建远程账号","link":"#修改密码、创建远程账号","children":[]},{"level":3,"title":"退出,重启mysql容器","slug":"退出-重启mysql容器","link":"#退出-重启mysql容器","children":[]}]},{"level":2,"title":"Navicat远程登录","slug":"navicat远程登录","link":"#navicat远程登录","children":[{"level":3,"title":"检查防火墙","slug":"检查防火墙","link":"#检查防火墙","children":[]},{"level":3,"title":"使用创建远程连接的账号登录","slug":"使用创建远程连接的账号登录","link":"#使用创建远程连接的账号登录","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.99,"words":597},"filePathRelative":"en/tutorial/docker/2305081101.md","localizedDate":"May 6, 2023","excerpt":"

Docker 安装 Mysql5.7

\\n

本文记录Docker容器安装Mysql5.7的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2305081101.html-bbaa39a8.js b/assets/2305081101.html-9acdefa8.js similarity index 99% rename from assets/2305081101.html-bbaa39a8.js rename to assets/2305081101.html-9acdefa8.js index 80b0447d..6becb0cd 100644 --- a/assets/2305081101.html-bbaa39a8.js +++ b/assets/2305081101.html-9acdefa8.js @@ -1,4 +1,4 @@ -import{_ as s,o as n,c as e,d as t,a,b as o,e as p}from"./app-1efcbe9f.js";const r={},l=a("h1",{id:"docker-安装-mysql5-7",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker-安装-mysql5-7","aria-hidden":"true"},"#"),o(" Docker 安装 Mysql5.7")],-1),c=a("p",null,"本文记录Docker容器安装Mysql5.7的详细步骤",-1),d=p(`

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
+import{_ as s,o as n,c as e,d as t,a,b as o,e as p}from"./app-6a63891c.js";const r={},l=a("h1",{id:"docker-安装-mysql5-7",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker-安装-mysql5-7","aria-hidden":"true"},"#"),o(" Docker 安装 Mysql5.7")],-1),c=a("p",null,"本文记录Docker容器安装Mysql5.7的详细步骤",-1),d=p(`

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
 docker pull mysql:5.7
 

查看已下载镜像

docker images
 

创建mysql容器

快捷添加

# 创建一个名为mysql的容器,密码123
diff --git a/assets/2305081106.html-00656be1.js b/assets/2305081106.html-5f411d69.js
similarity index 96%
rename from assets/2305081106.html-00656be1.js
rename to assets/2305081106.html-5f411d69.js
index 9831af02..61030cd6 100644
--- a/assets/2305081106.html-00656be1.js
+++ b/assets/2305081106.html-5f411d69.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-460112e6","path":"/en/tutorial/linux/2305081106.html","title":"CentOS 8 Linux服务器防火墙常用命令","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["教程"],"tag":["linux"]},"headers":[{"level":2,"title":"开放防火墙端口3306","slug":"开放防火墙端口3306","link":"#开放防火墙端口3306","children":[]},{"level":2,"title":"重启防火墙","slug":"重启防火墙","link":"#重启防火墙","children":[]},{"level":2,"title":"关闭防火墙端口","slug":"关闭防火墙端口","link":"#关闭防火墙端口","children":[]},{"level":2,"title":"查看防火墙状态","slug":"查看防火墙状态","link":"#查看防火墙状态","children":[]},{"level":2,"title":"关闭防火墙","slug":"关闭防火墙","link":"#关闭防火墙","children":[]},{"level":2,"title":"打开防火墙","slug":"打开防火墙","link":"#打开防火墙","children":[]},{"level":2,"title":"开放一段端口","slug":"开放一段端口","link":"#开放一段端口","children":[]},{"level":2,"title":"查看开放的端口列表","slug":"查看开放的端口列表","link":"#查看开放的端口列表","children":[]},{"level":2,"title":"查看被监听(Listen)的端口","slug":"查看被监听-listen-的端口","link":"#查看被监听-listen-的端口","children":[]},{"level":2,"title":"检查端口被哪个进程占用","slug":"检查端口被哪个进程占用","link":"#检查端口被哪个进程占用","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.68,"words":203},"filePathRelative":"en/tutorial/linux/2305081106.md","localizedDate":"May 7, 2023","excerpt":"

CentOS 8 Linux服务器防火墙常用命令

\\n

CentOS 8 Linux服务器防火墙常用命令

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-460112e6","path":"/en/tutorial/linux/2305081106.html","title":"CentOS 8 Linux服务器防火墙常用命令","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["教程"],"tag":["linux"]},"headers":[{"level":2,"title":"开放防火墙端口3306","slug":"开放防火墙端口3306","link":"#开放防火墙端口3306","children":[]},{"level":2,"title":"重启防火墙","slug":"重启防火墙","link":"#重启防火墙","children":[]},{"level":2,"title":"关闭防火墙端口","slug":"关闭防火墙端口","link":"#关闭防火墙端口","children":[]},{"level":2,"title":"查看防火墙状态","slug":"查看防火墙状态","link":"#查看防火墙状态","children":[]},{"level":2,"title":"关闭防火墙","slug":"关闭防火墙","link":"#关闭防火墙","children":[]},{"level":2,"title":"打开防火墙","slug":"打开防火墙","link":"#打开防火墙","children":[]},{"level":2,"title":"开放一段端口","slug":"开放一段端口","link":"#开放一段端口","children":[]},{"level":2,"title":"查看开放的端口列表","slug":"查看开放的端口列表","link":"#查看开放的端口列表","children":[]},{"level":2,"title":"查看被监听(Listen)的端口","slug":"查看被监听-listen-的端口","link":"#查看被监听-listen-的端口","children":[]},{"level":2,"title":"检查端口被哪个进程占用","slug":"检查端口被哪个进程占用","link":"#检查端口被哪个进程占用","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.68,"words":203},"filePathRelative":"en/tutorial/linux/2305081106.md","localizedDate":"May 7, 2023","excerpt":"

CentOS 8 Linux服务器防火墙常用命令

\\n

CentOS 8 Linux服务器防火墙常用命令

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081106.html-95836fad.js b/assets/2305081106.html-83475179.js similarity index 96% rename from assets/2305081106.html-95836fad.js rename to assets/2305081106.html-83475179.js index 2d67a7e4..737d3ad4 100644 --- a/assets/2305081106.html-95836fad.js +++ b/assets/2305081106.html-83475179.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-7febf202","path":"/note/other/2305081106.html","title":"CentOS 8 Linux服务器防火墙常用命令","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["教程"],"tag":["linux"]},"headers":[{"level":2,"title":"开放防火墙端口3306","slug":"开放防火墙端口3306","link":"#开放防火墙端口3306","children":[]},{"level":2,"title":"重启防火墙","slug":"重启防火墙","link":"#重启防火墙","children":[]},{"level":2,"title":"关闭防火墙端口","slug":"关闭防火墙端口","link":"#关闭防火墙端口","children":[]},{"level":2,"title":"查看防火墙状态","slug":"查看防火墙状态","link":"#查看防火墙状态","children":[]},{"level":2,"title":"关闭防火墙","slug":"关闭防火墙","link":"#关闭防火墙","children":[]},{"level":2,"title":"打开防火墙","slug":"打开防火墙","link":"#打开防火墙","children":[]},{"level":2,"title":"开放一段端口","slug":"开放一段端口","link":"#开放一段端口","children":[]},{"level":2,"title":"查看开放的端口列表","slug":"查看开放的端口列表","link":"#查看开放的端口列表","children":[]},{"level":2,"title":"查看被监听(Listen)的端口","slug":"查看被监听-listen-的端口","link":"#查看被监听-listen-的端口","children":[]},{"level":2,"title":"检查端口被哪个进程占用","slug":"检查端口被哪个进程占用","link":"#检查端口被哪个进程占用","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.68,"words":203},"filePathRelative":"note/other/2305081106.md","localizedDate":"2023年5月7日","excerpt":"

CentOS 8 Linux服务器防火墙常用命令

\\n

CentOS 8 Linux服务器防火墙常用命令

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-7febf202","path":"/note/other/2305081106.html","title":"CentOS 8 Linux服务器防火墙常用命令","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["教程"],"tag":["linux"]},"headers":[{"level":2,"title":"开放防火墙端口3306","slug":"开放防火墙端口3306","link":"#开放防火墙端口3306","children":[]},{"level":2,"title":"重启防火墙","slug":"重启防火墙","link":"#重启防火墙","children":[]},{"level":2,"title":"关闭防火墙端口","slug":"关闭防火墙端口","link":"#关闭防火墙端口","children":[]},{"level":2,"title":"查看防火墙状态","slug":"查看防火墙状态","link":"#查看防火墙状态","children":[]},{"level":2,"title":"关闭防火墙","slug":"关闭防火墙","link":"#关闭防火墙","children":[]},{"level":2,"title":"打开防火墙","slug":"打开防火墙","link":"#打开防火墙","children":[]},{"level":2,"title":"开放一段端口","slug":"开放一段端口","link":"#开放一段端口","children":[]},{"level":2,"title":"查看开放的端口列表","slug":"查看开放的端口列表","link":"#查看开放的端口列表","children":[]},{"level":2,"title":"查看被监听(Listen)的端口","slug":"查看被监听-listen-的端口","link":"#查看被监听-listen-的端口","children":[]},{"level":2,"title":"检查端口被哪个进程占用","slug":"检查端口被哪个进程占用","link":"#检查端口被哪个进程占用","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.68,"words":203},"filePathRelative":"note/other/2305081106.md","localizedDate":"2023年5月7日","excerpt":"

CentOS 8 Linux服务器防火墙常用命令

\\n

CentOS 8 Linux服务器防火墙常用命令

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081106.html-69cd6721.js b/assets/2305081106.html-892fddb0.js similarity index 97% rename from assets/2305081106.html-69cd6721.js rename to assets/2305081106.html-892fddb0.js index 78aafaf9..805c6be5 100644 --- a/assets/2305081106.html-69cd6721.js +++ b/assets/2305081106.html-892fddb0.js @@ -1,4 +1,4 @@ -import{_ as e,o as s,c as n,d as r,a,b as t,e as o}from"./app-1efcbe9f.js";const p={},c=a("h1",{id:"centos-8-linux服务器防火墙常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#centos-8-linux服务器防火墙常用命令","aria-hidden":"true"},"#"),t(" CentOS 8 Linux服务器防火墙常用命令")],-1),l=a("p",null,"CentOS 8 Linux服务器防火墙常用命令",-1),d=o(`

查看防火墙某个端口是否开放

firewall-cmd --query-port=3306/tcp
+import{_ as e,o as s,c as n,d as r,a,b as t,e as o}from"./app-6a63891c.js";const p={},c=a("h1",{id:"centos-8-linux服务器防火墙常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#centos-8-linux服务器防火墙常用命令","aria-hidden":"true"},"#"),t(" CentOS 8 Linux服务器防火墙常用命令")],-1),l=a("p",null,"CentOS 8 Linux服务器防火墙常用命令",-1),d=o(`

查看防火墙某个端口是否开放

firewall-cmd --query-port=3306/tcp
 

开放防火墙端口3306

# 注意:开放端口后要重启防火墙生效
 firewall-cmd --zone=public --add-port=3306/tcp --permanent
 

重启防火墙

systemctl restart firewalld
diff --git a/assets/2305081106.html-72a246ba.js b/assets/2305081106.html-a7a43331.js
similarity index 97%
rename from assets/2305081106.html-72a246ba.js
rename to assets/2305081106.html-a7a43331.js
index 78aafaf9..805c6be5 100644
--- a/assets/2305081106.html-72a246ba.js
+++ b/assets/2305081106.html-a7a43331.js
@@ -1,4 +1,4 @@
-import{_ as e,o as s,c as n,d as r,a,b as t,e as o}from"./app-1efcbe9f.js";const p={},c=a("h1",{id:"centos-8-linux服务器防火墙常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#centos-8-linux服务器防火墙常用命令","aria-hidden":"true"},"#"),t(" CentOS 8 Linux服务器防火墙常用命令")],-1),l=a("p",null,"CentOS 8 Linux服务器防火墙常用命令",-1),d=o(`

查看防火墙某个端口是否开放

firewall-cmd --query-port=3306/tcp
+import{_ as e,o as s,c as n,d as r,a,b as t,e as o}from"./app-6a63891c.js";const p={},c=a("h1",{id:"centos-8-linux服务器防火墙常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#centos-8-linux服务器防火墙常用命令","aria-hidden":"true"},"#"),t(" CentOS 8 Linux服务器防火墙常用命令")],-1),l=a("p",null,"CentOS 8 Linux服务器防火墙常用命令",-1),d=o(`

查看防火墙某个端口是否开放

firewall-cmd --query-port=3306/tcp
 

开放防火墙端口3306

# 注意:开放端口后要重启防火墙生效
 firewall-cmd --zone=public --add-port=3306/tcp --permanent
 

重启防火墙

systemctl restart firewalld
diff --git a/assets/2305081110.html-8dd46319.js b/assets/2305081110.html-453f2c67.js
similarity index 99%
rename from assets/2305081110.html-8dd46319.js
rename to assets/2305081110.html-453f2c67.js
index b329a126..618a3a05 100644
--- a/assets/2305081110.html-8dd46319.js
+++ b/assets/2305081110.html-453f2c67.js
@@ -1,4 +1,4 @@
-import{_ as c,r as l,o,c as p,d as r,a,b as n,f as e,e as t}from"./app-1efcbe9f.js";const i={},u=a("h1",{id:"docker搭建nacos单机",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker搭建nacos单机","aria-hidden":"true"},"#"),n(" Docker搭建Nacos单机")],-1),d=a("p",null,"本文记录Docker容器安装Nacos单机环境的详细步骤",-1),v=t(`

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
+import{_ as c,r as l,o,c as p,d as r,a,b as n,f as e,e as t}from"./app-6a63891c.js";const i={},u=a("h1",{id:"docker搭建nacos单机",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker搭建nacos单机","aria-hidden":"true"},"#"),n(" Docker搭建Nacos单机")],-1),d=a("p",null,"本文记录Docker容器安装Nacos单机环境的详细步骤",-1),v=t(`

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
 
`,6),m={href:"http://xn--ip-mn6c22jkc30cb73c:8848/nacos",target:"_blank",rel:"noopener noreferrer"},k=t(`

三、创建nacos的挂载目录

mkdir -p /home/nacos/logs/
 mkdir -p /home/nacos/init.d/
 

四、创建nacos配置文件

vim /home/nacos/init.d/custom.properties
diff --git a/assets/2305081110.html-1afd6cb9.js b/assets/2305081110.html-a3143460.js
similarity index 93%
rename from assets/2305081110.html-1afd6cb9.js
rename to assets/2305081110.html-a3143460.js
index 3bb6960c..d61e2625 100644
--- a/assets/2305081110.html-1afd6cb9.js
+++ b/assets/2305081110.html-a3143460.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-aad5ccee","path":"/note/other/2305081110.html","title":"Docker搭建Nacos单机","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"使用 docker pull nacos/nacos-server 拉取nacos镜像","slug":"使用-docker-pull-nacos-nacos-server-拉取nacos镜像","link":"#使用-docker-pull-nacos-nacos-server-拉取nacos镜像","children":[]},{"level":2,"title":"二、使用命令启动容器","slug":"二、使用命令启动容器","link":"#二、使用命令启动容器","children":[]},{"level":2,"title":"三、创建nacos的挂载目录","slug":"三、创建nacos的挂载目录","link":"#三、创建nacos的挂载目录","children":[]},{"level":2,"title":"四、创建nacos配置文件","slug":"四、创建nacos配置文件","link":"#四、创建nacos配置文件","children":[]},{"level":2,"title":"五、在刚刚创建的配置文件中写入下面内容","slug":"五、在刚刚创建的配置文件中写入下面内容","link":"#五、在刚刚创建的配置文件中写入下面内容","children":[]},{"level":2,"title":"六、启动容器","slug":"六、启动容器","link":"#六、启动容器","children":[]},{"level":2,"title":"七、访问nacos","slug":"七、访问nacos","link":"#七、访问nacos","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.74,"words":521},"filePathRelative":"note/other/2305081110.md","localizedDate":"2023年5月6日","excerpt":"

Docker搭建Nacos单机

\\n

本文记录Docker容器安装Nacos单机环境的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-aad5ccee","path":"/note/other/2305081110.html","title":"Docker搭建Nacos单机","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"使用 docker pull nacos/nacos-server 拉取nacos镜像","slug":"使用-docker-pull-nacos-nacos-server-拉取nacos镜像","link":"#使用-docker-pull-nacos-nacos-server-拉取nacos镜像","children":[]},{"level":2,"title":"二、使用命令启动容器","slug":"二、使用命令启动容器","link":"#二、使用命令启动容器","children":[]},{"level":2,"title":"三、创建nacos的挂载目录","slug":"三、创建nacos的挂载目录","link":"#三、创建nacos的挂载目录","children":[]},{"level":2,"title":"四、创建nacos配置文件","slug":"四、创建nacos配置文件","link":"#四、创建nacos配置文件","children":[]},{"level":2,"title":"五、在刚刚创建的配置文件中写入下面内容","slug":"五、在刚刚创建的配置文件中写入下面内容","link":"#五、在刚刚创建的配置文件中写入下面内容","children":[]},{"level":2,"title":"六、启动容器","slug":"六、启动容器","link":"#六、启动容器","children":[]},{"level":2,"title":"七、访问nacos","slug":"七、访问nacos","link":"#七、访问nacos","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.74,"words":521},"filePathRelative":"note/other/2305081110.md","localizedDate":"2023年5月6日","excerpt":"

Docker搭建Nacos单机

\\n

本文记录Docker容器安装Nacos单机环境的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081110.html-dcf819da.js b/assets/2305081110.html-a3aec89e.js similarity index 96% rename from assets/2305081110.html-dcf819da.js rename to assets/2305081110.html-a3aec89e.js index aae9c74f..c161590f 100644 --- a/assets/2305081110.html-dcf819da.js +++ b/assets/2305081110.html-a3aec89e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-b41d033e","path":"/en/tutorial/docker/2305081110.html","title":"Docker搭建Nacos单机","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"使用 docker pull nacos/nacos-server 拉取nacos镜像","slug":"使用-docker-pull-nacos-nacos-server-拉取nacos镜像","link":"#使用-docker-pull-nacos-nacos-server-拉取nacos镜像","children":[]},{"level":2,"title":"二、使用命令启动容器","slug":"二、使用命令启动容器","link":"#二、使用命令启动容器","children":[]},{"level":2,"title":"三、创建nacos的挂载目录","slug":"三、创建nacos的挂载目录","link":"#三、创建nacos的挂载目录","children":[]},{"level":2,"title":"四、创建nacos配置文件","slug":"四、创建nacos配置文件","link":"#四、创建nacos配置文件","children":[]},{"level":2,"title":"五、在刚刚创建的配置文件中写入下面内容","slug":"五、在刚刚创建的配置文件中写入下面内容","link":"#五、在刚刚创建的配置文件中写入下面内容","children":[]},{"level":2,"title":"六、启动容器","slug":"六、启动容器","link":"#六、启动容器","children":[]},{"level":2,"title":"七、访问nacos","slug":"七、访问nacos","link":"#七、访问nacos","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.74,"words":521},"filePathRelative":"en/tutorial/docker/2305081110.md","localizedDate":"May 6, 2023","excerpt":"

Docker搭建Nacos单机

\\n

本文记录Docker容器安装Nacos单机环境的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-b41d033e","path":"/en/tutorial/docker/2305081110.html","title":"Docker搭建Nacos单机","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-06T00:00:00.000Z","index":true,"category":["docker"],"tag":["docker"]},"headers":[{"level":2,"title":"使用 docker pull nacos/nacos-server 拉取nacos镜像","slug":"使用-docker-pull-nacos-nacos-server-拉取nacos镜像","link":"#使用-docker-pull-nacos-nacos-server-拉取nacos镜像","children":[]},{"level":2,"title":"二、使用命令启动容器","slug":"二、使用命令启动容器","link":"#二、使用命令启动容器","children":[]},{"level":2,"title":"三、创建nacos的挂载目录","slug":"三、创建nacos的挂载目录","link":"#三、创建nacos的挂载目录","children":[]},{"level":2,"title":"四、创建nacos配置文件","slug":"四、创建nacos配置文件","link":"#四、创建nacos配置文件","children":[]},{"level":2,"title":"五、在刚刚创建的配置文件中写入下面内容","slug":"五、在刚刚创建的配置文件中写入下面内容","link":"#五、在刚刚创建的配置文件中写入下面内容","children":[]},{"level":2,"title":"六、启动容器","slug":"六、启动容器","link":"#六、启动容器","children":[]},{"level":2,"title":"七、访问nacos","slug":"七、访问nacos","link":"#七、访问nacos","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.74,"words":521},"filePathRelative":"en/tutorial/docker/2305081110.md","localizedDate":"May 6, 2023","excerpt":"

Docker搭建Nacos单机

\\n

本文记录Docker容器安装Nacos单机环境的详细步骤

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081110.html-fdddce6e.js b/assets/2305081110.html-c7821167.js similarity index 99% rename from assets/2305081110.html-fdddce6e.js rename to assets/2305081110.html-c7821167.js index b329a126..618a3a05 100644 --- a/assets/2305081110.html-fdddce6e.js +++ b/assets/2305081110.html-c7821167.js @@ -1,4 +1,4 @@ -import{_ as c,r as l,o,c as p,d as r,a,b as n,f as e,e as t}from"./app-1efcbe9f.js";const i={},u=a("h1",{id:"docker搭建nacos单机",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker搭建nacos单机","aria-hidden":"true"},"#"),n(" Docker搭建Nacos单机")],-1),d=a("p",null,"本文记录Docker容器安装Nacos单机环境的详细步骤",-1),v=t(`

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
+import{_ as c,r as l,o,c as p,d as r,a,b as n,f as e,e as t}from"./app-6a63891c.js";const i={},u=a("h1",{id:"docker搭建nacos单机",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#docker搭建nacos单机","aria-hidden":"true"},"#"),n(" Docker搭建Nacos单机")],-1),d=a("p",null,"本文记录Docker容器安装Nacos单机环境的详细步骤",-1),v=t(`

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
 
`,6),m={href:"http://xn--ip-mn6c22jkc30cb73c:8848/nacos",target:"_blank",rel:"noopener noreferrer"},k=t(`

三、创建nacos的挂载目录

mkdir -p /home/nacos/logs/
 mkdir -p /home/nacos/init.d/
 

四、创建nacos配置文件

vim /home/nacos/init.d/custom.properties
diff --git a/assets/2305081112.html-926b8bac.js b/assets/2305081112.html-17ebbfc0.js
similarity index 72%
rename from assets/2305081112.html-926b8bac.js
rename to assets/2305081112.html-17ebbfc0.js
index c4862896..49146615 100644
--- a/assets/2305081112.html-926b8bac.js
+++ b/assets/2305081112.html-17ebbfc0.js
@@ -1 +1 @@
-import{_ as o,o as n,c as a,d as c,a as e,b as t}from"./app-1efcbe9f.js";const r={},s=e("p",null,[t("Registering an OpenAI Account"),e("br"),t(" Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.")],-1),i=e("p",null,"Copying the Project",-1);function _(l,d){return n(),a("div",null,[s,c(" more "),i])}const m=o(r,[["render",_],["__file","2305081112.html.vue"]]);export{m as default};
+import{_ as o,o as n,c as a,d as c,a as e,b as t}from"./app-6a63891c.js";const r={},s=e("p",null,[t("Registering an OpenAI Account"),e("br"),t(" Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.")],-1),i=e("p",null,"Copying the Project",-1);function _(l,d){return n(),a("div",null,[s,c(" more "),i])}const m=o(r,[["render",_],["__file","2305081112.html.vue"]]);export{m as default};
diff --git a/assets/2305081112.html-dab042fb.js b/assets/2305081112.html-27835a6c.js
similarity index 80%
rename from assets/2305081112.html-dab042fb.js
rename to assets/2305081112.html-27835a6c.js
index 7f6a2e26..05ec3e86 100644
--- a/assets/2305081112.html-dab042fb.js
+++ b/assets/2305081112.html-27835a6c.js
@@ -1 +1 @@
-import{_ as t,o as r,c as o,d as n,a as e,b as a}from"./app-1efcbe9f.js";const c={},s=e("h1",{id:"注册openai账号",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#注册openai账号","aria-hidden":"true"},"#"),a(" 注册OpenAI账号")],-1),i=e("p",null,"Siri ChatGPT 使用教程,将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话",-1),d=e("h2",{id:"拷贝项目",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#拷贝项目","aria-hidden":"true"},"#"),a(" 拷贝项目")],-1);function _(h,l){return r(),o("div",null,[s,i,n(" more "),d])}const m=t(c,[["render",_],["__file","2305081112.html.vue"]]);export{m as default};
+import{_ as t,o as r,c as o,d as n,a as e,b as a}from"./app-6a63891c.js";const c={},s=e("h1",{id:"注册openai账号",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#注册openai账号","aria-hidden":"true"},"#"),a(" 注册OpenAI账号")],-1),i=e("p",null,"Siri ChatGPT 使用教程,将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话",-1),d=e("h2",{id:"拷贝项目",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#拷贝项目","aria-hidden":"true"},"#"),a(" 拷贝项目")],-1);function _(h,l){return r(),o("div",null,[s,i,n(" more "),d])}const m=t(c,[["render",_],["__file","2305081112.html.vue"]]);export{m as default};
diff --git a/assets/2305081112.html-9eeae295.js b/assets/2305081112.html-2c2bf6c6.js
similarity index 91%
rename from assets/2305081112.html-9eeae295.js
rename to assets/2305081112.html-2c2bf6c6.js
index fbdbe84e..00ea31be 100644
--- a/assets/2305081112.html-9eeae295.js
+++ b/assets/2305081112.html-2c2bf6c6.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-6494b51e","path":"/en/tutorial/openai/2305081112.html","title":"","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["人工智能"],"tag":["OpenAI","ChatGPT"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.14,"words":41},"filePathRelative":"en/tutorial/openai/2305081112.md","localizedDate":"May 7, 2023","excerpt":"

Registering an OpenAI Account
\\nTutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-6494b51e","path":"/en/tutorial/openai/2305081112.html","title":"","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["人工智能"],"tag":["OpenAI","ChatGPT"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.14,"words":41},"filePathRelative":"en/tutorial/openai/2305081112.md","localizedDate":"May 7, 2023","excerpt":"

Registering an OpenAI Account
\\nTutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305081112.html-a670d54e.js b/assets/2305081112.html-eeb2f89f.js similarity index 92% rename from assets/2305081112.html-a670d54e.js rename to assets/2305081112.html-eeb2f89f.js index 35158517..1df90b9a 100644 --- a/assets/2305081112.html-a670d54e.js +++ b/assets/2305081112.html-eeb2f89f.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-a4026a72","path":"/note/other/2305081112.html","title":"注册OpenAI账号","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["人工智能"],"tag":["OpenAI","ChatGPT"]},"headers":[{"level":2,"title":"拷贝项目","slug":"拷贝项目","link":"#拷贝项目","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"note/other/2305081112.md","localizedDate":"2023年5月7日","excerpt":"

注册OpenAI账号

\\n

Siri ChatGPT 使用教程,将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-a4026a72","path":"/note/other/2305081112.html","title":"注册OpenAI账号","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["人工智能"],"tag":["OpenAI","ChatGPT"]},"headers":[{"level":2,"title":"拷贝项目","slug":"拷贝项目","link":"#拷贝项目","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"note/other/2305081112.md","localizedDate":"2023年5月7日","excerpt":"

注册OpenAI账号

\\n

Siri ChatGPT 使用教程,将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305090929.html-da8025a5.js b/assets/2305090929.html-8dbecdea.js similarity index 99% rename from assets/2305090929.html-da8025a5.js rename to assets/2305090929.html-8dbecdea.js index 46d30ebc..52c31b35 100644 --- a/assets/2305090929.html-da8025a5.js +++ b/assets/2305090929.html-8dbecdea.js @@ -1,4 +1,4 @@ -import{_ as a,o as e,c as p,d as t,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"跳表-skiplist",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#跳表-skiplist","aria-hidden":"true"},"#"),s(" 跳表(SkipList)")],-1),i=n("p",null,"跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。",-1),u=n("p",null,[s("对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写"),n("br"),s(" 呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。")],-1),k=o(`

快速了解跳表

跳跃表(简称跳表)由美国计算机科学家William Pugh发明于1989年。他在论文《Skip lists: a probabilistic alternative to balanced
trees》中详细介绍了跳表的数据结构和插入删除等操作。

跳表(SkipList,全称跳跃表)

是用于有序元素序列快速搜索查找的一个数据结构,跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它在性能上和红黑树,AVL树不相上下,但是跳表的原理非常简单,实现也比红黑树简单很多。

在这里你可以看到一些关键词:链表(有序链表)、索引、二分查找。想必你的脑海中已经有了一个初略的印象,不过你可能还是不清楚这个"会跳的链表"有多diao,甚至还可能会产生一点疑虑:跟随机化有什么关系?你在下文中很快就能得到答案!

回顾链表,我们知道链表和顺序表(数组)通常都是相爱相杀,成对出现,各有优劣。而链表的优势就是更高效的插入、删除。痛点就是查询很慢很慢!每次查询都是一种O(n)复杂度的操作,链表估计自己都气的想哭了 。

这是一个带头结点的链表(头结点相当于一个固定的入口,不存储有意义的值)
,每次查找都需要一个个枚举,相当的慢,我们能不能稍微优化一下,让它稍微跳一跳呢?答案是可以的,我们知道很多算法和数据结构以空间换时间,我们在上面加一层索引,让部分节点在上层能够直接定位到,这样链表的查询时间近乎减少一半,链表自己虽然没有开心起来,但收起了它想哭的脸。

这样,在查询某个节点的时候,首先会从上一层快速定位节点所在的一个范围,如果找到具体范围向下然后查找代价很小,当然在表的结构设计上会增加一个向下的索引(指针)用来查找确定底层节点。平均查找速度平均为O(n/2)
。但是当节点数量很大的时候,它依旧很慢很慢。我们都知道二分查找是每次都能折半的去压缩查找范围,要是有序链表也能这么跳起来那就太完美了。没错跳表就能让链表拥有近乎的接近二分查找的效率的一种数据结构,其原理依然是给上面加若干层索引,优化查找速度。

通过上图你可以看到,通过这样的一个数据结构对有序链表进行查找都能近乎二分的性能。就是在上面维护那么多层的索引,首先在最高级索引上查找最后一个小于当前查找元素的位置,然后再跳到次高级索引继续查找,直到跳到最底层为止,这时候以及十分接近要查找的元素的位置了(

如果查找元素存在的话)。由于根据索引可以一次跳过多个元素,所以跳查找的查找速度也就变快了。

对于理想的跳表,每向上一层索引节点数量都是下一层的1/2.那么如果n个节点增加的节点数量(1/2+1/4+…)<n。
并且层数较低,对查找效果影响不大。但是对于这么一个结构,你可能会疑惑,这样完美的结构真的存在吗?大概率不存在的,因为作为一个链表,少不了增删该查的一些操作。而删除和插入可能会改变整个结构,所以上面的这些都是理想的结构,在插入的时候是否添加上层索引是个概率问题(
1/2的概率),在后面会具体讲解。

跳表操作

上面稍微了解了跳表是个啥,那么在这里就给大家谈谈跳表的增删改查过程。在实现本跳表的过程为了便于操作,我们将跳表的头结点(head)的key设为int的最小值(一定满足左小右大方便比较)。

对于每个节点的设置,设置成SkipNode类,为了防止初学者将next向下还是向右搞混,直接设置right,down两个指针

public class SkipNode<T> {
+import{_ as a,o as e,c as p,d as t,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"跳表-skiplist",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#跳表-skiplist","aria-hidden":"true"},"#"),s(" 跳表(SkipList)")],-1),i=n("p",null,"跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。",-1),u=n("p",null,[s("对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写"),n("br"),s(" 呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。")],-1),k=o(`

快速了解跳表

跳跃表(简称跳表)由美国计算机科学家William Pugh发明于1989年。他在论文《Skip lists: a probabilistic alternative to balanced
trees》中详细介绍了跳表的数据结构和插入删除等操作。

跳表(SkipList,全称跳跃表)

是用于有序元素序列快速搜索查找的一个数据结构,跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它在性能上和红黑树,AVL树不相上下,但是跳表的原理非常简单,实现也比红黑树简单很多。

在这里你可以看到一些关键词:链表(有序链表)、索引、二分查找。想必你的脑海中已经有了一个初略的印象,不过你可能还是不清楚这个"会跳的链表"有多diao,甚至还可能会产生一点疑虑:跟随机化有什么关系?你在下文中很快就能得到答案!

回顾链表,我们知道链表和顺序表(数组)通常都是相爱相杀,成对出现,各有优劣。而链表的优势就是更高效的插入、删除。痛点就是查询很慢很慢!每次查询都是一种O(n)复杂度的操作,链表估计自己都气的想哭了 。

这是一个带头结点的链表(头结点相当于一个固定的入口,不存储有意义的值)
,每次查找都需要一个个枚举,相当的慢,我们能不能稍微优化一下,让它稍微跳一跳呢?答案是可以的,我们知道很多算法和数据结构以空间换时间,我们在上面加一层索引,让部分节点在上层能够直接定位到,这样链表的查询时间近乎减少一半,链表自己虽然没有开心起来,但收起了它想哭的脸。

这样,在查询某个节点的时候,首先会从上一层快速定位节点所在的一个范围,如果找到具体范围向下然后查找代价很小,当然在表的结构设计上会增加一个向下的索引(指针)用来查找确定底层节点。平均查找速度平均为O(n/2)
。但是当节点数量很大的时候,它依旧很慢很慢。我们都知道二分查找是每次都能折半的去压缩查找范围,要是有序链表也能这么跳起来那就太完美了。没错跳表就能让链表拥有近乎的接近二分查找的效率的一种数据结构,其原理依然是给上面加若干层索引,优化查找速度。

通过上图你可以看到,通过这样的一个数据结构对有序链表进行查找都能近乎二分的性能。就是在上面维护那么多层的索引,首先在最高级索引上查找最后一个小于当前查找元素的位置,然后再跳到次高级索引继续查找,直到跳到最底层为止,这时候以及十分接近要查找的元素的位置了(

如果查找元素存在的话)。由于根据索引可以一次跳过多个元素,所以跳查找的查找速度也就变快了。

对于理想的跳表,每向上一层索引节点数量都是下一层的1/2.那么如果n个节点增加的节点数量(1/2+1/4+…)<n。
并且层数较低,对查找效果影响不大。但是对于这么一个结构,你可能会疑惑,这样完美的结构真的存在吗?大概率不存在的,因为作为一个链表,少不了增删该查的一些操作。而删除和插入可能会改变整个结构,所以上面的这些都是理想的结构,在插入的时候是否添加上层索引是个概率问题(
1/2的概率),在后面会具体讲解。

跳表操作

上面稍微了解了跳表是个啥,那么在这里就给大家谈谈跳表的增删改查过程。在实现本跳表的过程为了便于操作,我们将跳表的头结点(head)的key设为int的最小值(一定满足左小右大方便比较)。

对于每个节点的设置,设置成SkipNode类,为了防止初学者将next向下还是向右搞混,直接设置right,down两个指针

public class SkipNode<T> {
 
     int key;
 
diff --git a/assets/2305090929.html-4bab3b53.js b/assets/2305090929.html-da6d9f65.js
similarity index 96%
rename from assets/2305090929.html-4bab3b53.js
rename to assets/2305090929.html-da6d9f65.js
index bccb4c47..1570d3bf 100644
--- a/assets/2305090929.html-4bab3b53.js
+++ b/assets/2305090929.html-da6d9f65.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0115d5da","path":"/note/structure/2305090929.html","title":"跳表(SkipList)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-02-23T00:00:00.000Z","index":true,"category":["数据结构"],"tag":["跳表"]},"headers":[{"level":2,"title":"快速了解跳表","slug":"快速了解跳表","link":"#快速了解跳表","children":[]},{"level":2,"title":"跳表操作","slug":"跳表操作","link":"#跳表操作","children":[{"level":3,"title":"查询操作","slug":"查询操作","link":"#查询操作","children":[]},{"level":3,"title":"删除操作","slug":"删除操作","link":"#删除操作","children":[]},{"level":3,"title":"插入操作","slug":"插入操作","link":"#插入操作","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":14.33,"words":4298},"filePathRelative":"note/structure/2305090929.md","localizedDate":"2023年2月23日","excerpt":"

跳表(SkipList)

\\n

跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

\\n

对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写
\\n呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0115d5da","path":"/note/structure/2305090929.html","title":"跳表(SkipList)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-02-23T00:00:00.000Z","index":true,"category":["数据结构"],"tag":["跳表"]},"headers":[{"level":2,"title":"快速了解跳表","slug":"快速了解跳表","link":"#快速了解跳表","children":[]},{"level":2,"title":"跳表操作","slug":"跳表操作","link":"#跳表操作","children":[{"level":3,"title":"查询操作","slug":"查询操作","link":"#查询操作","children":[]},{"level":3,"title":"删除操作","slug":"删除操作","link":"#删除操作","children":[]},{"level":3,"title":"插入操作","slug":"插入操作","link":"#插入操作","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":14.33,"words":4298},"filePathRelative":"note/structure/2305090929.md","localizedDate":"2023年2月23日","excerpt":"

跳表(SkipList)

\\n

跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

\\n

对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写
\\n呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305090935.html-5753cc3a.js b/assets/2305090935.html-5350ce3a.js similarity index 99% rename from assets/2305090935.html-5753cc3a.js rename to assets/2305090935.html-5350ce3a.js index bca9bce2..b2ef4fdb 100644 --- a/assets/2305090935.html-5753cc3a.js +++ b/assets/2305090935.html-5350ce3a.js @@ -1,4 +1,4 @@ -import{_ as a,o as e,c as t,d as p,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},i=n("h1",{id:"ddd-领域驱动设计模型",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ddd-领域驱动设计模型","aria-hidden":"true"},"#"),s(" DDD 领域驱动设计模型")],-1),l=n("p",null,[s("DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言("),n("br"),s(" Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。")],-1),u=o(`

开发目标

依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。1、拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月2、架构出高可用极易符合互联网高速迭代的应用服务3、物料化、组装化、可编排的服务,提高人效

服务架构

  • 应用层{application}
    • 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
    • 应用层的服务包括应用服务和领域事件相关服务。
    • 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
    • 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
  • 领域层{domain}
    • 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
    • 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
    • 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
    • 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
  • 基础层{infrastructrue}
    • 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
    • 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
  • 接口层{interfaces}
    • 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。

开发环境

  • jdk1.8【jdk1.7以下只能部分支持netty】
  • springboot 2.0.6.RELEASE
  • idea + maven

代码结构示例

itstack-demo-ddd-01
+import{_ as a,o as e,c as t,d as p,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},i=n("h1",{id:"ddd-领域驱动设计模型",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ddd-领域驱动设计模型","aria-hidden":"true"},"#"),s(" DDD 领域驱动设计模型")],-1),l=n("p",null,[s("DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言("),n("br"),s(" Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。")],-1),u=o(`

开发目标

依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。1、拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月2、架构出高可用极易符合互联网高速迭代的应用服务3、物料化、组装化、可编排的服务,提高人效

服务架构

  • 应用层{application}
    • 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
    • 应用层的服务包括应用服务和领域事件相关服务。
    • 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
    • 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
  • 领域层{domain}
    • 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
    • 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
    • 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
    • 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
  • 基础层{infrastructrue}
    • 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
    • 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
  • 接口层{interfaces}
    • 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。

开发环境

  • jdk1.8【jdk1.7以下只能部分支持netty】
  • springboot 2.0.6.RELEASE
  • idea + maven

代码结构示例

itstack-demo-ddd-01
 └── src
     ├── main
     │   ├── java
diff --git a/assets/2305090935.html-3b5537e3.js b/assets/2305090935.html-639f15d9.js
similarity index 92%
rename from assets/2305090935.html-3b5537e3.js
rename to assets/2305090935.html-639f15d9.js
index 37116136..fef11297 100644
--- a/assets/2305090935.html-3b5537e3.js
+++ b/assets/2305090935.html-639f15d9.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-ed1de9c6","path":"/informal/2305090935.html","title":"DDD 领域驱动设计模型","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["随笔","DDD"],"tag":["DDD"]},"headers":[{"level":2,"title":"开发目标","slug":"开发目标","link":"#开发目标","children":[{"level":3,"title":"服务架构","slug":"服务架构","link":"#服务架构","children":[]}]},{"level":2,"title":"开发环境","slug":"开发环境","link":"#开发环境","children":[]},{"level":2,"title":"代码结构示例","slug":"代码结构示例","link":"#代码结构示例","children":[{"level":3,"title":"部分重点代码","slug":"部分重点代码","link":"#部分重点代码","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.1,"words":1529},"filePathRelative":"informal/2305090935.md","localizedDate":"2023年5月7日","excerpt":"

DDD 领域驱动设计模型

\\n

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
\\nUbiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-ed1de9c6","path":"/informal/2305090935.html","title":"DDD 领域驱动设计模型","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["随笔","DDD"],"tag":["DDD"]},"headers":[{"level":2,"title":"开发目标","slug":"开发目标","link":"#开发目标","children":[{"level":3,"title":"服务架构","slug":"服务架构","link":"#服务架构","children":[]}]},{"level":2,"title":"开发环境","slug":"开发环境","link":"#开发环境","children":[]},{"level":2,"title":"代码结构示例","slug":"代码结构示例","link":"#代码结构示例","children":[{"level":3,"title":"部分重点代码","slug":"部分重点代码","link":"#部分重点代码","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.1,"words":1529},"filePathRelative":"informal/2305090935.md","localizedDate":"2023年5月7日","excerpt":"

DDD 领域驱动设计模型

\\n

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
\\nUbiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305090946.html-c353d76f.js b/assets/2305090946.html-51196ee1.js similarity index 94% rename from assets/2305090946.html-c353d76f.js rename to assets/2305090946.html-51196ee1.js index e4eca902..cb564f7b 100644 --- a/assets/2305090946.html-c353d76f.js +++ b/assets/2305090946.html-51196ee1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-0a4a23d6","path":"/en/tutorial/util/2305090946.html","title":"Compilation of commonly used plugins for IDEA, continuously updated.","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-09T00:00:00.000Z","index":true,"category":["工具"],"tag":["idea"]},"headers":[{"level":2,"title":"Lombok","slug":"lombok","link":"#lombok","children":[]},{"level":2,"title":"Features","slug":"features","link":"#features","children":[]},{"level":2,"title":"google-java-format","slug":"google-java-format","link":"#google-java-format","children":[]},{"level":2,"title":"Translation","slug":"translation","link":"#translation","children":[]},{"level":2,"title":"Alibaba Java Coding Guidelines","slug":"alibaba-java-coding-guidelines","link":"#alibaba-java-coding-guidelines","children":[]},{"level":2,"title":"Leetcode Editor","slug":"leetcode-editor","link":"#leetcode-editor","children":[]},{"level":2,"title":"Jclasslib Bytecode Viewer","slug":"jclasslib-bytecode-viewer","link":"#jclasslib-bytecode-viewer","children":[]},{"level":2,"title":"CamelCase","slug":"camelcase","link":"#camelcase","children":[]},{"level":2,"title":"CamelCase: First word lowercase, subsequent words uppercase.","slug":"camelcase-first-word-lowercase-subsequent-words-uppercase","link":"#camelcase-first-word-lowercase-subsequent-words-uppercase","children":[]},{"level":2,"title":"Free Mybatis Plugin","slug":"free-mybatis-plugin","link":"#free-mybatis-plugin","children":[]},{"level":2,"title":"Auto Filling Java Call Arguments","slug":"auto-filling-java-call-arguments","link":"#auto-filling-java-call-arguments","children":[]},{"level":2,"title":"FindBugs","slug":"findbugs","link":"#findbugs","children":[]},{"level":2,"title":"SequenceDiagram","slug":"sequencediagram","link":"#sequencediagram","children":[]},{"level":2,"title":"Codota","slug":"codota","link":"#codota","children":[]},{"level":2,"title":"Maven Helper","slug":"maven-helper","link":"#maven-helper","children":[]},{"level":2,"title":"JRebel","slug":"jrebel","link":"#jrebel","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.4,"words":421},"filePathRelative":"en/tutorial/util/2305090946.md","localizedDate":"May 9, 2023","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0a4a23d6","path":"/en/tutorial/util/2305090946.html","title":"Compilation of commonly used plugins for IDEA, continuously updated.","lang":"en-US","frontmatter":{"isOriginal":true,"date":"2023-05-09T00:00:00.000Z","index":true,"category":["工具"],"tag":["idea"]},"headers":[{"level":2,"title":"Lombok","slug":"lombok","link":"#lombok","children":[]},{"level":2,"title":"Features","slug":"features","link":"#features","children":[]},{"level":2,"title":"google-java-format","slug":"google-java-format","link":"#google-java-format","children":[]},{"level":2,"title":"Translation","slug":"translation","link":"#translation","children":[]},{"level":2,"title":"Alibaba Java Coding Guidelines","slug":"alibaba-java-coding-guidelines","link":"#alibaba-java-coding-guidelines","children":[]},{"level":2,"title":"Leetcode Editor","slug":"leetcode-editor","link":"#leetcode-editor","children":[]},{"level":2,"title":"Jclasslib Bytecode Viewer","slug":"jclasslib-bytecode-viewer","link":"#jclasslib-bytecode-viewer","children":[]},{"level":2,"title":"CamelCase","slug":"camelcase","link":"#camelcase","children":[]},{"level":2,"title":"CamelCase: First word lowercase, subsequent words uppercase.","slug":"camelcase-first-word-lowercase-subsequent-words-uppercase","link":"#camelcase-first-word-lowercase-subsequent-words-uppercase","children":[]},{"level":2,"title":"Free Mybatis Plugin","slug":"free-mybatis-plugin","link":"#free-mybatis-plugin","children":[]},{"level":2,"title":"Auto Filling Java Call Arguments","slug":"auto-filling-java-call-arguments","link":"#auto-filling-java-call-arguments","children":[]},{"level":2,"title":"FindBugs","slug":"findbugs","link":"#findbugs","children":[]},{"level":2,"title":"SequenceDiagram","slug":"sequencediagram","link":"#sequencediagram","children":[]},{"level":2,"title":"Codota","slug":"codota","link":"#codota","children":[]},{"level":2,"title":"Maven Helper","slug":"maven-helper","link":"#maven-helper","children":[]},{"level":2,"title":"JRebel","slug":"jrebel","link":"#jrebel","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.4,"words":421},"filePathRelative":"en/tutorial/util/2305090946.md","localizedDate":"May 9, 2023","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305090946.html-5beac4ec.js b/assets/2305090946.html-92feef10.js similarity index 98% rename from assets/2305090946.html-5beac4ec.js rename to assets/2305090946.html-92feef10.js index 5057eb29..67a3fa2c 100644 --- a/assets/2305090946.html-5beac4ec.js +++ b/assets/2305090946.html-92feef10.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,e as t}from"./app-1efcbe9f.js";const i={},n=t('

Compilation of commonly used plugins for IDEA, continuously updated.

Lombok

Introduction: Automatically generates getters, setters, and toString methods.

You only need to define class properties, let Lombok handle the rest.

Features

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor, and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val and @var experimental
@var
@UtilityClass

Usage: Apply the aforementioned annotations to your classes.

google-java-format

Introduction: The google-java-format plugin allows automatic code formatting without using specific shortcuts.

Translation

Introduction: Translation plugin supporting Google Translate, Baidu Translate, and Youdao Translate. Google Translate is recommended.

Alibaba Java Coding Guidelines

Introduction: Alibaba code style checker. Non-compliant code sections are highlighted with wave underlines, and corresponding suggestions are displayed when hovering the mouse. Some issues can even be quickly fixed.

Leetcode Editor

Introduction: LeetCode plugin for solving coding problems within IDEA. It's truly convenient for sneaking in some algorithmic exercises while appearing to be working diligently.

You can also visit the official LeetCode website.

Jclasslib Bytecode Viewer

Introduction: Viewing the bytecode files of classes.

CamelCase

Introduction: Switching between several string formats. The following formats are supported:

CamelCase: First word lowercase, subsequent words uppercase.

All lowercase with underscore between words.
All lowercase with space between words.
All lowercase with hyphen between words.
Each word with initial uppercase.
All uppercase with underscore between words.

Usage: Hold Shift + Alt and press U repeatedly to convert the selected content's words between underscore, camel case, and uppercase, until the desired format is achieved.

Free Mybatis Plugin

Introduction: Allows navigation from methods in mapper interfaces to corresponding mapper.xml files.

Auto Filling Java Call Arguments

Introduction: Automatic filling of function arguments. When calling pre-defined functions that require parameter input, it is often the case that the variable names align with the parameter names. Manually filling in individual parameters can be time-consuming, especially with a large number of parameters. This plugin solves this problem.

FindBugs

Introduction: Static code analysis tool. It detects potential issues in your code and provides explanations.

SequenceDiagram

Introduction: Generates sequence diagrams based on the call chain, greatly assisting in viewing class invocations and source code.

Codota

Introduction: Code completion plugin. Competes with IDEA's code suggestion feature and allows searching for third-party usage of a particular function.

Maven Helper

Introduction: Maven dependency management.

JRebel

Introduction: Hot deployment plugin. For detailed instructions, refer to: link to tutorial

',37),o=[n];function s(d,l){return a(),r("div",null,o)}const h=e(i,[["render",s],["__file","2305090946.html.vue"]]);export{h as default}; +import{_ as e,o as a,c as r,e as t}from"./app-6a63891c.js";const i={},n=t('

Compilation of commonly used plugins for IDEA, continuously updated.

Lombok

Introduction: Automatically generates getters, setters, and toString methods.

You only need to define class properties, let Lombok handle the rest.

Features

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor, and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val and @var experimental
@var
@UtilityClass

Usage: Apply the aforementioned annotations to your classes.

google-java-format

Introduction: The google-java-format plugin allows automatic code formatting without using specific shortcuts.

Translation

Introduction: Translation plugin supporting Google Translate, Baidu Translate, and Youdao Translate. Google Translate is recommended.

Alibaba Java Coding Guidelines

Introduction: Alibaba code style checker. Non-compliant code sections are highlighted with wave underlines, and corresponding suggestions are displayed when hovering the mouse. Some issues can even be quickly fixed.

Leetcode Editor

Introduction: LeetCode plugin for solving coding problems within IDEA. It's truly convenient for sneaking in some algorithmic exercises while appearing to be working diligently.

You can also visit the official LeetCode website.

Jclasslib Bytecode Viewer

Introduction: Viewing the bytecode files of classes.

CamelCase

Introduction: Switching between several string formats. The following formats are supported:

CamelCase: First word lowercase, subsequent words uppercase.

All lowercase with underscore between words.
All lowercase with space between words.
All lowercase with hyphen between words.
Each word with initial uppercase.
All uppercase with underscore between words.

Usage: Hold Shift + Alt and press U repeatedly to convert the selected content's words between underscore, camel case, and uppercase, until the desired format is achieved.

Free Mybatis Plugin

Introduction: Allows navigation from methods in mapper interfaces to corresponding mapper.xml files.

Auto Filling Java Call Arguments

Introduction: Automatic filling of function arguments. When calling pre-defined functions that require parameter input, it is often the case that the variable names align with the parameter names. Manually filling in individual parameters can be time-consuming, especially with a large number of parameters. This plugin solves this problem.

FindBugs

Introduction: Static code analysis tool. It detects potential issues in your code and provides explanations.

SequenceDiagram

Introduction: Generates sequence diagrams based on the call chain, greatly assisting in viewing class invocations and source code.

Codota

Introduction: Code completion plugin. Competes with IDEA's code suggestion feature and allows searching for third-party usage of a particular function.

Maven Helper

Introduction: Maven dependency management.

JRebel

Introduction: Hot deployment plugin. For detailed instructions, refer to: link to tutorial

',37),o=[n];function s(d,l){return a(),r("div",null,o)}const h=e(i,[["render",s],["__file","2305090946.html.vue"]]);export{h as default}; diff --git a/assets/2305090946.html-d1767897.js b/assets/2305090946.html-c7fed4f9.js similarity index 97% rename from assets/2305090946.html-d1767897.js rename to assets/2305090946.html-c7fed4f9.js index 8e73086a..4f450844 100644 --- a/assets/2305090946.html-d1767897.js +++ b/assets/2305090946.html-c7fed4f9.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1ad96fc8","path":"/note/other/2305090946.html","title":"IDEA 常用插件","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-09T00:00:00.000Z","index":true,"category":["工具"],"tag":["idea"]},"headers":[{"level":2,"title":"Lombok","slug":"lombok","link":"#lombok","children":[{"level":3,"title":"Features","slug":"features","link":"#features","children":[]}]},{"level":2,"title":"google-java-format","slug":"google-java-format","link":"#google-java-format","children":[]},{"level":2,"title":"Translation","slug":"translation","link":"#translation","children":[]},{"level":2,"title":"Alibaba Java Coding Guidelines","slug":"alibaba-java-coding-guidelines","link":"#alibaba-java-coding-guidelines","children":[]},{"level":2,"title":"Leetcode Editor","slug":"leetcode-editor","link":"#leetcode-editor","children":[]},{"level":2,"title":"Jclasslib Bytecode Viewer","slug":"jclasslib-bytecode-viewer","link":"#jclasslib-bytecode-viewer","children":[]},{"level":2,"title":"CamelCase","slug":"camelcase","link":"#camelcase","children":[]},{"level":2,"title":"Free Mybatis Plugin","slug":"free-mybatis-plugin","link":"#free-mybatis-plugin","children":[]},{"level":2,"title":"Auto filling Java call arguments","slug":"auto-filling-java-call-arguments","link":"#auto-filling-java-call-arguments","children":[]},{"level":2,"title":"FindBugs","slug":"findbugs","link":"#findbugs","children":[]},{"level":2,"title":"SequenceDiagram","slug":"sequencediagram","link":"#sequencediagram","children":[]},{"level":2,"title":"Codota","slug":"codota","link":"#codota","children":[]},{"level":2,"title":"Maven Helper","slug":"maven-helper","link":"#maven-helper","children":[]},{"level":2,"title":"JRebel","slug":"jrebel","link":"#jrebel","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.24,"words":671},"filePathRelative":"note/other/2305090946.md","localizedDate":"2023年5月9日","excerpt":"

IDEA 常用插件

\\n

IDEA 开发常用插件汇总,持续更新

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1ad96fc8","path":"/note/other/2305090946.html","title":"IDEA 常用插件","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-09T00:00:00.000Z","index":true,"category":["工具"],"tag":["idea"]},"headers":[{"level":2,"title":"Lombok","slug":"lombok","link":"#lombok","children":[{"level":3,"title":"Features","slug":"features","link":"#features","children":[]}]},{"level":2,"title":"google-java-format","slug":"google-java-format","link":"#google-java-format","children":[]},{"level":2,"title":"Translation","slug":"translation","link":"#translation","children":[]},{"level":2,"title":"Alibaba Java Coding Guidelines","slug":"alibaba-java-coding-guidelines","link":"#alibaba-java-coding-guidelines","children":[]},{"level":2,"title":"Leetcode Editor","slug":"leetcode-editor","link":"#leetcode-editor","children":[]},{"level":2,"title":"Jclasslib Bytecode Viewer","slug":"jclasslib-bytecode-viewer","link":"#jclasslib-bytecode-viewer","children":[]},{"level":2,"title":"CamelCase","slug":"camelcase","link":"#camelcase","children":[]},{"level":2,"title":"Free Mybatis Plugin","slug":"free-mybatis-plugin","link":"#free-mybatis-plugin","children":[]},{"level":2,"title":"Auto filling Java call arguments","slug":"auto-filling-java-call-arguments","link":"#auto-filling-java-call-arguments","children":[]},{"level":2,"title":"FindBugs","slug":"findbugs","link":"#findbugs","children":[]},{"level":2,"title":"SequenceDiagram","slug":"sequencediagram","link":"#sequencediagram","children":[]},{"level":2,"title":"Codota","slug":"codota","link":"#codota","children":[]},{"level":2,"title":"Maven Helper","slug":"maven-helper","link":"#maven-helper","children":[]},{"level":2,"title":"JRebel","slug":"jrebel","link":"#jrebel","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.24,"words":671},"filePathRelative":"note/other/2305090946.md","localizedDate":"2023年5月9日","excerpt":"

IDEA 常用插件

\\n

IDEA 开发常用插件汇总,持续更新

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305090946.html-4384696a.js b/assets/2305090946.html-ffd4afb2.js similarity index 98% rename from assets/2305090946.html-4384696a.js rename to assets/2305090946.html-ffd4afb2.js index 2267739c..09292993 100644 --- a/assets/2305090946.html-4384696a.js +++ b/assets/2305090946.html-ffd4afb2.js @@ -1 +1 @@ -import{_ as i,r as t,o,c as d,d as n,a as e,b as a,f as l,e as h}from"./app-1efcbe9f.js";const s={},c=e("h1",{id:"idea-常用插件",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea-常用插件","aria-hidden":"true"},"#"),a(" IDEA 常用插件")],-1),u=e("p",null,"IDEA 开发常用插件汇总,持续更新",-1),p=h('

Lombok

简介:自动get/set/toString等。

你只需要写好类属性,其他的交给Lombok

Features

@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and
@NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder
@SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var
@UtilityClass

使用方法:将上面的注解写到类上面即可

google-java-format

简介:google-java-format插件可以帮助我们不通过对应的快捷键就可以实现特定方式下自动格式化代码。

Translation

简介:翻译插件,支持google翻译、百度翻译、有道翻译。推荐使用谷歌翻译

Alibaba Java Coding Guidelines

简介:阿里巴巴代码规范检测。不符合代码规范的地方会有波浪线,鼠标移上去就会有相应的提示,有些问题甚至可以快速修复

Leetcode Editor

简介:LeetCode插件,可以在IDEA中在线刷题。上班摸鱼属实方便,表面上我在干活,实际上我在刷算法题。

也可前往 LeetCode 官网;

Jclasslib Bytecode Viewer

简介:看类的字节码文件

CamelCase

简介:在几种字符串格式之间来回切换。有一下几种格式:

  • 驼峰,第一个单词首字母小写,其他单词首字母大写
  • 所有字母小写,单词间下划线分隔
  • 所有字母小写,单词间空格分隔
  • 所有字母小写,单词间短横线分隔
  • 每个单词首字母全部大写
  • 所有字母大写,单词间下划线分隔

使用:按住Shift + Alt再不停的按U,会把选中内容的单词的下划线转驼峰转大写等,不停的转换,直到你想要的。

Free Mybatis Plugin

简介:可以通过mapper接口里的方法跳转到mapper.xml里。

Auto filling Java call arguments

简介:函数参数自动填充。已经编写好的函数,调用后需要填充参数,但是绝大多数情况下,传入的变量名称和该函数的参数名一致,当参数较多时,手动单个填充参数非常浪费时间。该插件就可以帮你解决这个问题。

FindBugs

简介:静态代码检查。可以检查你代码中的隐患,并给出原因。

SequenceDiagram

简介:根据调用链路生成时序图,对查看类调用及查看源码帮助很大;

Codota

简介:代码补全插件。争抢IDEA的代码提示功能,可以搜索第三方对该函数的使用方法

Maven Helper

简介:maven 依赖管理。

JRebel

',34),g={href:"https://www.jianshu.com/p/52e698c57131",target:"_blank",rel:"noopener noreferrer"};function b(m,f){const r=t("ExternalLinkIcon");return o(),d("div",null,[c,u,n(" more "),p,e("p",null,[a("简介:热部署插件 详情教程:"),e("a",g,[a("https://www.jianshu.com/p/52e698c57131"),l(r)])])])}const _=i(s,[["render",b],["__file","2305090946.html.vue"]]);export{_ as default}; +import{_ as i,r as t,o,c as d,d as n,a as e,b as a,f as l,e as h}from"./app-6a63891c.js";const s={},c=e("h1",{id:"idea-常用插件",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea-常用插件","aria-hidden":"true"},"#"),a(" IDEA 常用插件")],-1),u=e("p",null,"IDEA 开发常用插件汇总,持续更新",-1),p=h('

Lombok

简介:自动get/set/toString等。

你只需要写好类属性,其他的交给Lombok

Features

@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and
@NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder
@SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var
@UtilityClass

使用方法:将上面的注解写到类上面即可

google-java-format

简介:google-java-format插件可以帮助我们不通过对应的快捷键就可以实现特定方式下自动格式化代码。

Translation

简介:翻译插件,支持google翻译、百度翻译、有道翻译。推荐使用谷歌翻译

Alibaba Java Coding Guidelines

简介:阿里巴巴代码规范检测。不符合代码规范的地方会有波浪线,鼠标移上去就会有相应的提示,有些问题甚至可以快速修复

Leetcode Editor

简介:LeetCode插件,可以在IDEA中在线刷题。上班摸鱼属实方便,表面上我在干活,实际上我在刷算法题。

也可前往 LeetCode 官网;

Jclasslib Bytecode Viewer

简介:看类的字节码文件

CamelCase

简介:在几种字符串格式之间来回切换。有一下几种格式:

  • 驼峰,第一个单词首字母小写,其他单词首字母大写
  • 所有字母小写,单词间下划线分隔
  • 所有字母小写,单词间空格分隔
  • 所有字母小写,单词间短横线分隔
  • 每个单词首字母全部大写
  • 所有字母大写,单词间下划线分隔

使用:按住Shift + Alt再不停的按U,会把选中内容的单词的下划线转驼峰转大写等,不停的转换,直到你想要的。

Free Mybatis Plugin

简介:可以通过mapper接口里的方法跳转到mapper.xml里。

Auto filling Java call arguments

简介:函数参数自动填充。已经编写好的函数,调用后需要填充参数,但是绝大多数情况下,传入的变量名称和该函数的参数名一致,当参数较多时,手动单个填充参数非常浪费时间。该插件就可以帮你解决这个问题。

FindBugs

简介:静态代码检查。可以检查你代码中的隐患,并给出原因。

SequenceDiagram

简介:根据调用链路生成时序图,对查看类调用及查看源码帮助很大;

Codota

简介:代码补全插件。争抢IDEA的代码提示功能,可以搜索第三方对该函数的使用方法

Maven Helper

简介:maven 依赖管理。

JRebel

',34),g={href:"https://www.jianshu.com/p/52e698c57131",target:"_blank",rel:"noopener noreferrer"};function b(m,f){const r=t("ExternalLinkIcon");return o(),d("div",null,[c,u,n(" more "),p,e("p",null,[a("简介:热部署插件 详情教程:"),e("a",g,[a("https://www.jianshu.com/p/52e698c57131"),l(r)])])])}const _=i(s,[["render",b],["__file","2305090946.html.vue"]]);export{_ as default}; diff --git a/assets/2305091732.html-9b499596.js b/assets/2305091732.html-16493ce7.js similarity index 99% rename from assets/2305091732.html-9b499596.js rename to assets/2305091732.html-16493ce7.js index 84b261d4..07cc270e 100644 --- a/assets/2305091732.html-9b499596.js +++ b/assets/2305091732.html-16493ce7.js @@ -1,4 +1,4 @@ -import{_ as e,o as t,c as a,e as s}from"./app-1efcbe9f.js";const n={},d=s(`

Redis数据持久化

RDB快照(snapshot)

默认情况下,Redis会将内存数据库快照保存在一个名称为 dump.rdb 的二进制文件中。我们可以通过 .conf 配置文件中的 save 属性进行设置,让它在"N秒内数据集至少有M个改动"这一条件被满足时,自动保存一次数据。

当我们需要关闭RDB只需要将所有的save保存策略注释掉即可:

# ./redis.conf
+import{_ as e,o as t,c as a,e as s}from"./app-6a63891c.js";const n={},d=s(`

Redis数据持久化

RDB快照(snapshot)

默认情况下,Redis会将内存数据库快照保存在一个名称为 dump.rdb 的二进制文件中。我们可以通过 .conf 配置文件中的 save 属性进行设置,让它在"N秒内数据集至少有M个改动"这一条件被满足时,自动保存一次数据。

当我们需要关闭RDB只需要将所有的save保存策略注释掉即可:

# ./redis.conf
 
 # save 3600 1
 # save 300 100
diff --git a/assets/2305091732.html-3d7cfd23.js b/assets/2305091732.html-be6a821a.js
similarity index 91%
rename from assets/2305091732.html-3d7cfd23.js
rename to assets/2305091732.html-be6a821a.js
index 90784baf..dddb076f 100644
--- a/assets/2305091732.html-3d7cfd23.js
+++ b/assets/2305091732.html-be6a821a.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0ba8fdf8","path":"/note/db/redis/2305091732.html","title":"Redis数据持久化","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-05T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"RDB快照(snapshot)","slug":"rdb快照-snapshot","link":"#rdb快照-snapshot","children":[{"level":3,"title":"bgsave:写时复制(COW机制)","slug":"bgsave-写时复制-cow机制","link":"#bgsave-写时复制-cow机制","children":[]},{"level":3,"title":"save与bgsave对比","slug":"save与bgsave对比","link":"#save与bgsave对比","children":[]}]},{"level":2,"title":"AOF (append-only file)","slug":"aof-append-only-file","link":"#aof-append-only-file","children":[{"level":3,"title":"AOF重写","slug":"aof重写","link":"#aof重写","children":[]}]},{"level":2,"title":"RDB 和 AOF 对比","slug":"rdb-和-aof-对比","link":"#rdb-和-aof-对比","children":[]},{"level":2,"title":"混合持久化 (Redis 4.0)","slug":"混合持久化-redis-4-0","link":"#混合持久化-redis-4-0","children":[]},{"level":2,"title":"Redis备份参考策略","slug":"redis备份参考策略","link":"#redis备份参考策略","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.45,"words":1635},"filePathRelative":"note/db/redis/2305091732.md","localizedDate":"2021年1月5日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-0ba8fdf8","path":"/note/db/redis/2305091732.html","title":"Redis数据持久化","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-05T00:00:00.000Z","index":true,"order":2,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"RDB快照(snapshot)","slug":"rdb快照-snapshot","link":"#rdb快照-snapshot","children":[{"level":3,"title":"bgsave:写时复制(COW机制)","slug":"bgsave-写时复制-cow机制","link":"#bgsave-写时复制-cow机制","children":[]},{"level":3,"title":"save与bgsave对比","slug":"save与bgsave对比","link":"#save与bgsave对比","children":[]}]},{"level":2,"title":"AOF (append-only file)","slug":"aof-append-only-file","link":"#aof-append-only-file","children":[{"level":3,"title":"AOF重写","slug":"aof重写","link":"#aof重写","children":[]}]},{"level":2,"title":"RDB 和 AOF 对比","slug":"rdb-和-aof-对比","link":"#rdb-和-aof-对比","children":[]},{"level":2,"title":"混合持久化 (Redis 4.0)","slug":"混合持久化-redis-4-0","link":"#混合持久化-redis-4-0","children":[]},{"level":2,"title":"Redis备份参考策略","slug":"redis备份参考策略","link":"#redis备份参考策略","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.45,"words":1635},"filePathRelative":"note/db/redis/2305091732.md","localizedDate":"2021年1月5日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2305091734.html-74325375.js b/assets/2305091734.html-0e6eef80.js
similarity index 94%
rename from assets/2305091734.html-74325375.js
rename to assets/2305091734.html-0e6eef80.js
index 49f5eabb..96f90786 100644
--- a/assets/2305091734.html-74325375.js
+++ b/assets/2305091734.html-0e6eef80.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-04d59b7c","path":"/note/db/redis/2305091734.html","title":"Redis主从架构","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-09T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"工作原理","slug":"工作原理","link":"#工作原理","children":[{"level":3,"title":"主从复制(全量复制)","slug":"主从复制-全量复制","link":"#主从复制-全量复制","children":[]},{"level":3,"title":"主从复制(部分复制)","slug":"主从复制-部分复制","link":"#主从复制-部分复制","children":[]}]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[{"level":3,"title":"主从复制风暴","slug":"主从复制风暴","link":"#主从复制风暴","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.35,"words":706},"filePathRelative":"note/db/redis/2305091734.md","localizedDate":"2021年1月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-04d59b7c","path":"/note/db/redis/2305091734.html","title":"Redis主从架构","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-09T00:00:00.000Z","index":true,"order":4,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"工作原理","slug":"工作原理","link":"#工作原理","children":[{"level":3,"title":"主从复制(全量复制)","slug":"主从复制-全量复制","link":"#主从复制-全量复制","children":[]},{"level":3,"title":"主从复制(部分复制)","slug":"主从复制-部分复制","link":"#主从复制-部分复制","children":[]}]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[{"level":3,"title":"主从复制风暴","slug":"主从复制风暴","link":"#主从复制风暴","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.35,"words":706},"filePathRelative":"note/db/redis/2305091734.md","localizedDate":"2021年1月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2305091734.html-93fb7a8f.js b/assets/2305091734.html-47987fdb.js
similarity index 98%
rename from assets/2305091734.html-93fb7a8f.js
rename to assets/2305091734.html-47987fdb.js
index c9058054..b8352b55 100644
--- a/assets/2305091734.html-93fb7a8f.js
+++ b/assets/2305091734.html-47987fdb.js
@@ -1 +1 @@
-import{_ as r,o as s,c as t,e as n}from"./app-1efcbe9f.js";const o={},e=n('

Redis主从架构

工作原理

主从复制(全量复制)

  1. 如果你为 master 节点配置了一个 slave ,不管这个 slave 是否第一次连接上 master ,它都会发送一个 PSYNVC 命令给 master 请求复制数据。
  2. master 节点收到 PSYNC 命令后,会在后台通过 bgsave 进行数据持久化生成最新的rdb快照文件,持久化期间, master 会继续接收客户端的请求,他会把这些可能修改数据集的请求缓存在内存中。
  3. 当持久化进行完毕后, maser 会把这份rdb文件数据集发送给 slaveslave 会把己收到的数据进行rdb持久化,然后加载到内存中。若 master 内存中存在后续修改的数据集,再将之前缓存的命令发送给 slave
  4. masterslave 之间的链接由于某种原因断开连接时, slave 能够自动链接 master ,如果 master 收到多个 slave 并发链接请求,它只会进行一次持久化,而不是一次链接持久化一次,会把这一份持久化的数据发送给多个并发连接的 slave
Redis主从复制(全量复制)
Redis主从复制(全量复制)

主从复制(部分复制)

  1. masterslave 断开连接,一般都会对正分数据进行复制。但是从2.8版本开始,redis改用可以支持部分数据复制的命令 PSYNCmaster 同步数据,slavemaster 能够在网络连接断开重连后进行部分数据复制(断电续传);
  2. master 会在内存中创建一个缓存队列,缓存最近一段时间的数据,master 和它所有的 slave 都维护了缓存的数据下标 offsetmaster进程id
  3. 当网络断开,slave 请求 master 继续进行未完成的复制,从所记录的数据下标开始,如果 master 进程ID变了或者 slave 节点数据下标 offset 已经不在 master 缓存队列中时,会进行一次 全量复制
Reids主从复制(部分复制)
Reids主从复制(部分复制)

⚠️注意:repl buffer中存的数据是先进先出的,当偏移量(offset)已经找不到,则全量复制

问题

主从复制风暴

从上述可以看到我们redis主从同步的执行流程,当我们一个主节点存在很多从节点,从节点同时复制主节点会导致主节点压力过大。这就是我们所说的主从复制风暴。

对于主从复制风暴,我们可以通过让部分的从从节点不再从主节点同步数据,而是跟从节点同步数据,具体结构如下:

主从节点同步模型
主从节点同步模型
',14),a=[e];function g(i,l){return s(),t("div",null,a)}const h=r(o,[["render",g],["__file","2305091734.html.vue"]]);export{h as default}; +import{_ as r,o as s,c as t,e as n}from"./app-6a63891c.js";const o={},e=n('

Redis主从架构

工作原理

主从复制(全量复制)

  1. 如果你为 master 节点配置了一个 slave ,不管这个 slave 是否第一次连接上 master ,它都会发送一个 PSYNVC 命令给 master 请求复制数据。
  2. master 节点收到 PSYNC 命令后,会在后台通过 bgsave 进行数据持久化生成最新的rdb快照文件,持久化期间, master 会继续接收客户端的请求,他会把这些可能修改数据集的请求缓存在内存中。
  3. 当持久化进行完毕后, maser 会把这份rdb文件数据集发送给 slaveslave 会把己收到的数据进行rdb持久化,然后加载到内存中。若 master 内存中存在后续修改的数据集,再将之前缓存的命令发送给 slave
  4. masterslave 之间的链接由于某种原因断开连接时, slave 能够自动链接 master ,如果 master 收到多个 slave 并发链接请求,它只会进行一次持久化,而不是一次链接持久化一次,会把这一份持久化的数据发送给多个并发连接的 slave
Redis主从复制(全量复制)
Redis主从复制(全量复制)

主从复制(部分复制)

  1. masterslave 断开连接,一般都会对正分数据进行复制。但是从2.8版本开始,redis改用可以支持部分数据复制的命令 PSYNCmaster 同步数据,slavemaster 能够在网络连接断开重连后进行部分数据复制(断电续传);
  2. master 会在内存中创建一个缓存队列,缓存最近一段时间的数据,master 和它所有的 slave 都维护了缓存的数据下标 offsetmaster进程id
  3. 当网络断开,slave 请求 master 继续进行未完成的复制,从所记录的数据下标开始,如果 master 进程ID变了或者 slave 节点数据下标 offset 已经不在 master 缓存队列中时,会进行一次 全量复制
Reids主从复制(部分复制)
Reids主从复制(部分复制)

⚠️注意:repl buffer中存的数据是先进先出的,当偏移量(offset)已经找不到,则全量复制

问题

主从复制风暴

从上述可以看到我们redis主从同步的执行流程,当我们一个主节点存在很多从节点,从节点同时复制主节点会导致主节点压力过大。这就是我们所说的主从复制风暴。

对于主从复制风暴,我们可以通过让部分的从从节点不再从主节点同步数据,而是跟从节点同步数据,具体结构如下:

主从节点同步模型
主从节点同步模型
',14),a=[e];function g(i,l){return s(),t("div",null,a)}const h=r(o,[["render",g],["__file","2305091734.html.vue"]]);export{h as default}; diff --git a/assets/2305091736.html-35ac6b25.js b/assets/2305091736.html-8c380a4b.js similarity index 86% rename from assets/2305091736.html-35ac6b25.js rename to assets/2305091736.html-8c380a4b.js index cb95bb03..4c0bbe10 100644 --- a/assets/2305091736.html-35ac6b25.js +++ b/assets/2305091736.html-8c380a4b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-00fee380","path":"/note/db/redis/2305091736.html","title":"Redis事务","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-15T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"事务实现","slug":"事务实现","link":"#事务实现","children":[]},{"level":2,"title":"管道","slug":"管道","link":"#管道","children":[]},{"level":2,"title":"Lua脚本","slug":"lua脚本","link":"#lua脚本","children":[{"level":3,"title":"优势","slug":"优势","link":"#优势","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.72,"words":816},"filePathRelative":"note/db/redis/2305091736.md","localizedDate":"2021年1月15日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-00fee380","path":"/note/db/redis/2305091736.html","title":"Redis事务","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-15T00:00:00.000Z","index":true,"order":3,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"事务实现","slug":"事务实现","link":"#事务实现","children":[]},{"level":2,"title":"管道","slug":"管道","link":"#管道","children":[]},{"level":2,"title":"Lua脚本","slug":"lua脚本","link":"#lua脚本","children":[{"level":3,"title":"优势","slug":"优势","link":"#优势","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.72,"words":816},"filePathRelative":"note/db/redis/2305091736.md","localizedDate":"2021年1月15日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305091736.html-bbffee07.js b/assets/2305091736.html-c4b92ec7.js similarity index 98% rename from assets/2305091736.html-bbffee07.js rename to assets/2305091736.html-c4b92ec7.js index 02b0913c..bbd3cc1f 100644 --- a/assets/2305091736.html-bbffee07.js +++ b/assets/2305091736.html-c4b92ec7.js @@ -1,4 +1,4 @@ -import{_ as n,o as s,c as a,e}from"./app-1efcbe9f.js";const i={},t=e(`

Redis事务

事务实现

管道

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完成后再一次性读取服务器的响应,这样可以极大的降低多条命令执行的网络传输开销。管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意的是用pipeline方式打包命令发送,redis必须处理完所有命令前先缓存起所有命令的处理结构。打包的命令越多,缓存消耗的内存也越多,所以并不是打包的命令越多越好。

pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达所有的命令一起成功的语义,管道中前面命令失败并不会影响到后面命令的执行,同时管道的操作并 非原子 的。

Lua脚本

reids 在2.6版本推出的脚本功能,允许开发者使用lua语言编写脚本传到redis中执行。通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。EVAL命令格式如下:

EVAL script numkeys key [key..] arg [arg...]
+import{_ as n,o as s,c as a,e}from"./app-6a63891c.js";const i={},t=e(`

Redis事务

事务实现

管道

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完成后再一次性读取服务器的响应,这样可以极大的降低多条命令执行的网络传输开销。管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意的是用pipeline方式打包命令发送,redis必须处理完所有命令前先缓存起所有命令的处理结构。打包的命令越多,缓存消耗的内存也越多,所以并不是打包的命令越多越好。

pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达所有的命令一起成功的语义,管道中前面命令失败并不会影响到后面命令的执行,同时管道的操作并 非原子 的。

Lua脚本

reids 在2.6版本推出的脚本功能,允许开发者使用lua语言编写脚本传到redis中执行。通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。EVAL命令格式如下:

EVAL script numkeys key [key..] arg [arg...]
 
 # 1.script参数是一段Lua脚本程序,它会被运行在redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。
 # 2.numkeys参数用于指定键名参数的个数。
diff --git a/assets/2305091740.html-9f9bd2cb.js b/assets/2305091740.html-d1cf475e.js
similarity index 96%
rename from assets/2305091740.html-9f9bd2cb.js
rename to assets/2305091740.html-d1cf475e.js
index 48c394d5..7b3ef196 100644
--- a/assets/2305091740.html-9f9bd2cb.js
+++ b/assets/2305091740.html-d1cf475e.js
@@ -1 +1 @@
-import{_ as e,o as i,c as n,e as s}from"./app-1efcbe9f.js";const a={},t=s('

Redis哨兵(Sentinel)

工作原理

sentinel哨兵是特殊的redis服务,不提供读写,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis主节点,后续就直接访问redis主节点,不会每次都通过sentinel代理访问redis主节点,当redis主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端。

client端会实现订阅功能,订阅sentinel发布的节点变动消息。如果redis主节点挂了,哨兵集群会重新选举出新的redis主节点。

Reids哨兵架构
Reids哨兵架构

优缺点

在Redis 3.0以前的版本要实现集群一般是借助哨兵sentinel节点的状态,在高可用高并发等场景下会存在以下问题:

  1. 如果mastr节点异常,则会做主从切换,将某一台slave作为master,消耗时间和性能;
  2. 哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间会存在访问瞬断的情况;
  3. 哨兵模式只有一个主节点对外提供服务,没法支持很高的并发;
  4. 当耽搁主节点内存设置过大,否则会导致持久化文件多大,影响数据恢复或主从同步的效率;
',9),r=[t];function d(l,c){return i(),n("div",null,r)}const h=e(a,[["render",d],["__file","2305091740.html.vue"]]);export{h as default}; +import{_ as e,o as i,c as n,e as s}from"./app-6a63891c.js";const a={},t=s('

Redis哨兵(Sentinel)

工作原理

sentinel哨兵是特殊的redis服务,不提供读写,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis主节点,后续就直接访问redis主节点,不会每次都通过sentinel代理访问redis主节点,当redis主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端。

client端会实现订阅功能,订阅sentinel发布的节点变动消息。如果redis主节点挂了,哨兵集群会重新选举出新的redis主节点。

Reids哨兵架构
Reids哨兵架构

优缺点

在Redis 3.0以前的版本要实现集群一般是借助哨兵sentinel节点的状态,在高可用高并发等场景下会存在以下问题:

  1. 如果mastr节点异常,则会做主从切换,将某一台slave作为master,消耗时间和性能;
  2. 哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间会存在访问瞬断的情况;
  3. 哨兵模式只有一个主节点对外提供服务,没法支持很高的并发;
  4. 当耽搁主节点内存设置过大,否则会导致持久化文件多大,影响数据恢复或主从同步的效率;
',9),r=[t];function d(l,c){return i(),n("div",null,r)}const h=e(a,[["render",d],["__file","2305091740.html.vue"]]);export{h as default}; diff --git a/assets/2305091740.html-8f88a3ea.js b/assets/2305091740.html-f7b9f78e.js similarity index 91% rename from assets/2305091740.html-8f88a3ea.js rename to assets/2305091740.html-f7b9f78e.js index 06923ecd..f4e86e5a 100644 --- a/assets/2305091740.html-8f88a3ea.js +++ b/assets/2305091740.html-f7b9f78e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-2ba80b07","path":"/note/db/redis/2305091740.html","title":"Redis哨兵(Sentinel)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-18T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"工作原理","slug":"工作原理","link":"#工作原理","children":[]},{"level":2,"title":"优缺点","slug":"优缺点","link":"#优缺点","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.23,"words":370},"filePathRelative":"note/db/redis/2305091740.md","localizedDate":"2021年1月18日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-2ba80b07","path":"/note/db/redis/2305091740.html","title":"Redis哨兵(Sentinel)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-18T00:00:00.000Z","index":true,"order":5,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"工作原理","slug":"工作原理","link":"#工作原理","children":[]},{"level":2,"title":"优缺点","slug":"优缺点","link":"#优缺点","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.23,"words":370},"filePathRelative":"note/db/redis/2305091740.md","localizedDate":"2021年1月18日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305091744.html-21ea524d.js b/assets/2305091744.html-97d55f51.js similarity index 99% rename from assets/2305091744.html-21ea524d.js rename to assets/2305091744.html-97d55f51.js index 4820cdde..5fbe2ca6 100644 --- a/assets/2305091744.html-21ea524d.js +++ b/assets/2305091744.html-97d55f51.js @@ -1,4 +1,4 @@ -import{_ as n,o as s,c as a,e as p}from"./app-1efcbe9f.js";const t={},e=p(`

Redis集群(Cluster)

redis集群是一个又多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片的特性。
redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展。
根据官方文档称,可线性扩展到上万个节点(推荐不超过1W个节点)。
redis集群的性能和高可用性均优于哨兵模式,且配置简单。

原理分析

Redis Cluster将所有数据划分为16384个槽位(slots),每个节点负责其中一部分槽位。槽位信息存储在每一个节点中。

当Redis Cluster的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。如此一来当客户端要查找某个key时,可以直接地你个为到目标节点。
而因为槽位的信息可能会存在客户端与服务器不一致的情况,因此还实现了纠正机制来实现槽位信息的校验调整。

槽位定位

Redis Cluster默认对key使用crc16算法进行hash后得到一个整数值,然后用这个整数值对16384个槽位进行取模后获取具体的槽位。

# JedisClusterCRC16 核心源码实现
+import{_ as n,o as s,c as a,e as p}from"./app-6a63891c.js";const t={},e=p(`

Redis集群(Cluster)

redis集群是一个又多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片的特性。
redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展。
根据官方文档称,可线性扩展到上万个节点(推荐不超过1W个节点)。
redis集群的性能和高可用性均优于哨兵模式,且配置简单。

原理分析

Redis Cluster将所有数据划分为16384个槽位(slots),每个节点负责其中一部分槽位。槽位信息存储在每一个节点中。

当Redis Cluster的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。如此一来当客户端要查找某个key时,可以直接地你个为到目标节点。
而因为槽位的信息可能会存在客户端与服务器不一致的情况,因此还实现了纠正机制来实现槽位信息的校验调整。

槽位定位

Redis Cluster默认对key使用crc16算法进行hash后得到一个整数值,然后用这个整数值对16384个槽位进行取模后获取具体的槽位。

# JedisClusterCRC16 核心源码实现
 
 package redis.clients.util;
 
diff --git a/assets/2305091744.html-e174d288.js b/assets/2305091744.html-e2b447fb.js
similarity index 89%
rename from assets/2305091744.html-e174d288.js
rename to assets/2305091744.html-e2b447fb.js
index 766ab18b..d4976229 100644
--- a/assets/2305091744.html-e174d288.js
+++ b/assets/2305091744.html-e2b447fb.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-327b6d83","path":"/note/db/redis/2305091744.html","title":"Redis集群(Cluster)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-20T00:00:00.000Z","index":true,"order":6,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"原理分析","slug":"原理分析","link":"#原理分析","children":[{"level":3,"title":"槽位定位","slug":"槽位定位","link":"#槽位定位","children":[]},{"level":3,"title":"跳转重定位","slug":"跳转重定位","link":"#跳转重定位","children":[]}]},{"level":2,"title":"集群节点间通信","slug":"集群节点间通信","link":"#集群节点间通信","children":[{"level":3,"title":"集中式","slug":"集中式","link":"#集中式","children":[]},{"level":3,"title":"gossip","slug":"gossip","link":"#gossip","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.62,"words":1385},"filePathRelative":"note/db/redis/2305091744.md","localizedDate":"2021年1月20日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-327b6d83","path":"/note/db/redis/2305091744.html","title":"Redis集群(Cluster)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2021-01-20T00:00:00.000Z","index":true,"order":6,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"原理分析","slug":"原理分析","link":"#原理分析","children":[{"level":3,"title":"槽位定位","slug":"槽位定位","link":"#槽位定位","children":[]},{"level":3,"title":"跳转重定位","slug":"跳转重定位","link":"#跳转重定位","children":[]}]},{"level":2,"title":"集群节点间通信","slug":"集群节点间通信","link":"#集群节点间通信","children":[{"level":3,"title":"集中式","slug":"集中式","link":"#集中式","children":[]},{"level":3,"title":"gossip","slug":"gossip","link":"#gossip","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.62,"words":1385},"filePathRelative":"note/db/redis/2305091744.md","localizedDate":"2021年1月20日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2305110857.html-f140f1fd.js b/assets/2305110857.html-10721076.js
similarity index 99%
rename from assets/2305110857.html-f140f1fd.js
rename to assets/2305110857.html-10721076.js
index 0e728ba7..7ab13017 100644
--- a/assets/2305110857.html-f140f1fd.js
+++ b/assets/2305110857.html-10721076.js
@@ -1,4 +1,4 @@
-import{_ as n,o as a,c as s,e}from"./app-1efcbe9f.js";const t={},p=e(`

zookeeper基本使用

基本概念

通常情况下,单个物理节点很容易达到性能,计算或者容量的瓶颈,所以这个时候就需要多个物理节点来共同完成某项任务,一个分布式系统的本质是分布在不同网络或计算机上的程序组件,彼此通过信息传递来协同工作的系统,而Zookeeper正是一个分布式应用协调框架,在分布式系统架构中有广泛的应用场景。

官方文档解释:zookeeper它是一个分布式协调框架,是ApacheHadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等

核心概念

一个基于内存,用于存储少量数据的数据库。核心概念:文件系统存储结构、监听通知机制

文件系统存储结构

Zookeeper维护一个了类似文件系统的数据结构,每个子目录项都被称作为znode (目录节点),和文件系统类似,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode。

zookeeper有以下几种类型的znode:

1、PERSISTENT­持久化目录节点客户端与zookeeper断开连接后,该节点依旧存在,只要不手动删除该节点,他将永远存在

2、PERSISTENT_SEQUENTIAL­持久化顺序编号目录节点客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3、EPHEMERAL­临时目录节点客户端与zookeeper断开连接后,该节点被删除

4、EPHEMERAL_SEQUENTIAL­临时顺序编号目录节点客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

5、Container节点(3.5.3版本新增,如果Container节点下面没有子节点,则Container节点在未来会被Zookeeper自动清除,定时任务默认60s检查一次)

6、TTL节点(默认禁用,只能通过系统配置zookeeper.extendedTypesEnabled=true开启,不稳定

监听通知机制

客户端注册监听它关心的任意节点,或者目录节点及递归子目录节点。

1.如果注册的是对某个节点的监听,则当这个节点被删除,或者被修改时,对应的客户端将被通知。

2.如果注册的是对某个目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对应的客户端将被通知。

3.如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有目录结构的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。

注意:所有的通知都是 一次性 的,及无论是对节点还是对目录进行的监听,一旦触发,对应的监听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被 触发一次

应用场景

  1. 分布式配置中心
  2. 分布式注册中心
  3. 分布式锁
  4. 分布式队列
  5. 分布式屏障
  6. 集群选举
  7. 发布/订阅

安装

Step1:配置JAVA环境,并检查环境(建议JDK1.8)以上版本

java -version
+import{_ as n,o as a,c as s,e}from"./app-6a63891c.js";const t={},p=e(`

zookeeper基本使用

基本概念

通常情况下,单个物理节点很容易达到性能,计算或者容量的瓶颈,所以这个时候就需要多个物理节点来共同完成某项任务,一个分布式系统的本质是分布在不同网络或计算机上的程序组件,彼此通过信息传递来协同工作的系统,而Zookeeper正是一个分布式应用协调框架,在分布式系统架构中有广泛的应用场景。

官方文档解释:zookeeper它是一个分布式协调框架,是ApacheHadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等

核心概念

一个基于内存,用于存储少量数据的数据库。核心概念:文件系统存储结构、监听通知机制

文件系统存储结构

Zookeeper维护一个了类似文件系统的数据结构,每个子目录项都被称作为znode (目录节点),和文件系统类似,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode。

zookeeper有以下几种类型的znode:

1、PERSISTENT­持久化目录节点客户端与zookeeper断开连接后,该节点依旧存在,只要不手动删除该节点,他将永远存在

2、PERSISTENT_SEQUENTIAL­持久化顺序编号目录节点客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3、EPHEMERAL­临时目录节点客户端与zookeeper断开连接后,该节点被删除

4、EPHEMERAL_SEQUENTIAL­临时顺序编号目录节点客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

5、Container节点(3.5.3版本新增,如果Container节点下面没有子节点,则Container节点在未来会被Zookeeper自动清除,定时任务默认60s检查一次)

6、TTL节点(默认禁用,只能通过系统配置zookeeper.extendedTypesEnabled=true开启,不稳定

监听通知机制

客户端注册监听它关心的任意节点,或者目录节点及递归子目录节点。

1.如果注册的是对某个节点的监听,则当这个节点被删除,或者被修改时,对应的客户端将被通知。

2.如果注册的是对某个目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对应的客户端将被通知。

3.如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有目录结构的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。

注意:所有的通知都是 一次性 的,及无论是对节点还是对目录进行的监听,一旦触发,对应的监听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被 触发一次

应用场景

  1. 分布式配置中心
  2. 分布式注册中心
  3. 分布式锁
  4. 分布式队列
  5. 分布式屏障
  6. 集群选举
  7. 发布/订阅

安装

Step1:配置JAVA环境,并检查环境(建议JDK1.8)以上版本

java -version
 

Step2:下载并解压zookeeper

wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper‐3.5.8/apache‐zookeeper‐3.5.8‐bin.tar.gz
 # 此处若报错  XXXX use ‘--no-check-certificate’. 则在 wget后加入 --no-check-certificate 跳过证书检查
 
diff --git a/assets/2305110857.html-911475e0.js b/assets/2305110857.html-24d27337.js
similarity index 97%
rename from assets/2305110857.html-911475e0.js
rename to assets/2305110857.html-24d27337.js
index 4f26fb51..ab10bc85 100644
--- a/assets/2305110857.html-911475e0.js
+++ b/assets/2305110857.html-24d27337.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-e97c7d16","path":"/note/microservices/zookeeper/2305110857.html","title":"zookeeper基本使用","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["微服务"],"tag":["zookeeper"]},"headers":[{"level":2,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[]},{"level":2,"title":"核心概念","slug":"核心概念","link":"#核心概念","children":[]},{"level":2,"title":"文件系统存储结构","slug":"文件系统存储结构","link":"#文件系统存储结构","children":[]},{"level":2,"title":"监听通知机制","slug":"监听通知机制","link":"#监听通知机制","children":[]},{"level":2,"title":"应用场景","slug":"应用场景","link":"#应用场景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"基础命令","slug":"基础命令","link":"#基础命令","children":[]},{"level":2,"title":"help :查看zookeeper所支持的所有命令","slug":"help-查看zookeeper所支持的所有命令","link":"#help-查看zookeeper所支持的所有命令","children":[]},{"level":2,"title":"create :创建新节点","slug":"create-创建新节点","link":"#create-创建新节点","children":[]},{"level":2,"title":"get :查看结点","slug":"get-查看结点","link":"#get-查看结点","children":[]},{"level":2,"title":"set :修改结点","slug":"set-修改结点","link":"#set-修改结点","children":[]},{"level":2,"title":"stat :查看结点状态","slug":"stat-查看结点状态","link":"#stat-查看结点状态","children":[]},{"level":2,"title":"ls :查看子节点信息","slug":"ls-查看子节点信息","link":"#ls-查看子节点信息","children":[]},{"level":2,"title":"事件监听机制","slug":"事件监听机制","link":"#事件监听机制","children":[]},{"level":2,"title":"ACL权限控制","slug":"acl权限控制","link":"#acl权限控制","children":[]},{"level":2,"title":"Scheme(权限模式)","slug":"scheme-权限模式","link":"#scheme-权限模式","children":[]},{"level":2,"title":"ID(授权对象)","slug":"id-授权对象","link":"#id-授权对象","children":[]},{"level":2,"title":"Permission(权限信息)","slug":"permission-权限信息","link":"#permission-权限信息","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"生成授权ID的方式","slug":"生成授权id的方式","link":"#生成授权id的方式","children":[]},{"level":2,"title":"设置ACL的两种方式","slug":"设置acl的两种方式","link":"#设置acl的两种方式","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.19,"words":3057},"filePathRelative":"note/microservices/zookeeper/2305110857.md","localizedDate":"2023年5月7日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-e97c7d16","path":"/note/microservices/zookeeper/2305110857.html","title":"zookeeper基本使用","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["微服务"],"tag":["zookeeper"]},"headers":[{"level":2,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[]},{"level":2,"title":"核心概念","slug":"核心概念","link":"#核心概念","children":[]},{"level":2,"title":"文件系统存储结构","slug":"文件系统存储结构","link":"#文件系统存储结构","children":[]},{"level":2,"title":"监听通知机制","slug":"监听通知机制","link":"#监听通知机制","children":[]},{"level":2,"title":"应用场景","slug":"应用场景","link":"#应用场景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"基础命令","slug":"基础命令","link":"#基础命令","children":[]},{"level":2,"title":"help :查看zookeeper所支持的所有命令","slug":"help-查看zookeeper所支持的所有命令","link":"#help-查看zookeeper所支持的所有命令","children":[]},{"level":2,"title":"create :创建新节点","slug":"create-创建新节点","link":"#create-创建新节点","children":[]},{"level":2,"title":"get :查看结点","slug":"get-查看结点","link":"#get-查看结点","children":[]},{"level":2,"title":"set :修改结点","slug":"set-修改结点","link":"#set-修改结点","children":[]},{"level":2,"title":"stat :查看结点状态","slug":"stat-查看结点状态","link":"#stat-查看结点状态","children":[]},{"level":2,"title":"ls :查看子节点信息","slug":"ls-查看子节点信息","link":"#ls-查看子节点信息","children":[]},{"level":2,"title":"事件监听机制","slug":"事件监听机制","link":"#事件监听机制","children":[]},{"level":2,"title":"ACL权限控制","slug":"acl权限控制","link":"#acl权限控制","children":[]},{"level":2,"title":"Scheme(权限模式)","slug":"scheme-权限模式","link":"#scheme-权限模式","children":[]},{"level":2,"title":"ID(授权对象)","slug":"id-授权对象","link":"#id-授权对象","children":[]},{"level":2,"title":"Permission(权限信息)","slug":"permission-权限信息","link":"#permission-权限信息","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"生成授权ID的方式","slug":"生成授权id的方式","link":"#生成授权id的方式","children":[]},{"level":2,"title":"设置ACL的两种方式","slug":"设置acl的两种方式","link":"#设置acl的两种方式","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.19,"words":3057},"filePathRelative":"note/microservices/zookeeper/2305110857.md","localizedDate":"2023年5月7日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2305111000.html-37cd3213.js b/assets/2305111000.html-726d6584.js
similarity index 99%
rename from assets/2305111000.html-37cd3213.js
rename to assets/2305111000.html-726d6584.js
index ddc643c9..9d143c76 100644
--- a/assets/2305111000.html-37cd3213.js
+++ b/assets/2305111000.html-726d6584.js
@@ -1,4 +1,4 @@
-import{_ as n,o as a,c as p,d as t,a as s,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=s("h1",{id:"jvm类加载机制",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#jvm类加载机制","aria-hidden":"true"},"#"),e(" JVM类加载机制")],-1),i=s("p",null,"本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。",-1),u=o(`

运行全程

现假设有一个JAVA程序,程序内编写如下代码并启动main()方法

package cn.yaien.jvm;
+import{_ as n,o as a,c as p,d as t,a as s,b as e,e as o}from"./app-6a63891c.js";const c={},l=s("h1",{id:"jvm类加载机制",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#jvm类加载机制","aria-hidden":"true"},"#"),e(" JVM类加载机制")],-1),i=s("p",null,"本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。",-1),u=o(`

运行全程

现假设有一个JAVA程序,程序内编写如下代码并启动main()方法

package cn.yaien.jvm;
 
 /**
  * <h1> 模拟类加载过程 </h1>
diff --git a/assets/2305111000.html-2f6393ab.js b/assets/2305111000.html-7275fa91.js
similarity index 93%
rename from assets/2305111000.html-2f6393ab.js
rename to assets/2305111000.html-7275fa91.js
index 16b4edac..b0e02656 100644
--- a/assets/2305111000.html-2f6393ab.js
+++ b/assets/2305111000.html-7275fa91.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-d0e2c786","path":"/note/java/jvm/2305111000.html","title":"JVM类加载机制","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":2,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","类加载"]},"headers":[{"level":2,"title":"运行全程","slug":"运行全程","link":"#运行全程","children":[]},{"level":2,"title":"类加载器","slug":"类加载器","link":"#类加载器","children":[{"level":3,"title":"加载器类型","slug":"加载器类型","link":"#加载器类型","children":[]},{"level":3,"title":"类加载器实例源码(sun.misc.Launcher)","slug":"类加载器实例源码-sun-misc-launcher","link":"#类加载器实例源码-sun-misc-launcher","children":[]}]},{"level":2,"title":"类加载过程(loadClass)","slug":"类加载过程-loadclass","link":"#类加载过程-loadclass","children":[]},{"level":2,"title":"双亲委派机制","slug":"双亲委派机制","link":"#双亲委派机制","children":[{"level":3,"title":"双亲委派源码","slug":"双亲委派源码","link":"#双亲委派源码","children":[]},{"level":3,"title":"设计好处","slug":"设计好处","link":"#设计好处","children":[]},{"level":3,"title":"全盘负责机制","slug":"全盘负责机制","link":"#全盘负责机制","children":[]},{"level":3,"title":"自定义类加载器","slug":"自定义类加载器","link":"#自定义类加载器","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.27,"words":1880},"filePathRelative":"note/java/jvm/2305111000.md","localizedDate":"2023年5月7日","excerpt":"

JVM类加载机制

\\n

本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-d0e2c786","path":"/note/java/jvm/2305111000.html","title":"JVM类加载机制","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":2,"date":"2023-05-07T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","类加载"]},"headers":[{"level":2,"title":"运行全程","slug":"运行全程","link":"#运行全程","children":[]},{"level":2,"title":"类加载器","slug":"类加载器","link":"#类加载器","children":[{"level":3,"title":"加载器类型","slug":"加载器类型","link":"#加载器类型","children":[]},{"level":3,"title":"类加载器实例源码(sun.misc.Launcher)","slug":"类加载器实例源码-sun-misc-launcher","link":"#类加载器实例源码-sun-misc-launcher","children":[]}]},{"level":2,"title":"类加载过程(loadClass)","slug":"类加载过程-loadclass","link":"#类加载过程-loadclass","children":[]},{"level":2,"title":"双亲委派机制","slug":"双亲委派机制","link":"#双亲委派机制","children":[{"level":3,"title":"双亲委派源码","slug":"双亲委派源码","link":"#双亲委派源码","children":[]},{"level":3,"title":"设计好处","slug":"设计好处","link":"#设计好处","children":[]},{"level":3,"title":"全盘负责机制","slug":"全盘负责机制","link":"#全盘负责机制","children":[]},{"level":3,"title":"自定义类加载器","slug":"自定义类加载器","link":"#自定义类加载器","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.27,"words":1880},"filePathRelative":"note/java/jvm/2305111000.md","localizedDate":"2023年5月7日","excerpt":"

JVM类加载机制

\\n

本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305140023.html-535c91ba.js b/assets/2305140023.html-34d7cfb7.js similarity index 98% rename from assets/2305140023.html-535c91ba.js rename to assets/2305140023.html-34d7cfb7.js index 314d668b..0049d0b5 100644 --- a/assets/2305140023.html-535c91ba.js +++ b/assets/2305140023.html-34d7cfb7.js @@ -1 +1 @@ -import{_ as i,o as r,c as o,d as n,a as e,b as a,e as d}from"./app-1efcbe9f.js";const t={},h=e("h1",{id:"dubbo-基本介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#dubbo-基本介绍","aria-hidden":"true"},"#"),a(" Dubbo 基本介绍")],-1),b=e("p",null,[a("Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用"),e("br"),a(" Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。")],-1),c=e("p",null,"在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。",-1),s=d('

Dubbo一开始的定位就是RPC,专注于两个服务之间的调用。但随着微服务的盛行,除开服务调用之外,Dubbo也在逐步的涉猎服务治理、服务监控、服务网关等等,所以现在的Dubbo目标已经不止是RPC框架了,而是和Spring Cloud类似想成为了一个服务框架

核心组件

服务提供者(Provider)

作为服务的提供者,将自己的服务注册到注册中心,接收消费者的调用请求并提供相应的服务实现。提供者通常是一个独立的应用程序,它将自己的服务暴露给消费者。

服务消费者(Consumer)

从注册中心获取服务提供者的地址,并发起远程调用请求

注册中心(Register)

用于服务的注册与发现,提供服务地址的管理

监控中心(Monitor)

实时监控和统计分析服务的运行情况和性能指标,帮助用户更好地管理和优化分布式服务,提高系统的可靠性和可用性

基本原理

基本原理
基本原理

架构

从抽象架构上分为两层:服务治理抽象控制面 和 Dubbo 数据面

架构
架构

服务治理控制面

服务治理控制面不是特指如注册中心类的单个具体组件,而是对 Dubbo 治理体系的抽象表达。控制面包含协调服务发现的注册中心、流量管控策略、Dubbo Admin 控制台等,如果采用了 Service Mesh 架构则还包含 Istio
等服务网格控制面。

Dubbo数据面

数据面代表集群部署的所有 Dubbo 进程,进程之间通过 RPC 协议实现数据交换,Dubbo 定义了微服务应用开发与调用规范并负责完成数据传输的编解码工作

  • 服务消费者 (Dubbo Consumer),发起业务调用或 RPC 通信的 Dubbo 进程
  • 服务提供者 (Dubbo Provider),接收业务调用或 RPC 通信的 Dubbo 进程

通信协议

Dubbo 从设计上不绑定任何一款特定通信协议,HTTP/2、REST、gRPC、JsonRPC、Thrift、Hessian2 等几乎所有主流的通信协议,Dubbo 框架都可以提供支持。

这样的 Protocol 设计模式给构建微服务带来了最大的灵活性,开发者可以根据需要如性能、通用型等选择不同的通信协议,不再需要任何的代理来实现协议转换,甚至你还可以通过 Dubbo 实现不同协议间的迁移。

',24);function u(l,p){return r(),o("div",null,[h,b,c,n(" more "),s])}const f=i(t,[["render",u],["__file","2305140023.html.vue"]]);export{f as default}; +import{_ as i,o as r,c as o,d as n,a as e,b as a,e as d}from"./app-6a63891c.js";const t={},h=e("h1",{id:"dubbo-基本介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#dubbo-基本介绍","aria-hidden":"true"},"#"),a(" Dubbo 基本介绍")],-1),b=e("p",null,[a("Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用"),e("br"),a(" Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。")],-1),c=e("p",null,"在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。",-1),s=d('

Dubbo一开始的定位就是RPC,专注于两个服务之间的调用。但随着微服务的盛行,除开服务调用之外,Dubbo也在逐步的涉猎服务治理、服务监控、服务网关等等,所以现在的Dubbo目标已经不止是RPC框架了,而是和Spring Cloud类似想成为了一个服务框架

核心组件

服务提供者(Provider)

作为服务的提供者,将自己的服务注册到注册中心,接收消费者的调用请求并提供相应的服务实现。提供者通常是一个独立的应用程序,它将自己的服务暴露给消费者。

服务消费者(Consumer)

从注册中心获取服务提供者的地址,并发起远程调用请求

注册中心(Register)

用于服务的注册与发现,提供服务地址的管理

监控中心(Monitor)

实时监控和统计分析服务的运行情况和性能指标,帮助用户更好地管理和优化分布式服务,提高系统的可靠性和可用性

基本原理

基本原理
基本原理

架构

从抽象架构上分为两层:服务治理抽象控制面 和 Dubbo 数据面

架构
架构

服务治理控制面

服务治理控制面不是特指如注册中心类的单个具体组件,而是对 Dubbo 治理体系的抽象表达。控制面包含协调服务发现的注册中心、流量管控策略、Dubbo Admin 控制台等,如果采用了 Service Mesh 架构则还包含 Istio
等服务网格控制面。

Dubbo数据面

数据面代表集群部署的所有 Dubbo 进程,进程之间通过 RPC 协议实现数据交换,Dubbo 定义了微服务应用开发与调用规范并负责完成数据传输的编解码工作

  • 服务消费者 (Dubbo Consumer),发起业务调用或 RPC 通信的 Dubbo 进程
  • 服务提供者 (Dubbo Provider),接收业务调用或 RPC 通信的 Dubbo 进程

通信协议

Dubbo 从设计上不绑定任何一款特定通信协议,HTTP/2、REST、gRPC、JsonRPC、Thrift、Hessian2 等几乎所有主流的通信协议,Dubbo 框架都可以提供支持。

这样的 Protocol 设计模式给构建微服务带来了最大的灵活性,开发者可以根据需要如性能、通用型等选择不同的通信协议,不再需要任何的代理来实现协议转换,甚至你还可以通过 Dubbo 实现不同协议间的迁移。

',24);function u(l,p){return r(),o("div",null,[h,b,c,n(" more "),s])}const f=i(t,[["render",u],["__file","2305140023.html.vue"]]);export{f as default}; diff --git a/assets/2305140023.html-2b296e15.js b/assets/2305140023.html-aaa7b4f6.js similarity index 94% rename from assets/2305140023.html-2b296e15.js rename to assets/2305140023.html-aaa7b4f6.js index e865a69f..b473c8bd 100644 --- a/assets/2305140023.html-2b296e15.js +++ b/assets/2305140023.html-aaa7b4f6.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-a7a8f066","path":"/note/microservices/dubbo/2305140023.html","title":"Dubbo 基本介绍","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-13T00:00:00.000Z","order":1,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"核心组件","slug":"核心组件","link":"#核心组件","children":[{"level":3,"title":"服务提供者(Provider)","slug":"服务提供者-provider","link":"#服务提供者-provider","children":[]},{"level":3,"title":"服务消费者(Consumer)","slug":"服务消费者-consumer","link":"#服务消费者-consumer","children":[]},{"level":3,"title":"注册中心(Register)","slug":"注册中心-register","link":"#注册中心-register","children":[]},{"level":3,"title":"监控中心(Monitor)","slug":"监控中心-monitor","link":"#监控中心-monitor","children":[]},{"level":3,"title":"基本原理","slug":"基本原理","link":"#基本原理","children":[]}]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[{"level":3,"title":"服务治理控制面","slug":"服务治理控制面","link":"#服务治理控制面","children":[]},{"level":3,"title":"Dubbo数据面","slug":"dubbo数据面","link":"#dubbo数据面","children":[]}]},{"level":2,"title":"通信协议","slug":"通信协议","link":"#通信协议","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.96,"words":888},"filePathRelative":"note/microservices/dubbo/2305140023.md","localizedDate":"2023年5月13日","excerpt":"

Dubbo 基本介绍

\\n

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
\\nDubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

\\n

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-a7a8f066","path":"/note/microservices/dubbo/2305140023.html","title":"Dubbo 基本介绍","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-13T00:00:00.000Z","order":1,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"核心组件","slug":"核心组件","link":"#核心组件","children":[{"level":3,"title":"服务提供者(Provider)","slug":"服务提供者-provider","link":"#服务提供者-provider","children":[]},{"level":3,"title":"服务消费者(Consumer)","slug":"服务消费者-consumer","link":"#服务消费者-consumer","children":[]},{"level":3,"title":"注册中心(Register)","slug":"注册中心-register","link":"#注册中心-register","children":[]},{"level":3,"title":"监控中心(Monitor)","slug":"监控中心-monitor","link":"#监控中心-monitor","children":[]},{"level":3,"title":"基本原理","slug":"基本原理","link":"#基本原理","children":[]}]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[{"level":3,"title":"服务治理控制面","slug":"服务治理控制面","link":"#服务治理控制面","children":[]},{"level":3,"title":"Dubbo数据面","slug":"dubbo数据面","link":"#dubbo数据面","children":[]}]},{"level":2,"title":"通信协议","slug":"通信协议","link":"#通信协议","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":2.96,"words":888},"filePathRelative":"note/microservices/dubbo/2305140023.md","localizedDate":"2023年5月13日","excerpt":"

Dubbo 基本介绍

\\n

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
\\nDubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

\\n

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305140203.html-391123eb.js b/assets/2305140203.html-d122f931.js similarity index 91% rename from assets/2305140203.html-391123eb.js rename to assets/2305140203.html-d122f931.js index b3aa6bc9..790a03e6 100644 --- a/assets/2305140203.html-391123eb.js +++ b/assets/2305140203.html-d122f931.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-12216b09","path":"/note/microservices/dubbo/2305140203.html","title":"功能支持","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-14T00:00:00.000Z","order":2,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"负载均衡","slug":"负载均衡","link":"#负载均衡","children":[]},{"level":2,"title":"服务发现","slug":"服务发现","link":"#服务发现","children":[]},{"level":2,"title":"服务超时","slug":"服务超时","link":"#服务超时","children":[]},{"level":2,"title":"集群容错","slug":"集群容错","link":"#集群容错","children":[]},{"level":2,"title":"服务降级","slug":"服务降级","link":"#服务降级","children":[]},{"level":2,"title":"参数回调","slug":"参数回调","link":"#参数回调","children":[]},{"level":2,"title":"异步调用","slug":"异步调用","link":"#异步调用","children":[]},{"level":2,"title":"泛化调用","slug":"泛化调用","link":"#泛化调用","children":[]},{"level":2,"title":"流量管控","slug":"流量管控","link":"#流量管控","children":[]},{"level":2,"title":"REST","slug":"rest","link":"#rest","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.2,"words":60},"filePathRelative":"note/microservices/dubbo/2305140203.md","localizedDate":"2023年5月14日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-12216b09","path":"/note/microservices/dubbo/2305140203.html","title":"功能支持","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-14T00:00:00.000Z","order":2,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"负载均衡","slug":"负载均衡","link":"#负载均衡","children":[]},{"level":2,"title":"服务发现","slug":"服务发现","link":"#服务发现","children":[]},{"level":2,"title":"服务超时","slug":"服务超时","link":"#服务超时","children":[]},{"level":2,"title":"集群容错","slug":"集群容错","link":"#集群容错","children":[]},{"level":2,"title":"服务降级","slug":"服务降级","link":"#服务降级","children":[]},{"level":2,"title":"参数回调","slug":"参数回调","link":"#参数回调","children":[]},{"level":2,"title":"异步调用","slug":"异步调用","link":"#异步调用","children":[]},{"level":2,"title":"泛化调用","slug":"泛化调用","link":"#泛化调用","children":[]},{"level":2,"title":"流量管控","slug":"流量管控","link":"#流量管控","children":[]},{"level":2,"title":"REST","slug":"rest","link":"#rest","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.2,"words":60},"filePathRelative":"note/microservices/dubbo/2305140203.md","localizedDate":"2023年5月14日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305140203.html-612f47fa.js b/assets/2305140203.html-ee51bbb4.js similarity index 95% rename from assets/2305140203.html-612f47fa.js rename to assets/2305140203.html-ee51bbb4.js index 3e302498..bb7fe534 100644 --- a/assets/2305140203.html-612f47fa.js +++ b/assets/2305140203.html-ee51bbb4.js @@ -1 +1 @@ -import{_ as a,o as e,c as h,e as r}from"./app-1efcbe9f.js";const d={},i=r('

功能支持

负载均衡

服务发现

服务超时

集群容错

服务降级

参数回调

异步调用

泛化调用

流量管控

REST

源码地址

',12),n=[i];function t(c,s){return e(),h("div",null,n)}const l=a(d,[["render",t],["__file","2305140203.html.vue"]]);export{l as default}; +import{_ as a,o as e,c as h,e as r}from"./app-6a63891c.js";const d={},i=r('

功能支持

负载均衡

服务发现

服务超时

集群容错

服务降级

参数回调

异步调用

泛化调用

流量管控

REST

源码地址

',12),n=[i];function t(c,s){return e(),h("div",null,n)}const l=a(d,[["render",t],["__file","2305140203.html.vue"]]);export{l as default}; diff --git a/assets/2305140206.html-132196df.js b/assets/2305140206.html-9c1ba303.js similarity index 96% rename from assets/2305140206.html-132196df.js rename to assets/2305140206.html-9c1ba303.js index 3c2a04b3..eaad4ecb 100644 --- a/assets/2305140206.html-132196df.js +++ b/assets/2305140206.html-9c1ba303.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-173ff4e6","path":"/note/microservices/dubbo/2305140206.html","title":"SPI扩展及其源码","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-14T00:00:00.000Z","order":3,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"核心对象(ExtensionLoader)","slug":"核心对象-extensionloader","link":"#核心对象-extensionloader","children":[{"level":3,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"源码","slug":"源码","link":"#源码","children":[{"level":3,"title":"扩展点加载器","slug":"扩展点加载器","link":"#扩展点加载器","children":[]},{"level":3,"title":"获取扩展点","slug":"获取扩展点","link":"#获取扩展点","children":[]},{"level":3,"title":"创建扩展点(createExtension)","slug":"创建扩展点-createextension","link":"#创建扩展点-createextension","children":[]},{"level":3,"title":"包装器(AOP)","slug":"包装器-aop","link":"#包装器-aop","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.96,"words":2088},"filePathRelative":"note/microservices/dubbo/2305140206.md","localizedDate":"2023年5月14日","excerpt":"

SPI扩展及其源码

\\n

dubbo的扩展点实现与JAVA中实现的扩展点类似,但是功能比JAVA提供的会强大一些。 本次文章简单记录Dubbo SPI扩展点的使用以及核心原理,通过对源码的解读学习dubbo spi扩展点的核心实现以及内部的使用。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-173ff4e6","path":"/note/microservices/dubbo/2305140206.html","title":"SPI扩展及其源码","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-14T00:00:00.000Z","order":3,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"核心对象(ExtensionLoader)","slug":"核心对象-extensionloader","link":"#核心对象-extensionloader","children":[{"level":3,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"源码","slug":"源码","link":"#源码","children":[{"level":3,"title":"扩展点加载器","slug":"扩展点加载器","link":"#扩展点加载器","children":[]},{"level":3,"title":"获取扩展点","slug":"获取扩展点","link":"#获取扩展点","children":[]},{"level":3,"title":"创建扩展点(createExtension)","slug":"创建扩展点-createextension","link":"#创建扩展点-createextension","children":[]},{"level":3,"title":"包装器(AOP)","slug":"包装器-aop","link":"#包装器-aop","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.96,"words":2088},"filePathRelative":"note/microservices/dubbo/2305140206.md","localizedDate":"2023年5月14日","excerpt":"

SPI扩展及其源码

\\n

dubbo的扩展点实现与JAVA中实现的扩展点类似,但是功能比JAVA提供的会强大一些。 本次文章简单记录Dubbo SPI扩展点的使用以及核心原理,通过对源码的解读学习dubbo spi扩展点的核心实现以及内部的使用。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305140206.html-bf63ad54.js b/assets/2305140206.html-e9afa266.js similarity index 99% rename from assets/2305140206.html-bf63ad54.js rename to assets/2305140206.html-e9afa266.js index 29b91d8e..9b630fd1 100644 --- a/assets/2305140206.html-bf63ad54.js +++ b/assets/2305140206.html-e9afa266.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"spi扩展及其源码",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spi扩展及其源码","aria-hidden":"true"},"#"),e(" SPI扩展及其源码")],-1),i=n("p",null,"dubbo的扩展点实现与JAVA中实现的扩展点类似,但是功能比JAVA提供的会强大一些。 本次文章简单记录Dubbo SPI扩展点的使用以及核心原理,通过对源码的解读学习dubbo spi扩展点的核心实现以及内部的使用。",-1),u=o(`

SPI 扩展点的实现,都是基于接口来实现的,所以扩展点都是通过实现某个接口来进行扩展的。

核心对象(ExtensionLoader)

ExtensionLoader表示某个接口的扩展点加载器,可以用来加载某个扩展点实例。

核心属性

  • extensionInstances:用来缓存某个接口类型所对应的ExtensionLoader实例
  • type:表示当前ExtensionLoader实例是那个接口的扩展点加载器
  • objectFactory:扩展点工厂,可以通过工厂获取某个扩展点的具体对象实例

使用

  • 新建maven工程,并引入dubbo相关依赖
  • 在resource包下创建子包:MEAT-INF.dubbo
  • 在dubbo包下创建扩展点文件,文件名为扩展点的全类名(包含包名和接口名)
  • 在文件内填写扩展点实现类的全类名以及映射关系

当以上步骤都完成以后,在启动类main方法中通过dubbo的扩展点加载器来加载扩展点

    // 获取加载扩展点加载器
+import{_ as s,o as a,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"spi扩展及其源码",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spi扩展及其源码","aria-hidden":"true"},"#"),e(" SPI扩展及其源码")],-1),i=n("p",null,"dubbo的扩展点实现与JAVA中实现的扩展点类似,但是功能比JAVA提供的会强大一些。 本次文章简单记录Dubbo SPI扩展点的使用以及核心原理,通过对源码的解读学习dubbo spi扩展点的核心实现以及内部的使用。",-1),u=o(`

SPI 扩展点的实现,都是基于接口来实现的,所以扩展点都是通过实现某个接口来进行扩展的。

核心对象(ExtensionLoader)

ExtensionLoader表示某个接口的扩展点加载器,可以用来加载某个扩展点实例。

核心属性

  • extensionInstances:用来缓存某个接口类型所对应的ExtensionLoader实例
  • type:表示当前ExtensionLoader实例是那个接口的扩展点加载器
  • objectFactory:扩展点工厂,可以通过工厂获取某个扩展点的具体对象实例

使用

  • 新建maven工程,并引入dubbo相关依赖
  • 在resource包下创建子包:MEAT-INF.dubbo
  • 在dubbo包下创建扩展点文件,文件名为扩展点的全类名(包含包名和接口名)
  • 在文件内填写扩展点实现类的全类名以及映射关系

当以上步骤都完成以后,在启动类main方法中通过dubbo的扩展点加载器来加载扩展点

    // 获取加载扩展点加载器
     ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
     // 获取扩展点实现
     Car car = extensionLoader.getExtension("red");
diff --git a/assets/2305172157.html-ad0f4179.js b/assets/2305172157.html-ed5c9cb7.js
similarity index 91%
rename from assets/2305172157.html-ad0f4179.js
rename to assets/2305172157.html-ed5c9cb7.js
index f5de02ef..213a3fac 100644
--- a/assets/2305172157.html-ad0f4179.js
+++ b/assets/2305172157.html-ed5c9cb7.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-82100b6c","path":"/note/microservices/dubbo/2305172157.html","title":"Dubbo 整合 Spring","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-17T00:00:00.000Z","order":4,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"启动类","slug":"启动类","link":"#启动类","children":[]},{"level":2,"title":"@EnableDubbo","slug":"enabledubbo","link":"#enabledubbo","children":[]},{"level":2,"title":"@EnableDubboConfig","slug":"enabledubboconfig","link":"#enabledubboconfig","children":[{"level":3,"title":"解析properties","slug":"解析properties","link":"#解析properties","children":[]}]},{"level":2,"title":"@DubboComponentScan","slug":"dubbocomponentscan","link":"#dubbocomponentscan","children":[{"level":3,"title":"ServiceAnnotationBeanPostProcessor","slug":"serviceannotationbeanpostprocessor","link":"#serviceannotationbeanpostprocessor","children":[]},{"level":3,"title":"ReferenceAnnotationBeanPostProcessor","slug":"referenceannotationbeanpostprocessor","link":"#referenceannotationbeanpostprocessor","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.15,"words":1545},"filePathRelative":"note/microservices/dubbo/2305172157.md","localizedDate":"2023年5月17日","excerpt":"

Dubbo 整合 Spring

\\n

本文主要记录学习Dubbo 整合 Spring 的源码笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-82100b6c","path":"/note/microservices/dubbo/2305172157.html","title":"Dubbo 整合 Spring","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-17T00:00:00.000Z","order":4,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"启动类","slug":"启动类","link":"#启动类","children":[]},{"level":2,"title":"@EnableDubbo","slug":"enabledubbo","link":"#enabledubbo","children":[]},{"level":2,"title":"@EnableDubboConfig","slug":"enabledubboconfig","link":"#enabledubboconfig","children":[{"level":3,"title":"解析properties","slug":"解析properties","link":"#解析properties","children":[]}]},{"level":2,"title":"@DubboComponentScan","slug":"dubbocomponentscan","link":"#dubbocomponentscan","children":[{"level":3,"title":"ServiceAnnotationBeanPostProcessor","slug":"serviceannotationbeanpostprocessor","link":"#serviceannotationbeanpostprocessor","children":[]},{"level":3,"title":"ReferenceAnnotationBeanPostProcessor","slug":"referenceannotationbeanpostprocessor","link":"#referenceannotationbeanpostprocessor","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.15,"words":1545},"filePathRelative":"note/microservices/dubbo/2305172157.md","localizedDate":"2023年5月17日","excerpt":"

Dubbo 整合 Spring

\\n

本文主要记录学习Dubbo 整合 Spring 的源码笔记

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305172157.html-46e73c5d.js b/assets/2305172157.html-fb09971d.js similarity index 99% rename from assets/2305172157.html-46e73c5d.js rename to assets/2305172157.html-fb09971d.js index 603c2937..8bb9d7b5 100644 --- a/assets/2305172157.html-46e73c5d.js +++ b/assets/2305172157.html-fb09971d.js @@ -1,4 +1,4 @@ -import{_ as t,r as o,o as p,c as i,d as c,a as n,b as s,f as l,w as u,e as a}from"./app-1efcbe9f.js";const r={},k=n("h1",{id:"dubbo-整合-spring",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#dubbo-整合-spring","aria-hidden":"true"},"#"),s(" Dubbo 整合 Spring")],-1),d=n("p",null,"本文主要记录学习Dubbo 整合 Spring 的源码笔记",-1),b=a(`

启动类

public class Application {
+import{_ as t,r as o,o as p,c as i,d as c,a as n,b as s,f as l,w as u,e as a}from"./app-6a63891c.js";const r={},k=n("h1",{id:"dubbo-整合-spring",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#dubbo-整合-spring","aria-hidden":"true"},"#"),s(" Dubbo 整合 Spring")],-1),d=n("p",null,"本文主要记录学习Dubbo 整合 Spring 的源码笔记",-1),b=a(`

启动类

public class Application {
     public static void main(String[] args) throws Exception {
         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
         context.start();
diff --git a/assets/2305191622.html-3205e6b6.js b/assets/2305191622.html-443278ce.js
similarity index 96%
rename from assets/2305191622.html-3205e6b6.js
rename to assets/2305191622.html-443278ce.js
index 9b03c71e..39920c7a 100644
--- a/assets/2305191622.html-3205e6b6.js
+++ b/assets/2305191622.html-443278ce.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-73716104","path":"/note/microservices/dubbo/2305191622.html","title":"服务导出(服务注册)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-22T00:00:00.000Z","order":5,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"大体流程","slug":"大体流程","link":"#大体流程","children":[]},{"level":2,"title":"checkAndUpdateSubConfigs()","slug":"checkandupdatesubconfigs","link":"#checkandupdatesubconfigs","children":[{"level":3,"title":"刷新ServiceConfig配置的源码","slug":"刷新serviceconfig配置的源码","link":"#刷新serviceconfig配置的源码","children":[]},{"level":3,"title":"export","slug":"export","link":"#export","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]}]},{"level":2,"title":"doExport()","slug":"doexport","link":"#doexport","children":[{"level":3,"title":"loadRegistries()","slug":"loadregistries","link":"#loadregistries","children":[]},{"level":3,"title":"doExportUrlsFor1Protocol()","slug":"doexporturlsfor1protocol","link":"#doexporturlsfor1protocol","children":[]},{"level":3,"title":"doLocalExport()","slug":"dolocalexport","link":"#dolocalexport","children":[]}]},{"level":2,"title":"服务提供者监听配置更新","slug":"服务提供者监听配置更新","link":"#服务提供者监听配置更新","children":[]},{"level":2,"title":"补充","slug":"补充","link":"#补充","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.8,"words":1741},"filePathRelative":"note/microservices/dubbo/2305191622.md","localizedDate":"2023年5月22日","excerpt":"

服务导出(服务注册)

\\n

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-73716104","path":"/note/microservices/dubbo/2305191622.html","title":"服务导出(服务注册)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-22T00:00:00.000Z","order":5,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[{"level":2,"title":"大体流程","slug":"大体流程","link":"#大体流程","children":[]},{"level":2,"title":"checkAndUpdateSubConfigs()","slug":"checkandupdatesubconfigs","link":"#checkandupdatesubconfigs","children":[{"level":3,"title":"刷新ServiceConfig配置的源码","slug":"刷新serviceconfig配置的源码","link":"#刷新serviceconfig配置的源码","children":[]},{"level":3,"title":"export","slug":"export","link":"#export","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]}]},{"level":2,"title":"doExport()","slug":"doexport","link":"#doexport","children":[{"level":3,"title":"loadRegistries()","slug":"loadregistries","link":"#loadregistries","children":[]},{"level":3,"title":"doExportUrlsFor1Protocol()","slug":"doexporturlsfor1protocol","link":"#doexporturlsfor1protocol","children":[]},{"level":3,"title":"doLocalExport()","slug":"dolocalexport","link":"#dolocalexport","children":[]}]},{"level":2,"title":"服务提供者监听配置更新","slug":"服务提供者监听配置更新","link":"#服务提供者监听配置更新","children":[]},{"level":2,"title":"补充","slug":"补充","link":"#补充","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.8,"words":1741},"filePathRelative":"note/microservices/dubbo/2305191622.md","localizedDate":"2023年5月22日","excerpt":"

服务导出(服务注册)

\\n

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305191622.html-ffaebf4d.js b/assets/2305191622.html-c69c5b9c.js similarity index 99% rename from assets/2305191622.html-ffaebf4d.js rename to assets/2305191622.html-c69c5b9c.js index d8b9b925..c5129a7b 100644 --- a/assets/2305191622.html-ffaebf4d.js +++ b/assets/2305191622.html-c69c5b9c.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as e,a as n,b as p,e as o}from"./app-1efcbe9f.js";const i={},c=n("h1",{id:"服务导出-服务注册",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#服务导出-服务注册","aria-hidden":"true"},"#"),p(" 服务导出(服务注册)")],-1),l=n("p",null,"本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。",-1),u=o(`

服务导出(服务注册)的核心方法是ServiceBean对象中监听器里的export();调用时机是在Spring容器启动完成之后发布的完成事件,
ServiceBean通过监听该事件然后去进行服务导出(服务注册)。

    @Override
+import{_ as s,o as a,c as t,d as e,a as n,b as p,e as o}from"./app-6a63891c.js";const i={},c=n("h1",{id:"服务导出-服务注册",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#服务导出-服务注册","aria-hidden":"true"},"#"),p(" 服务导出(服务注册)")],-1),l=n("p",null,"本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。",-1),u=o(`

服务导出(服务注册)的核心方法是ServiceBean对象中监听器里的export();调用时机是在Spring容器启动完成之后发布的完成事件,
ServiceBean通过监听该事件然后去进行服务导出(服务注册)。

    @Override
     public void onApplicationEvent(ContextRefreshedEvent event) {
         // 当前服务没有被导出并且没有卸载,才导出服务
         if (!isExported() && !isUnexported()) {
diff --git a/assets/2305232045.html-7551cbd9.js b/assets/2305232045.html-93c9c12d.js
similarity index 81%
rename from assets/2305232045.html-7551cbd9.js
rename to assets/2305232045.html-93c9c12d.js
index 8e1692d2..f91109cc 100644
--- a/assets/2305232045.html-7551cbd9.js
+++ b/assets/2305232045.html-93c9c12d.js
@@ -1 +1 @@
-import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"服务发现",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#服务发现","aria-hidden":"true"},"#"),o(" 服务发现")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2305232045.html.vue"]]);export{l as default};
+import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"服务发现",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#服务发现","aria-hidden":"true"},"#"),o(" 服务发现")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2305232045.html.vue"]]);export{l as default};
diff --git a/assets/2305232045.html-266a3660.js b/assets/2305232045.html-ebef571a.js
similarity index 79%
rename from assets/2305232045.html-266a3660.js
rename to assets/2305232045.html-ebef571a.js
index f28505d4..24a31ea9 100644
--- a/assets/2305232045.html-266a3660.js
+++ b/assets/2305232045.html-ebef571a.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-3af4619e","path":"/note/microservices/dubbo/2305232045.html","title":"服务发现","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-24T00:00:00.000Z","order":6,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.06,"words":19},"filePathRelative":"note/microservices/dubbo/2305232045.md","localizedDate":"2023年5月24日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-3af4619e","path":"/note/microservices/dubbo/2305232045.html","title":"服务发现","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-24T00:00:00.000Z","order":6,"category":["微服务","RPC"],"tag":["dubbo"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.06,"words":19},"filePathRelative":"note/microservices/dubbo/2305232045.md","localizedDate":"2023年5月24日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2305252031.html-fa13474f.js b/assets/2305252031.html-6d81b955.js
similarity index 96%
rename from assets/2305252031.html-fa13474f.js
rename to assets/2305252031.html-6d81b955.js
index e39b2b96..9386941b 100644
--- a/assets/2305252031.html-fa13474f.js
+++ b/assets/2305252031.html-6d81b955.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-55cc2415","path":"/note/java/concurrency/atomic/2305252031.html","title":"ThreadLocal","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-25T00:00:00.000Z","order":1,"category":["并发"],"tag":["ThreadLocal"]},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"开放地址法","slug":"开放地址法","link":"#开放地址法","children":[]}]},{"level":2,"title":"内存泄露问题","slug":"内存泄露问题","link":"#内存泄露问题","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":11.25,"words":3375},"filePathRelative":"note/java/concurrency/atomic/2305252031.md","localizedDate":"2023年5月25日","excerpt":"

ThreadLocal

\\n

ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。

\\n

ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。

\\n

使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-55cc2415","path":"/note/java/concurrency/atomic/2305252031.html","title":"ThreadLocal","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-25T00:00:00.000Z","order":1,"category":["并发"],"tag":["ThreadLocal"]},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"作用","slug":"作用","link":"#作用","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"开放地址法","slug":"开放地址法","link":"#开放地址法","children":[]}]},{"level":2,"title":"内存泄露问题","slug":"内存泄露问题","link":"#内存泄露问题","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":11.25,"words":3375},"filePathRelative":"note/java/concurrency/atomic/2305252031.md","localizedDate":"2023年5月25日","excerpt":"

ThreadLocal

\\n

ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。

\\n

ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。

\\n

使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305252031.html-3a3e12e9.js b/assets/2305252031.html-78ce716c.js similarity index 99% rename from assets/2305252031.html-3a3e12e9.js rename to assets/2305252031.html-78ce716c.js index 28cfdc1b..09619a70 100644 --- a/assets/2305252031.html-3a3e12e9.js +++ b/assets/2305252031.html-78ce716c.js @@ -1,4 +1,4 @@ -import{_ as n,o as s,c as e,d as t,a,b as c,e as p}from"./app-1efcbe9f.js";const o={},l=a("h1",{id:"threadlocal",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#threadlocal","aria-hidden":"true"},"#"),c(" ThreadLocal")],-1),i=a("p",null,"ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。",-1),r=a("p",null,"ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。",-1),d=a("p",null,"使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。",-1),u=p(`

使用

public class UserContext {
+import{_ as n,o as s,c as e,d as t,a,b as c,e as p}from"./app-6a63891c.js";const o={},l=a("h1",{id:"threadlocal",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#threadlocal","aria-hidden":"true"},"#"),c(" ThreadLocal")],-1),i=a("p",null,"ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。",-1),r=a("p",null,"ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。",-1),d=a("p",null,"使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。",-1),u=p(`

使用

public class UserContext {
     private static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
 
     public static void setUser(User user) {
diff --git a/assets/2305281438.html-da0bbf09.js b/assets/2305281438.html-07e8ac89.js
similarity index 99%
rename from assets/2305281438.html-da0bbf09.js
rename to assets/2305281438.html-07e8ac89.js
index e82c28ce..168b4630 100644
--- a/assets/2305281438.html-da0bbf09.js
+++ b/assets/2305281438.html-07e8ac89.js
@@ -1,4 +1,4 @@
-import{_ as a,o as s,c as e,d as t,a as n,b as o,e as c}from"./app-1efcbe9f.js";const i={},p=n("h1",{id:"atomic原子操作详解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#atomic原子操作详解","aria-hidden":"true"},"#"),o(" Atomic原子操作详解")],-1),l=n("p",null,"原子操作是在并发编程中指不能被中断的、不可分割的操作。它要么完全执行,要么完全不执行,不会出现部分执行的情况。",-1),d=c(`

并发原子操作理解

在并发编程中,原子操作是指不可被中断的、不可分割的最小操作单位。原子操作要么完全执行,要么完全不执行,不存在中间状态。

在多线程环境下,多个线程可以同时执行,因此可能出现竞争条件(Race Condition),即多个线程同时访问和修改共享的数据,导致数据不一致或不确定的结果。
原子操作可以帮助我们解决这个问题,通过确保某个操作以原子方式执行,从而避免了竞争条件。

在Java中,你可以通过以下几种方式来实现并发原子操作:

  1. 使用原子类:Java提供了java.util.concurrent.atomic包,其中包含了一系列的原子类,如AtomicInteger、AtomicLong等。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。通过使用这些原子类,你可以在多线程环境中实现对共享数据的安全访问和修改。
  2. 使用锁:通过使用锁机制,比如synchronized关键字或ReentrantLock类,你可以确保同一时间只有一个线程可以访问被锁定的代码块或资源。通过在关键的代码段上加锁,可以保证原子性操作的执行。只有持有锁的线程才能执行被锁定的代码块,其他线程必须等待锁释放。
  3. 使用volatile关键字:将共享变量声明为volatile,可以确保变量的读取和写入操作具有可见性和有序性。volatile关键字保证了变量在多个线程之间的一致性,每次对volatile变量的读取都是从主内存中获取最新值,每次对volatile变量的写入都会立即刷新到主内存。
  4. 使用原子操作工具类:Java还提供了一些原子操作的工具类,如java.util.concurrent.atomic.AtomicIntegerArray和java.util.concurrent.atomic.AtomicReference等。这些工具类提供了对数组和引用类型的原子操作支持,可以在并发环境中实现对数组元素或引用对象的原子操作。

synchronized VS atomic

synchronized

synchronized 是 Java 中的关键字,用于实现线程同步和互斥访问共享资源。
通过使用synchronized关键字修饰代码块或方法,可以确保同一时间只有一个线程可以执行被锁定的代码块或方法。synchronized关键字提供了内置的互斥机制,它会自动获取锁和释放锁,保证了代码块或方法的原子性操作。在使用synchronized时,需要注意锁的粒度和范围,以避免死锁和性能问题。

atomic

atomic 是 Java 并发包 (java.util.concurrent.atomic)中提供的一组原子类。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。它们通过使用底层的CAS(Compare-and-Swap)操作来实现线程安全的原子操作。CAS
是一种乐观锁机制,它利用硬件的原子性操作来实现对共享变量的并发修改。原子类中的方法都是原子的,不需要显式加锁,因此可以在高并发环境中获得较好的性能。使用原子类可以避免使用锁带来的开销和潜在的死锁问题。

选择使用 synchronized 还是 atomic 取决于具体的需求和场景。一般来说:

  • 当只需要在特定的代码块或方法上实现原子操作时,可以选择使用synchronized。它更适合于复杂的同步逻辑和资源访问控制。
  • 当只需要对单个变量进行原子操作时,可以选择使用atomic。它更适合于简单的操作,并且在高并发环境中性能更好。

atomic实现原子操作

java.util.concurrent.atomic 包中的原子类通过底层的CAS(Compare-and-Swap)操作来实现原子操作。CAS 是一种乐观锁机制,它利用硬件原子性操作来实现对共享变量的并发修改。

CAS实现原理

CAS 操作包含三个参数:内存位置(变量)、期望值和新值。CAS 操作的执行过程如下:

  • 通过读取内存位置的值,获取当前的变量值。
  • 比较当前的变量值与期望值是否相等。如果相等,则说明变量值没有被其他线程修改,可以执行更新操作。 将新值写入内存位置,更新变量的值。
  • 如果不相等,说明其他线程已经修改了变量的值,当前线程的更新操作失败。此时可以选择重试或执行其他逻辑。

CAS 操作是原子的,因为在执行比较和写入操作期间,不会被其他线程中断或干扰。它依赖于底层硬件的原子性操作,例如处理器提供的原子指令(例如 Compare-and-Swap、Load-Link/Store-Conditional)。

CAS存在的问题

自旋重试

CAS 操作失败时,线程需要不断地自旋重试,直到成功为止。这会消耗一定的 CPU 资源。在高并发情况下,多个线程同时进行 CAS 操作可能会导致大量的自旋重试,从而增加系统负载。

ABA 问题

CAS 操作只关注变量的值是否与期望值相等,而不考虑变量值在操作期间是否发生了变化。这可能引发 ABA 问题。例如,线程 A 将变量从初始值 A 修改为 B,然后再修改回 A,而线程 B 在此期间执行了一个操作,期望变量的值为 A,CAS操作会认为符合条件,导致操作成功。然而,线程 A 并不知道变量的值已经被修改过。解决 ABA 问题的方法之一是使用带有版本号的原子类(如AtomicStampedReference),以便在比较时同时考虑变量值和版本号。

只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,可以使用循环CAS的方式保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原则性。

从JDk5开始提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里进行CAS操作

竞争条件

在高并发环境下,多个线程同时进行 CAS 操作,竞争同一个变量,可能会导致竞争条件的发生。如果多个线程同时执行 CAS 操作,只有一个线程的操作会成功,其他线程的操作会失败并进行重试。这种竞争会降低整体的性能。

内存模型限制

CAS 操作依赖于底层硬件的原子性指令,因此对内存模型的限制较大。在一些特殊的内存模型或平台上,CAS 操作可能不具备原子性,或者在使用过程中需要特殊的配置或处理。

LongAdder

LongAdder是JDK8时引入的一个原子类。主要解决的是在高并发环境下热点数据读写,对原子属性进行写操作时,通过写热点分散,减少竞争,它可以提供更好的性能和吞吐量。

与AtomicLong相比,LongAdder在高并发环境下通常具有更好的性能其原理就是因为LongAdder内部维护了一组变量,将计数器的值分散到这些变量中,不同线程对不同变量进行累加操作,从而减少了竞争。当需要获取计数器的总和时,LongAdder会将所有变量的值累加起来,得到最终的结果。

需要注意的是,LongAdder在某些情况下可能会产生略微的误差,因为它的结果是根据当前的内部状态计算得出的,并且可能会受到并发更新的影响。如果需要精确的结果,可以使用LongAccumulator或AtomicLong等其他适用的原子类。

import java.util.concurrent.atomic.LongAdder;
+import{_ as a,o as s,c as e,d as t,a as n,b as o,e as c}from"./app-6a63891c.js";const i={},p=n("h1",{id:"atomic原子操作详解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#atomic原子操作详解","aria-hidden":"true"},"#"),o(" Atomic原子操作详解")],-1),l=n("p",null,"原子操作是在并发编程中指不能被中断的、不可分割的操作。它要么完全执行,要么完全不执行,不会出现部分执行的情况。",-1),d=c(`

并发原子操作理解

在并发编程中,原子操作是指不可被中断的、不可分割的最小操作单位。原子操作要么完全执行,要么完全不执行,不存在中间状态。

在多线程环境下,多个线程可以同时执行,因此可能出现竞争条件(Race Condition),即多个线程同时访问和修改共享的数据,导致数据不一致或不确定的结果。
原子操作可以帮助我们解决这个问题,通过确保某个操作以原子方式执行,从而避免了竞争条件。

在Java中,你可以通过以下几种方式来实现并发原子操作:

  1. 使用原子类:Java提供了java.util.concurrent.atomic包,其中包含了一系列的原子类,如AtomicInteger、AtomicLong等。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。通过使用这些原子类,你可以在多线程环境中实现对共享数据的安全访问和修改。
  2. 使用锁:通过使用锁机制,比如synchronized关键字或ReentrantLock类,你可以确保同一时间只有一个线程可以访问被锁定的代码块或资源。通过在关键的代码段上加锁,可以保证原子性操作的执行。只有持有锁的线程才能执行被锁定的代码块,其他线程必须等待锁释放。
  3. 使用volatile关键字:将共享变量声明为volatile,可以确保变量的读取和写入操作具有可见性和有序性。volatile关键字保证了变量在多个线程之间的一致性,每次对volatile变量的读取都是从主内存中获取最新值,每次对volatile变量的写入都会立即刷新到主内存。
  4. 使用原子操作工具类:Java还提供了一些原子操作的工具类,如java.util.concurrent.atomic.AtomicIntegerArray和java.util.concurrent.atomic.AtomicReference等。这些工具类提供了对数组和引用类型的原子操作支持,可以在并发环境中实现对数组元素或引用对象的原子操作。

synchronized VS atomic

synchronized

synchronized 是 Java 中的关键字,用于实现线程同步和互斥访问共享资源。
通过使用synchronized关键字修饰代码块或方法,可以确保同一时间只有一个线程可以执行被锁定的代码块或方法。synchronized关键字提供了内置的互斥机制,它会自动获取锁和释放锁,保证了代码块或方法的原子性操作。在使用synchronized时,需要注意锁的粒度和范围,以避免死锁和性能问题。

atomic

atomic 是 Java 并发包 (java.util.concurrent.atomic)中提供的一组原子类。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。它们通过使用底层的CAS(Compare-and-Swap)操作来实现线程安全的原子操作。CAS
是一种乐观锁机制,它利用硬件的原子性操作来实现对共享变量的并发修改。原子类中的方法都是原子的,不需要显式加锁,因此可以在高并发环境中获得较好的性能。使用原子类可以避免使用锁带来的开销和潜在的死锁问题。

选择使用 synchronized 还是 atomic 取决于具体的需求和场景。一般来说:

  • 当只需要在特定的代码块或方法上实现原子操作时,可以选择使用synchronized。它更适合于复杂的同步逻辑和资源访问控制。
  • 当只需要对单个变量进行原子操作时,可以选择使用atomic。它更适合于简单的操作,并且在高并发环境中性能更好。

atomic实现原子操作

java.util.concurrent.atomic 包中的原子类通过底层的CAS(Compare-and-Swap)操作来实现原子操作。CAS 是一种乐观锁机制,它利用硬件原子性操作来实现对共享变量的并发修改。

CAS实现原理

CAS 操作包含三个参数:内存位置(变量)、期望值和新值。CAS 操作的执行过程如下:

  • 通过读取内存位置的值,获取当前的变量值。
  • 比较当前的变量值与期望值是否相等。如果相等,则说明变量值没有被其他线程修改,可以执行更新操作。 将新值写入内存位置,更新变量的值。
  • 如果不相等,说明其他线程已经修改了变量的值,当前线程的更新操作失败。此时可以选择重试或执行其他逻辑。

CAS 操作是原子的,因为在执行比较和写入操作期间,不会被其他线程中断或干扰。它依赖于底层硬件的原子性操作,例如处理器提供的原子指令(例如 Compare-and-Swap、Load-Link/Store-Conditional)。

CAS存在的问题

自旋重试

CAS 操作失败时,线程需要不断地自旋重试,直到成功为止。这会消耗一定的 CPU 资源。在高并发情况下,多个线程同时进行 CAS 操作可能会导致大量的自旋重试,从而增加系统负载。

ABA 问题

CAS 操作只关注变量的值是否与期望值相等,而不考虑变量值在操作期间是否发生了变化。这可能引发 ABA 问题。例如,线程 A 将变量从初始值 A 修改为 B,然后再修改回 A,而线程 B 在此期间执行了一个操作,期望变量的值为 A,CAS操作会认为符合条件,导致操作成功。然而,线程 A 并不知道变量的值已经被修改过。解决 ABA 问题的方法之一是使用带有版本号的原子类(如AtomicStampedReference),以便在比较时同时考虑变量值和版本号。

只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,可以使用循环CAS的方式保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原则性。

从JDk5开始提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里进行CAS操作

竞争条件

在高并发环境下,多个线程同时进行 CAS 操作,竞争同一个变量,可能会导致竞争条件的发生。如果多个线程同时执行 CAS 操作,只有一个线程的操作会成功,其他线程的操作会失败并进行重试。这种竞争会降低整体的性能。

内存模型限制

CAS 操作依赖于底层硬件的原子性指令,因此对内存模型的限制较大。在一些特殊的内存模型或平台上,CAS 操作可能不具备原子性,或者在使用过程中需要特殊的配置或处理。

LongAdder

LongAdder是JDK8时引入的一个原子类。主要解决的是在高并发环境下热点数据读写,对原子属性进行写操作时,通过写热点分散,减少竞争,它可以提供更好的性能和吞吐量。

与AtomicLong相比,LongAdder在高并发环境下通常具有更好的性能其原理就是因为LongAdder内部维护了一组变量,将计数器的值分散到这些变量中,不同线程对不同变量进行累加操作,从而减少了竞争。当需要获取计数器的总和时,LongAdder会将所有变量的值累加起来,得到最终的结果。

需要注意的是,LongAdder在某些情况下可能会产生略微的误差,因为它的结果是根据当前的内部状态计算得出的,并且可能会受到并发更新的影响。如果需要精确的结果,可以使用LongAccumulator或AtomicLong等其他适用的原子类。

import java.util.concurrent.atomic.LongAdder;
 
 public class Example {
     private LongAdder counter = new LongAdder();
diff --git a/assets/2305281438.html-c1e8c165.js b/assets/2305281438.html-ed7ab2ff.js
similarity index 92%
rename from assets/2305281438.html-c1e8c165.js
rename to assets/2305281438.html-ed7ab2ff.js
index ae0d9a9d..157c2cf2 100644
--- a/assets/2305281438.html-c1e8c165.js
+++ b/assets/2305281438.html-ed7ab2ff.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-49daa846","path":"/note/java/concurrency/atomic/2305281438.html","title":"Atomic原子操作详解","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-28T00:00:00.000Z","order":2,"category":["并发"],"tag":["Atomic"]},"headers":[{"level":2,"title":"并发原子操作理解","slug":"并发原子操作理解","link":"#并发原子操作理解","children":[]},{"level":2,"title":"synchronized VS atomic","slug":"synchronized-vs-atomic","link":"#synchronized-vs-atomic","children":[{"level":3,"title":"synchronized","slug":"synchronized","link":"#synchronized","children":[]},{"level":3,"title":"atomic","slug":"atomic","link":"#atomic","children":[]}]},{"level":2,"title":"atomic实现原子操作","slug":"atomic实现原子操作","link":"#atomic实现原子操作","children":[{"level":3,"title":"CAS实现原理","slug":"cas实现原理","link":"#cas实现原理","children":[]},{"level":3,"title":"CAS存在的问题","slug":"cas存在的问题","link":"#cas存在的问题","children":[]}]},{"level":2,"title":"LongAdder","slug":"longadder","link":"#longadder","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.34,"words":2201},"filePathRelative":"note/java/concurrency/atomic/2305281438.md","localizedDate":"2023年5月28日","excerpt":"

Atomic原子操作详解

\\n

原子操作是在并发编程中指不能被中断的、不可分割的操作。它要么完全执行,要么完全不执行,不会出现部分执行的情况。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-49daa846","path":"/note/java/concurrency/atomic/2305281438.html","title":"Atomic原子操作详解","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-28T00:00:00.000Z","order":2,"category":["并发"],"tag":["Atomic"]},"headers":[{"level":2,"title":"并发原子操作理解","slug":"并发原子操作理解","link":"#并发原子操作理解","children":[]},{"level":2,"title":"synchronized VS atomic","slug":"synchronized-vs-atomic","link":"#synchronized-vs-atomic","children":[{"level":3,"title":"synchronized","slug":"synchronized","link":"#synchronized","children":[]},{"level":3,"title":"atomic","slug":"atomic","link":"#atomic","children":[]}]},{"level":2,"title":"atomic实现原子操作","slug":"atomic实现原子操作","link":"#atomic实现原子操作","children":[{"level":3,"title":"CAS实现原理","slug":"cas实现原理","link":"#cas实现原理","children":[]},{"level":3,"title":"CAS存在的问题","slug":"cas存在的问题","link":"#cas存在的问题","children":[]}]},{"level":2,"title":"LongAdder","slug":"longadder","link":"#longadder","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.34,"words":2201},"filePathRelative":"note/java/concurrency/atomic/2305281438.md","localizedDate":"2023年5月28日","excerpt":"

Atomic原子操作详解

\\n

原子操作是在并发编程中指不能被中断的、不可分割的操作。它要么完全执行,要么完全不执行,不会出现部分执行的情况。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305281702.html-a504b9bb.js b/assets/2305281702.html-0d3c8665.js similarity index 99% rename from assets/2305281702.html-a504b9bb.js rename to assets/2305281702.html-0d3c8665.js index 471fd551..82f3803c 100644 --- a/assets/2305281702.html-a504b9bb.js +++ b/assets/2305281702.html-0d3c8665.js @@ -1,4 +1,4 @@ -import{_ as a,o as e,c as p,d as t,a as n,b as s,e as c}from"./app-1efcbe9f.js";const o={},l=n("h1",{id:"线程安全",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#线程安全","aria-hidden":"true"},"#"),s(" 线程安全")],-1),i=n("p",null,[s("线程安全性是指多线程环境下,一个函数、对象或系统的行为是否可以正确地处理多个线程同时访问或修改共享的数据而不会产生不确定的结果或导致数据损坏。"),n("br"),s(" 在并发编程中,线程安全性是一个非常重要的概念,因为多线程同时操作共享资源时可能引发竞态条件(race conditions)和其他并发问题。")],-1),u=c(`

如果要实现线程安全性,就要保证我们的类是线程安全的的。在《 Java 并发 编程实战》 中, 定义“类是线程安全的”如下:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

理解

原子性(Atomicity)

指一个操作是不可中断的。一个原子操作要么完全执行成功,要么完全不执行,不会出现中间状态。在并发环境中,如果多个线程同时执行某个原子操作,那么该操作的执行结果应当与线程的执行顺序无关,保证数据的一致性。

可见性(Visibility)

指一个线程对共享数据的修改对其他线程是可见的。当一个线程修改了共享数据时,其他线程应当能够立即看到最新的修改结果,而不是看到过期或无效的数据。

实现线程安全

线程封闭

线程封闭就是把对象封装到一个线程里,只有这一个线程能看到此对象。那么这个对象就算不是线程安全的也不会出现任何安全问题。

线程封闭是一种简单有效的并发编程技术,适用于某些场景下的数据隔离需求。它能够减少线程间的竞争和同步开销,提高并发程序的性能和可靠性。然而,需要注意线程封闭可能带来的局限性,如线程安全性和数据一致性的保证,以及对并发性能的影响等。因此,在使用线程封闭时需要仔细评估场景和需求,确保其适用性和正确性。

栈封闭(Stack Confinement)

将数据保存在线程栈的局部变量中。由于局部变量的作用域仅限于所属线程的执行上下文,其他线程无法访问到该数据,因此可以避免并发访问的问题。

ThreadLocal

使用Java中的ThreadLocal类,可以将数据与当前线程关联起来,使得每个线程都有自己的数据副本。ThreadLocal提供了线程级别的数据隔离,每个线程对数据的访问都是独立的,从而避免了并发访问的问题。

单线程模型

某些情况下,可以将任务或资源限制在单个线程中进行处理,从而避免了并发访问的问题。例如,使用单个线程的事件驱动模型,或者使用单个线程的线程池来处理任务。

无状态的类

无状态的类是指不包含任何实例变量(或称为状态)的类,其行为仅依赖于传入的参数。换句话说,无状态类的方法不会受到类级别的状态影响,每次调用方法时,都只关注输入参数和方法的逻辑,而不依赖于类内部的状态信息。

public class StringUtils {
+import{_ as a,o as e,c as p,d as t,a as n,b as s,e as c}from"./app-6a63891c.js";const o={},l=n("h1",{id:"线程安全",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#线程安全","aria-hidden":"true"},"#"),s(" 线程安全")],-1),i=n("p",null,[s("线程安全性是指多线程环境下,一个函数、对象或系统的行为是否可以正确地处理多个线程同时访问或修改共享的数据而不会产生不确定的结果或导致数据损坏。"),n("br"),s(" 在并发编程中,线程安全性是一个非常重要的概念,因为多线程同时操作共享资源时可能引发竞态条件(race conditions)和其他并发问题。")],-1),u=c(`

如果要实现线程安全性,就要保证我们的类是线程安全的的。在《 Java 并发 编程实战》 中, 定义“类是线程安全的”如下:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

理解

原子性(Atomicity)

指一个操作是不可中断的。一个原子操作要么完全执行成功,要么完全不执行,不会出现中间状态。在并发环境中,如果多个线程同时执行某个原子操作,那么该操作的执行结果应当与线程的执行顺序无关,保证数据的一致性。

可见性(Visibility)

指一个线程对共享数据的修改对其他线程是可见的。当一个线程修改了共享数据时,其他线程应当能够立即看到最新的修改结果,而不是看到过期或无效的数据。

实现线程安全

线程封闭

线程封闭就是把对象封装到一个线程里,只有这一个线程能看到此对象。那么这个对象就算不是线程安全的也不会出现任何安全问题。

线程封闭是一种简单有效的并发编程技术,适用于某些场景下的数据隔离需求。它能够减少线程间的竞争和同步开销,提高并发程序的性能和可靠性。然而,需要注意线程封闭可能带来的局限性,如线程安全性和数据一致性的保证,以及对并发性能的影响等。因此,在使用线程封闭时需要仔细评估场景和需求,确保其适用性和正确性。

栈封闭(Stack Confinement)

将数据保存在线程栈的局部变量中。由于局部变量的作用域仅限于所属线程的执行上下文,其他线程无法访问到该数据,因此可以避免并发访问的问题。

ThreadLocal

使用Java中的ThreadLocal类,可以将数据与当前线程关联起来,使得每个线程都有自己的数据副本。ThreadLocal提供了线程级别的数据隔离,每个线程对数据的访问都是独立的,从而避免了并发访问的问题。

单线程模型

某些情况下,可以将任务或资源限制在单个线程中进行处理,从而避免了并发访问的问题。例如,使用单个线程的事件驱动模型,或者使用单个线程的线程池来处理任务。

无状态的类

无状态的类是指不包含任何实例变量(或称为状态)的类,其行为仅依赖于传入的参数。换句话说,无状态类的方法不会受到类级别的状态影响,每次调用方法时,都只关注输入参数和方法的逻辑,而不依赖于类内部的状态信息。

public class StringUtils {
 
     public static String concatenate(String str1, String str2) {
         return str1 + str2;
diff --git a/assets/2305281702.html-7e5c45ef.js b/assets/2305281702.html-fe603ffb.js
similarity index 97%
rename from assets/2305281702.html-7e5c45ef.js
rename to assets/2305281702.html-fe603ffb.js
index c0d2e8ea..14cc219f 100644
--- a/assets/2305281702.html-7e5c45ef.js
+++ b/assets/2305281702.html-fe603ffb.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-4ee32d34","path":"/note/java/concurrency/atomic/2305281702.html","title":"线程安全","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-28T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全"]},"headers":[{"level":2,"title":"理解","slug":"理解","link":"#理解","children":[]},{"level":2,"title":"实现线程安全","slug":"实现线程安全","link":"#实现线程安全","children":[{"level":3,"title":"线程封闭","slug":"线程封闭","link":"#线程封闭","children":[]},{"level":3,"title":"无状态的类","slug":"无状态的类","link":"#无状态的类","children":[]},{"level":3,"title":"类不可变","slug":"类不可变","link":"#类不可变","children":[]},{"level":3,"title":"synchronized","slug":"synchronized","link":"#synchronized","children":[]}]},{"level":2,"title":"死锁问题","slug":"死锁问题","link":"#死锁问题","children":[{"level":3,"title":"学术化定义","slug":"学术化定义","link":"#学术化定义","children":[]},{"level":3,"title":"死锁的危害","slug":"死锁的危害","link":"#死锁的危害","children":[]},{"level":3,"title":"避免死锁的方式","slug":"避免死锁的方式","link":"#避免死锁的方式","children":[]}]},{"level":2,"title":"其他线程安全问题","slug":"其他线程安全问题","link":"#其他线程安全问题","children":[{"level":3,"title":"活锁","slug":"活锁","link":"#活锁","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":12.34,"words":3703},"filePathRelative":"note/java/concurrency/atomic/2305281702.md","localizedDate":"2023年5月28日","excerpt":"

线程安全

\\n

线程安全性是指多线程环境下,一个函数、对象或系统的行为是否可以正确地处理多个线程同时访问或修改共享的数据而不会产生不确定的结果或导致数据损坏。
\\n在并发编程中,线程安全性是一个非常重要的概念,因为多线程同时操作共享资源时可能引发竞态条件(race conditions)和其他并发问题。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-4ee32d34","path":"/note/java/concurrency/atomic/2305281702.html","title":"线程安全","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-28T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全"]},"headers":[{"level":2,"title":"理解","slug":"理解","link":"#理解","children":[]},{"level":2,"title":"实现线程安全","slug":"实现线程安全","link":"#实现线程安全","children":[{"level":3,"title":"线程封闭","slug":"线程封闭","link":"#线程封闭","children":[]},{"level":3,"title":"无状态的类","slug":"无状态的类","link":"#无状态的类","children":[]},{"level":3,"title":"类不可变","slug":"类不可变","link":"#类不可变","children":[]},{"level":3,"title":"synchronized","slug":"synchronized","link":"#synchronized","children":[]}]},{"level":2,"title":"死锁问题","slug":"死锁问题","link":"#死锁问题","children":[{"level":3,"title":"学术化定义","slug":"学术化定义","link":"#学术化定义","children":[]},{"level":3,"title":"死锁的危害","slug":"死锁的危害","link":"#死锁的危害","children":[]},{"level":3,"title":"避免死锁的方式","slug":"避免死锁的方式","link":"#避免死锁的方式","children":[]}]},{"level":2,"title":"其他线程安全问题","slug":"其他线程安全问题","link":"#其他线程安全问题","children":[{"level":3,"title":"活锁","slug":"活锁","link":"#活锁","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":12.34,"words":3703},"filePathRelative":"note/java/concurrency/atomic/2305281702.md","localizedDate":"2023年5月28日","excerpt":"

线程安全

\\n

线程安全性是指多线程环境下,一个函数、对象或系统的行为是否可以正确地处理多个线程同时访问或修改共享的数据而不会产生不确定的结果或导致数据损坏。
\\n在并发编程中,线程安全性是一个非常重要的概念,因为多线程同时操作共享资源时可能引发竞态条件(race conditions)和其他并发问题。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305312110.html-87e5d5a0.js b/assets/2305312110.html-4f2af84a.js similarity index 93% rename from assets/2305312110.html-87e5d5a0.js rename to assets/2305312110.html-4f2af84a.js index 7969051c..31300dab 100644 --- a/assets/2305312110.html-87e5d5a0.js +++ b/assets/2305312110.html-4f2af84a.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-15eaf018","path":"/note/java/concurrency/atomic/2305312110.html","title":"JUC","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-29T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","JUC"]},"headers":[{"level":2,"title":"ReentrantLock","slug":"reentrantlock","link":"#reentrantlock","children":[{"level":3,"title":"特点","slug":"特点","link":"#特点","children":[]},{"level":3,"title":"Lock","slug":"lock","link":"#lock","children":[]},{"level":3,"title":"公平锁和非公平锁","slug":"公平锁和非公平锁","link":"#公平锁和非公平锁","children":[]},{"level":3,"title":"可重入锁","slug":"可重入锁","link":"#可重入锁","children":[]},{"level":3,"title":"Condition","slug":"condition","link":"#condition","children":[]},{"level":3,"title":"需注意的问题","slug":"需注意的问题","link":"#需注意的问题","children":[]}]},{"level":2,"title":"Semaphore","slug":"semaphore","link":"#semaphore","children":[{"level":3,"title":"应用场景","slug":"应用场景","link":"#应用场景","children":[]}]},{"level":2,"title":"CountDownLatch","slug":"countdownlatch","link":"#countdownlatch","children":[{"level":3,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.26,"words":3079},"filePathRelative":"note/java/concurrency/atomic/2305312110.md","localizedDate":"2023年5月29日","excerpt":"

JUC

\\n

JUC 是 Java Util Concurrent(Java 并发工具包)的缩写,是 Java 提供的用于并发编程的工具包。Java 并发工具包位于 java.util.concurrent
\\n包下,提供了一组强大的工具和类,用于简化并发编程、提高性能和可扩展性。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-15eaf018","path":"/note/java/concurrency/atomic/2305312110.html","title":"JUC","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-29T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","JUC"]},"headers":[{"level":2,"title":"ReentrantLock","slug":"reentrantlock","link":"#reentrantlock","children":[{"level":3,"title":"特点","slug":"特点","link":"#特点","children":[]},{"level":3,"title":"Lock","slug":"lock","link":"#lock","children":[]},{"level":3,"title":"公平锁和非公平锁","slug":"公平锁和非公平锁","link":"#公平锁和非公平锁","children":[]},{"level":3,"title":"可重入锁","slug":"可重入锁","link":"#可重入锁","children":[]},{"level":3,"title":"Condition","slug":"condition","link":"#condition","children":[]},{"level":3,"title":"需注意的问题","slug":"需注意的问题","link":"#需注意的问题","children":[]}]},{"level":2,"title":"Semaphore","slug":"semaphore","link":"#semaphore","children":[{"level":3,"title":"应用场景","slug":"应用场景","link":"#应用场景","children":[]}]},{"level":2,"title":"CountDownLatch","slug":"countdownlatch","link":"#countdownlatch","children":[{"level":3,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.26,"words":3079},"filePathRelative":"note/java/concurrency/atomic/2305312110.md","localizedDate":"2023年5月29日","excerpt":"

JUC

\\n

JUC 是 Java Util Concurrent(Java 并发工具包)的缩写,是 Java 提供的用于并发编程的工具包。Java 并发工具包位于 java.util.concurrent
\\n包下,提供了一组强大的工具和类,用于简化并发编程、提高性能和可扩展性。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2305312110.html-c5be0574.js b/assets/2305312110.html-4f40549f.js similarity index 99% rename from assets/2305312110.html-c5be0574.js rename to assets/2305312110.html-4f40549f.js index 912e9cfc..62fb79e1 100644 --- a/assets/2305312110.html-c5be0574.js +++ b/assets/2305312110.html-4f40549f.js @@ -1,4 +1,4 @@ -import{_ as s,o as t,c as p,d as e,a as n,b as a,e as o}from"./app-1efcbe9f.js";const c={},i=n("h1",{id:"juc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#juc","aria-hidden":"true"},"#"),a(" JUC")],-1),l=n("p",null,[a("JUC 是 Java Util Concurrent(Java 并发工具包)的缩写,是 Java 提供的用于并发编程的工具包。Java 并发工具包位于 java.util.concurrent"),n("br"),a(" 包下,提供了一组强大的工具和类,用于简化并发编程、提高性能和可扩展性。")],-1),u=o(`

ReentrantLock

ReentrantLock 是 Java 并发工具包中提供的一个可重入锁类。它实现了 Lock 接口,提供了比使用 synchronized 关键字更灵活和可扩展的锁机制。

import java.util.concurrent.locks.ReentrantLock;
+import{_ as s,o as t,c as p,d as e,a as n,b as a,e as o}from"./app-6a63891c.js";const c={},i=n("h1",{id:"juc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#juc","aria-hidden":"true"},"#"),a(" JUC")],-1),l=n("p",null,[a("JUC 是 Java Util Concurrent(Java 并发工具包)的缩写,是 Java 提供的用于并发编程的工具包。Java 并发工具包位于 java.util.concurrent"),n("br"),a(" 包下,提供了一组强大的工具和类,用于简化并发编程、提高性能和可扩展性。")],-1),u=o(`

ReentrantLock

ReentrantLock 是 Java 并发工具包中提供的一个可重入锁类。它实现了 Lock 接口,提供了比使用 synchronized 关键字更灵活和可扩展的锁机制。

import java.util.concurrent.locks.ReentrantLock;
 
 public class ReentrantLockExample {
     private static ReentrantLock lock = new ReentrantLock();
diff --git a/assets/2305312243.html-c14a6223.js b/assets/2305312243.html-3ff5d044.js
similarity index 98%
rename from assets/2305312243.html-c14a6223.js
rename to assets/2305312243.html-3ff5d044.js
index 74354211..10d7aba0 100644
--- a/assets/2305312243.html-c14a6223.js
+++ b/assets/2305312243.html-3ff5d044.js
@@ -1 +1 @@
-import{_ as i,o as r,c as t,d as l,a,b as e,e as h}from"./app-1efcbe9f.js";const n={},d=a("h1",{id:"aqs",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#aqs","aria-hidden":"true"},"#"),e(" AQS")],-1),o=a("p",null,[e("AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS"),a("br"),e(" 提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。")],-1),s=h('

AQS 的设计思想是使用一个等待队列来管理线程的竞争和等待状态。它维护了一个双向链表的队列,其中的每个节点表示一个等待线程,线程以 FIFO(先进先出)的顺序排队等待。AQS 提供了基于 CAS(Compare and
Swap)操作的方法来管理队列和线程的状态,实现了线程的挂起、唤醒和竞争。

特性

  1. 状态管理

AQS 维护了一个同步状态(synchronization state),它表示同步器的状态信息。同步状态可以是任意的整数值,并且可以被子类用来表示不同的状态和含义。

  1. 线程阻塞和唤醒

AQS 提供了线程阻塞和唤醒的机制,以实现线程的等待和恢复。线程在无法获取同步资源时可以被阻塞,直到其他线程释放资源并唤醒它们。

  1. 等待队列

AQS 使用一个等待队列(wait queue)来管理等待获取同步资源的线程。等待队列是一个双向链表,线程以 FIFO(先进先出)的顺序排队等待。通过等待队列,AQS 实现了公平性和线程的顺序保证。

  1. 独占与共享模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

  1. 可重入性

AQS 支持同一个线程多次获取同步资源,即可重入(Reentrant)特性。同一个线程可以多次获取同步资源,而不会造成死锁或其他并发问题。

  1. 条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

核心结构

同步状态(Synchronization State)

AQS 内部维护了一个同步状态(synchronization state),它是一个整数值,表示同步器的状态信息。同步状态可以用来表示不同的状态和含义,具体取决于具体的同步器实现。

等待队列(Wait Queue)

AQS 使用一个等待队列来管理等待获取同步资源的线程。等待队列是一个双向链表,每个节点表示一个等待线程。等待队列的设计保证了线程的先进先出(FIFO)顺序。

Node 对象

等待队列中的每个节点都由 Node 对象表示,它包含了线程等待状态、线程引用以及等待条件等信息。Node 对象的设计和状态变化对于实现同步的正确性和性能至关重要。

CAS 操作

AQS 使用 CAS(Compare and Swap)操作来实现同步状态的原子性操作。通过 CAS,可以确保同步状态的更新是原子的,避免多个线程同时修改同一状态造成的竞态条件。

共享模式和独占模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

方法

AQS 的主要方法包括:

  • acquire(int arg):获取同步状态,如果获取不到则进入等待状态。
  • release(int arg):释放同步状态,并唤醒等待队列中的下一个线程。
  • tryAcquire(int arg):尝试获取同步状态,如果成功则返回 true,否则返回 false。
  • tryRelease(int arg):尝试释放同步状态,如果成功则返回 true,否则返回 false。
  • isHeldExclusively():判断当前线程是否独占持有同步状态。

访问方式

Exclusive(独占)

独占模式(Exclusive)是指在同一时刻只允许一个线程持有锁或访问临界区,其他线程必须等待锁的释放才能继续执行。

通过 AQS 的独占模式,多个线程可以竞争同一个锁,但只有一个线程可以持有锁进行临界区的访问,从而实现线程间的互斥和同步。许多基于 AQS 的同步器和锁,如ReentrantLock就是基于 AQS
的独占模式实现的。开发者可以通过继承 AQS 并实现相关的抽象方法来构建自定义的独占模式同步器。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在独占锁模式下,state 的值通常用于表示锁的持有状态,例如 0 表示未锁定状态,1 表示锁定状态。
  2. 获取锁
    • 当一个线程尝试获取独占锁时,它会调用 AQS 的 acquire() 方法。
    • 在 acquire() 方法中,线程首先会尝试通过 CAS(Compare and Swap)操作将 state 从 0 更新为 1,以尝试获取锁。
    • 如果 CAS 操作成功,表示锁获取成功,线程可以继续执行临界区的代码。
    • 如果 CAS 操作失败,表示锁已被其他线程持有,当前线程将被加入到等待队列尾部,进入等待状态。
  3. 释放锁
    • 当持有锁的线程需要释放锁时,它会调用 AQS 的 release() 方法。
    • 在 release() 方法中,线程首先会将 state 的值设置为 0,表示锁已释放,然后,线程会检查等待队列中是否有等待的线程,如果有,则选择一个线程唤醒,使其能够继续尝试获取锁。

Share(共享)

共享模式(Shared Mode)用于实现共享锁的功能。其允许多个线程同时获取同一个锁或访问临界区,以实现并发访问共享资源的能力。

通过 AQS 的共享模式,多个线程可以同时获取同一个共享锁,进入临界区执行共享资源的访问。这种模式适用于一些读多写少的场景,允许多个线程同时读取共享资源,而在写操作时需要独占访问。基于 AQS 的共享锁,如
Semaphore或者CountDownLatch,提供了更高的并发性能,允许多个线程同时读取数据,提高系统的吞吐量。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在共享模式下,state 的值通常用于表示当前锁被多少个线程持有。
  2. 获取锁
    • 当一个线程尝试获取共享锁时,它会调用 AQS 的 acquireShared() 方法。
    • 在 acquireShared() 方法中,线程会根据当前的同步状态(state)和其他线程的状态,决定是否能够获取共享锁。
    • 如果可以获取共享锁,则线程可以继续执行临界区的代码。
    • 如果无法获取共享锁,线程将进入等待状态,并加入等待队列。
  3. 释放锁
    • 当持有共享锁的线程需要释放锁时,它会调用 AQS 的 releaseShared() 方法。
    • 在 releaseShared() 方法中,线程首先会更新同步状态(state)来释放共享锁,然后,线程会通知等待队列中的其他线程,让它们有机会竞争获取共享锁。

队列

Synchronization Wait Queue(同步等待队列)

同步等待队列是 AQS 中用于管理等待获取锁的线程的队列。当一个线程无法获取锁而需要等待时,它会被加入到同步等待队列中。同步等待队列是一个双向链表,由 Node 对象表示,每个节点对应一个等待线程。

Condition Wait Queue(条件等待队列)

条件等待队列是 AQS 中用于管理等待特定条件的线程的队列。AQS 提供了 Condition 对象来支持条件变量的功能。当一个线程在某个条件上等待时,它会被加入到条件等待队列中。条件等待队列是一个单向链表,由 Node
对象表示,每个节点对应一个等待线程。

区别

这两种队列的区别在于它们所管理的线程的目的和等待条件不同:

  • 同步等待队列用于管理等待获取锁的线程,这些线程都是在同步操作中等待锁的释放。
  • 条件等待队列用于管理等待特定条件的线程,这些线程等待条件满足才能继续执行。

节点状态

  1. 初始值

值为0,表示当前节点在sync队列中,等待着获取锁。

  1. CANCELLED

值为1,表示节点被取消。当一个节点的线程被中断或超时等情况下,节点可能会被取消。

  1. SIGNAL

值为-1,表示后继节点被阻塞。当一个节点的线程需要释放锁或满足某个条件时,它会唤醒其后继节点。

  1. CONDITION

值为-2,表示节点在条件队列中等待。当一个线程调用了 Condition 的 await() 方法后,它会被移动到条件队列中,并处于等待状态。

  1. PROPAGATE

值为-3,表示共享模式传播。用于共享模式同步器中,表示当前线程需要唤醒其后继节点。

',60);function c(u,p){return r(),t("div",null,[d,o,l(" more "),s])}const A=i(n,[["render",c],["__file","2305312243.html.vue"]]);export{A as default}; +import{_ as i,o as r,c as t,d as l,a,b as e,e as h}from"./app-6a63891c.js";const n={},d=a("h1",{id:"aqs",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#aqs","aria-hidden":"true"},"#"),e(" AQS")],-1),o=a("p",null,[e("AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS"),a("br"),e(" 提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。")],-1),s=h('

AQS 的设计思想是使用一个等待队列来管理线程的竞争和等待状态。它维护了一个双向链表的队列,其中的每个节点表示一个等待线程,线程以 FIFO(先进先出)的顺序排队等待。AQS 提供了基于 CAS(Compare and
Swap)操作的方法来管理队列和线程的状态,实现了线程的挂起、唤醒和竞争。

特性

  1. 状态管理

AQS 维护了一个同步状态(synchronization state),它表示同步器的状态信息。同步状态可以是任意的整数值,并且可以被子类用来表示不同的状态和含义。

  1. 线程阻塞和唤醒

AQS 提供了线程阻塞和唤醒的机制,以实现线程的等待和恢复。线程在无法获取同步资源时可以被阻塞,直到其他线程释放资源并唤醒它们。

  1. 等待队列

AQS 使用一个等待队列(wait queue)来管理等待获取同步资源的线程。等待队列是一个双向链表,线程以 FIFO(先进先出)的顺序排队等待。通过等待队列,AQS 实现了公平性和线程的顺序保证。

  1. 独占与共享模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

  1. 可重入性

AQS 支持同一个线程多次获取同步资源,即可重入(Reentrant)特性。同一个线程可以多次获取同步资源,而不会造成死锁或其他并发问题。

  1. 条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

核心结构

同步状态(Synchronization State)

AQS 内部维护了一个同步状态(synchronization state),它是一个整数值,表示同步器的状态信息。同步状态可以用来表示不同的状态和含义,具体取决于具体的同步器实现。

等待队列(Wait Queue)

AQS 使用一个等待队列来管理等待获取同步资源的线程。等待队列是一个双向链表,每个节点表示一个等待线程。等待队列的设计保证了线程的先进先出(FIFO)顺序。

Node 对象

等待队列中的每个节点都由 Node 对象表示,它包含了线程等待状态、线程引用以及等待条件等信息。Node 对象的设计和状态变化对于实现同步的正确性和性能至关重要。

CAS 操作

AQS 使用 CAS(Compare and Swap)操作来实现同步状态的原子性操作。通过 CAS,可以确保同步状态的更新是原子的,避免多个线程同时修改同一状态造成的竞态条件。

共享模式和独占模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

方法

AQS 的主要方法包括:

  • acquire(int arg):获取同步状态,如果获取不到则进入等待状态。
  • release(int arg):释放同步状态,并唤醒等待队列中的下一个线程。
  • tryAcquire(int arg):尝试获取同步状态,如果成功则返回 true,否则返回 false。
  • tryRelease(int arg):尝试释放同步状态,如果成功则返回 true,否则返回 false。
  • isHeldExclusively():判断当前线程是否独占持有同步状态。

访问方式

Exclusive(独占)

独占模式(Exclusive)是指在同一时刻只允许一个线程持有锁或访问临界区,其他线程必须等待锁的释放才能继续执行。

通过 AQS 的独占模式,多个线程可以竞争同一个锁,但只有一个线程可以持有锁进行临界区的访问,从而实现线程间的互斥和同步。许多基于 AQS 的同步器和锁,如ReentrantLock就是基于 AQS
的独占模式实现的。开发者可以通过继承 AQS 并实现相关的抽象方法来构建自定义的独占模式同步器。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在独占锁模式下,state 的值通常用于表示锁的持有状态,例如 0 表示未锁定状态,1 表示锁定状态。
  2. 获取锁
    • 当一个线程尝试获取独占锁时,它会调用 AQS 的 acquire() 方法。
    • 在 acquire() 方法中,线程首先会尝试通过 CAS(Compare and Swap)操作将 state 从 0 更新为 1,以尝试获取锁。
    • 如果 CAS 操作成功,表示锁获取成功,线程可以继续执行临界区的代码。
    • 如果 CAS 操作失败,表示锁已被其他线程持有,当前线程将被加入到等待队列尾部,进入等待状态。
  3. 释放锁
    • 当持有锁的线程需要释放锁时,它会调用 AQS 的 release() 方法。
    • 在 release() 方法中,线程首先会将 state 的值设置为 0,表示锁已释放,然后,线程会检查等待队列中是否有等待的线程,如果有,则选择一个线程唤醒,使其能够继续尝试获取锁。

Share(共享)

共享模式(Shared Mode)用于实现共享锁的功能。其允许多个线程同时获取同一个锁或访问临界区,以实现并发访问共享资源的能力。

通过 AQS 的共享模式,多个线程可以同时获取同一个共享锁,进入临界区执行共享资源的访问。这种模式适用于一些读多写少的场景,允许多个线程同时读取共享资源,而在写操作时需要独占访问。基于 AQS 的共享锁,如
Semaphore或者CountDownLatch,提供了更高的并发性能,允许多个线程同时读取数据,提高系统的吞吐量。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在共享模式下,state 的值通常用于表示当前锁被多少个线程持有。
  2. 获取锁
    • 当一个线程尝试获取共享锁时,它会调用 AQS 的 acquireShared() 方法。
    • 在 acquireShared() 方法中,线程会根据当前的同步状态(state)和其他线程的状态,决定是否能够获取共享锁。
    • 如果可以获取共享锁,则线程可以继续执行临界区的代码。
    • 如果无法获取共享锁,线程将进入等待状态,并加入等待队列。
  3. 释放锁
    • 当持有共享锁的线程需要释放锁时,它会调用 AQS 的 releaseShared() 方法。
    • 在 releaseShared() 方法中,线程首先会更新同步状态(state)来释放共享锁,然后,线程会通知等待队列中的其他线程,让它们有机会竞争获取共享锁。

队列

Synchronization Wait Queue(同步等待队列)

同步等待队列是 AQS 中用于管理等待获取锁的线程的队列。当一个线程无法获取锁而需要等待时,它会被加入到同步等待队列中。同步等待队列是一个双向链表,由 Node 对象表示,每个节点对应一个等待线程。

Condition Wait Queue(条件等待队列)

条件等待队列是 AQS 中用于管理等待特定条件的线程的队列。AQS 提供了 Condition 对象来支持条件变量的功能。当一个线程在某个条件上等待时,它会被加入到条件等待队列中。条件等待队列是一个单向链表,由 Node
对象表示,每个节点对应一个等待线程。

区别

这两种队列的区别在于它们所管理的线程的目的和等待条件不同:

  • 同步等待队列用于管理等待获取锁的线程,这些线程都是在同步操作中等待锁的释放。
  • 条件等待队列用于管理等待特定条件的线程,这些线程等待条件满足才能继续执行。

节点状态

  1. 初始值

值为0,表示当前节点在sync队列中,等待着获取锁。

  1. CANCELLED

值为1,表示节点被取消。当一个节点的线程被中断或超时等情况下,节点可能会被取消。

  1. SIGNAL

值为-1,表示后继节点被阻塞。当一个节点的线程需要释放锁或满足某个条件时,它会唤醒其后继节点。

  1. CONDITION

值为-2,表示节点在条件队列中等待。当一个线程调用了 Condition 的 await() 方法后,它会被移动到条件队列中,并处于等待状态。

  1. PROPAGATE

值为-3,表示共享模式传播。用于共享模式同步器中,表示当前线程需要唤醒其后继节点。

',60);function c(u,p){return r(),t("div",null,[d,o,l(" more "),s])}const A=i(n,[["render",c],["__file","2305312243.html.vue"]]);export{A as default}; diff --git a/assets/2305312243.html-9ece2619.js b/assets/2305312243.html-79934d0d.js similarity index 97% rename from assets/2305312243.html-9ece2619.js rename to assets/2305312243.html-79934d0d.js index 8d3141b5..55bdeccd 100644 --- a/assets/2305312243.html-9ece2619.js +++ b/assets/2305312243.html-79934d0d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3b8d5cd7","path":"/note/java/concurrency/lock/2305312243.html","title":"AQS","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-31T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全","AQS"]},"headers":[{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[]},{"level":2,"title":"核心结构","slug":"核心结构","link":"#核心结构","children":[{"level":3,"title":"同步状态(Synchronization State)","slug":"同步状态-synchronization-state","link":"#同步状态-synchronization-state","children":[]},{"level":3,"title":"等待队列(Wait Queue)","slug":"等待队列-wait-queue","link":"#等待队列-wait-queue","children":[]},{"level":3,"title":"Node 对象","slug":"node-对象","link":"#node-对象","children":[]},{"level":3,"title":"CAS 操作","slug":"cas-操作","link":"#cas-操作","children":[]},{"level":3,"title":"共享模式和独占模式","slug":"共享模式和独占模式","link":"#共享模式和独占模式","children":[]},{"level":3,"title":"条件变量支持","slug":"条件变量支持","link":"#条件变量支持","children":[]}]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"访问方式","slug":"访问方式","link":"#访问方式","children":[{"level":3,"title":"Exclusive(独占)","slug":"exclusive-独占","link":"#exclusive-独占","children":[]},{"level":3,"title":"Share(共享)","slug":"share-共享","link":"#share-共享","children":[]}]},{"level":2,"title":"队列","slug":"队列","link":"#队列","children":[{"level":3,"title":"Synchronization Wait Queue(同步等待队列)","slug":"synchronization-wait-queue-同步等待队列","link":"#synchronization-wait-queue-同步等待队列","children":[]},{"level":3,"title":"Condition Wait Queue(条件等待队列)","slug":"condition-wait-queue-条件等待队列","link":"#condition-wait-queue-条件等待队列","children":[]},{"level":3,"title":"区别","slug":"区别","link":"#区别","children":[]},{"level":3,"title":"节点状态","slug":"节点状态","link":"#节点状态","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":8.28,"words":2484},"filePathRelative":"note/java/concurrency/lock/2305312243.md","localizedDate":"2023年5月31日","excerpt":"

AQS

\\n

AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
\\n提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-3b8d5cd7","path":"/note/java/concurrency/lock/2305312243.html","title":"AQS","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-05-31T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全","AQS"]},"headers":[{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[]},{"level":2,"title":"核心结构","slug":"核心结构","link":"#核心结构","children":[{"level":3,"title":"同步状态(Synchronization State)","slug":"同步状态-synchronization-state","link":"#同步状态-synchronization-state","children":[]},{"level":3,"title":"等待队列(Wait Queue)","slug":"等待队列-wait-queue","link":"#等待队列-wait-queue","children":[]},{"level":3,"title":"Node 对象","slug":"node-对象","link":"#node-对象","children":[]},{"level":3,"title":"CAS 操作","slug":"cas-操作","link":"#cas-操作","children":[]},{"level":3,"title":"共享模式和独占模式","slug":"共享模式和独占模式","link":"#共享模式和独占模式","children":[]},{"level":3,"title":"条件变量支持","slug":"条件变量支持","link":"#条件变量支持","children":[]}]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"访问方式","slug":"访问方式","link":"#访问方式","children":[{"level":3,"title":"Exclusive(独占)","slug":"exclusive-独占","link":"#exclusive-独占","children":[]},{"level":3,"title":"Share(共享)","slug":"share-共享","link":"#share-共享","children":[]}]},{"level":2,"title":"队列","slug":"队列","link":"#队列","children":[{"level":3,"title":"Synchronization Wait Queue(同步等待队列)","slug":"synchronization-wait-queue-同步等待队列","link":"#synchronization-wait-queue-同步等待队列","children":[]},{"level":3,"title":"Condition Wait Queue(条件等待队列)","slug":"condition-wait-queue-条件等待队列","link":"#condition-wait-queue-条件等待队列","children":[]},{"level":3,"title":"区别","slug":"区别","link":"#区别","children":[]},{"level":3,"title":"节点状态","slug":"节点状态","link":"#节点状态","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":8.28,"words":2484},"filePathRelative":"note/java/concurrency/lock/2305312243.md","localizedDate":"2023年5月31日","excerpt":"

AQS

\\n

AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
\\n提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306011640.html-77488a8d.js b/assets/2306011640.html-95e24e4c.js similarity index 95% rename from assets/2306011640.html-77488a8d.js rename to assets/2306011640.html-95e24e4c.js index 928cc713..ba556dc7 100644 --- a/assets/2306011640.html-77488a8d.js +++ b/assets/2306011640.html-95e24e4c.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-ea9ab6de","path":"/note/java/concurrency/lock/2306011640.html","title":"ReentrantLock(独占锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-01T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"公平锁和非公平锁","slug":"公平锁和非公平锁","link":"#公平锁和非公平锁","children":[{"level":3,"title":"公平锁","slug":"公平锁","link":"#公平锁","children":[]},{"level":3,"title":"非公平锁","slug":"非公平锁","link":"#非公平锁","children":[]},{"level":3,"title":"注意","slug":"注意","link":"#注意","children":[]}]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.92,"words":1475},"filePathRelative":"note/java/concurrency/lock/2306011640.md","localizedDate":"2023年6月1日","excerpt":"

ReentrantLock(独占锁)

\\n

ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock
\\n接口的一个实现。ReentrantLock
\\n是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-ea9ab6de","path":"/note/java/concurrency/lock/2306011640.html","title":"ReentrantLock(独占锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-01T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"公平锁和非公平锁","slug":"公平锁和非公平锁","link":"#公平锁和非公平锁","children":[{"level":3,"title":"公平锁","slug":"公平锁","link":"#公平锁","children":[]},{"level":3,"title":"非公平锁","slug":"非公平锁","link":"#非公平锁","children":[]},{"level":3,"title":"注意","slug":"注意","link":"#注意","children":[]}]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.92,"words":1475},"filePathRelative":"note/java/concurrency/lock/2306011640.md","localizedDate":"2023年6月1日","excerpt":"

ReentrantLock(独占锁)

\\n

ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock
\\n接口的一个实现。ReentrantLock
\\n是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306011640.html-f2812653.js b/assets/2306011640.html-ac314c8d.js similarity index 99% rename from assets/2306011640.html-f2812653.js rename to assets/2306011640.html-ac314c8d.js index 033de27a..994258ba 100644 --- a/assets/2306011640.html-f2812653.js +++ b/assets/2306011640.html-ac314c8d.js @@ -1,4 +1,4 @@ -import{_ as a,o as e,c as t,d as p,a as n,b as s,e as c}from"./app-1efcbe9f.js";const o={},l=n("h1",{id:"reentrantlock-独占锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#reentrantlock-独占锁","aria-hidden":"true"},"#"),s(" ReentrantLock(独占锁)")],-1),i=n("p",null,[s("ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock"),n("br"),s(" 接口的一个实现。ReentrantLock"),n("br"),s(" 是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。")],-1),u=c(`
import java.util.concurrent.locks.ReentrantLock;
+import{_ as a,o as e,c as t,d as p,a as n,b as s,e as c}from"./app-6a63891c.js";const o={},l=n("h1",{id:"reentrantlock-独占锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#reentrantlock-独占锁","aria-hidden":"true"},"#"),s(" ReentrantLock(独占锁)")],-1),i=n("p",null,[s("ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock"),n("br"),s(" 接口的一个实现。ReentrantLock"),n("br"),s(" 是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。")],-1),u=c(`
import java.util.concurrent.locks.ReentrantLock;
 
 public class ReentrantLockExample {
     private final ReentrantLock lock = new ReentrantLock();
diff --git a/assets/2306021120.html-3b158a72.js b/assets/2306021120.html-0c4cd9c4.js
similarity index 96%
rename from assets/2306021120.html-3b158a72.js
rename to assets/2306021120.html-0c4cd9c4.js
index 1e8c9331..974a9910 100644
--- a/assets/2306021120.html-3b158a72.js
+++ b/assets/2306021120.html-0c4cd9c4.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0dc9bed3","path":"/note/java/concurrency/lock/2306021120.html","title":"ReentrantReadWriteLock(读写锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-02T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"读写锁实现原理","slug":"读写锁实现原理","link":"#读写锁实现原理","children":[{"level":3,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":3,"title":"锁降级","slug":"锁降级","link":"#锁降级","children":[]},{"level":3,"title":"锁升级","slug":"锁升级","link":"#锁升级","children":[]},{"level":3,"title":"实现流程","slug":"实现流程","link":"#实现流程","children":[]}]},{"level":2,"title":"性能问题","slug":"性能问题","link":"#性能问题","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.32,"words":2195},"filePathRelative":"note/java/concurrency/lock/2306021120.md","localizedDate":"2023年6月2日","excerpt":"

ReentrantReadWriteLock(读写锁)

\\n

ReentrantReadWriteLock是一个读写锁,它内部维护了两个锁:ReadLockWriteLock。ReadLock用于只读操作,WriteLock用于写操作。 如果没有写操作,读锁是可以被多个线程同时持有的,即写锁是独占的,读锁是共享的。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0dc9bed3","path":"/note/java/concurrency/lock/2306021120.html","title":"ReentrantReadWriteLock(读写锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-02T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"读写锁实现原理","slug":"读写锁实现原理","link":"#读写锁实现原理","children":[{"level":3,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":3,"title":"锁降级","slug":"锁降级","link":"#锁降级","children":[]},{"level":3,"title":"锁升级","slug":"锁升级","link":"#锁升级","children":[]},{"level":3,"title":"实现流程","slug":"实现流程","link":"#实现流程","children":[]}]},{"level":2,"title":"性能问题","slug":"性能问题","link":"#性能问题","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.32,"words":2195},"filePathRelative":"note/java/concurrency/lock/2306021120.md","localizedDate":"2023年6月2日","excerpt":"

ReentrantReadWriteLock(读写锁)

\\n

ReentrantReadWriteLock是一个读写锁,它内部维护了两个锁:ReadLockWriteLock。ReadLock用于只读操作,WriteLock用于写操作。 如果没有写操作,读锁是可以被多个线程同时持有的,即写锁是独占的,读锁是共享的。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306021120.html-04e95df3.js b/assets/2306021120.html-4b51bdce.js similarity index 99% rename from assets/2306021120.html-04e95df3.js rename to assets/2306021120.html-4b51bdce.js index 4b127bf8..83db3e2d 100644 --- a/assets/2306021120.html-04e95df3.js +++ b/assets/2306021120.html-4b51bdce.js @@ -1,4 +1,4 @@ -import{_ as a,o as t,c as p,d as e,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"reentrantreadwritelock-读写锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#reentrantreadwritelock-读写锁","aria-hidden":"true"},"#"),s(" ReentrantReadWriteLock(读写锁)")],-1),i=n("p",null,[s("ReentrantReadWriteLock是一个读写锁,它内部维护了两个锁:"),n("strong",null,"ReadLock"),s("和"),n("strong",null,"WriteLock"),s("。ReadLock用于只读操作,WriteLock用于写操作。 如果没有写操作,读锁是可以被多个线程同时持有的,即写锁是独占的,读锁是共享的。")],-1),u=o(`
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import{_ as a,o as t,c as p,d as e,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"reentrantreadwritelock-读写锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#reentrantreadwritelock-读写锁","aria-hidden":"true"},"#"),s(" ReentrantReadWriteLock(读写锁)")],-1),i=n("p",null,[s("ReentrantReadWriteLock是一个读写锁,它内部维护了两个锁:"),n("strong",null,"ReadLock"),s("和"),n("strong",null,"WriteLock"),s("。ReadLock用于只读操作,WriteLock用于写操作。 如果没有写操作,读锁是可以被多个线程同时持有的,即写锁是独占的,读锁是共享的。")],-1),u=o(`
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public class ReentrantReadWriteLockExample {
 
diff --git a/assets/2306021924.html-401e345f.js b/assets/2306021924.html-55c87dff.js
similarity index 96%
rename from assets/2306021924.html-401e345f.js
rename to assets/2306021924.html-55c87dff.js
index 518af705..127769b6 100644
--- a/assets/2306021924.html-401e345f.js
+++ b/assets/2306021924.html-55c87dff.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-53a68847","path":"/note/java/concurrency/lock/2306021924.html","title":"StampedLock(读写锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-02T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[{"level":3,"title":"三种锁模式","slug":"三种锁模式","link":"#三种锁模式","children":[]},{"level":3,"title":"不支持重入","slug":"不支持重入","link":"#不支持重入","children":[]},{"level":3,"title":"不支持锁升级","slug":"不支持锁升级","link":"#不支持锁升级","children":[]},{"level":3,"title":"支持锁降级","slug":"支持锁降级","link":"#支持锁降级","children":[]}]},{"level":2,"title":"乐观读","slug":"乐观读","link":"#乐观读","children":[{"level":3,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":3,"title":"优缺点","slug":"优缺点","link":"#优缺点","children":[]}]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.48,"words":1043},"filePathRelative":"note/java/concurrency/lock/2306021924.md","localizedDate":"2023年6月2日","excerpt":"

StampedLock(读写锁)

\\n

StampedLock 是 Java 8 引入的一个新的读写锁,其设计目标是为了解决 ReentrantReadWriteLock 的一些性能问题,提供了乐观读锁的机制。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-53a68847","path":"/note/java/concurrency/lock/2306021924.html","title":"StampedLock(读写锁)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-02T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","Lock"]},"headers":[{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[{"level":3,"title":"三种锁模式","slug":"三种锁模式","link":"#三种锁模式","children":[]},{"level":3,"title":"不支持重入","slug":"不支持重入","link":"#不支持重入","children":[]},{"level":3,"title":"不支持锁升级","slug":"不支持锁升级","link":"#不支持锁升级","children":[]},{"level":3,"title":"支持锁降级","slug":"支持锁降级","link":"#支持锁降级","children":[]}]},{"level":2,"title":"乐观读","slug":"乐观读","link":"#乐观读","children":[{"level":3,"title":"基本思想","slug":"基本思想","link":"#基本思想","children":[]},{"level":3,"title":"优缺点","slug":"优缺点","link":"#优缺点","children":[]}]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.48,"words":1043},"filePathRelative":"note/java/concurrency/lock/2306021924.md","localizedDate":"2023年6月2日","excerpt":"

StampedLock(读写锁)

\\n

StampedLock 是 Java 8 引入的一个新的读写锁,其设计目标是为了解决 ReentrantReadWriteLock 的一些性能问题,提供了乐观读锁的机制。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306021924.html-b63de92c.js b/assets/2306021924.html-8c927565.js similarity index 99% rename from assets/2306021924.html-b63de92c.js rename to assets/2306021924.html-8c927565.js index b538176f..d7795756 100644 --- a/assets/2306021924.html-b63de92c.js +++ b/assets/2306021924.html-8c927565.js @@ -1,4 +1,4 @@ -import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"stampedlock-读写锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#stampedlock-读写锁","aria-hidden":"true"},"#"),e(" StampedLock(读写锁)")],-1),i=n("p",null,"StampedLock 是 Java 8 引入的一个新的读写锁,其设计目标是为了解决 ReentrantReadWriteLock 的一些性能问题,提供了乐观读锁的机制。",-1),u=o(`
import java.util.concurrent.locks.StampedLock;
+import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"stampedlock-读写锁",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#stampedlock-读写锁","aria-hidden":"true"},"#"),e(" StampedLock(读写锁)")],-1),i=n("p",null,"StampedLock 是 Java 8 引入的一个新的读写锁,其设计目标是为了解决 ReentrantReadWriteLock 的一些性能问题,提供了乐观读锁的机制。",-1),u=o(`
import java.util.concurrent.locks.StampedLock;
 
 class Point {
     private double x, y;
diff --git a/assets/2306041045.html-90dab8c8.js b/assets/2306041045.html-48088652.js
similarity index 91%
rename from assets/2306041045.html-90dab8c8.js
rename to assets/2306041045.html-48088652.js
index addeb28c..a25abd7b 100644
--- a/assets/2306041045.html-90dab8c8.js
+++ b/assets/2306041045.html-48088652.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-ba51a5ea","path":"/note/java/concurrency/queue/2306041045.html","title":"BlockingQueue(阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-04T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"Queue接口","slug":"queue接口","link":"#queue接口","children":[]},{"level":2,"title":"核心方法","slug":"核心方法","link":"#核心方法","children":[]},{"level":2,"title":"主要实现","slug":"主要实现","link":"#主要实现","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.77,"words":1132},"filePathRelative":"note/java/concurrency/queue/2306041045.md","localizedDate":"2023年6月4日","excerpt":"

BlockingQueue(阻塞队列)

\\n

BlockingQueue是java.util.concurrent包下的一个接口,它是Queue接口的一个子接口。相比于普通的Queue,BlockingQueue的主要特性是,当试图向满的队列中添加元素或从空的队列中获取元素时,队列会阻塞插入/获取操作。这两种操作使得BlockingQueue适合用于生产者-消费者模型,在多线程环境中处理数据共享问题。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-ba51a5ea","path":"/note/java/concurrency/queue/2306041045.html","title":"BlockingQueue(阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-04T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"Queue接口","slug":"queue接口","link":"#queue接口","children":[]},{"level":2,"title":"核心方法","slug":"核心方法","link":"#核心方法","children":[]},{"level":2,"title":"主要实现","slug":"主要实现","link":"#主要实现","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.77,"words":1132},"filePathRelative":"note/java/concurrency/queue/2306041045.md","localizedDate":"2023年6月4日","excerpt":"

BlockingQueue(阻塞队列)

\\n

BlockingQueue是java.util.concurrent包下的一个接口,它是Queue接口的一个子接口。相比于普通的Queue,BlockingQueue的主要特性是,当试图向满的队列中添加元素或从空的队列中获取元素时,队列会阻塞插入/获取操作。这两种操作使得BlockingQueue适合用于生产者-消费者模型,在多线程环境中处理数据共享问题。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306041045.html-52f54902.js b/assets/2306041045.html-bcdb211e.js similarity index 97% rename from assets/2306041045.html-52f54902.js rename to assets/2306041045.html-bcdb211e.js index 16f8fc11..eca6bd87 100644 --- a/assets/2306041045.html-52f54902.js +++ b/assets/2306041045.html-bcdb211e.js @@ -1 +1 @@ -import{_ as n,o,c as r,d as t,a as e,b as l,e as i}from"./app-1efcbe9f.js";const s={},a=e("h1",{id:"blockingqueue-阻塞队列",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#blockingqueue-阻塞队列","aria-hidden":"true"},"#"),l(" BlockingQueue(阻塞队列)")],-1),u=e("p",null,"BlockingQueue是java.util.concurrent包下的一个接口,它是Queue接口的一个子接口。相比于普通的Queue,BlockingQueue的主要特性是,当试图向满的队列中添加元素或从空的队列中获取元素时,队列会阻塞插入/获取操作。这两种操作使得BlockingQueue适合用于生产者-消费者模型,在多线程环境中处理数据共享问题。",-1),g=i('

Queue接口

  • boolean add(E e): 添加一个元素,添加成功返回true, 如果队列满了,就会抛出异常

  • boolean offer(E e): 添加一个元素,添加成功返回true, 如果队列满了,返回false

  • E remove(): 返回并删除队首元素,队列为空则抛出异常

  • E poll(): 返回并删除队首元素,队列为空则返回null

  • E element(): 返回队首元素,但不移除,队列为空则抛出异常

  • E peek(): 获取队首元素,但不移除,队列为空则返回null

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则等待可用的空间。这是一个阻塞操作。

  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。

  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。

  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间,如果在指定的时间内,队列仍为空,则返回
    null。

  • int remainingCapacity(): 返回队列还剩下多少空间。

  • boolean drainTo(Collection<? super E> c): 移除此队列中所有可用的元素,并将它们添加到给定的集合中。

  • boolean drainTo(Collection<? super E> c, int maxElements): 从此队列中移除最多给定数量的可用元素,并将这些元素添加到给定的集合中。

主要实现

  • ArrayBlockingQueue: 一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
  • DelayQueue: 一个使用优先级队列实现的无界阻塞延迟队列。
  • PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列。
  • SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作。

使用场景

  • 线程池:
    线程池中的任务队列通常是一个阻塞队列。当任务数超过线程池的容量时,新提交的任务将被放入任务队列中等待执行。线程池中的工作线程从任务队列中取出任务进行处理,如果队列为空,则工作线程会被阻塞,直到队列中有新的任务被提交。

  • 生产者-消费者模型:
    在生产者-消费者模型中,生产者向队列中添加元素,消费者从队列中取出元素进行处理。阻塞队列可以很好地解决生产者和消费者之间的并发问题,避免线程间的竞争和冲突。

  • 消息队列:
    消息队列使用阻塞队列来存储消息,生产者将消息放入队列中,消费者从队列中取出消息进行处理。消息队列可以实现异步通信,提高系统的吞吐量和响应性能,同时还可以将不同的组件解耦,提高系统的可维护性和可扩展性。

  • 缓存系统:
    缓存系统使用阻塞队列来存储缓存数据,当缓存数据被更新时,它会被放入队列中,其他线程可以从队列中取出最新的数据进行使用。使用阻塞队列可以避免并发更新缓存数据时的竞争和冲突。

  • 并发任务处理:
    在并发任务处理中,可以将待处理的任务放入阻塞队列中,多个工作线程可以从队列中取出任务进行处理。使用阻塞队列可以避免多个线程同时处理同一个任务的问题,并且可以将任务的提交和执行解耦,提高系统的可维护性和可扩展性。

',8);function c(p,d){return o(),r("div",null,[a,u,t(" more "),g])}const b=n(s,[["render",c],["__file","2306041045.html.vue"]]);export{b as default}; +import{_ as n,o,c as r,d as t,a as e,b as l,e as i}from"./app-6a63891c.js";const s={},a=e("h1",{id:"blockingqueue-阻塞队列",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#blockingqueue-阻塞队列","aria-hidden":"true"},"#"),l(" BlockingQueue(阻塞队列)")],-1),u=e("p",null,"BlockingQueue是java.util.concurrent包下的一个接口,它是Queue接口的一个子接口。相比于普通的Queue,BlockingQueue的主要特性是,当试图向满的队列中添加元素或从空的队列中获取元素时,队列会阻塞插入/获取操作。这两种操作使得BlockingQueue适合用于生产者-消费者模型,在多线程环境中处理数据共享问题。",-1),g=i('

Queue接口

  • boolean add(E e): 添加一个元素,添加成功返回true, 如果队列满了,就会抛出异常

  • boolean offer(E e): 添加一个元素,添加成功返回true, 如果队列满了,返回false

  • E remove(): 返回并删除队首元素,队列为空则抛出异常

  • E poll(): 返回并删除队首元素,队列为空则返回null

  • E element(): 返回队首元素,但不移除,队列为空则抛出异常

  • E peek(): 获取队首元素,但不移除,队列为空则返回null

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则等待可用的空间。这是一个阻塞操作。

  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。

  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。

  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间,如果在指定的时间内,队列仍为空,则返回
    null。

  • int remainingCapacity(): 返回队列还剩下多少空间。

  • boolean drainTo(Collection<? super E> c): 移除此队列中所有可用的元素,并将它们添加到给定的集合中。

  • boolean drainTo(Collection<? super E> c, int maxElements): 从此队列中移除最多给定数量的可用元素,并将这些元素添加到给定的集合中。

主要实现

  • ArrayBlockingQueue: 一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
  • DelayQueue: 一个使用优先级队列实现的无界阻塞延迟队列。
  • PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列。
  • SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作。

使用场景

  • 线程池:
    线程池中的任务队列通常是一个阻塞队列。当任务数超过线程池的容量时,新提交的任务将被放入任务队列中等待执行。线程池中的工作线程从任务队列中取出任务进行处理,如果队列为空,则工作线程会被阻塞,直到队列中有新的任务被提交。

  • 生产者-消费者模型:
    在生产者-消费者模型中,生产者向队列中添加元素,消费者从队列中取出元素进行处理。阻塞队列可以很好地解决生产者和消费者之间的并发问题,避免线程间的竞争和冲突。

  • 消息队列:
    消息队列使用阻塞队列来存储消息,生产者将消息放入队列中,消费者从队列中取出消息进行处理。消息队列可以实现异步通信,提高系统的吞吐量和响应性能,同时还可以将不同的组件解耦,提高系统的可维护性和可扩展性。

  • 缓存系统:
    缓存系统使用阻塞队列来存储缓存数据,当缓存数据被更新时,它会被放入队列中,其他线程可以从队列中取出最新的数据进行使用。使用阻塞队列可以避免并发更新缓存数据时的竞争和冲突。

  • 并发任务处理:
    在并发任务处理中,可以将待处理的任务放入阻塞队列中,多个工作线程可以从队列中取出任务进行处理。使用阻塞队列可以避免多个线程同时处理同一个任务的问题,并且可以将任务的提交和执行解耦,提高系统的可维护性和可扩展性。

',8);function c(p,d){return o(),r("div",null,[a,u,t(" more "),g])}const b=n(s,[["render",c],["__file","2306041045.html.vue"]]);export{b as default}; diff --git a/assets/2306042021.html-f9ce8db2.js b/assets/2306042021.html-b2695927.js similarity index 99% rename from assets/2306042021.html-f9ce8db2.js rename to assets/2306042021.html-b2695927.js index 664f165a..66c0a46e 100644 --- a/assets/2306042021.html-f9ce8db2.js +++ b/assets/2306042021.html-b2695927.js @@ -1,4 +1,4 @@ -import{_ as a,o as t,c as e,d as p,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"arrayblockingqueue-有界阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#arrayblockingqueue-有界阻塞队列","aria-hidden":"true"},"#"),s(" ArrayBlockingQueue(有界阻塞队列)")],-1),i=n("p",null,[s("ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的"),n("strong",null,"有界阻塞队列"),s("。"),n("br"),s(" 队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。")],-1),u=o(`

原理

  1. 使用有界阻塞队列,队列中元素先进先出,存取元素操作相互排斥
  2. 使用静态数组,容量固定,在构建ArrayBlockingQueue时必须指定长度,并且没有扩容机制
  3. 线程安全使用ReentrantLock来实现,存取的是同一把锁,操作的是同一个数组队形,存取操作相互排斥
  4. 入队是从队首开始添加元素,并记录putIndex,同时唤醒notEmpty(当putIndex到达队尾时设置为0)
  5. 出队也是从队首开始取出元素,并记录takeIndex,同时唤醒notFull(当takeIndex到达队尾时设置为0)

注意:两个Index指针都是从队首像队尾移动,保证队列先进先出原则

ArrayBlockingQueue使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个线程可以进行入队或者出队操作;这也就意味着生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。

核心属性

  • final Object[] items: 这是一个数组,用来保存队列中的元素。

  • int takeIndex: 表示下一个要被获取(或“take”)的元素在数组中的位置。如果队列为空,则没有具体的意义。

  • int putIndex: 表示下一个要添加(或“put”)的元素在数组中的位置。如果队列已满,则没有具体的意义。

  • int count: 表示队列中当前的元素数量。

  • final ReentrantLock lock: 重入锁,用于控制对队列的并发访问。

  • private final Condition notEmpty: 当队列为空时,获取元素的线程可以在这个条件上等待。

  • private final Condition notFull: 当队列已满时,添加元素的线程可以在这个条件上等待。

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则阻塞等待可用的空间。
  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。
  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。
  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间。如果在指定的时间内,队列仍为空,则返回
    null。
  • int remainingCapacity(): 返回此队列中剩余的可用空间。

入队源码

    // 添加元素
+import{_ as a,o as t,c as e,d as p,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"arrayblockingqueue-有界阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#arrayblockingqueue-有界阻塞队列","aria-hidden":"true"},"#"),s(" ArrayBlockingQueue(有界阻塞队列)")],-1),i=n("p",null,[s("ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的"),n("strong",null,"有界阻塞队列"),s("。"),n("br"),s(" 队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。")],-1),u=o(`

原理

  1. 使用有界阻塞队列,队列中元素先进先出,存取元素操作相互排斥
  2. 使用静态数组,容量固定,在构建ArrayBlockingQueue时必须指定长度,并且没有扩容机制
  3. 线程安全使用ReentrantLock来实现,存取的是同一把锁,操作的是同一个数组队形,存取操作相互排斥
  4. 入队是从队首开始添加元素,并记录putIndex,同时唤醒notEmpty(当putIndex到达队尾时设置为0)
  5. 出队也是从队首开始取出元素,并记录takeIndex,同时唤醒notFull(当takeIndex到达队尾时设置为0)

注意:两个Index指针都是从队首像队尾移动,保证队列先进先出原则

ArrayBlockingQueue使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个线程可以进行入队或者出队操作;这也就意味着生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。

核心属性

  • final Object[] items: 这是一个数组,用来保存队列中的元素。

  • int takeIndex: 表示下一个要被获取(或“take”)的元素在数组中的位置。如果队列为空,则没有具体的意义。

  • int putIndex: 表示下一个要添加(或“put”)的元素在数组中的位置。如果队列已满,则没有具体的意义。

  • int count: 表示队列中当前的元素数量。

  • final ReentrantLock lock: 重入锁,用于控制对队列的并发访问。

  • private final Condition notEmpty: 当队列为空时,获取元素的线程可以在这个条件上等待。

  • private final Condition notFull: 当队列已满时,添加元素的线程可以在这个条件上等待。

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则阻塞等待可用的空间。
  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。
  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。
  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间。如果在指定的时间内,队列仍为空,则返回
    null。
  • int remainingCapacity(): 返回此队列中剩余的可用空间。

入队源码

    // 添加元素
     public void put(E e) throws InterruptedException {
         // 检查元素是否为空
         checkNotNull(e);
diff --git a/assets/2306042021.html-4e558a36.js b/assets/2306042021.html-b8014719.js
similarity index 92%
rename from assets/2306042021.html-4e558a36.js
rename to assets/2306042021.html-b8014719.js
index 4a2236e9..982449cb 100644
--- a/assets/2306042021.html-4e558a36.js
+++ b/assets/2306042021.html-b8014719.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-730879e4","path":"/note/java/concurrency/queue/2306042021.html","title":"ArrayBlockingQueue(有界阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-04T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"核心方法","slug":"核心方法","link":"#核心方法","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.75,"words":1725},"filePathRelative":"note/java/concurrency/queue/2306042021.md","localizedDate":"2023年6月4日","excerpt":"

ArrayBlockingQueue(有界阻塞队列)

\\n

ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的有界阻塞队列
\\n队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-730879e4","path":"/note/java/concurrency/queue/2306042021.html","title":"ArrayBlockingQueue(有界阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-04T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"核心方法","slug":"核心方法","link":"#核心方法","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.75,"words":1725},"filePathRelative":"note/java/concurrency/queue/2306042021.md","localizedDate":"2023年6月4日","excerpt":"

ArrayBlockingQueue(有界阻塞队列)

\\n

ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的有界阻塞队列
\\n队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306051101.html-035bba35.js b/assets/2306051101.html-2d3adfa7.js similarity index 99% rename from assets/2306051101.html-035bba35.js rename to assets/2306051101.html-2d3adfa7.js index 99018fd4..e033562e 100644 --- a/assets/2306051101.html-035bba35.js +++ b/assets/2306051101.html-2d3adfa7.js @@ -1,4 +1,4 @@ -import{_ as a,o as t,c as e,d as p,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"linkedblockingqueue-链表结构的阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#linkedblockingqueue-链表结构的阻塞队列","aria-hidden":"true"},"#"),s(" LinkedBlockingQueue(链表结构的阻塞队列)")],-1),i=n("p",null,[s("LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于"),n("strong",null,"链表结构的阻塞队列"),s(",按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。")],-1),u=o(`

原理

  1. 实现的是无界阻塞队列,可以指定容量,默认Integer.MAX_VALUE,先进先出,存取互不干扰
  2. 数据结构选用链表,可指定容量,内存存粗Node元素
  3. 实现两把锁,存取互不干扰,存取操作的是不同的Node对象,但是删除元素时存取元素都会加锁
  4. 入队是从队尾入队,有last指针记录
  5. 出队从队首出,有head指针记录

核心属性

  • Node[] items:链表实现,用来存储队列中的元素。每个节点包含一个元素和指向下一个节点的链接。

  • ReentrantLock takeLock:可重入锁,用于控制元素的移除操作。当多个线程试图移除队列中的元素时,这个锁确保了只有一个线程可以执行该操作。

  • ReentrantLock putLock:可重入锁,用于控制元素的插入操作。当多个线程试图向队列中插入元素时,这个锁确保了只有一个线程可以执行该操作。

  • Condition notEmpty:用于协调消费者线程。当队列为空,消费者线程试图移除元素时,它们会等待这个条件变量。

  • Condition notFull:用于协调生产者线程。当队列已满,生产者线程试图插入元素时,它们会等待这个条件变量。

  • AtomicInteger count:用来记录队列中当前的元素数量。

  • capacity (int):队列的容量,如果在创建队列时没有指定容量,那么容量将等于 Integer.MAX_VALUE。

入队源码

    // 添加元素
+import{_ as a,o as t,c as e,d as p,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"linkedblockingqueue-链表结构的阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#linkedblockingqueue-链表结构的阻塞队列","aria-hidden":"true"},"#"),s(" LinkedBlockingQueue(链表结构的阻塞队列)")],-1),i=n("p",null,[s("LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于"),n("strong",null,"链表结构的阻塞队列"),s(",按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。")],-1),u=o(`

原理

  1. 实现的是无界阻塞队列,可以指定容量,默认Integer.MAX_VALUE,先进先出,存取互不干扰
  2. 数据结构选用链表,可指定容量,内存存粗Node元素
  3. 实现两把锁,存取互不干扰,存取操作的是不同的Node对象,但是删除元素时存取元素都会加锁
  4. 入队是从队尾入队,有last指针记录
  5. 出队从队首出,有head指针记录

核心属性

  • Node[] items:链表实现,用来存储队列中的元素。每个节点包含一个元素和指向下一个节点的链接。

  • ReentrantLock takeLock:可重入锁,用于控制元素的移除操作。当多个线程试图移除队列中的元素时,这个锁确保了只有一个线程可以执行该操作。

  • ReentrantLock putLock:可重入锁,用于控制元素的插入操作。当多个线程试图向队列中插入元素时,这个锁确保了只有一个线程可以执行该操作。

  • Condition notEmpty:用于协调消费者线程。当队列为空,消费者线程试图移除元素时,它们会等待这个条件变量。

  • Condition notFull:用于协调生产者线程。当队列已满,生产者线程试图插入元素时,它们会等待这个条件变量。

  • AtomicInteger count:用来记录队列中当前的元素数量。

  • capacity (int):队列的容量,如果在创建队列时没有指定容量,那么容量将等于 Integer.MAX_VALUE。

入队源码

    // 添加元素
     public void put(E e) throws InterruptedException {
         if (e == null) throw new NullPointerException();
         // Note: convention in all put/take/etc is to preset local var
diff --git a/assets/2306051101.html-76410b93.js b/assets/2306051101.html-d0d6602d.js
similarity index 95%
rename from assets/2306051101.html-76410b93.js
rename to assets/2306051101.html-d0d6602d.js
index 92f7373d..ff5ba682 100644
--- a/assets/2306051101.html-76410b93.js
+++ b/assets/2306051101.html-d0d6602d.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-24957b89","path":"/note/java/concurrency/queue/2306051101.html","title":"LinkedBlockingQueue(链表结构的阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-05T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.14,"words":1243},"filePathRelative":"note/java/concurrency/queue/2306051101.md","localizedDate":"2023年6月5日","excerpt":"

LinkedBlockingQueue(链表结构的阻塞队列)

\\n

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-24957b89","path":"/note/java/concurrency/queue/2306051101.html","title":"LinkedBlockingQueue(链表结构的阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-05T00:00:00.000Z","order":3,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.14,"words":1243},"filePathRelative":"note/java/concurrency/queue/2306051101.md","localizedDate":"2023年6月5日","excerpt":"

LinkedBlockingQueue(链表结构的阻塞队列)

\\n

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306052321.html-85a0876e.js b/assets/2306052321.html-ec7a6f43.js similarity index 95% rename from assets/2306052321.html-85a0876e.js rename to assets/2306052321.html-ec7a6f43.js index 032ccb05..ff8a6bcd 100644 --- a/assets/2306052321.html-85a0876e.js +++ b/assets/2306052321.html-ec7a6f43.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1b2e8a6c","path":"/note/java/concurrency/queue/2306052321.html","title":"DelayQueue(无界阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-05T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1326},"filePathRelative":"note/java/concurrency/queue/2306052321.md","localizedDate":"2023年6月5日","excerpt":"

DelayQueue(无界阻塞队列)

\\n

DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1b2e8a6c","path":"/note/java/concurrency/queue/2306052321.html","title":"DelayQueue(无界阻塞队列)","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-05T00:00:00.000Z","order":4,"category":["并发"],"tag":["线程安全","Queue"]},"headers":[{"level":2,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":2,"title":"特性","slug":"特性","link":"#特性","children":[]},{"level":2,"title":"核心属性","slug":"核心属性","link":"#核心属性","children":[]},{"level":2,"title":"入队源码","slug":"入队源码","link":"#入队源码","children":[]},{"level":2,"title":"出队源码","slug":"出队源码","link":"#出队源码","children":[]},{"level":2,"title":"使用场景","slug":"使用场景","link":"#使用场景","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1326},"filePathRelative":"note/java/concurrency/queue/2306052321.md","localizedDate":"2023年6月5日","excerpt":"

DelayQueue(无界阻塞队列)

\\n

DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306052321.html-b65cca12.js b/assets/2306052321.html-ff8efb4e.js similarity index 99% rename from assets/2306052321.html-b65cca12.js rename to assets/2306052321.html-ff8efb4e.js index c8a4595b..14b8005f 100644 --- a/assets/2306052321.html-b65cca12.js +++ b/assets/2306052321.html-ff8efb4e.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as e,d as t,a as n,b as p,e as o}from"./app-1efcbe9f.js";const l={},c=n("h1",{id:"delayqueue-无界阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#delayqueue-无界阻塞队列","aria-hidden":"true"},"#"),p(" DelayQueue(无界阻塞队列)")],-1),i=n("p",null,"DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。",-1),u=o(`

原理

  1. 使用优先级队列实现的无界阻塞队列
  2. 优先级队列(PriorityQueue)与PriorityBlockingQueue类似,不过没有阻塞功能
  3. 线程安全使用ReentrantLock来实现,通过Condition available控制阻塞条件
  4. 入队不阻塞,并且队列没有边界,与优先级队列入队相同
  5. 出队为空时阻塞,不为空时检查堆顶元素过期时间,小于等于0则出队,否则表示元素还未过期,阻塞
  6. 阻塞时先判断leader线程是否为空(为了保证优先级),不为空表示已经有线程阻塞了,为空则将当前线程设置为leader,并按照过期时间进行阻塞

特性

  • 队列中的元素必须实现Delayed接口。在创建元素时,可以定义该元素的存活时间,当从队列获取元素时,只有满足该存活时间的元素才能被取出。
  • 向队列中插入元素的操作(例如put和offer)永远不会被阻塞。只有当队列为空,或者队列中的元素没有到达其存活时间时,获取元素的操作(例如take和poll)才会被阻塞。

核心属性

  • final PriorityQueue q: 实际存储队列元素的数据结构。它是一个优先队列,队列中的元素按到期时间排序,最早到期的元素在队列的头部。

  • Thread leader: 用于标记当前是否有线程在排队(仅用于取元素时) leader 指向的是第一个从队列获取元素阻塞的线程。

  • final transient ReentrantLock lock: 锁,用于控制对队列的并发访问。

  • final Condition available: 条件,用于表示现在是否有可取的元素 当新元素到达,或新线程可能需要成为leader时被通知。

入队源码

public void put(E e) {
+import{_ as s,o as a,c as e,d as t,a as n,b as p,e as o}from"./app-6a63891c.js";const l={},c=n("h1",{id:"delayqueue-无界阻塞队列",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#delayqueue-无界阻塞队列","aria-hidden":"true"},"#"),p(" DelayQueue(无界阻塞队列)")],-1),i=n("p",null,"DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。",-1),u=o(`

原理

  1. 使用优先级队列实现的无界阻塞队列
  2. 优先级队列(PriorityQueue)与PriorityBlockingQueue类似,不过没有阻塞功能
  3. 线程安全使用ReentrantLock来实现,通过Condition available控制阻塞条件
  4. 入队不阻塞,并且队列没有边界,与优先级队列入队相同
  5. 出队为空时阻塞,不为空时检查堆顶元素过期时间,小于等于0则出队,否则表示元素还未过期,阻塞
  6. 阻塞时先判断leader线程是否为空(为了保证优先级),不为空表示已经有线程阻塞了,为空则将当前线程设置为leader,并按照过期时间进行阻塞

特性

  • 队列中的元素必须实现Delayed接口。在创建元素时,可以定义该元素的存活时间,当从队列获取元素时,只有满足该存活时间的元素才能被取出。
  • 向队列中插入元素的操作(例如put和offer)永远不会被阻塞。只有当队列为空,或者队列中的元素没有到达其存活时间时,获取元素的操作(例如take和poll)才会被阻塞。

核心属性

  • final PriorityQueue q: 实际存储队列元素的数据结构。它是一个优先队列,队列中的元素按到期时间排序,最早到期的元素在队列的头部。

  • Thread leader: 用于标记当前是否有线程在排队(仅用于取元素时) leader 指向的是第一个从队列获取元素阻塞的线程。

  • final transient ReentrantLock lock: 锁,用于控制对队列的并发访问。

  • final Condition available: 条件,用于表示现在是否有可取的元素 当新元素到达,或新线程可能需要成为leader时被通知。

入队源码

public void put(E e) {
     offer(e);
 }
 public boolean offer(E e) {
diff --git a/assets/2306210953.html-8549440f.js b/assets/2306210953.html-12b841e7.js
similarity index 96%
rename from assets/2306210953.html-8549440f.js
rename to assets/2306210953.html-12b841e7.js
index d30f5e81..ff5adc8a 100644
--- a/assets/2306210953.html-8549440f.js
+++ b/assets/2306210953.html-12b841e7.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-26c13b6a","path":"/note/framework/springboot/plugin/2306210953.html","title":"spring-boot-maven-plugin 插件详解","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-21T00:00:00.000Z","index":true,"order":1,"category":["SpringBoot"],"tag":["插件"]},"headers":[{"level":2,"title":"spring boot 自带插件的原因","slug":"spring-boot-自带插件的原因","link":"#spring-boot-自带插件的原因","children":[]},{"level":2,"title":"spring boot maven plugin插件详解","slug":"spring-boot-maven-plugin插件详解","link":"#spring-boot-maven-plugin插件详解","children":[{"level":3,"title":"插件标签","slug":"插件标签","link":"#插件标签","children":[]},{"level":3,"title":"内部 goals 详解","slug":"内部-goals-详解","link":"#内部-goals-详解","children":[]},{"level":3,"title":"功能扩展","slug":"功能扩展","link":"#功能扩展","children":[]}]},{"level":2,"title":"知识补充","slug":"知识补充","link":"#知识补充","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.53,"words":1058},"filePathRelative":"note/framework/springboot/plugin/2306210953.md","localizedDate":"2023年6月21日","excerpt":"

spring-boot-maven-plugin 插件详解

\\n

Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring
\\n应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven
\\n插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-26c13b6a","path":"/note/framework/springboot/plugin/2306210953.html","title":"spring-boot-maven-plugin 插件详解","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-21T00:00:00.000Z","index":true,"order":1,"category":["SpringBoot"],"tag":["插件"]},"headers":[{"level":2,"title":"spring boot 自带插件的原因","slug":"spring-boot-自带插件的原因","link":"#spring-boot-自带插件的原因","children":[]},{"level":2,"title":"spring boot maven plugin插件详解","slug":"spring-boot-maven-plugin插件详解","link":"#spring-boot-maven-plugin插件详解","children":[{"level":3,"title":"插件标签","slug":"插件标签","link":"#插件标签","children":[]},{"level":3,"title":"内部 goals 详解","slug":"内部-goals-详解","link":"#内部-goals-详解","children":[]},{"level":3,"title":"功能扩展","slug":"功能扩展","link":"#功能扩展","children":[]}]},{"level":2,"title":"知识补充","slug":"知识补充","link":"#知识补充","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.53,"words":1058},"filePathRelative":"note/framework/springboot/plugin/2306210953.md","localizedDate":"2023年6月21日","excerpt":"

spring-boot-maven-plugin 插件详解

\\n

Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring
\\n应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven
\\n插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2306210953.html-01a62948.js b/assets/2306210953.html-2a1e60b2.js similarity index 99% rename from assets/2306210953.html-01a62948.js rename to assets/2306210953.html-2a1e60b2.js index a955f79f..983556e3 100644 --- a/assets/2306210953.html-01a62948.js +++ b/assets/2306210953.html-2a1e60b2.js @@ -1,4 +1,4 @@ -import{_ as a,o as t,c as p,d as o,a as n,b as s,e}from"./app-1efcbe9f.js";const l={},c=n("h1",{id:"spring-boot-maven-plugin-插件详解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-boot-maven-plugin-插件详解","aria-hidden":"true"},"#"),s(" spring-boot-maven-plugin 插件详解")],-1),i=n("p",null,[s("Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring"),n("br"),s(" 应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven"),n("br"),s(" 插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。")],-1),u=e(`

spring boot 自带插件的原因

Spring Boot 的目标之一是使开发者能够更快速、更便捷地构建 Spring 应用程序。为了实现这个目标,Spring Boot
提供了一系列的开箱即用的功能和约定,其中就包括了自带的 Maven
插件。通过自带插件,开发者可以在项目中快速配置和打包应用程序,而无需手动编写大量的配置代码。同时,这个插件还提供了一些附加功能,如启动应用程序、运行测试等,可以极大地提高开发效率。

spring boot maven plugin插件详解

插件标签

在 Maven 的 pom.xml 文件中,可以使用 build 标签来配置 Spring Boot Maven 插件。常用的配置选项包括:


+import{_ as a,o as t,c as p,d as o,a as n,b as s,e}from"./app-6a63891c.js";const l={},c=n("h1",{id:"spring-boot-maven-plugin-插件详解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-boot-maven-plugin-插件详解","aria-hidden":"true"},"#"),s(" spring-boot-maven-plugin 插件详解")],-1),i=n("p",null,[s("Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring"),n("br"),s(" 应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven"),n("br"),s(" 插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。")],-1),u=e(`

spring boot 自带插件的原因

Spring Boot 的目标之一是使开发者能够更快速、更便捷地构建 Spring 应用程序。为了实现这个目标,Spring Boot
提供了一系列的开箱即用的功能和约定,其中就包括了自带的 Maven
插件。通过自带插件,开发者可以在项目中快速配置和打包应用程序,而无需手动编写大量的配置代码。同时,这个插件还提供了一些附加功能,如启动应用程序、运行测试等,可以极大地提高开发效率。

spring boot maven plugin插件详解

插件标签

在 Maven 的 pom.xml 文件中,可以使用 build 标签来配置 Spring Boot Maven 插件。常用的配置选项包括:


 <build>
     <plugins>
         <plugin>
diff --git a/assets/2306211023.html-ef38ff55.js b/assets/2306211023.html-2cd08382.js
similarity index 99%
rename from assets/2306211023.html-ef38ff55.js
rename to assets/2306211023.html-2cd08382.js
index dc1f74ee..dab59754 100644
--- a/assets/2306211023.html-ef38ff55.js
+++ b/assets/2306211023.html-2cd08382.js
@@ -1,4 +1,4 @@
-import{_ as a,o as t,c as p,d as e,a as n,b as s,e as o}from"./app-1efcbe9f.js";const l={},c=n("h1",{id:"spring-boot-工程-jar-包瘦身",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-boot-工程-jar-包瘦身","aria-hidden":"true"},"#"),s(" spring boot 工程 jar 包瘦身")],-1),i=n("p",null,[s("Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件,该插件的作用就是会将依赖的jar包全部打包进去。"),n("br"),s(" 但是该文件包含了所有的依赖和资源文件,会导致打出来的包会比较大。")],-1),u=n("p",null,"而如果我们使用一般的打包命令时",-1),g=n("div",{class:"language-bash","data-ext":"sh"},[n("pre",{class:"language-bash"},[n("code",null,`mvn clean package
+import{_ as a,o as t,c as p,d as e,a as n,b as s,e as o}from"./app-6a63891c.js";const l={},c=n("h1",{id:"spring-boot-工程-jar-包瘦身",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#spring-boot-工程-jar-包瘦身","aria-hidden":"true"},"#"),s(" spring boot 工程 jar 包瘦身")],-1),i=n("p",null,[s("Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件,该插件的作用就是会将依赖的jar包全部打包进去。"),n("br"),s(" 但是该文件包含了所有的依赖和资源文件,会导致打出来的包会比较大。")],-1),u=n("p",null,"而如果我们使用一般的打包命令时",-1),g=n("div",{class:"language-bash","data-ext":"sh"},[n("pre",{class:"language-bash"},[n("code",null,`mvn clean package
 `)])],-1),r=n("p",null,"不会把依赖的jar包也打进去,这样打出来的包就会很小。",-1),k=o(`

问题描述

但当一个系统上线运行后,肯定会有需求迭代和Bug修复,那也就免不了进行重新打包部署。

此时有一个场景:线上有一个紧急BUG,并且BUG很快被定位并且修复,其实就是一行代码的事情,现在代码修改好了并且完成了构建然后开始打包交付时发现,jar包很大,一直在上传。

而当经常需要迭代发布,每一次都上传一个庞大的jar包文件,会浪费很多时间。

此时就需要为jar包瘦瘦身,降低jar包文件的大小。

瘦身原理

一个spring boot正常的打包,包含所有依赖时打出的jar包有120多M:

解压查看内部有三个包,BOOT-INFMETA-INFOorg

打开BOOT-INF

  • classes: 存放当前项目编译好的代码,这部分是非常小的。
  • lib: 存放我们所依赖的 jar 包,lib部分会很大。

原理

当一个项目的依赖越多,lib包就会越大。虽然依赖多,但是当版本迭代稳定之后,依赖基本就不会再变动了。

我们就可以把这些不变的依赖提前都放到服务器上,打包的时候忽略这些依赖,只打本项目编译好的文件,这样打出来的jar包就会很小,提高发版效率。

配置实现


 <build>
     <plugins>
diff --git a/assets/2306211023.html-831dca01.js b/assets/2306211023.html-af1f7eaa.js
similarity index 92%
rename from assets/2306211023.html-831dca01.js
rename to assets/2306211023.html-af1f7eaa.js
index 5729378b..01be23d8 100644
--- a/assets/2306211023.html-831dca01.js
+++ b/assets/2306211023.html-af1f7eaa.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-3b468bb2","path":"/note/framework/springboot/plugin/2306211023.html","title":"spring boot 工程 jar 包瘦身","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-21T00:00:00.000Z","index":true,"order":2,"category":["SpringBoot"],"tag":["插件"]},"headers":[{"level":2,"title":"问题描述","slug":"问题描述","link":"#问题描述","children":[]},{"level":2,"title":"瘦身原理","slug":"瘦身原理","link":"#瘦身原理","children":[{"level":3,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":3,"title":"配置实现","slug":"配置实现","link":"#配置实现","children":[]},{"level":3,"title":"问题","slug":"问题","link":"#问题","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.72,"words":1116},"filePathRelative":"note/framework/springboot/plugin/2306211023.md","localizedDate":"2023年6月21日","excerpt":"

spring boot 工程 jar 包瘦身

\\n

Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件,该插件的作用就是会将依赖的jar包全部打包进去。
\\n但是该文件包含了所有的依赖和资源文件,会导致打出来的包会比较大。

\\n

而如果我们使用一般的打包命令时

\\n
mvn clean package\\n

不会把依赖的jar包也打进去,这样打出来的包就会很小。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-3b468bb2","path":"/note/framework/springboot/plugin/2306211023.html","title":"spring boot 工程 jar 包瘦身","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-06-21T00:00:00.000Z","index":true,"order":2,"category":["SpringBoot"],"tag":["插件"]},"headers":[{"level":2,"title":"问题描述","slug":"问题描述","link":"#问题描述","children":[]},{"level":2,"title":"瘦身原理","slug":"瘦身原理","link":"#瘦身原理","children":[{"level":3,"title":"原理","slug":"原理","link":"#原理","children":[]},{"level":3,"title":"配置实现","slug":"配置实现","link":"#配置实现","children":[]},{"level":3,"title":"问题","slug":"问题","link":"#问题","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.72,"words":1116},"filePathRelative":"note/framework/springboot/plugin/2306211023.md","localizedDate":"2023年6月21日","excerpt":"

spring boot 工程 jar 包瘦身

\\n

Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件,该插件的作用就是会将依赖的jar包全部打包进去。
\\n但是该文件包含了所有的依赖和资源文件,会导致打出来的包会比较大。

\\n

而如果我们使用一般的打包命令时

\\n
mvn clean package\\n

不会把依赖的jar包也打进去,这样打出来的包就会很小。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2307051931.html-30066e1f.js b/assets/2307051931.html-43434fe3.js similarity index 99% rename from assets/2307051931.html-30066e1f.js rename to assets/2307051931.html-43434fe3.js index a2eb205d..5368b60a 100644 --- a/assets/2307051931.html-30066e1f.js +++ b/assets/2307051931.html-43434fe3.js @@ -1,4 +1,4 @@ -import{_ as s,r as e,o as t,c as l,a,b as r,f as i,e as d}from"./app-1efcbe9f.js";const c={},p=d(`

K8S + Flannel 公网IP搭建笔记

159.75.154.193 master 1.12.242.126 node1 139.159.219.202 node2 101.34.229.23 node3

版本简介

软件版本信息

名称版本
docker10.10.21
kubernetes1.21.0-0
Flannel0.20.2

集群角色规划

服务商内核公网IP节点
腾讯云centOS7.6159.75.154.193master
腾讯云centOS7.61.12.242.126node1
华为云centOS7.6139.159.219.202node2
腾讯云centOS7.6101.34.229.23node3

准备工作

修改服务器名称

修改所有的服务器名称,使用命令

# instanceName 处填写每个服务器的名称
+import{_ as s,r as e,o as t,c as l,a,b as r,f as i,e as d}from"./app-6a63891c.js";const c={},p=d(`

K8S + Flannel 公网IP搭建笔记

159.75.154.193 master 1.12.242.126 node1 139.159.219.202 node2 101.34.229.23 node3

版本简介

软件版本信息

名称版本
docker10.10.21
kubernetes1.21.0-0
Flannel0.20.2

集群角色规划

服务商内核公网IP节点
腾讯云centOS7.6159.75.154.193master
腾讯云centOS7.61.12.242.126node1
华为云centOS7.6139.159.219.202node2
腾讯云centOS7.6101.34.229.23node3

准备工作

修改服务器名称

修改所有的服务器名称,使用命令

# instanceName 处填写每个服务器的名称
 sudo hostnamectl set-hostname <instanceName>
 

修改服务器hosts文件

修改所有服务器的hosts文件内容,往hosts文件中添加我们的服务器公网ip和对应的服务器名称

sudo tee -a /etc/hosts <<EOF
 159.75.154.193 master
diff --git a/assets/2307051931.html-0784e57a.js b/assets/2307051931.html-953e69bb.js
similarity index 97%
rename from assets/2307051931.html-0784e57a.js
rename to assets/2307051931.html-953e69bb.js
index cd3834f0..d53cbe52 100644
--- a/assets/2307051931.html-0784e57a.js
+++ b/assets/2307051931.html-953e69bb.js
@@ -1 +1 @@
-const l=JSON.parse('{"key":"v-f0b271fa","path":"/note/other/2307051931.html","title":"K8S + Flannel 公网IP搭建笔记","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-07-05T00:00:00.000Z","index":true,"category":["kubernetes"],"tag":["kubernetes"]},"headers":[{"level":2,"title":"版本简介","slug":"版本简介","link":"#版本简介","children":[{"level":3,"title":"软件版本信息","slug":"软件版本信息","link":"#软件版本信息","children":[]},{"level":3,"title":"集群角色规划","slug":"集群角色规划","link":"#集群角色规划","children":[]}]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[{"level":3,"title":"修改服务器名称","slug":"修改服务器名称","link":"#修改服务器名称","children":[]},{"level":3,"title":"修改服务器hosts文件","slug":"修改服务器hosts文件","link":"#修改服务器hosts文件","children":[]},{"level":3,"title":"创建虚拟网卡","slug":"创建虚拟网卡","link":"#创建虚拟网卡","children":[]},{"level":3,"title":"云服务器安全组设置","slug":"云服务器安全组设置","link":"#云服务器安全组设置","children":[]}]},{"level":2,"title":"基础配置","slug":"基础配置","link":"#基础配置","children":[{"level":3,"title":"更新安装依赖","slug":"更新安装依赖","link":"#更新安装依赖","children":[]},{"level":3,"title":"关闭SELinux","slug":"关闭selinux","link":"#关闭selinux","children":[]},{"level":3,"title":"关闭swap","slug":"关闭swap","link":"#关闭swap","children":[]},{"level":3,"title":"配置iptables的ACCEPT规则","slug":"配置iptables的accept规则","link":"#配置iptables的accept规则","children":[]},{"level":3,"title":"设置系统参数","slug":"设置系统参数","link":"#设置系统参数","children":[]},{"level":3,"title":"重启服务器","slug":"重启服务器","link":"#重启服务器","children":[]}]},{"level":2,"title":"安装Docker","slug":"安装docker","link":"#安装docker","children":[{"level":3,"title":"配置阿里云镜像","slug":"配置阿里云镜像","link":"#配置阿里云镜像","children":[]},{"level":3,"title":"安装(指定20.10.21版本)","slug":"安装-指定20-10-21版本","link":"#安装-指定20-10-21版本","children":[]},{"level":3,"title":"启动","slug":"启动","link":"#启动","children":[]},{"level":3,"title":"开启自启动","slug":"开启自启动","link":"#开启自启动","children":[]},{"level":3,"title":"查看版本","slug":"查看版本","link":"#查看版本","children":[]}]},{"level":2,"title":"安装k8s及组件","slug":"安装k8s及组件","link":"#安装k8s及组件","children":[{"level":3,"title":"配置yum源","slug":"配置yum源","link":"#配置yum源","children":[]},{"level":3,"title":"安装(指定1.21.0-0版本)","slug":"安装-指定1-21-0-0版本","link":"#安装-指定1-21-0-0版本","children":[]},{"level":3,"title":"修改daemon.json文件","slug":"修改daemon-json文件","link":"#修改daemon-json文件","children":[]},{"level":3,"title":"修改 kubelet启动参数","slug":"修改-kubelet启动参数","link":"#修改-kubelet启动参数","children":[]},{"level":3,"title":"重新加载","slug":"重新加载","link":"#重新加载","children":[]},{"level":3,"title":"启动kubelet","slug":"启动kubelet","link":"#启动kubelet","children":[]},{"level":3,"title":"拉取镜像","slug":"拉取镜像","link":"#拉取镜像","children":[]}]},{"level":2,"title":"搭建集群(仅master节点执行)","slug":"搭建集群-仅master节点执行","link":"#搭建集群-仅master节点执行","children":[{"level":3,"title":"初始化master节点","slug":"初始化master节点","link":"#初始化master节点","children":[]},{"level":3,"title":"集群健康检查","slug":"集群健康检查","link":"#集群健康检查","children":[]},{"level":3,"title":"修改kube-apiserver.yaml配置","slug":"修改kube-apiserver-yaml配置","link":"#修改kube-apiserver-yaml配置","children":[]},{"level":3,"title":"检查集群状态","slug":"检查集群状态","link":"#检查集群状态","children":[]}]},{"level":2,"title":"安装flannel插件 (仅在master执行)","slug":"安装flannel插件-仅在master执行","link":"#安装flannel插件-仅在master执行","children":[{"level":3,"title":"安装flannel","slug":"安装flannel","link":"#安装flannel","children":[]},{"level":3,"title":"修改vim kube-flannel.yml文件","slug":"修改vim-kube-flannel-yml文件","link":"#修改vim-kube-flannel-yml文件","children":[]},{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]}]},{"level":2,"title":"从节点加入集群","slug":"从节点加入集群","link":"#从节点加入集群","children":[{"level":3,"title":"所有从节点执行上面保存的kubeadm join","slug":"所有从节点执行上面保存的kubeadm-join","link":"#所有从节点执行上面保存的kubeadm-join","children":[]},{"level":3,"title":"从新获取","slug":"从新获取","link":"#从新获取","children":[]}]},{"level":2,"title":"自定义Pod测试","slug":"自定义pod测试","link":"#自定义pod测试","children":[{"level":3,"title":"添加文件","slug":"添加文件","link":"#添加文件","children":[]},{"level":3,"title":"执行","slug":"执行","link":"#执行","children":[]},{"level":3,"title":"查看对外暴露的端口","slug":"查看对外暴露的端口","link":"#查看对外暴露的端口","children":[]},{"level":3,"title":"查看pod提供的服务","slug":"查看pod提供的服务","link":"#查看pod提供的服务","children":[]},{"level":3,"title":"访问","slug":"访问","link":"#访问","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.65,"words":1995},"filePathRelative":"note/other/2307051931.md","localizedDate":"2023年7月5日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data};
+const l=JSON.parse('{"key":"v-f0b271fa","path":"/note/other/2307051931.html","title":"K8S + Flannel 公网IP搭建笔记","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-07-05T00:00:00.000Z","index":true,"category":["kubernetes"],"tag":["kubernetes"]},"headers":[{"level":2,"title":"版本简介","slug":"版本简介","link":"#版本简介","children":[{"level":3,"title":"软件版本信息","slug":"软件版本信息","link":"#软件版本信息","children":[]},{"level":3,"title":"集群角色规划","slug":"集群角色规划","link":"#集群角色规划","children":[]}]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[{"level":3,"title":"修改服务器名称","slug":"修改服务器名称","link":"#修改服务器名称","children":[]},{"level":3,"title":"修改服务器hosts文件","slug":"修改服务器hosts文件","link":"#修改服务器hosts文件","children":[]},{"level":3,"title":"创建虚拟网卡","slug":"创建虚拟网卡","link":"#创建虚拟网卡","children":[]},{"level":3,"title":"云服务器安全组设置","slug":"云服务器安全组设置","link":"#云服务器安全组设置","children":[]}]},{"level":2,"title":"基础配置","slug":"基础配置","link":"#基础配置","children":[{"level":3,"title":"更新安装依赖","slug":"更新安装依赖","link":"#更新安装依赖","children":[]},{"level":3,"title":"关闭SELinux","slug":"关闭selinux","link":"#关闭selinux","children":[]},{"level":3,"title":"关闭swap","slug":"关闭swap","link":"#关闭swap","children":[]},{"level":3,"title":"配置iptables的ACCEPT规则","slug":"配置iptables的accept规则","link":"#配置iptables的accept规则","children":[]},{"level":3,"title":"设置系统参数","slug":"设置系统参数","link":"#设置系统参数","children":[]},{"level":3,"title":"重启服务器","slug":"重启服务器","link":"#重启服务器","children":[]}]},{"level":2,"title":"安装Docker","slug":"安装docker","link":"#安装docker","children":[{"level":3,"title":"配置阿里云镜像","slug":"配置阿里云镜像","link":"#配置阿里云镜像","children":[]},{"level":3,"title":"安装(指定20.10.21版本)","slug":"安装-指定20-10-21版本","link":"#安装-指定20-10-21版本","children":[]},{"level":3,"title":"启动","slug":"启动","link":"#启动","children":[]},{"level":3,"title":"开启自启动","slug":"开启自启动","link":"#开启自启动","children":[]},{"level":3,"title":"查看版本","slug":"查看版本","link":"#查看版本","children":[]}]},{"level":2,"title":"安装k8s及组件","slug":"安装k8s及组件","link":"#安装k8s及组件","children":[{"level":3,"title":"配置yum源","slug":"配置yum源","link":"#配置yum源","children":[]},{"level":3,"title":"安装(指定1.21.0-0版本)","slug":"安装-指定1-21-0-0版本","link":"#安装-指定1-21-0-0版本","children":[]},{"level":3,"title":"修改daemon.json文件","slug":"修改daemon-json文件","link":"#修改daemon-json文件","children":[]},{"level":3,"title":"修改 kubelet启动参数","slug":"修改-kubelet启动参数","link":"#修改-kubelet启动参数","children":[]},{"level":3,"title":"重新加载","slug":"重新加载","link":"#重新加载","children":[]},{"level":3,"title":"启动kubelet","slug":"启动kubelet","link":"#启动kubelet","children":[]},{"level":3,"title":"拉取镜像","slug":"拉取镜像","link":"#拉取镜像","children":[]}]},{"level":2,"title":"搭建集群(仅master节点执行)","slug":"搭建集群-仅master节点执行","link":"#搭建集群-仅master节点执行","children":[{"level":3,"title":"初始化master节点","slug":"初始化master节点","link":"#初始化master节点","children":[]},{"level":3,"title":"集群健康检查","slug":"集群健康检查","link":"#集群健康检查","children":[]},{"level":3,"title":"修改kube-apiserver.yaml配置","slug":"修改kube-apiserver-yaml配置","link":"#修改kube-apiserver-yaml配置","children":[]},{"level":3,"title":"检查集群状态","slug":"检查集群状态","link":"#检查集群状态","children":[]}]},{"level":2,"title":"安装flannel插件 (仅在master执行)","slug":"安装flannel插件-仅在master执行","link":"#安装flannel插件-仅在master执行","children":[{"level":3,"title":"安装flannel","slug":"安装flannel","link":"#安装flannel","children":[]},{"level":3,"title":"修改vim kube-flannel.yml文件","slug":"修改vim-kube-flannel-yml文件","link":"#修改vim-kube-flannel-yml文件","children":[]},{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]}]},{"level":2,"title":"从节点加入集群","slug":"从节点加入集群","link":"#从节点加入集群","children":[{"level":3,"title":"所有从节点执行上面保存的kubeadm join","slug":"所有从节点执行上面保存的kubeadm-join","link":"#所有从节点执行上面保存的kubeadm-join","children":[]},{"level":3,"title":"从新获取","slug":"从新获取","link":"#从新获取","children":[]}]},{"level":2,"title":"自定义Pod测试","slug":"自定义pod测试","link":"#自定义pod测试","children":[{"level":3,"title":"添加文件","slug":"添加文件","link":"#添加文件","children":[]},{"level":3,"title":"执行","slug":"执行","link":"#执行","children":[]},{"level":3,"title":"查看对外暴露的端口","slug":"查看对外暴露的端口","link":"#查看对外暴露的端口","children":[]},{"level":3,"title":"查看pod提供的服务","slug":"查看pod提供的服务","link":"#查看pod提供的服务","children":[]},{"level":3,"title":"访问","slug":"访问","link":"#访问","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.65,"words":1995},"filePathRelative":"note/other/2307051931.md","localizedDate":"2023年7月5日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data};
diff --git a/assets/2307062006.html-b05c085f.js b/assets/2307062006.html-13b4fbb5.js
similarity index 92%
rename from assets/2307062006.html-b05c085f.js
rename to assets/2307062006.html-13b4fbb5.js
index 4fcb8c50..bfdfd54b 100644
--- a/assets/2307062006.html-b05c085f.js
+++ b/assets/2307062006.html-13b4fbb5.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-59c0c6b8","path":"/note/other/2307062006.html","title":"博客整合Github Actions实现自动发布记录","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"服务器操作","slug":"服务器操作","link":"#服务器操作","children":[{"level":3,"title":"创建密钥","slug":"创建密钥","link":"#创建密钥","children":[]},{"level":3,"title":"开启ssh登录","slug":"开启ssh登录","link":"#开启ssh登录","children":[]},{"level":3,"title":"创建git仓库","slug":"创建git仓库","link":"#创建git仓库","children":[]},{"level":3,"title":"修改配置","slug":"修改配置","link":"#修改配置","children":[]}]},{"level":2,"title":"github操作","slug":"github操作","link":"#github操作","children":[]},{"level":2,"title":"github 项目添加配置","slug":"github-项目添加配置","link":"#github-项目添加配置","children":[{"level":3,"title":"创建github账号访问令牌","slug":"创建github账号访问令牌","link":"#创建github账号访问令牌","children":[]},{"level":3,"title":"创建 github 项目访问令牌","slug":"创建-github-项目访问令牌","link":"#创建-github-项目访问令牌","children":[]}]},{"level":2,"title":"博客","slug":"博客","link":"#博客","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.89,"words":568},"filePathRelative":"note/other/2307062006.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
+const e=JSON.parse('{"key":"v-59c0c6b8","path":"/note/other/2307062006.html","title":"博客整合Github Actions实现自动发布记录","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"服务器操作","slug":"服务器操作","link":"#服务器操作","children":[{"level":3,"title":"创建密钥","slug":"创建密钥","link":"#创建密钥","children":[]},{"level":3,"title":"开启ssh登录","slug":"开启ssh登录","link":"#开启ssh登录","children":[]},{"level":3,"title":"创建git仓库","slug":"创建git仓库","link":"#创建git仓库","children":[]},{"level":3,"title":"修改配置","slug":"修改配置","link":"#修改配置","children":[]}]},{"level":2,"title":"github操作","slug":"github操作","link":"#github操作","children":[]},{"level":2,"title":"github 项目添加配置","slug":"github-项目添加配置","link":"#github-项目添加配置","children":[{"level":3,"title":"创建github账号访问令牌","slug":"创建github账号访问令牌","link":"#创建github账号访问令牌","children":[]},{"level":3,"title":"创建 github 项目访问令牌","slug":"创建-github-项目访问令牌","link":"#创建-github-项目访问令牌","children":[]}]},{"level":2,"title":"博客","slug":"博客","link":"#博客","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.89,"words":568},"filePathRelative":"note/other/2307062006.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data};
diff --git a/assets/2307062006.html-9e3117c4.js b/assets/2307062006.html-214b1769.js
similarity index 99%
rename from assets/2307062006.html-9e3117c4.js
rename to assets/2307062006.html-214b1769.js
index 40a5422b..546d0dcf 100644
--- a/assets/2307062006.html-9e3117c4.js
+++ b/assets/2307062006.html-214b1769.js
@@ -1,4 +1,4 @@
-import{_ as s,o as n,c as a,e}from"./app-1efcbe9f.js";const i={},l=e(`

博客整合Github Actions实现自动发布记录

博客网站结合git提供的快速博客实现,现记录提交文档时实现自动发布,并复制一份到自己的远程服务器,通过域名访问远程服务器进而访问博客

服务器操作

创建密钥

ssh-keygen -t rsa -b 4096
+import{_ as s,o as n,c as a,e}from"./app-6a63891c.js";const i={},l=e(`

博客整合Github Actions实现自动发布记录

博客网站结合git提供的快速博客实现,现记录提交文档时实现自动发布,并复制一份到自己的远程服务器,通过域名访问远程服务器进而访问博客

服务器操作

创建密钥

ssh-keygen -t rsa -b 4096
 
  • 密钥文件保存位置可以修改,如果不想修改可以直接Enter,默认的文件保存路径在 ~/.ssh/ 文件夹下
  • 命令会生成两个文件,分别为id_rsa.pub(公钥)和id_rsa(私钥)

开启ssh登录

  • 复制公钥内内容到~/.ssh/authorized_keys文件中
cd ~/.ssh/
 cp id_rsa.pub authorized_keys
 
  • 修改文件访问权限
chmod 600 ~/.ssh/authorized_keys
diff --git a/assets/2311071055.html-fff34505.js b/assets/2311071055.html-67906c7c.js
similarity index 99%
rename from assets/2311071055.html-fff34505.js
rename to assets/2311071055.html-67906c7c.js
index a1de6cb9..6aa72b72 100644
--- a/assets/2311071055.html-fff34505.js
+++ b/assets/2311071055.html-67906c7c.js
@@ -1,4 +1,4 @@
-import{_ as t,r as p,o,c,d as i,a as n,b as a,f as l,w as u,e as s}from"./app-1efcbe9f.js";const r={},d=n("h1",{id:"深入理解synchronized",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#深入理解synchronized","aria-hidden":"true"},"#"),a(" 深入理解Synchronized")],-1),k=n("p",null,[n("strong",null,"synchronized"),a("关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。"),n("br"),n("strong",null,"synchronized"),a("关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。")],-1),h=s(`

基本概念

临界资源

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区,其共享资源为临界资源

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

Synchronized

在Java中,所有的对象都有一个内置的锁。当一个线程进入一个synchronized方法或代码块时,它会获取这个锁,并在执行完毕后释放这个锁。其他任何尝试进入这个方法或代码块的线程都会被阻塞,直到当前线程释放锁。

synchronized关键字可以应用于实例方法、静态方法以及代码块。当它应用于实例方法时,锁是与当前对象实例关联的。当它应用于静态方法时,锁是与当前类关联的。当它应用于代码块时,锁是与当前对象实例或类关联的。

基本用法

Synchronized方法

当你声明一个方法为synchronized时,这个方法在同一时刻只能被一个线程访问。例如:

/**
+import{_ as t,r as p,o,c,d as i,a as n,b as a,f as l,w as u,e as s}from"./app-6a63891c.js";const r={},d=n("h1",{id:"深入理解synchronized",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#深入理解synchronized","aria-hidden":"true"},"#"),a(" 深入理解Synchronized")],-1),k=n("p",null,[n("strong",null,"synchronized"),a("关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。"),n("br"),n("strong",null,"synchronized"),a("关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。")],-1),h=s(`

基本概念

临界资源

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区,其共享资源为临界资源

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

Synchronized

在Java中,所有的对象都有一个内置的锁。当一个线程进入一个synchronized方法或代码块时,它会获取这个锁,并在执行完毕后释放这个锁。其他任何尝试进入这个方法或代码块的线程都会被阻塞,直到当前线程释放锁。

synchronized关键字可以应用于实例方法、静态方法以及代码块。当它应用于实例方法时,锁是与当前对象实例关联的。当它应用于静态方法时,锁是与当前类关联的。当它应用于代码块时,锁是与当前对象实例或类关联的。

基本用法

Synchronized方法

当你声明一个方法为synchronized时,这个方法在同一时刻只能被一个线程访问。例如:

/**
 * 实例方法,锁的是该类的实例对象
 */
 public synchronized void synchronizedMethod() {  
diff --git a/assets/2311071055.html-e32999dd.js b/assets/2311071055.html-b55ba638.js
similarity index 95%
rename from assets/2311071055.html-e32999dd.js
rename to assets/2311071055.html-b55ba638.js
index da5f86f0..f40ba114 100644
--- a/assets/2311071055.html-e32999dd.js
+++ b/assets/2311071055.html-b55ba638.js
@@ -1 +1 @@
-const l=JSON.parse('{"key":"v-c5b336ca","path":"/note/java/jvm/2311071055.html","title":"深入理解Synchronized","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":5,"date":"2023-11-07T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","Lock"]},"headers":[{"level":2,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[{"level":3,"title":"临界资源","slug":"临界资源","link":"#临界资源","children":[]},{"level":3,"title":"Synchronized","slug":"synchronized","link":"#synchronized","children":[]}]},{"level":2,"title":"基本用法","slug":"基本用法","link":"#基本用法","children":[{"level":3,"title":"Synchronized方法","slug":"synchronized方法","link":"#synchronized方法","children":[]},{"level":3,"title":"Synchronized代码块","slug":"synchronized代码块","link":"#synchronized代码块","children":[]}]},{"level":2,"title":"深入理解","slug":"深入理解","link":"#深入理解","children":[{"level":3,"title":"字节码层面的实现","slug":"字节码层面的实现","link":"#字节码层面的实现","children":[]},{"level":3,"title":"Monitor(管程/监视器)机制","slug":"monitor-管程-监视器-机制","link":"#monitor-管程-监视器-机制","children":[]}]},{"level":2,"title":"锁实现","slug":"锁实现","link":"#锁实现","children":[{"level":3,"title":"偏向锁","slug":"偏向锁","link":"#偏向锁","children":[]},{"level":3,"title":"轻量锁","slug":"轻量锁","link":"#轻量锁","children":[]},{"level":3,"title":"重量锁","slug":"重量锁","link":"#重量锁","children":[]},{"level":3,"title":"锁升级流程","slug":"锁升级流程","link":"#锁升级流程","children":[]}]},{"level":2,"title":"锁优化进阶","slug":"锁优化进阶","link":"#锁优化进阶","children":[{"level":3,"title":"批量重偏向&批量撤销","slug":"批量重偏向-批量撤销","link":"#批量重偏向-批量撤销","children":[]},{"level":3,"title":"自旋优化","slug":"自旋优化","link":"#自旋优化","children":[]},{"level":3,"title":"锁粗化","slug":"锁粗化","link":"#锁粗化","children":[]},{"level":3,"title":"锁消除","slug":"锁消除","link":"#锁消除","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":11.29,"words":3386},"filePathRelative":"note/java/jvm/2311071055.md","localizedDate":"2023年11月7日","excerpt":"

深入理解Synchronized

\\n

synchronized关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。
\\nsynchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; +const l=JSON.parse('{"key":"v-c5b336ca","path":"/note/java/jvm/2311071055.html","title":"深入理解Synchronized","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":5,"date":"2023-11-07T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","Lock"]},"headers":[{"level":2,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[{"level":3,"title":"临界资源","slug":"临界资源","link":"#临界资源","children":[]},{"level":3,"title":"Synchronized","slug":"synchronized","link":"#synchronized","children":[]}]},{"level":2,"title":"基本用法","slug":"基本用法","link":"#基本用法","children":[{"level":3,"title":"Synchronized方法","slug":"synchronized方法","link":"#synchronized方法","children":[]},{"level":3,"title":"Synchronized代码块","slug":"synchronized代码块","link":"#synchronized代码块","children":[]}]},{"level":2,"title":"深入理解","slug":"深入理解","link":"#深入理解","children":[{"level":3,"title":"字节码层面的实现","slug":"字节码层面的实现","link":"#字节码层面的实现","children":[]},{"level":3,"title":"Monitor(管程/监视器)机制","slug":"monitor-管程-监视器-机制","link":"#monitor-管程-监视器-机制","children":[]}]},{"level":2,"title":"锁实现","slug":"锁实现","link":"#锁实现","children":[{"level":3,"title":"偏向锁","slug":"偏向锁","link":"#偏向锁","children":[]},{"level":3,"title":"轻量锁","slug":"轻量锁","link":"#轻量锁","children":[]},{"level":3,"title":"重量锁","slug":"重量锁","link":"#重量锁","children":[]},{"level":3,"title":"锁升级流程","slug":"锁升级流程","link":"#锁升级流程","children":[]}]},{"level":2,"title":"锁优化进阶","slug":"锁优化进阶","link":"#锁优化进阶","children":[{"level":3,"title":"批量重偏向&批量撤销","slug":"批量重偏向-批量撤销","link":"#批量重偏向-批量撤销","children":[]},{"level":3,"title":"自旋优化","slug":"自旋优化","link":"#自旋优化","children":[]},{"level":3,"title":"锁粗化","slug":"锁粗化","link":"#锁粗化","children":[]},{"level":3,"title":"锁消除","slug":"锁消除","link":"#锁消除","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":11.29,"words":3386},"filePathRelative":"note/java/jvm/2311071055.md","localizedDate":"2023年11月7日","excerpt":"

深入理解Synchronized

\\n

synchronized关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。
\\nsynchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2311081011.html-6fc54cd5.js b/assets/2311081011.html-3d7ff885.js similarity index 94% rename from assets/2311081011.html-6fc54cd5.js rename to assets/2311081011.html-3d7ff885.js index 2eee2d23..5788dd36 100644 --- a/assets/2311081011.html-6fc54cd5.js +++ b/assets/2311081011.html-3d7ff885.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-91f8f38c","path":"/note/java/jvm/2311081011.html","title":"运行时数据区详解(内存模型)","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":4,"date":"2023-11-08T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"方法区(Method Area)","slug":"方法区-method-area","link":"#方法区-method-area","children":[{"level":3,"title":"运行时常量池(Runtime Constant Pool)","slug":"运行时常量池-runtime-constant-pool","link":"#运行时常量池-runtime-constant-pool","children":[]},{"level":3,"title":"直接内存(Direct Memory)","slug":"直接内存-direct-memory","link":"#直接内存-direct-memory","children":[]}]},{"level":2,"title":"堆内存(Heap)","slug":"堆内存-heap","link":"#堆内存-heap","children":[]},{"level":2,"title":"虚拟机栈(Virtual Machine Stack)","slug":"虚拟机栈-virtual-machine-stack","link":"#虚拟机栈-virtual-machine-stack","children":[{"level":3,"title":"局部变量表(LVT)","slug":"局部变量表-lvt","link":"#局部变量表-lvt","children":[]},{"level":3,"title":"操作数栈(OS)","slug":"操作数栈-os","link":"#操作数栈-os","children":[]},{"level":3,"title":"动态链接","slug":"动态链接","link":"#动态链接","children":[]}]},{"level":2,"title":"本地方法栈(Native Method Stack)","slug":"本地方法栈-native-method-stack","link":"#本地方法栈-native-method-stack","children":[]},{"level":2,"title":"程序计数器(Program Counter Register)","slug":"程序计数器-program-counter-register","link":"#程序计数器-program-counter-register","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.27,"words":1881},"filePathRelative":"note/java/jvm/2311081011.md","localizedDate":"2023年11月8日","excerpt":"

运行时数据区详解(内存模型)

\\n

JVM运行时数据区数Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,其中包含:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-91f8f38c","path":"/note/java/jvm/2311081011.html","title":"运行时数据区详解(内存模型)","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":4,"date":"2023-11-08T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"方法区(Method Area)","slug":"方法区-method-area","link":"#方法区-method-area","children":[{"level":3,"title":"运行时常量池(Runtime Constant Pool)","slug":"运行时常量池-runtime-constant-pool","link":"#运行时常量池-runtime-constant-pool","children":[]},{"level":3,"title":"直接内存(Direct Memory)","slug":"直接内存-direct-memory","link":"#直接内存-direct-memory","children":[]}]},{"level":2,"title":"堆内存(Heap)","slug":"堆内存-heap","link":"#堆内存-heap","children":[]},{"level":2,"title":"虚拟机栈(Virtual Machine Stack)","slug":"虚拟机栈-virtual-machine-stack","link":"#虚拟机栈-virtual-machine-stack","children":[{"level":3,"title":"局部变量表(LVT)","slug":"局部变量表-lvt","link":"#局部变量表-lvt","children":[]},{"level":3,"title":"操作数栈(OS)","slug":"操作数栈-os","link":"#操作数栈-os","children":[]},{"level":3,"title":"动态链接","slug":"动态链接","link":"#动态链接","children":[]}]},{"level":2,"title":"本地方法栈(Native Method Stack)","slug":"本地方法栈-native-method-stack","link":"#本地方法栈-native-method-stack","children":[]},{"level":2,"title":"程序计数器(Program Counter Register)","slug":"程序计数器-program-counter-register","link":"#程序计数器-program-counter-register","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.27,"words":1881},"filePathRelative":"note/java/jvm/2311081011.md","localizedDate":"2023年11月8日","excerpt":"

运行时数据区详解(内存模型)

\\n

JVM运行时数据区数Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,其中包含:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2311081011.html-c9392a00.js b/assets/2311081011.html-beebc081.js similarity index 98% rename from assets/2311081011.html-c9392a00.js rename to assets/2311081011.html-beebc081.js index 987b52d4..10ff7c8d 100644 --- a/assets/2311081011.html-c9392a00.js +++ b/assets/2311081011.html-beebc081.js @@ -1 +1 @@ -import{_ as r,o as t,c as o,d as n,a,b as e,e as i}from"./app-1efcbe9f.js";const h={},s=a("h1",{id:"运行时数据区详解-内存模型",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#运行时数据区详解-内存模型","aria-hidden":"true"},"#"),e(" 运行时数据区详解(内存模型)")],-1),d=a("p",null,[e("JVM运行时数据区数Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,其中包含:"),a("strong",null,"方法区、堆内存、虚拟机栈、本地方法栈、程序计数器"),e("。")],-1),c=i('

方法区(Method Area)

方法区是线程间共享的区域,在JVM启动时创建,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

方法区可以被实现成大小固定或可动态扩展和收缩,如果内存空间不满足内存分配要求就会抛出OutOfMemoryError异常。

对于HotSpot虚拟机而言,在JDK 1.8以前,方法区被实现为 “永久代”(Permanent Generation),属于堆的逻辑组成部分,并提供了两个参数调节其大小,-XX:PermSize用于设定初始容量,-XX:MaxPermSize用于设定最大容量。JDK 1.8之后,HotSpot不再有“永久代”的概念,类的元信息数据迁移到被称为“元空间”(Metaspace)的新区域,而静态变量、常量等则存储于堆中。元空间没有使用堆内存,而是分配在 本地内存(直接内存) 中,默认情况下其容量只受可用的本地内存大小限制。类似地,HotShot虚拟机也提供了两个参数来调节其大小, -XX:MetaspaceSize用于设定初始容量,-XX:MaxMetaspaceSize用于设定最大容量

运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量符号引用,这部分内容将在类加载之后进入到方法的运行常量池中存放。

字面值常量:

  • 字符串字面量
  • 用final修饰的基础类型成员变量的字面值
  • 由字面值常量相加得到的结果

直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,因此直接内存的分配不受Java堆大小的限制,但是还是会受到本机总内存(RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。

直接内存主要用于NIO类库,实现基于通道(Channel)和缓冲区(Buffer)的IO方式。通常,当需要处理大量数据的读写操作时,可以考虑使用直接内存,例如文件传输、网络编程等。

直接内存优点在于可以避免在Java堆和本地堆之间复制数据,提高IO操作的性能,缺点就是分配和释放代价较高,而且不受JVM垃圾回收的管理,容易造成内存溢出。

直接内存可以通过ByteBuffer类的allocateDirect方法来分配和操作,

堆内存(Heap)

堆内存是Java虚拟机中内存最大的一块,也是被所有线程共享的,在虚拟机启动时创建,Java对唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配。

虚拟机栈(Virtual Machine Stack)

虚拟机栈(线程栈)描述的是Java方法执行的内存模型,是线程私有的,它的生命周期与线程相同。当每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

栈帧在线程栈中属于先进后出(FILO),每个方法从调用知道执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量表(LVT)

局部变量表是一个索引以0开始的字节数组,存储了一个方法的所有入参和局部变量。LVT所存储的类型都是编译期可知的,包括各基础类型(byte、char、short、int、long、float、double、boolean)、对象引用(reference类型)和returnAddress类型(指向一条字节码指令的地址)

LVT有如下特点:

  • 第0个Slot(槽位)固定存储指向方法所属对象的this指针
  • 除了long和double占用连续两个Slot之外,其余类型只占用一个Slot
  • LVT按照变量的声明顺序进行存储

操作数栈(OS)

操作数栈用于在方法运算过程存储其中间的运算结果、方法入参和返回结果,它是一个后进先出(Last-In-First-Out,LIFO)的队列。

JVM提供了对OS出栈和入栈的指令,如load指令属于入栈指令、store指令属于出栈指令。

动态链接

每个栈帧内都包含一个指向当前方法所属类的运行时常量池引用,也称为符号引用(Symbolic Reference),用于在类加载阶段对代码进行动态链接。动态链接所做的就是根据符号引用所表示名字,转换成对方法或变量的实际引用,从而实现运行时绑定(Late Binding)。

本地方法栈(Native Method Stack)

本地方法栈的作用与Java虚拟机栈类似,区别在于后者是为Java方法服务,而本地方法栈则为native方法服务。Java虚拟机规范没有对native方法机制及其实现语言做强制规定,如果JVM不提供native方法,则无需实现本地方法栈。

本地方法栈既可以被实现成固定大小,也可以实现成可动态地扩展和收缩,因此在特定的场景下也会抛出StackOverflowError异常和OutOfMemoryError异常。

程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

',34);function l(p,u){return t(),o("div",null,[s,d,n(" more "),c])}const f=r(h,[["render",l],["__file","2311081011.html.vue"]]);export{f as default}; +import{_ as r,o as t,c as o,d as n,a,b as e,e as i}from"./app-6a63891c.js";const h={},s=a("h1",{id:"运行时数据区详解-内存模型",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#运行时数据区详解-内存模型","aria-hidden":"true"},"#"),e(" 运行时数据区详解(内存模型)")],-1),d=a("p",null,[e("JVM运行时数据区数Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,其中包含:"),a("strong",null,"方法区、堆内存、虚拟机栈、本地方法栈、程序计数器"),e("。")],-1),c=i('

方法区(Method Area)

方法区是线程间共享的区域,在JVM启动时创建,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

方法区可以被实现成大小固定或可动态扩展和收缩,如果内存空间不满足内存分配要求就会抛出OutOfMemoryError异常。

对于HotSpot虚拟机而言,在JDK 1.8以前,方法区被实现为 “永久代”(Permanent Generation),属于堆的逻辑组成部分,并提供了两个参数调节其大小,-XX:PermSize用于设定初始容量,-XX:MaxPermSize用于设定最大容量。JDK 1.8之后,HotSpot不再有“永久代”的概念,类的元信息数据迁移到被称为“元空间”(Metaspace)的新区域,而静态变量、常量等则存储于堆中。元空间没有使用堆内存,而是分配在 本地内存(直接内存) 中,默认情况下其容量只受可用的本地内存大小限制。类似地,HotShot虚拟机也提供了两个参数来调节其大小, -XX:MetaspaceSize用于设定初始容量,-XX:MaxMetaspaceSize用于设定最大容量

运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量符号引用,这部分内容将在类加载之后进入到方法的运行常量池中存放。

字面值常量:

  • 字符串字面量
  • 用final修饰的基础类型成员变量的字面值
  • 由字面值常量相加得到的结果

直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,因此直接内存的分配不受Java堆大小的限制,但是还是会受到本机总内存(RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。

直接内存主要用于NIO类库,实现基于通道(Channel)和缓冲区(Buffer)的IO方式。通常,当需要处理大量数据的读写操作时,可以考虑使用直接内存,例如文件传输、网络编程等。

直接内存优点在于可以避免在Java堆和本地堆之间复制数据,提高IO操作的性能,缺点就是分配和释放代价较高,而且不受JVM垃圾回收的管理,容易造成内存溢出。

直接内存可以通过ByteBuffer类的allocateDirect方法来分配和操作,

堆内存(Heap)

堆内存是Java虚拟机中内存最大的一块,也是被所有线程共享的,在虚拟机启动时创建,Java对唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配。

虚拟机栈(Virtual Machine Stack)

虚拟机栈(线程栈)描述的是Java方法执行的内存模型,是线程私有的,它的生命周期与线程相同。当每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

栈帧在线程栈中属于先进后出(FILO),每个方法从调用知道执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量表(LVT)

局部变量表是一个索引以0开始的字节数组,存储了一个方法的所有入参和局部变量。LVT所存储的类型都是编译期可知的,包括各基础类型(byte、char、short、int、long、float、double、boolean)、对象引用(reference类型)和returnAddress类型(指向一条字节码指令的地址)

LVT有如下特点:

  • 第0个Slot(槽位)固定存储指向方法所属对象的this指针
  • 除了long和double占用连续两个Slot之外,其余类型只占用一个Slot
  • LVT按照变量的声明顺序进行存储

操作数栈(OS)

操作数栈用于在方法运算过程存储其中间的运算结果、方法入参和返回结果,它是一个后进先出(Last-In-First-Out,LIFO)的队列。

JVM提供了对OS出栈和入栈的指令,如load指令属于入栈指令、store指令属于出栈指令。

动态链接

每个栈帧内都包含一个指向当前方法所属类的运行时常量池引用,也称为符号引用(Symbolic Reference),用于在类加载阶段对代码进行动态链接。动态链接所做的就是根据符号引用所表示名字,转换成对方法或变量的实际引用,从而实现运行时绑定(Late Binding)。

本地方法栈(Native Method Stack)

本地方法栈的作用与Java虚拟机栈类似,区别在于后者是为Java方法服务,而本地方法栈则为native方法服务。Java虚拟机规范没有对native方法机制及其实现语言做强制规定,如果JVM不提供native方法,则无需实现本地方法栈。

本地方法栈既可以被实现成固定大小,也可以实现成可动态地扩展和收缩,因此在特定的场景下也会抛出StackOverflowError异常和OutOfMemoryError异常。

程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

',34);function l(p,u){return t(),o("div",null,[s,d,n(" more "),c])}const f=r(h,[["render",l],["__file","2311081011.html.vue"]]);export{f as default}; diff --git a/assets/2311081109.html-5eebe14d.js b/assets/2311081109.html-885b98f4.js similarity index 99% rename from assets/2311081109.html-5eebe14d.js rename to assets/2311081109.html-885b98f4.js index 7cc8bf71..59c54cfd 100644 --- a/assets/2311081109.html-5eebe14d.js +++ b/assets/2311081109.html-885b98f4.js @@ -1,2 +1,2 @@ -import{_ as a,o as e,c as d,d as i,e as r}from"./app-1efcbe9f.js";const l={},o=r(`

JVM指令手册

通过执行jvm的指令

javap -C <classPath>
+import{_ as a,o as e,c as d,d as i,e as r}from"./app-6a63891c.js";const l={},o=r(`

JVM指令手册

通过执行jvm的指令

javap -C <classPath>
 

就可以查看编写的java代码是如何一步步执行的,而相对应的含义就如下内容

`,4),t=r('

栈和局部变量操作

将常量压入栈的指令

aconst_null 将null对象引用压入栈
iconst_m1 将int类型常量-1压入栈
iconst_0 将int类型常量0压入栈
iconst_1 将int类型常量1压入操作数栈
iconst_2 将int类型常量2压入栈
iconst_3 将int类型常量3压入栈
iconst_4 将int类型常量4压入栈
iconst_5 将int类型常量5压入栈
lconst_0 将long类型常量0压入栈
lconst_1 将long类型常量1压入栈
fconst_0 将float类型常量0压入栈
fconst_1 将float类型常量1压入栈
dconst_0 将double类型常量0压入栈
dconst_1 将double类型常量1压入栈
bipush 将一个8位带符号整数压入栈
sipush 将16位带符号整数压入栈
ldc 把常量池中的项压入栈
ldc_w 把常量池中的项压入栈(使用宽索引)
ldc2_w 把常量池中long类型或者double类型的项压入栈(使用宽索引)

从栈中的局部变量中装载值的指令

iload 从局部变量中装载int类型值
lload 从局部变量中装载long类型值
fload 从局部变量中装载float类型值
dload 从局部变量中装载double类型值
aload 从局部变量中装载引用类型值(refernce)
iload_0 从局部变量0中装载int类型值
iload_1 从局部变量1中装载int类型值
iload_2 从局部变量2中装载int类型值
iload_3 从局部变量3中装载int类型值
lload_0 从局部变量0中装载long类型值
lload_1 从局部变量1中装载long类型值
lload_2 从局部变量2中装载long类型值
lload_3 从局部变量3中装载long类型值
fload_0 从局部变量0中装载float类型值
fload_1 从局部变量1中装载float类型值
fload_2 从局部变量2中装载float类型值
fload_3 从局部变量3中装载float类型值
dload_0 从局部变量0中装载double类型值
dload_1 从局部变量1中装载double类型值
dload_2 从局部变量2中装载double类型值
dload_3 从局部变量3中装载double类型值
aload_0 从局部变量0中装载引用类型值
aload_1 从局部变量1中装载引用类型值
aload_2 从局部变量2中装载引用类型值
aload_3 从局部变量3中装载引用类型值
iaload 从数组中装载int类型值
laload 从数组中装载long类型值
faload 从数组中装载float类型值
daload 从数组中装载double类型值
aaload 从数组中装载引用类型值
baload 从数组中装载byte类型或boolean类型值
caload 从数组中装载char类型值
saload 从数组中装载short类型值

将栈中的值存入局部变量的指令

istore 将int类型值存入局部变量
lstore 将long类型值存入局部变量
fstore 将float类型值存入局部变量
dstore 将double类型值存入局部变量
astore 将将引用类型或returnAddress类型值存入局部变量
istore_0 将int类型值存入局部变量0
istore_1 将int类型值存入局部变量1
istore_2 将int类型值存入局部变量2
istore_3 将int类型值存入局部变量3
lstore_0 将long类型值存入局部变量0
lstore_1 将long类型值存入局部变量1
lstore_2 将long类型值存入局部变量2
lstore_3 将long类型值存入局部变量3
fstore_0 将float类型值存入局部变量0
fstore_1 将float类型值存入局部变量1
fstore_2 将float类型值存入局部变量2
fstore_3 将float类型值存入局部变量3
dstore_0 将double类型值存入局部变量0
dstore_1 将double类型值存入局部变量1
dstore_2 将double类型值存入局部变量2
dstore_3 将double类型值存入局部变量3
astore_0 将引用类型或returnAddress类型值存入局部变量0
astore_1 将引用类型或returnAddress类型值存入局部变量1
astore_2 将引用类型或returnAddress类型值存入局部变量2
astore_3 将引用类型或returnAddress类型值存入局部变量3
iastore 将int类型值存入数组中
lastore 将long类型值存入数组中
fastore 将float类型值存入数组中
dastore 将double类型值存入数组中
aastore 将引用类型值存入数组中
bastore 将byte类型或者boolean类型值存入数组中
castore 将char类型值存入数组中
sastore 将short类型值存入数组中

wide指令

wide 使用附加字节扩展局部变量索引

通用(无类型)栈操作

nop 不做任何操作
pop 弹出栈顶端一个字长的内容
pop2 弹出栈顶端两个字长的内容
dup 复制栈顶部一个字长内容
dup_x1 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的两个字长的内容压入栈
dup_x2 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
dup2 复制栈顶部两个字长内容
dup2_x1 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
dup2_x2 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的四个字长的内容压入栈
swap 交换栈顶部两个字长内容

类型转换

i2l 把int类型的数据转化为long类型
i2f 把int类型的数据转化为float类型
i2d 把int类型的数据转化为double类型
l2i 把long类型的数据转化为int类型
l2f 把long类型的数据转化为float类型
l2d 把long类型的数据转化为double类型
f2i 把float类型的数据转化为int类型
f2l 把float类型的数据转化为long类型
f2d 把float类型的数据转化为double类型
d2i 把double类型的数据转化为int类型
d2l 把double类型的数据转化为long类型
d2f 把double类型的数据转化为float类型
i2b 把int类型的数据转化为byte类型
i2c 把int类型的数据转化为char类型
i2s 把int类型的数据转化为short类型

整数运算

iadd 执行int类型的加法
ladd 执行long类型的加法
isub 执行int类型的减法
lsub 执行long类型的减法
imul 执行int类型的乘法
lmul 执行long类型的乘法
idiv 执行int类型的除法
ldiv 执行long类型的除法
irem 计算int类型除法的余数
lrem 计算long类型除法的余数
ineg 对一个int类型值进行取反操作
lneg 对一个long类型值进行取反操作
iinc 把一个常量值加到一个int类型的局部变量上

逻辑运算

移位操作

ishl 执行int类型的向左移位操作
lshl 执行long类型的向左移位操作
ishr 执行int类型的向右移位操作
lshr 执行long类型的向右移位操作
iushr 执行int类型的向右逻辑移位操作
lushr 执行long类型的向右逻辑移位操作

按位布尔运算

iand 对int类型值进行“逻辑与”操作
land 对long类型值进行“逻辑与”操作
ior 对int类型值进行“逻辑或”操作
lor 对long类型值进行“逻辑或”操作
ixor 对int类型值进行“逻辑异或”操作
lxor 对long类型值进行“逻辑异或”操作

浮点运算

fadd 执行float类型的加法
dadd 执行double类型的加法
fsub 执行float类型的减法
dsub 执行double类型的减法
fmul 执行float类型的乘法
dmul 执行double类型的乘法
fdiv 执行float类型的除法
ddiv 执行double类型的除法
frem 计算float类型除法的余数
drem 计算double类型除法的余数
fneg 将一个float类型的数值取反
dneg 将一个double类型的数值取反

对象和数组

对象操作指令

new 创建一个新对象
checkcast 确定对象为所给定的类型
getfield 从对象中获取字段
putfield 设置对象中字段的值
getstatic 从类中获取静态字段
putstatic 设置类中静态字段的值
instanceof 判断对象是否为给定的类型

数组操作指令

newarray 分配数据成员类型为基本上数据类型的新数组
anewarray 分配数据成员类型为引用类型的新数组
arraylength 获取数组长度
multianewarray 分配新的多维数组

控制流

条件分支指令

ifeq 如果等于0,则跳转
ifne 如果不等于0,则跳转
iflt 如果小于0,则跳转
ifge 如果大于等于0,则跳转
ifgt 如果大于0,则跳转
ifle 如果小于等于0,则跳转
if_icmpcq 如果两个int值相等,则跳转
if_icmpne 如果两个int类型值不相等,则跳转
if_icmplt 如果一个int类型值小于另外一个int类型值,则跳转
f_icmpge 如果一个int类型值大于或者等于另外一个int类型值,则跳转
if_icmpgt 如果一个int类型值大于另外一个int类型值,则跳转
if_icmple 如果一个int类型值小于或者等于另外一个int类型值,则跳转
ifnull 如果等于null,则跳转
ifnonnull 如果不等于null,则跳转
if_acmpeq 如果两个对象引用相等,则跳转
if_acmpnc 如果两个对象引用不相等,则跳转

比较指令

lcmp 比较long类型值
fcmpl 比较float类型值(当遇到NaN时,返回-1)
fcmpg 比较float类型值(当遇到NaN时,返回1)
dcmpl 比较double类型值(当遇到NaN时,返回-1)
dcmpg 比较double类型值(当遇到NaN时,返回1)

无条件转移指令

goto 无条件跳转
goto_w 无条件跳转(宽索引)

表跳转指令

tableswitch 通过索引访问跳转表,并跳转
lookupswitch 通过键值匹配访问跳转表,并执行跳转操作

异常

athrow 抛出异常或错误
finally子句
jsr 跳转到子例程
jsr_w 跳转到子例程(宽索引)
rct 从子例程返回

方法调用与返回

方法调用指令

invokcvirtual 运行时按照对象的类来调用实例方法
invokespecial 根据编译时类型来调用实例方法
invokestatic 调用类(静态)方法
invokcinterface 调用接口方法

方法返回指令

ireturn 从方法中返回int类型的数据
lreturn 从方法中返回long类型的数据
freturn 从方法中返回float类型的数据
dreturn 从方法中返回double类型的数据
areturn 从方法中返回引用类型的数据
return 从方法中返回,返回值为void

线程同步

montiorenter 进入并获取对象监视器
monitorexit 释放并退出对象监视器

JVM指令助记符

变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_
操作数栈到变量:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_
常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_
加:iadd,ladd,fadd,dadd
减:isub,lsub,fsub,dsub
乘:imul,lmul,fmul,dmul
除:idiv,ldiv,fdiv,ddiv
余数:irem,lrem,frem,drem
取负:ineg,lneg,fneg,dneg
移位:ishl,lshr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位与:iand,land
按位异或:ixor,lxor
类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换)
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换)
创建类实便:new
创建新数组:newarray,anewarray,multianwarray
访问类的域和类实例域:getfield,putfield,getstatic,putstatic
把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload
从操作数栈存存储到数组:
bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
获取数组长度:arraylength
检相类实例或数组属性:instanceof,checkcast
操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
fcmpg,dcmpl,dcmpg
复合条件转移:tableswitch,lookupswitch
无条件转移:goto,goto_w,jsr,jsr_w,ret
调度对象的实便方法:invokevirtual
调用由接口实现的方法:invokeinterface
调用需要特殊处理的实例方法:invokespecial
调用命名类中的静态方法:invokestatic
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
异常:athrow
finally关键字的实现使用:jsr,jsr_w,ret

',47);function b(n,s){return e(),d("div",null,[o,i(" more "),t])}const c=a(l,[["render",b],["__file","2311081109.html.vue"]]);export{c as default}; diff --git a/assets/2311081109.html-09a2e140.js b/assets/2311081109.html-b4edd1d4.js similarity index 98% rename from assets/2311081109.html-09a2e140.js rename to assets/2311081109.html-b4edd1d4.js index 7490e4ab..39056677 100644 --- a/assets/2311081109.html-09a2e140.js +++ b/assets/2311081109.html-b4edd1d4.js @@ -1 +1 @@ -const l=JSON.parse('{"key":"v-10b58660","path":"/note/java/jvm/2311081109.html","title":"JVM指令手册","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":0,"date":"2023-11-08T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"栈和局部变量操作","slug":"栈和局部变量操作","link":"#栈和局部变量操作","children":[{"level":3,"title":"将常量压入栈的指令","slug":"将常量压入栈的指令","link":"#将常量压入栈的指令","children":[]},{"level":3,"title":"从栈中的局部变量中装载值的指令","slug":"从栈中的局部变量中装载值的指令","link":"#从栈中的局部变量中装载值的指令","children":[]},{"level":3,"title":"将栈中的值存入局部变量的指令","slug":"将栈中的值存入局部变量的指令","link":"#将栈中的值存入局部变量的指令","children":[]},{"level":3,"title":"wide指令","slug":"wide指令","link":"#wide指令","children":[]},{"level":3,"title":"通用(无类型)栈操作","slug":"通用-无类型-栈操作","link":"#通用-无类型-栈操作","children":[]}]},{"level":2,"title":"类型转换","slug":"类型转换","link":"#类型转换","children":[]},{"level":2,"title":"整数运算","slug":"整数运算","link":"#整数运算","children":[]},{"level":2,"title":"逻辑运算","slug":"逻辑运算","link":"#逻辑运算","children":[{"level":3,"title":"移位操作","slug":"移位操作","link":"#移位操作","children":[]},{"level":3,"title":"按位布尔运算","slug":"按位布尔运算","link":"#按位布尔运算","children":[]},{"level":3,"title":"浮点运算","slug":"浮点运算","link":"#浮点运算","children":[]}]},{"level":2,"title":"对象和数组","slug":"对象和数组","link":"#对象和数组","children":[{"level":3,"title":"对象操作指令","slug":"对象操作指令","link":"#对象操作指令","children":[]},{"level":3,"title":"数组操作指令","slug":"数组操作指令","link":"#数组操作指令","children":[]}]},{"level":2,"title":"控制流","slug":"控制流","link":"#控制流","children":[{"level":3,"title":"条件分支指令","slug":"条件分支指令","link":"#条件分支指令","children":[]},{"level":3,"title":"比较指令","slug":"比较指令","link":"#比较指令","children":[]},{"level":3,"title":"无条件转移指令","slug":"无条件转移指令","link":"#无条件转移指令","children":[]},{"level":3,"title":"表跳转指令","slug":"表跳转指令","link":"#表跳转指令","children":[]}]},{"level":2,"title":"异常","slug":"异常","link":"#异常","children":[]},{"level":2,"title":"方法调用与返回","slug":"方法调用与返回","link":"#方法调用与返回","children":[{"level":3,"title":"方法调用指令","slug":"方法调用指令","link":"#方法调用指令","children":[]},{"level":3,"title":"方法返回指令","slug":"方法返回指令","link":"#方法返回指令","children":[]}]},{"level":2,"title":"线程同步","slug":"线程同步","link":"#线程同步","children":[]},{"level":2,"title":"JVM指令助记符","slug":"jvm指令助记符","link":"#jvm指令助记符","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.68,"words":3205},"filePathRelative":"note/java/jvm/2311081109.md","localizedDate":"2023年11月8日","excerpt":"

JVM指令手册

\\n

通过执行jvm的指令

\\n
javap -C <classPath>\\n

就可以查看编写的java代码是如何一步步执行的,而相对应的含义就如下内容

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; +const l=JSON.parse('{"key":"v-10b58660","path":"/note/java/jvm/2311081109.html","title":"JVM指令手册","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":0,"date":"2023-11-08T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"栈和局部变量操作","slug":"栈和局部变量操作","link":"#栈和局部变量操作","children":[{"level":3,"title":"将常量压入栈的指令","slug":"将常量压入栈的指令","link":"#将常量压入栈的指令","children":[]},{"level":3,"title":"从栈中的局部变量中装载值的指令","slug":"从栈中的局部变量中装载值的指令","link":"#从栈中的局部变量中装载值的指令","children":[]},{"level":3,"title":"将栈中的值存入局部变量的指令","slug":"将栈中的值存入局部变量的指令","link":"#将栈中的值存入局部变量的指令","children":[]},{"level":3,"title":"wide指令","slug":"wide指令","link":"#wide指令","children":[]},{"level":3,"title":"通用(无类型)栈操作","slug":"通用-无类型-栈操作","link":"#通用-无类型-栈操作","children":[]}]},{"level":2,"title":"类型转换","slug":"类型转换","link":"#类型转换","children":[]},{"level":2,"title":"整数运算","slug":"整数运算","link":"#整数运算","children":[]},{"level":2,"title":"逻辑运算","slug":"逻辑运算","link":"#逻辑运算","children":[{"level":3,"title":"移位操作","slug":"移位操作","link":"#移位操作","children":[]},{"level":3,"title":"按位布尔运算","slug":"按位布尔运算","link":"#按位布尔运算","children":[]},{"level":3,"title":"浮点运算","slug":"浮点运算","link":"#浮点运算","children":[]}]},{"level":2,"title":"对象和数组","slug":"对象和数组","link":"#对象和数组","children":[{"level":3,"title":"对象操作指令","slug":"对象操作指令","link":"#对象操作指令","children":[]},{"level":3,"title":"数组操作指令","slug":"数组操作指令","link":"#数组操作指令","children":[]}]},{"level":2,"title":"控制流","slug":"控制流","link":"#控制流","children":[{"level":3,"title":"条件分支指令","slug":"条件分支指令","link":"#条件分支指令","children":[]},{"level":3,"title":"比较指令","slug":"比较指令","link":"#比较指令","children":[]},{"level":3,"title":"无条件转移指令","slug":"无条件转移指令","link":"#无条件转移指令","children":[]},{"level":3,"title":"表跳转指令","slug":"表跳转指令","link":"#表跳转指令","children":[]}]},{"level":2,"title":"异常","slug":"异常","link":"#异常","children":[]},{"level":2,"title":"方法调用与返回","slug":"方法调用与返回","link":"#方法调用与返回","children":[{"level":3,"title":"方法调用指令","slug":"方法调用指令","link":"#方法调用指令","children":[]},{"level":3,"title":"方法返回指令","slug":"方法返回指令","link":"#方法返回指令","children":[]}]},{"level":2,"title":"线程同步","slug":"线程同步","link":"#线程同步","children":[]},{"level":2,"title":"JVM指令助记符","slug":"jvm指令助记符","link":"#jvm指令助记符","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":10.68,"words":3205},"filePathRelative":"note/java/jvm/2311081109.md","localizedDate":"2023年11月8日","excerpt":"

JVM指令手册

\\n

通过执行jvm的指令

\\n
javap -C <classPath>\\n

就可以查看编写的java代码是如何一步步执行的,而相对应的含义就如下内容

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2311091006.html-2173149a.js b/assets/2311091006.html-1cb57dcd.js similarity index 95% rename from assets/2311091006.html-2173149a.js rename to assets/2311091006.html-1cb57dcd.js index 6de8a4dc..ef23cfe8 100644 --- a/assets/2311091006.html-2173149a.js +++ b/assets/2311091006.html-1cb57dcd.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-7eeef733","path":"/note/java/jvm/2311091006.html","title":"JVM对象内存布局","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":3,"date":"2023-11-09T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"对象头","slug":"对象头","link":"#对象头","children":[{"level":3,"title":"MarkWord","slug":"markword","link":"#markword","children":[]},{"level":3,"title":"KlassPointer","slug":"klasspointer","link":"#klasspointer","children":[]},{"level":3,"title":"数组长度","slug":"数组长度","link":"#数组长度","children":[]}]},{"level":2,"title":"内存布局查看实战","slug":"内存布局查看实战","link":"#内存布局查看实战","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1145},"filePathRelative":"note/java/jvm/2311091006.md","localizedDate":"2023年11月9日","excerpt":"

JVM对象内存布局

\\n

Hotspot虚拟机中,将对象在内存中存储的布局分为三块:对象头(Header)实例数据(Instance Data)对齐填充(Padding)

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-7eeef733","path":"/note/java/jvm/2311091006.html","title":"JVM对象内存布局","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":3,"date":"2023-11-09T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"对象头","slug":"对象头","link":"#对象头","children":[{"level":3,"title":"MarkWord","slug":"markword","link":"#markword","children":[]},{"level":3,"title":"KlassPointer","slug":"klasspointer","link":"#klasspointer","children":[]},{"level":3,"title":"数组长度","slug":"数组长度","link":"#数组长度","children":[]}]},{"level":2,"title":"内存布局查看实战","slug":"内存布局查看实战","link":"#内存布局查看实战","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1145},"filePathRelative":"note/java/jvm/2311091006.md","localizedDate":"2023年11月9日","excerpt":"

JVM对象内存布局

\\n

Hotspot虚拟机中,将对象在内存中存储的布局分为三块:对象头(Header)实例数据(Instance Data)对齐填充(Padding)

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2311091006.html-6e892cea.js b/assets/2311091006.html-d02a6bfb.js similarity index 99% rename from assets/2311091006.html-6e892cea.js rename to assets/2311091006.html-d02a6bfb.js index ea7bab62..6dc959fd 100644 --- a/assets/2311091006.html-6e892cea.js +++ b/assets/2311091006.html-d02a6bfb.js @@ -1,4 +1,4 @@ -import{_ as s,o as t,c as e,d as p,a as n,b as a,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"jvm对象内存布局",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#jvm对象内存布局","aria-hidden":"true"},"#"),a(" JVM对象内存布局")],-1),i=n("p",null,[a("Hotspot虚拟机中,将对象在内存中存储的布局分为三块:"),n("strong",null,"对象头(Header)"),a("、"),n("strong",null,"实例数据(Instance Data)"),a("、"),n("strong",null,"对齐填充(Padding)")],-1),r=o(`
对象内存布局
对象内存布局

对象头

HotSpot主要将对象头划分为:MarkWordKlassPointer数组长度,记录了对象Hash码、对象所属年代、对象锁、锁状态、偏向锁的线程ID、偏向时间、数组长度等等信息

MarkWord

MarkWord用于存储对象自身运行时的数据,例如哈希码(HashCode)、GC分代年龄、锁状态、持有锁的线程、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit。

  • 32位MarkWord
32位MarkWord
32位MarkWord
  • 64位MarkWord
64位MarkWord
64位MarkWord

MarkWord结构搞得那么复杂,是因为需要节省内存,让同一内存区域在不同锁阶段有不同的用处

  • hash:保存对象的哈希码,在运行期间调用System.identityHashCode()进行计算,这是一个延迟计算,并将结果赋值到hash内存中
  • age:保存对象的分代年龄。记录对象被GC的次数,当该次数到达阈值后就会由年轻代转入老年代
  • biased_lock:偏向锁标识位。由于无锁和偏向锁的锁标识都是记01,为了区分引入了一位来标识是否为偏向锁
  • lock:锁状态标识。区分锁状态,比如00时表示轻量锁,只有最后2位锁标识(00)有效
  • JavaThread:保存持有偏向锁的线程ID。当处于偏行模式时,有线程持有对象,则对象的这里就会保存持有线程的ID,后续再获取锁时就无需再进行尝试获取锁的动作
  • epoch:保存偏向时间戳。偏向锁再CAS锁操作过程中,偏向性标识,标识对象更偏向哪个锁

KlassPointer

KlassPointer又称类型指针,是指向对象的类元数据的指针。虚拟机可以通过这个指针来确定这个对象是那个类的实例。

在32位的JVM内,类型指针占4byte
在64位的JVM内,类型指针正常占8byte,若开启指针压缩或者最大堆内存小于32G时占4byte。JDK8后默认开启指针压缩

进行指针压缩的原因:

  • 在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据会占用较大宽带,同时GC也会承受较大压力
  • JVM中32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得JVM只用32位地址就可以支持更大的内存配置(小于等于32G)

需要注意的是:

  • 堆内存小于4G时不需要开启指针压缩,JVM会直接去除32位地址,即使用低虚拟地址空间
  • 堆内存大于32G时,压缩指针会失效,会强制使用64(8字节)来对Java对象进行寻址,所以堆内存最好不要大于32G。

数组长度

如果这个对象是一个数组,则对象头中会有一块4byte长度数数据区用于记录数组的长度,如果不是数组则这部分长度为0

Header内存占用
Header内存占用

内存布局查看实战

为了验证对象内存布局,可使用Java对象的内部布局工具JOL(JAVA OBJECT LAYOUT)
,用此工具可以查看new出来的一个java对象的内部布局,以及一个普通的java对象占用多少字节。

Maven依赖引入

<!--  JAVA对象布局、大小查看  -->
+import{_ as s,o as t,c as e,d as p,a as n,b as a,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"jvm对象内存布局",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#jvm对象内存布局","aria-hidden":"true"},"#"),a(" JVM对象内存布局")],-1),i=n("p",null,[a("Hotspot虚拟机中,将对象在内存中存储的布局分为三块:"),n("strong",null,"对象头(Header)"),a("、"),n("strong",null,"实例数据(Instance Data)"),a("、"),n("strong",null,"对齐填充(Padding)")],-1),r=o(`
对象内存布局
对象内存布局

对象头

HotSpot主要将对象头划分为:MarkWordKlassPointer数组长度,记录了对象Hash码、对象所属年代、对象锁、锁状态、偏向锁的线程ID、偏向时间、数组长度等等信息

MarkWord

MarkWord用于存储对象自身运行时的数据,例如哈希码(HashCode)、GC分代年龄、锁状态、持有锁的线程、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit。

  • 32位MarkWord
32位MarkWord
32位MarkWord
  • 64位MarkWord
64位MarkWord
64位MarkWord

MarkWord结构搞得那么复杂,是因为需要节省内存,让同一内存区域在不同锁阶段有不同的用处

  • hash:保存对象的哈希码,在运行期间调用System.identityHashCode()进行计算,这是一个延迟计算,并将结果赋值到hash内存中
  • age:保存对象的分代年龄。记录对象被GC的次数,当该次数到达阈值后就会由年轻代转入老年代
  • biased_lock:偏向锁标识位。由于无锁和偏向锁的锁标识都是记01,为了区分引入了一位来标识是否为偏向锁
  • lock:锁状态标识。区分锁状态,比如00时表示轻量锁,只有最后2位锁标识(00)有效
  • JavaThread:保存持有偏向锁的线程ID。当处于偏行模式时,有线程持有对象,则对象的这里就会保存持有线程的ID,后续再获取锁时就无需再进行尝试获取锁的动作
  • epoch:保存偏向时间戳。偏向锁再CAS锁操作过程中,偏向性标识,标识对象更偏向哪个锁

KlassPointer

KlassPointer又称类型指针,是指向对象的类元数据的指针。虚拟机可以通过这个指针来确定这个对象是那个类的实例。

在32位的JVM内,类型指针占4byte
在64位的JVM内,类型指针正常占8byte,若开启指针压缩或者最大堆内存小于32G时占4byte。JDK8后默认开启指针压缩

进行指针压缩的原因:

  • 在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据会占用较大宽带,同时GC也会承受较大压力
  • JVM中32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得JVM只用32位地址就可以支持更大的内存配置(小于等于32G)

需要注意的是:

  • 堆内存小于4G时不需要开启指针压缩,JVM会直接去除32位地址,即使用低虚拟地址空间
  • 堆内存大于32G时,压缩指针会失效,会强制使用64(8字节)来对Java对象进行寻址,所以堆内存最好不要大于32G。

数组长度

如果这个对象是一个数组,则对象头中会有一块4byte长度数数据区用于记录数组的长度,如果不是数组则这部分长度为0

Header内存占用
Header内存占用

内存布局查看实战

为了验证对象内存布局,可使用Java对象的内部布局工具JOL(JAVA OBJECT LAYOUT)
,用此工具可以查看new出来的一个java对象的内部布局,以及一个普通的java对象占用多少字节。

Maven依赖引入

<!--  JAVA对象布局、大小查看  -->
 <dependency>
     <groupId>org.openjdk.jol</groupId>
     <artifactId>jol-core</artifactId>
diff --git a/assets/2311221518.html-5a91ddfe.js b/assets/2311221518.html-ad437492.js
similarity index 95%
rename from assets/2311221518.html-5a91ddfe.js
rename to assets/2311221518.html-ad437492.js
index 91793ece..e7a2e3a4 100644
--- a/assets/2311221518.html-5a91ddfe.js
+++ b/assets/2311221518.html-ad437492.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-3a6c11b6","path":"/note/java/jvm/2311221518.html","title":"JIT(即时编译)深入理解","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":6,"date":"2023-11-22T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"热点探测技术","slug":"热点探测技术","link":"#热点探测技术","children":[]},{"level":2,"title":"方法内联","slug":"方法内联","link":"#方法内联","children":[]},{"level":2,"title":"锁消除","slug":"锁消除","link":"#锁消除","children":[]},{"level":2,"title":"锁粗化","slug":"锁粗化","link":"#锁粗化","children":[]},{"level":2,"title":"逃逸分析技术","slug":"逃逸分析技术","link":"#逃逸分析技术","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.31,"words":2194},"filePathRelative":"note/java/jvm/2311221518.md","localizedDate":"2023年11月22日","excerpt":"

JIT(即时编译)深入理解

\\n

Java是一门解释型语言,通过编译器(javac)将源代码编译成平台无关的Java字节码文件(.class)。然后JVM解释执行这些字节码文件,实现平台无关性。
\\n但是,解释执行的速度相对较慢。为了提高执行速度,引入了JIT技术。JIT是JUST IN TIME的缩写,意味着即时编译

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-3a6c11b6","path":"/note/java/jvm/2311221518.html","title":"JIT(即时编译)深入理解","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":6,"date":"2023-11-22T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"热点探测技术","slug":"热点探测技术","link":"#热点探测技术","children":[]},{"level":2,"title":"方法内联","slug":"方法内联","link":"#方法内联","children":[]},{"level":2,"title":"锁消除","slug":"锁消除","link":"#锁消除","children":[]},{"level":2,"title":"锁粗化","slug":"锁粗化","link":"#锁粗化","children":[]},{"level":2,"title":"逃逸分析技术","slug":"逃逸分析技术","link":"#逃逸分析技术","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.31,"words":2194},"filePathRelative":"note/java/jvm/2311221518.md","localizedDate":"2023年11月22日","excerpt":"

JIT(即时编译)深入理解

\\n

Java是一门解释型语言,通过编译器(javac)将源代码编译成平台无关的Java字节码文件(.class)。然后JVM解释执行这些字节码文件,实现平台无关性。
\\n但是,解释执行的速度相对较慢。为了提高执行速度,引入了JIT技术。JIT是JUST IN TIME的缩写,意味着即时编译

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2311221518.html-c7b06941.js b/assets/2311221518.html-c71bf788.js similarity index 99% rename from assets/2311221518.html-c7b06941.js rename to assets/2311221518.html-c71bf788.js index 1b66c6b6..6bf0f9de 100644 --- a/assets/2311221518.html-c7b06941.js +++ b/assets/2311221518.html-c71bf788.js @@ -1,4 +1,4 @@ -import{_ as a,o as p,c as t,d as e,a as n,b as s,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"jit-即时编译-深入理解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#jit-即时编译-深入理解","aria-hidden":"true"},"#"),s(" JIT(即时编译)深入理解")],-1),i=n("p",null,[s("Java是一门解释型语言,通过编译器(javac)将源代码编译成平台无关的Java字节码文件(.class)。然后JVM解释执行这些字节码文件,实现平台无关性。"),n("br"),s(" 但是,解释执行的速度相对较慢。为了提高执行速度,引入了JIT技术。JIT是JUST IN TIME的缩写,意味着"),n("strong",null,"即时编译"),s("。")],-1),u=o(`

热点探测技术

当虚拟机发现某个方法或代码块运行特别频繁时,就会将这些代码标记为热点代码(Hot Spot Code)。热点探测的目的是识别出这些热点代码,以便进行即时编译(JIT)优化。

这种探测方法周期性地检查各个线程的虚拟机栈的栈顶。如果某个方法经常出现在栈顶,就认为这个方法是热点方法。基于采样的热点探测是一种常见的热点代码判定方式。

对于热点方法,JIT会触发标准编译,将其编译成机器码,以提高执行效率。对于循环体,JIT可能触发OSR(On-Stack
Replacement)栈上替换,直接切换到本地代码执行。

热点探测技术的触发,只需要满足以下一个条件即可:

  • 方法调用计数器
    • 方法计数器用于记录方法被调用的次数。当某个方法被多次调用时,方法计数器的值会逐渐增加
    • 在Client模式下,默认的方法计数器阈值是1500次。
    • 在Server模式下,默认的方法计数器阈值是10000次。
  • 回边计数器
    • 回边计数器用于判断循环是否热点。当循环体被多次执行时,回边计数器的值会增加。
    • 在Client模式下,回边计数器的阈值计算公式为:OSRP = 方法调用计数器阈值 * 933 / 100。
    • 在Server模式下,回边计数器的阈值计算公式为:OSRP = (方法调用计数器阈值 * 140 - 监控解释器比率) / 100。
public class HotSpotDetectionExample {
+import{_ as a,o as p,c as t,d as e,a as n,b as s,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"jit-即时编译-深入理解",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#jit-即时编译-深入理解","aria-hidden":"true"},"#"),s(" JIT(即时编译)深入理解")],-1),i=n("p",null,[s("Java是一门解释型语言,通过编译器(javac)将源代码编译成平台无关的Java字节码文件(.class)。然后JVM解释执行这些字节码文件,实现平台无关性。"),n("br"),s(" 但是,解释执行的速度相对较慢。为了提高执行速度,引入了JIT技术。JIT是JUST IN TIME的缩写,意味着"),n("strong",null,"即时编译"),s("。")],-1),u=o(`

热点探测技术

当虚拟机发现某个方法或代码块运行特别频繁时,就会将这些代码标记为热点代码(Hot Spot Code)。热点探测的目的是识别出这些热点代码,以便进行即时编译(JIT)优化。

这种探测方法周期性地检查各个线程的虚拟机栈的栈顶。如果某个方法经常出现在栈顶,就认为这个方法是热点方法。基于采样的热点探测是一种常见的热点代码判定方式。

对于热点方法,JIT会触发标准编译,将其编译成机器码,以提高执行效率。对于循环体,JIT可能触发OSR(On-Stack
Replacement)栈上替换,直接切换到本地代码执行。

热点探测技术的触发,只需要满足以下一个条件即可:

  • 方法调用计数器
    • 方法计数器用于记录方法被调用的次数。当某个方法被多次调用时,方法计数器的值会逐渐增加
    • 在Client模式下,默认的方法计数器阈值是1500次。
    • 在Server模式下,默认的方法计数器阈值是10000次。
  • 回边计数器
    • 回边计数器用于判断循环是否热点。当循环体被多次执行时,回边计数器的值会增加。
    • 在Client模式下,回边计数器的阈值计算公式为:OSRP = 方法调用计数器阈值 * 933 / 100。
    • 在Server模式下,回边计数器的阈值计算公式为:OSRP = (方法调用计数器阈值 * 140 - 监控解释器比率) / 100。
public class HotSpotDetectionExample {
 
     public static void main(String[] args) {
         // Simulate some method calls
diff --git a/assets/2312130940.html-b7d420f1.js b/assets/2312130940.html-203d827f.js
similarity index 99%
rename from assets/2312130940.html-b7d420f1.js
rename to assets/2312130940.html-203d827f.js
index 76c9696b..93fdef86 100644
--- a/assets/2312130940.html-b7d420f1.js
+++ b/assets/2312130940.html-203d827f.js
@@ -1,4 +1,4 @@
-import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-1efcbe9f.js";const c={},l=n("h1",{id:"线程池参数配置",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#线程池参数配置","aria-hidden":"true"},"#"),e(" 线程池参数配置")],-1),u=n("p",null,"记录线程池参数配置参考,以及配置的理论原理以及思路",-1),i=o(`

参考理论

工作中对于线程池核心数与最大线程数的配置,需要根据业务实际需求来进行配置。对于业务功能任务,通常可分为三种:CPU密集型、IO密集型、混合型。

CPU密集型

CPU密集型任务,也被称作计算密集型任务,指的是那些在执行过程中,主要依赖于中央处理器(CPU)的计算能力来完成任务的工作负载。这类任务的特点是需要进行大量的数据处理、计算和逻辑判断,如数值计算、图像处理、视频编码等。在执行这些任务时,CPU的利用率通常很高,而其他资源如磁盘、网络等可能处于较空闲的状态。

    static class DefaultRunnable implements Runnable {
+import{_ as a,o as s,c as t,d as p,a as n,b as e,e as o}from"./app-6a63891c.js";const c={},l=n("h1",{id:"线程池参数配置",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#线程池参数配置","aria-hidden":"true"},"#"),e(" 线程池参数配置")],-1),u=n("p",null,"记录线程池参数配置参考,以及配置的理论原理以及思路",-1),i=o(`

参考理论

工作中对于线程池核心数与最大线程数的配置,需要根据业务实际需求来进行配置。对于业务功能任务,通常可分为三种:CPU密集型、IO密集型、混合型。

CPU密集型

CPU密集型任务,也被称作计算密集型任务,指的是那些在执行过程中,主要依赖于中央处理器(CPU)的计算能力来完成任务的工作负载。这类任务的特点是需要进行大量的数据处理、计算和逻辑判断,如数值计算、图像处理、视频编码等。在执行这些任务时,CPU的利用率通常很高,而其他资源如磁盘、网络等可能处于较空闲的状态。

    static class DefaultRunnable implements Runnable {
         @Override
         public void run() {
             int i = 0;
diff --git a/assets/2312130940.html-69186843.js b/assets/2312130940.html-9f37cd57.js
similarity index 90%
rename from assets/2312130940.html-69186843.js
rename to assets/2312130940.html-9f37cd57.js
index 9b659a39..c4fa251f 100644
--- a/assets/2312130940.html-69186843.js
+++ b/assets/2312130940.html-9f37cd57.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-e12a9278","path":"/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312130940.html","title":"线程池参数配置","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-13T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全"]},"headers":[{"level":2,"title":"参考理论","slug":"参考理论","link":"#参考理论","children":[{"level":3,"title":"CPU密集型","slug":"cpu密集型","link":"#cpu密集型","children":[]},{"level":3,"title":"IO密集型","slug":"io密集型","link":"#io密集型","children":[]}]},{"level":2,"title":"参数配置","slug":"参数配置","link":"#参数配置","children":[{"level":3,"title":"核心数与最大线程数","slug":"核心数与最大线程数","link":"#核心数与最大线程数","children":[]},{"level":3,"title":"阻塞队列容量","slug":"阻塞队列容量","link":"#阻塞队列容量","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.28,"words":1883},"filePathRelative":"note/java/concurrency/线程池/2312130940.md","localizedDate":"2023年12月13日","excerpt":"

线程池参数配置

\\n

记录线程池参数配置参考,以及配置的理论原理以及思路

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-e12a9278","path":"/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312130940.html","title":"线程池参数配置","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-13T00:00:00.000Z","order":1,"category":["并发"],"tag":["线程安全"]},"headers":[{"level":2,"title":"参考理论","slug":"参考理论","link":"#参考理论","children":[{"level":3,"title":"CPU密集型","slug":"cpu密集型","link":"#cpu密集型","children":[]},{"level":3,"title":"IO密集型","slug":"io密集型","link":"#io密集型","children":[]}]},{"level":2,"title":"参数配置","slug":"参数配置","link":"#参数配置","children":[{"level":3,"title":"核心数与最大线程数","slug":"核心数与最大线程数","link":"#核心数与最大线程数","children":[]},{"level":3,"title":"阻塞队列容量","slug":"阻塞队列容量","link":"#阻塞队列容量","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.28,"words":1883},"filePathRelative":"note/java/concurrency/线程池/2312130940.md","localizedDate":"2023年12月13日","excerpt":"

线程池参数配置

\\n

记录线程池参数配置参考,以及配置的理论原理以及思路

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312141621.html-d5edb34f.js b/assets/2312141621.html-6b399b4e.js similarity index 82% rename from assets/2312141621.html-d5edb34f.js rename to assets/2312141621.html-6b399b4e.js index 4112ae7c..5c4f19dc 100644 --- a/assets/2312141621.html-d5edb34f.js +++ b/assets/2312141621.html-6b399b4e.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"线程池核心原理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#线程池核心原理","aria-hidden":"true"},"#"),o(" 线程池核心原理")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2312141621.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"线程池核心原理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#线程池核心原理","aria-hidden":"true"},"#"),o(" 线程池核心原理")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2312141621.html.vue"]]);export{l as default}; diff --git a/assets/2312141621.html-def3b7de.js b/assets/2312141621.html-ba899b4a.js similarity index 90% rename from assets/2312141621.html-def3b7de.js rename to assets/2312141621.html-ba899b4a.js index 1a387508..9fa4efb0 100644 --- a/assets/2312141621.html-def3b7de.js +++ b/assets/2312141621.html-ba899b4a.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-783d7264","path":"/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312141621.html","title":"线程池核心原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-14T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.08,"words":23},"filePathRelative":"note/java/concurrency/线程池/2312141621.md","localizedDate":"2023年12月14日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-783d7264","path":"/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312141621.html","title":"线程池核心原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-14T00:00:00.000Z","order":2,"category":["并发"],"tag":["线程安全"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.08,"words":23},"filePathRelative":"note/java/concurrency/线程池/2312141621.md","localizedDate":"2023年12月14日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312220952.html-d54014ac.js b/assets/2312220952.html-16ea3a95.js similarity index 95% rename from assets/2312220952.html-d54014ac.js rename to assets/2312220952.html-16ea3a95.js index b2fef666..a02d881e 100644 --- a/assets/2312220952.html-d54014ac.js +++ b/assets/2312220952.html-16ea3a95.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1aa97360","path":"/note/db/redis/2312220952.html","title":"Redis主从/哨兵/集群架构搭建","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-25T00:00:00.000Z","index":true,"order":7,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"环境","slug":"环境","link":"#环境","children":[{"level":3,"title":"服务器环境","slug":"服务器环境","link":"#服务器环境","children":[]},{"level":3,"title":"开放防火墙端口","slug":"开放防火墙端口","link":"#开放防火墙端口","children":[]},{"level":3,"title":"Redis版本","slug":"redis版本","link":"#redis版本","children":[]}]},{"level":2,"title":"主从复制搭建","slug":"主从复制搭建","link":"#主从复制搭建","children":[]},{"level":2,"title":"哨兵架构搭建","slug":"哨兵架构搭建","link":"#哨兵架构搭建","children":[]},{"level":2,"title":"集群架构搭建","slug":"集群架构搭建","link":"#集群架构搭建","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.95,"words":1484},"filePathRelative":"note/db/redis/2312220952.md","localizedDate":"2023年12月25日","excerpt":"

Redis主从/哨兵/集群架构搭建

\\n

记录Redis主从复制架构、哨兵架构以及集群(cluster)架构的搭建。本次搭建使用不同云服务器厂商公网ip搭建。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-1aa97360","path":"/note/db/redis/2312220952.html","title":"Redis主从/哨兵/集群架构搭建","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-25T00:00:00.000Z","index":true,"order":7,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"环境","slug":"环境","link":"#环境","children":[{"level":3,"title":"服务器环境","slug":"服务器环境","link":"#服务器环境","children":[]},{"level":3,"title":"开放防火墙端口","slug":"开放防火墙端口","link":"#开放防火墙端口","children":[]},{"level":3,"title":"Redis版本","slug":"redis版本","link":"#redis版本","children":[]}]},{"level":2,"title":"主从复制搭建","slug":"主从复制搭建","link":"#主从复制搭建","children":[]},{"level":2,"title":"哨兵架构搭建","slug":"哨兵架构搭建","link":"#哨兵架构搭建","children":[]},{"level":2,"title":"集群架构搭建","slug":"集群架构搭建","link":"#集群架构搭建","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":4.95,"words":1484},"filePathRelative":"note/db/redis/2312220952.md","localizedDate":"2023年12月25日","excerpt":"

Redis主从/哨兵/集群架构搭建

\\n

记录Redis主从复制架构、哨兵架构以及集群(cluster)架构的搭建。本次搭建使用不同云服务器厂商公网ip搭建。

\\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312220952.html-62da4e4c.js b/assets/2312220952.html-1f4baa89.js similarity index 99% rename from assets/2312220952.html-62da4e4c.js rename to assets/2312220952.html-1f4baa89.js index 7c8d1cce..9ad168a3 100644 --- a/assets/2312220952.html-62da4e4c.js +++ b/assets/2312220952.html-1f4baa89.js @@ -1,4 +1,4 @@ -import{_ as n,o as e,c as a,d as i,a as s,b as l,e as c}from"./app-1efcbe9f.js";const d={},t=s("h1",{id:"redis主从-哨兵-集群架构搭建",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redis主从-哨兵-集群架构搭建","aria-hidden":"true"},"#"),l(" Redis主从/哨兵/集群架构搭建")],-1),r=s("p",null,"记录Redis主从复制架构、哨兵架构以及集群(cluster)架构的搭建。本次搭建使用不同云服务器厂商公网ip搭建。",-1),o=c(`

环境

服务器环境

服务器IP版本节点
腾讯云159.159.159.159CentOS 7.9master
腾讯云139.139.139.139CentOS 7.9slave
华为云101.101.101.101CentOS 7.9slave

开放防火墙端口

  1. 关闭服务器防火墙
  2. 开放云服务提供商服务器实例防火墙端口:6379、6380、6381、16379、16380、16381、26379、26380、26381

Redis版本

redis-7.2.0

# 安装路径
+import{_ as n,o as e,c as a,d as i,a as s,b as l,e as c}from"./app-6a63891c.js";const d={},t=s("h1",{id:"redis主从-哨兵-集群架构搭建",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redis主从-哨兵-集群架构搭建","aria-hidden":"true"},"#"),l(" Redis主从/哨兵/集群架构搭建")],-1),r=s("p",null,"记录Redis主从复制架构、哨兵架构以及集群(cluster)架构的搭建。本次搭建使用不同云服务器厂商公网ip搭建。",-1),o=c(`

环境

服务器环境

服务器IP版本节点
腾讯云159.159.159.159CentOS 7.9master
腾讯云139.139.139.139CentOS 7.9slave
华为云101.101.101.101CentOS 7.9slave

开放防火墙端口

  1. 关闭服务器防火墙
  2. 开放云服务提供商服务器实例防火墙端口:6379、6380、6381、16379、16380、16381、26379、26380、26381

Redis版本

redis-7.2.0

# 安装路径
  cd /usr/local/src/redis-7.2.0/
 

主从复制搭建

  1. 进入安装目录

    # 安装路径
     cd /usr/local/src/redis-7.2.0/
    diff --git a/assets/2312280935.html-edfaba23.js b/assets/2312280935.html-45b1270f.js
    similarity index 90%
    rename from assets/2312280935.html-edfaba23.js
    rename to assets/2312280935.html-45b1270f.js
    index fdc04665..ff1b1cb0 100644
    --- a/assets/2312280935.html-edfaba23.js
    +++ b/assets/2312280935.html-45b1270f.js
    @@ -1 +1 @@
    -const e=JSON.parse('{"key":"v-47b60065","path":"/note/db/redis/2312280935.html","title":"Redis高并发缓存实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-28T00:00:00.000Z","index":true,"order":8,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"缓存穿透","slug":"缓存穿透","link":"#缓存穿透","children":[]},{"level":3,"title":"缓存失效","slug":"缓存失效","link":"#缓存失效","children":[]},{"level":3,"title":"缓存雪崩","slug":"缓存雪崩","link":"#缓存雪崩","children":[]},{"level":3,"title":"热点缓存重建","slug":"热点缓存重建","link":"#热点缓存重建","children":[]},{"level":3,"title":"数据不一致","slug":"数据不一致","link":"#数据不一致","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.49,"words":1648},"filePathRelative":"note/db/redis/2312280935.md","localizedDate":"2023年12月28日","excerpt":"

    Redis高并发缓存实战

    \\n

    记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-47b60065","path":"/note/db/redis/2312280935.html","title":"Redis高并发缓存实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-28T00:00:00.000Z","index":true,"order":8,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"缓存穿透","slug":"缓存穿透","link":"#缓存穿透","children":[]},{"level":3,"title":"缓存失效","slug":"缓存失效","link":"#缓存失效","children":[]},{"level":3,"title":"缓存雪崩","slug":"缓存雪崩","link":"#缓存雪崩","children":[]},{"level":3,"title":"热点缓存重建","slug":"热点缓存重建","link":"#热点缓存重建","children":[]},{"level":3,"title":"数据不一致","slug":"数据不一致","link":"#数据不一致","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":5.49,"words":1648},"filePathRelative":"note/db/redis/2312280935.md","localizedDate":"2023年12月28日","excerpt":"

    Redis高并发缓存实战

    \\n

    记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312280935.html-ab7e085b.js b/assets/2312280935.html-720e7480.js similarity index 99% rename from assets/2312280935.html-ab7e085b.js rename to assets/2312280935.html-720e7480.js index c8259569..182e45e5 100644 --- a/assets/2312280935.html-ab7e085b.js +++ b/assets/2312280935.html-720e7480.js @@ -1,4 +1,4 @@ -import{_ as s,o as a,c as t,d as p,a as n,b as e,e as c}from"./app-1efcbe9f.js";const o={},i=n("h1",{id:"redis高并发缓存实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#redis高并发缓存实战","aria-hidden":"true"},"#"),e(" Redis高并发缓存实战")],-1),l=n("p",null,"记录高并发场景下Redis部署、使用、存在的问题以及处理方案等",-1),u=c(`

    常见问题

    在中小并发场景下,我们在使用缓存架构基本的业务流程是:

    1. 查询缓存,缓存存在则返回
    2. 缓存没有,查找数据库,更新缓存
        /**
    +import{_ as s,o as a,c as t,d as p,a as n,b as e,e as c}from"./app-6a63891c.js";const o={},i=n("h1",{id:"redis高并发缓存实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#redis高并发缓存实战","aria-hidden":"true"},"#"),e(" Redis高并发缓存实战")],-1),l=n("p",null,"记录高并发场景下Redis部署、使用、存在的问题以及处理方案等",-1),u=c(`

    常见问题

    在中小并发场景下,我们在使用缓存架构基本的业务流程是:

    1. 查询缓存,缓存存在则返回
    2. 缓存没有,查找数据库,更新缓存
        /**
          * 查询商品信息
          *
          * @param productId 商品ID
    diff --git a/assets/2312291000.html-6241ad44.js b/assets/2312291000.html-7d4f645e.js
    similarity index 83%
    rename from assets/2312291000.html-6241ad44.js
    rename to assets/2312291000.html-7d4f645e.js
    index c32a55d3..f0db93ea 100644
    --- a/assets/2312291000.html-6241ad44.js
    +++ b/assets/2312291000.html-7d4f645e.js
    @@ -1 +1 @@
    -import{_ as r,o as t,c as d,d as s,a as e,b as a}from"./app-1efcbe9f.js";const o={},c=e("h1",{id:"redis分布式锁实战",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#redis分布式锁实战","aria-hidden":"true"},"#"),a(" Redis分布式锁实战")],-1),n=e("p",null,"记录高并发场景下Redis部署、使用、存在的问题以及处理方案等",-1),i=e("h2",{id:"常见问题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#常见问题","aria-hidden":"true"},"#"),a(" 常见问题")],-1),h=e("h3",{id:"",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#","aria-hidden":"true"},"#")],-1);function _(l,f){return t(),d("div",null,[c,n,s(" more "),i,h])}const u=r(o,[["render",_],["__file","2312291000.html.vue"]]);export{u as default};
    +import{_ as r,o as t,c as d,d as s,a as e,b as a}from"./app-6a63891c.js";const o={},c=e("h1",{id:"redis分布式锁实战",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#redis分布式锁实战","aria-hidden":"true"},"#"),a(" Redis分布式锁实战")],-1),n=e("p",null,"记录高并发场景下Redis部署、使用、存在的问题以及处理方案等",-1),i=e("h2",{id:"常见问题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#常见问题","aria-hidden":"true"},"#"),a(" 常见问题")],-1),h=e("h3",{id:"",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#","aria-hidden":"true"},"#")],-1);function _(l,f){return t(),d("div",null,[c,n,s(" more "),i,h])}const u=r(o,[["render",_],["__file","2312291000.html.vue"]]);export{u as default};
    diff --git a/assets/2312291000.html-135d7164.js b/assets/2312291000.html-9218ce69.js
    similarity index 85%
    rename from assets/2312291000.html-135d7164.js
    rename to assets/2312291000.html-9218ce69.js
    index ba729b08..d3035795 100644
    --- a/assets/2312291000.html-135d7164.js
    +++ b/assets/2312291000.html-9218ce69.js
    @@ -1 +1 @@
    -const e=JSON.parse('{"key":"v-fbc34060","path":"/note/db/redis/2312291000.html","title":"Redis分布式锁实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-29T00:00:00.000Z","index":true,"order":9,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"","slug":"","link":"#","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"note/db/redis/2312291000.md","localizedDate":"2023年12月29日","excerpt":"

    Redis分布式锁实战

    \\n

    记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-fbc34060","path":"/note/db/redis/2312291000.html","title":"Redis分布式锁实战","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-29T00:00:00.000Z","index":true,"order":9,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"","slug":"","link":"#","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"note/db/redis/2312291000.md","localizedDate":"2023年12月29日","excerpt":"

    Redis分布式锁实战

    \\n

    记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312291432.html-29834115.js b/assets/2312291432.html-425bb655.js similarity index 84% rename from assets/2312291432.html-29834115.js rename to assets/2312291432.html-425bb655.js index d2995984..f6dc6f6c 100644 --- a/assets/2312291432.html-29834115.js +++ b/assets/2312291432.html-425bb655.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-78811366","path":"/note/db/redis/2312291432.html","title":"布隆过滤器","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-29T00:00:00.000Z","index":true,"order":9,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"","slug":"","link":"#","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":33},"filePathRelative":"note/db/redis/2312291432.md","localizedDate":"2023年12月29日","excerpt":"

    布隆过滤器

    \\n

    布隆过滤器简单实现

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-78811366","path":"/note/db/redis/2312291432.html","title":"布隆过滤器","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2023-12-29T00:00:00.000Z","index":true,"order":9,"category":["DB"],"tag":["Redis"]},"headers":[{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"","slug":"","link":"#","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":33},"filePathRelative":"note/db/redis/2312291432.md","localizedDate":"2023年12月29日","excerpt":"

    布隆过滤器

    \\n

    布隆过滤器简单实现

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2312291432.html-dfb22bda.js b/assets/2312291432.html-d4287519.js similarity index 81% rename from assets/2312291432.html-dfb22bda.js rename to assets/2312291432.html-d4287519.js index 1156136d..9e380f06 100644 --- a/assets/2312291432.html-dfb22bda.js +++ b/assets/2312291432.html-d4287519.js @@ -1 +1 @@ -import{_ as t,o as r,c as d,d as o,a as e,b as a}from"./app-1efcbe9f.js";const s={},c=e("h1",{id:"布隆过滤器",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#布隆过滤器","aria-hidden":"true"},"#"),a(" 布隆过滤器")],-1),n=e("p",null,"布隆过滤器简单实现",-1),h=e("h2",{id:"常见问题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#常见问题","aria-hidden":"true"},"#"),a(" 常见问题")],-1),i=e("h3",{id:"",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#","aria-hidden":"true"},"#")],-1);function _(l,f){return r(),d("div",null,[c,n,o(" more "),h,i])}const u=t(s,[["render",_],["__file","2312291432.html.vue"]]);export{u as default}; +import{_ as t,o as r,c as d,d as o,a as e,b as a}from"./app-6a63891c.js";const s={},c=e("h1",{id:"布隆过滤器",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#布隆过滤器","aria-hidden":"true"},"#"),a(" 布隆过滤器")],-1),n=e("p",null,"布隆过滤器简单实现",-1),h=e("h2",{id:"常见问题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#常见问题","aria-hidden":"true"},"#"),a(" 常见问题")],-1),i=e("h3",{id:"",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#","aria-hidden":"true"},"#")],-1);function _(l,f){return r(),d("div",null,[c,n,o(" more "),h,i])}const u=t(s,[["render",_],["__file","2312291432.html.vue"]]);export{u as default}; diff --git a/assets/2401171030.html-7d4132fe.js b/assets/2401171030.html-8dce4f83.js similarity index 85% rename from assets/2401171030.html-7d4132fe.js rename to assets/2401171030.html-8dce4f83.js index 173a1b69..2b8f08f0 100644 --- a/assets/2401171030.html-7d4132fe.js +++ b/assets/2401171030.html-8dce4f83.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"深入理解网络通信与tcp-ip协议",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#深入理解网络通信与tcp-ip协议","aria-hidden":"true"},"#"),o(" 深入理解网络通信与TCP/IP协议")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2401171030.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"深入理解网络通信与tcp-ip协议",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#深入理解网络通信与tcp-ip协议","aria-hidden":"true"},"#"),o(" 深入理解网络通信与TCP/IP协议")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2401171030.html.vue"]]);export{l as default}; diff --git a/assets/2401171030.html-88e63301.js b/assets/2401171030.html-fc81356c.js similarity index 90% rename from assets/2401171030.html-88e63301.js rename to assets/2401171030.html-fc81356c.js index 98f63fc7..75b4af79 100644 --- a/assets/2401171030.html-88e63301.js +++ b/assets/2401171030.html-fc81356c.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-da7165c6","path":"/note/net/basic/2401171030.html","title":"深入理解网络通信与TCP/IP协议","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2024-01-17T00:00:00.000Z","index":true,"sticky":1,"star":1,"category":["TCP","IP"],"tag":["网络编程"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":32},"filePathRelative":"note/net/basic/2401171030.md","localizedDate":"2024年1月17日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-da7165c6","path":"/note/net/basic/2401171030.html","title":"深入理解网络通信与TCP/IP协议","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2024-01-17T00:00:00.000Z","index":true,"sticky":1,"star":1,"category":["TCP","IP"],"tag":["网络编程"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":32},"filePathRelative":"note/net/basic/2401171030.md","localizedDate":"2024年1月17日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403021055.html-4582e424.js b/assets/2403021055.html-9afcdbef.js similarity index 98% rename from assets/2403021055.html-4582e424.js rename to assets/2403021055.html-9afcdbef.js index 77e10cde..9c2e8b5d 100644 --- a/assets/2403021055.html-4582e424.js +++ b/assets/2403021055.html-9afcdbef.js @@ -1 +1 @@ -import{_ as t,r as n,o,c as s,d as h,a as e,b as a,f as d,w as i,e as c}from"./app-1efcbe9f.js";const p={},l=e("h1",{id:"对象创建与内存分配",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对象创建与内存分配","aria-hidden":"true"},"#"),a(" 对象创建与内存分配")],-1),u=e("p",null,"当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。",-1),f=c('

    当我们去new一个对象的时候,完整的流程如下:类加载检查、内存分配、初始化、设置对象头、执行init()

    类加载检查

    虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

    new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等。

    内存分配

    类加载完成之后,会为新对象进行内存分配。对象所需的内存大小在类加载完成之后便可以确定。此时为对象分配内存空间等于把一块确定大小的内存从JVM堆中划分出来给对象使用。

    在进行堆内存划分的时候会存在两个问题:如何划分问题和并发问题

    内存划分方式

    为新对象在堆中划分内存,划分方式有两种:指针碰撞(默认使用)和空闲列表

    指针碰撞(Bump the Pointer)

    通过一个指针指向分界点,这个指针表示已分配未分配内存的分界。当需要分配内存时,将指针往空闲一侧移动与对象大小相等的距离即可。

    需要注意的是这需要Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边。主要用于Serial和ParNew等不会产生内存碎片的垃圾收集器。

    空闲列表(Free List)

    JVM维护一张内存列表,记录可用的内存块信息。当分配内存时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。

    空闲列表即使是Java堆中的内存并不规整,已分配内存和未分配内存交错也是适用的。最常见使用此方案的垃圾收集器是CMS(Concurrent
    Mark-Sweep)

    并发问题处理

    在并发情况对象在内存划分时,可能会出现正在给A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况,导致数据不一致或内存分配错误。

    JVM采用以下方法来解决并发问题:CMS和本地线程分配缓冲(TLAB)

    CMS(Compare and Swap)

    虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。

    本地线程分配缓冲(Thread Local Allocation Buffer)

    为每个线程在Java堆中预先分配一小块内存,哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

    虚拟机是否使用TLAB,可以通过 -XX:+/-UseTLAB(默认开启) 参数来设定。

    栈上分配

    通常Java对象都是在堆上分配内存,当对象没有被引用时,光依靠GC进行回收内存,对象创建过多就会给GC带来较大的压力,间接影响应用的性能。

    为了减少临时对象在堆内存分配的数量,JVM通过逃逸分析技术确定对象不会被外部访问,则说明对象不会逃逸出改栈帧,可以将对象在栈上分配内存,这样对象占用的内存就会随着栈帧出栈而销毁,达到减轻GC回收的压力。

    ',26),g=e("strong",null,"热点探测技术",-1);function _(m,b){const r=n("RouterLink");return o(),s("div",null,[l,u,h(" more "),f,e("p",null,[a("逃逸分析技术还需要结合"),g,a(",以此为前提,详情可在 "),e("strong",null,[d(r,{to:"/note/java/jvm/2311221518.html"},{default:i(()=>[a("JIT(即时编译)深入理解")]),_:1})]),a(" 文章查看。")])])}const B=t(p,[["render",_],["__file","2403021055.html.vue"]]);export{B as default}; +import{_ as t,r as n,o,c as s,d as h,a as e,b as a,f as d,w as i,e as c}from"./app-6a63891c.js";const p={},l=e("h1",{id:"对象创建与内存分配",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对象创建与内存分配","aria-hidden":"true"},"#"),a(" 对象创建与内存分配")],-1),u=e("p",null,"当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。",-1),f=c('

    当我们去new一个对象的时候,完整的流程如下:类加载检查、内存分配、初始化、设置对象头、执行init()

    类加载检查

    虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

    new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等。

    内存分配

    类加载完成之后,会为新对象进行内存分配。对象所需的内存大小在类加载完成之后便可以确定。此时为对象分配内存空间等于把一块确定大小的内存从JVM堆中划分出来给对象使用。

    在进行堆内存划分的时候会存在两个问题:如何划分问题和并发问题

    内存划分方式

    为新对象在堆中划分内存,划分方式有两种:指针碰撞(默认使用)和空闲列表

    指针碰撞(Bump the Pointer)

    通过一个指针指向分界点,这个指针表示已分配未分配内存的分界。当需要分配内存时,将指针往空闲一侧移动与对象大小相等的距离即可。

    需要注意的是这需要Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边。主要用于Serial和ParNew等不会产生内存碎片的垃圾收集器。

    空闲列表(Free List)

    JVM维护一张内存列表,记录可用的内存块信息。当分配内存时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。

    空闲列表即使是Java堆中的内存并不规整,已分配内存和未分配内存交错也是适用的。最常见使用此方案的垃圾收集器是CMS(Concurrent
    Mark-Sweep)

    并发问题处理

    在并发情况对象在内存划分时,可能会出现正在给A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况,导致数据不一致或内存分配错误。

    JVM采用以下方法来解决并发问题:CMS和本地线程分配缓冲(TLAB)

    CMS(Compare and Swap)

    虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。

    本地线程分配缓冲(Thread Local Allocation Buffer)

    为每个线程在Java堆中预先分配一小块内存,哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

    虚拟机是否使用TLAB,可以通过 -XX:+/-UseTLAB(默认开启) 参数来设定。

    栈上分配

    通常Java对象都是在堆上分配内存,当对象没有被引用时,光依靠GC进行回收内存,对象创建过多就会给GC带来较大的压力,间接影响应用的性能。

    为了减少临时对象在堆内存分配的数量,JVM通过逃逸分析技术确定对象不会被外部访问,则说明对象不会逃逸出改栈帧,可以将对象在栈上分配内存,这样对象占用的内存就会随着栈帧出栈而销毁,达到减轻GC回收的压力。

    ',26),g=e("strong",null,"热点探测技术",-1);function _(m,b){const r=n("RouterLink");return o(),s("div",null,[l,u,h(" more "),f,e("p",null,[a("逃逸分析技术还需要结合"),g,a(",以此为前提,详情可在 "),e("strong",null,[d(r,{to:"/note/java/jvm/2311221518.html"},{default:i(()=>[a("JIT(即时编译)深入理解")]),_:1})]),a(" 文章查看。")])])}const B=t(p,[["render",_],["__file","2403021055.html.vue"]]);export{B as default}; diff --git a/assets/2403021055.html-98304a19.js b/assets/2403021055.html-f21c50f4.js similarity index 90% rename from assets/2403021055.html-98304a19.js rename to assets/2403021055.html-f21c50f4.js index d2d2ae8f..93dbaabc 100644 --- a/assets/2403021055.html-98304a19.js +++ b/assets/2403021055.html-f21c50f4.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-4b158648","path":"/note/java/jvm/2403021055.html","title":"对象创建与内存分配","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":8,"date":"2024-03-02T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"类加载检查","slug":"类加载检查","link":"#类加载检查","children":[]},{"level":2,"title":"内存分配","slug":"内存分配","link":"#内存分配","children":[{"level":3,"title":"内存划分方式","slug":"内存划分方式","link":"#内存划分方式","children":[]},{"level":3,"title":"并发问题处理","slug":"并发问题处理","link":"#并发问题处理","children":[]},{"level":3,"title":"栈上分配","slug":"栈上分配","link":"#栈上分配","children":[]}]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.54,"words":1062},"filePathRelative":"note/java/jvm/2403021055.md","localizedDate":"2024年3月2日","excerpt":"

    对象创建与内存分配

    \\n

    当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-4b158648","path":"/note/java/jvm/2403021055.html","title":"对象创建与内存分配","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":8,"date":"2024-03-02T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM"]},"headers":[{"level":2,"title":"类加载检查","slug":"类加载检查","link":"#类加载检查","children":[]},{"level":2,"title":"内存分配","slug":"内存分配","link":"#内存分配","children":[{"level":3,"title":"内存划分方式","slug":"内存划分方式","link":"#内存划分方式","children":[]},{"level":3,"title":"并发问题处理","slug":"并发问题处理","link":"#并发问题处理","children":[]},{"level":3,"title":"栈上分配","slug":"栈上分配","link":"#栈上分配","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.54,"words":1062},"filePathRelative":"note/java/jvm/2403021055.md","localizedDate":"2024年3月2日","excerpt":"

    对象创建与内存分配

    \\n

    当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403031236.html-154d9a90.js b/assets/2403031236.html-b90ced32.js similarity index 97% rename from assets/2403031236.html-154d9a90.js rename to assets/2403031236.html-b90ced32.js index f90a1b7d..4b7eae65 100644 --- a/assets/2403031236.html-154d9a90.js +++ b/assets/2403031236.html-b90ced32.js @@ -1 +1 @@ -import{_ as l,o as r,c as i,d as n,a,b as e,e as t}from"./app-1efcbe9f.js";const o={},g=a("h1",{id:"垃圾收集器及原理",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#垃圾收集器及原理","aria-hidden":"true"},"#"),e(" 垃圾收集器及原理")],-1),s=a("p",null,[e("JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有"),a("strong",null,"Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC"),e("等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。")],-1),d=a("figure",null,[a("img",{src:"https://qiniu.yanggl.cn/image/2403031236_1.png",alt:"垃圾收集器",tabindex:"0",loading:"lazy"}),a("figcaption",null,"垃圾收集器")],-1),c=t('

    GC算法

    • 分代收集算法: 这种算法根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代。新生代中的对象一般存活时间较短,所以选择复制算法,而老年代中的对象存活时间长,空间大,所以选择标记-清理或者标记-整理算法。
    • 复制算法: 这种算法将内存分为两块,每次只使用其中一块。当这一块内存用完后,就将还存活的对象复制到另一块上面,然后再把已使用的内存空间一次清理掉。这种算法的主要缺点是内存利用率只有50%。
    • 标记-清除算法: 这种算法首先标记出所有需要回收的对象,然后进行清除。这种算法的主要缺点是效率不高,并且清除后会产生大量的内存碎片。
    • 标记-整理算法: 这种算法是在标记-清除算法的基础上进行的改进,标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

    Serial收集器

    Serial收集器是Java虚拟机(JVM)中最基本,也是历史最悠久的垃圾收集器。此收集器是一个单线程的收集器,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束,这种机制被称为Stop-The-World(STW)

    Serial收集器的优点在于是单线程运行,没有线程交互的开销,对内存的消耗小,可以专心做垃圾收集,因此可以获得很高的单线程收集效益。缺点就是在垃圾收集过程中其他CPU资源被闲置,必须暂停其他所有的工作线程(STW),这会影响到应用程序的响应性能。

    Serial收集器
    Serial收集器

    Serial Old收集器被用作Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:

    • 在JDK1.5版本及以前的Parallel Scavenge收集器搭配使用
    • 作为CMS收集器的后备方案

    Serial收集器主要用于新生代的垃圾回收,采用的是复制算法。 Serial Old收集器使用的是标记-整理算法

    Parallel收集器

    Parallel收集器是Serial收集器的多线程版本,除了使用多线程进行垃圾收集以外,其行为(参数控制、收集算法、回收策略等)和Serial相似。

    Parallel收集器主要用于多核处理器的服务器中,旨在提高垃圾收集的吞吐量(高效的利用CPU),所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总耗时的比值。在注重吞吐量以及CPU资源的场合,都可以优先考虑Parallel收集器配合Parallel Old收集器来进行垃圾收集(JDK1.8默认垃圾收集组合)

    Parallel收集器
    Parallel收集器

    Parallel Old收集器是Parallel收集器的老年代版本,同样是使用多线程来进行垃圾收集。Parallel采用的是复制算法。 Parallel Old收集器使用的是标记-整理算法

    ParNew收集器

    ParNew垃圾收集器是一种用于新生代的并行垃圾回收器,与Parallel收集器类似,都是并行收集器。它的名字中的 “Par” 是 “Parallel” 的缩写,表示并行,而 “New” 表示它主要处理新生代的垃圾回收。

    ParNew收集器
    ParNew收集器

    ParNew收集器的诞生主要是为了配合CMS(Concurrent Mark-Sweep)垃圾回收器一起使用,用于处理新生代的垃圾回收。这是因为在新生代中,回收次数频繁,使用并行方式更高效。此外,目前只有Serial和ParNew可以与CMS进行配合垃圾收集

    ',18);function p(h,u){return r(),i("div",null,[g,s,d,n(" more "),c])}const f=l(o,[["render",p],["__file","2403031236.html.vue"]]);export{f as default}; +import{_ as l,o as r,c as i,d as n,a,b as e,e as t}from"./app-6a63891c.js";const o={},g=a("h1",{id:"垃圾收集器及原理",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#垃圾收集器及原理","aria-hidden":"true"},"#"),e(" 垃圾收集器及原理")],-1),s=a("p",null,[e("JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有"),a("strong",null,"Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC"),e("等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。")],-1),d=a("figure",null,[a("img",{src:"https://qiniu.yanggl.cn/image/2403031236_1.png",alt:"垃圾收集器",tabindex:"0",loading:"lazy"}),a("figcaption",null,"垃圾收集器")],-1),c=t('

    GC算法

    • 分代收集算法: 这种算法根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代。新生代中的对象一般存活时间较短,所以选择复制算法,而老年代中的对象存活时间长,空间大,所以选择标记-清理或者标记-整理算法。
    • 复制算法: 这种算法将内存分为两块,每次只使用其中一块。当这一块内存用完后,就将还存活的对象复制到另一块上面,然后再把已使用的内存空间一次清理掉。这种算法的主要缺点是内存利用率只有50%。
    • 标记-清除算法: 这种算法首先标记出所有需要回收的对象,然后进行清除。这种算法的主要缺点是效率不高,并且清除后会产生大量的内存碎片。
    • 标记-整理算法: 这种算法是在标记-清除算法的基础上进行的改进,标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

    Serial收集器

    Serial收集器是Java虚拟机(JVM)中最基本,也是历史最悠久的垃圾收集器。此收集器是一个单线程的收集器,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束,这种机制被称为Stop-The-World(STW)

    Serial收集器的优点在于是单线程运行,没有线程交互的开销,对内存的消耗小,可以专心做垃圾收集,因此可以获得很高的单线程收集效益。缺点就是在垃圾收集过程中其他CPU资源被闲置,必须暂停其他所有的工作线程(STW),这会影响到应用程序的响应性能。

    Serial收集器
    Serial收集器

    Serial Old收集器被用作Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:

    • 在JDK1.5版本及以前的Parallel Scavenge收集器搭配使用
    • 作为CMS收集器的后备方案

    Serial收集器主要用于新生代的垃圾回收,采用的是复制算法。 Serial Old收集器使用的是标记-整理算法

    Parallel收集器

    Parallel收集器是Serial收集器的多线程版本,除了使用多线程进行垃圾收集以外,其行为(参数控制、收集算法、回收策略等)和Serial相似。

    Parallel收集器主要用于多核处理器的服务器中,旨在提高垃圾收集的吞吐量(高效的利用CPU),所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总耗时的比值。在注重吞吐量以及CPU资源的场合,都可以优先考虑Parallel收集器配合Parallel Old收集器来进行垃圾收集(JDK1.8默认垃圾收集组合)

    Parallel收集器
    Parallel收集器

    Parallel Old收集器是Parallel收集器的老年代版本,同样是使用多线程来进行垃圾收集。Parallel采用的是复制算法。 Parallel Old收集器使用的是标记-整理算法

    ParNew收集器

    ParNew垃圾收集器是一种用于新生代的并行垃圾回收器,与Parallel收集器类似,都是并行收集器。它的名字中的 “Par” 是 “Parallel” 的缩写,表示并行,而 “New” 表示它主要处理新生代的垃圾回收。

    ParNew收集器
    ParNew收集器

    ParNew收集器的诞生主要是为了配合CMS(Concurrent Mark-Sweep)垃圾回收器一起使用,用于处理新生代的垃圾回收。这是因为在新生代中,回收次数频繁,使用并行方式更高效。此外,目前只有Serial和ParNew可以与CMS进行配合垃圾收集

    ',18);function p(h,u){return r(),i("div",null,[g,s,d,n(" more "),c])}const f=l(o,[["render",p],["__file","2403031236.html.vue"]]);export{f as default}; diff --git a/assets/2403031236.html-2b9b9380.js b/assets/2403031236.html-d5769672.js similarity index 91% rename from assets/2403031236.html-2b9b9380.js rename to assets/2403031236.html-d5769672.js index 6a3b80bf..bffec18e 100644 --- a/assets/2403031236.html-2b9b9380.js +++ b/assets/2403031236.html-d5769672.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-366969d6","path":"/note/java/jvm/2403031236.html","title":"垃圾收集器及原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":9,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[{"level":2,"title":"GC算法","slug":"gc算法","link":"#gc算法","children":[]},{"level":2,"title":"Serial收集器","slug":"serial收集器","link":"#serial收集器","children":[]},{"level":2,"title":"Parallel收集器","slug":"parallel收集器","link":"#parallel收集器","children":[]},{"level":2,"title":"ParNew收集器","slug":"parnew收集器","link":"#parnew收集器","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.78,"words":1133},"filePathRelative":"note/java/jvm/2403031236.md","localizedDate":"2024年3月3日","excerpt":"

    垃圾收集器及原理

    \\n

    JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

    \\n
    \\"垃圾收集器\\"
    垃圾收集器
    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-366969d6","path":"/note/java/jvm/2403031236.html","title":"垃圾收集器及原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":9,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[{"level":2,"title":"GC算法","slug":"gc算法","link":"#gc算法","children":[]},{"level":2,"title":"Serial收集器","slug":"serial收集器","link":"#serial收集器","children":[]},{"level":2,"title":"Parallel收集器","slug":"parallel收集器","link":"#parallel收集器","children":[]},{"level":2,"title":"ParNew收集器","slug":"parnew收集器","link":"#parnew收集器","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":3.78,"words":1133},"filePathRelative":"note/java/jvm/2403031236.md","localizedDate":"2024年3月3日","excerpt":"

    垃圾收集器及原理

    \\n

    JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

    \\n
    \\"垃圾收集器\\"
    垃圾收集器
    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403031505.html-a575f427.js b/assets/2403031505.html-2a5a633c.js similarity index 79% rename from assets/2403031505.html-a575f427.js rename to assets/2403031505.html-2a5a633c.js index 36f28299..3a77684d 100644 --- a/assets/2403031505.html-a575f427.js +++ b/assets/2403031505.html-2a5a633c.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-64b533de","path":"/note/java/jvm/2403031505.html","title":"垃圾收集之CMS","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":10,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":21},"filePathRelative":"note/java/jvm/2403031505.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-64b533de","path":"/note/java/jvm/2403031505.html","title":"垃圾收集之CMS","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":10,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":21},"filePathRelative":"note/java/jvm/2403031505.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403031505.html-2eedce2a.js b/assets/2403031505.html-dcfbb3f8.js similarity index 82% rename from assets/2403031505.html-2eedce2a.js rename to assets/2403031505.html-dcfbb3f8.js index b570a55e..3e4f2424 100644 --- a/assets/2403031505.html-2eedce2a.js +++ b/assets/2403031505.html-dcfbb3f8.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as s}from"./app-1efcbe9f.js";const o={},r=e("h1",{id:"垃圾收集之cms",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#垃圾收集之cms","aria-hidden":"true"},"#"),s(" 垃圾收集之CMS")],-1),_=[r];function n(d,i){return a(),c("div",null,_)}const l=t(o,[["render",n],["__file","2403031505.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as s}from"./app-6a63891c.js";const o={},r=e("h1",{id:"垃圾收集之cms",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#垃圾收集之cms","aria-hidden":"true"},"#"),s(" 垃圾收集之CMS")],-1),_=[r];function n(d,i){return a(),c("div",null,_)}const l=t(o,[["render",n],["__file","2403031505.html.vue"]]);export{l as default}; diff --git a/assets/2403032005.html-cabf1633.js b/assets/2403032005.html-1cb9fb80.js similarity index 82% rename from assets/2403032005.html-cabf1633.js rename to assets/2403032005.html-1cb9fb80.js index 771e6b17..19a05245 100644 --- a/assets/2403032005.html-cabf1633.js +++ b/assets/2403032005.html-1cb9fb80.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"垃圾收集之g1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#垃圾收集之g1","aria-hidden":"true"},"#"),o(" 垃圾收集之G1")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2403032005.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"垃圾收集之g1",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#垃圾收集之g1","aria-hidden":"true"},"#"),o(" 垃圾收集之G1")],-1),_=[s];function n(d,i){return a(),c("div",null,_)}const l=t(r,[["render",n],["__file","2403032005.html.vue"]]);export{l as default}; diff --git a/assets/2403032005.html-892a310b.js b/assets/2403032005.html-5a8fc529.js similarity index 79% rename from assets/2403032005.html-892a310b.js rename to assets/2403032005.html-5a8fc529.js index 58aa19bc..b0b74204 100644 --- a/assets/2403032005.html-892a310b.js +++ b/assets/2403032005.html-5a8fc529.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-5a83f4b7","path":"/note/java/jvm/2403032005.html","title":"垃圾收集之G1","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":11,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":21},"filePathRelative":"note/java/jvm/2403032005.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-5a83f4b7","path":"/note/java/jvm/2403032005.html","title":"垃圾收集之G1","lang":"zh-CN","frontmatter":{"isOriginal":true,"order":11,"date":"2024-03-03T00:00:00.000Z","index":true,"category":["Java"],"tag":["JVM","GC"]},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":21},"filePathRelative":"note/java/jvm/2403032005.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403061142.html-ceb43054.js b/assets/2403061142.html-ceb43054.js new file mode 100644 index 00000000..d2a9095a --- /dev/null +++ b/assets/2403061142.html-ceb43054.js @@ -0,0 +1 @@ +import{_ as l,o as n,c as r,d as s,a as t,b as e,e as i}from"./app-6a63891c.js";const a={},o=t("h1",{id:"mysql事务原理",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#mysql事务原理","aria-hidden":"true"},"#"),e(" Mysql事务原理")],-1),d=t("p",null,[e("为了解决多事务并发问题,数据库设计了"),t("strong",null,"事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制"),e(",用一整套机制来解决多事务并发问题。")],-1),g=i('

    事务

    MySQL事务是一组SQL语句的集合,它们作为一个工作单位执行。事务主要用于处理操作量大,复杂度高的数据。在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。

    ACID

    事务的ACID属性是数据库管理系统在写入或更新数据的过程中,为保证事务是正确可靠的。其具备有原子性、一致性、隔离性、持久性四个属性,简称事务的ACID属性。

    • 原子性(Atomicity): 指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。由Undo Log来实现
    • 一致性(Consistency): 指事务必须使数据库从一个一致性状态变换到另外一个一致性状态。换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
      由其他三个属性以及业务代码正确逻辑来实现
    • 隔离性(Isolation): 指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。由锁机制以及MVCC机制来实现
    • 持久性(Durability): 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。由redo log来实现

    并发带来的事务问题

    在并发场景下,MySQL事务可能会带来更新丢失、脏读、不可重复读、幻读问题。

    • 更新丢失(Lost Update)或脏写: 两个或者多个事务同时选择同一行数据,都基于最初选定的值更新该行,由于每个事务都不知道其它事务的存在,就会发生更新丢失的问题。最后提交的更新覆盖了之前其它事务所做的更新。
    • 脏读(Dirty Reads): 一个事务正在对一条记录进行修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态。此时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并依据此做了进一步的处理,就会产生对未提交的数据的依赖关系。
    • 不可重复读(Non-Repeatable Reads): 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了。
    • 幻读(Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据。

    事务隔隔离级别

    脏读、不可重复读、幻读其实都是数据库读一致性问题,可以由数据库提供的隔离机制来解决。数据库提供了读未提交、读已提交、可重复读、可串行化四种隔离机制。

    • 读未提交(Read Uncommitted): 在读未提交隔离级别下,事务A可以读取到事务B修改过但未提交的数据。可能发生脏读、不可重复读和幻读问题,一般很少使用此隔离级别
    • 读已提交(Read Committed): 在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据。读已提交隔离级别解决了脏读的问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别
    • 可重复读(Repeatable Read): 在可重复读隔离级别下,事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务B修改的数据。可重复读隔离级别解决了脏读和不可重复读的问题,但可能发生幻读问题
    • 可串行化(Serializable): 各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。
    隔离级别/事务问题更新丢失脏读不可重复读幻读
    读未提交可以不可以不可以不可以
    读已提交可以可以不可以不可以
    可重复读可以可以可以不可以
    可串行化可以可以可以可以

    数据库隔离级别越严格,并发问题越小,但是付出的代价也就越大,因为事务隔离实际上就是使事务在一定程度上“串行化”,这与“并发”是冲突的。同时,不同应用对读一致性和事务隔离程度的要求也是不同的。

    Mysql默认的事务隔离级别是可重复读。对于RC和RR的选择,如果对并发要求高,可以选择RC,如果对同一时期的数据有要求,可以选择RR。

    面试题:查询操作方法需要使用事务吗?

    答:如果只查询一条语句,可以不加事务。
    如果执行多条查询语句,假设现在使用的隔离级别是RR,最好加上事务。对于报表数据,如果使用RR,添加事务可以确保查询出来的数据是同一时间的数据,而对于RC或者不加事务,获取到的值一直都是最新的。
    假设报表数据处理耗时1S,这期间有可能已经执行了很多的数据操作了,RR通过事务可以保证查出来的数据集是同一时间的,而RC或者不加事务有可能已经是不同时期的数据集。

    大事务的影响

    在使用事务的时候,应该尽量避免编写过大的事务,大事务在执行时可能会存在以下问题:

    • 高并发场景下,由于事务操作是需要占用服务器链接的,大事务执行耗时过多,数据库连接池容易被撑爆
    • 事务锁定的数据太多,容易造成大量的阻塞和锁超时
    • 执行耗时过多,容易造成主从延迟
    • 回滚需要的时间过长
    • undo log膨胀
    • 容易死锁

    事务使用原则

    • 将查询等数据准备操作放到事务外执行
    • 事务中应避免远程调用或者远程调用设置超时,防止事务等待时间太久
    • 事务中避免一次性处理太多数据,如果可以应拆分多个事务分次处理
    • 更新等设计加锁操作的应尽可能的放在事务靠后的位置
    • 能异步处理的尽量异步处理
    • 应用端(业务代码)保证数据一致性,非事务执行
    ',21);function c(h,y){return n(),r("div",null,[o,d,s(" more "),g])}const u=l(a,[["render",c],["__file","2403061142.html.vue"]]);export{u as default}; diff --git a/assets/2403061142.html-dbc6d88d.js b/assets/2403061142.html-dbc6d88d.js new file mode 100644 index 00000000..e2bf7228 --- /dev/null +++ b/assets/2403061142.html-dbc6d88d.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-36bed06a","path":"/note/db/mysql/further/2403061142.html","title":"Mysql事务原理","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2024-03-06T00:00:00.000Z","index":true,"order":6,"category":["DB"],"tag":["MySQL","事务"]},"headers":[{"level":2,"title":"事务","slug":"事务","link":"#事务","children":[{"level":3,"title":"ACID","slug":"acid","link":"#acid","children":[]},{"level":3,"title":"并发带来的事务问题","slug":"并发带来的事务问题","link":"#并发带来的事务问题","children":[]},{"level":3,"title":"事务隔隔离级别","slug":"事务隔隔离级别","link":"#事务隔隔离级别","children":[]},{"level":3,"title":"大事务的影响","slug":"大事务的影响","link":"#大事务的影响","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":6.13,"words":1838},"filePathRelative":"note/db/mysql/further/2403061142.md","localizedDate":"2024年3月6日","excerpt":"

    Mysql事务原理

    \\n

    为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/2403081801.html-640f260f.js b/assets/2403081801.html-640f260f.js new file mode 100644 index 00000000..385ac938 --- /dev/null +++ b/assets/2403081801.html-640f260f.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-61e42302","path":"/note/db/mysql/further/2403081801.html","title":"锁机制","lang":"zh-CN","frontmatter":{"isOriginal":true,"date":"2024-03-06T00:00:00.000Z","index":true,"order":7,"category":["DB"],"tag":["MySQL","锁机制"]},"headers":[{"level":2,"title":"锁分类","slug":"锁分类","link":"#锁分类","children":[{"level":3,"title":"乐观锁","slug":"乐观锁","link":"#乐观锁","children":[]},{"level":3,"title":"悲观锁","slug":"悲观锁","link":"#悲观锁","children":[]},{"level":3,"title":"表锁","slug":"表锁","link":"#表锁","children":[]},{"level":3,"title":"叶锁","slug":"叶锁","link":"#叶锁","children":[]},{"level":3,"title":"行锁","slug":"行锁","link":"#行锁","children":[]},{"level":3,"title":"读锁","slug":"读锁","link":"#读锁","children":[]},{"level":3,"title":"写锁","slug":"写锁","link":"#写锁","children":[]},{"level":3,"title":"意向锁","slug":"意向锁","link":"#意向锁","children":[]},{"level":3,"title":"间隙锁(Gap Lock)","slug":"间隙锁-gap-lock","link":"#间隙锁-gap-lock","children":[]},{"level":3,"title":"临建锁(Next-key Locks)","slug":"临建锁-next-key-locks","link":"#临建锁-next-key-locks","children":[]}]},{"level":2,"title":"MyISAM与InnoDB锁实现","slug":"myisam与innodb锁实现","link":"#myisam与innodb锁实现","children":[]},{"level":2,"title":"死锁","slug":"死锁","link":"#死锁","children":[{"level":3,"title":"锁优化实践","slug":"锁优化实践","link":"#锁优化实践","children":[]}]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":7.43,"words":2228},"filePathRelative":"note/db/mysql/further/2403081801.md","localizedDate":"2024年3月6日","excerpt":"

    锁机制

    \\n

    当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

    \\n

    在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

    \\n

    锁冲突也是影响数据库并发访问性能的一个重要因素。

    \\n","copyright":{"author":"Yaien","license":"MIT"}}');export{l as data}; diff --git a/assets/2403081801.html-b3b0e084.js b/assets/2403081801.html-b3b0e084.js new file mode 100644 index 00000000..b54f7dcc --- /dev/null +++ b/assets/2403081801.html-b3b0e084.js @@ -0,0 +1,4 @@ +import{_ as a,o as n,c as t,d as s,a as e,b as r,e as o}from"./app-6a63891c.js";const d={},p=e("h1",{id:"锁机制",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#锁机制","aria-hidden":"true"},"#"),r(" 锁机制")],-1),l=e("p",null,"当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。",-1),c=e("p",null,"在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。",-1),i=e("p",null,"锁冲突也是影响数据库并发访问性能的一个重要因素。",-1),h=o(`

    锁分类

    从性能上分,分为乐观锁(用版本对比或者CAS机制)和悲观锁;从数据操作的粒度上分,分为表锁、页锁、行锁;从数据操作的类型上分,分为读锁、写锁和意向锁,读锁和写锁两种锁都属于悲观锁

    乐观锁

    适合读多写少的场景,如果在写操作多的场景下使用,会导致比对次数过多从而影响性能

    悲观锁

    适合写操作多的场景

    表锁

    锁定粒度最大的一种锁,对当前操作的整张表加锁。实现简单,加锁力度大,资源消耗较小,加锁快,不会出现死锁,并发度最低。大部分引擎都支持。一般用于表迁移。

    叶锁

    粒度介于表锁和行锁之间,锁定的是相邻的一组记录(一页)。基于索引加锁,锁粒度介于表锁和行锁之间,会出现死锁,并发度一般。只有BDB支持叶锁。

    行锁

    粒度最小的一种锁,只针对当前操作的行加锁。基于索引加锁,锁粒度最小,资源开销大,加锁慢,会出现死锁,并发度最高。Innodb支持行锁。

    InnoDB的行锁实际上是针对索引加的锁(在索引对应的索引项上做标记),不是针对整个行记录加的锁。该索引不能失效,否则会从行锁升级为表锁(RR级别会升级为表锁,RC级别不会)。

    例如:

    select * from user where name='zhangsan' for update;
    +

    如果where条件中name字段没有索引,其他Session对该表任意一行做修改操作都会被阻塞住。

    RR级别行锁升级为表锁的原因分析:

    RR级别下,需要解决不可重复读和幻读问题,所以在便利扫描聚集索引时,为了防止扫描过的索引被其他事务修改(不可重复读)或间隙被其它事务插入新数据(幻读),从而导致数据不一致。
    所以MySQL的解决方案就是把所有扫描过的索引记录和间隙都上锁(并不是直接将整张表加表锁,因为不一定能加上表锁,可能会有其他事务已经锁住了表里的某些行数据)。

    读锁

    又称共享锁,简称S锁(Shared)。它允许多个事务对统一数据进行读取,但不能进行修改。对同一个数据,多个读操作可以同时进行,互不干扰。可以通过添加lock in share mode来加读锁。

    写锁

    又称排他锁,简称X锁(Exclusive)。它允许获取排它锁的事务读取和修改数据,阻止其他事务取得相同数据集的共享锁或排它锁。如果当前写操作没有完毕,则无法进行其他的读、写操作。数据新增、修改都会加写锁,查询也可以通过加for update来加写锁。

    读锁会阻塞写,但是不会阻塞读;写锁则会把读、写都阻塞。

    意向锁

    属于InnoDB引擎中的一种机制。当有事务给表的数据加了锁(读锁或写锁),同时会给表设置一个 标识(意向锁) 表示表已经有了行锁。当其他事务要对表进行加表锁时,就不用便利表数据判断数据有没有行锁会跟表锁冲突了,直接读取这个标识就可以进行判断是否可以加表锁。在表中记录很多时,逐行判断加表锁的方式效率很低。

    意向锁由分为:

    • 意向共享锁(IS): 事务有意向对表中的某些行加共享锁(S锁)。
    • 意向排它锁(IX): 事务有意向对表中的某些行加排它锁(X锁)。

    间隙锁(Gap Lock)

    间隙锁锁的是两个值之间的空隙,间隙锁是在可重复读隔离级别下才会生效。 对于RR级别下幻读的问题,可以通过间隙锁可以解决。

    假设user表有如下数据:

    idnameage
    1lilei5
    2zhangsan6
    5lisi7
    18wangwu8

    则此表的间隙有id为(2,5)、(5,18)、(18,+∞)这三个区间(开区间),当某个连接执行

    select * from user where id = 3 for update;
    +

    则其他的连接将没法在(2,5)这个间隙范围里插入任何数据。

    如果执行的是

    select * from user where id = 20 for update;
    +

    则其他的连接将没法在(18,+∞)这个间隙范围里插入任何数据。

    也就是说,只要在间隙范围内锁了一条不存在的记录就会锁住整个间隙范围,不锁边界记录,这样就能防止其他的连接在这个间隙范围内插入数据,就解决了RR级别的幻读问题。

    临建锁(Next-key Locks)

    临建锁是行锁于间隙锁的组合,行锁锁间隙两头的行。

    在MySQL中,当你执行范围查询时,例如\`SELECT…从表value1和value2; '之间的列中,InnoDB不仅锁住了满足查询条件的行(键),还锁住了键之间的间隔或范围。这被称为下一键锁定或间隙锁定。

    临建锁目的是防止其他事务插入原本应该包含在原始范围查询中的新行,从而导致幻影读。当一个事务检索一个范围的行,而另一个事务在第一个事务提交或回滚之前将新行插入到范围中时,就会发生幻影读取,导致第一个事务看到一个在最初读取范围时并不存在的“幻影”行。

    通过锁定键之间的空隙,InnoDB确保了没有新行被插入到被查询的范围中,保持了事务的一致性和可序列化性。

    当您需要在特定的值范围内处理频繁的插入、更新和删除时,下一键锁特别有用。它们保证范围查询的结果集在整个事务执行过程中保持稳定,即使其他事务修改了该范围内的数据。

    值得注意的是,下一个键锁定可能会导致锁争用增加和并发性降低,特别是在涉及许多范围查询和相同范围内并发修改的工作负载中。在这种情况下,您可能需要考虑其他技术,如应用程序级别的锁或分区,以平衡并发和一致性需求。

    MyISAM与InnoDB锁实现

    MyIsAM 在执行查询语句之前,会自动给设计的所有表加读锁,在执行insert、update、delete操作会自动给设计的表加写锁

    InnoDB在执行查询语句(非串行化隔离级别),不会加锁,但是insert、update、delete操作会加行锁。

    InnoDB由于实现了行级锁,虽然在锁定机制的实现方面锁带来的性能损耗可能会比表锁更高,但整体并发处理能力要远远优于MyISAM的表级锁定。当系统并发量高时,InnoDB的整体性能额MyISAM相比就会有比较明显的优势了。
    但是,InnoDB的行级锁定同样有不好的地方,当我们使用不当,可能就会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至有可能会更差。

    死锁

    大多数情况下MySQL可以自动检测死锁并回滚产生死锁的事务,但是在某些情况MySQL是没法自动检测的,这就需要我们可以通过日志找到对应的事务线程ID,通过ID来kill杀掉事务。

    锁优化实践

    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
    • 合理设计索引,尽量缩小锁的范围
    • 尽可能减少索引条件范围,避免间隙锁
    • 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的SQL尽量放在事务最后执行
    • 尽可能用低的事务隔离级别
    `,52);function k(g,u){return n(),t("div",null,[p,l,c,i,s(" more "),h])}const x=a(d,[["render",k],["__file","2403081801.html.vue"]]);export{x as default}; diff --git a/assets/404.html-4b85a0f5.js b/assets/404.html-8ce18156.js similarity index 63% rename from assets/404.html-4b85a0f5.js rename to assets/404.html-8ce18156.js index 7cce3b0a..00e19b86 100644 --- a/assets/404.html-4b85a0f5.js +++ b/assets/404.html-8ce18156.js @@ -1 +1 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const _={};function o(r,n){return c(),t("div")}const a=e(_,[["render",o],["__file","404.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const _={};function o(r,n){return c(),t("div")}const a=e(_,[["render",o],["__file","404.html.vue"]]);export{a as default}; diff --git a/assets/app-1efcbe9f.js b/assets/app-6a63891c.js similarity index 53% rename from assets/app-1efcbe9f.js rename to assets/app-6a63891c.js index 661d8f74..9dc36ba5 100644 --- a/assets/app-1efcbe9f.js +++ b/assets/app-6a63891c.js @@ -2,30 +2,30 @@ const Vu="modulepreload",Mu=function(e){return"/"+e},ri={},f=function(t,n,r){if( * @vue/shared v3.4.21 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT -**/function ua(e,t){const n=new Set(e.split(","));return t?r=>n.has(r.toLowerCase()):r=>n.has(r)}const Ae={},vn=[],Je=()=>{},Bu=()=>!1,ar=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),da=e=>e.startsWith("onUpdate:"),De=Object.assign,fa=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},$u=Object.prototype.hasOwnProperty,fe=(e,t)=>$u.call(e,t),ee=Array.isArray,Nn=e=>Zr(e)==="[object Map]",Nu=e=>Zr(e)==="[object Set]",re=e=>typeof e=="function",pe=e=>typeof e=="string",Xr=e=>typeof e=="symbol",ke=e=>e!==null&&typeof e=="object",Il=e=>(ke(e)||re(e))&&re(e.then)&&re(e.catch),Hu=Object.prototype.toString,Zr=e=>Hu.call(e),Fu=e=>Zr(e).slice(8,-1),ju=e=>Zr(e)==="[object Object]",pa=e=>pe(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,hn=ua(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),eo=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},zu=/-(\w)/g,ot=eo(e=>e.replace(zu,(t,n)=>n?n.toUpperCase():"")),qu=/\B([A-Z])/g,kn=eo(e=>e.replace(qu,"-$1").toLowerCase()),ir=eo(e=>e.charAt(0).toUpperCase()+e.slice(1)),bo=eo(e=>e?`on${ir(e)}`:""),zt=(e,t)=>!Object.is(e,t),yo=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Uu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gu=e=>{const t=pe(e)?Number(e):NaN;return isNaN(t)?e:t};let oi;const wl=()=>oi||(oi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function to(e){if(ee(e)){const t={};for(let n=0;n{if(n){const r=n.split(Ju);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function no(e){let t="";if(pe(e))t=e;else if(ee(e))for(let n=0;nn.has(r.toLowerCase()):r=>n.has(r)}const Te={},vn=[],Je=()=>{},Bu=()=>!1,ar=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),da=e=>e.startsWith("onUpdate:"),De=Object.assign,fa=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},$u=Object.prototype.hasOwnProperty,fe=(e,t)=>$u.call(e,t),ee=Array.isArray,Nn=e=>Zr(e)==="[object Map]",Nu=e=>Zr(e)==="[object Set]",re=e=>typeof e=="function",pe=e=>typeof e=="string",Xr=e=>typeof e=="symbol",ke=e=>e!==null&&typeof e=="object",Il=e=>(ke(e)||re(e))&&re(e.then)&&re(e.catch),Hu=Object.prototype.toString,Zr=e=>Hu.call(e),Fu=e=>Zr(e).slice(8,-1),ju=e=>Zr(e)==="[object Object]",pa=e=>pe(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,hn=ua(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),eo=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},zu=/-(\w)/g,ot=eo(e=>e.replace(zu,(t,n)=>n?n.toUpperCase():"")),qu=/\B([A-Z])/g,kn=eo(e=>e.replace(qu,"-$1").toLowerCase()),ir=eo(e=>e.charAt(0).toUpperCase()+e.slice(1)),bo=eo(e=>e?`on${ir(e)}`:""),zt=(e,t)=>!Object.is(e,t),yo=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Uu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gu=e=>{const t=pe(e)?Number(e):NaN;return isNaN(t)?e:t};let oi;const wl=()=>oi||(oi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function to(e){if(ee(e)){const t={};for(let n=0;n{if(n){const r=n.split(Ju);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function no(e){let t="";if(pe(e))t=e;else if(ee(e))for(let n=0;n=4))break}this._dirtyLevel===1&&(this._dirtyLevel=0),nn()}return this._dirtyLevel>=4}set dirty(t){this._dirtyLevel=t?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=Ft,n=Xt;try{return Ft=!0,Xt=this,this._runnings++,ai(this),this.fn()}finally{ii(this),this._runnings--,Xt=n,Ft=t}}stop(){var t;this.active&&(ai(this),ii(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function r0(e){return e.value}function ai(e){e._trackId++,e._depsLength=0}function ii(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},zr=new WeakMap,Zt=Symbol(""),Fo=Symbol("");function qe(e,t,n){if(Ft&&Xt){let r=zr.get(e);r||zr.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=xl(()=>r.delete(n))),Dl(Xt,o)}}function At(e,t,n,r,o,a){const i=zr.get(e);if(!i)return;let s=[];if(t==="clear")s=[...i.values()];else if(n==="length"&&ee(e)){const c=Number(r);i.forEach((u,d)=>{(d==="length"||!Xr(d)&&d>=c)&&s.push(u)})}else switch(n!==void 0&&s.push(i.get(n)),t){case"add":ee(e)?pa(n)&&s.push(i.get("length")):(s.push(i.get(Zt)),Nn(e)&&s.push(i.get(Fo)));break;case"delete":ee(e)||(s.push(i.get(Zt)),Nn(e)&&s.push(i.get(Fo)));break;case"set":Nn(e)&&s.push(i.get(Zt));break}ha();for(const c of s)c&&Sl(c,4);ma()}function o0(e,t){var n;return(n=zr.get(e))==null?void 0:n.get(t)}const a0=ua("__proto__,__v_isRef,__isVue"),Cl=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Xr)),li=i0();function i0(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=ce(this);for(let a=0,i=this.length;a{e[t]=function(...n){tn(),ha();const r=ce(this)[t].apply(this,n);return ma(),nn(),r}}),e}function l0(e){const t=ce(this);return qe(t,"has",e),t.hasOwnProperty(e)}class Vl{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){const o=this._isReadonly,a=this._isShallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return a;if(n==="__v_raw")return r===(o?a?y0:Nl:a?$l:Bl).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const i=ee(t);if(!o){if(i&&fe(li,n))return Reflect.get(li,n,r);if(n==="hasOwnProperty")return l0}const s=Reflect.get(t,n,r);return(Xr(n)?Cl.has(n):a0(n))||(o||qe(t,"get",n),a)?s:Ve(s)?i&&pa(n)?s:s.value:ke(s)?o?Ut(s):lr(s):s}}class Ml extends Vl{constructor(t=!1){super(!1,t)}set(t,n,r,o){let a=t[n];if(!this._isShallow){const c=yn(a);if(!qr(r)&&!yn(r)&&(a=ce(a),r=ce(r)),!ee(t)&&Ve(a)&&!Ve(r))return c?!1:(a.value=r,!0)}const i=ee(t)&&pa(n)?Number(n)e,ro=e=>Reflect.getPrototypeOf(e);function wr(e,t,n=!1,r=!1){e=e.__v_raw;const o=ce(e),a=ce(t);n||(zt(t,a)&&qe(o,"get",t),qe(o,"get",a));const{has:i}=ro(o),s=r?ga:n?ya:Jn;if(i.call(o,t))return s(e.get(t));if(i.call(o,a))return s(e.get(a));e!==o&&e.get(t)}function kr(e,t=!1){const n=this.__v_raw,r=ce(n),o=ce(e);return t||(zt(e,o)&&qe(r,"has",e),qe(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function Pr(e,t=!1){return e=e.__v_raw,!t&&qe(ce(e),"iterate",Zt),Reflect.get(e,"size",e)}function si(e){e=ce(e);const t=ce(this);return ro(t).has.call(t,e)||(t.add(e),At(t,"add",e,e)),this}function ci(e,t){t=ce(t);const n=ce(this),{has:r,get:o}=ro(n);let a=r.call(n,e);a||(e=ce(e),a=r.call(n,e));const i=o.call(n,e);return n.set(e,t),a?zt(t,i)&&At(n,"set",e,t):At(n,"add",e,t),this}function ui(e){const t=ce(this),{has:n,get:r}=ro(t);let o=n.call(t,e);o||(e=ce(e),o=n.call(t,e)),r&&r.call(t,e);const a=t.delete(e);return o&&At(t,"delete",e,void 0),a}function di(){const e=ce(this),t=e.size!==0,n=e.clear();return t&&At(e,"clear",void 0,void 0),n}function Or(e,t){return function(r,o){const a=this,i=a.__v_raw,s=ce(i),c=t?ga:e?ya:Jn;return!e&&qe(s,"iterate",Zt),i.forEach((u,d)=>r.call(o,c(u),c(d),a))}}function Rr(e,t,n){return function(...r){const o=this.__v_raw,a=ce(o),i=Nn(a),s=e==="entries"||e===Symbol.iterator&&i,c=e==="keys"&&i,u=o[e](...r),d=n?ga:t?ya:Jn;return!t&&qe(a,"iterate",c?Fo:Zt),{next(){const{value:p,done:v}=u.next();return v?{value:p,done:v}:{value:s?[d(p[0]),d(p[1])]:d(p),done:v}},[Symbol.iterator](){return this}}}}function Dt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function f0(){const e={get(a){return wr(this,a)},get size(){return Pr(this)},has:kr,add:si,set:ci,delete:ui,clear:di,forEach:Or(!1,!1)},t={get(a){return wr(this,a,!1,!0)},get size(){return Pr(this)},has:kr,add:si,set:ci,delete:ui,clear:di,forEach:Or(!1,!0)},n={get(a){return wr(this,a,!0)},get size(){return Pr(this,!0)},has(a){return kr.call(this,a,!0)},add:Dt("add"),set:Dt("set"),delete:Dt("delete"),clear:Dt("clear"),forEach:Or(!0,!1)},r={get(a){return wr(this,a,!0,!0)},get size(){return Pr(this,!0)},has(a){return kr.call(this,a,!0)},add:Dt("add"),set:Dt("set"),delete:Dt("delete"),clear:Dt("clear"),forEach:Or(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(a=>{e[a]=Rr(a,!1,!1),n[a]=Rr(a,!0,!1),t[a]=Rr(a,!1,!0),r[a]=Rr(a,!0,!0)}),[e,n,t,r]}const[p0,v0,h0,m0]=f0();function _a(e,t){const n=t?e?m0:h0:e?v0:p0;return(r,o,a)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(fe(n,o)&&o in r?n:r,o,a)}const g0={get:_a(!1,!1)},_0={get:_a(!1,!0)},b0={get:_a(!0,!1)},Bl=new WeakMap,$l=new WeakMap,Nl=new WeakMap,y0=new WeakMap;function E0(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function L0(e){return e.__v_skip||!Object.isExtensible(e)?0:E0(Fu(e))}function lr(e){return yn(e)?e:ba(e,!1,c0,g0,Bl)}function Hl(e){return ba(e,!1,d0,_0,$l)}function Ut(e){return ba(e,!0,u0,b0,Nl)}function ba(e,t,n,r,o){if(!ke(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const a=o.get(e);if(a)return a;const i=L0(e);if(i===0)return e;const s=new Proxy(e,i===2?r:n);return o.set(e,s),s}function mn(e){return yn(e)?mn(e.__v_raw):!!(e&&e.__v_isReactive)}function yn(e){return!!(e&&e.__v_isReadonly)}function qr(e){return!!(e&&e.__v_isShallow)}function Fl(e){return mn(e)||yn(e)}function ce(e){const t=e&&e.__v_raw;return t?ce(t):e}function jl(e){return Object.isExtensible(e)&&jr(e,"__v_skip",!0),e}const Jn=e=>ke(e)?lr(e):e,ya=e=>ke(e)?Ut(e):e;class zl{constructor(t,n,r,o){this.getter=t,this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new va(()=>t(this._value),()=>Hn(this,this.effect._dirtyLevel===2?2:3)),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=ce(this);return(!t._cacheable||t.effect.dirty)&&zt(t._value,t._value=t.effect.run())&&Hn(t,4),Ea(t),t.effect._dirtyLevel>=2&&Hn(t,2),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function T0(e,t,n=!1){let r,o;const a=re(e);return a?(r=e,o=Je):(r=e.get,o=e.set),new zl(r,o,a||!o,n)}function Ea(e){var t;Ft&&Xt&&(e=ce(e),Dl(Xt,(t=e.dep)!=null?t:e.dep=xl(()=>e.dep=void 0,e instanceof zl?e:void 0)))}function Hn(e,t=4,n){e=ce(e);const r=e.dep;r&&Sl(r,t)}function Ve(e){return!!(e&&e.__v_isRef===!0)}function Y(e){return ql(e,!1)}function ft(e){return ql(e,!0)}function ql(e,t){return Ve(e)?e:new A0(e,t)}class A0{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:ce(t),this._value=n?t:Jn(t)}get value(){return Ea(this),this._value}set value(t){const n=this.__v_isShallow||qr(t)||yn(t);t=n?t:ce(t),zt(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Jn(t),Hn(this,4))}}function _t(e){return Ve(e)?e.value:e}const I0={get:(e,t,n)=>_t(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Ve(o)&&!Ve(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Ul(e){return mn(e)?e:new Proxy(e,I0)}class w0{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>Ea(this),()=>Hn(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Gl(e){return new w0(e)}class k0{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return o0(ce(this._object),this._key)}}class P0{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Pn(e,t,n){return Ve(e)?e:re(e)?new P0(e):ke(e)&&arguments.length>1?O0(e,t,n):Y(e)}function O0(e,t,n){const r=e[t];return Ve(r)?r:new k0(e,t,n)}/** +**/let Ge;class e0{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Ge,!t&&Ge&&(this.index=(Ge.scopes||(Ge.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Ge;try{return Ge=this,t()}finally{Ge=n}}}on(){Ge=this}off(){Ge=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n=4))break}this._dirtyLevel===1&&(this._dirtyLevel=0),nn()}return this._dirtyLevel>=4}set dirty(t){this._dirtyLevel=t?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=Ft,n=Xt;try{return Ft=!0,Xt=this,this._runnings++,ai(this),this.fn()}finally{ii(this),this._runnings--,Xt=n,Ft=t}}stop(){var t;this.active&&(ai(this),ii(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function r0(e){return e.value}function ai(e){e._trackId++,e._depsLength=0}function ii(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},zr=new WeakMap,Zt=Symbol(""),Fo=Symbol("");function qe(e,t,n){if(Ft&&Xt){let r=zr.get(e);r||zr.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=xl(()=>r.delete(n))),Dl(Xt,o)}}function Tt(e,t,n,r,o,a){const i=zr.get(e);if(!i)return;let s=[];if(t==="clear")s=[...i.values()];else if(n==="length"&&ee(e)){const c=Number(r);i.forEach((u,d)=>{(d==="length"||!Xr(d)&&d>=c)&&s.push(u)})}else switch(n!==void 0&&s.push(i.get(n)),t){case"add":ee(e)?pa(n)&&s.push(i.get("length")):(s.push(i.get(Zt)),Nn(e)&&s.push(i.get(Fo)));break;case"delete":ee(e)||(s.push(i.get(Zt)),Nn(e)&&s.push(i.get(Fo)));break;case"set":Nn(e)&&s.push(i.get(Zt));break}ha();for(const c of s)c&&Sl(c,4);ma()}function o0(e,t){var n;return(n=zr.get(e))==null?void 0:n.get(t)}const a0=ua("__proto__,__v_isRef,__isVue"),Cl=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Xr)),li=i0();function i0(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=ce(this);for(let a=0,i=this.length;a{e[t]=function(...n){tn(),ha();const r=ce(this)[t].apply(this,n);return ma(),nn(),r}}),e}function l0(e){const t=ce(this);return qe(t,"has",e),t.hasOwnProperty(e)}class Vl{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){const o=this._isReadonly,a=this._isShallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return a;if(n==="__v_raw")return r===(o?a?y0:Nl:a?$l:Bl).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const i=ee(t);if(!o){if(i&&fe(li,n))return Reflect.get(li,n,r);if(n==="hasOwnProperty")return l0}const s=Reflect.get(t,n,r);return(Xr(n)?Cl.has(n):a0(n))||(o||qe(t,"get",n),a)?s:Ve(s)?i&&pa(n)?s:s.value:ke(s)?o?Ut(s):lr(s):s}}class Ml extends Vl{constructor(t=!1){super(!1,t)}set(t,n,r,o){let a=t[n];if(!this._isShallow){const c=yn(a);if(!qr(r)&&!yn(r)&&(a=ce(a),r=ce(r)),!ee(t)&&Ve(a)&&!Ve(r))return c?!1:(a.value=r,!0)}const i=ee(t)&&pa(n)?Number(n)e,ro=e=>Reflect.getPrototypeOf(e);function wr(e,t,n=!1,r=!1){e=e.__v_raw;const o=ce(e),a=ce(t);n||(zt(t,a)&&qe(o,"get",t),qe(o,"get",a));const{has:i}=ro(o),s=r?ga:n?ya:Jn;if(i.call(o,t))return s(e.get(t));if(i.call(o,a))return s(e.get(a));e!==o&&e.get(t)}function kr(e,t=!1){const n=this.__v_raw,r=ce(n),o=ce(e);return t||(zt(e,o)&&qe(r,"has",e),qe(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function Pr(e,t=!1){return e=e.__v_raw,!t&&qe(ce(e),"iterate",Zt),Reflect.get(e,"size",e)}function si(e){e=ce(e);const t=ce(this);return ro(t).has.call(t,e)||(t.add(e),Tt(t,"add",e,e)),this}function ci(e,t){t=ce(t);const n=ce(this),{has:r,get:o}=ro(n);let a=r.call(n,e);a||(e=ce(e),a=r.call(n,e));const i=o.call(n,e);return n.set(e,t),a?zt(t,i)&&Tt(n,"set",e,t):Tt(n,"add",e,t),this}function ui(e){const t=ce(this),{has:n,get:r}=ro(t);let o=n.call(t,e);o||(e=ce(e),o=n.call(t,e)),r&&r.call(t,e);const a=t.delete(e);return o&&Tt(t,"delete",e,void 0),a}function di(){const e=ce(this),t=e.size!==0,n=e.clear();return t&&Tt(e,"clear",void 0,void 0),n}function Or(e,t){return function(r,o){const a=this,i=a.__v_raw,s=ce(i),c=t?ga:e?ya:Jn;return!e&&qe(s,"iterate",Zt),i.forEach((u,d)=>r.call(o,c(u),c(d),a))}}function Rr(e,t,n){return function(...r){const o=this.__v_raw,a=ce(o),i=Nn(a),s=e==="entries"||e===Symbol.iterator&&i,c=e==="keys"&&i,u=o[e](...r),d=n?ga:t?ya:Jn;return!t&&qe(a,"iterate",c?Fo:Zt),{next(){const{value:p,done:v}=u.next();return v?{value:p,done:v}:{value:s?[d(p[0]),d(p[1])]:d(p),done:v}},[Symbol.iterator](){return this}}}}function Dt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function f0(){const e={get(a){return wr(this,a)},get size(){return Pr(this)},has:kr,add:si,set:ci,delete:ui,clear:di,forEach:Or(!1,!1)},t={get(a){return wr(this,a,!1,!0)},get size(){return Pr(this)},has:kr,add:si,set:ci,delete:ui,clear:di,forEach:Or(!1,!0)},n={get(a){return wr(this,a,!0)},get size(){return Pr(this,!0)},has(a){return kr.call(this,a,!0)},add:Dt("add"),set:Dt("set"),delete:Dt("delete"),clear:Dt("clear"),forEach:Or(!0,!1)},r={get(a){return wr(this,a,!0,!0)},get size(){return Pr(this,!0)},has(a){return kr.call(this,a,!0)},add:Dt("add"),set:Dt("set"),delete:Dt("delete"),clear:Dt("clear"),forEach:Or(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(a=>{e[a]=Rr(a,!1,!1),n[a]=Rr(a,!0,!1),t[a]=Rr(a,!1,!0),r[a]=Rr(a,!0,!0)}),[e,n,t,r]}const[p0,v0,h0,m0]=f0();function _a(e,t){const n=t?e?m0:h0:e?v0:p0;return(r,o,a)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(fe(n,o)&&o in r?n:r,o,a)}const g0={get:_a(!1,!1)},_0={get:_a(!1,!0)},b0={get:_a(!0,!1)},Bl=new WeakMap,$l=new WeakMap,Nl=new WeakMap,y0=new WeakMap;function E0(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function L0(e){return e.__v_skip||!Object.isExtensible(e)?0:E0(Fu(e))}function lr(e){return yn(e)?e:ba(e,!1,c0,g0,Bl)}function Hl(e){return ba(e,!1,d0,_0,$l)}function Ut(e){return ba(e,!0,u0,b0,Nl)}function ba(e,t,n,r,o){if(!ke(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const a=o.get(e);if(a)return a;const i=L0(e);if(i===0)return e;const s=new Proxy(e,i===2?r:n);return o.set(e,s),s}function mn(e){return yn(e)?mn(e.__v_raw):!!(e&&e.__v_isReactive)}function yn(e){return!!(e&&e.__v_isReadonly)}function qr(e){return!!(e&&e.__v_isShallow)}function Fl(e){return mn(e)||yn(e)}function ce(e){const t=e&&e.__v_raw;return t?ce(t):e}function jl(e){return Object.isExtensible(e)&&jr(e,"__v_skip",!0),e}const Jn=e=>ke(e)?lr(e):e,ya=e=>ke(e)?Ut(e):e;class zl{constructor(t,n,r,o){this.getter=t,this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new va(()=>t(this._value),()=>Hn(this,this.effect._dirtyLevel===2?2:3)),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=ce(this);return(!t._cacheable||t.effect.dirty)&&zt(t._value,t._value=t.effect.run())&&Hn(t,4),Ea(t),t.effect._dirtyLevel>=2&&Hn(t,2),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function A0(e,t,n=!1){let r,o;const a=re(e);return a?(r=e,o=Je):(r=e.get,o=e.set),new zl(r,o,a||!o,n)}function Ea(e){var t;Ft&&Xt&&(e=ce(e),Dl(Xt,(t=e.dep)!=null?t:e.dep=xl(()=>e.dep=void 0,e instanceof zl?e:void 0)))}function Hn(e,t=4,n){e=ce(e);const r=e.dep;r&&Sl(r,t)}function Ve(e){return!!(e&&e.__v_isRef===!0)}function Y(e){return ql(e,!1)}function ft(e){return ql(e,!0)}function ql(e,t){return Ve(e)?e:new T0(e,t)}class T0{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:ce(t),this._value=n?t:Jn(t)}get value(){return Ea(this),this._value}set value(t){const n=this.__v_isShallow||qr(t)||yn(t);t=n?t:ce(t),zt(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Jn(t),Hn(this,4))}}function _t(e){return Ve(e)?e.value:e}const I0={get:(e,t,n)=>_t(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Ve(o)&&!Ve(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Ul(e){return mn(e)?e:new Proxy(e,I0)}class w0{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>Ea(this),()=>Hn(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Gl(e){return new w0(e)}class k0{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return o0(ce(this._object),this._key)}}class P0{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Pn(e,t,n){return Ve(e)?e:re(e)?new P0(e):ke(e)&&arguments.length>1?O0(e,t,n):Y(e)}function O0(e,t,n){const r=e[t];return Ve(r)?r:new k0(e,t,n)}/** * @vue/runtime-core v3.4.21 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT -**/function jt(e,t,n,r){try{return r?e(...r):e()}catch(o){sr(o,t,n)}}function tt(e,t,n,r){if(re(e)){const a=jt(e,t,n,r);return a&&Il(a)&&a.catch(i=>{sr(i,t,n)}),a}const o=[];for(let a=0;a>>1,o=Be[r],a=Kn(o);agt&&Be.splice(t,1)}function x0(e){ee(e)?gn.push(...e):(!Mt||!Mt.includes(e,e.allowRecurse?Kt+1:Kt))&&gn.push(e),Jl()}function fi(e,t,n=Qn?gt+1:0){for(;nKn(n)-Kn(r));if(gn.length=0,Mt){Mt.push(...t);return}for(Mt=t,Kt=0;Kte.id==null?1/0:e.id,C0=(e,t)=>{const n=Kn(e)-Kn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ql(e){jo=!1,Qn=!0,Be.sort(C0);const t=Je;try{for(gt=0;gtpe(h)?h.trim():h)),p&&(o=n.map(Uu))}let s,c=r[s=bo(t)]||r[s=bo(ot(t))];!c&&a&&(c=r[s=bo(kn(t))]),c&&tt(c,e,6,o);const u=r[s+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[s])return;e.emitted[s]=!0,tt(u,e,6,o)}}function Kl(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const a=e.emits;let i={},s=!1;if(!re(e)){const c=u=>{const d=Kl(u,t,!0);d&&(s=!0,De(i,d))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!a&&!s?(ke(e)&&r.set(e,null),null):(ee(a)?a.forEach(c=>i[c]=null):De(i,a),ke(e)&&r.set(e,i),i)}function ao(e,t){return!e||!ar(t)?!1:(t=t.slice(2).replace(/Once$/,""),fe(e,t[0].toLowerCase()+t.slice(1))||fe(e,kn(t))||fe(e,t))}let et=null,Yl=null;function Gr(e){const t=et;return et=e,Yl=e&&e.type.__scopeId||null,t}function zo(e,t=et,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Ai(-1);const a=Gr(t);let i;try{i=e(...o)}finally{Gr(a),r._d&&Ai(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Eo(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:a,propsOptions:[i],slots:s,attrs:c,emit:u,render:d,renderCache:p,data:v,setupState:h,ctx:b,inheritAttrs:A}=e;let I,E;const k=Gr(e);try{if(n.shapeFlag&4){const P=o||r,$=P;I=lt(d.call($,P,p,a,h,v,b)),E=c}else{const P=t;I=lt(P.length>1?P(a,{attrs:c,slots:s,emit:u}):P(a,null)),E=t.props?c:M0(c)}}catch(P){Un.length=0,sr(P,e,1),I=we(nt)}let y=I;if(E&&A!==!1){const P=Object.keys(E),{shapeFlag:$}=y;P.length&&$&7&&(i&&P.some(da)&&(E=B0(E,i)),y=qt(y,E))}return n.dirs&&(y=qt(y),y.dirs=y.dirs?y.dirs.concat(n.dirs):n.dirs),n.transition&&(y.transition=n.transition),I=y,Gr(k),I}const M0=e=>{let t;for(const n in e)(n==="class"||n==="style"||ar(n))&&((t||(t={}))[n]=e[n]);return t},B0=(e,t)=>{const n={};for(const r in e)(!da(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function $0(e,t,n){const{props:r,children:o,component:a}=e,{props:i,children:s,patchFlag:c}=t,u=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?pi(r,i,u):!!i;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Zl(e,t){t&&t.pendingBranch?ee(e)?t.effects.push(...e):t.effects.push(e):x0(e)}const z0=Symbol.for("v-scx"),q0=()=>he(z0);function Ta(e,t){return Aa(e,null,t)}const Dr={};function ve(e,t,n){return Aa(e,t,n)}function Aa(e,t,{immediate:n,deep:r,flush:o,once:a,onTrack:i,onTrigger:s}=Ae){if(t&&a){const w=t;t=(...U)=>{w(...U),$()}}const c=Ce,u=w=>r===!0?w:fn(w,r===!1?1:void 0);let d,p=!1,v=!1;if(Ve(e)?(d=()=>e.value,p=qr(e)):mn(e)?(d=()=>u(e),p=!0):ee(e)?(v=!0,p=e.some(w=>mn(w)||qr(w)),d=()=>e.map(w=>{if(Ve(w))return w.value;if(mn(w))return u(w);if(re(w))return jt(w,c,2)})):re(e)?t?d=()=>jt(e,c,2):d=()=>(h&&h(),tt(e,c,3,[b])):d=Je,t&&r){const w=d;d=()=>fn(w())}let h,b=w=>{h=y.onStop=()=>{jt(w,c,4),h=y.onStop=void 0}},A;if(fr)if(b=Je,t?n&&tt(t,c,3,[d(),v?[]:void 0,b]):d(),o==="sync"){const w=q0();A=w.__watcherHandles||(w.__watcherHandles=[])}else return Je;let I=v?new Array(e.length).fill(Dr):Dr;const E=()=>{if(!(!y.active||!y.dirty))if(t){const w=y.run();(r||p||(v?w.some((U,H)=>zt(U,I[H])):zt(w,I)))&&(h&&h(),tt(t,c,3,[w,I===Dr?void 0:v&&I[0]===Dr?[]:I,b]),I=w)}else y.run()};E.allowRecurse=!!t;let k;o==="sync"?k=E:o==="post"?k=()=>je(E,c&&c.suspense):(E.pre=!0,c&&(E.id=c.uid),k=()=>oo(E));const y=new va(d,Je,k),P=Pl(),$=()=>{y.stop(),P&&fa(P.effects,y)};return t?n?E():I=y.run():o==="post"?je(y.run.bind(y),c&&c.suspense):y.run(),A&&A.push($),$}function U0(e,t,n){const r=this.proxy,o=pe(e)?e.includes(".")?es(r,e):()=>r[e]:e.bind(r,r);let a;re(t)?a=t:(a=t.handler,n=t);const i=dr(this),s=Aa(o,a.bind(r),n);return i(),s}function es(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o0){if(n>=t)return e;n++}if(r=r||new Set,r.has(e))return e;if(r.add(e),Ve(e))fn(e.value,t,n,r);else if(ee(e))for(let o=0;o{fn(o,t,n,r)});else if(ju(e))for(const o in e)fn(e[o],t,n,r);return e}function mt(e,t,n,r){const o=e.dirs,a=t&&t.dirs;for(let i=0;i{e.isMounted=!0}),wa(()=>{e.isUnmounting=!0}),e}const Xe=[Function,Array],ns={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Xe,onEnter:Xe,onAfterEnter:Xe,onEnterCancelled:Xe,onBeforeLeave:Xe,onLeave:Xe,onAfterLeave:Xe,onLeaveCancelled:Xe,onBeforeAppear:Xe,onAppear:Xe,onAfterAppear:Xe,onAppearCancelled:Xe},G0={name:"BaseTransition",props:ns,setup(e,{slots:t}){const n=On(),r=ts();return()=>{const o=t.default&&Ia(t.default(),!0);if(!o||!o.length)return;let a=o[0];if(o.length>1){for(const v of o)if(v.type!==nt){a=v;break}}const i=ce(e),{mode:s}=i;if(r.isLeaving)return Lo(a);const c=hi(a);if(!c)return Lo(a);const u=Yn(c,i,r,n);Xn(c,u);const d=n.subTree,p=d&&hi(d);if(p&&p.type!==nt&&!Yt(c,p)){const v=Yn(p,i,r,n);if(Xn(p,v),s==="out-in")return r.isLeaving=!0,v.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},Lo(a);s==="in-out"&&c.type!==nt&&(v.delayLeave=(h,b,A)=>{const I=rs(r,p);I[String(p.key)]=p,h[Bt]=()=>{b(),h[Bt]=void 0,delete u.delayedLeave},u.delayedLeave=A})}return a}}},W0=G0;function rs(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function Yn(e,t,n,r){const{appear:o,mode:a,persisted:i=!1,onBeforeEnter:s,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:v,onAfterLeave:h,onLeaveCancelled:b,onBeforeAppear:A,onAppear:I,onAfterAppear:E,onAppearCancelled:k}=t,y=String(e.key),P=rs(n,e),$=(H,J)=>{H&&tt(H,r,9,J)},w=(H,J)=>{const x=J[1];$(H,J),ee(H)?H.every(X=>X.length<=1)&&x():H.length<=1&&x()},U={mode:a,persisted:i,beforeEnter(H){let J=s;if(!n.isMounted)if(o)J=A||s;else return;H[Bt]&&H[Bt](!0);const x=P[y];x&&Yt(e,x)&&x.el[Bt]&&x.el[Bt](),$(J,[H])},enter(H){let J=c,x=u,X=d;if(!n.isMounted)if(o)J=I||c,x=E||u,X=k||d;else return;let M=!1;const te=H[Sr]=Pe=>{M||(M=!0,Pe?$(X,[H]):$(x,[H]),U.delayedLeave&&U.delayedLeave(),H[Sr]=void 0)};J?w(J,[H,te]):te()},leave(H,J){const x=String(e.key);if(H[Sr]&&H[Sr](!0),n.isUnmounting)return J();$(p,[H]);let X=!1;const M=H[Bt]=te=>{X||(X=!0,J(),te?$(b,[H]):$(h,[H]),H[Bt]=void 0,P[x]===e&&delete P[x])};P[x]=e,v?w(v,[H,M]):M()},clone(H){return Yn(H,t,n,r)}};return U}function Lo(e){if(cr(e))return e=qt(e),e.children=null,e}function hi(e){return cr(e)?e.children?e.children[0]:void 0:e}function Xn(e,t){e.shapeFlag&6&&e.component?Xn(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Ia(e,t=!1,n){let r=[],o=0;for(let a=0;a1)for(let a=0;aDe({name:e.name},t,{setup:e}))():e}const Fn=e=>!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function _(e){re(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:a,suspensible:i=!0,onError:s}=e;let c=null,u,d=0;const p=()=>(d++,c=null,v()),v=()=>{let h;return c||(h=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),s)return new Promise((A,I)=>{s(b,()=>A(p()),()=>I(b),d+1)});throw b}).then(b=>h!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return B({name:"AsyncComponentWrapper",__asyncLoader:v,get __asyncResolved(){return u},setup(){const h=Ce;if(u)return()=>To(u,h);const b=k=>{c=null,sr(k,h,13,!r)};if(i&&h.suspense||fr)return v().then(k=>()=>To(k,h)).catch(k=>(b(k),()=>r?we(r,{error:k}):null));const A=Y(!1),I=Y(),E=Y(!!o);return o&&setTimeout(()=>{E.value=!1},o),a!=null&&setTimeout(()=>{if(!A.value&&!I.value){const k=new Error(`Async component timed out after ${a}ms.`);b(k),I.value=k}},a),v().then(()=>{A.value=!0,h.parent&&cr(h.parent.vnode)&&(h.parent.effect.dirty=!0,oo(h.parent.update))}).catch(k=>{b(k),I.value=k}),()=>{if(A.value&&u)return To(u,h);if(I.value&&r)return we(r,{error:I.value});if(n&&!E.value)return we(n)}}})}function To(e,t){const{ref:n,props:r,children:o,ce:a}=t.vnode,i=we(e,r,o);return i.ref=n,i.ce=a,delete t.vnode.ce,i}const cr=e=>e.type.__isKeepAlive;function J0(e,t){os(e,"a",t)}function Q0(e,t){os(e,"da",t)}function os(e,t,n=Ce){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(io(t,r,n),n){let o=n.parent;for(;o&&o.parent;)cr(o.parent.vnode)&&K0(r,t,n,o),o=o.parent}}function K0(e,t,n,r){const o=io(t,e,r,!0);ur(()=>{fa(r[t],o)},n)}function io(e,t,n=Ce,r=!1){if(n){const o=n[e]||(n[e]=[]),a=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;tn();const s=dr(n),c=tt(t,n,e,i);return s(),nn(),c});return r?o.unshift(a):o.push(a),a}}const Ot=e=>(t,n=Ce)=>(!fr||e==="sp")&&io(e,(...r)=>t(...r),n),Y0=Ot("bm"),ye=Ot("m"),X0=Ot("bu"),as=Ot("u"),wa=Ot("bum"),ur=Ot("um"),Z0=Ot("sp"),e1=Ot("rtg"),t1=Ot("rtc");function n1(e,t=Ce){io("ec",e,t)}const qo=e=>e?Ts(e)?Ra(e)||e.proxy:qo(e.parent):null,jn=De(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>qo(e.parent),$root:e=>qo(e.root),$emit:e=>e.emit,$options:e=>ka(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,oo(e.update)}),$nextTick:e=>e.n||(e.n=rn.bind(e.proxy)),$watch:e=>U0.bind(e)}),Ao=(e,t)=>e!==Ae&&!e.__isScriptSetup&&fe(e,t),r1={get({_:e},t){const{ctx:n,setupState:r,data:o,props:a,accessCache:i,type:s,appContext:c}=e;let u;if(t[0]!=="$"){const h=i[t];if(h!==void 0)switch(h){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return a[t]}else{if(Ao(r,t))return i[t]=1,r[t];if(o!==Ae&&fe(o,t))return i[t]=2,o[t];if((u=e.propsOptions[0])&&fe(u,t))return i[t]=3,a[t];if(n!==Ae&&fe(n,t))return i[t]=4,n[t];Uo&&(i[t]=0)}}const d=jn[t];let p,v;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=s.__cssModules)&&(p=p[t]))return p;if(n!==Ae&&fe(n,t))return i[t]=4,n[t];if(v=c.config.globalProperties,fe(v,t))return v[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:a}=e;return Ao(o,t)?(o[t]=n,!0):r!==Ae&&fe(r,t)?(r[t]=n,!0):fe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(a[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:a}},i){let s;return!!n[i]||e!==Ae&&fe(e,i)||Ao(t,i)||(s=a[0])&&fe(s,i)||fe(r,i)||fe(jn,i)||fe(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:fe(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function mi(e){return ee(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let Uo=!0;function o1(e){const t=ka(e),n=e.proxy,r=e.ctx;Uo=!1,t.beforeCreate&&gi(t.beforeCreate,e,"bc");const{data:o,computed:a,methods:i,watch:s,provide:c,inject:u,created:d,beforeMount:p,mounted:v,beforeUpdate:h,updated:b,activated:A,deactivated:I,beforeDestroy:E,beforeUnmount:k,destroyed:y,unmounted:P,render:$,renderTracked:w,renderTriggered:U,errorCaptured:H,serverPrefetch:J,expose:x,inheritAttrs:X,components:M,directives:te,filters:Pe}=t;if(u&&a1(u,r,null),i)for(const oe in i){const W=i[oe];re(W)&&(r[oe]=W.bind(n))}if(o){const oe=o.call(n,n);ke(oe)&&(e.data=lr(oe))}if(Uo=!0,a)for(const oe in a){const W=a[oe],at=re(W)?W.bind(n,n):re(W.get)?W.get.bind(n,n):Je,Rt=!re(W)&&re(W.set)?W.set.bind(n):Je,vt=L({get:at,set:Rt});Object.defineProperty(r,oe,{enumerable:!0,configurable:!0,get:()=>vt.value,set:Fe=>vt.value=Fe})}if(s)for(const oe in s)is(s[oe],r,n,oe);if(c){const oe=re(c)?c.call(n):c;Reflect.ownKeys(oe).forEach(W=>{ct(W,oe[W])})}d&&gi(d,e,"c");function Q(oe,W){ee(W)?W.forEach(at=>oe(at.bind(n))):W&&oe(W.bind(n))}if(Q(Y0,p),Q(ye,v),Q(X0,h),Q(as,b),Q(J0,A),Q(Q0,I),Q(n1,H),Q(t1,w),Q(e1,U),Q(wa,k),Q(ur,P),Q(Z0,J),ee(x))if(x.length){const oe=e.exposed||(e.exposed={});x.forEach(W=>{Object.defineProperty(oe,W,{get:()=>n[W],set:at=>n[W]=at})})}else e.exposed||(e.exposed={});$&&e.render===Je&&(e.render=$),X!=null&&(e.inheritAttrs=X),M&&(e.components=M),te&&(e.directives=te)}function a1(e,t,n=Je){ee(e)&&(e=Go(e));for(const r in e){const o=e[r];let a;ke(o)?"default"in o?a=he(o.from||r,o.default,!0):a=he(o.from||r):a=he(o),Ve(a)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>a.value,set:i=>a.value=i}):t[r]=a}}function gi(e,t,n){tt(ee(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function is(e,t,n,r){const o=r.includes(".")?es(n,r):()=>n[r];if(pe(e)){const a=t[e];re(a)&&ve(o,a)}else if(re(e))ve(o,e.bind(n));else if(ke(e))if(ee(e))e.forEach(a=>is(a,t,n,r));else{const a=re(e.handler)?e.handler.bind(n):t[e.handler];re(a)&&ve(o,a,e)}}function ka(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:a,config:{optionMergeStrategies:i}}=e.appContext,s=a.get(t);let c;return s?c=s:!o.length&&!n&&!r?c=t:(c={},o.length&&o.forEach(u=>Wr(c,u,i,!0)),Wr(c,t,i)),ke(t)&&a.set(t,c),c}function Wr(e,t,n,r=!1){const{mixins:o,extends:a}=t;a&&Wr(e,a,n,!0),o&&o.forEach(i=>Wr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const s=i1[i]||n&&n[i];e[i]=s?s(e[i],t[i]):t[i]}return e}const i1={data:_i,props:bi,emits:bi,methods:$n,computed:$n,beforeCreate:He,created:He,beforeMount:He,mounted:He,beforeUpdate:He,updated:He,beforeDestroy:He,beforeUnmount:He,destroyed:He,unmounted:He,activated:He,deactivated:He,errorCaptured:He,serverPrefetch:He,components:$n,directives:$n,watch:s1,provide:_i,inject:l1};function _i(e,t){return t?e?function(){return De(re(e)?e.call(this,this):e,re(t)?t.call(this,this):t)}:t:e}function l1(e,t){return $n(Go(e),Go(t))}function Go(e){if(ee(e)){const t={};for(let n=0;n1)return n&&re(t)?t.call(r&&r.proxy):t}}function d1(e,t,n,r=!1){const o={},a={};jr(a,lo,1),e.propsDefaults=Object.create(null),ss(e,t,o,a);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:Hl(o):e.type.props?e.props=o:e.props=a,e.attrs=a}function f1(e,t,n,r){const{props:o,attrs:a,vnode:{patchFlag:i}}=e,s=ce(o),[c]=e.propsOptions;let u=!1;if((r||i>0)&&!(i&16)){if(i&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[v,h]=cs(p,t,!0);De(i,v),h&&s.push(...h)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!a&&!c)return ke(e)&&r.set(e,vn),vn;if(ee(a))for(let d=0;d-1,h[1]=A<0||b-1||fe(h,"default"))&&s.push(p)}}}const u=[i,s];return ke(e)&&r.set(e,u),u}function yi(e){return e[0]!=="$"&&!hn(e)}function Ei(e){return e===null?"null":typeof e=="function"?e.name||"":typeof e=="object"&&e.constructor&&e.constructor.name||""}function Li(e,t){return Ei(e)===Ei(t)}function Ti(e,t){return ee(t)?t.findIndex(n=>Li(n,e)):re(t)&&Li(t,e)?0:-1}const us=e=>e[0]==="_"||e==="$stable",Pa=e=>ee(e)?e.map(lt):[lt(e)],p1=(e,t,n)=>{if(t._n)return t;const r=zo((...o)=>Pa(t(...o)),n);return r._c=!1,r},ds=(e,t,n)=>{const r=e._ctx;for(const o in e){if(us(o))continue;const a=e[o];if(re(a))t[o]=p1(o,a,r);else if(a!=null){const i=Pa(a);t[o]=()=>i}}},fs=(e,t)=>{const n=Pa(t);e.slots.default=()=>n},v1=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=ce(t),jr(t,"_",n)):ds(t,e.slots={})}else e.slots={},t&&fs(e,t);jr(e.slots,lo,1)},h1=(e,t,n)=>{const{vnode:r,slots:o}=e;let a=!0,i=Ae;if(r.shapeFlag&32){const s=t._;s?n&&s===1?a=!1:(De(o,t),!n&&s===1&&delete o._):(a=!t.$stable,ds(t,o)),i=t}else t&&(fs(e,t),i={default:1});if(a)for(const s in o)!us(s)&&i[s]==null&&delete o[s]};function Jr(e,t,n,r,o=!1){if(ee(e)){e.forEach((v,h)=>Jr(v,t&&(ee(t)?t[h]:t),n,r,o));return}if(Fn(r)&&!o)return;const a=r.shapeFlag&4?Ra(r.component)||r.component.proxy:r.el,i=o?null:a,{i:s,r:c}=e,u=t&&t.r,d=s.refs===Ae?s.refs={}:s.refs,p=s.setupState;if(u!=null&&u!==c&&(pe(u)?(d[u]=null,fe(p,u)&&(p[u]=null)):Ve(u)&&(u.value=null)),re(c))jt(c,s,12,[i,d]);else{const v=pe(c),h=Ve(c);if(v||h){const b=()=>{if(e.f){const A=v?fe(p,c)?p[c]:d[c]:c.value;o?ee(A)&&fa(A,a):ee(A)?A.includes(a)||A.push(a):v?(d[c]=[a],fe(p,c)&&(p[c]=d[c])):(c.value=[a],e.k&&(d[e.k]=c.value))}else v?(d[c]=i,fe(p,c)&&(p[c]=i)):h&&(c.value=i,e.k&&(d[e.k]=i))};i?(b.id=-1,je(b,n)):b()}}}let St=!1;const m1=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",g1=e=>e.namespaceURI.includes("MathML"),xr=e=>{if(m1(e))return"svg";if(g1(e))return"mathml"},Cr=e=>e.nodeType===8;function _1(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:a,parentNode:i,remove:s,insert:c,createComment:u}}=e,d=(y,P)=>{if(!P.hasChildNodes()){n(null,y,P),Ur(),P._vnode=y;return}St=!1,p(P.firstChild,y,null,null,null),Ur(),P._vnode=y,St&&console.error("Hydration completed but contains mismatches.")},p=(y,P,$,w,U,H=!1)=>{const J=Cr(y)&&y.data==="[",x=()=>A(y,P,$,w,U,J),{type:X,ref:M,shapeFlag:te,patchFlag:Pe}=P;let Oe=y.nodeType;P.el=y,Pe===-2&&(H=!1,P.dynamicChildren=null);let Q=null;switch(X){case En:Oe!==3?P.children===""?(c(P.el=o(""),i(y),y),Q=y):Q=x():(y.data!==P.children&&(St=!0,y.data=P.children),Q=a(y));break;case nt:k(y)?(Q=a(y),E(P.el=y.content.firstChild,y,$)):Oe!==8||J?Q=x():Q=a(y);break;case qn:if(J&&(y=a(y),Oe=y.nodeType),Oe===1||Oe===3){Q=y;const oe=!P.children.length;for(let W=0;W{H=H||!!P.dynamicChildren;const{type:J,props:x,patchFlag:X,shapeFlag:M,dirs:te,transition:Pe}=P,Oe=J==="input"||J==="option";if(Oe||X!==-1){te&&mt(P,null,$,"created");let Q=!1;if(k(y)){Q=ps(w,Pe)&&$&&$.vnode.props&&$.vnode.props.appear;const W=y.content.firstChild;Q&&Pe.beforeEnter(W),E(W,y,$),P.el=y=W}if(M&16&&!(x&&(x.innerHTML||x.textContent))){let W=h(y.firstChild,P,y,$,w,U,H);for(;W;){St=!0;const at=W;W=W.nextSibling,s(at)}}else M&8&&y.textContent!==P.children&&(St=!0,y.textContent=P.children);if(x)if(Oe||!H||X&48)for(const W in x)(Oe&&(W.endsWith("value")||W==="indeterminate")||ar(W)&&!hn(W)||W[0]===".")&&r(y,W,null,x[W],void 0,void 0,$);else x.onClick&&r(y,"onClick",null,x.onClick,void 0,void 0,$);let oe;(oe=x&&x.onVnodeBeforeMount)&&Ze(oe,$,P),te&&mt(P,null,$,"beforeMount"),((oe=x&&x.onVnodeMounted)||te||Q)&&Zl(()=>{oe&&Ze(oe,$,P),Q&&Pe.enter(y),te&&mt(P,null,$,"mounted")},w)}return y.nextSibling},h=(y,P,$,w,U,H,J)=>{J=J||!!P.dynamicChildren;const x=P.children,X=x.length;for(let M=0;M{const{slotScopeIds:J}=P;J&&(U=U?U.concat(J):J);const x=i(y),X=h(a(y),P,x,$,w,U,H);return X&&Cr(X)&&X.data==="]"?a(P.anchor=X):(St=!0,c(P.anchor=u("]"),x,X),X)},A=(y,P,$,w,U,H)=>{if(St=!0,P.el=null,H){const X=I(y);for(;;){const M=a(y);if(M&&M!==X)s(M);else break}}const J=a(y),x=i(y);return s(y),n(null,P,x,J,$,w,xr(x),U),J},I=(y,P="[",$="]")=>{let w=0;for(;y;)if(y=a(y),y&&Cr(y)&&(y.data===P&&w++,y.data===$)){if(w===0)return a(y);w--}return y},E=(y,P,$)=>{const w=P.parentNode;w&&w.replaceChild(y,P);let U=$;for(;U;)U.vnode.el===P&&(U.vnode.el=U.subTree.el=y),U=U.parent},k=y=>y.nodeType===1&&y.tagName.toLowerCase()==="template";return[d,p]}const je=Zl;function b1(e){return y1(e,_1)}function y1(e,t){const n=wl();n.__VUE__=!0;const{insert:r,remove:o,patchProp:a,createElement:i,createText:s,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:v,setScopeId:h=Je,insertStaticContent:b}=e,A=(m,g,T,D=null,O=null,V=null,j=void 0,C=null,N=!!g.dynamicChildren)=>{if(m===g)return;m&&!Yt(m,g)&&(D=R(m),Fe(m,O,V,!0),m=null),g.patchFlag===-2&&(N=!1,g.dynamicChildren=null);const{type:S,ref:q,shapeFlag:Z}=g;switch(S){case En:I(m,g,T,D);break;case nt:E(m,g,T,D);break;case qn:m==null&&k(g,T,D,j);break;case We:M(m,g,T,D,O,V,j,C,N);break;default:Z&1?$(m,g,T,D,O,V,j,C,N):Z&6?te(m,g,T,D,O,V,j,C,N):(Z&64||Z&128)&&S.process(m,g,T,D,O,V,j,C,N,G)}q!=null&&O&&Jr(q,m&&m.ref,V,g||m,!g)},I=(m,g,T,D)=>{if(m==null)r(g.el=s(g.children),T,D);else{const O=g.el=m.el;g.children!==m.children&&u(O,g.children)}},E=(m,g,T,D)=>{m==null?r(g.el=c(g.children||""),T,D):g.el=m.el},k=(m,g,T,D)=>{[m.el,m.anchor]=b(m.children,g,T,D,m.el,m.anchor)},y=({el:m,anchor:g},T,D)=>{let O;for(;m&&m!==g;)O=v(m),r(m,T,D),m=O;r(g,T,D)},P=({el:m,anchor:g})=>{let T;for(;m&&m!==g;)T=v(m),o(m),m=T;o(g)},$=(m,g,T,D,O,V,j,C,N)=>{g.type==="svg"?j="svg":g.type==="math"&&(j="mathml"),m==null?w(g,T,D,O,V,j,C,N):J(m,g,O,V,j,C,N)},w=(m,g,T,D,O,V,j,C)=>{let N,S;const{props:q,shapeFlag:Z,transition:K,dirs:ne}=m;if(N=m.el=i(m.type,V,q&&q.is,q),Z&8?d(N,m.children):Z&16&&H(m.children,N,null,D,O,Io(m,V),j,C),ne&&mt(m,null,D,"created"),U(N,m,m.scopeId,j,D),q){for(const be in q)be!=="value"&&!hn(be)&&a(N,be,null,q[be],V,m.children,D,O,Me);"value"in q&&a(N,"value",null,q.value,V),(S=q.onVnodeBeforeMount)&&Ze(S,D,m)}ne&&mt(m,null,D,"beforeMount");const ae=ps(O,K);ae&&K.beforeEnter(N),r(N,g,T),((S=q&&q.onVnodeMounted)||ae||ne)&&je(()=>{S&&Ze(S,D,m),ae&&K.enter(N),ne&&mt(m,null,D,"mounted")},O)},U=(m,g,T,D,O)=>{if(T&&h(m,T),D)for(let V=0;V{for(let S=N;S{const C=g.el=m.el;let{patchFlag:N,dynamicChildren:S,dirs:q}=g;N|=m.patchFlag&16;const Z=m.props||Ae,K=g.props||Ae;let ne;if(T&&Jt(T,!1),(ne=K.onVnodeBeforeUpdate)&&Ze(ne,T,g,m),q&&mt(g,m,T,"beforeUpdate"),T&&Jt(T,!0),S?x(m.dynamicChildren,S,C,T,D,Io(g,O),V):j||W(m,g,C,null,T,D,Io(g,O),V,!1),N>0){if(N&16)X(C,g,Z,K,T,D,O);else if(N&2&&Z.class!==K.class&&a(C,"class",null,K.class,O),N&4&&a(C,"style",Z.style,K.style,O),N&8){const ae=g.dynamicProps;for(let be=0;be{ne&&Ze(ne,T,g,m),q&&mt(g,m,T,"updated")},D)},x=(m,g,T,D,O,V,j)=>{for(let C=0;C{if(T!==D){if(T!==Ae)for(const C in T)!hn(C)&&!(C in D)&&a(m,C,T[C],null,j,g.children,O,V,Me);for(const C in D){if(hn(C))continue;const N=D[C],S=T[C];N!==S&&C!=="value"&&a(m,C,S,N,j,g.children,O,V,Me)}"value"in D&&a(m,"value",T.value,D.value,j)}},M=(m,g,T,D,O,V,j,C,N)=>{const S=g.el=m?m.el:s(""),q=g.anchor=m?m.anchor:s("");let{patchFlag:Z,dynamicChildren:K,slotScopeIds:ne}=g;ne&&(C=C?C.concat(ne):ne),m==null?(r(S,T,D),r(q,T,D),H(g.children||[],T,q,O,V,j,C,N)):Z>0&&Z&64&&K&&m.dynamicChildren?(x(m.dynamicChildren,K,T,O,V,j,C),(g.key!=null||O&&g===O.subTree)&&vs(m,g,!0)):W(m,g,T,q,O,V,j,C,N)},te=(m,g,T,D,O,V,j,C,N)=>{g.slotScopeIds=C,m==null?g.shapeFlag&512?O.ctx.activate(g,T,D,j,N):Pe(g,T,D,O,V,j,N):Oe(m,g,N)},Pe=(m,g,T,D,O,V,j)=>{const C=m.component=P1(m,D,O);if(cr(m)&&(C.ctx.renderer=G),O1(C),C.asyncDep){if(O&&O.registerDep(C,Q),!m.el){const N=C.subTree=we(nt);E(null,N,g,T)}}else Q(C,m,g,T,O,V,j)},Oe=(m,g,T)=>{const D=g.component=m.component;if($0(m,g,T))if(D.asyncDep&&!D.asyncResolved){oe(D,g,T);return}else D.next=g,S0(D.update),D.effect.dirty=!0,D.update();else g.el=m.el,D.vnode=g},Q=(m,g,T,D,O,V,j)=>{const C=()=>{if(m.isMounted){let{next:q,bu:Z,u:K,parent:ne,vnode:ae}=m;{const cn=hs(m);if(cn){q&&(q.el=ae.el,oe(m,q,j)),cn.asyncDep.then(()=>{m.isUnmounted||C()});return}}let be=q,Te;Jt(m,!1),q?(q.el=ae.el,oe(m,q,j)):q=ae,Z&&yo(Z),(Te=q.props&&q.props.onVnodeBeforeUpdate)&&Ze(Te,ne,q,ae),Jt(m,!0);const xe=Eo(m),it=m.subTree;m.subTree=xe,A(it,xe,p(it.el),R(it),m,O,V),q.el=xe.el,be===null&&N0(m,xe.el),K&&je(K,O),(Te=q.props&&q.props.onVnodeUpdated)&&je(()=>Ze(Te,ne,q,ae),O)}else{let q;const{el:Z,props:K}=g,{bm:ne,m:ae,parent:be}=m,Te=Fn(g);if(Jt(m,!1),ne&&yo(ne),!Te&&(q=K&&K.onVnodeBeforeMount)&&Ze(q,be,g),Jt(m,!0),Z&&Le){const xe=()=>{m.subTree=Eo(m),Le(Z,m.subTree,m,O,null)};Te?g.type.__asyncLoader().then(()=>!m.isUnmounted&&xe()):xe()}else{const xe=m.subTree=Eo(m);A(null,xe,T,D,m,O,V),g.el=xe.el}if(ae&&je(ae,O),!Te&&(q=K&&K.onVnodeMounted)){const xe=g;je(()=>Ze(q,be,xe),O)}(g.shapeFlag&256||be&&Fn(be.vnode)&&be.vnode.shapeFlag&256)&&m.a&&je(m.a,O),m.isMounted=!0,g=T=D=null}},N=m.effect=new va(C,Je,()=>oo(S),m.scope),S=m.update=()=>{N.dirty&&N.run()};S.id=m.uid,Jt(m,!0),S()},oe=(m,g,T)=>{g.component=m;const D=m.vnode.props;m.vnode=g,m.next=null,f1(m,g.props,D,T),h1(m,g.children,T),tn(),fi(m),nn()},W=(m,g,T,D,O,V,j,C,N=!1)=>{const S=m&&m.children,q=m?m.shapeFlag:0,Z=g.children,{patchFlag:K,shapeFlag:ne}=g;if(K>0){if(K&128){Rt(S,Z,T,D,O,V,j,C,N);return}else if(K&256){at(S,Z,T,D,O,V,j,C,N);return}}ne&8?(q&16&&Me(S,O,V),Z!==S&&d(T,Z)):q&16?ne&16?Rt(S,Z,T,D,O,V,j,C,N):Me(S,O,V,!0):(q&8&&d(T,""),ne&16&&H(Z,T,D,O,V,j,C,N))},at=(m,g,T,D,O,V,j,C,N)=>{m=m||vn,g=g||vn;const S=m.length,q=g.length,Z=Math.min(S,q);let K;for(K=0;Kq?Me(m,O,V,!0,!1,Z):H(g,T,D,O,V,j,C,N,Z)},Rt=(m,g,T,D,O,V,j,C,N)=>{let S=0;const q=g.length;let Z=m.length-1,K=q-1;for(;S<=Z&&S<=K;){const ne=m[S],ae=g[S]=N?$t(g[S]):lt(g[S]);if(Yt(ne,ae))A(ne,ae,T,null,O,V,j,C,N);else break;S++}for(;S<=Z&&S<=K;){const ne=m[Z],ae=g[K]=N?$t(g[K]):lt(g[K]);if(Yt(ne,ae))A(ne,ae,T,null,O,V,j,C,N);else break;Z--,K--}if(S>Z){if(S<=K){const ne=K+1,ae=neK)for(;S<=Z;)Fe(m[S],O,V,!0),S++;else{const ne=S,ae=S,be=new Map;for(S=ae;S<=K;S++){const Ue=g[S]=N?$t(g[S]):lt(g[S]);Ue.key!=null&&be.set(Ue.key,S)}let Te,xe=0;const it=K-ae+1;let cn=!1,ei=0;const Cn=new Array(it);for(S=0;S=it){Fe(Ue,O,V,!0);continue}let ht;if(Ue.key!=null)ht=be.get(Ue.key);else for(Te=ae;Te<=K;Te++)if(Cn[Te-ae]===0&&Yt(Ue,g[Te])){ht=Te;break}ht===void 0?Fe(Ue,O,V,!0):(Cn[ht-ae]=S+1,ht>=ei?ei=ht:cn=!0,A(Ue,g[ht],T,null,O,V,j,C,N),xe++)}const ti=cn?E1(Cn):vn;for(Te=ti.length-1,S=it-1;S>=0;S--){const Ue=ae+S,ht=g[Ue],ni=Ue+1{const{el:V,type:j,transition:C,children:N,shapeFlag:S}=m;if(S&6){vt(m.component.subTree,g,T,D);return}if(S&128){m.suspense.move(g,T,D);return}if(S&64){j.move(m,g,T,G);return}if(j===We){r(V,g,T);for(let Z=0;ZC.enter(V),O);else{const{leave:Z,delayLeave:K,afterLeave:ne}=C,ae=()=>r(V,g,T),be=()=>{Z(V,()=>{ae(),ne&&ne()})};K?K(V,ae,be):be()}else r(V,g,T)},Fe=(m,g,T,D=!1,O=!1)=>{const{type:V,props:j,ref:C,children:N,dynamicChildren:S,shapeFlag:q,patchFlag:Z,dirs:K}=m;if(C!=null&&Jr(C,null,T,m,!0),q&256){g.ctx.deactivate(m);return}const ne=q&1&&K,ae=!Fn(m);let be;if(ae&&(be=j&&j.onVnodeBeforeUnmount)&&Ze(be,g,m),q&6)Ir(m.component,T,D);else{if(q&128){m.suspense.unmount(T,D);return}ne&&mt(m,null,g,"beforeUnmount"),q&64?m.type.remove(m,g,T,O,G,D):S&&(V!==We||Z>0&&Z&64)?Me(S,g,T,!1,!0):(V===We&&Z&384||!O&&q&16)&&Me(N,g,T),D&&ln(m)}(ae&&(be=j&&j.onVnodeUnmounted)||ne)&&je(()=>{be&&Ze(be,g,m),ne&&mt(m,null,g,"unmounted")},T)},ln=m=>{const{type:g,el:T,anchor:D,transition:O}=m;if(g===We){sn(T,D);return}if(g===qn){P(m);return}const V=()=>{o(T),O&&!O.persisted&&O.afterLeave&&O.afterLeave()};if(m.shapeFlag&1&&O&&!O.persisted){const{leave:j,delayLeave:C}=O,N=()=>j(T,V);C?C(m.el,V,N):N()}else V()},sn=(m,g)=>{let T;for(;m!==g;)T=v(m),o(m),m=T;o(g)},Ir=(m,g,T)=>{const{bum:D,scope:O,update:V,subTree:j,um:C}=m;D&&yo(D),O.stop(),V&&(V.active=!1,Fe(j,m,g,T)),C&&je(C,g),je(()=>{m.isUnmounted=!0},g),g&&g.pendingBranch&&!g.isUnmounted&&m.asyncDep&&!m.asyncResolved&&m.suspenseId===g.pendingId&&(g.deps--,g.deps===0&&g.resolve())},Me=(m,g,T,D=!1,O=!1,V=0)=>{for(let j=V;jm.shapeFlag&6?R(m.component.subTree):m.shapeFlag&128?m.suspense.next():v(m.anchor||m.el);let z=!1;const F=(m,g,T)=>{m==null?g._vnode&&Fe(g._vnode,null,null,!0):A(g._vnode||null,m,g,null,null,null,T),z||(z=!0,fi(),Ur(),z=!1),g._vnode=m},G={p:A,um:Fe,m:vt,r:ln,mt:Pe,mc:H,pc:W,pbc:x,n:R,o:e};let me,Le;return t&&([me,Le]=t(G)),{render:F,hydrate:me,createApp:u1(F,me)}}function Io({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function Jt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function ps(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function vs(e,t,n=!1){const r=e.children,o=t.children;if(ee(r)&&ee(o))for(let a=0;a>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,i=n[a-1];a-- >0;)n[a]=i,i=t[i];return n}function hs(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:hs(t)}const L1=e=>e.__isTeleport,We=Symbol.for("v-fgt"),En=Symbol.for("v-txt"),nt=Symbol.for("v-cmt"),qn=Symbol.for("v-stc"),Un=[];let st=null;function ms(e=!1){Un.push(st=e?null:[])}function T1(){Un.pop(),st=Un[Un.length-1]||null}let Zn=1;function Ai(e){Zn+=e}function gs(e){return e.dynamicChildren=Zn>0?st||vn:null,T1(),Zn>0&&st&&st.push(e),e}function R3(e,t,n,r,o,a){return gs(ys(e,t,n,r,o,a,!0))}function _s(e,t,n,r,o){return gs(we(e,t,n,r,o,!0))}function Jo(e){return e?e.__v_isVNode===!0:!1}function Yt(e,t){return e.type===t.type&&e.key===t.key}const lo="__vInternal",bs=({key:e})=>e??null,Hr=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?pe(e)||Ve(e)||re(e)?{i:et,r:e,k:t,f:!!n}:e:null);function ys(e,t=null,n=null,r=0,o=null,a=e===We?0:1,i=!1,s=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&bs(t),ref:t&&Hr(t),scopeId:Yl,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:et};return s?(Oa(c,n),a&128&&e.normalize(c)):n&&(c.shapeFlag|=pe(n)?8:16),Zn>0&&!i&&st&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&st.push(c),c}const we=A1;function A1(e,t=null,n=null,r=0,o=null,a=!1){if((!e||e===H0)&&(e=nt),Jo(e)){const s=qt(e,t,!0);return n&&Oa(s,n),Zn>0&&!a&&st&&(s.shapeFlag&6?st[st.indexOf(e)]=s:st.push(s)),s.patchFlag|=-2,s}if(C1(e)&&(e=e.__vccOpts),t){t=Es(t);let{class:s,style:c}=t;s&&!pe(s)&&(t.class=no(s)),ke(c)&&(Fl(c)&&!ee(c)&&(c=De({},c)),t.style=to(c))}const i=pe(e)?1:j0(e)?128:L1(e)?64:ke(e)?4:re(e)?2:0;return ys(e,t,n,r,o,i,a,!0)}function Es(e){return e?Fl(e)||lo in e?De({},e):e:null}function qt(e,t,n=!1){const{props:r,ref:o,patchFlag:a,children:i}=e,s=t?I1(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:s,key:s&&bs(s),ref:t&&t.ref?n&&o?ee(o)?o.concat(Hr(t)):[o,Hr(t)]:Hr(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==We?a===-1?16:a|16:a,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&qt(e.ssContent),ssFallback:e.ssFallback&&qt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Ls(e=" ",t=0){return we(En,null,e,t)}function D3(e,t){const n=we(qn,null,e);return n.staticCount=t,n}function S3(e="",t=!1){return t?(ms(),_s(nt,null,e)):we(nt,null,e)}function lt(e){return e==null||typeof e=="boolean"?we(nt):ee(e)?we(We,null,e.slice()):typeof e=="object"?$t(e):we(En,null,String(e))}function $t(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:qt(e)}function Oa(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ee(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),Oa(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!(lo in t)?t._ctx=et:o===3&&et&&(et.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else re(t)?(t={default:t,_ctx:et},n=32):(t=String(t),r&64?(n=16,t=[Ls(t)]):n=8);e.children=t,e.shapeFlag|=n}function I1(...e){const t={};for(let n=0;nCe||et;let Qr,Qo;{const e=wl(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),a=>{o.length>1?o.forEach(i=>i(a)):o[0](a)}};Qr=t("__VUE_INSTANCE_SETTERS__",n=>Ce=n),Qo=t("__VUE_SSR_SETTERS__",n=>fr=n)}const dr=e=>{const t=Ce;return Qr(e),e.scope.on(),()=>{e.scope.off(),Qr(t)}},Ii=()=>{Ce&&Ce.scope.off(),Qr(null)};function Ts(e){return e.vnode.shapeFlag&4}let fr=!1;function O1(e,t=!1){t&&Qo(t);const{props:n,children:r}=e.vnode,o=Ts(e);d1(e,n,o,t),v1(e,r);const a=o?R1(e,t):void 0;return t&&Qo(!1),a}function R1(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=jl(new Proxy(e.ctx,r1));const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?S1(e):null,a=dr(e);tn();const i=jt(r,e,0,[e.props,o]);if(nn(),a(),Il(i)){if(i.then(Ii,Ii),t)return i.then(s=>{wi(e,s,t)}).catch(s=>{sr(s,e,0)});e.asyncDep=i}else wi(e,i,t)}else As(e,t)}function wi(e,t,n){re(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ke(t)&&(e.setupState=Ul(t)),As(e,n)}let ki;function As(e,t,n){const r=e.type;if(!e.render){if(!t&&ki&&!r.render){const o=r.template||ka(e).template;if(o){const{isCustomElement:a,compilerOptions:i}=e.appContext.config,{delimiters:s,compilerOptions:c}=r,u=De(De({isCustomElement:a,delimiters:s},i),c);r.render=ki(o,u)}}e.render=r.render||Je}{const o=dr(e);tn();try{o1(e)}finally{nn(),o()}}}function D1(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return qe(e,"get","$attrs"),t[n]}}))}function S1(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return D1(e)},slots:e.slots,emit:e.emit,expose:t}}function Ra(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Ul(jl(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in jn)return jn[n](e)},has(t,n){return n in t||n in jn}}))}function x1(e,t=!0){return re(e)?e.displayName||e.name:e.name||t&&e.__name}function C1(e){return re(e)&&"__vccOpts"in e}const L=(e,t)=>T0(e,t,fr);function l(e,t,n){const r=arguments.length;return r===2?ke(t)&&!ee(t)?Jo(t)?we(e,null,[t]):we(e,t):we(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Jo(n)&&(n=[n]),we(e,t,n))}const V1="3.4.21";/** +**/function jt(e,t,n,r){try{return r?e(...r):e()}catch(o){sr(o,t,n)}}function tt(e,t,n,r){if(re(e)){const a=jt(e,t,n,r);return a&&Il(a)&&a.catch(i=>{sr(i,t,n)}),a}const o=[];for(let a=0;a>>1,o=Be[r],a=Kn(o);agt&&Be.splice(t,1)}function x0(e){ee(e)?gn.push(...e):(!Mt||!Mt.includes(e,e.allowRecurse?Kt+1:Kt))&&gn.push(e),Jl()}function fi(e,t,n=Qn?gt+1:0){for(;nKn(n)-Kn(r));if(gn.length=0,Mt){Mt.push(...t);return}for(Mt=t,Kt=0;Kte.id==null?1/0:e.id,C0=(e,t)=>{const n=Kn(e)-Kn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ql(e){jo=!1,Qn=!0,Be.sort(C0);const t=Je;try{for(gt=0;gtpe(h)?h.trim():h)),p&&(o=n.map(Uu))}let s,c=r[s=bo(t)]||r[s=bo(ot(t))];!c&&a&&(c=r[s=bo(kn(t))]),c&&tt(c,e,6,o);const u=r[s+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[s])return;e.emitted[s]=!0,tt(u,e,6,o)}}function Kl(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const a=e.emits;let i={},s=!1;if(!re(e)){const c=u=>{const d=Kl(u,t,!0);d&&(s=!0,De(i,d))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!a&&!s?(ke(e)&&r.set(e,null),null):(ee(a)?a.forEach(c=>i[c]=null):De(i,a),ke(e)&&r.set(e,i),i)}function ao(e,t){return!e||!ar(t)?!1:(t=t.slice(2).replace(/Once$/,""),fe(e,t[0].toLowerCase()+t.slice(1))||fe(e,kn(t))||fe(e,t))}let et=null,Yl=null;function Gr(e){const t=et;return et=e,Yl=e&&e.type.__scopeId||null,t}function zo(e,t=et,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Ti(-1);const a=Gr(t);let i;try{i=e(...o)}finally{Gr(a),r._d&&Ti(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Eo(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:a,propsOptions:[i],slots:s,attrs:c,emit:u,render:d,renderCache:p,data:v,setupState:h,ctx:b,inheritAttrs:T}=e;let I,E;const k=Gr(e);try{if(n.shapeFlag&4){const P=o||r,$=P;I=lt(d.call($,P,p,a,h,v,b)),E=c}else{const P=t;I=lt(P.length>1?P(a,{attrs:c,slots:s,emit:u}):P(a,null)),E=t.props?c:M0(c)}}catch(P){Un.length=0,sr(P,e,1),I=we(nt)}let y=I;if(E&&T!==!1){const P=Object.keys(E),{shapeFlag:$}=y;P.length&&$&7&&(i&&P.some(da)&&(E=B0(E,i)),y=qt(y,E))}return n.dirs&&(y=qt(y),y.dirs=y.dirs?y.dirs.concat(n.dirs):n.dirs),n.transition&&(y.transition=n.transition),I=y,Gr(k),I}const M0=e=>{let t;for(const n in e)(n==="class"||n==="style"||ar(n))&&((t||(t={}))[n]=e[n]);return t},B0=(e,t)=>{const n={};for(const r in e)(!da(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function $0(e,t,n){const{props:r,children:o,component:a}=e,{props:i,children:s,patchFlag:c}=t,u=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?pi(r,i,u):!!i;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Zl(e,t){t&&t.pendingBranch?ee(e)?t.effects.push(...e):t.effects.push(e):x0(e)}const z0=Symbol.for("v-scx"),q0=()=>he(z0);function Aa(e,t){return Ta(e,null,t)}const Dr={};function ve(e,t,n){return Ta(e,t,n)}function Ta(e,t,{immediate:n,deep:r,flush:o,once:a,onTrack:i,onTrigger:s}=Te){if(t&&a){const w=t;t=(...U)=>{w(...U),$()}}const c=Ce,u=w=>r===!0?w:fn(w,r===!1?1:void 0);let d,p=!1,v=!1;if(Ve(e)?(d=()=>e.value,p=qr(e)):mn(e)?(d=()=>u(e),p=!0):ee(e)?(v=!0,p=e.some(w=>mn(w)||qr(w)),d=()=>e.map(w=>{if(Ve(w))return w.value;if(mn(w))return u(w);if(re(w))return jt(w,c,2)})):re(e)?t?d=()=>jt(e,c,2):d=()=>(h&&h(),tt(e,c,3,[b])):d=Je,t&&r){const w=d;d=()=>fn(w())}let h,b=w=>{h=y.onStop=()=>{jt(w,c,4),h=y.onStop=void 0}},T;if(fr)if(b=Je,t?n&&tt(t,c,3,[d(),v?[]:void 0,b]):d(),o==="sync"){const w=q0();T=w.__watcherHandles||(w.__watcherHandles=[])}else return Je;let I=v?new Array(e.length).fill(Dr):Dr;const E=()=>{if(!(!y.active||!y.dirty))if(t){const w=y.run();(r||p||(v?w.some((U,H)=>zt(U,I[H])):zt(w,I)))&&(h&&h(),tt(t,c,3,[w,I===Dr?void 0:v&&I[0]===Dr?[]:I,b]),I=w)}else y.run()};E.allowRecurse=!!t;let k;o==="sync"?k=E:o==="post"?k=()=>je(E,c&&c.suspense):(E.pre=!0,c&&(E.id=c.uid),k=()=>oo(E));const y=new va(d,Je,k),P=Pl(),$=()=>{y.stop(),P&&fa(P.effects,y)};return t?n?E():I=y.run():o==="post"?je(y.run.bind(y),c&&c.suspense):y.run(),T&&T.push($),$}function U0(e,t,n){const r=this.proxy,o=pe(e)?e.includes(".")?es(r,e):()=>r[e]:e.bind(r,r);let a;re(t)?a=t:(a=t.handler,n=t);const i=dr(this),s=Ta(o,a.bind(r),n);return i(),s}function es(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o0){if(n>=t)return e;n++}if(r=r||new Set,r.has(e))return e;if(r.add(e),Ve(e))fn(e.value,t,n,r);else if(ee(e))for(let o=0;o{fn(o,t,n,r)});else if(ju(e))for(const o in e)fn(e[o],t,n,r);return e}function mt(e,t,n,r){const o=e.dirs,a=t&&t.dirs;for(let i=0;i{e.isMounted=!0}),wa(()=>{e.isUnmounting=!0}),e}const Xe=[Function,Array],ns={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Xe,onEnter:Xe,onAfterEnter:Xe,onEnterCancelled:Xe,onBeforeLeave:Xe,onLeave:Xe,onAfterLeave:Xe,onLeaveCancelled:Xe,onBeforeAppear:Xe,onAppear:Xe,onAfterAppear:Xe,onAppearCancelled:Xe},G0={name:"BaseTransition",props:ns,setup(e,{slots:t}){const n=On(),r=ts();return()=>{const o=t.default&&Ia(t.default(),!0);if(!o||!o.length)return;let a=o[0];if(o.length>1){for(const v of o)if(v.type!==nt){a=v;break}}const i=ce(e),{mode:s}=i;if(r.isLeaving)return Lo(a);const c=hi(a);if(!c)return Lo(a);const u=Yn(c,i,r,n);Xn(c,u);const d=n.subTree,p=d&&hi(d);if(p&&p.type!==nt&&!Yt(c,p)){const v=Yn(p,i,r,n);if(Xn(p,v),s==="out-in")return r.isLeaving=!0,v.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},Lo(a);s==="in-out"&&c.type!==nt&&(v.delayLeave=(h,b,T)=>{const I=rs(r,p);I[String(p.key)]=p,h[Bt]=()=>{b(),h[Bt]=void 0,delete u.delayedLeave},u.delayedLeave=T})}return a}}},W0=G0;function rs(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function Yn(e,t,n,r){const{appear:o,mode:a,persisted:i=!1,onBeforeEnter:s,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:v,onAfterLeave:h,onLeaveCancelled:b,onBeforeAppear:T,onAppear:I,onAfterAppear:E,onAppearCancelled:k}=t,y=String(e.key),P=rs(n,e),$=(H,J)=>{H&&tt(H,r,9,J)},w=(H,J)=>{const x=J[1];$(H,J),ee(H)?H.every(X=>X.length<=1)&&x():H.length<=1&&x()},U={mode:a,persisted:i,beforeEnter(H){let J=s;if(!n.isMounted)if(o)J=T||s;else return;H[Bt]&&H[Bt](!0);const x=P[y];x&&Yt(e,x)&&x.el[Bt]&&x.el[Bt](),$(J,[H])},enter(H){let J=c,x=u,X=d;if(!n.isMounted)if(o)J=I||c,x=E||u,X=k||d;else return;let M=!1;const te=H[Sr]=Pe=>{M||(M=!0,Pe?$(X,[H]):$(x,[H]),U.delayedLeave&&U.delayedLeave(),H[Sr]=void 0)};J?w(J,[H,te]):te()},leave(H,J){const x=String(e.key);if(H[Sr]&&H[Sr](!0),n.isUnmounting)return J();$(p,[H]);let X=!1;const M=H[Bt]=te=>{X||(X=!0,J(),te?$(b,[H]):$(h,[H]),H[Bt]=void 0,P[x]===e&&delete P[x])};P[x]=e,v?w(v,[H,M]):M()},clone(H){return Yn(H,t,n,r)}};return U}function Lo(e){if(cr(e))return e=qt(e),e.children=null,e}function hi(e){return cr(e)?e.children?e.children[0]:void 0:e}function Xn(e,t){e.shapeFlag&6&&e.component?Xn(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Ia(e,t=!1,n){let r=[],o=0;for(let a=0;a1)for(let a=0;aDe({name:e.name},t,{setup:e}))():e}const Fn=e=>!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function _(e){re(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:a,suspensible:i=!0,onError:s}=e;let c=null,u,d=0;const p=()=>(d++,c=null,v()),v=()=>{let h;return c||(h=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),s)return new Promise((T,I)=>{s(b,()=>T(p()),()=>I(b),d+1)});throw b}).then(b=>h!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return B({name:"AsyncComponentWrapper",__asyncLoader:v,get __asyncResolved(){return u},setup(){const h=Ce;if(u)return()=>Ao(u,h);const b=k=>{c=null,sr(k,h,13,!r)};if(i&&h.suspense||fr)return v().then(k=>()=>Ao(k,h)).catch(k=>(b(k),()=>r?we(r,{error:k}):null));const T=Y(!1),I=Y(),E=Y(!!o);return o&&setTimeout(()=>{E.value=!1},o),a!=null&&setTimeout(()=>{if(!T.value&&!I.value){const k=new Error(`Async component timed out after ${a}ms.`);b(k),I.value=k}},a),v().then(()=>{T.value=!0,h.parent&&cr(h.parent.vnode)&&(h.parent.effect.dirty=!0,oo(h.parent.update))}).catch(k=>{b(k),I.value=k}),()=>{if(T.value&&u)return Ao(u,h);if(I.value&&r)return we(r,{error:I.value});if(n&&!E.value)return we(n)}}})}function Ao(e,t){const{ref:n,props:r,children:o,ce:a}=t.vnode,i=we(e,r,o);return i.ref=n,i.ce=a,delete t.vnode.ce,i}const cr=e=>e.type.__isKeepAlive;function J0(e,t){os(e,"a",t)}function Q0(e,t){os(e,"da",t)}function os(e,t,n=Ce){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(io(t,r,n),n){let o=n.parent;for(;o&&o.parent;)cr(o.parent.vnode)&&K0(r,t,n,o),o=o.parent}}function K0(e,t,n,r){const o=io(t,e,r,!0);ur(()=>{fa(r[t],o)},n)}function io(e,t,n=Ce,r=!1){if(n){const o=n[e]||(n[e]=[]),a=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;tn();const s=dr(n),c=tt(t,n,e,i);return s(),nn(),c});return r?o.unshift(a):o.push(a),a}}const Ot=e=>(t,n=Ce)=>(!fr||e==="sp")&&io(e,(...r)=>t(...r),n),Y0=Ot("bm"),ye=Ot("m"),X0=Ot("bu"),as=Ot("u"),wa=Ot("bum"),ur=Ot("um"),Z0=Ot("sp"),e1=Ot("rtg"),t1=Ot("rtc");function n1(e,t=Ce){io("ec",e,t)}const qo=e=>e?As(e)?Ra(e)||e.proxy:qo(e.parent):null,jn=De(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>qo(e.parent),$root:e=>qo(e.root),$emit:e=>e.emit,$options:e=>ka(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,oo(e.update)}),$nextTick:e=>e.n||(e.n=rn.bind(e.proxy)),$watch:e=>U0.bind(e)}),To=(e,t)=>e!==Te&&!e.__isScriptSetup&&fe(e,t),r1={get({_:e},t){const{ctx:n,setupState:r,data:o,props:a,accessCache:i,type:s,appContext:c}=e;let u;if(t[0]!=="$"){const h=i[t];if(h!==void 0)switch(h){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return a[t]}else{if(To(r,t))return i[t]=1,r[t];if(o!==Te&&fe(o,t))return i[t]=2,o[t];if((u=e.propsOptions[0])&&fe(u,t))return i[t]=3,a[t];if(n!==Te&&fe(n,t))return i[t]=4,n[t];Uo&&(i[t]=0)}}const d=jn[t];let p,v;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=s.__cssModules)&&(p=p[t]))return p;if(n!==Te&&fe(n,t))return i[t]=4,n[t];if(v=c.config.globalProperties,fe(v,t))return v[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:a}=e;return To(o,t)?(o[t]=n,!0):r!==Te&&fe(r,t)?(r[t]=n,!0):fe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(a[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:a}},i){let s;return!!n[i]||e!==Te&&fe(e,i)||To(t,i)||(s=a[0])&&fe(s,i)||fe(r,i)||fe(jn,i)||fe(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:fe(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function mi(e){return ee(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let Uo=!0;function o1(e){const t=ka(e),n=e.proxy,r=e.ctx;Uo=!1,t.beforeCreate&&gi(t.beforeCreate,e,"bc");const{data:o,computed:a,methods:i,watch:s,provide:c,inject:u,created:d,beforeMount:p,mounted:v,beforeUpdate:h,updated:b,activated:T,deactivated:I,beforeDestroy:E,beforeUnmount:k,destroyed:y,unmounted:P,render:$,renderTracked:w,renderTriggered:U,errorCaptured:H,serverPrefetch:J,expose:x,inheritAttrs:X,components:M,directives:te,filters:Pe}=t;if(u&&a1(u,r,null),i)for(const oe in i){const W=i[oe];re(W)&&(r[oe]=W.bind(n))}if(o){const oe=o.call(n,n);ke(oe)&&(e.data=lr(oe))}if(Uo=!0,a)for(const oe in a){const W=a[oe],at=re(W)?W.bind(n,n):re(W.get)?W.get.bind(n,n):Je,Rt=!re(W)&&re(W.set)?W.set.bind(n):Je,vt=L({get:at,set:Rt});Object.defineProperty(r,oe,{enumerable:!0,configurable:!0,get:()=>vt.value,set:Fe=>vt.value=Fe})}if(s)for(const oe in s)is(s[oe],r,n,oe);if(c){const oe=re(c)?c.call(n):c;Reflect.ownKeys(oe).forEach(W=>{ct(W,oe[W])})}d&&gi(d,e,"c");function Q(oe,W){ee(W)?W.forEach(at=>oe(at.bind(n))):W&&oe(W.bind(n))}if(Q(Y0,p),Q(ye,v),Q(X0,h),Q(as,b),Q(J0,T),Q(Q0,I),Q(n1,H),Q(t1,w),Q(e1,U),Q(wa,k),Q(ur,P),Q(Z0,J),ee(x))if(x.length){const oe=e.exposed||(e.exposed={});x.forEach(W=>{Object.defineProperty(oe,W,{get:()=>n[W],set:at=>n[W]=at})})}else e.exposed||(e.exposed={});$&&e.render===Je&&(e.render=$),X!=null&&(e.inheritAttrs=X),M&&(e.components=M),te&&(e.directives=te)}function a1(e,t,n=Je){ee(e)&&(e=Go(e));for(const r in e){const o=e[r];let a;ke(o)?"default"in o?a=he(o.from||r,o.default,!0):a=he(o.from||r):a=he(o),Ve(a)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>a.value,set:i=>a.value=i}):t[r]=a}}function gi(e,t,n){tt(ee(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function is(e,t,n,r){const o=r.includes(".")?es(n,r):()=>n[r];if(pe(e)){const a=t[e];re(a)&&ve(o,a)}else if(re(e))ve(o,e.bind(n));else if(ke(e))if(ee(e))e.forEach(a=>is(a,t,n,r));else{const a=re(e.handler)?e.handler.bind(n):t[e.handler];re(a)&&ve(o,a,e)}}function ka(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:a,config:{optionMergeStrategies:i}}=e.appContext,s=a.get(t);let c;return s?c=s:!o.length&&!n&&!r?c=t:(c={},o.length&&o.forEach(u=>Wr(c,u,i,!0)),Wr(c,t,i)),ke(t)&&a.set(t,c),c}function Wr(e,t,n,r=!1){const{mixins:o,extends:a}=t;a&&Wr(e,a,n,!0),o&&o.forEach(i=>Wr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const s=i1[i]||n&&n[i];e[i]=s?s(e[i],t[i]):t[i]}return e}const i1={data:_i,props:bi,emits:bi,methods:$n,computed:$n,beforeCreate:He,created:He,beforeMount:He,mounted:He,beforeUpdate:He,updated:He,beforeDestroy:He,beforeUnmount:He,destroyed:He,unmounted:He,activated:He,deactivated:He,errorCaptured:He,serverPrefetch:He,components:$n,directives:$n,watch:s1,provide:_i,inject:l1};function _i(e,t){return t?e?function(){return De(re(e)?e.call(this,this):e,re(t)?t.call(this,this):t)}:t:e}function l1(e,t){return $n(Go(e),Go(t))}function Go(e){if(ee(e)){const t={};for(let n=0;n1)return n&&re(t)?t.call(r&&r.proxy):t}}function d1(e,t,n,r=!1){const o={},a={};jr(a,lo,1),e.propsDefaults=Object.create(null),ss(e,t,o,a);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:Hl(o):e.type.props?e.props=o:e.props=a,e.attrs=a}function f1(e,t,n,r){const{props:o,attrs:a,vnode:{patchFlag:i}}=e,s=ce(o),[c]=e.propsOptions;let u=!1;if((r||i>0)&&!(i&16)){if(i&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[v,h]=cs(p,t,!0);De(i,v),h&&s.push(...h)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!a&&!c)return ke(e)&&r.set(e,vn),vn;if(ee(a))for(let d=0;d-1,h[1]=T<0||b-1||fe(h,"default"))&&s.push(p)}}}const u=[i,s];return ke(e)&&r.set(e,u),u}function yi(e){return e[0]!=="$"&&!hn(e)}function Ei(e){return e===null?"null":typeof e=="function"?e.name||"":typeof e=="object"&&e.constructor&&e.constructor.name||""}function Li(e,t){return Ei(e)===Ei(t)}function Ai(e,t){return ee(t)?t.findIndex(n=>Li(n,e)):re(t)&&Li(t,e)?0:-1}const us=e=>e[0]==="_"||e==="$stable",Pa=e=>ee(e)?e.map(lt):[lt(e)],p1=(e,t,n)=>{if(t._n)return t;const r=zo((...o)=>Pa(t(...o)),n);return r._c=!1,r},ds=(e,t,n)=>{const r=e._ctx;for(const o in e){if(us(o))continue;const a=e[o];if(re(a))t[o]=p1(o,a,r);else if(a!=null){const i=Pa(a);t[o]=()=>i}}},fs=(e,t)=>{const n=Pa(t);e.slots.default=()=>n},v1=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=ce(t),jr(t,"_",n)):ds(t,e.slots={})}else e.slots={},t&&fs(e,t);jr(e.slots,lo,1)},h1=(e,t,n)=>{const{vnode:r,slots:o}=e;let a=!0,i=Te;if(r.shapeFlag&32){const s=t._;s?n&&s===1?a=!1:(De(o,t),!n&&s===1&&delete o._):(a=!t.$stable,ds(t,o)),i=t}else t&&(fs(e,t),i={default:1});if(a)for(const s in o)!us(s)&&i[s]==null&&delete o[s]};function Jr(e,t,n,r,o=!1){if(ee(e)){e.forEach((v,h)=>Jr(v,t&&(ee(t)?t[h]:t),n,r,o));return}if(Fn(r)&&!o)return;const a=r.shapeFlag&4?Ra(r.component)||r.component.proxy:r.el,i=o?null:a,{i:s,r:c}=e,u=t&&t.r,d=s.refs===Te?s.refs={}:s.refs,p=s.setupState;if(u!=null&&u!==c&&(pe(u)?(d[u]=null,fe(p,u)&&(p[u]=null)):Ve(u)&&(u.value=null)),re(c))jt(c,s,12,[i,d]);else{const v=pe(c),h=Ve(c);if(v||h){const b=()=>{if(e.f){const T=v?fe(p,c)?p[c]:d[c]:c.value;o?ee(T)&&fa(T,a):ee(T)?T.includes(a)||T.push(a):v?(d[c]=[a],fe(p,c)&&(p[c]=d[c])):(c.value=[a],e.k&&(d[e.k]=c.value))}else v?(d[c]=i,fe(p,c)&&(p[c]=i)):h&&(c.value=i,e.k&&(d[e.k]=i))};i?(b.id=-1,je(b,n)):b()}}}let St=!1;const m1=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",g1=e=>e.namespaceURI.includes("MathML"),xr=e=>{if(m1(e))return"svg";if(g1(e))return"mathml"},Cr=e=>e.nodeType===8;function _1(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:a,parentNode:i,remove:s,insert:c,createComment:u}}=e,d=(y,P)=>{if(!P.hasChildNodes()){n(null,y,P),Ur(),P._vnode=y;return}St=!1,p(P.firstChild,y,null,null,null),Ur(),P._vnode=y,St&&console.error("Hydration completed but contains mismatches.")},p=(y,P,$,w,U,H=!1)=>{const J=Cr(y)&&y.data==="[",x=()=>T(y,P,$,w,U,J),{type:X,ref:M,shapeFlag:te,patchFlag:Pe}=P;let Oe=y.nodeType;P.el=y,Pe===-2&&(H=!1,P.dynamicChildren=null);let Q=null;switch(X){case En:Oe!==3?P.children===""?(c(P.el=o(""),i(y),y),Q=y):Q=x():(y.data!==P.children&&(St=!0,y.data=P.children),Q=a(y));break;case nt:k(y)?(Q=a(y),E(P.el=y.content.firstChild,y,$)):Oe!==8||J?Q=x():Q=a(y);break;case qn:if(J&&(y=a(y),Oe=y.nodeType),Oe===1||Oe===3){Q=y;const oe=!P.children.length;for(let W=0;W{H=H||!!P.dynamicChildren;const{type:J,props:x,patchFlag:X,shapeFlag:M,dirs:te,transition:Pe}=P,Oe=J==="input"||J==="option";if(Oe||X!==-1){te&&mt(P,null,$,"created");let Q=!1;if(k(y)){Q=ps(w,Pe)&&$&&$.vnode.props&&$.vnode.props.appear;const W=y.content.firstChild;Q&&Pe.beforeEnter(W),E(W,y,$),P.el=y=W}if(M&16&&!(x&&(x.innerHTML||x.textContent))){let W=h(y.firstChild,P,y,$,w,U,H);for(;W;){St=!0;const at=W;W=W.nextSibling,s(at)}}else M&8&&y.textContent!==P.children&&(St=!0,y.textContent=P.children);if(x)if(Oe||!H||X&48)for(const W in x)(Oe&&(W.endsWith("value")||W==="indeterminate")||ar(W)&&!hn(W)||W[0]===".")&&r(y,W,null,x[W],void 0,void 0,$);else x.onClick&&r(y,"onClick",null,x.onClick,void 0,void 0,$);let oe;(oe=x&&x.onVnodeBeforeMount)&&Ze(oe,$,P),te&&mt(P,null,$,"beforeMount"),((oe=x&&x.onVnodeMounted)||te||Q)&&Zl(()=>{oe&&Ze(oe,$,P),Q&&Pe.enter(y),te&&mt(P,null,$,"mounted")},w)}return y.nextSibling},h=(y,P,$,w,U,H,J)=>{J=J||!!P.dynamicChildren;const x=P.children,X=x.length;for(let M=0;M{const{slotScopeIds:J}=P;J&&(U=U?U.concat(J):J);const x=i(y),X=h(a(y),P,x,$,w,U,H);return X&&Cr(X)&&X.data==="]"?a(P.anchor=X):(St=!0,c(P.anchor=u("]"),x,X),X)},T=(y,P,$,w,U,H)=>{if(St=!0,P.el=null,H){const X=I(y);for(;;){const M=a(y);if(M&&M!==X)s(M);else break}}const J=a(y),x=i(y);return s(y),n(null,P,x,J,$,w,xr(x),U),J},I=(y,P="[",$="]")=>{let w=0;for(;y;)if(y=a(y),y&&Cr(y)&&(y.data===P&&w++,y.data===$)){if(w===0)return a(y);w--}return y},E=(y,P,$)=>{const w=P.parentNode;w&&w.replaceChild(y,P);let U=$;for(;U;)U.vnode.el===P&&(U.vnode.el=U.subTree.el=y),U=U.parent},k=y=>y.nodeType===1&&y.tagName.toLowerCase()==="template";return[d,p]}const je=Zl;function b1(e){return y1(e,_1)}function y1(e,t){const n=wl();n.__VUE__=!0;const{insert:r,remove:o,patchProp:a,createElement:i,createText:s,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:v,setScopeId:h=Je,insertStaticContent:b}=e,T=(m,g,A,D=null,O=null,V=null,j=void 0,C=null,N=!!g.dynamicChildren)=>{if(m===g)return;m&&!Yt(m,g)&&(D=R(m),Fe(m,O,V,!0),m=null),g.patchFlag===-2&&(N=!1,g.dynamicChildren=null);const{type:S,ref:q,shapeFlag:Z}=g;switch(S){case En:I(m,g,A,D);break;case nt:E(m,g,A,D);break;case qn:m==null&&k(g,A,D,j);break;case We:M(m,g,A,D,O,V,j,C,N);break;default:Z&1?$(m,g,A,D,O,V,j,C,N):Z&6?te(m,g,A,D,O,V,j,C,N):(Z&64||Z&128)&&S.process(m,g,A,D,O,V,j,C,N,G)}q!=null&&O&&Jr(q,m&&m.ref,V,g||m,!g)},I=(m,g,A,D)=>{if(m==null)r(g.el=s(g.children),A,D);else{const O=g.el=m.el;g.children!==m.children&&u(O,g.children)}},E=(m,g,A,D)=>{m==null?r(g.el=c(g.children||""),A,D):g.el=m.el},k=(m,g,A,D)=>{[m.el,m.anchor]=b(m.children,g,A,D,m.el,m.anchor)},y=({el:m,anchor:g},A,D)=>{let O;for(;m&&m!==g;)O=v(m),r(m,A,D),m=O;r(g,A,D)},P=({el:m,anchor:g})=>{let A;for(;m&&m!==g;)A=v(m),o(m),m=A;o(g)},$=(m,g,A,D,O,V,j,C,N)=>{g.type==="svg"?j="svg":g.type==="math"&&(j="mathml"),m==null?w(g,A,D,O,V,j,C,N):J(m,g,O,V,j,C,N)},w=(m,g,A,D,O,V,j,C)=>{let N,S;const{props:q,shapeFlag:Z,transition:K,dirs:ne}=m;if(N=m.el=i(m.type,V,q&&q.is,q),Z&8?d(N,m.children):Z&16&&H(m.children,N,null,D,O,Io(m,V),j,C),ne&&mt(m,null,D,"created"),U(N,m,m.scopeId,j,D),q){for(const be in q)be!=="value"&&!hn(be)&&a(N,be,null,q[be],V,m.children,D,O,Me);"value"in q&&a(N,"value",null,q.value,V),(S=q.onVnodeBeforeMount)&&Ze(S,D,m)}ne&&mt(m,null,D,"beforeMount");const ae=ps(O,K);ae&&K.beforeEnter(N),r(N,g,A),((S=q&&q.onVnodeMounted)||ae||ne)&&je(()=>{S&&Ze(S,D,m),ae&&K.enter(N),ne&&mt(m,null,D,"mounted")},O)},U=(m,g,A,D,O)=>{if(A&&h(m,A),D)for(let V=0;V{for(let S=N;S{const C=g.el=m.el;let{patchFlag:N,dynamicChildren:S,dirs:q}=g;N|=m.patchFlag&16;const Z=m.props||Te,K=g.props||Te;let ne;if(A&&Jt(A,!1),(ne=K.onVnodeBeforeUpdate)&&Ze(ne,A,g,m),q&&mt(g,m,A,"beforeUpdate"),A&&Jt(A,!0),S?x(m.dynamicChildren,S,C,A,D,Io(g,O),V):j||W(m,g,C,null,A,D,Io(g,O),V,!1),N>0){if(N&16)X(C,g,Z,K,A,D,O);else if(N&2&&Z.class!==K.class&&a(C,"class",null,K.class,O),N&4&&a(C,"style",Z.style,K.style,O),N&8){const ae=g.dynamicProps;for(let be=0;be{ne&&Ze(ne,A,g,m),q&&mt(g,m,A,"updated")},D)},x=(m,g,A,D,O,V,j)=>{for(let C=0;C{if(A!==D){if(A!==Te)for(const C in A)!hn(C)&&!(C in D)&&a(m,C,A[C],null,j,g.children,O,V,Me);for(const C in D){if(hn(C))continue;const N=D[C],S=A[C];N!==S&&C!=="value"&&a(m,C,S,N,j,g.children,O,V,Me)}"value"in D&&a(m,"value",A.value,D.value,j)}},M=(m,g,A,D,O,V,j,C,N)=>{const S=g.el=m?m.el:s(""),q=g.anchor=m?m.anchor:s("");let{patchFlag:Z,dynamicChildren:K,slotScopeIds:ne}=g;ne&&(C=C?C.concat(ne):ne),m==null?(r(S,A,D),r(q,A,D),H(g.children||[],A,q,O,V,j,C,N)):Z>0&&Z&64&&K&&m.dynamicChildren?(x(m.dynamicChildren,K,A,O,V,j,C),(g.key!=null||O&&g===O.subTree)&&vs(m,g,!0)):W(m,g,A,q,O,V,j,C,N)},te=(m,g,A,D,O,V,j,C,N)=>{g.slotScopeIds=C,m==null?g.shapeFlag&512?O.ctx.activate(g,A,D,j,N):Pe(g,A,D,O,V,j,N):Oe(m,g,N)},Pe=(m,g,A,D,O,V,j)=>{const C=m.component=P1(m,D,O);if(cr(m)&&(C.ctx.renderer=G),O1(C),C.asyncDep){if(O&&O.registerDep(C,Q),!m.el){const N=C.subTree=we(nt);E(null,N,g,A)}}else Q(C,m,g,A,O,V,j)},Oe=(m,g,A)=>{const D=g.component=m.component;if($0(m,g,A))if(D.asyncDep&&!D.asyncResolved){oe(D,g,A);return}else D.next=g,S0(D.update),D.effect.dirty=!0,D.update();else g.el=m.el,D.vnode=g},Q=(m,g,A,D,O,V,j)=>{const C=()=>{if(m.isMounted){let{next:q,bu:Z,u:K,parent:ne,vnode:ae}=m;{const cn=hs(m);if(cn){q&&(q.el=ae.el,oe(m,q,j)),cn.asyncDep.then(()=>{m.isUnmounted||C()});return}}let be=q,Ae;Jt(m,!1),q?(q.el=ae.el,oe(m,q,j)):q=ae,Z&&yo(Z),(Ae=q.props&&q.props.onVnodeBeforeUpdate)&&Ze(Ae,ne,q,ae),Jt(m,!0);const xe=Eo(m),it=m.subTree;m.subTree=xe,T(it,xe,p(it.el),R(it),m,O,V),q.el=xe.el,be===null&&N0(m,xe.el),K&&je(K,O),(Ae=q.props&&q.props.onVnodeUpdated)&&je(()=>Ze(Ae,ne,q,ae),O)}else{let q;const{el:Z,props:K}=g,{bm:ne,m:ae,parent:be}=m,Ae=Fn(g);if(Jt(m,!1),ne&&yo(ne),!Ae&&(q=K&&K.onVnodeBeforeMount)&&Ze(q,be,g),Jt(m,!0),Z&&Le){const xe=()=>{m.subTree=Eo(m),Le(Z,m.subTree,m,O,null)};Ae?g.type.__asyncLoader().then(()=>!m.isUnmounted&&xe()):xe()}else{const xe=m.subTree=Eo(m);T(null,xe,A,D,m,O,V),g.el=xe.el}if(ae&&je(ae,O),!Ae&&(q=K&&K.onVnodeMounted)){const xe=g;je(()=>Ze(q,be,xe),O)}(g.shapeFlag&256||be&&Fn(be.vnode)&&be.vnode.shapeFlag&256)&&m.a&&je(m.a,O),m.isMounted=!0,g=A=D=null}},N=m.effect=new va(C,Je,()=>oo(S),m.scope),S=m.update=()=>{N.dirty&&N.run()};S.id=m.uid,Jt(m,!0),S()},oe=(m,g,A)=>{g.component=m;const D=m.vnode.props;m.vnode=g,m.next=null,f1(m,g.props,D,A),h1(m,g.children,A),tn(),fi(m),nn()},W=(m,g,A,D,O,V,j,C,N=!1)=>{const S=m&&m.children,q=m?m.shapeFlag:0,Z=g.children,{patchFlag:K,shapeFlag:ne}=g;if(K>0){if(K&128){Rt(S,Z,A,D,O,V,j,C,N);return}else if(K&256){at(S,Z,A,D,O,V,j,C,N);return}}ne&8?(q&16&&Me(S,O,V),Z!==S&&d(A,Z)):q&16?ne&16?Rt(S,Z,A,D,O,V,j,C,N):Me(S,O,V,!0):(q&8&&d(A,""),ne&16&&H(Z,A,D,O,V,j,C,N))},at=(m,g,A,D,O,V,j,C,N)=>{m=m||vn,g=g||vn;const S=m.length,q=g.length,Z=Math.min(S,q);let K;for(K=0;Kq?Me(m,O,V,!0,!1,Z):H(g,A,D,O,V,j,C,N,Z)},Rt=(m,g,A,D,O,V,j,C,N)=>{let S=0;const q=g.length;let Z=m.length-1,K=q-1;for(;S<=Z&&S<=K;){const ne=m[S],ae=g[S]=N?$t(g[S]):lt(g[S]);if(Yt(ne,ae))T(ne,ae,A,null,O,V,j,C,N);else break;S++}for(;S<=Z&&S<=K;){const ne=m[Z],ae=g[K]=N?$t(g[K]):lt(g[K]);if(Yt(ne,ae))T(ne,ae,A,null,O,V,j,C,N);else break;Z--,K--}if(S>Z){if(S<=K){const ne=K+1,ae=neK)for(;S<=Z;)Fe(m[S],O,V,!0),S++;else{const ne=S,ae=S,be=new Map;for(S=ae;S<=K;S++){const Ue=g[S]=N?$t(g[S]):lt(g[S]);Ue.key!=null&&be.set(Ue.key,S)}let Ae,xe=0;const it=K-ae+1;let cn=!1,ei=0;const Cn=new Array(it);for(S=0;S=it){Fe(Ue,O,V,!0);continue}let ht;if(Ue.key!=null)ht=be.get(Ue.key);else for(Ae=ae;Ae<=K;Ae++)if(Cn[Ae-ae]===0&&Yt(Ue,g[Ae])){ht=Ae;break}ht===void 0?Fe(Ue,O,V,!0):(Cn[ht-ae]=S+1,ht>=ei?ei=ht:cn=!0,T(Ue,g[ht],A,null,O,V,j,C,N),xe++)}const ti=cn?E1(Cn):vn;for(Ae=ti.length-1,S=it-1;S>=0;S--){const Ue=ae+S,ht=g[Ue],ni=Ue+1{const{el:V,type:j,transition:C,children:N,shapeFlag:S}=m;if(S&6){vt(m.component.subTree,g,A,D);return}if(S&128){m.suspense.move(g,A,D);return}if(S&64){j.move(m,g,A,G);return}if(j===We){r(V,g,A);for(let Z=0;ZC.enter(V),O);else{const{leave:Z,delayLeave:K,afterLeave:ne}=C,ae=()=>r(V,g,A),be=()=>{Z(V,()=>{ae(),ne&&ne()})};K?K(V,ae,be):be()}else r(V,g,A)},Fe=(m,g,A,D=!1,O=!1)=>{const{type:V,props:j,ref:C,children:N,dynamicChildren:S,shapeFlag:q,patchFlag:Z,dirs:K}=m;if(C!=null&&Jr(C,null,A,m,!0),q&256){g.ctx.deactivate(m);return}const ne=q&1&&K,ae=!Fn(m);let be;if(ae&&(be=j&&j.onVnodeBeforeUnmount)&&Ze(be,g,m),q&6)Ir(m.component,A,D);else{if(q&128){m.suspense.unmount(A,D);return}ne&&mt(m,null,g,"beforeUnmount"),q&64?m.type.remove(m,g,A,O,G,D):S&&(V!==We||Z>0&&Z&64)?Me(S,g,A,!1,!0):(V===We&&Z&384||!O&&q&16)&&Me(N,g,A),D&&ln(m)}(ae&&(be=j&&j.onVnodeUnmounted)||ne)&&je(()=>{be&&Ze(be,g,m),ne&&mt(m,null,g,"unmounted")},A)},ln=m=>{const{type:g,el:A,anchor:D,transition:O}=m;if(g===We){sn(A,D);return}if(g===qn){P(m);return}const V=()=>{o(A),O&&!O.persisted&&O.afterLeave&&O.afterLeave()};if(m.shapeFlag&1&&O&&!O.persisted){const{leave:j,delayLeave:C}=O,N=()=>j(A,V);C?C(m.el,V,N):N()}else V()},sn=(m,g)=>{let A;for(;m!==g;)A=v(m),o(m),m=A;o(g)},Ir=(m,g,A)=>{const{bum:D,scope:O,update:V,subTree:j,um:C}=m;D&&yo(D),O.stop(),V&&(V.active=!1,Fe(j,m,g,A)),C&&je(C,g),je(()=>{m.isUnmounted=!0},g),g&&g.pendingBranch&&!g.isUnmounted&&m.asyncDep&&!m.asyncResolved&&m.suspenseId===g.pendingId&&(g.deps--,g.deps===0&&g.resolve())},Me=(m,g,A,D=!1,O=!1,V=0)=>{for(let j=V;jm.shapeFlag&6?R(m.component.subTree):m.shapeFlag&128?m.suspense.next():v(m.anchor||m.el);let z=!1;const F=(m,g,A)=>{m==null?g._vnode&&Fe(g._vnode,null,null,!0):T(g._vnode||null,m,g,null,null,null,A),z||(z=!0,fi(),Ur(),z=!1),g._vnode=m},G={p:T,um:Fe,m:vt,r:ln,mt:Pe,mc:H,pc:W,pbc:x,n:R,o:e};let me,Le;return t&&([me,Le]=t(G)),{render:F,hydrate:me,createApp:u1(F,me)}}function Io({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function Jt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function ps(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function vs(e,t,n=!1){const r=e.children,o=t.children;if(ee(r)&&ee(o))for(let a=0;a>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,i=n[a-1];a-- >0;)n[a]=i,i=t[i];return n}function hs(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:hs(t)}const L1=e=>e.__isTeleport,We=Symbol.for("v-fgt"),En=Symbol.for("v-txt"),nt=Symbol.for("v-cmt"),qn=Symbol.for("v-stc"),Un=[];let st=null;function ms(e=!1){Un.push(st=e?null:[])}function A1(){Un.pop(),st=Un[Un.length-1]||null}let Zn=1;function Ti(e){Zn+=e}function gs(e){return e.dynamicChildren=Zn>0?st||vn:null,A1(),Zn>0&&st&&st.push(e),e}function R3(e,t,n,r,o,a){return gs(ys(e,t,n,r,o,a,!0))}function _s(e,t,n,r,o){return gs(we(e,t,n,r,o,!0))}function Jo(e){return e?e.__v_isVNode===!0:!1}function Yt(e,t){return e.type===t.type&&e.key===t.key}const lo="__vInternal",bs=({key:e})=>e??null,Hr=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?pe(e)||Ve(e)||re(e)?{i:et,r:e,k:t,f:!!n}:e:null);function ys(e,t=null,n=null,r=0,o=null,a=e===We?0:1,i=!1,s=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&bs(t),ref:t&&Hr(t),scopeId:Yl,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:et};return s?(Oa(c,n),a&128&&e.normalize(c)):n&&(c.shapeFlag|=pe(n)?8:16),Zn>0&&!i&&st&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&st.push(c),c}const we=T1;function T1(e,t=null,n=null,r=0,o=null,a=!1){if((!e||e===H0)&&(e=nt),Jo(e)){const s=qt(e,t,!0);return n&&Oa(s,n),Zn>0&&!a&&st&&(s.shapeFlag&6?st[st.indexOf(e)]=s:st.push(s)),s.patchFlag|=-2,s}if(C1(e)&&(e=e.__vccOpts),t){t=Es(t);let{class:s,style:c}=t;s&&!pe(s)&&(t.class=no(s)),ke(c)&&(Fl(c)&&!ee(c)&&(c=De({},c)),t.style=to(c))}const i=pe(e)?1:j0(e)?128:L1(e)?64:ke(e)?4:re(e)?2:0;return ys(e,t,n,r,o,i,a,!0)}function Es(e){return e?Fl(e)||lo in e?De({},e):e:null}function qt(e,t,n=!1){const{props:r,ref:o,patchFlag:a,children:i}=e,s=t?I1(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:s,key:s&&bs(s),ref:t&&t.ref?n&&o?ee(o)?o.concat(Hr(t)):[o,Hr(t)]:Hr(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==We?a===-1?16:a|16:a,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&qt(e.ssContent),ssFallback:e.ssFallback&&qt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Ls(e=" ",t=0){return we(En,null,e,t)}function D3(e,t){const n=we(qn,null,e);return n.staticCount=t,n}function S3(e="",t=!1){return t?(ms(),_s(nt,null,e)):we(nt,null,e)}function lt(e){return e==null||typeof e=="boolean"?we(nt):ee(e)?we(We,null,e.slice()):typeof e=="object"?$t(e):we(En,null,String(e))}function $t(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:qt(e)}function Oa(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ee(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),Oa(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!(lo in t)?t._ctx=et:o===3&&et&&(et.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else re(t)?(t={default:t,_ctx:et},n=32):(t=String(t),r&64?(n=16,t=[Ls(t)]):n=8);e.children=t,e.shapeFlag|=n}function I1(...e){const t={};for(let n=0;nCe||et;let Qr,Qo;{const e=wl(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),a=>{o.length>1?o.forEach(i=>i(a)):o[0](a)}};Qr=t("__VUE_INSTANCE_SETTERS__",n=>Ce=n),Qo=t("__VUE_SSR_SETTERS__",n=>fr=n)}const dr=e=>{const t=Ce;return Qr(e),e.scope.on(),()=>{e.scope.off(),Qr(t)}},Ii=()=>{Ce&&Ce.scope.off(),Qr(null)};function As(e){return e.vnode.shapeFlag&4}let fr=!1;function O1(e,t=!1){t&&Qo(t);const{props:n,children:r}=e.vnode,o=As(e);d1(e,n,o,t),v1(e,r);const a=o?R1(e,t):void 0;return t&&Qo(!1),a}function R1(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=jl(new Proxy(e.ctx,r1));const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?S1(e):null,a=dr(e);tn();const i=jt(r,e,0,[e.props,o]);if(nn(),a(),Il(i)){if(i.then(Ii,Ii),t)return i.then(s=>{wi(e,s,t)}).catch(s=>{sr(s,e,0)});e.asyncDep=i}else wi(e,i,t)}else Ts(e,t)}function wi(e,t,n){re(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ke(t)&&(e.setupState=Ul(t)),Ts(e,n)}let ki;function Ts(e,t,n){const r=e.type;if(!e.render){if(!t&&ki&&!r.render){const o=r.template||ka(e).template;if(o){const{isCustomElement:a,compilerOptions:i}=e.appContext.config,{delimiters:s,compilerOptions:c}=r,u=De(De({isCustomElement:a,delimiters:s},i),c);r.render=ki(o,u)}}e.render=r.render||Je}{const o=dr(e);tn();try{o1(e)}finally{nn(),o()}}}function D1(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return qe(e,"get","$attrs"),t[n]}}))}function S1(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return D1(e)},slots:e.slots,emit:e.emit,expose:t}}function Ra(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Ul(jl(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in jn)return jn[n](e)},has(t,n){return n in t||n in jn}}))}function x1(e,t=!0){return re(e)?e.displayName||e.name:e.name||t&&e.__name}function C1(e){return re(e)&&"__vccOpts"in e}const L=(e,t)=>A0(e,t,fr);function l(e,t,n){const r=arguments.length;return r===2?ke(t)&&!ee(t)?Jo(t)?we(e,null,[t]):we(e,t):we(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Jo(n)&&(n=[n]),we(e,t,n))}const V1="3.4.21";/** * @vue/runtime-dom v3.4.21 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT -**/const M1="http://www.w3.org/2000/svg",B1="http://www.w3.org/1998/Math/MathML",Nt=typeof document<"u"?document:null,Pi=Nt&&Nt.createElement("template"),$1={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t==="svg"?Nt.createElementNS(M1,e):t==="mathml"?Nt.createElementNS(B1,e):Nt.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>Nt.createTextNode(e),createComment:e=>Nt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Nt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,a){const i=n?n.previousSibling:t.lastChild;if(o&&(o===a||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===a||!(o=o.nextSibling)););else{Pi.innerHTML=r==="svg"?`${e}`:r==="mathml"?`${e}`:e;const s=Pi.content;if(r==="svg"||r==="mathml"){const c=s.firstChild;for(;c.firstChild;)s.appendChild(c.firstChild);s.removeChild(c)}t.insertBefore(s,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},xt="transition",Vn="animation",Ln=Symbol("_vtc"),wt=(e,{slots:t})=>l(W0,ws(e),t);wt.displayName="Transition";const Is={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},N1=wt.props=De({},ns,Is),Qt=(e,t=[])=>{ee(e)?e.forEach(n=>n(...t)):e&&e(...t)},Oi=e=>e?ee(e)?e.some(t=>t.length>1):e.length>1:!1;function ws(e){const t={};for(const M in e)M in Is||(t[M]=e[M]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:a=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:u=i,appearToClass:d=s,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:v=`${n}-leave-active`,leaveToClass:h=`${n}-leave-to`}=e,b=H1(o),A=b&&b[0],I=b&&b[1],{onBeforeEnter:E,onEnter:k,onEnterCancelled:y,onLeave:P,onLeaveCancelled:$,onBeforeAppear:w=E,onAppear:U=k,onAppearCancelled:H=y}=t,J=(M,te,Pe)=>{Vt(M,te?d:s),Vt(M,te?u:i),Pe&&Pe()},x=(M,te)=>{M._isLeaving=!1,Vt(M,p),Vt(M,h),Vt(M,v),te&&te()},X=M=>(te,Pe)=>{const Oe=M?U:k,Q=()=>J(te,M,Pe);Qt(Oe,[te,Q]),Ri(()=>{Vt(te,M?c:a),Et(te,M?d:s),Oi(Oe)||Di(te,r,A,Q)})};return De(t,{onBeforeEnter(M){Qt(E,[M]),Et(M,a),Et(M,i)},onBeforeAppear(M){Qt(w,[M]),Et(M,c),Et(M,u)},onEnter:X(!1),onAppear:X(!0),onLeave(M,te){M._isLeaving=!0;const Pe=()=>x(M,te);Et(M,p),Ps(),Et(M,v),Ri(()=>{M._isLeaving&&(Vt(M,p),Et(M,h),Oi(P)||Di(M,r,I,Pe))}),Qt(P,[M,Pe])},onEnterCancelled(M){J(M,!1),Qt(y,[M])},onAppearCancelled(M){J(M,!0),Qt(H,[M])},onLeaveCancelled(M){x(M),Qt($,[M])}})}function H1(e){if(e==null)return null;if(ke(e))return[wo(e.enter),wo(e.leave)];{const t=wo(e);return[t,t]}}function wo(e){return Gu(e)}function Et(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Ln]||(e[Ln]=new Set)).add(t)}function Vt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[Ln];n&&(n.delete(t),n.size||(e[Ln]=void 0))}function Ri(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let F1=0;function Di(e,t,n,r){const o=e._endId=++F1,a=()=>{o===e._endId&&r()};if(n)return setTimeout(a,n);const{type:i,timeout:s,propCount:c}=ks(e,t);if(!i)return r();const u=i+"end";let d=0;const p=()=>{e.removeEventListener(u,v),a()},v=h=>{h.target===e&&++d>=c&&p()};setTimeout(()=>{d(n[b]||"").split(", "),o=r(`${xt}Delay`),a=r(`${xt}Duration`),i=Si(o,a),s=r(`${Vn}Delay`),c=r(`${Vn}Duration`),u=Si(s,c);let d=null,p=0,v=0;t===xt?i>0&&(d=xt,p=i,v=a.length):t===Vn?u>0&&(d=Vn,p=u,v=c.length):(p=Math.max(i,u),d=p>0?i>u?xt:Vn:null,v=d?d===xt?a.length:c.length:0);const h=d===xt&&/\b(transform|all)(,|$)/.test(r(`${xt}Property`).toString());return{type:d,timeout:p,propCount:v,hasTransform:h}}function Si(e,t){for(;e.lengthxi(n)+xi(e[r])))}function xi(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Ps(){return document.body.offsetHeight}function j1(e,t,n){const r=e[Ln];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Ci=Symbol("_vod"),z1=Symbol("_vsh"),q1=Symbol(""),U1=/(^|;)\s*display\s*:/;function G1(e,t,n){const r=e.style,o=pe(n);let a=!1;if(n&&!o){if(t)if(pe(t))for(const i of t.split(";")){const s=i.slice(0,i.indexOf(":")).trim();n[s]==null&&Fr(r,s,"")}else for(const i in t)n[i]==null&&Fr(r,i,"");for(const i in n)i==="display"&&(a=!0),Fr(r,i,n[i])}else if(o){if(t!==n){const i=r[q1];i&&(n+=";"+i),r.cssText=n,a=U1.test(n)}}else t&&e.removeAttribute("style");Ci in e&&(e[Ci]=a?r.display:"",e[z1]&&(r.display="none"))}const Vi=/\s*!important$/;function Fr(e,t,n){if(ee(n))n.forEach(r=>Fr(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=W1(e,t);Vi.test(n)?e.setProperty(kn(r),n.replace(Vi,""),"important"):e[r]=n}}const Mi=["Webkit","Moz","ms"],ko={};function W1(e,t){const n=ko[t];if(n)return n;let r=ot(t);if(r!=="filter"&&r in e)return ko[t]=r;r=ir(r);for(let o=0;oPo||(ed.then(()=>Po=0),Po=Date.now());function nd(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;tt(rd(r,n.value),t,5,[r])};return n.value=e,n.attached=td(),n}function rd(e,t){if(ee(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const Hi=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,od=(e,t,n,r,o,a,i,s,c)=>{const u=o==="svg";t==="class"?j1(e,r,u):t==="style"?G1(e,n,r):ar(t)?da(t)||X1(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):ad(e,t,r,u))?Q1(e,t,r,a,i,s,c):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),J1(e,t,r,u))};function ad(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&Hi(t)&&re(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const o=e.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return Hi(t)&&pe(n)?!1:t in e}const Os=new WeakMap,Rs=new WeakMap,Kr=Symbol("_moveCb"),Fi=Symbol("_enterCb"),Ds={name:"TransitionGroup",props:De({},N1,{tag:String,moveClass:String}),setup(e,{slots:t}){const n=On(),r=ts();let o,a;return as(()=>{if(!o.length)return;const i=e.moveClass||`${e.name||"v"}-move`;if(!dd(o[0].el,n.vnode.el,i))return;o.forEach(sd),o.forEach(cd);const s=o.filter(ud);Ps(),s.forEach(c=>{const u=c.el,d=u.style;Et(u,i),d.transform=d.webkitTransform=d.transitionDuration="";const p=u[Kr]=v=>{v&&v.target!==u||(!v||/transform$/.test(v.propertyName))&&(u.removeEventListener("transitionend",p),u[Kr]=null,Vt(u,i))};u.addEventListener("transitionend",p)})}),()=>{const i=ce(e),s=ws(i);let c=i.tag||We;o=a,a=t.default?Ia(t.default()):[];for(let u=0;udelete e.mode;Ds.props;const ld=Ds;function sd(e){const t=e.el;t[Kr]&&t[Kr](),t[Fi]&&t[Fi]()}function cd(e){Rs.set(e,e.el.getBoundingClientRect())}function ud(e){const t=Os.get(e),n=Rs.get(e),r=t.left-n.left,o=t.top-n.top;if(r||o){const a=e.el.style;return a.transform=a.webkitTransform=`translate(${r}px,${o}px)`,a.transitionDuration="0s",e}}function dd(e,t,n){const r=e.cloneNode(),o=e[Ln];o&&o.forEach(s=>{s.split(/\s+/).forEach(c=>c&&r.classList.remove(c))}),n.split(/\s+/).forEach(s=>s&&r.classList.add(s)),r.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(r);const{hasTransform:i}=ks(r);return a.removeChild(r),i}const fd=De({patchProp:od},$1);let Oo,ji=!1;function pd(){return Oo=ji?Oo:b1(fd),ji=!0,Oo}const vd=(...e)=>{const t=pd().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=md(r);if(o)return n(o,!0,hd(o))},t};function hd(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function md(e){return pe(e)?document.querySelector(e):e}const gd={"v-8daa1a0e":()=>f(()=>import("./index.html-1299121d.js"),[]).then(({data:e})=>e),"v-fd1808c0":()=>f(()=>import("./friend.html-a1c44619.js"),[]).then(({data:e})=>e),"v-2d175b02":()=>f(()=>import("./hobby.html-32792606.js"),[]).then(({data:e})=>e),"v-513bdf66":()=>f(()=>import("./me.html-c3abaf6e.js"),[]).then(({data:e})=>e),"v-0c86f055":()=>f(()=>import("./update.html-0c62b864.js"),[]).then(({data:e})=>e),"v-2d0a870d":()=>f(()=>import("./index.html-0ad43b75.js"),[]).then(({data:e})=>e),"v-5aa3d8ba":()=>f(()=>import("./intro.html-def64f28.js"),[]).then(({data:e})=>e),"v-ed1de9c6":()=>f(()=>import("./2305090935.html-3b5537e3.js"),[]).then(({data:e})=>e),"v-48e62a82":()=>f(()=>import("./index.html-c81dddfc.js"),[]).then(({data:e})=>e),"v-79efb2b0":()=>f(()=>import("./2301101203.html-c687ba0e.js"),[]).then(({data:e})=>e),"v-7ba48b4f":()=>f(()=>import("./2301101204.html-e47dc170.js"),[]).then(({data:e})=>e),"v-1dbb5152":()=>f(()=>import("./2302201400.html-e38b2bce.js"),[]).then(({data:e})=>e),"v-1f7029f1":()=>f(()=>import("./2302201401.html-a4498f7c.js"),[]).then(({data:e})=>e),"v-248eb3ce":()=>f(()=>import("./2302201404.html-18c11cc6.js"),[]).then(({data:e})=>e),"v-4468c549":()=>f(()=>import("./2305081011.html-1e891ec3.js"),[]).then(({data:e})=>e),"v-7763b6e7":()=>f(()=>import("./2305081101.html-17d24cf1.js"),[]).then(({data:e})=>e),"v-7febf202":()=>f(()=>import("./2305081106.html-95836fad.js"),[]).then(({data:e})=>e),"v-aad5ccee":()=>f(()=>import("./2305081110.html-1afd6cb9.js"),[]).then(({data:e})=>e),"v-a4026a72":()=>f(()=>import("./2305081112.html-a670d54e.js"),[]).then(({data:e})=>e),"v-1ad96fc8":()=>f(()=>import("./2305090946.html-d1767897.js"),[]).then(({data:e})=>e),"v-f0b271fa":()=>f(()=>import("./2307051931.html-0784e57a.js"),[]).then(({data:e})=>e),"v-59c0c6b8":()=>f(()=>import("./2307062006.html-b05c085f.js"),[]).then(({data:e})=>e),"v-249319d7":()=>f(()=>import("./2301101200.html-9fa665b1.js"),[]).then(({data:e})=>e),"v-0115d5da":()=>f(()=>import("./2305090929.html-4bab3b53.js"),[]).then(({data:e})=>e),"v-64c61a30":()=>f(()=>import("./index.html-e7dd7f4e.js"),[]).then(({data:e})=>e),"v-db09d350":()=>f(()=>import("./index.html-b72ef4a7.js"),[]).then(({data:e})=>e),"v-3fc52a21":()=>f(()=>import("./2305081011.html-fcf35e80.js"),[]).then(({data:e})=>e),"v-72c01bbf":()=>f(()=>import("./2305081101.html-cf7208d0.js"),[]).then(({data:e})=>e),"v-b41d033e":()=>f(()=>import("./2305081110.html-dcf819da.js"),[]).then(({data:e})=>e),"v-460112e6":()=>f(()=>import("./2305081106.html-00656be1.js"),[]).then(({data:e})=>e),"v-6494b51e":()=>f(()=>import("./2305081112.html-9eeae295.js"),[]).then(({data:e})=>e),"v-0a4a23d6":()=>f(()=>import("./2305090946.html-c353d76f.js"),[]).then(({data:e})=>e),"v-1452e9f3":()=>f(()=>import("./2304010120.html-e922b0a2.js"),[]).then(({data:e})=>e),"v-0ba8fdf8":()=>f(()=>import("./2305091732.html-3d7cfd23.js"),[]).then(({data:e})=>e),"v-04d59b7c":()=>f(()=>import("./2305091734.html-74325375.js"),[]).then(({data:e})=>e),"v-00fee380":()=>f(()=>import("./2305091736.html-35ac6b25.js"),[]).then(({data:e})=>e),"v-2ba80b07":()=>f(()=>import("./2305091740.html-8f88a3ea.js"),[]).then(({data:e})=>e),"v-327b6d83":()=>f(()=>import("./2305091744.html-e174d288.js"),[]).then(({data:e})=>e),"v-1aa97360":()=>f(()=>import("./2312220952.html-d54014ac.js"),[]).then(({data:e})=>e),"v-47b60065":()=>f(()=>import("./2312280935.html-edfaba23.js"),[]).then(({data:e})=>e),"v-fbc34060":()=>f(()=>import("./2312291000.html-135d7164.js"),[]).then(({data:e})=>e),"v-78811366":()=>f(()=>import("./2312291432.html-29834115.js"),[]).then(({data:e})=>e),"v-0b555a5d":()=>f(()=>import("./1911011300.html-f9e364ef.js"),[]).then(({data:e})=>e),"v-305fe098":()=>f(()=>import("./2305052159.html-dafcd578.js"),[]).then(({data:e})=>e),"v-d0e2c786":()=>f(()=>import("./2305111000.html-2f6393ab.js"),[]).then(({data:e})=>e),"v-c5b336ca":()=>f(()=>import("./2311071055.html-e32999dd.js"),[]).then(({data:e})=>e),"v-91f8f38c":()=>f(()=>import("./2311081011.html-6fc54cd5.js"),[]).then(({data:e})=>e),"v-10b58660":()=>f(()=>import("./2311081109.html-09a2e140.js"),[]).then(({data:e})=>e),"v-7eeef733":()=>f(()=>import("./2311091006.html-2173149a.js"),[]).then(({data:e})=>e),"v-3a6c11b6":()=>f(()=>import("./2311221518.html-5a91ddfe.js"),[]).then(({data:e})=>e),"v-4b158648":()=>f(()=>import("./2403021055.html-98304a19.js"),[]).then(({data:e})=>e),"v-366969d6":()=>f(()=>import("./2403031236.html-2b9b9380.js"),[]).then(({data:e})=>e),"v-64b533de":()=>f(()=>import("./2403031505.html-a575f427.js"),[]).then(({data:e})=>e),"v-5a83f4b7":()=>f(()=>import("./2403032005.html-892a310b.js"),[]).then(({data:e})=>e),"v-a7a8f066":()=>f(()=>import("./2305140023.html-2b296e15.js"),[]).then(({data:e})=>e),"v-12216b09":()=>f(()=>import("./2305140203.html-391123eb.js"),[]).then(({data:e})=>e),"v-173ff4e6":()=>f(()=>import("./2305140206.html-132196df.js"),[]).then(({data:e})=>e),"v-82100b6c":()=>f(()=>import("./2305172157.html-ad0f4179.js"),[]).then(({data:e})=>e),"v-73716104":()=>f(()=>import("./2305191622.html-3205e6b6.js"),[]).then(({data:e})=>e),"v-3af4619e":()=>f(()=>import("./2305232045.html-266a3660.js"),[]).then(({data:e})=>e),"v-e97c7d16":()=>f(()=>import("./2305110857.html-911475e0.js"),[]).then(({data:e})=>e),"v-da7165c6":()=>f(()=>import("./2401171030.html-88e63301.js"),[]).then(({data:e})=>e),"v-01034d18":()=>f(()=>import("./1910011300.html-4fb37a7b.js"),[]).then(({data:e})=>e),"v-01333213":()=>f(()=>import("./1910011301.html-df2c9fce.js"),[]).then(({data:e})=>e),"v-02e80ab2":()=>f(()=>import("./1910011302.html-67fec82f.js"),[]).then(({data:e})=>e),"v-049ce351":()=>f(()=>import("./1910011303.html-4155e70d.js"),[]).then(({data:e})=>e),"v-0651bbf0":()=>f(()=>import("./1910011304.html-3e17f12f.js"),[]).then(({data:e})=>e),"v-113cc96c":()=>f(()=>import("./2305011002.html-ffdcf33e.js"),[]).then(({data:e})=>e),"v-b8674064":()=>f(()=>import("./2305012001.html-f4132e9f.js"),[]).then(({data:e})=>e),"v-753dfaa7":()=>f(()=>import("./2305020500.html-3e7b5938.js"),[]).then(({data:e})=>e),"v-76f2d346":()=>f(()=>import("./2305020501.html-18e5137b.js"),[]).then(({data:e})=>e),"v-1ae8d368":()=>f(()=>import("./2305032100.html-d828cb0c.js"),[]).then(({data:e})=>e),"v-06496550":()=>f(()=>import("./2101202010.html-bb29e7a7.js"),[]).then(({data:e})=>e),"v-07fe3def":()=>f(()=>import("./2101202011.html-d98fdd85.js"),[]).then(({data:e})=>e),"v-09b3168e":()=>f(()=>import("./2101202012.html-7b6182e5.js"),[]).then(({data:e})=>e),"v-4f779f12":()=>f(()=>import("./2304021002.html-21c638b0.js"),[]).then(({data:e})=>e),"v-26c13b6a":()=>f(()=>import("./2306210953.html-8549440f.js"),[]).then(({data:e})=>e),"v-3b468bb2":()=>f(()=>import("./2306211023.html-831dca01.js"),[]).then(({data:e})=>e),"v-ed30c3f6":()=>f(()=>import("./2102202010.html-5e81c37e.js"),[]).then(({data:e})=>e),"v-e9c712b8":()=>f(()=>import("./2102202011.html-cfac6552.js"),[]).then(({data:e})=>e),"v-e65d617a":()=>f(()=>import("./2102202012.html-b2c89c59.js"),[]).then(({data:e})=>e),"v-55cc2415":()=>f(()=>import("./2305252031.html-fa13474f.js"),[]).then(({data:e})=>e),"v-49daa846":()=>f(()=>import("./2305281438.html-c1e8c165.js"),[]).then(({data:e})=>e),"v-4ee32d34":()=>f(()=>import("./2305281702.html-7e5c45ef.js"),[]).then(({data:e})=>e),"v-15eaf018":()=>f(()=>import("./2305312110.html-87e5d5a0.js"),[]).then(({data:e})=>e),"v-3b8d5cd7":()=>f(()=>import("./2305312243.html-9ece2619.js"),[]).then(({data:e})=>e),"v-ea9ab6de":()=>f(()=>import("./2306011640.html-77488a8d.js"),[]).then(({data:e})=>e),"v-0dc9bed3":()=>f(()=>import("./2306021120.html-3b158a72.js"),[]).then(({data:e})=>e),"v-53a68847":()=>f(()=>import("./2306021924.html-401e345f.js"),[]).then(({data:e})=>e),"v-ba51a5ea":()=>f(()=>import("./2306041045.html-90dab8c8.js"),[]).then(({data:e})=>e),"v-730879e4":()=>f(()=>import("./2306042021.html-4e558a36.js"),[]).then(({data:e})=>e),"v-24957b89":()=>f(()=>import("./2306051101.html-76410b93.js"),[]).then(({data:e})=>e),"v-1b2e8a6c":()=>f(()=>import("./2306052321.html-85a0876e.js"),[]).then(({data:e})=>e),"v-e12a9278":()=>f(()=>import("./2312130940.html-69186843.js"),[]).then(({data:e})=>e),"v-783d7264":()=>f(()=>import("./2312141621.html-def3b7de.js"),[]).then(({data:e})=>e),"v-3706649a":()=>f(()=>import("./404.html-78a9acc1.js"),[]).then(({data:e})=>e),"v-74bc627b":()=>f(()=>import("./index.html-ac597c67.js"),[]).then(({data:e})=>e),"v-76bd8bc4":()=>f(()=>import("./index.html-1881782d.js"),[]).then(({data:e})=>e),"v-15054f24":()=>f(()=>import("./index.html-2693244f.js"),[]).then(({data:e})=>e),"v-8428717a":()=>f(()=>import("./index.html-bb983df4.js"),[]).then(({data:e})=>e),"v-3dbf9980":()=>f(()=>import("./index.html-c728fdda.js"),[]).then(({data:e})=>e),"v-7fe15663":()=>f(()=>import("./index.html-0e2285d8.js"),[]).then(({data:e})=>e),"v-a945652a":()=>f(()=>import("./index.html-de207251.js"),[]).then(({data:e})=>e),"v-65b85824":()=>f(()=>import("./index.html-1b508e6a.js"),[]).then(({data:e})=>e),"v-e2850a42":()=>f(()=>import("./index.html-ca9a74ca.js"),[]).then(({data:e})=>e),"v-19d7a44e":()=>f(()=>import("./index.html-2bdef7c0.js"),[]).then(({data:e})=>e),"v-4857a26e":()=>f(()=>import("./index.html-0937571b.js"),[]).then(({data:e})=>e),"v-1b5ca3be":()=>f(()=>import("./index.html-0807a1f7.js"),[]).then(({data:e})=>e),"v-34d631cd":()=>f(()=>import("./index.html-a46d5cf2.js"),[]).then(({data:e})=>e),"v-115f5fd1":()=>f(()=>import("./index.html-bf318dcd.js"),[]).then(({data:e})=>e),"v-716fffd6":()=>f(()=>import("./index.html-e2532819.js"),[]).then(({data:e})=>e),"v-e025c652":()=>f(()=>import("./index.html-20105346.js"),[]).then(({data:e})=>e),"v-586621a9":()=>f(()=>import("./index.html-721ab647.js"),[]).then(({data:e})=>e),"v-6a12e6a2":()=>f(()=>import("./index.html-9e4305af.js"),[]).then(({data:e})=>e),"v-3033cd1e":()=>f(()=>import("./index.html-0769bc4b.js"),[]).then(({data:e})=>e),"v-5c743294":()=>f(()=>import("./index.html-f8a821da.js"),[]).then(({data:e})=>e),"v-7c89fed2":()=>f(()=>import("./index.html-c752a5d2.js"),[]).then(({data:e})=>e),"v-65f4a756":()=>f(()=>import("./index.html-af75d85c.js"),[]).then(({data:e})=>e),"v-7a09c3ca":()=>f(()=>import("./index.html-92322d72.js"),[]).then(({data:e})=>e),"v-2a2b904c":()=>f(()=>import("./index.html-4d6212fb.js"),[]).then(({data:e})=>e),"v-9dd2bf36":()=>f(()=>import("./index.html-261a0083.js"),[]).then(({data:e})=>e),"v-88fbbbe4":()=>f(()=>import("./index.html-cca717eb.js"),[]).then(({data:e})=>e),"v-0633ee0d":()=>f(()=>import("./index.html-780f0a5f.js"),[]).then(({data:e})=>e),"v-9a498f0c":()=>f(()=>import("./index.html-3d88f099.js"),[]).then(({data:e})=>e),"v-3fd38ebe":()=>f(()=>import("./index.html-6117a249.js"),[]).then(({data:e})=>e),"v-5b721b4a":()=>f(()=>import("./index.html-199319a5.js"),[]).then(({data:e})=>e),"v-02880d78":()=>f(()=>import("./index.html-d447ba25.js"),[]).then(({data:e})=>e),"v-557d9e12":()=>f(()=>import("./index.html-18b032be.js"),[]).then(({data:e})=>e),"v-519d06c2":()=>f(()=>import("./index.html-c7639322.js"),[]).then(({data:e})=>e),"v-214de1b6":()=>f(()=>import("./index.html-71f7650d.js"),[]).then(({data:e})=>e),"v-c37cff42":()=>f(()=>import("./index.html-715334a6.js"),[]).then(({data:e})=>e),"v-32cc68a3":()=>f(()=>import("./index.html-f1da9706.js"),[]).then(({data:e})=>e),"v-657b4fa8":()=>f(()=>import("./index.html-c6106121.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>f(()=>import("./index.html-1e1044b2.js"),[]).then(({data:e})=>e),"v-744d024e":()=>f(()=>import("./index.html-2e1bbfe0.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>f(()=>import("./index.html-32b452ef.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>f(()=>import("./index.html-72ecb2a5.js"),[]).then(({data:e})=>e),"v-01560935":()=>f(()=>import("./index.html-371ab9bd.js"),[]).then(({data:e})=>e),"v-60649a06":()=>f(()=>import("./index.html-ee388aa7.js"),[]).then(({data:e})=>e),"v-b313b7ce":()=>f(()=>import("./index.html-548d4fbc.js"),[]).then(({data:e})=>e),"v-506407f4":()=>f(()=>import("./index.html-270884f7.js"),[]).then(({data:e})=>e),"v-37a8c5a0":()=>f(()=>import("./index.html-d7f7154f.js"),[]).then(({data:e})=>e),"v-0379cba1":()=>f(()=>import("./index.html-0969c194.js"),[]).then(({data:e})=>e),"v-65ee64e3":()=>f(()=>import("./index.html-daf8e465.js"),[]).then(({data:e})=>e),"v-5d93e6df":()=>f(()=>import("./index.html-40fe4fc1.js"),[]).then(({data:e})=>e),"v-06be9332":()=>f(()=>import("./index.html-c01f49a5.js"),[]).then(({data:e})=>e),"v-87044d8a":()=>f(()=>import("./index.html-3b3f5637.js"),[]).then(({data:e})=>e),"v-f56a87fe":()=>f(()=>import("./index.html-c10f004b.js"),[]).then(({data:e})=>e),"v-6f232062":()=>f(()=>import("./index.html-7c29333f.js"),[]).then(({data:e})=>e),"v-4880d88d":()=>f(()=>import("./index.html-19bce6c8.js"),[]).then(({data:e})=>e),"v-21f6c076":()=>f(()=>import("./index.html-26d50eca.js"),[]).then(({data:e})=>e),"v-e9c7e408":()=>f(()=>import("./index.html-fbcb3f86.js"),[]).then(({data:e})=>e),"v-6106c001":()=>f(()=>import("./index.html-30ee18fd.js"),[]).then(({data:e})=>e),"v-27fb5a12":()=>f(()=>import("./index.html-8edd6edb.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>f(()=>import("./index.html-31922929.js"),[]).then(({data:e})=>e),"v-4f7b1987":()=>f(()=>import("./index.html-796bfce3.js"),[]).then(({data:e})=>e),"v-ae84bf22":()=>f(()=>import("./index.html-652b5245.js"),[]).then(({data:e})=>e),"v-72c4ce0d":()=>f(()=>import("./index.html-7c7d99fe.js"),[]).then(({data:e})=>e),"v-3a8afeec":()=>f(()=>import("./index.html-8a1795ab.js"),[]).then(({data:e})=>e),"v-14145d44":()=>f(()=>import("./index.html-cfc3817d.js"),[]).then(({data:e})=>e),"v-2894de8a":()=>f(()=>import("./index.html-648bc8a8.js"),[]).then(({data:e})=>e),"v-209ce691":()=>f(()=>import("./index.html-f40a1c63.js"),[]).then(({data:e})=>e),"v-742fbe9b":()=>f(()=>import("./index.html-1bac15ee.js"),[]).then(({data:e})=>e),"v-4f178b9c":()=>f(()=>import("./index.html-26abe9e6.js"),[]).then(({data:e})=>e),"v-b6d14f14":()=>f(()=>import("./index.html-5dc42ad9.js"),[]).then(({data:e})=>e),"v-3d183459":()=>f(()=>import("./index.html-f89a03a9.js"),[]).then(({data:e})=>e),"v-36899bd6":()=>f(()=>import("./index.html-dd8cb354.js"),[]).then(({data:e})=>e),"v-49f5e4d4":()=>f(()=>import("./index.html-7b40ec51.js"),[]).then(({data:e})=>e),"v-0d1f4c3c":()=>f(()=>import("./index.html-4fd70df8.js"),[]).then(({data:e})=>e),"v-5831b135":()=>f(()=>import("./index.html-19d7cc65.js"),[]).then(({data:e})=>e),"v-14748cc9":()=>f(()=>import("./index.html-83eb5f40.js"),[]).then(({data:e})=>e),"v-5bd10ded":()=>f(()=>import("./index.html-cbe0bab9.js"),[]).then(({data:e})=>e),"v-b30dba08":()=>f(()=>import("./index.html-8dcb3a98.js"),[]).then(({data:e})=>e),"v-65f4ef02":()=>f(()=>import("./index.html-c02d8530.js"),[]).then(({data:e})=>e),"v-2e8071d8":()=>f(()=>import("./index.html-618d94a6.js"),[]).then(({data:e})=>e),"v-65f5a886":()=>f(()=>import("./index.html-086bbf97.js"),[]).then(({data:e})=>e),"v-28c41ed6":()=>f(()=>import("./index.html-66022c8c.js"),[]).then(({data:e})=>e),"v-3d1848d0":()=>f(()=>import("./index.html-1ae0db6c.js"),[]).then(({data:e})=>e),"v-0da0c1c5":()=>f(()=>import("./index.html-da988511.js"),[]).then(({data:e})=>e),"v-08073caa":()=>f(()=>import("./index.html-2f757efc.js"),[]).then(({data:e})=>e),"v-3b261482":()=>f(()=>import("./index.html-2e57ddb2.js"),[]).then(({data:e})=>e),"v-0667aa78":()=>f(()=>import("./index.html-7f1f7188.js"),[]).then(({data:e})=>e),"v-2671299e":()=>f(()=>import("./index.html-a5d95c38.js"),[]).then(({data:e})=>e),"v-424a813a":()=>f(()=>import("./index.html-01bd39ee.js"),[]).then(({data:e})=>e),"v-2bce0156":()=>f(()=>import("./index.html-037fc9a5.js"),[]).then(({data:e})=>e),"v-573729ca":()=>f(()=>import("./index.html-8e24cb1e.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>f(()=>import("./index.html-894f559a.js"),[]).then(({data:e})=>e),"v-5decfa84":()=>f(()=>import("./index.html-4f2a8a3a.js"),[]).then(({data:e})=>e),"v-6ebee387":()=>f(()=>import("./index.html-2d2a48f2.js"),[]).then(({data:e})=>e),"v-437f3f0b":()=>f(()=>import("./index.html-6cfcdef7.js"),[]).then(({data:e})=>e),"v-07f91f24":()=>f(()=>import("./index.html-a438395b.js"),[]).then(({data:e})=>e),"v-8d9af0ca":()=>f(()=>import("./index.html-113e6b25.js"),[]).then(({data:e})=>e),"v-50c2828c":()=>f(()=>import("./index.html-7910d7cf.js"),[]).then(({data:e})=>e),"v-77e741be":()=>f(()=>import("./index.html-644ca61e.js"),[]).then(({data:e})=>e),"v-20b8e9fc":()=>f(()=>import("./index.html-6ba6fa31.js"),[]).then(({data:e})=>e),"v-5fad3f72":()=>f(()=>import("./index.html-265c3c73.js"),[]).then(({data:e})=>e),"v-68f34d54":()=>f(()=>import("./index.html-a4a83aea.js"),[]).then(({data:e})=>e),"v-721d1794":()=>f(()=>import("./index.html-8e3d413a.js"),[]).then(({data:e})=>e),"v-b30dc3f6":()=>f(()=>import("./index.html-04c04872.js"),[]).then(({data:e})=>e),"v-b3160ccc":()=>f(()=>import("./index.html-21f5d79c.js"),[]).then(({data:e})=>e),"v-0ec4cde8":()=>f(()=>import("./index.html-0861c688.js"),[]).then(({data:e})=>e),"v-075c6c62":()=>f(()=>import("./index.html-6cb1772c.js"),[]).then(({data:e})=>e),"v-6943976d":()=>f(()=>import("./index.html-f05ee4bf.js"),[]).then(({data:e})=>e),"v-31c4e89d":()=>f(()=>import("./index.html-c902c523.js"),[]).then(({data:e})=>e),"v-9e0b104a":()=>f(()=>import("./index.html-7b8a1bed.js"),[]).then(({data:e})=>e),"v-3bced2c4":()=>f(()=>import("./index.html-7b1b1e8a.js"),[]).then(({data:e})=>e),"v-2ab8a0f6":()=>f(()=>import("./index.html-9b5e92dc.js"),[]).then(({data:e})=>e)},_d=JSON.parse(`{"base":"/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"preconnect","href":"https://fonts.googleapis.com"}],["link",{"rel":"preconnect","href":"https://fonts.gstatic.com","crossorigin":""}],["link",{"href":"https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;700&display=swap","rel":"stylesheet"}],["link",{"rel":"icon","href":"/logo.png"}]],"locales":{"/":{"lang":"zh-CN","title":"Yaien Blog","description":"一位靓仔的博客"},"/en/":{"lang":"en-US","title":"Yaien Blog","description":"A pretty guy's blog"}}}`);var bd=([e,t,n])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,n]),yd=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=bd(r);t.has(o)||(t.add(o),n.push(r))}),n},Ed=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,Ld=e=>e.startsWith("ftp://"),on=e=>/^(https?:)?\/\//.test(e),Td=/.md((\?|#).*)?$/,er=(e,t="/")=>!!(on(e)||Ld(e)||e.startsWith("/")&&!e.startsWith(t)&&!Td.test(e)),Ss=e=>/^mailto:/.test(e),Ad=e=>/^tel:/.test(e),so=e=>Object.prototype.toString.call(e)==="[object Object]",Da=e=>e[e.length-1]==="/"?e.slice(0,-1):e,xs=e=>e[0]==="/"?e.slice(1):e,Id=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const a=o.split("/").length-r.split("/").length;return a!==0?a:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"};const Cs={"v-8daa1a0e":_(()=>f(()=>import("./index.html-d5282ff8.js"),[])),"v-fd1808c0":_(()=>f(()=>import("./friend.html-741bf052.js"),[])),"v-2d175b02":_(()=>f(()=>import("./hobby.html-e4d044b2.js"),[])),"v-513bdf66":_(()=>f(()=>import("./me.html-b6f2479a.js"),[])),"v-0c86f055":_(()=>f(()=>import("./update.html-a4f6157b.js"),[])),"v-2d0a870d":_(()=>f(()=>import("./index.html-8aac1f45.js"),[])),"v-5aa3d8ba":_(()=>f(()=>import("./intro.html-3701b0ed.js"),[])),"v-ed1de9c6":_(()=>f(()=>import("./2305090935.html-5753cc3a.js"),[])),"v-48e62a82":_(()=>f(()=>import("./index.html-af94dd98.js"),[])),"v-79efb2b0":_(()=>f(()=>import("./2301101203.html-fd6bbc11.js"),[])),"v-7ba48b4f":_(()=>f(()=>import("./2301101204.html-d8c173f0.js"),[])),"v-1dbb5152":_(()=>f(()=>import("./2302201400.html-1488f35d.js"),[])),"v-1f7029f1":_(()=>f(()=>import("./2302201401.html-ef86aa7a.js"),[])),"v-248eb3ce":_(()=>f(()=>import("./2302201404.html-7d4e0f66.js"),[])),"v-4468c549":_(()=>f(()=>import("./2305081011.html-652250d9.js"),[])),"v-7763b6e7":_(()=>f(()=>import("./2305081101.html-46a00fec.js"),[])),"v-7febf202":_(()=>f(()=>import("./2305081106.html-69cd6721.js"),[])),"v-aad5ccee":_(()=>f(()=>import("./2305081110.html-fdddce6e.js"),[])),"v-a4026a72":_(()=>f(()=>import("./2305081112.html-dab042fb.js"),[])),"v-1ad96fc8":_(()=>f(()=>import("./2305090946.html-4384696a.js"),[])),"v-f0b271fa":_(()=>f(()=>import("./2307051931.html-30066e1f.js"),[])),"v-59c0c6b8":_(()=>f(()=>import("./2307062006.html-9e3117c4.js"),[])),"v-249319d7":_(()=>f(()=>import("./2301101200.html-564885be.js"),[])),"v-0115d5da":_(()=>f(()=>import("./2305090929.html-da8025a5.js"),[])),"v-64c61a30":_(()=>f(()=>import("./index.html-3fa35adf.js"),[])),"v-db09d350":_(()=>f(()=>import("./index.html-6dc332ee.js"),[])),"v-3fc52a21":_(()=>f(()=>import("./2305081011.html-c5f5302c.js"),[])),"v-72c01bbf":_(()=>f(()=>import("./2305081101.html-bbaa39a8.js"),[])),"v-b41d033e":_(()=>f(()=>import("./2305081110.html-8dd46319.js"),[])),"v-460112e6":_(()=>f(()=>import("./2305081106.html-72a246ba.js"),[])),"v-6494b51e":_(()=>f(()=>import("./2305081112.html-926b8bac.js"),[])),"v-0a4a23d6":_(()=>f(()=>import("./2305090946.html-5beac4ec.js"),[])),"v-1452e9f3":_(()=>f(()=>import("./2304010120.html-10394f60.js"),[])),"v-0ba8fdf8":_(()=>f(()=>import("./2305091732.html-9b499596.js"),[])),"v-04d59b7c":_(()=>f(()=>import("./2305091734.html-93fb7a8f.js"),[])),"v-00fee380":_(()=>f(()=>import("./2305091736.html-bbffee07.js"),[])),"v-2ba80b07":_(()=>f(()=>import("./2305091740.html-9f9bd2cb.js"),[])),"v-327b6d83":_(()=>f(()=>import("./2305091744.html-21ea524d.js"),[])),"v-1aa97360":_(()=>f(()=>import("./2312220952.html-62da4e4c.js"),[])),"v-47b60065":_(()=>f(()=>import("./2312280935.html-ab7e085b.js"),[])),"v-fbc34060":_(()=>f(()=>import("./2312291000.html-6241ad44.js"),[])),"v-78811366":_(()=>f(()=>import("./2312291432.html-dfb22bda.js"),[])),"v-0b555a5d":_(()=>f(()=>import("./1911011300.html-0f56ebf0.js"),[])),"v-305fe098":_(()=>f(()=>import("./2305052159.html-6918d3f2.js"),[])),"v-d0e2c786":_(()=>f(()=>import("./2305111000.html-37cd3213.js"),[])),"v-c5b336ca":_(()=>f(()=>import("./2311071055.html-fff34505.js"),[])),"v-91f8f38c":_(()=>f(()=>import("./2311081011.html-c9392a00.js"),[])),"v-10b58660":_(()=>f(()=>import("./2311081109.html-5eebe14d.js"),[])),"v-7eeef733":_(()=>f(()=>import("./2311091006.html-6e892cea.js"),[])),"v-3a6c11b6":_(()=>f(()=>import("./2311221518.html-c7b06941.js"),[])),"v-4b158648":_(()=>f(()=>import("./2403021055.html-4582e424.js"),[])),"v-366969d6":_(()=>f(()=>import("./2403031236.html-154d9a90.js"),[])),"v-64b533de":_(()=>f(()=>import("./2403031505.html-2eedce2a.js"),[])),"v-5a83f4b7":_(()=>f(()=>import("./2403032005.html-cabf1633.js"),[])),"v-a7a8f066":_(()=>f(()=>import("./2305140023.html-535c91ba.js"),[])),"v-12216b09":_(()=>f(()=>import("./2305140203.html-612f47fa.js"),[])),"v-173ff4e6":_(()=>f(()=>import("./2305140206.html-bf63ad54.js"),[])),"v-82100b6c":_(()=>f(()=>import("./2305172157.html-46e73c5d.js"),[])),"v-73716104":_(()=>f(()=>import("./2305191622.html-ffaebf4d.js"),[])),"v-3af4619e":_(()=>f(()=>import("./2305232045.html-7551cbd9.js"),[])),"v-e97c7d16":_(()=>f(()=>import("./2305110857.html-f140f1fd.js"),[])),"v-da7165c6":_(()=>f(()=>import("./2401171030.html-7d4132fe.js"),[])),"v-01034d18":_(()=>f(()=>import("./1910011300.html-b91fce53.js"),[])),"v-01333213":_(()=>f(()=>import("./1910011301.html-2a29a21a.js"),[])),"v-02e80ab2":_(()=>f(()=>import("./1910011302.html-13fdb17f.js"),[])),"v-049ce351":_(()=>f(()=>import("./1910011303.html-5556c3cb.js"),[])),"v-0651bbf0":_(()=>f(()=>import("./1910011304.html-14b3f0b3.js"),[])),"v-113cc96c":_(()=>f(()=>import("./2305011002.html-8fc17830.js"),[])),"v-b8674064":_(()=>f(()=>import("./2305012001.html-f8c1b846.js"),[])),"v-753dfaa7":_(()=>f(()=>import("./2305020500.html-e661241b.js"),[])),"v-76f2d346":_(()=>f(()=>import("./2305020501.html-ec79daf8.js"),[])),"v-1ae8d368":_(()=>f(()=>import("./2305032100.html-eda76f6c.js"),[])),"v-06496550":_(()=>f(()=>import("./2101202010.html-67358898.js"),[])),"v-07fe3def":_(()=>f(()=>import("./2101202011.html-03acbdf1.js"),[])),"v-09b3168e":_(()=>f(()=>import("./2101202012.html-bff452d5.js"),[])),"v-4f779f12":_(()=>f(()=>import("./2304021002.html-836cddaf.js"),[])),"v-26c13b6a":_(()=>f(()=>import("./2306210953.html-01a62948.js"),[])),"v-3b468bb2":_(()=>f(()=>import("./2306211023.html-ef38ff55.js"),[])),"v-ed30c3f6":_(()=>f(()=>import("./2102202010.html-63e2a662.js"),[])),"v-e9c712b8":_(()=>f(()=>import("./2102202011.html-edc165f3.js"),[])),"v-e65d617a":_(()=>f(()=>import("./2102202012.html-5b6457df.js"),[])),"v-55cc2415":_(()=>f(()=>import("./2305252031.html-3a3e12e9.js"),[])),"v-49daa846":_(()=>f(()=>import("./2305281438.html-da0bbf09.js"),[])),"v-4ee32d34":_(()=>f(()=>import("./2305281702.html-a504b9bb.js"),[])),"v-15eaf018":_(()=>f(()=>import("./2305312110.html-c5be0574.js"),[])),"v-3b8d5cd7":_(()=>f(()=>import("./2305312243.html-c14a6223.js"),[])),"v-ea9ab6de":_(()=>f(()=>import("./2306011640.html-f2812653.js"),[])),"v-0dc9bed3":_(()=>f(()=>import("./2306021120.html-04e95df3.js"),[])),"v-53a68847":_(()=>f(()=>import("./2306021924.html-b63de92c.js"),[])),"v-ba51a5ea":_(()=>f(()=>import("./2306041045.html-52f54902.js"),[])),"v-730879e4":_(()=>f(()=>import("./2306042021.html-f9ce8db2.js"),[])),"v-24957b89":_(()=>f(()=>import("./2306051101.html-035bba35.js"),[])),"v-1b2e8a6c":_(()=>f(()=>import("./2306052321.html-b65cca12.js"),[])),"v-e12a9278":_(()=>f(()=>import("./2312130940.html-b7d420f1.js"),[])),"v-783d7264":_(()=>f(()=>import("./2312141621.html-d5edb34f.js"),[])),"v-3706649a":_(()=>f(()=>import("./404.html-4b85a0f5.js"),[])),"v-74bc627b":_(()=>f(()=>import("./index.html-e806ccd2.js"),[])),"v-76bd8bc4":_(()=>f(()=>import("./index.html-c544d31c.js"),[])),"v-15054f24":_(()=>f(()=>import("./index.html-139c0752.js"),[])),"v-8428717a":_(()=>f(()=>import("./index.html-44013f94.js"),[])),"v-3dbf9980":_(()=>f(()=>import("./index.html-278160ed.js"),[])),"v-7fe15663":_(()=>f(()=>import("./index.html-3dd01ab6.js"),[])),"v-a945652a":_(()=>f(()=>import("./index.html-58ed29e7.js"),[])),"v-65b85824":_(()=>f(()=>import("./index.html-48e53762.js"),[])),"v-e2850a42":_(()=>f(()=>import("./index.html-40b038f9.js"),[])),"v-19d7a44e":_(()=>f(()=>import("./index.html-013e7589.js"),[])),"v-4857a26e":_(()=>f(()=>import("./index.html-55ed2d56.js"),[])),"v-1b5ca3be":_(()=>f(()=>import("./index.html-aa6f926f.js"),[])),"v-34d631cd":_(()=>f(()=>import("./index.html-e20e1302.js"),[])),"v-115f5fd1":_(()=>f(()=>import("./index.html-d60f96c1.js"),[])),"v-716fffd6":_(()=>f(()=>import("./index.html-e030af3a.js"),[])),"v-e025c652":_(()=>f(()=>import("./index.html-8f405e82.js"),[])),"v-586621a9":_(()=>f(()=>import("./index.html-cf04d22a.js"),[])),"v-6a12e6a2":_(()=>f(()=>import("./index.html-752cb670.js"),[])),"v-3033cd1e":_(()=>f(()=>import("./index.html-4416aa74.js"),[])),"v-5c743294":_(()=>f(()=>import("./index.html-d267ecb7.js"),[])),"v-7c89fed2":_(()=>f(()=>import("./index.html-90dd2fc6.js"),[])),"v-65f4a756":_(()=>f(()=>import("./index.html-32b3b73b.js"),[])),"v-7a09c3ca":_(()=>f(()=>import("./index.html-b6211302.js"),[])),"v-2a2b904c":_(()=>f(()=>import("./index.html-59c688cd.js"),[])),"v-9dd2bf36":_(()=>f(()=>import("./index.html-ea30e109.js"),[])),"v-88fbbbe4":_(()=>f(()=>import("./index.html-891759b6.js"),[])),"v-0633ee0d":_(()=>f(()=>import("./index.html-2900fd6d.js"),[])),"v-9a498f0c":_(()=>f(()=>import("./index.html-da898e57.js"),[])),"v-3fd38ebe":_(()=>f(()=>import("./index.html-b1681d44.js"),[])),"v-5b721b4a":_(()=>f(()=>import("./index.html-ec98ddf6.js"),[])),"v-02880d78":_(()=>f(()=>import("./index.html-a326038d.js"),[])),"v-557d9e12":_(()=>f(()=>import("./index.html-d53f4c2d.js"),[])),"v-519d06c2":_(()=>f(()=>import("./index.html-3d8df219.js"),[])),"v-214de1b6":_(()=>f(()=>import("./index.html-7e2d0ee5.js"),[])),"v-c37cff42":_(()=>f(()=>import("./index.html-6f8c8f56.js"),[])),"v-32cc68a3":_(()=>f(()=>import("./index.html-06d1eb22.js"),[])),"v-657b4fa8":_(()=>f(()=>import("./index.html-b1355071.js"),[])),"v-5bc93818":_(()=>f(()=>import("./index.html-57d9bf0e.js"),[])),"v-744d024e":_(()=>f(()=>import("./index.html-b8eb1f32.js"),[])),"v-e52c881c":_(()=>f(()=>import("./index.html-6e9b5913.js"),[])),"v-154dc4c4":_(()=>f(()=>import("./index.html-165c0158.js"),[])),"v-01560935":_(()=>f(()=>import("./index.html-f2c796cd.js"),[])),"v-60649a06":_(()=>f(()=>import("./index.html-5ff6fe2a.js"),[])),"v-b313b7ce":_(()=>f(()=>import("./index.html-3e2a279f.js"),[])),"v-506407f4":_(()=>f(()=>import("./index.html-6c781776.js"),[])),"v-37a8c5a0":_(()=>f(()=>import("./index.html-2b491aea.js"),[])),"v-0379cba1":_(()=>f(()=>import("./index.html-d1cd5458.js"),[])),"v-65ee64e3":_(()=>f(()=>import("./index.html-de623115.js"),[])),"v-5d93e6df":_(()=>f(()=>import("./index.html-9c2578f6.js"),[])),"v-06be9332":_(()=>f(()=>import("./index.html-9c347168.js"),[])),"v-87044d8a":_(()=>f(()=>import("./index.html-fd89aef8.js"),[])),"v-f56a87fe":_(()=>f(()=>import("./index.html-a9e1d4ba.js"),[])),"v-6f232062":_(()=>f(()=>import("./index.html-8f91d0c9.js"),[])),"v-4880d88d":_(()=>f(()=>import("./index.html-a89ff6ed.js"),[])),"v-21f6c076":_(()=>f(()=>import("./index.html-f73ecfb4.js"),[])),"v-e9c7e408":_(()=>f(()=>import("./index.html-e4b3035d.js"),[])),"v-6106c001":_(()=>f(()=>import("./index.html-7cbabdae.js"),[])),"v-27fb5a12":_(()=>f(()=>import("./index.html-ad37fdd7.js"),[])),"v-211f44ee":_(()=>f(()=>import("./index.html-ba4ac173.js"),[])),"v-4f7b1987":_(()=>f(()=>import("./index.html-16a33c6b.js"),[])),"v-ae84bf22":_(()=>f(()=>import("./index.html-30cc402b.js"),[])),"v-72c4ce0d":_(()=>f(()=>import("./index.html-25ffa926.js"),[])),"v-3a8afeec":_(()=>f(()=>import("./index.html-e6b6057d.js"),[])),"v-14145d44":_(()=>f(()=>import("./index.html-9d8ae846.js"),[])),"v-2894de8a":_(()=>f(()=>import("./index.html-a544815b.js"),[])),"v-209ce691":_(()=>f(()=>import("./index.html-6663db00.js"),[])),"v-742fbe9b":_(()=>f(()=>import("./index.html-65e9ca5c.js"),[])),"v-4f178b9c":_(()=>f(()=>import("./index.html-999fe6e7.js"),[])),"v-b6d14f14":_(()=>f(()=>import("./index.html-597f253c.js"),[])),"v-3d183459":_(()=>f(()=>import("./index.html-2f222b05.js"),[])),"v-36899bd6":_(()=>f(()=>import("./index.html-cb81c8ee.js"),[])),"v-49f5e4d4":_(()=>f(()=>import("./index.html-6229fea5.js"),[])),"v-0d1f4c3c":_(()=>f(()=>import("./index.html-b58a21c9.js"),[])),"v-5831b135":_(()=>f(()=>import("./index.html-6622e199.js"),[])),"v-14748cc9":_(()=>f(()=>import("./index.html-7d4bebaa.js"),[])),"v-5bd10ded":_(()=>f(()=>import("./index.html-8a2b8c18.js"),[])),"v-b30dba08":_(()=>f(()=>import("./index.html-0cb0f3a4.js"),[])),"v-65f4ef02":_(()=>f(()=>import("./index.html-5fddaaec.js"),[])),"v-2e8071d8":_(()=>f(()=>import("./index.html-61511692.js"),[])),"v-65f5a886":_(()=>f(()=>import("./index.html-c7522be6.js"),[])),"v-28c41ed6":_(()=>f(()=>import("./index.html-bec33a9f.js"),[])),"v-3d1848d0":_(()=>f(()=>import("./index.html-6d9f98a2.js"),[])),"v-0da0c1c5":_(()=>f(()=>import("./index.html-19e06afe.js"),[])),"v-08073caa":_(()=>f(()=>import("./index.html-6bcb739a.js"),[])),"v-3b261482":_(()=>f(()=>import("./index.html-6dab2702.js"),[])),"v-0667aa78":_(()=>f(()=>import("./index.html-50db02e4.js"),[])),"v-2671299e":_(()=>f(()=>import("./index.html-efd678f8.js"),[])),"v-424a813a":_(()=>f(()=>import("./index.html-c029ebd5.js"),[])),"v-2bce0156":_(()=>f(()=>import("./index.html-6f663488.js"),[])),"v-573729ca":_(()=>f(()=>import("./index.html-39f0791c.js"),[])),"v-1bee38ca":_(()=>f(()=>import("./index.html-f1467ce1.js"),[])),"v-5decfa84":_(()=>f(()=>import("./index.html-6f38caf8.js"),[])),"v-6ebee387":_(()=>f(()=>import("./index.html-5a95e16e.js"),[])),"v-437f3f0b":_(()=>f(()=>import("./index.html-8f9d8ed5.js"),[])),"v-07f91f24":_(()=>f(()=>import("./index.html-fe031c38.js"),[])),"v-8d9af0ca":_(()=>f(()=>import("./index.html-9ed6285d.js"),[])),"v-50c2828c":_(()=>f(()=>import("./index.html-6969f8b1.js"),[])),"v-77e741be":_(()=>f(()=>import("./index.html-a18de994.js"),[])),"v-20b8e9fc":_(()=>f(()=>import("./index.html-b98a09c4.js"),[])),"v-5fad3f72":_(()=>f(()=>import("./index.html-3caf49bf.js"),[])),"v-68f34d54":_(()=>f(()=>import("./index.html-c553b57a.js"),[])),"v-721d1794":_(()=>f(()=>import("./index.html-858cdc31.js"),[])),"v-b30dc3f6":_(()=>f(()=>import("./index.html-90171d91.js"),[])),"v-b3160ccc":_(()=>f(()=>import("./index.html-51191316.js"),[])),"v-0ec4cde8":_(()=>f(()=>import("./index.html-62260527.js"),[])),"v-075c6c62":_(()=>f(()=>import("./index.html-3ab4778c.js"),[])),"v-6943976d":_(()=>f(()=>import("./index.html-ce1e3055.js"),[])),"v-31c4e89d":_(()=>f(()=>import("./index.html-335f0eec.js"),[])),"v-9e0b104a":_(()=>f(()=>import("./index.html-8ff98832.js"),[])),"v-3bced2c4":_(()=>f(()=>import("./index.html-862e8c5c.js"),[])),"v-2ab8a0f6":_(()=>f(()=>import("./index.html-a386ae34.js"),[]))};var wd=Symbol(""),Vs=Symbol(""),kd=Ut({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),ie=()=>{const e=he(Vs);if(!e)throw new Error("pageData() is called without provider.");return e},Ms=Symbol(""),Ee=()=>{const e=he(Ms);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},Bs=Symbol(""),Pd=()=>{const e=he(Bs);if(!e)throw new Error("usePageHead() is called without provider.");return e},Od=Symbol(""),$s=Symbol(""),co=()=>{const e=he($s);if(!e)throw new Error("usePageLang() is called without provider.");return e},Ns=Symbol(""),Rd=()=>{const e=he(Ns);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Dd=Y(gd),Sa=Symbol(""),bt=()=>{const e=he(Sa);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},pn=Y(_d),Hs=()=>pn,Fs=Symbol(""),Rn=()=>{const e=he(Fs);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},Sd=Symbol(""),xd="Layout",Cd="NotFound",Lt=lr({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageData:async e=>{const t=Dd.value[e];return await(t==null?void 0:t())??kd},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const r=pe(t.description)?t.description:n.description,o=[...ee(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return yd(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let n;if(e.path){const r=e.frontmatter.layout;pe(r)?n=r:n=xd}else n=Cd;return t[n]},resolveRouteLocale:(e,t)=>Id(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),pr=B({name:"ClientOnly",setup(e,t){const n=Y(!1);return ye(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),js=B({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ie(),n=L(()=>Cs[e.pageKey||t.value.key]);return()=>n.value?l(n.value):l("div","404 Not Found")}}),Ye=(e={})=>e,Ie=e=>on(e)?e:`/${xs(e)}`;const Vd={};/*! +**/const M1="http://www.w3.org/2000/svg",B1="http://www.w3.org/1998/Math/MathML",Nt=typeof document<"u"?document:null,Pi=Nt&&Nt.createElement("template"),$1={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t==="svg"?Nt.createElementNS(M1,e):t==="mathml"?Nt.createElementNS(B1,e):Nt.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>Nt.createTextNode(e),createComment:e=>Nt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Nt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,a){const i=n?n.previousSibling:t.lastChild;if(o&&(o===a||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===a||!(o=o.nextSibling)););else{Pi.innerHTML=r==="svg"?`${e}`:r==="mathml"?`${e}`:e;const s=Pi.content;if(r==="svg"||r==="mathml"){const c=s.firstChild;for(;c.firstChild;)s.appendChild(c.firstChild);s.removeChild(c)}t.insertBefore(s,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},xt="transition",Vn="animation",Ln=Symbol("_vtc"),wt=(e,{slots:t})=>l(W0,ws(e),t);wt.displayName="Transition";const Is={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},N1=wt.props=De({},ns,Is),Qt=(e,t=[])=>{ee(e)?e.forEach(n=>n(...t)):e&&e(...t)},Oi=e=>e?ee(e)?e.some(t=>t.length>1):e.length>1:!1;function ws(e){const t={};for(const M in e)M in Is||(t[M]=e[M]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:a=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:u=i,appearToClass:d=s,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:v=`${n}-leave-active`,leaveToClass:h=`${n}-leave-to`}=e,b=H1(o),T=b&&b[0],I=b&&b[1],{onBeforeEnter:E,onEnter:k,onEnterCancelled:y,onLeave:P,onLeaveCancelled:$,onBeforeAppear:w=E,onAppear:U=k,onAppearCancelled:H=y}=t,J=(M,te,Pe)=>{Vt(M,te?d:s),Vt(M,te?u:i),Pe&&Pe()},x=(M,te)=>{M._isLeaving=!1,Vt(M,p),Vt(M,h),Vt(M,v),te&&te()},X=M=>(te,Pe)=>{const Oe=M?U:k,Q=()=>J(te,M,Pe);Qt(Oe,[te,Q]),Ri(()=>{Vt(te,M?c:a),Et(te,M?d:s),Oi(Oe)||Di(te,r,T,Q)})};return De(t,{onBeforeEnter(M){Qt(E,[M]),Et(M,a),Et(M,i)},onBeforeAppear(M){Qt(w,[M]),Et(M,c),Et(M,u)},onEnter:X(!1),onAppear:X(!0),onLeave(M,te){M._isLeaving=!0;const Pe=()=>x(M,te);Et(M,p),Ps(),Et(M,v),Ri(()=>{M._isLeaving&&(Vt(M,p),Et(M,h),Oi(P)||Di(M,r,I,Pe))}),Qt(P,[M,Pe])},onEnterCancelled(M){J(M,!1),Qt(y,[M])},onAppearCancelled(M){J(M,!0),Qt(H,[M])},onLeaveCancelled(M){x(M),Qt($,[M])}})}function H1(e){if(e==null)return null;if(ke(e))return[wo(e.enter),wo(e.leave)];{const t=wo(e);return[t,t]}}function wo(e){return Gu(e)}function Et(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Ln]||(e[Ln]=new Set)).add(t)}function Vt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[Ln];n&&(n.delete(t),n.size||(e[Ln]=void 0))}function Ri(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let F1=0;function Di(e,t,n,r){const o=e._endId=++F1,a=()=>{o===e._endId&&r()};if(n)return setTimeout(a,n);const{type:i,timeout:s,propCount:c}=ks(e,t);if(!i)return r();const u=i+"end";let d=0;const p=()=>{e.removeEventListener(u,v),a()},v=h=>{h.target===e&&++d>=c&&p()};setTimeout(()=>{d(n[b]||"").split(", "),o=r(`${xt}Delay`),a=r(`${xt}Duration`),i=Si(o,a),s=r(`${Vn}Delay`),c=r(`${Vn}Duration`),u=Si(s,c);let d=null,p=0,v=0;t===xt?i>0&&(d=xt,p=i,v=a.length):t===Vn?u>0&&(d=Vn,p=u,v=c.length):(p=Math.max(i,u),d=p>0?i>u?xt:Vn:null,v=d?d===xt?a.length:c.length:0);const h=d===xt&&/\b(transform|all)(,|$)/.test(r(`${xt}Property`).toString());return{type:d,timeout:p,propCount:v,hasTransform:h}}function Si(e,t){for(;e.lengthxi(n)+xi(e[r])))}function xi(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Ps(){return document.body.offsetHeight}function j1(e,t,n){const r=e[Ln];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Ci=Symbol("_vod"),z1=Symbol("_vsh"),q1=Symbol(""),U1=/(^|;)\s*display\s*:/;function G1(e,t,n){const r=e.style,o=pe(n);let a=!1;if(n&&!o){if(t)if(pe(t))for(const i of t.split(";")){const s=i.slice(0,i.indexOf(":")).trim();n[s]==null&&Fr(r,s,"")}else for(const i in t)n[i]==null&&Fr(r,i,"");for(const i in n)i==="display"&&(a=!0),Fr(r,i,n[i])}else if(o){if(t!==n){const i=r[q1];i&&(n+=";"+i),r.cssText=n,a=U1.test(n)}}else t&&e.removeAttribute("style");Ci in e&&(e[Ci]=a?r.display:"",e[z1]&&(r.display="none"))}const Vi=/\s*!important$/;function Fr(e,t,n){if(ee(n))n.forEach(r=>Fr(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=W1(e,t);Vi.test(n)?e.setProperty(kn(r),n.replace(Vi,""),"important"):e[r]=n}}const Mi=["Webkit","Moz","ms"],ko={};function W1(e,t){const n=ko[t];if(n)return n;let r=ot(t);if(r!=="filter"&&r in e)return ko[t]=r;r=ir(r);for(let o=0;oPo||(ed.then(()=>Po=0),Po=Date.now());function nd(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;tt(rd(r,n.value),t,5,[r])};return n.value=e,n.attached=td(),n}function rd(e,t){if(ee(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const Hi=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,od=(e,t,n,r,o,a,i,s,c)=>{const u=o==="svg";t==="class"?j1(e,r,u):t==="style"?G1(e,n,r):ar(t)?da(t)||X1(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):ad(e,t,r,u))?Q1(e,t,r,a,i,s,c):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),J1(e,t,r,u))};function ad(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&Hi(t)&&re(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const o=e.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return Hi(t)&&pe(n)?!1:t in e}const Os=new WeakMap,Rs=new WeakMap,Kr=Symbol("_moveCb"),Fi=Symbol("_enterCb"),Ds={name:"TransitionGroup",props:De({},N1,{tag:String,moveClass:String}),setup(e,{slots:t}){const n=On(),r=ts();let o,a;return as(()=>{if(!o.length)return;const i=e.moveClass||`${e.name||"v"}-move`;if(!dd(o[0].el,n.vnode.el,i))return;o.forEach(sd),o.forEach(cd);const s=o.filter(ud);Ps(),s.forEach(c=>{const u=c.el,d=u.style;Et(u,i),d.transform=d.webkitTransform=d.transitionDuration="";const p=u[Kr]=v=>{v&&v.target!==u||(!v||/transform$/.test(v.propertyName))&&(u.removeEventListener("transitionend",p),u[Kr]=null,Vt(u,i))};u.addEventListener("transitionend",p)})}),()=>{const i=ce(e),s=ws(i);let c=i.tag||We;o=a,a=t.default?Ia(t.default()):[];for(let u=0;udelete e.mode;Ds.props;const ld=Ds;function sd(e){const t=e.el;t[Kr]&&t[Kr](),t[Fi]&&t[Fi]()}function cd(e){Rs.set(e,e.el.getBoundingClientRect())}function ud(e){const t=Os.get(e),n=Rs.get(e),r=t.left-n.left,o=t.top-n.top;if(r||o){const a=e.el.style;return a.transform=a.webkitTransform=`translate(${r}px,${o}px)`,a.transitionDuration="0s",e}}function dd(e,t,n){const r=e.cloneNode(),o=e[Ln];o&&o.forEach(s=>{s.split(/\s+/).forEach(c=>c&&r.classList.remove(c))}),n.split(/\s+/).forEach(s=>s&&r.classList.add(s)),r.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(r);const{hasTransform:i}=ks(r);return a.removeChild(r),i}const fd=De({patchProp:od},$1);let Oo,ji=!1;function pd(){return Oo=ji?Oo:b1(fd),ji=!0,Oo}const vd=(...e)=>{const t=pd().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=md(r);if(o)return n(o,!0,hd(o))},t};function hd(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function md(e){return pe(e)?document.querySelector(e):e}const gd={"v-8daa1a0e":()=>f(()=>import("./index.html-a54836e1.js"),[]).then(({data:e})=>e),"v-fd1808c0":()=>f(()=>import("./friend.html-80311cd2.js"),[]).then(({data:e})=>e),"v-2d175b02":()=>f(()=>import("./hobby.html-2d902f7a.js"),[]).then(({data:e})=>e),"v-513bdf66":()=>f(()=>import("./me.html-c71dfce2.js"),[]).then(({data:e})=>e),"v-0c86f055":()=>f(()=>import("./update.html-14b8caef.js"),[]).then(({data:e})=>e),"v-2d0a870d":()=>f(()=>import("./index.html-2785f8b4.js"),[]).then(({data:e})=>e),"v-5aa3d8ba":()=>f(()=>import("./intro.html-283584de.js"),[]).then(({data:e})=>e),"v-ed1de9c6":()=>f(()=>import("./2305090935.html-639f15d9.js"),[]).then(({data:e})=>e),"v-48e62a82":()=>f(()=>import("./index.html-079ca76b.js"),[]).then(({data:e})=>e),"v-79efb2b0":()=>f(()=>import("./2301101203.html-12235a8e.js"),[]).then(({data:e})=>e),"v-7ba48b4f":()=>f(()=>import("./2301101204.html-12a956c7.js"),[]).then(({data:e})=>e),"v-1dbb5152":()=>f(()=>import("./2302201400.html-94557db3.js"),[]).then(({data:e})=>e),"v-1f7029f1":()=>f(()=>import("./2302201401.html-81f9bb22.js"),[]).then(({data:e})=>e),"v-248eb3ce":()=>f(()=>import("./2302201404.html-31e7a726.js"),[]).then(({data:e})=>e),"v-4468c549":()=>f(()=>import("./2305081011.html-699d2aef.js"),[]).then(({data:e})=>e),"v-7763b6e7":()=>f(()=>import("./2305081101.html-1759f57e.js"),[]).then(({data:e})=>e),"v-7febf202":()=>f(()=>import("./2305081106.html-83475179.js"),[]).then(({data:e})=>e),"v-aad5ccee":()=>f(()=>import("./2305081110.html-a3143460.js"),[]).then(({data:e})=>e),"v-a4026a72":()=>f(()=>import("./2305081112.html-eeb2f89f.js"),[]).then(({data:e})=>e),"v-1ad96fc8":()=>f(()=>import("./2305090946.html-c7fed4f9.js"),[]).then(({data:e})=>e),"v-f0b271fa":()=>f(()=>import("./2307051931.html-953e69bb.js"),[]).then(({data:e})=>e),"v-59c0c6b8":()=>f(()=>import("./2307062006.html-13b4fbb5.js"),[]).then(({data:e})=>e),"v-249319d7":()=>f(()=>import("./2301101200.html-3dfbc147.js"),[]).then(({data:e})=>e),"v-0115d5da":()=>f(()=>import("./2305090929.html-da6d9f65.js"),[]).then(({data:e})=>e),"v-64c61a30":()=>f(()=>import("./index.html-2350b047.js"),[]).then(({data:e})=>e),"v-db09d350":()=>f(()=>import("./index.html-4eb83f41.js"),[]).then(({data:e})=>e),"v-3fc52a21":()=>f(()=>import("./2305081011.html-4b54635e.js"),[]).then(({data:e})=>e),"v-72c01bbf":()=>f(()=>import("./2305081101.html-880cd6a8.js"),[]).then(({data:e})=>e),"v-b41d033e":()=>f(()=>import("./2305081110.html-a3aec89e.js"),[]).then(({data:e})=>e),"v-460112e6":()=>f(()=>import("./2305081106.html-5f411d69.js"),[]).then(({data:e})=>e),"v-6494b51e":()=>f(()=>import("./2305081112.html-2c2bf6c6.js"),[]).then(({data:e})=>e),"v-0a4a23d6":()=>f(()=>import("./2305090946.html-51196ee1.js"),[]).then(({data:e})=>e),"v-1452e9f3":()=>f(()=>import("./2304010120.html-46b62f31.js"),[]).then(({data:e})=>e),"v-0ba8fdf8":()=>f(()=>import("./2305091732.html-be6a821a.js"),[]).then(({data:e})=>e),"v-04d59b7c":()=>f(()=>import("./2305091734.html-0e6eef80.js"),[]).then(({data:e})=>e),"v-00fee380":()=>f(()=>import("./2305091736.html-8c380a4b.js"),[]).then(({data:e})=>e),"v-2ba80b07":()=>f(()=>import("./2305091740.html-f7b9f78e.js"),[]).then(({data:e})=>e),"v-327b6d83":()=>f(()=>import("./2305091744.html-e2b447fb.js"),[]).then(({data:e})=>e),"v-1aa97360":()=>f(()=>import("./2312220952.html-16ea3a95.js"),[]).then(({data:e})=>e),"v-47b60065":()=>f(()=>import("./2312280935.html-45b1270f.js"),[]).then(({data:e})=>e),"v-fbc34060":()=>f(()=>import("./2312291000.html-9218ce69.js"),[]).then(({data:e})=>e),"v-78811366":()=>f(()=>import("./2312291432.html-425bb655.js"),[]).then(({data:e})=>e),"v-0b555a5d":()=>f(()=>import("./1911011300.html-ba390a45.js"),[]).then(({data:e})=>e),"v-305fe098":()=>f(()=>import("./2305052159.html-29576ffe.js"),[]).then(({data:e})=>e),"v-d0e2c786":()=>f(()=>import("./2305111000.html-7275fa91.js"),[]).then(({data:e})=>e),"v-c5b336ca":()=>f(()=>import("./2311071055.html-b55ba638.js"),[]).then(({data:e})=>e),"v-91f8f38c":()=>f(()=>import("./2311081011.html-3d7ff885.js"),[]).then(({data:e})=>e),"v-10b58660":()=>f(()=>import("./2311081109.html-b4edd1d4.js"),[]).then(({data:e})=>e),"v-7eeef733":()=>f(()=>import("./2311091006.html-1cb57dcd.js"),[]).then(({data:e})=>e),"v-3a6c11b6":()=>f(()=>import("./2311221518.html-ad437492.js"),[]).then(({data:e})=>e),"v-4b158648":()=>f(()=>import("./2403021055.html-f21c50f4.js"),[]).then(({data:e})=>e),"v-366969d6":()=>f(()=>import("./2403031236.html-d5769672.js"),[]).then(({data:e})=>e),"v-64b533de":()=>f(()=>import("./2403031505.html-2a5a633c.js"),[]).then(({data:e})=>e),"v-5a83f4b7":()=>f(()=>import("./2403032005.html-5a8fc529.js"),[]).then(({data:e})=>e),"v-a7a8f066":()=>f(()=>import("./2305140023.html-aaa7b4f6.js"),[]).then(({data:e})=>e),"v-12216b09":()=>f(()=>import("./2305140203.html-d122f931.js"),[]).then(({data:e})=>e),"v-173ff4e6":()=>f(()=>import("./2305140206.html-9c1ba303.js"),[]).then(({data:e})=>e),"v-82100b6c":()=>f(()=>import("./2305172157.html-ed5c9cb7.js"),[]).then(({data:e})=>e),"v-73716104":()=>f(()=>import("./2305191622.html-443278ce.js"),[]).then(({data:e})=>e),"v-3af4619e":()=>f(()=>import("./2305232045.html-ebef571a.js"),[]).then(({data:e})=>e),"v-e97c7d16":()=>f(()=>import("./2305110857.html-24d27337.js"),[]).then(({data:e})=>e),"v-da7165c6":()=>f(()=>import("./2401171030.html-fc81356c.js"),[]).then(({data:e})=>e),"v-01034d18":()=>f(()=>import("./1910011300.html-b19ba2e7.js"),[]).then(({data:e})=>e),"v-01333213":()=>f(()=>import("./1910011301.html-aa1860e5.js"),[]).then(({data:e})=>e),"v-02e80ab2":()=>f(()=>import("./1910011302.html-32d1a4b4.js"),[]).then(({data:e})=>e),"v-049ce351":()=>f(()=>import("./1910011303.html-41984d55.js"),[]).then(({data:e})=>e),"v-0651bbf0":()=>f(()=>import("./1910011304.html-f8bda883.js"),[]).then(({data:e})=>e),"v-b8674064":()=>f(()=>import("./2305012001.html-1c65085b.js"),[]).then(({data:e})=>e),"v-753dfaa7":()=>f(()=>import("./2305020500.html-a0beaa1e.js"),[]).then(({data:e})=>e),"v-76f2d346":()=>f(()=>import("./2305020501.html-f66fe47a.js"),[]).then(({data:e})=>e),"v-1ae8d368":()=>f(()=>import("./2305032100.html-3e380eec.js"),[]).then(({data:e})=>e),"v-36bed06a":()=>f(()=>import("./2403061142.html-dbc6d88d.js"),[]).then(({data:e})=>e),"v-61e42302":()=>f(()=>import("./2403081801.html-640f260f.js"),[]).then(({data:e})=>e),"v-06496550":()=>f(()=>import("./2101202010.html-419c8446.js"),[]).then(({data:e})=>e),"v-07fe3def":()=>f(()=>import("./2101202011.html-6519750e.js"),[]).then(({data:e})=>e),"v-09b3168e":()=>f(()=>import("./2101202012.html-2a241835.js"),[]).then(({data:e})=>e),"v-4f779f12":()=>f(()=>import("./2304021002.html-6c07516c.js"),[]).then(({data:e})=>e),"v-26c13b6a":()=>f(()=>import("./2306210953.html-12b841e7.js"),[]).then(({data:e})=>e),"v-3b468bb2":()=>f(()=>import("./2306211023.html-af1f7eaa.js"),[]).then(({data:e})=>e),"v-ed30c3f6":()=>f(()=>import("./2102202010.html-a632e59c.js"),[]).then(({data:e})=>e),"v-e9c712b8":()=>f(()=>import("./2102202011.html-a488edf1.js"),[]).then(({data:e})=>e),"v-e65d617a":()=>f(()=>import("./2102202012.html-2374338c.js"),[]).then(({data:e})=>e),"v-55cc2415":()=>f(()=>import("./2305252031.html-6d81b955.js"),[]).then(({data:e})=>e),"v-49daa846":()=>f(()=>import("./2305281438.html-ed7ab2ff.js"),[]).then(({data:e})=>e),"v-4ee32d34":()=>f(()=>import("./2305281702.html-fe603ffb.js"),[]).then(({data:e})=>e),"v-15eaf018":()=>f(()=>import("./2305312110.html-4f2af84a.js"),[]).then(({data:e})=>e),"v-3b8d5cd7":()=>f(()=>import("./2305312243.html-79934d0d.js"),[]).then(({data:e})=>e),"v-ea9ab6de":()=>f(()=>import("./2306011640.html-95e24e4c.js"),[]).then(({data:e})=>e),"v-0dc9bed3":()=>f(()=>import("./2306021120.html-0c4cd9c4.js"),[]).then(({data:e})=>e),"v-53a68847":()=>f(()=>import("./2306021924.html-55c87dff.js"),[]).then(({data:e})=>e),"v-ba51a5ea":()=>f(()=>import("./2306041045.html-48088652.js"),[]).then(({data:e})=>e),"v-730879e4":()=>f(()=>import("./2306042021.html-b8014719.js"),[]).then(({data:e})=>e),"v-24957b89":()=>f(()=>import("./2306051101.html-d0d6602d.js"),[]).then(({data:e})=>e),"v-1b2e8a6c":()=>f(()=>import("./2306052321.html-ec7a6f43.js"),[]).then(({data:e})=>e),"v-e12a9278":()=>f(()=>import("./2312130940.html-9f37cd57.js"),[]).then(({data:e})=>e),"v-783d7264":()=>f(()=>import("./2312141621.html-ba899b4a.js"),[]).then(({data:e})=>e),"v-3706649a":()=>f(()=>import("./404.html-78a9acc1.js"),[]).then(({data:e})=>e),"v-74bc627b":()=>f(()=>import("./index.html-ac597c67.js"),[]).then(({data:e})=>e),"v-76bd8bc4":()=>f(()=>import("./index.html-1881782d.js"),[]).then(({data:e})=>e),"v-15054f24":()=>f(()=>import("./index.html-2693244f.js"),[]).then(({data:e})=>e),"v-8428717a":()=>f(()=>import("./index.html-bb983df4.js"),[]).then(({data:e})=>e),"v-3dbf9980":()=>f(()=>import("./index.html-c728fdda.js"),[]).then(({data:e})=>e),"v-7fe15663":()=>f(()=>import("./index.html-0e2285d8.js"),[]).then(({data:e})=>e),"v-a945652a":()=>f(()=>import("./index.html-de207251.js"),[]).then(({data:e})=>e),"v-65b85824":()=>f(()=>import("./index.html-1b508e6a.js"),[]).then(({data:e})=>e),"v-e2850a42":()=>f(()=>import("./index.html-ca9a74ca.js"),[]).then(({data:e})=>e),"v-19d7a44e":()=>f(()=>import("./index.html-2bdef7c0.js"),[]).then(({data:e})=>e),"v-4857a26e":()=>f(()=>import("./index.html-0937571b.js"),[]).then(({data:e})=>e),"v-1b5ca3be":()=>f(()=>import("./index.html-0807a1f7.js"),[]).then(({data:e})=>e),"v-34d631cd":()=>f(()=>import("./index.html-a46d5cf2.js"),[]).then(({data:e})=>e),"v-115f5fd1":()=>f(()=>import("./index.html-bf318dcd.js"),[]).then(({data:e})=>e),"v-716fffd6":()=>f(()=>import("./index.html-e2532819.js"),[]).then(({data:e})=>e),"v-e025c652":()=>f(()=>import("./index.html-20105346.js"),[]).then(({data:e})=>e),"v-586621a9":()=>f(()=>import("./index.html-721ab647.js"),[]).then(({data:e})=>e),"v-6a12e6a2":()=>f(()=>import("./index.html-9e4305af.js"),[]).then(({data:e})=>e),"v-3033cd1e":()=>f(()=>import("./index.html-0769bc4b.js"),[]).then(({data:e})=>e),"v-5c743294":()=>f(()=>import("./index.html-f8a821da.js"),[]).then(({data:e})=>e),"v-7c89fed2":()=>f(()=>import("./index.html-c752a5d2.js"),[]).then(({data:e})=>e),"v-65f4a756":()=>f(()=>import("./index.html-af75d85c.js"),[]).then(({data:e})=>e),"v-7a09c3ca":()=>f(()=>import("./index.html-92322d72.js"),[]).then(({data:e})=>e),"v-2a2b904c":()=>f(()=>import("./index.html-4d6212fb.js"),[]).then(({data:e})=>e),"v-9dd2bf36":()=>f(()=>import("./index.html-261a0083.js"),[]).then(({data:e})=>e),"v-88fbbbe4":()=>f(()=>import("./index.html-cca717eb.js"),[]).then(({data:e})=>e),"v-0633ee0d":()=>f(()=>import("./index.html-780f0a5f.js"),[]).then(({data:e})=>e),"v-9a498f0c":()=>f(()=>import("./index.html-3d88f099.js"),[]).then(({data:e})=>e),"v-3fd38ebe":()=>f(()=>import("./index.html-6117a249.js"),[]).then(({data:e})=>e),"v-5b721b4a":()=>f(()=>import("./index.html-199319a5.js"),[]).then(({data:e})=>e),"v-02880d78":()=>f(()=>import("./index.html-d447ba25.js"),[]).then(({data:e})=>e),"v-557d9e12":()=>f(()=>import("./index.html-18b032be.js"),[]).then(({data:e})=>e),"v-519d06c2":()=>f(()=>import("./index.html-c7639322.js"),[]).then(({data:e})=>e),"v-214de1b6":()=>f(()=>import("./index.html-71f7650d.js"),[]).then(({data:e})=>e),"v-c37cff42":()=>f(()=>import("./index.html-715334a6.js"),[]).then(({data:e})=>e),"v-32cc68a3":()=>f(()=>import("./index.html-f1da9706.js"),[]).then(({data:e})=>e),"v-657b4fa8":()=>f(()=>import("./index.html-c6106121.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>f(()=>import("./index.html-1e1044b2.js"),[]).then(({data:e})=>e),"v-744d024e":()=>f(()=>import("./index.html-2e1bbfe0.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>f(()=>import("./index.html-32b452ef.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>f(()=>import("./index.html-72ecb2a5.js"),[]).then(({data:e})=>e),"v-01560935":()=>f(()=>import("./index.html-371ab9bd.js"),[]).then(({data:e})=>e),"v-60649a06":()=>f(()=>import("./index.html-ee388aa7.js"),[]).then(({data:e})=>e),"v-b313b7ce":()=>f(()=>import("./index.html-548d4fbc.js"),[]).then(({data:e})=>e),"v-506407f4":()=>f(()=>import("./index.html-270884f7.js"),[]).then(({data:e})=>e),"v-37a8c5a0":()=>f(()=>import("./index.html-d7f7154f.js"),[]).then(({data:e})=>e),"v-0379cba1":()=>f(()=>import("./index.html-0969c194.js"),[]).then(({data:e})=>e),"v-65ee64e3":()=>f(()=>import("./index.html-daf8e465.js"),[]).then(({data:e})=>e),"v-5d93e6df":()=>f(()=>import("./index.html-40fe4fc1.js"),[]).then(({data:e})=>e),"v-06be9332":()=>f(()=>import("./index.html-c01f49a5.js"),[]).then(({data:e})=>e),"v-87044d8a":()=>f(()=>import("./index.html-3b3f5637.js"),[]).then(({data:e})=>e),"v-f56a87fe":()=>f(()=>import("./index.html-c10f004b.js"),[]).then(({data:e})=>e),"v-6f232062":()=>f(()=>import("./index.html-7c29333f.js"),[]).then(({data:e})=>e),"v-4880d88d":()=>f(()=>import("./index.html-19bce6c8.js"),[]).then(({data:e})=>e),"v-21f6c076":()=>f(()=>import("./index.html-26d50eca.js"),[]).then(({data:e})=>e),"v-e9c7e408":()=>f(()=>import("./index.html-fbcb3f86.js"),[]).then(({data:e})=>e),"v-6106c001":()=>f(()=>import("./index.html-30ee18fd.js"),[]).then(({data:e})=>e),"v-27fb5a12":()=>f(()=>import("./index.html-8edd6edb.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>f(()=>import("./index.html-31922929.js"),[]).then(({data:e})=>e),"v-4f7b1987":()=>f(()=>import("./index.html-796bfce3.js"),[]).then(({data:e})=>e),"v-ae84bf22":()=>f(()=>import("./index.html-652b5245.js"),[]).then(({data:e})=>e),"v-72c4ce0d":()=>f(()=>import("./index.html-7c7d99fe.js"),[]).then(({data:e})=>e),"v-3a8afeec":()=>f(()=>import("./index.html-8a1795ab.js"),[]).then(({data:e})=>e),"v-14145d44":()=>f(()=>import("./index.html-cfc3817d.js"),[]).then(({data:e})=>e),"v-2894de8a":()=>f(()=>import("./index.html-648bc8a8.js"),[]).then(({data:e})=>e),"v-209ce691":()=>f(()=>import("./index.html-f40a1c63.js"),[]).then(({data:e})=>e),"v-742fbe9b":()=>f(()=>import("./index.html-1bac15ee.js"),[]).then(({data:e})=>e),"v-4f178b9c":()=>f(()=>import("./index.html-26abe9e6.js"),[]).then(({data:e})=>e),"v-b6d14f14":()=>f(()=>import("./index.html-5dc42ad9.js"),[]).then(({data:e})=>e),"v-3d183459":()=>f(()=>import("./index.html-f89a03a9.js"),[]).then(({data:e})=>e),"v-36899bd6":()=>f(()=>import("./index.html-dd8cb354.js"),[]).then(({data:e})=>e),"v-49f5e4d4":()=>f(()=>import("./index.html-7b40ec51.js"),[]).then(({data:e})=>e),"v-0d1f4c3c":()=>f(()=>import("./index.html-4fd70df8.js"),[]).then(({data:e})=>e),"v-5831b135":()=>f(()=>import("./index.html-19d7cc65.js"),[]).then(({data:e})=>e),"v-14748cc9":()=>f(()=>import("./index.html-83eb5f40.js"),[]).then(({data:e})=>e),"v-5bd10ded":()=>f(()=>import("./index.html-cbe0bab9.js"),[]).then(({data:e})=>e),"v-b30dba08":()=>f(()=>import("./index.html-8dcb3a98.js"),[]).then(({data:e})=>e),"v-65f4ef02":()=>f(()=>import("./index.html-c02d8530.js"),[]).then(({data:e})=>e),"v-2e8071d8":()=>f(()=>import("./index.html-618d94a6.js"),[]).then(({data:e})=>e),"v-65f5a886":()=>f(()=>import("./index.html-086bbf97.js"),[]).then(({data:e})=>e),"v-28c41ed6":()=>f(()=>import("./index.html-66022c8c.js"),[]).then(({data:e})=>e),"v-3d1848d0":()=>f(()=>import("./index.html-1ae0db6c.js"),[]).then(({data:e})=>e),"v-0da0c1c5":()=>f(()=>import("./index.html-da988511.js"),[]).then(({data:e})=>e),"v-08073caa":()=>f(()=>import("./index.html-2f757efc.js"),[]).then(({data:e})=>e),"v-3b261482":()=>f(()=>import("./index.html-2e57ddb2.js"),[]).then(({data:e})=>e),"v-0667aa78":()=>f(()=>import("./index.html-7f1f7188.js"),[]).then(({data:e})=>e),"v-2671299e":()=>f(()=>import("./index.html-a5d95c38.js"),[]).then(({data:e})=>e),"v-424a813a":()=>f(()=>import("./index.html-01bd39ee.js"),[]).then(({data:e})=>e),"v-2bce0156":()=>f(()=>import("./index.html-037fc9a5.js"),[]).then(({data:e})=>e),"v-573729ca":()=>f(()=>import("./index.html-8e24cb1e.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>f(()=>import("./index.html-894f559a.js"),[]).then(({data:e})=>e),"v-5decfa84":()=>f(()=>import("./index.html-4f2a8a3a.js"),[]).then(({data:e})=>e),"v-6ebee387":()=>f(()=>import("./index.html-2d2a48f2.js"),[]).then(({data:e})=>e),"v-437f3f0b":()=>f(()=>import("./index.html-6cfcdef7.js"),[]).then(({data:e})=>e),"v-26374ab8":()=>f(()=>import("./index.html-2f510971.js"),[]).then(({data:e})=>e),"v-8d9af0ca":()=>f(()=>import("./index.html-113e6b25.js"),[]).then(({data:e})=>e),"v-64c651c8":()=>f(()=>import("./index.html-3b8c93dc.js"),[]).then(({data:e})=>e),"v-77e741be":()=>f(()=>import("./index.html-644ca61e.js"),[]).then(({data:e})=>e),"v-07f91f24":()=>f(()=>import("./index.html-a438395b.js"),[]).then(({data:e})=>e),"v-5fad3f72":()=>f(()=>import("./index.html-265c3c73.js"),[]).then(({data:e})=>e),"v-50c2828c":()=>f(()=>import("./index.html-7910d7cf.js"),[]).then(({data:e})=>e),"v-20b8e9fc":()=>f(()=>import("./index.html-6ba6fa31.js"),[]).then(({data:e})=>e),"v-68f34d54":()=>f(()=>import("./index.html-a4a83aea.js"),[]).then(({data:e})=>e),"v-721d1794":()=>f(()=>import("./index.html-8e3d413a.js"),[]).then(({data:e})=>e),"v-b30dc3f6":()=>f(()=>import("./index.html-04c04872.js"),[]).then(({data:e})=>e),"v-b3160ccc":()=>f(()=>import("./index.html-21f5d79c.js"),[]).then(({data:e})=>e),"v-0ec4cde8":()=>f(()=>import("./index.html-0861c688.js"),[]).then(({data:e})=>e),"v-075c6c62":()=>f(()=>import("./index.html-6cb1772c.js"),[]).then(({data:e})=>e),"v-6943976d":()=>f(()=>import("./index.html-f05ee4bf.js"),[]).then(({data:e})=>e),"v-31c4e89d":()=>f(()=>import("./index.html-c902c523.js"),[]).then(({data:e})=>e),"v-9e0b104a":()=>f(()=>import("./index.html-7b8a1bed.js"),[]).then(({data:e})=>e),"v-3bced2c4":()=>f(()=>import("./index.html-7b1b1e8a.js"),[]).then(({data:e})=>e),"v-2ab8a0f6":()=>f(()=>import("./index.html-9b5e92dc.js"),[]).then(({data:e})=>e)},_d=JSON.parse(`{"base":"/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"preconnect","href":"https://fonts.googleapis.com"}],["link",{"rel":"preconnect","href":"https://fonts.gstatic.com","crossorigin":""}],["link",{"href":"https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;700&display=swap","rel":"stylesheet"}],["link",{"rel":"icon","href":"/logo.png"}]],"locales":{"/":{"lang":"zh-CN","title":"Yaien Blog","description":"一位靓仔的博客"},"/en/":{"lang":"en-US","title":"Yaien Blog","description":"A pretty guy's blog"}}}`);var bd=([e,t,n])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,n]),yd=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=bd(r);t.has(o)||(t.add(o),n.push(r))}),n},Ed=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,Ld=e=>e.startsWith("ftp://"),on=e=>/^(https?:)?\/\//.test(e),Ad=/.md((\?|#).*)?$/,er=(e,t="/")=>!!(on(e)||Ld(e)||e.startsWith("/")&&!e.startsWith(t)&&!Ad.test(e)),Ss=e=>/^mailto:/.test(e),Td=e=>/^tel:/.test(e),so=e=>Object.prototype.toString.call(e)==="[object Object]",Da=e=>e[e.length-1]==="/"?e.slice(0,-1):e,xs=e=>e[0]==="/"?e.slice(1):e,Id=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const a=o.split("/").length-r.split("/").length;return a!==0?a:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"};const Cs={"v-8daa1a0e":_(()=>f(()=>import("./index.html-2137cedf.js"),[])),"v-fd1808c0":_(()=>f(()=>import("./friend.html-3da35bba.js"),[])),"v-2d175b02":_(()=>f(()=>import("./hobby.html-b0beb222.js"),[])),"v-513bdf66":_(()=>f(()=>import("./me.html-70c029c8.js"),[])),"v-0c86f055":_(()=>f(()=>import("./update.html-74cd8c2b.js"),[])),"v-2d0a870d":_(()=>f(()=>import("./index.html-c865a580.js"),[])),"v-5aa3d8ba":_(()=>f(()=>import("./intro.html-ddfba02d.js"),[])),"v-ed1de9c6":_(()=>f(()=>import("./2305090935.html-5350ce3a.js"),[])),"v-48e62a82":_(()=>f(()=>import("./index.html-b4e4caa4.js"),[])),"v-79efb2b0":_(()=>f(()=>import("./2301101203.html-bffe4d93.js"),[])),"v-7ba48b4f":_(()=>f(()=>import("./2301101204.html-e1f3ef94.js"),[])),"v-1dbb5152":_(()=>f(()=>import("./2302201400.html-3f58b59f.js"),[])),"v-1f7029f1":_(()=>f(()=>import("./2302201401.html-08f31c5e.js"),[])),"v-248eb3ce":_(()=>f(()=>import("./2302201404.html-1b00791e.js"),[])),"v-4468c549":_(()=>f(()=>import("./2305081011.html-667135b8.js"),[])),"v-7763b6e7":_(()=>f(()=>import("./2305081101.html-73b58578.js"),[])),"v-7febf202":_(()=>f(()=>import("./2305081106.html-892fddb0.js"),[])),"v-aad5ccee":_(()=>f(()=>import("./2305081110.html-c7821167.js"),[])),"v-a4026a72":_(()=>f(()=>import("./2305081112.html-27835a6c.js"),[])),"v-1ad96fc8":_(()=>f(()=>import("./2305090946.html-ffd4afb2.js"),[])),"v-f0b271fa":_(()=>f(()=>import("./2307051931.html-43434fe3.js"),[])),"v-59c0c6b8":_(()=>f(()=>import("./2307062006.html-214b1769.js"),[])),"v-249319d7":_(()=>f(()=>import("./2301101200.html-794943d8.js"),[])),"v-0115d5da":_(()=>f(()=>import("./2305090929.html-8dbecdea.js"),[])),"v-64c61a30":_(()=>f(()=>import("./index.html-702a3106.js"),[])),"v-db09d350":_(()=>f(()=>import("./index.html-f6996b95.js"),[])),"v-3fc52a21":_(()=>f(()=>import("./2305081011.html-902da75d.js"),[])),"v-72c01bbf":_(()=>f(()=>import("./2305081101.html-9acdefa8.js"),[])),"v-b41d033e":_(()=>f(()=>import("./2305081110.html-453f2c67.js"),[])),"v-460112e6":_(()=>f(()=>import("./2305081106.html-a7a43331.js"),[])),"v-6494b51e":_(()=>f(()=>import("./2305081112.html-17ebbfc0.js"),[])),"v-0a4a23d6":_(()=>f(()=>import("./2305090946.html-92feef10.js"),[])),"v-1452e9f3":_(()=>f(()=>import("./2304010120.html-2b707481.js"),[])),"v-0ba8fdf8":_(()=>f(()=>import("./2305091732.html-16493ce7.js"),[])),"v-04d59b7c":_(()=>f(()=>import("./2305091734.html-47987fdb.js"),[])),"v-00fee380":_(()=>f(()=>import("./2305091736.html-c4b92ec7.js"),[])),"v-2ba80b07":_(()=>f(()=>import("./2305091740.html-d1cf475e.js"),[])),"v-327b6d83":_(()=>f(()=>import("./2305091744.html-97d55f51.js"),[])),"v-1aa97360":_(()=>f(()=>import("./2312220952.html-1f4baa89.js"),[])),"v-47b60065":_(()=>f(()=>import("./2312280935.html-720e7480.js"),[])),"v-fbc34060":_(()=>f(()=>import("./2312291000.html-7d4f645e.js"),[])),"v-78811366":_(()=>f(()=>import("./2312291432.html-d4287519.js"),[])),"v-0b555a5d":_(()=>f(()=>import("./1911011300.html-9196bca3.js"),[])),"v-305fe098":_(()=>f(()=>import("./2305052159.html-de6983c6.js"),[])),"v-d0e2c786":_(()=>f(()=>import("./2305111000.html-726d6584.js"),[])),"v-c5b336ca":_(()=>f(()=>import("./2311071055.html-67906c7c.js"),[])),"v-91f8f38c":_(()=>f(()=>import("./2311081011.html-beebc081.js"),[])),"v-10b58660":_(()=>f(()=>import("./2311081109.html-885b98f4.js"),[])),"v-7eeef733":_(()=>f(()=>import("./2311091006.html-d02a6bfb.js"),[])),"v-3a6c11b6":_(()=>f(()=>import("./2311221518.html-c71bf788.js"),[])),"v-4b158648":_(()=>f(()=>import("./2403021055.html-9afcdbef.js"),[])),"v-366969d6":_(()=>f(()=>import("./2403031236.html-b90ced32.js"),[])),"v-64b533de":_(()=>f(()=>import("./2403031505.html-dcfbb3f8.js"),[])),"v-5a83f4b7":_(()=>f(()=>import("./2403032005.html-1cb9fb80.js"),[])),"v-a7a8f066":_(()=>f(()=>import("./2305140023.html-34d7cfb7.js"),[])),"v-12216b09":_(()=>f(()=>import("./2305140203.html-ee51bbb4.js"),[])),"v-173ff4e6":_(()=>f(()=>import("./2305140206.html-e9afa266.js"),[])),"v-82100b6c":_(()=>f(()=>import("./2305172157.html-fb09971d.js"),[])),"v-73716104":_(()=>f(()=>import("./2305191622.html-c69c5b9c.js"),[])),"v-3af4619e":_(()=>f(()=>import("./2305232045.html-93c9c12d.js"),[])),"v-e97c7d16":_(()=>f(()=>import("./2305110857.html-10721076.js"),[])),"v-da7165c6":_(()=>f(()=>import("./2401171030.html-8dce4f83.js"),[])),"v-01034d18":_(()=>f(()=>import("./1910011300.html-a1fde421.js"),[])),"v-01333213":_(()=>f(()=>import("./1910011301.html-64cc386a.js"),[])),"v-02e80ab2":_(()=>f(()=>import("./1910011302.html-26c408a0.js"),[])),"v-049ce351":_(()=>f(()=>import("./1910011303.html-df8db159.js"),[])),"v-0651bbf0":_(()=>f(()=>import("./1910011304.html-f19eccf5.js"),[])),"v-b8674064":_(()=>f(()=>import("./2305012001.html-9305c46c.js"),[])),"v-753dfaa7":_(()=>f(()=>import("./2305020500.html-3b17715a.js"),[])),"v-76f2d346":_(()=>f(()=>import("./2305020501.html-199e39c0.js"),[])),"v-1ae8d368":_(()=>f(()=>import("./2305032100.html-89e390c8.js"),[])),"v-36bed06a":_(()=>f(()=>import("./2403061142.html-ceb43054.js"),[])),"v-61e42302":_(()=>f(()=>import("./2403081801.html-b3b0e084.js"),[])),"v-06496550":_(()=>f(()=>import("./2101202010.html-7c28455a.js"),[])),"v-07fe3def":_(()=>f(()=>import("./2101202011.html-4bd3357e.js"),[])),"v-09b3168e":_(()=>f(()=>import("./2101202012.html-d6ea40fa.js"),[])),"v-4f779f12":_(()=>f(()=>import("./2304021002.html-da517711.js"),[])),"v-26c13b6a":_(()=>f(()=>import("./2306210953.html-2a1e60b2.js"),[])),"v-3b468bb2":_(()=>f(()=>import("./2306211023.html-2cd08382.js"),[])),"v-ed30c3f6":_(()=>f(()=>import("./2102202010.html-ceaf693a.js"),[])),"v-e9c712b8":_(()=>f(()=>import("./2102202011.html-d7d74653.js"),[])),"v-e65d617a":_(()=>f(()=>import("./2102202012.html-c2e13622.js"),[])),"v-55cc2415":_(()=>f(()=>import("./2305252031.html-78ce716c.js"),[])),"v-49daa846":_(()=>f(()=>import("./2305281438.html-07e8ac89.js"),[])),"v-4ee32d34":_(()=>f(()=>import("./2305281702.html-0d3c8665.js"),[])),"v-15eaf018":_(()=>f(()=>import("./2305312110.html-4f40549f.js"),[])),"v-3b8d5cd7":_(()=>f(()=>import("./2305312243.html-3ff5d044.js"),[])),"v-ea9ab6de":_(()=>f(()=>import("./2306011640.html-ac314c8d.js"),[])),"v-0dc9bed3":_(()=>f(()=>import("./2306021120.html-4b51bdce.js"),[])),"v-53a68847":_(()=>f(()=>import("./2306021924.html-8c927565.js"),[])),"v-ba51a5ea":_(()=>f(()=>import("./2306041045.html-bcdb211e.js"),[])),"v-730879e4":_(()=>f(()=>import("./2306042021.html-b2695927.js"),[])),"v-24957b89":_(()=>f(()=>import("./2306051101.html-2d3adfa7.js"),[])),"v-1b2e8a6c":_(()=>f(()=>import("./2306052321.html-ff8efb4e.js"),[])),"v-e12a9278":_(()=>f(()=>import("./2312130940.html-203d827f.js"),[])),"v-783d7264":_(()=>f(()=>import("./2312141621.html-6b399b4e.js"),[])),"v-3706649a":_(()=>f(()=>import("./404.html-8ce18156.js"),[])),"v-74bc627b":_(()=>f(()=>import("./index.html-e27794d0.js"),[])),"v-76bd8bc4":_(()=>f(()=>import("./index.html-bd3b3724.js"),[])),"v-15054f24":_(()=>f(()=>import("./index.html-287b3ad3.js"),[])),"v-8428717a":_(()=>f(()=>import("./index.html-b7052789.js"),[])),"v-3dbf9980":_(()=>f(()=>import("./index.html-d3cd5a3a.js"),[])),"v-7fe15663":_(()=>f(()=>import("./index.html-af420c02.js"),[])),"v-a945652a":_(()=>f(()=>import("./index.html-d3d5bda5.js"),[])),"v-65b85824":_(()=>f(()=>import("./index.html-fbb8ee40.js"),[])),"v-e2850a42":_(()=>f(()=>import("./index.html-99d52e98.js"),[])),"v-19d7a44e":_(()=>f(()=>import("./index.html-0bf6ed17.js"),[])),"v-4857a26e":_(()=>f(()=>import("./index.html-f0f0b9c9.js"),[])),"v-1b5ca3be":_(()=>f(()=>import("./index.html-a4bc9d27.js"),[])),"v-34d631cd":_(()=>f(()=>import("./index.html-ac828602.js"),[])),"v-115f5fd1":_(()=>f(()=>import("./index.html-2e1d318a.js"),[])),"v-716fffd6":_(()=>f(()=>import("./index.html-35c8c7ba.js"),[])),"v-e025c652":_(()=>f(()=>import("./index.html-67965a53.js"),[])),"v-586621a9":_(()=>f(()=>import("./index.html-02fae00a.js"),[])),"v-6a12e6a2":_(()=>f(()=>import("./index.html-fd787609.js"),[])),"v-3033cd1e":_(()=>f(()=>import("./index.html-307c2c56.js"),[])),"v-5c743294":_(()=>f(()=>import("./index.html-f97ddf9d.js"),[])),"v-7c89fed2":_(()=>f(()=>import("./index.html-c34502db.js"),[])),"v-65f4a756":_(()=>f(()=>import("./index.html-128c30d4.js"),[])),"v-7a09c3ca":_(()=>f(()=>import("./index.html-4f8e791e.js"),[])),"v-2a2b904c":_(()=>f(()=>import("./index.html-8c38fce5.js"),[])),"v-9dd2bf36":_(()=>f(()=>import("./index.html-196a39b3.js"),[])),"v-88fbbbe4":_(()=>f(()=>import("./index.html-1f45f6a9.js"),[])),"v-0633ee0d":_(()=>f(()=>import("./index.html-51f89f01.js"),[])),"v-9a498f0c":_(()=>f(()=>import("./index.html-674b2b83.js"),[])),"v-3fd38ebe":_(()=>f(()=>import("./index.html-2df6a92a.js"),[])),"v-5b721b4a":_(()=>f(()=>import("./index.html-c3d62038.js"),[])),"v-02880d78":_(()=>f(()=>import("./index.html-a4585d58.js"),[])),"v-557d9e12":_(()=>f(()=>import("./index.html-9fd0514e.js"),[])),"v-519d06c2":_(()=>f(()=>import("./index.html-ad8286ac.js"),[])),"v-214de1b6":_(()=>f(()=>import("./index.html-8756cc4d.js"),[])),"v-c37cff42":_(()=>f(()=>import("./index.html-f0ad001a.js"),[])),"v-32cc68a3":_(()=>f(()=>import("./index.html-3f5a891d.js"),[])),"v-657b4fa8":_(()=>f(()=>import("./index.html-bb69aa58.js"),[])),"v-5bc93818":_(()=>f(()=>import("./index.html-c0d34818.js"),[])),"v-744d024e":_(()=>f(()=>import("./index.html-bfd54f12.js"),[])),"v-e52c881c":_(()=>f(()=>import("./index.html-e1130fb9.js"),[])),"v-154dc4c4":_(()=>f(()=>import("./index.html-19072b4f.js"),[])),"v-01560935":_(()=>f(()=>import("./index.html-b4bcc9f2.js"),[])),"v-60649a06":_(()=>f(()=>import("./index.html-b7700f9a.js"),[])),"v-b313b7ce":_(()=>f(()=>import("./index.html-eee582b4.js"),[])),"v-506407f4":_(()=>f(()=>import("./index.html-595541c4.js"),[])),"v-37a8c5a0":_(()=>f(()=>import("./index.html-70ff954c.js"),[])),"v-0379cba1":_(()=>f(()=>import("./index.html-d9e7528a.js"),[])),"v-65ee64e3":_(()=>f(()=>import("./index.html-0d28deb1.js"),[])),"v-5d93e6df":_(()=>f(()=>import("./index.html-477f0894.js"),[])),"v-06be9332":_(()=>f(()=>import("./index.html-7c3502af.js"),[])),"v-87044d8a":_(()=>f(()=>import("./index.html-bab327ad.js"),[])),"v-f56a87fe":_(()=>f(()=>import("./index.html-f9583176.js"),[])),"v-6f232062":_(()=>f(()=>import("./index.html-88dd6423.js"),[])),"v-4880d88d":_(()=>f(()=>import("./index.html-7ad1ada8.js"),[])),"v-21f6c076":_(()=>f(()=>import("./index.html-885d2d66.js"),[])),"v-e9c7e408":_(()=>f(()=>import("./index.html-8ca5d6d0.js"),[])),"v-6106c001":_(()=>f(()=>import("./index.html-89b3bc4f.js"),[])),"v-27fb5a12":_(()=>f(()=>import("./index.html-8fa9a741.js"),[])),"v-211f44ee":_(()=>f(()=>import("./index.html-ecfcf913.js"),[])),"v-4f7b1987":_(()=>f(()=>import("./index.html-56f1c82e.js"),[])),"v-ae84bf22":_(()=>f(()=>import("./index.html-80b68195.js"),[])),"v-72c4ce0d":_(()=>f(()=>import("./index.html-56ff77a6.js"),[])),"v-3a8afeec":_(()=>f(()=>import("./index.html-7d32cae3.js"),[])),"v-14145d44":_(()=>f(()=>import("./index.html-5032b125.js"),[])),"v-2894de8a":_(()=>f(()=>import("./index.html-8819f9aa.js"),[])),"v-209ce691":_(()=>f(()=>import("./index.html-dd2aaa1b.js"),[])),"v-742fbe9b":_(()=>f(()=>import("./index.html-a5e9cc70.js"),[])),"v-4f178b9c":_(()=>f(()=>import("./index.html-bb83e974.js"),[])),"v-b6d14f14":_(()=>f(()=>import("./index.html-24e592b8.js"),[])),"v-3d183459":_(()=>f(()=>import("./index.html-ba65114f.js"),[])),"v-36899bd6":_(()=>f(()=>import("./index.html-fd3d5abd.js"),[])),"v-49f5e4d4":_(()=>f(()=>import("./index.html-23fbd1f9.js"),[])),"v-0d1f4c3c":_(()=>f(()=>import("./index.html-5267cfee.js"),[])),"v-5831b135":_(()=>f(()=>import("./index.html-e17e5e38.js"),[])),"v-14748cc9":_(()=>f(()=>import("./index.html-1529aa11.js"),[])),"v-5bd10ded":_(()=>f(()=>import("./index.html-886738d6.js"),[])),"v-b30dba08":_(()=>f(()=>import("./index.html-a7377d86.js"),[])),"v-65f4ef02":_(()=>f(()=>import("./index.html-8461bbdf.js"),[])),"v-2e8071d8":_(()=>f(()=>import("./index.html-3c855670.js"),[])),"v-65f5a886":_(()=>f(()=>import("./index.html-463d76d6.js"),[])),"v-28c41ed6":_(()=>f(()=>import("./index.html-cbbd3b7c.js"),[])),"v-3d1848d0":_(()=>f(()=>import("./index.html-8ca57fa5.js"),[])),"v-0da0c1c5":_(()=>f(()=>import("./index.html-ad95b869.js"),[])),"v-08073caa":_(()=>f(()=>import("./index.html-26dacff9.js"),[])),"v-3b261482":_(()=>f(()=>import("./index.html-23968938.js"),[])),"v-0667aa78":_(()=>f(()=>import("./index.html-62a7a99d.js"),[])),"v-2671299e":_(()=>f(()=>import("./index.html-3f217e7b.js"),[])),"v-424a813a":_(()=>f(()=>import("./index.html-ec9692d0.js"),[])),"v-2bce0156":_(()=>f(()=>import("./index.html-51e523d0.js"),[])),"v-573729ca":_(()=>f(()=>import("./index.html-8548e270.js"),[])),"v-1bee38ca":_(()=>f(()=>import("./index.html-2fb606ee.js"),[])),"v-5decfa84":_(()=>f(()=>import("./index.html-ef75afa3.js"),[])),"v-6ebee387":_(()=>f(()=>import("./index.html-bfc377fd.js"),[])),"v-437f3f0b":_(()=>f(()=>import("./index.html-6782fbcf.js"),[])),"v-26374ab8":_(()=>f(()=>import("./index.html-41716a03.js"),[])),"v-8d9af0ca":_(()=>f(()=>import("./index.html-5d324fd9.js"),[])),"v-64c651c8":_(()=>f(()=>import("./index.html-67b81d61.js"),[])),"v-77e741be":_(()=>f(()=>import("./index.html-fe58a276.js"),[])),"v-07f91f24":_(()=>f(()=>import("./index.html-758e905d.js"),[])),"v-5fad3f72":_(()=>f(()=>import("./index.html-37792e31.js"),[])),"v-50c2828c":_(()=>f(()=>import("./index.html-869af4d9.js"),[])),"v-20b8e9fc":_(()=>f(()=>import("./index.html-06351497.js"),[])),"v-68f34d54":_(()=>f(()=>import("./index.html-b106ced5.js"),[])),"v-721d1794":_(()=>f(()=>import("./index.html-5f68b0d4.js"),[])),"v-b30dc3f6":_(()=>f(()=>import("./index.html-78f530ac.js"),[])),"v-b3160ccc":_(()=>f(()=>import("./index.html-9ff5508b.js"),[])),"v-0ec4cde8":_(()=>f(()=>import("./index.html-21cd7b10.js"),[])),"v-075c6c62":_(()=>f(()=>import("./index.html-71240a37.js"),[])),"v-6943976d":_(()=>f(()=>import("./index.html-8635b44a.js"),[])),"v-31c4e89d":_(()=>f(()=>import("./index.html-5c7bac98.js"),[])),"v-9e0b104a":_(()=>f(()=>import("./index.html-7cef443f.js"),[])),"v-3bced2c4":_(()=>f(()=>import("./index.html-30e58062.js"),[])),"v-2ab8a0f6":_(()=>f(()=>import("./index.html-22ca0620.js"),[]))};var wd=Symbol(""),Vs=Symbol(""),kd=Ut({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),ie=()=>{const e=he(Vs);if(!e)throw new Error("pageData() is called without provider.");return e},Ms=Symbol(""),Ee=()=>{const e=he(Ms);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},Bs=Symbol(""),Pd=()=>{const e=he(Bs);if(!e)throw new Error("usePageHead() is called without provider.");return e},Od=Symbol(""),$s=Symbol(""),co=()=>{const e=he($s);if(!e)throw new Error("usePageLang() is called without provider.");return e},Ns=Symbol(""),Rd=()=>{const e=he(Ns);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Dd=Y(gd),Sa=Symbol(""),bt=()=>{const e=he(Sa);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},pn=Y(_d),Hs=()=>pn,Fs=Symbol(""),Rn=()=>{const e=he(Fs);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},Sd=Symbol(""),xd="Layout",Cd="NotFound",Lt=lr({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageData:async e=>{const t=Dd.value[e];return await(t==null?void 0:t())??kd},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const r=pe(t.description)?t.description:n.description,o=[...ee(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return yd(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let n;if(e.path){const r=e.frontmatter.layout;pe(r)?n=r:n=xd}else n=Cd;return t[n]},resolveRouteLocale:(e,t)=>Id(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),pr=B({name:"ClientOnly",setup(e,t){const n=Y(!1);return ye(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),js=B({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ie(),n=L(()=>Cs[e.pageKey||t.value.key]);return()=>n.value?l(n.value):l("div","404 Not Found")}}),Ye=(e={})=>e,Ie=e=>on(e)?e:`/${xs(e)}`;const Vd={};/*! * vue-router v4.3.0 * (c) 2024 Eduardo San Martin Morote * @license MIT - */const dn=typeof document<"u";function Md(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function Ro(e,t){const n={};for(const r in t){const o=t[r];n[r]=ut(o)?o.map(e):e(o)}return n}const Gn=()=>{},ut=Array.isArray,zs=/#/g,Bd=/&/g,$d=/\//g,Nd=/=/g,Hd=/\?/g,qs=/\+/g,Fd=/%5B/g,jd=/%5D/g,Us=/%5E/g,zd=/%60/g,Gs=/%7B/g,qd=/%7C/g,Ws=/%7D/g,Ud=/%20/g;function xa(e){return encodeURI(""+e).replace(qd,"|").replace(Fd,"[").replace(jd,"]")}function Gd(e){return xa(e).replace(Gs,"{").replace(Ws,"}").replace(Us,"^")}function Ko(e){return xa(e).replace(qs,"%2B").replace(Ud,"+").replace(zs,"%23").replace(Bd,"%26").replace(zd,"`").replace(Gs,"{").replace(Ws,"}").replace(Us,"^")}function Wd(e){return Ko(e).replace(Nd,"%3D")}function Jd(e){return xa(e).replace(zs,"%23").replace(Hd,"%3F")}function Qd(e){return e==null?"":Jd(e).replace($d,"%2F")}function tr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const Kd=/\/$/,Yd=e=>e.replace(Kd,"");function Do(e,t,n="/"){let r,o={},a="",i="";const s=t.indexOf("#");let c=t.indexOf("?");return s=0&&(c=-1),c>-1&&(r=t.slice(0,c),a=t.slice(c+1,s>-1?s:t.length),o=e(a)),s>-1&&(r=r||t.slice(0,s),i=t.slice(s,t.length)),r=tf(r??t,n),{fullPath:r+(a&&"?")+a+i,path:r,query:o,hash:tr(i)}}function Xd(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function zi(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function Zd(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&Tn(t.matched[r],n.matched[o])&&Js(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Tn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Js(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!ef(e[n],t[n]))return!1;return!0}function ef(e,t){return ut(e)?qi(e,t):ut(t)?qi(t,e):e===t}function qi(e,t){return ut(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function tf(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let a=n.length-1,i,s;for(i=0;i1&&a--;else break;return n.slice(0,a).join("/")+"/"+r.slice(i).join("/")}var nr;(function(e){e.pop="pop",e.push="push"})(nr||(nr={}));var Wn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Wn||(Wn={}));function nf(e){if(!e)if(dn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Yd(e)}const rf=/^[^#]+#/;function of(e,t){return e.replace(rf,"#")+t}function af(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const uo=()=>({left:window.scrollX,top:window.scrollY});function lf(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=af(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function Ui(e,t){return(history.state?history.state.position-t:-1)+e}const Yo=new Map;function sf(e,t){Yo.set(e,t)}function cf(e){const t=Yo.get(e);return Yo.delete(e),t}let uf=()=>location.protocol+"//"+location.host;function Qs(e,t){const{pathname:n,search:r,hash:o}=t,a=e.indexOf("#");if(a>-1){let s=o.includes(e.slice(a))?e.slice(a).length:1,c=o.slice(s);return c[0]!=="/"&&(c="/"+c),zi(c,"")}return zi(n,e)+r+o}function df(e,t,n,r){let o=[],a=[],i=null;const s=({state:v})=>{const h=Qs(e,location),b=n.value,A=t.value;let I=0;if(v){if(n.value=h,t.value=v,i&&i===b){i=null;return}I=A?v.position-A.position:0}else r(h);o.forEach(E=>{E(n.value,b,{delta:I,type:nr.pop,direction:I?I>0?Wn.forward:Wn.back:Wn.unknown})})};function c(){i=n.value}function u(v){o.push(v);const h=()=>{const b=o.indexOf(v);b>-1&&o.splice(b,1)};return a.push(h),h}function d(){const{history:v}=window;v.state&&v.replaceState(ge({},v.state,{scroll:uo()}),"")}function p(){for(const v of a)v();a=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Gi(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?uo():null}}function ff(e){const{history:t,location:n}=window,r={value:Qs(e,n)},o={value:t.state};o.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(c,u,d){const p=e.indexOf("#"),v=p>-1?(n.host&&document.querySelector("base")?e:e.slice(p))+c:uf()+e+c;try{t[d?"replaceState":"pushState"](u,"",v),o.value=u}catch(h){console.error(h),n[d?"replace":"assign"](v)}}function i(c,u){const d=ge({},t.state,Gi(o.value.back,c,o.value.forward,!0),u,{position:o.value.position});a(c,d,!0),r.value=c}function s(c,u){const d=ge({},o.value,t.state,{forward:c,scroll:uo()});a(d.current,d,!0);const p=ge({},Gi(r.value,c,null),{position:d.position+1},u);a(c,p,!1),r.value=c}return{location:r,state:o,push:s,replace:i}}function pf(e){e=nf(e);const t=ff(e),n=df(e,t.state,t.location,t.replace);function r(a,i=!0){i||n.pauseListeners(),history.go(a)}const o=ge({location:"",base:e,go:r,createHref:of.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function vf(e){return typeof e=="string"||e&&typeof e=="object"}function Ks(e){return typeof e=="string"||typeof e=="symbol"}const Tt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ys=Symbol("");var Wi;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Wi||(Wi={}));function An(e,t){return ge(new Error,{type:e,[Ys]:!0},t)}function yt(e,t){return e instanceof Error&&Ys in e&&(t==null||!!(e.type&t))}const Ji="[^/]+?",hf={sensitive:!1,strict:!1,start:!0,end:!0},mf=/[.+*?^${}()[\]/\\]/g;function gf(e,t){const n=ge({},hf,t),r=[];let o=n.start?"^":"";const a=[];for(const u of e){const d=u.length?[]:[90];n.strict&&!u.length&&(o+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function bf(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const yf={type:0,value:""},Ef=/[a-zA-Z0-9_]/;function Lf(e){if(!e)return[[]];if(e==="/")return[[yf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${u}": ${h}`)}let n=0,r=n;const o=[];let a;function i(){a&&o.push(a),a=[]}let s=0,c,u="",d="";function p(){u&&(n===0?a.push({type:0,value:u}):n===1||n===2||n===3?(a.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),a.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function v(){u+=c}for(;s{i(k)}:Gn}function i(d){if(Ks(d)){const p=r.get(d);p&&(r.delete(d),n.splice(n.indexOf(p),1),p.children.forEach(i),p.alias.forEach(i))}else{const p=n.indexOf(d);p>-1&&(n.splice(p,1),d.record.name&&r.delete(d.record.name),d.children.forEach(i),d.alias.forEach(i))}}function s(){return n}function c(d){let p=0;for(;p=0&&(d.record.path!==n[p].record.path||!Xs(d,n[p]));)p++;n.splice(p,0,d),d.record.name&&!Yi(d)&&r.set(d.record.name,d)}function u(d,p){let v,h={},b,A;if("name"in d&&d.name){if(v=r.get(d.name),!v)throw An(1,{location:d});A=v.record.name,h=ge(Ki(p.params,v.keys.filter(k=>!k.optional).concat(v.parent?v.parent.keys.filter(k=>k.optional):[]).map(k=>k.name)),d.params&&Ki(d.params,v.keys.map(k=>k.name))),b=v.stringify(h)}else if(d.path!=null)b=d.path,v=n.find(k=>k.re.test(b)),v&&(h=v.parse(b),A=v.record.name);else{if(v=p.name?r.get(p.name):n.find(k=>k.re.test(p.path)),!v)throw An(1,{location:d,currentLocation:p});A=v.record.name,h=ge({},p.params,d.params),b=v.stringify(h)}const I=[];let E=v;for(;E;)I.unshift(E.record),E=E.parent;return{name:A,path:b,params:h,matched:I,meta:kf(I)}}return e.forEach(d=>a(d)),{addRoute:a,resolve:u,removeRoute:i,getRoutes:s,getRecordMatcher:o}}function Ki(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function If(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:wf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function wf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function Yi(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function kf(e){return e.reduce((t,n)=>ge(t,n.meta),{})}function Xi(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Xs(e,t){return t.children.some(n=>n===e||Xs(e,n))}function Pf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;oa&&Ko(a)):[r&&Ko(r)]).forEach(a=>{a!==void 0&&(t+=(t.length?"&":"")+n,a!=null&&(t+="="+a))})}return t}function Of(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=ut(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Rf=Symbol(""),el=Symbol(""),fo=Symbol(""),Ca=Symbol(""),Xo=Symbol("");function Mn(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Ht(e,t,n,r,o,a=i=>i()){const i=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((s,c)=>{const u=v=>{v===!1?c(An(4,{from:n,to:t})):v instanceof Error?c(v):vf(v)?c(An(2,{from:t,to:v})):(i&&r.enterCallbacks[o]===i&&typeof v=="function"&&i.push(v),s())},d=a(()=>e.call(r&&r.instances[o],t,n,u));let p=Promise.resolve(d);e.length<3&&(p=p.then(u)),p.catch(v=>c(v))})}function So(e,t,n,r,o=a=>a()){const a=[];for(const i of e)for(const s in i.components){let c=i.components[s];if(!(t!=="beforeRouteEnter"&&!i.instances[s]))if(Df(c)){const d=(c.__vccOpts||c)[t];d&&a.push(Ht(d,n,r,i,s,o))}else{let u=c();a.push(()=>u.then(d=>{if(!d)return Promise.reject(new Error(`Couldn't resolve component "${s}" at "${i.path}"`));const p=Md(d)?d.default:d;i.components[s]=p;const h=(p.__vccOpts||p)[t];return h&&Ht(h,n,r,i,s,o)()}))}}return a}function Df(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Zo(e){const t=he(fo),n=he(Ca),r=L(()=>t.resolve(_t(e.to))),o=L(()=>{const{matched:c}=r.value,{length:u}=c,d=c[u-1],p=n.matched;if(!d||!p.length)return-1;const v=p.findIndex(Tn.bind(null,d));if(v>-1)return v;const h=tl(c[u-2]);return u>1&&tl(d)===h&&p[p.length-1].path!==h?p.findIndex(Tn.bind(null,c[u-2])):v}),a=L(()=>o.value>-1&&Vf(n.params,r.value.params)),i=L(()=>o.value>-1&&o.value===n.matched.length-1&&Js(n.params,r.value.params));function s(c={}){return Cf(c)?t[_t(e.replace)?"replace":"push"](_t(e.to)).catch(Gn):Promise.resolve()}return{route:r,href:L(()=>r.value.href),isActive:a,isExactActive:i,navigate:s}}const Sf=B({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:Zo,setup(e,{slots:t}){const n=lr(Zo(e)),{options:r}=he(fo),o=L(()=>({[nl(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[nl(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const a=t.default&&t.default(n);return e.custom?a:l("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},a)}}}),xf=Sf;function Cf(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Vf(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!ut(o)||o.length!==r.length||r.some((a,i)=>a!==o[i]))return!1}return!0}function tl(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const nl=(e,t,n)=>e??t??n,Mf=B({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=he(Xo),o=L(()=>e.route||r.value),a=he(el,0),i=L(()=>{let u=_t(a);const{matched:d}=o.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),s=L(()=>o.value.matched[i.value]);ct(el,L(()=>i.value+1)),ct(Rf,s),ct(Xo,o);const c=Y();return ve(()=>[c.value,s.value,e.name],([u,d,p],[v,h,b])=>{d&&(d.instances[p]=u,h&&h!==d&&u&&u===v&&(d.leaveGuards.size||(d.leaveGuards=h.leaveGuards),d.updateGuards.size||(d.updateGuards=h.updateGuards))),u&&d&&(!h||!Tn(d,h)||!v)&&(d.enterCallbacks[p]||[]).forEach(A=>A(u))},{flush:"post"}),()=>{const u=o.value,d=e.name,p=s.value,v=p&&p.components[d];if(!v)return rl(n.default,{Component:v,route:u});const h=p.props[d],b=h?h===!0?u.params:typeof h=="function"?h(u):h:null,I=l(v,ge({},b,t,{onVnodeUnmounted:E=>{E.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return rl(n.default,{Component:I,route:u})||I}}});function rl(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Zs=Mf;function Bf(e){const t=Af(e.routes,e),n=e.parseQuery||Pf,r=e.stringifyQuery||Zi,o=e.history,a=Mn(),i=Mn(),s=Mn(),c=ft(Tt);let u=Tt;dn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=Ro.bind(null,R=>""+R),p=Ro.bind(null,Qd),v=Ro.bind(null,tr);function h(R,z){let F,G;return Ks(R)?(F=t.getRecordMatcher(R),G=z):G=R,t.addRoute(G,F)}function b(R){const z=t.getRecordMatcher(R);z&&t.removeRoute(z)}function A(){return t.getRoutes().map(R=>R.record)}function I(R){return!!t.getRecordMatcher(R)}function E(R,z){if(z=ge({},z||c.value),typeof R=="string"){const g=Do(n,R,z.path),T=t.resolve({path:g.path},z),D=o.createHref(g.fullPath);return ge(g,T,{params:v(T.params),hash:tr(g.hash),redirectedFrom:void 0,href:D})}let F;if(R.path!=null)F=ge({},R,{path:Do(n,R.path,z.path).path});else{const g=ge({},R.params);for(const T in g)g[T]==null&&delete g[T];F=ge({},R,{params:p(g)}),z.params=p(z.params)}const G=t.resolve(F,z),me=R.hash||"";G.params=d(v(G.params));const Le=Xd(r,ge({},R,{hash:Gd(me),path:G.path})),m=o.createHref(Le);return ge({fullPath:Le,hash:me,query:r===Zi?Of(R.query):R.query||{}},G,{redirectedFrom:void 0,href:m})}function k(R){return typeof R=="string"?Do(n,R,c.value.path):ge({},R)}function y(R,z){if(u!==R)return An(8,{from:z,to:R})}function P(R){return U(R)}function $(R){return P(ge(k(R),{replace:!0}))}function w(R){const z=R.matched[R.matched.length-1];if(z&&z.redirect){const{redirect:F}=z;let G=typeof F=="function"?F(R):F;return typeof G=="string"&&(G=G.includes("?")||G.includes("#")?G=k(G):{path:G},G.params={}),ge({query:R.query,hash:R.hash,params:G.path!=null?{}:R.params},G)}}function U(R,z){const F=u=E(R),G=c.value,me=R.state,Le=R.force,m=R.replace===!0,g=w(F);if(g)return U(ge(k(g),{state:typeof g=="object"?ge({},me,g.state):me,force:Le,replace:m}),z||F);const T=F;T.redirectedFrom=z;let D;return!Le&&Zd(r,G,F)&&(D=An(16,{to:T,from:G}),vt(G,G,!0,!1)),(D?Promise.resolve(D):x(T,G)).catch(O=>yt(O)?yt(O,2)?O:Rt(O):W(O,T,G)).then(O=>{if(O){if(yt(O,2))return U(ge({replace:m},k(O.to),{state:typeof O.to=="object"?ge({},me,O.to.state):me,force:Le}),z||T)}else O=M(T,G,!0,m,me);return X(T,G,O),O})}function H(R,z){const F=y(R,z);return F?Promise.reject(F):Promise.resolve()}function J(R){const z=sn.values().next().value;return z&&typeof z.runWithContext=="function"?z.runWithContext(R):R()}function x(R,z){let F;const[G,me,Le]=$f(R,z);F=So(G.reverse(),"beforeRouteLeave",R,z);for(const g of G)g.leaveGuards.forEach(T=>{F.push(Ht(T,R,z))});const m=H.bind(null,R,z);return F.push(m),Me(F).then(()=>{F=[];for(const g of a.list())F.push(Ht(g,R,z));return F.push(m),Me(F)}).then(()=>{F=So(me,"beforeRouteUpdate",R,z);for(const g of me)g.updateGuards.forEach(T=>{F.push(Ht(T,R,z))});return F.push(m),Me(F)}).then(()=>{F=[];for(const g of Le)if(g.beforeEnter)if(ut(g.beforeEnter))for(const T of g.beforeEnter)F.push(Ht(T,R,z));else F.push(Ht(g.beforeEnter,R,z));return F.push(m),Me(F)}).then(()=>(R.matched.forEach(g=>g.enterCallbacks={}),F=So(Le,"beforeRouteEnter",R,z,J),F.push(m),Me(F))).then(()=>{F=[];for(const g of i.list())F.push(Ht(g,R,z));return F.push(m),Me(F)}).catch(g=>yt(g,8)?g:Promise.reject(g))}function X(R,z,F){s.list().forEach(G=>J(()=>G(R,z,F)))}function M(R,z,F,G,me){const Le=y(R,z);if(Le)return Le;const m=z===Tt,g=dn?history.state:{};F&&(G||m?o.replace(R.fullPath,ge({scroll:m&&g&&g.scroll},me)):o.push(R.fullPath,me)),c.value=R,vt(R,z,F,m),Rt()}let te;function Pe(){te||(te=o.listen((R,z,F)=>{if(!Ir.listening)return;const G=E(R),me=w(G);if(me){U(ge(me,{replace:!0}),G).catch(Gn);return}u=G;const Le=c.value;dn&&sf(Ui(Le.fullPath,F.delta),uo()),x(G,Le).catch(m=>yt(m,12)?m:yt(m,2)?(U(m.to,G).then(g=>{yt(g,20)&&!F.delta&&F.type===nr.pop&&o.go(-1,!1)}).catch(Gn),Promise.reject()):(F.delta&&o.go(-F.delta,!1),W(m,G,Le))).then(m=>{m=m||M(G,Le,!1),m&&(F.delta&&!yt(m,8)?o.go(-F.delta,!1):F.type===nr.pop&&yt(m,20)&&o.go(-1,!1)),X(G,Le,m)}).catch(Gn)}))}let Oe=Mn(),Q=Mn(),oe;function W(R,z,F){Rt(R);const G=Q.list();return G.length?G.forEach(me=>me(R,z,F)):console.error(R),Promise.reject(R)}function at(){return oe&&c.value!==Tt?Promise.resolve():new Promise((R,z)=>{Oe.add([R,z])})}function Rt(R){return oe||(oe=!R,Pe(),Oe.list().forEach(([z,F])=>R?F(R):z()),Oe.reset()),R}function vt(R,z,F,G){const{scrollBehavior:me}=e;if(!dn||!me)return Promise.resolve();const Le=!F&&cf(Ui(R.fullPath,0))||(G||!F)&&history.state&&history.state.scroll||null;return rn().then(()=>me(R,z,Le)).then(m=>m&&lf(m)).catch(m=>W(m,R,z))}const Fe=R=>o.go(R);let ln;const sn=new Set,Ir={currentRoute:c,listening:!0,addRoute:h,removeRoute:b,hasRoute:I,getRoutes:A,resolve:E,options:e,push:P,replace:$,go:Fe,back:()=>Fe(-1),forward:()=>Fe(1),beforeEach:a.add,beforeResolve:i.add,afterEach:s.add,onError:Q.add,isReady:at,install(R){const z=this;R.component("RouterLink",xf),R.component("RouterView",Zs),R.config.globalProperties.$router=z,Object.defineProperty(R.config.globalProperties,"$route",{enumerable:!0,get:()=>_t(c)}),dn&&!ln&&c.value===Tt&&(ln=!0,P(o.location).catch(me=>{}));const F={};for(const me in Tt)Object.defineProperty(F,me,{get:()=>c.value[me],enumerable:!0});R.provide(fo,z),R.provide(Ca,Hl(F)),R.provide(Xo,c);const G=R.unmount;sn.add(R),R.unmount=function(){sn.delete(R),sn.size<1&&(u=Tt,te&&te(),te=null,c.value=Tt,ln=!1,oe=!1),G()}}};function Me(R){return R.reduce((z,F)=>z.then(()=>J(F)),Promise.resolve())}return Ir}function $f(e,t){const n=[],r=[],o=[],a=Math.max(t.matched.length,e.matched.length);for(let i=0;iTn(u,s))?r.push(s):n.push(s));const c=e.matched[i];c&&(t.matched.find(u=>Tn(u,c))||o.push(c))}return[n,r,o]}function Ne(){return he(fo)}function pt(){return he(Ca)}const ue=({name:e="",color:t="currentColor"},{slots:n})=>{var r;return l("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(r=n.default)==null?void 0:r.call(n))};ue.displayName="IconBase";const ec=({size:e=48,stroke:t=4,wrapper:n=!0,height:r=2*e})=>{const o=l("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[l("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),l("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[l("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),l("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return n?l("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${r}px`},o):o};ec.displayName="LoadingIcon";const tc=(e,{slots:t})=>{var n;return(n=t.default)==null?void 0:n.call(t)},Va=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},po=(e,t)=>{let n=1;for(let r=0;r>6;return n+=n<<3,n^=n>>11,n%t},nc=Array.isArray,Nf=e=>typeof e=="function",Hf=e=>typeof e=="string";var Ff=e=>e.startsWith("ftp://"),Ma=e=>/^(https?:)?\/\//.test(e),jf=/.md((\?|#).*)?$/,zf=(e,t="/")=>!!(Ma(e)||Ff(e)||e.startsWith("/")&&!e.startsWith(t)&&!jf.test(e)),rc=e=>Object.prototype.toString.call(e)==="[object Object]";function qf(){const e=Y(!1);return On()&&ye(()=>{e.value=!0}),e}function Uf(e){return qf(),L(()=>!!e())}const kt=e=>typeof e=="string",rr=(e,t)=>kt(e)&&e.startsWith(t),un=(e,t)=>kt(e)&&e.endsWith(t),Dn=Object.entries,Gf=Object.fromEntries,dt=Object.keys,Wf=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),oc=e=>{const[t,n=""]=e.split("#");return t?`${Wf(t)}${n?`#${n}`:""}`:e},ol=e=>rc(e)&&kt(e.name),or=(e,t=!1)=>e?nc(e)?e.map(n=>kt(n)?{name:n}:ol(n)?n:null).filter(n=>n!==null):kt(e)?[{name:e}]:ol(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],ac=(e,t)=>{if(e){if(nc(e)&&e.every(kt))return e;if(kt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},ic=e=>ac(e,"category"),lc=e=>ac(e,"tag"),vo=e=>rr(e,"/");class Jf{constructor(){this.messageElements={};const t="message-container",n=document.getElementById(t);n?this.containerElement=n:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,n=2e3){const r=document.createElement("div"),o=Date.now();return r.className="message move-in",r.innerHTML=t,this.containerElement.appendChild(r),this.messageElements[o]=r,n>0&&setTimeout(()=>{this.close(o)},n),o}close(t){if(t){const n=this.messageElements[t];n.classList.remove("move-in"),n.classList.add("move-out"),n.addEventListener("animationend",()=>{n.remove(),delete this.messageElements[t]})}else dt(this.messageElements).forEach(n=>this.close(Number(n)))}destroy(){document.body.removeChild(this.containerElement)}}const sc=/#.*$/u,Qf=e=>{const t=sc.exec(e);return t?t[0]:""},al=e=>decodeURI(e).replace(sc,"").replace(/(index)?\.(md|html)$/,""),Ba=(e,t)=>{if(t===void 0)return!1;const n=al(e.path),r=al(t),o=Qf(t);return o?o===e.hash&&(!r||n===r):n===r},Kf=e=>Ma(e)?e:`https://github.com/${e}`,cc=e=>!Ma(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,In=(e,...t)=>{const n=e.resolve(...t),r=n.matched[n.matched.length-1];if(!(r!=null&&r.redirect))return n;const{redirect:o}=r,a=Nf(o)?o(n):o,i=Hf(a)?{path:a}:a;return In(e,{hash:n.hash,query:n.query,params:n.params,...i})},Yf=e=>{var t;if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)&&!(e.currentTarget&&((t=e.currentTarget.getAttribute("target"))!=null&&t.match(/\b_blank\b/i))))return e.preventDefault(),!0},Re=({to:e="",class:t="",...n},{slots:r})=>{var i;const o=Ne(),a=(s={})=>Yf(s)?o.push(e).catch():Promise.resolve();return l("a",{...n,class:["vp-link",t],href:Ie(oc(e)),onClick:a},(i=r.default)==null?void 0:i.call(r))};Re.displayName="VPLink";const uc=()=>l(ue,{name:"github"},()=>l("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));uc.displayName="GitHubIcon";const dc=()=>l(ue,{name:"gitlab"},()=>l("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));dc.displayName="GitLabIcon";const fc=()=>l(ue,{name:"gitee"},()=>l("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));fc.displayName="GiteeIcon";const pc=()=>l(ue,{name:"bitbucket"},()=>l("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));pc.displayName="BitbucketIcon";const vc=()=>l(ue,{name:"source"},()=>l("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));vc.displayName="SourceIcon";const It=(e,t)=>{const n=t?t._instance:On();return rc(n==null?void 0:n.appContext.components)&&(e in n.appContext.components||ot(e)in n.appContext.components||ir(ot(e))in n.appContext.components)},Xf=()=>Uf(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),Zf=()=>{const e=Xf();return L(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},Sn=e=>{const t=bt();return L(()=>e[t.value])};function il(e,t){var n;const r=ft();return Ta(()=>{r.value=e()},{...t,flush:(n=t==null?void 0:t.flush)!=null?n:"sync"}),Ut(r)}function ho(e,t){let n,r,o;const a=Y(!0),i=()=>{a.value=!0,o()};ve(e,i,{flush:"sync"});const s=typeof t=="function"?t:t.get,c=typeof t=="function"?void 0:t.set,u=Gl((d,p)=>(r=d,o=p,{get(){return a.value&&(n=s(),a.value=!1),r(),n},set(v){c==null||c(v)}}));return Object.isExtensible(u)&&(u.trigger=i),u}function an(e){return Pl()?(n0(e),!0):!1}function rt(e){return typeof e=="function"?e():_t(e)}const vr=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const ep=Object.prototype.toString,tp=e=>ep.call(e)==="[object Object]",en=()=>{},ea=np();function np(){var e,t;return vr&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||((t=window==null?void 0:window.navigator)==null?void 0:t.maxTouchPoints)>2&&/iPad|Macintosh/.test(window==null?void 0:window.navigator.userAgent))}function hc(e,t){function n(...r){return new Promise((o,a)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(a)})}return n}const mc=e=>e();function rp(...e){let t=0,n,r=!0,o=en,a,i,s,c,u;!Ve(e[0])&&typeof e[0]=="object"?{delay:i,trailing:s=!0,leading:c=!0,rejectOnCancel:u=!1}=e[0]:[i,s=!0,c=!0,u=!1]=e;const d=()=>{n&&(clearTimeout(n),n=void 0,o(),o=en)};return v=>{const h=rt(i),b=Date.now()-t,A=()=>a=v();return d(),h<=0?(t=Date.now(),A()):(b>h&&(c||!r)?(t=Date.now(),A()):s&&(a=new Promise((I,E)=>{o=u?E:I,n=setTimeout(()=>{t=Date.now(),r=!0,I(A()),d()},Math.max(0,h-b))})),!c&&!n&&(n=setTimeout(()=>r=!0,h)),r=!1,a)}}function op(e=mc){const t=Y(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...a)=>{t.value&&e(...a)};return{isActive:Ut(t),pause:n,resume:r,eventFilter:o}}function ap(e){let t;function n(){return t||(t=e()),t}return n.reset=async()=>{const r=t;t=void 0,r&&await r},n}function ip(e){return e||On()}function lp(...e){if(e.length!==1)return Pn(...e);const t=e[0];return typeof t=="function"?Ut(Gl(()=>({get:t,set:en}))):Y(t)}function sp(e,t=200,n=!1,r=!0,o=!1){return hc(rp(t,n,r,o),e)}function cp(e,t,n={}){const{eventFilter:r=mc,...o}=n;return ve(e,hc(r,t),o)}function up(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:a,pause:i,resume:s,isActive:c}=op(r);return{stop:cp(e,t,{...o,eventFilter:a}),pause:i,resume:s,isActive:c}}function mo(e,t=!0,n){ip()?ye(e,n):t?e():rn(e)}function dp(e,t,n={}){const{immediate:r=!0}=n,o=Y(!1);let a=null;function i(){a&&(clearTimeout(a),a=null)}function s(){o.value=!1,i()}function c(...u){i(),o.value=!0,a=setTimeout(()=>{o.value=!1,a=null,e(...u)},rt(t))}return r&&(o.value=!0,vr&&c()),an(s),{isPending:Ut(o),start:c,stop:s}}function ll(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=Ve(e),a=Y(e);function i(s){if(arguments.length)return a.value=s,a.value;{const c=rt(n);return a.value=a.value===c?rt(r):c,a.value}}return o?i:[a,i]}function Qe(e){var t;const n=rt(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Pt=vr?window:void 0,gc=vr?window.document:void 0,_c=vr?window.navigator:void 0;function Se(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=Pt):[t,n,r,o]=e,!t)return en;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const a=[],i=()=>{a.forEach(d=>d()),a.length=0},s=(d,p,v,h)=>(d.addEventListener(p,v,h),()=>d.removeEventListener(p,v,h)),c=ve(()=>[Qe(t),rt(o)],([d,p])=>{if(i(),!d)return;const v=tp(p)?{...p}:p;a.push(...n.flatMap(h=>r.map(b=>s(d,h,b,v))))},{immediate:!0,flush:"post"}),u=()=>{c(),i()};return an(u),u}let sl=!1;function fp(e,t,n={}){const{window:r=Pt,ignore:o=[],capture:a=!0,detectIframe:i=!1}=n;if(!r)return en;ea&&!sl&&(sl=!0,Array.from(r.document.body.children).forEach(v=>v.addEventListener("click",en)),r.document.documentElement.addEventListener("click",en));let s=!0;const c=v=>o.some(h=>{if(typeof h=="string")return Array.from(r.document.querySelectorAll(h)).some(b=>b===v.target||v.composedPath().includes(b));{const b=Qe(h);return b&&(v.target===b||v.composedPath().includes(b))}}),d=[Se(r,"click",v=>{const h=Qe(e);if(!(!h||h===v.target||v.composedPath().includes(h))){if(v.detail===0&&(s=!c(v)),!s){s=!0;return}t(v)}},{passive:!0,capture:a}),Se(r,"pointerdown",v=>{const h=Qe(e);s=!c(v)&&!!(h&&!v.composedPath().includes(h))},{passive:!0}),i&&Se(r,"blur",v=>{setTimeout(()=>{var h;const b=Qe(e);((h=r.document.activeElement)==null?void 0:h.tagName)==="IFRAME"&&!(b!=null&&b.contains(r.document.activeElement))&&t(v)},0)})].filter(Boolean);return()=>d.forEach(v=>v())}function pp(){const e=Y(!1),t=On();return t&&ye(()=>{e.value=!0},t),e}function hr(e){const t=pp();return L(()=>(t.value,!!e()))}function bc(e,t={}){const{window:n=Pt}=t,r=hr(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const a=Y(!1),i=u=>{a.value=u.matches},s=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},c=Ta(()=>{r.value&&(s(),o=n.matchMedia(rt(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),a.value=o.matches)});return an(()=>{c(),s(),o=void 0}),a}function cl(e,t={}){const{controls:n=!1,navigator:r=_c}=t,o=hr(()=>r&&"permissions"in r);let a;const i=typeof e=="string"?{name:e}:e,s=Y(),c=()=>{a&&(s.value=a.state)},u=ap(async()=>{if(o.value){if(!a)try{a=await r.permissions.query(i),Se(a,"change",c),c()}catch{s.value="prompt"}return a}});return u(),n?{state:s,isSupported:o,query:u}:s}function vp(e={}){const{navigator:t=_c,read:n=!1,source:r,copiedDuring:o=1500,legacy:a=!1}=e,i=hr(()=>t&&"clipboard"in t),s=cl("clipboard-read"),c=cl("clipboard-write"),u=L(()=>i.value||a),d=Y(""),p=Y(!1),v=dp(()=>p.value=!1,o);function h(){i.value&&E(s.value)?t.clipboard.readText().then(k=>{d.value=k}):d.value=I()}u.value&&n&&Se(["copy","cut"],h);async function b(k=rt(r)){u.value&&k!=null&&(i.value&&E(c.value)?await t.clipboard.writeText(k):A(k),d.value=k,p.value=!0,v.start())}function A(k){const y=document.createElement("textarea");y.value=k??"",y.style.position="absolute",y.style.opacity="0",document.body.appendChild(y),y.select(),document.execCommand("copy"),y.remove()}function I(){var k,y,P;return(P=(y=(k=document==null?void 0:document.getSelection)==null?void 0:k.call(document))==null?void 0:y.toString())!=null?P:""}function E(k){return k==="granted"||k==="prompt"}return{isSupported:u,text:d,copied:p,copy:b}}const Vr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},Mr="__vueuse_ssr_handlers__",hp=mp();function mp(){return Mr in Vr||(Vr[Mr]=Vr[Mr]||{}),Vr[Mr]}function gp(e,t){return hp[e]||t}function _p(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const bp={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},ul="vueuse-storage";function yc(e,t,n,r={}){var o;const{flush:a="pre",deep:i=!0,listenToStorageChanges:s=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=Pt,eventFilter:v,onError:h=x=>{console.error(x)},initOnMounted:b}=r,A=(d?ft:Y)(typeof t=="function"?t():t);if(!n)try{n=gp("getDefaultStorage",()=>{var x;return(x=Pt)==null?void 0:x.localStorage})()}catch(x){h(x)}if(!n)return A;const I=rt(t),E=_p(I),k=(o=r.serializer)!=null?o:bp[E],{pause:y,resume:P}=up(A,()=>w(A.value),{flush:a,deep:i,eventFilter:v});p&&s&&mo(()=>{Se(p,"storage",H),Se(p,ul,J),b&&H()}),b||H();function $(x,X){p&&p.dispatchEvent(new CustomEvent(ul,{detail:{key:e,oldValue:x,newValue:X,storageArea:n}}))}function w(x){try{const X=n.getItem(e);if(x==null)$(X,null),n.removeItem(e);else{const M=k.write(x);X!==M&&(n.setItem(e,M),$(X,M))}}catch(X){h(X)}}function U(x){const X=x?x.newValue:n.getItem(e);if(X==null)return c&&I!=null&&n.setItem(e,k.write(I)),I;if(!x&&u){const M=k.read(X);return typeof u=="function"?u(M,I):E==="object"&&!Array.isArray(M)?{...I,...M}:M}else return typeof X!="string"?X:k.read(X)}function H(x){if(!(x&&x.storageArea!==n)){if(x&&x.key==null){A.value=I;return}if(!(x&&x.key!==e)){y();try{(x==null?void 0:x.newValue)!==k.write(A.value)&&(A.value=U(x))}catch(X){h(X)}finally{x?rn(P):P()}}}}function J(x){H(x.detail)}return A}function yp(e){return bc("(prefers-color-scheme: dark)",e)}function Ep(e,t,n={}){const{window:r=Pt,...o}=n;let a;const i=hr(()=>r&&"ResizeObserver"in r),s=()=>{a&&(a.disconnect(),a=void 0)},c=L(()=>Array.isArray(e)?e.map(p=>Qe(p)):[Qe(e)]),u=ve(c,p=>{if(s(),i.value&&r){a=new ResizeObserver(t);for(const v of p)v&&a.observe(v,o)}},{immediate:!0,flush:"post"}),d=()=>{s(),u()};return an(d),{isSupported:i,stop:d}}function Lp(e,t={width:0,height:0},n={}){const{window:r=Pt,box:o="content-box"}=n,a=L(()=>{var p,v;return(v=(p=Qe(e))==null?void 0:p.namespaceURI)==null?void 0:v.includes("svg")}),i=Y(t.width),s=Y(t.height),{stop:c}=Ep(e,([p])=>{const v=o==="border-box"?p.borderBoxSize:o==="content-box"?p.contentBoxSize:p.devicePixelContentBoxSize;if(r&&a.value){const h=Qe(e);if(h){const b=r.getComputedStyle(h);i.value=Number.parseFloat(b.width),s.value=Number.parseFloat(b.height)}}else if(v){const h=Array.isArray(v)?v:[v];i.value=h.reduce((b,{inlineSize:A})=>b+A,0),s.value=h.reduce((b,{blockSize:A})=>b+A,0)}else i.value=p.contentRect.width,s.value=p.contentRect.height},n);mo(()=>{const p=Qe(e);p&&(i.value="offsetWidth"in p?p.offsetWidth:t.width,s.value="offsetHeight"in p?p.offsetHeight:t.height)});const u=ve(()=>Qe(e),p=>{i.value=p?t.width:0,s.value=p?t.height:0});function d(){c(),u()}return{width:i,height:s,stop:d}}const dl=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function $a(e,t={}){const{document:n=gc,autoExit:r=!1}=t,o=L(()=>{var E;return(E=Qe(e))!=null?E:n==null?void 0:n.querySelector("html")}),a=Y(!1),i=L(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(E=>n&&E in n||o.value&&E in o.value)),s=L(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(E=>n&&E in n||o.value&&E in o.value)),c=L(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(E=>n&&E in n||o.value&&E in o.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(E=>n&&E in n),d=hr(()=>o.value&&n&&i.value!==void 0&&s.value!==void 0&&c.value!==void 0),p=()=>u?(n==null?void 0:n[u])===o.value:!1,v=()=>{if(c.value){if(n&&n[c.value]!=null)return n[c.value];{const E=o.value;if((E==null?void 0:E[c.value])!=null)return!!E[c.value]}}return!1};async function h(){if(!(!d.value||!a.value)){if(s.value)if((n==null?void 0:n[s.value])!=null)await n[s.value]();else{const E=o.value;(E==null?void 0:E[s.value])!=null&&await E[s.value]()}a.value=!1}}async function b(){if(!d.value||a.value)return;v()&&await h();const E=o.value;i.value&&(E==null?void 0:E[i.value])!=null&&(await E[i.value](),a.value=!0)}async function A(){await(a.value?h():b())}const I=()=>{const E=v();(!E||E&&p())&&(a.value=E)};return Se(n,dl,I,!1),Se(()=>Qe(o),dl,I,!1),r&&an(h),{isSupported:d,isFullscreen:a,enter:b,exit:h,toggle:A}}function xo(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function Ec(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}const Br=new WeakMap;function Lc(e,t=!1){const n=Y(t);let r=null;ve(lp(e),i=>{const s=xo(rt(i));if(s){const c=s;Br.get(c)||Br.set(c,c.style.overflow),n.value&&(c.style.overflow="hidden")}},{immediate:!0});const o=()=>{const i=xo(rt(e));!i||n.value||(ea&&(r=Se(i,"touchmove",s=>{Tp(s)},{passive:!1})),i.style.overflow="hidden",n.value=!0)},a=()=>{var i;const s=xo(rt(e));!s||!n.value||(ea&&(r==null||r()),s.style.overflow=(i=Br.get(s))!=null?i:"",Br.delete(s),n.value=!1)};return an(a),L({get(){return n.value},set(i){i?o():a()}})}let Ap=0;function Ip(e,t={}){const n=Y(!1),{document:r=gc,immediate:o=!0,manual:a=!1,id:i=`vueuse_styletag_${++Ap}`}=t,s=Y(e);let c=()=>{};const u=()=>{if(!r)return;const p=r.getElementById(i)||r.createElement("style");p.isConnected||(p.id=i,t.media&&(p.media=t.media),r.head.appendChild(p)),!n.value&&(c=ve(s,v=>{p.textContent=v},{immediate:!0}),n.value=!0)},d=()=>{!r||!n.value||(c(),r.head.removeChild(r.getElementById(i)),n.value=!1)};return o&&!a&&mo(u),a||an(d),{id:i,css:s,unload:d,load:u,isLoaded:Ut(n)}}function wp(e={}){const{window:t=Pt,behavior:n="auto"}=e;if(!t)return{x:Y(0),y:Y(0)};const r=Y(t.scrollX),o=Y(t.scrollY),a=L({get(){return r.value},set(s){scrollTo({left:s,behavior:n})}}),i=L({get(){return o.value},set(s){scrollTo({top:s,behavior:n})}});return Se(t,"scroll",()=>{r.value=t.scrollX,o.value=t.scrollY},{capture:!1,passive:!0}),{x:a,y:i}}function kp(e={}){const{window:t=Pt,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:a=!0}=e,i=Y(n),s=Y(r),c=()=>{t&&(a?(i.value=t.innerWidth,s.value=t.innerHeight):(i.value=t.document.documentElement.clientWidth,s.value=t.document.documentElement.clientHeight))};if(c(),mo(c),Se("resize",c,{passive:!0}),o){const u=bc("(orientation: portrait)");ve(u,()=>c())}return{width:i,height:s}}const Tc=({type:e="info",text:t="",vertical:n,color:r},{slots:o})=>{var a;return l("span",{class:["vp-badge",e,{diy:r}],style:{verticalAlign:n??!1,backgroundColor:r??!1}},((a=o.default)==null?void 0:a.call(o))||t)};Tc.displayName="Badge";var Pp=B({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=L(()=>{const r=["font-icon icon"],o=`iconfont icon-${e.icon}`;return r.push(o),r}),n=L(()=>{const r={};return e.color&&(r.color=e.color),e.size&&(r["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),dt(r).length?r:null});return()=>e.icon?l("span",{key:e.icon,class:t.value,style:n.value}):null}});const Ac=()=>l(ue,{name:"back-to-top"},()=>[l("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),l("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);Ac.displayName="BackToTopIcon";var Op=B({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),n=Sn({"/en/":{backToTop:"Back to top"},"/":{backToTop:"返回顶部"}}),r=ft(),{height:o}=Lp(r),{height:a}=kp(),{y:i}=wp(),s=L(()=>t.value.backToTop!==!1&&i.value>e.threshold),c=L(()=>i.value/(o.value-a.value));return ye(()=>{r.value=document.body}),()=>l(wt,{name:"fade"},()=>s.value?l("button",{type:"button",class:"vp-back-to-top-button","aria-label":n.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:l("svg",{class:"vp-scroll-progress"},l("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),l(Ac)]):null)}});const Rp=Ye({enhance:({app:e})=>{It("Badge")||e.component("Badge",Tc),It("FontIcon")||e.component("FontIcon",Pp)},setup:()=>{Ip(` @import url("https://at.alicdn.com/t/c/font_2410206_5vb9zlyghj.css"); - `)},rootComponents:[()=>l(Op,{})]});function Dp(e,t,n){var r,o,a;t===void 0&&(t=50),n===void 0&&(n={});var i=(r=n.isImmediate)!=null&&r,s=(o=n.callback)!=null&&o,c=n.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var h=Date.now()-u;if(h+t>=c)return c-h}return t}var v=function(){var h=[].slice.call(arguments),b=this;return new Promise(function(A,I){var E=i&&a===void 0;if(a!==void 0&&clearTimeout(a),a=setTimeout(function(){if(a=void 0,u=Date.now(),!i){var y=e.apply(b,h);s&&s(y),d.forEach(function(P){return(0,P.resolve)(y)}),d=[]}},p()),E){var k=e.apply(b,h);return s&&s(k),A(k)}d.push({resolve:A,reject:I})})};return v.cancel=function(h){a!==void 0&&clearTimeout(a),d.forEach(function(b){return(0,b.reject)(h)}),d=[]},v}const Sp=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=Ne(),i=Dp(()=>{var A,I;const s=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(s-0)v.some(k=>k.hash===E.hash));for(let E=0;E=(((A=k.parentElement)==null?void 0:A.offsetTop)??0)-r,$=!y||s<(((I=y.parentElement)==null?void 0:I.offsetTop)??0)-r;if(!(P&&$))continue;const U=decodeURIComponent(o.currentRoute.value.hash),H=decodeURIComponent(k.hash);if(U===H)return;if(p){for(let J=E+1;J{window.addEventListener("scroll",i)}),wa(()=>{window.removeEventListener("scroll",i)})},fl=async(e,t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=n)},xp=".vp-sidebar-link, .toc-link",Cp=".header-anchor",Vp=200,Mp=5,Bp=Ye({setup(){Sp({headerLinkSelector:xp,headerAnchorSelector:Cp,delay:Vp,offset:Mp})}});let Ic=()=>null;const wc=Symbol(""),$p=e=>{Ic=e},Np=()=>he(wc),Hp=e=>{e.provide(wc,Ic)};var Fp=B({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean,indexType:{type:String,default:"ul"},hideHeading:Boolean},setup(e){const t=Np(),n=Sn({"/en/":{title:"Catalog",empty:"No catalog"},"/":{title:"目录",empty:"暂无目录"}}),r=ie(),o=Ne(),a=Hs(),i=u=>{const d=u.I;return typeof d>"u"||d},s=()=>{const u=e.base||r.value.path.replace(/\/[^/]+$/,"/"),d=o.getRoutes(),p=[];return d.filter(({meta:v,path:h})=>{if(!rr(h,u)||h===u)return!1;if(u==="/"){const b=dt(a.value.locales).filter(A=>A!=="/");if(h==="/404.html"||b.some(A=>rr(h,A)))return!1}return(un(h,".html")&&!un(h,"/index.html")||un(h,"/"))&&i(v)}).map(({path:v,meta:h})=>{const b=v.substring(u.length).split("/").length;return{title:h.t||"",icon:h.i,base:v.replace(/\/[^/]+\/?$/,"/"),order:h.O||null,level:un(v,"/")?b-1:b,path:v}}).filter(({title:v,level:h})=>v&&h<=e.level).sort(({title:v,level:h,path:b,order:A},{title:I,level:E,path:k,order:y})=>h-E||(un(b,"/index.html")?-1:un(k,"/index.html")?1:A===null?y===null?v.localeCompare(I):y:y===null?A:A>0?y>0?A-y:-1:y<0?A-y:1)).forEach(v=>{var A;const{base:h,level:b}=v;switch(b){case 1:p.push(v);break;case 2:{const I=p.find(E=>E.path===h);I&&(I.children??(I.children=[])).push(v);break}default:{const I=p.find(E=>E.path===h.replace(/\/[^/]+\/$/,"/"));if(I){const E=(A=I.children)==null?void 0:A.find(k=>k.path===h);E&&(E.children??(E.children=[])).push(v)}}}}),p},c=L(()=>s());return()=>l("div",{class:"vp-catalog"},[e.hideHeading?null:l("h2",{class:"vp-catalog-main-title"},n.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:v},h)=>[l("h3",{id:v,class:["vp-catalog-child-title",{"has-children":u.length}]},[l("a",{href:`#${v}`,class:"header-anchor","aria-hidden":!0},"#"),l(Re,{class:"vp-catalog-title",to:p},()=>[e.index?`${h+1}.`:null,d&&t?l(t,{icon:d}):null,v||p])]),u.length?l(e.index&&e.indexType==="ol"?"ol":"ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:A,path:I,title:E},k)=>l("li",{class:"vp-child-catalog"},[l("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[l("a",{href:`#${E}`,class:"header-anchor"},"#"),l(Re,{class:"vp-catalog-title",to:I},()=>[e.index?`${h+1}.${k+1}`:null,A&&t?l(t,{icon:A}):null,E||I])]),b.length?l("div",{class:"v-sub-catalogs"},b.map(({icon:y,path:P,title:$},w)=>l(Re,{class:"vp-sub-catalog",to:P},()=>[e.index&&e.indexType!=="ol"?`${h+1}.${k+1}.${w+1}`:null,y&&t?l(t,{icon:y}):null,$||P]))):null]))):null]):l("p",{class:"vp-empty-catalog"},n.value.empty)])}}),jp=Ye({enhance:({app:e})=>{Hp(e),It("AutoCatalog",e)||e.component("AutoCatalog",Fp)}});const zp=l("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[l("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),l("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),kc=B({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=bt(),n=L(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>l("span",[zp,l("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}}),qp={},Up=Ye({enhance({app:e}){e.component("ExternalLinkIcon",l(kc,{locales:qp}))}});/** + */const dn=typeof document<"u";function Md(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function Ro(e,t){const n={};for(const r in t){const o=t[r];n[r]=ut(o)?o.map(e):e(o)}return n}const Gn=()=>{},ut=Array.isArray,zs=/#/g,Bd=/&/g,$d=/\//g,Nd=/=/g,Hd=/\?/g,qs=/\+/g,Fd=/%5B/g,jd=/%5D/g,Us=/%5E/g,zd=/%60/g,Gs=/%7B/g,qd=/%7C/g,Ws=/%7D/g,Ud=/%20/g;function xa(e){return encodeURI(""+e).replace(qd,"|").replace(Fd,"[").replace(jd,"]")}function Gd(e){return xa(e).replace(Gs,"{").replace(Ws,"}").replace(Us,"^")}function Ko(e){return xa(e).replace(qs,"%2B").replace(Ud,"+").replace(zs,"%23").replace(Bd,"%26").replace(zd,"`").replace(Gs,"{").replace(Ws,"}").replace(Us,"^")}function Wd(e){return Ko(e).replace(Nd,"%3D")}function Jd(e){return xa(e).replace(zs,"%23").replace(Hd,"%3F")}function Qd(e){return e==null?"":Jd(e).replace($d,"%2F")}function tr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const Kd=/\/$/,Yd=e=>e.replace(Kd,"");function Do(e,t,n="/"){let r,o={},a="",i="";const s=t.indexOf("#");let c=t.indexOf("?");return s=0&&(c=-1),c>-1&&(r=t.slice(0,c),a=t.slice(c+1,s>-1?s:t.length),o=e(a)),s>-1&&(r=r||t.slice(0,s),i=t.slice(s,t.length)),r=tf(r??t,n),{fullPath:r+(a&&"?")+a+i,path:r,query:o,hash:tr(i)}}function Xd(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function zi(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function Zd(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&An(t.matched[r],n.matched[o])&&Js(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function An(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Js(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!ef(e[n],t[n]))return!1;return!0}function ef(e,t){return ut(e)?qi(e,t):ut(t)?qi(t,e):e===t}function qi(e,t){return ut(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function tf(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let a=n.length-1,i,s;for(i=0;i1&&a--;else break;return n.slice(0,a).join("/")+"/"+r.slice(i).join("/")}var nr;(function(e){e.pop="pop",e.push="push"})(nr||(nr={}));var Wn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Wn||(Wn={}));function nf(e){if(!e)if(dn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Yd(e)}const rf=/^[^#]+#/;function of(e,t){return e.replace(rf,"#")+t}function af(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const uo=()=>({left:window.scrollX,top:window.scrollY});function lf(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=af(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function Ui(e,t){return(history.state?history.state.position-t:-1)+e}const Yo=new Map;function sf(e,t){Yo.set(e,t)}function cf(e){const t=Yo.get(e);return Yo.delete(e),t}let uf=()=>location.protocol+"//"+location.host;function Qs(e,t){const{pathname:n,search:r,hash:o}=t,a=e.indexOf("#");if(a>-1){let s=o.includes(e.slice(a))?e.slice(a).length:1,c=o.slice(s);return c[0]!=="/"&&(c="/"+c),zi(c,"")}return zi(n,e)+r+o}function df(e,t,n,r){let o=[],a=[],i=null;const s=({state:v})=>{const h=Qs(e,location),b=n.value,T=t.value;let I=0;if(v){if(n.value=h,t.value=v,i&&i===b){i=null;return}I=T?v.position-T.position:0}else r(h);o.forEach(E=>{E(n.value,b,{delta:I,type:nr.pop,direction:I?I>0?Wn.forward:Wn.back:Wn.unknown})})};function c(){i=n.value}function u(v){o.push(v);const h=()=>{const b=o.indexOf(v);b>-1&&o.splice(b,1)};return a.push(h),h}function d(){const{history:v}=window;v.state&&v.replaceState(ge({},v.state,{scroll:uo()}),"")}function p(){for(const v of a)v();a=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Gi(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?uo():null}}function ff(e){const{history:t,location:n}=window,r={value:Qs(e,n)},o={value:t.state};o.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(c,u,d){const p=e.indexOf("#"),v=p>-1?(n.host&&document.querySelector("base")?e:e.slice(p))+c:uf()+e+c;try{t[d?"replaceState":"pushState"](u,"",v),o.value=u}catch(h){console.error(h),n[d?"replace":"assign"](v)}}function i(c,u){const d=ge({},t.state,Gi(o.value.back,c,o.value.forward,!0),u,{position:o.value.position});a(c,d,!0),r.value=c}function s(c,u){const d=ge({},o.value,t.state,{forward:c,scroll:uo()});a(d.current,d,!0);const p=ge({},Gi(r.value,c,null),{position:d.position+1},u);a(c,p,!1),r.value=c}return{location:r,state:o,push:s,replace:i}}function pf(e){e=nf(e);const t=ff(e),n=df(e,t.state,t.location,t.replace);function r(a,i=!0){i||n.pauseListeners(),history.go(a)}const o=ge({location:"",base:e,go:r,createHref:of.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function vf(e){return typeof e=="string"||e&&typeof e=="object"}function Ks(e){return typeof e=="string"||typeof e=="symbol"}const At={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ys=Symbol("");var Wi;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Wi||(Wi={}));function Tn(e,t){return ge(new Error,{type:e,[Ys]:!0},t)}function yt(e,t){return e instanceof Error&&Ys in e&&(t==null||!!(e.type&t))}const Ji="[^/]+?",hf={sensitive:!1,strict:!1,start:!0,end:!0},mf=/[.+*?^${}()[\]/\\]/g;function gf(e,t){const n=ge({},hf,t),r=[];let o=n.start?"^":"";const a=[];for(const u of e){const d=u.length?[]:[90];n.strict&&!u.length&&(o+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function bf(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const yf={type:0,value:""},Ef=/[a-zA-Z0-9_]/;function Lf(e){if(!e)return[[]];if(e==="/")return[[yf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${u}": ${h}`)}let n=0,r=n;const o=[];let a;function i(){a&&o.push(a),a=[]}let s=0,c,u="",d="";function p(){u&&(n===0?a.push({type:0,value:u}):n===1||n===2||n===3?(a.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),a.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function v(){u+=c}for(;s{i(k)}:Gn}function i(d){if(Ks(d)){const p=r.get(d);p&&(r.delete(d),n.splice(n.indexOf(p),1),p.children.forEach(i),p.alias.forEach(i))}else{const p=n.indexOf(d);p>-1&&(n.splice(p,1),d.record.name&&r.delete(d.record.name),d.children.forEach(i),d.alias.forEach(i))}}function s(){return n}function c(d){let p=0;for(;p=0&&(d.record.path!==n[p].record.path||!Xs(d,n[p]));)p++;n.splice(p,0,d),d.record.name&&!Yi(d)&&r.set(d.record.name,d)}function u(d,p){let v,h={},b,T;if("name"in d&&d.name){if(v=r.get(d.name),!v)throw Tn(1,{location:d});T=v.record.name,h=ge(Ki(p.params,v.keys.filter(k=>!k.optional).concat(v.parent?v.parent.keys.filter(k=>k.optional):[]).map(k=>k.name)),d.params&&Ki(d.params,v.keys.map(k=>k.name))),b=v.stringify(h)}else if(d.path!=null)b=d.path,v=n.find(k=>k.re.test(b)),v&&(h=v.parse(b),T=v.record.name);else{if(v=p.name?r.get(p.name):n.find(k=>k.re.test(p.path)),!v)throw Tn(1,{location:d,currentLocation:p});T=v.record.name,h=ge({},p.params,d.params),b=v.stringify(h)}const I=[];let E=v;for(;E;)I.unshift(E.record),E=E.parent;return{name:T,path:b,params:h,matched:I,meta:kf(I)}}return e.forEach(d=>a(d)),{addRoute:a,resolve:u,removeRoute:i,getRoutes:s,getRecordMatcher:o}}function Ki(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function If(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:wf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function wf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function Yi(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function kf(e){return e.reduce((t,n)=>ge(t,n.meta),{})}function Xi(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Xs(e,t){return t.children.some(n=>n===e||Xs(e,n))}function Pf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;oa&&Ko(a)):[r&&Ko(r)]).forEach(a=>{a!==void 0&&(t+=(t.length?"&":"")+n,a!=null&&(t+="="+a))})}return t}function Of(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=ut(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Rf=Symbol(""),el=Symbol(""),fo=Symbol(""),Ca=Symbol(""),Xo=Symbol("");function Mn(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Ht(e,t,n,r,o,a=i=>i()){const i=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((s,c)=>{const u=v=>{v===!1?c(Tn(4,{from:n,to:t})):v instanceof Error?c(v):vf(v)?c(Tn(2,{from:t,to:v})):(i&&r.enterCallbacks[o]===i&&typeof v=="function"&&i.push(v),s())},d=a(()=>e.call(r&&r.instances[o],t,n,u));let p=Promise.resolve(d);e.length<3&&(p=p.then(u)),p.catch(v=>c(v))})}function So(e,t,n,r,o=a=>a()){const a=[];for(const i of e)for(const s in i.components){let c=i.components[s];if(!(t!=="beforeRouteEnter"&&!i.instances[s]))if(Df(c)){const d=(c.__vccOpts||c)[t];d&&a.push(Ht(d,n,r,i,s,o))}else{let u=c();a.push(()=>u.then(d=>{if(!d)return Promise.reject(new Error(`Couldn't resolve component "${s}" at "${i.path}"`));const p=Md(d)?d.default:d;i.components[s]=p;const h=(p.__vccOpts||p)[t];return h&&Ht(h,n,r,i,s,o)()}))}}return a}function Df(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Zo(e){const t=he(fo),n=he(Ca),r=L(()=>t.resolve(_t(e.to))),o=L(()=>{const{matched:c}=r.value,{length:u}=c,d=c[u-1],p=n.matched;if(!d||!p.length)return-1;const v=p.findIndex(An.bind(null,d));if(v>-1)return v;const h=tl(c[u-2]);return u>1&&tl(d)===h&&p[p.length-1].path!==h?p.findIndex(An.bind(null,c[u-2])):v}),a=L(()=>o.value>-1&&Vf(n.params,r.value.params)),i=L(()=>o.value>-1&&o.value===n.matched.length-1&&Js(n.params,r.value.params));function s(c={}){return Cf(c)?t[_t(e.replace)?"replace":"push"](_t(e.to)).catch(Gn):Promise.resolve()}return{route:r,href:L(()=>r.value.href),isActive:a,isExactActive:i,navigate:s}}const Sf=B({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:Zo,setup(e,{slots:t}){const n=lr(Zo(e)),{options:r}=he(fo),o=L(()=>({[nl(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[nl(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const a=t.default&&t.default(n);return e.custom?a:l("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},a)}}}),xf=Sf;function Cf(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Vf(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!ut(o)||o.length!==r.length||r.some((a,i)=>a!==o[i]))return!1}return!0}function tl(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const nl=(e,t,n)=>e??t??n,Mf=B({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=he(Xo),o=L(()=>e.route||r.value),a=he(el,0),i=L(()=>{let u=_t(a);const{matched:d}=o.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),s=L(()=>o.value.matched[i.value]);ct(el,L(()=>i.value+1)),ct(Rf,s),ct(Xo,o);const c=Y();return ve(()=>[c.value,s.value,e.name],([u,d,p],[v,h,b])=>{d&&(d.instances[p]=u,h&&h!==d&&u&&u===v&&(d.leaveGuards.size||(d.leaveGuards=h.leaveGuards),d.updateGuards.size||(d.updateGuards=h.updateGuards))),u&&d&&(!h||!An(d,h)||!v)&&(d.enterCallbacks[p]||[]).forEach(T=>T(u))},{flush:"post"}),()=>{const u=o.value,d=e.name,p=s.value,v=p&&p.components[d];if(!v)return rl(n.default,{Component:v,route:u});const h=p.props[d],b=h?h===!0?u.params:typeof h=="function"?h(u):h:null,I=l(v,ge({},b,t,{onVnodeUnmounted:E=>{E.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return rl(n.default,{Component:I,route:u})||I}}});function rl(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Zs=Mf;function Bf(e){const t=Tf(e.routes,e),n=e.parseQuery||Pf,r=e.stringifyQuery||Zi,o=e.history,a=Mn(),i=Mn(),s=Mn(),c=ft(At);let u=At;dn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=Ro.bind(null,R=>""+R),p=Ro.bind(null,Qd),v=Ro.bind(null,tr);function h(R,z){let F,G;return Ks(R)?(F=t.getRecordMatcher(R),G=z):G=R,t.addRoute(G,F)}function b(R){const z=t.getRecordMatcher(R);z&&t.removeRoute(z)}function T(){return t.getRoutes().map(R=>R.record)}function I(R){return!!t.getRecordMatcher(R)}function E(R,z){if(z=ge({},z||c.value),typeof R=="string"){const g=Do(n,R,z.path),A=t.resolve({path:g.path},z),D=o.createHref(g.fullPath);return ge(g,A,{params:v(A.params),hash:tr(g.hash),redirectedFrom:void 0,href:D})}let F;if(R.path!=null)F=ge({},R,{path:Do(n,R.path,z.path).path});else{const g=ge({},R.params);for(const A in g)g[A]==null&&delete g[A];F=ge({},R,{params:p(g)}),z.params=p(z.params)}const G=t.resolve(F,z),me=R.hash||"";G.params=d(v(G.params));const Le=Xd(r,ge({},R,{hash:Gd(me),path:G.path})),m=o.createHref(Le);return ge({fullPath:Le,hash:me,query:r===Zi?Of(R.query):R.query||{}},G,{redirectedFrom:void 0,href:m})}function k(R){return typeof R=="string"?Do(n,R,c.value.path):ge({},R)}function y(R,z){if(u!==R)return Tn(8,{from:z,to:R})}function P(R){return U(R)}function $(R){return P(ge(k(R),{replace:!0}))}function w(R){const z=R.matched[R.matched.length-1];if(z&&z.redirect){const{redirect:F}=z;let G=typeof F=="function"?F(R):F;return typeof G=="string"&&(G=G.includes("?")||G.includes("#")?G=k(G):{path:G},G.params={}),ge({query:R.query,hash:R.hash,params:G.path!=null?{}:R.params},G)}}function U(R,z){const F=u=E(R),G=c.value,me=R.state,Le=R.force,m=R.replace===!0,g=w(F);if(g)return U(ge(k(g),{state:typeof g=="object"?ge({},me,g.state):me,force:Le,replace:m}),z||F);const A=F;A.redirectedFrom=z;let D;return!Le&&Zd(r,G,F)&&(D=Tn(16,{to:A,from:G}),vt(G,G,!0,!1)),(D?Promise.resolve(D):x(A,G)).catch(O=>yt(O)?yt(O,2)?O:Rt(O):W(O,A,G)).then(O=>{if(O){if(yt(O,2))return U(ge({replace:m},k(O.to),{state:typeof O.to=="object"?ge({},me,O.to.state):me,force:Le}),z||A)}else O=M(A,G,!0,m,me);return X(A,G,O),O})}function H(R,z){const F=y(R,z);return F?Promise.reject(F):Promise.resolve()}function J(R){const z=sn.values().next().value;return z&&typeof z.runWithContext=="function"?z.runWithContext(R):R()}function x(R,z){let F;const[G,me,Le]=$f(R,z);F=So(G.reverse(),"beforeRouteLeave",R,z);for(const g of G)g.leaveGuards.forEach(A=>{F.push(Ht(A,R,z))});const m=H.bind(null,R,z);return F.push(m),Me(F).then(()=>{F=[];for(const g of a.list())F.push(Ht(g,R,z));return F.push(m),Me(F)}).then(()=>{F=So(me,"beforeRouteUpdate",R,z);for(const g of me)g.updateGuards.forEach(A=>{F.push(Ht(A,R,z))});return F.push(m),Me(F)}).then(()=>{F=[];for(const g of Le)if(g.beforeEnter)if(ut(g.beforeEnter))for(const A of g.beforeEnter)F.push(Ht(A,R,z));else F.push(Ht(g.beforeEnter,R,z));return F.push(m),Me(F)}).then(()=>(R.matched.forEach(g=>g.enterCallbacks={}),F=So(Le,"beforeRouteEnter",R,z,J),F.push(m),Me(F))).then(()=>{F=[];for(const g of i.list())F.push(Ht(g,R,z));return F.push(m),Me(F)}).catch(g=>yt(g,8)?g:Promise.reject(g))}function X(R,z,F){s.list().forEach(G=>J(()=>G(R,z,F)))}function M(R,z,F,G,me){const Le=y(R,z);if(Le)return Le;const m=z===At,g=dn?history.state:{};F&&(G||m?o.replace(R.fullPath,ge({scroll:m&&g&&g.scroll},me)):o.push(R.fullPath,me)),c.value=R,vt(R,z,F,m),Rt()}let te;function Pe(){te||(te=o.listen((R,z,F)=>{if(!Ir.listening)return;const G=E(R),me=w(G);if(me){U(ge(me,{replace:!0}),G).catch(Gn);return}u=G;const Le=c.value;dn&&sf(Ui(Le.fullPath,F.delta),uo()),x(G,Le).catch(m=>yt(m,12)?m:yt(m,2)?(U(m.to,G).then(g=>{yt(g,20)&&!F.delta&&F.type===nr.pop&&o.go(-1,!1)}).catch(Gn),Promise.reject()):(F.delta&&o.go(-F.delta,!1),W(m,G,Le))).then(m=>{m=m||M(G,Le,!1),m&&(F.delta&&!yt(m,8)?o.go(-F.delta,!1):F.type===nr.pop&&yt(m,20)&&o.go(-1,!1)),X(G,Le,m)}).catch(Gn)}))}let Oe=Mn(),Q=Mn(),oe;function W(R,z,F){Rt(R);const G=Q.list();return G.length?G.forEach(me=>me(R,z,F)):console.error(R),Promise.reject(R)}function at(){return oe&&c.value!==At?Promise.resolve():new Promise((R,z)=>{Oe.add([R,z])})}function Rt(R){return oe||(oe=!R,Pe(),Oe.list().forEach(([z,F])=>R?F(R):z()),Oe.reset()),R}function vt(R,z,F,G){const{scrollBehavior:me}=e;if(!dn||!me)return Promise.resolve();const Le=!F&&cf(Ui(R.fullPath,0))||(G||!F)&&history.state&&history.state.scroll||null;return rn().then(()=>me(R,z,Le)).then(m=>m&&lf(m)).catch(m=>W(m,R,z))}const Fe=R=>o.go(R);let ln;const sn=new Set,Ir={currentRoute:c,listening:!0,addRoute:h,removeRoute:b,hasRoute:I,getRoutes:T,resolve:E,options:e,push:P,replace:$,go:Fe,back:()=>Fe(-1),forward:()=>Fe(1),beforeEach:a.add,beforeResolve:i.add,afterEach:s.add,onError:Q.add,isReady:at,install(R){const z=this;R.component("RouterLink",xf),R.component("RouterView",Zs),R.config.globalProperties.$router=z,Object.defineProperty(R.config.globalProperties,"$route",{enumerable:!0,get:()=>_t(c)}),dn&&!ln&&c.value===At&&(ln=!0,P(o.location).catch(me=>{}));const F={};for(const me in At)Object.defineProperty(F,me,{get:()=>c.value[me],enumerable:!0});R.provide(fo,z),R.provide(Ca,Hl(F)),R.provide(Xo,c);const G=R.unmount;sn.add(R),R.unmount=function(){sn.delete(R),sn.size<1&&(u=At,te&&te(),te=null,c.value=At,ln=!1,oe=!1),G()}}};function Me(R){return R.reduce((z,F)=>z.then(()=>J(F)),Promise.resolve())}return Ir}function $f(e,t){const n=[],r=[],o=[],a=Math.max(t.matched.length,e.matched.length);for(let i=0;iAn(u,s))?r.push(s):n.push(s));const c=e.matched[i];c&&(t.matched.find(u=>An(u,c))||o.push(c))}return[n,r,o]}function Ne(){return he(fo)}function pt(){return he(Ca)}const ue=({name:e="",color:t="currentColor"},{slots:n})=>{var r;return l("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(r=n.default)==null?void 0:r.call(n))};ue.displayName="IconBase";const ec=({size:e=48,stroke:t=4,wrapper:n=!0,height:r=2*e})=>{const o=l("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[l("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),l("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[l("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),l("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return n?l("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${r}px`},o):o};ec.displayName="LoadingIcon";const tc=(e,{slots:t})=>{var n;return(n=t.default)==null?void 0:n.call(t)},Va=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},po=(e,t)=>{let n=1;for(let r=0;r>6;return n+=n<<3,n^=n>>11,n%t},nc=Array.isArray,Nf=e=>typeof e=="function",Hf=e=>typeof e=="string";var Ff=e=>e.startsWith("ftp://"),Ma=e=>/^(https?:)?\/\//.test(e),jf=/.md((\?|#).*)?$/,zf=(e,t="/")=>!!(Ma(e)||Ff(e)||e.startsWith("/")&&!e.startsWith(t)&&!jf.test(e)),rc=e=>Object.prototype.toString.call(e)==="[object Object]";function qf(){const e=Y(!1);return On()&&ye(()=>{e.value=!0}),e}function Uf(e){return qf(),L(()=>!!e())}const kt=e=>typeof e=="string",rr=(e,t)=>kt(e)&&e.startsWith(t),un=(e,t)=>kt(e)&&e.endsWith(t),Dn=Object.entries,Gf=Object.fromEntries,dt=Object.keys,Wf=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),oc=e=>{const[t,n=""]=e.split("#");return t?`${Wf(t)}${n?`#${n}`:""}`:e},ol=e=>rc(e)&&kt(e.name),or=(e,t=!1)=>e?nc(e)?e.map(n=>kt(n)?{name:n}:ol(n)?n:null).filter(n=>n!==null):kt(e)?[{name:e}]:ol(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],ac=(e,t)=>{if(e){if(nc(e)&&e.every(kt))return e;if(kt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},ic=e=>ac(e,"category"),lc=e=>ac(e,"tag"),vo=e=>rr(e,"/");class Jf{constructor(){this.messageElements={};const t="message-container",n=document.getElementById(t);n?this.containerElement=n:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,n=2e3){const r=document.createElement("div"),o=Date.now();return r.className="message move-in",r.innerHTML=t,this.containerElement.appendChild(r),this.messageElements[o]=r,n>0&&setTimeout(()=>{this.close(o)},n),o}close(t){if(t){const n=this.messageElements[t];n.classList.remove("move-in"),n.classList.add("move-out"),n.addEventListener("animationend",()=>{n.remove(),delete this.messageElements[t]})}else dt(this.messageElements).forEach(n=>this.close(Number(n)))}destroy(){document.body.removeChild(this.containerElement)}}const sc=/#.*$/u,Qf=e=>{const t=sc.exec(e);return t?t[0]:""},al=e=>decodeURI(e).replace(sc,"").replace(/(index)?\.(md|html)$/,""),Ba=(e,t)=>{if(t===void 0)return!1;const n=al(e.path),r=al(t),o=Qf(t);return o?o===e.hash&&(!r||n===r):n===r},Kf=e=>Ma(e)?e:`https://github.com/${e}`,cc=e=>!Ma(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,In=(e,...t)=>{const n=e.resolve(...t),r=n.matched[n.matched.length-1];if(!(r!=null&&r.redirect))return n;const{redirect:o}=r,a=Nf(o)?o(n):o,i=Hf(a)?{path:a}:a;return In(e,{hash:n.hash,query:n.query,params:n.params,...i})},Yf=e=>{var t;if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)&&!(e.currentTarget&&((t=e.currentTarget.getAttribute("target"))!=null&&t.match(/\b_blank\b/i))))return e.preventDefault(),!0},Re=({to:e="",class:t="",...n},{slots:r})=>{var i;const o=Ne(),a=(s={})=>Yf(s)?o.push(e).catch():Promise.resolve();return l("a",{...n,class:["vp-link",t],href:Ie(oc(e)),onClick:a},(i=r.default)==null?void 0:i.call(r))};Re.displayName="VPLink";const uc=()=>l(ue,{name:"github"},()=>l("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));uc.displayName="GitHubIcon";const dc=()=>l(ue,{name:"gitlab"},()=>l("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));dc.displayName="GitLabIcon";const fc=()=>l(ue,{name:"gitee"},()=>l("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));fc.displayName="GiteeIcon";const pc=()=>l(ue,{name:"bitbucket"},()=>l("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));pc.displayName="BitbucketIcon";const vc=()=>l(ue,{name:"source"},()=>l("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));vc.displayName="SourceIcon";const It=(e,t)=>{const n=t?t._instance:On();return rc(n==null?void 0:n.appContext.components)&&(e in n.appContext.components||ot(e)in n.appContext.components||ir(ot(e))in n.appContext.components)},Xf=()=>Uf(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),Zf=()=>{const e=Xf();return L(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},Sn=e=>{const t=bt();return L(()=>e[t.value])};function il(e,t){var n;const r=ft();return Aa(()=>{r.value=e()},{...t,flush:(n=t==null?void 0:t.flush)!=null?n:"sync"}),Ut(r)}function ho(e,t){let n,r,o;const a=Y(!0),i=()=>{a.value=!0,o()};ve(e,i,{flush:"sync"});const s=typeof t=="function"?t:t.get,c=typeof t=="function"?void 0:t.set,u=Gl((d,p)=>(r=d,o=p,{get(){return a.value&&(n=s(),a.value=!1),r(),n},set(v){c==null||c(v)}}));return Object.isExtensible(u)&&(u.trigger=i),u}function an(e){return Pl()?(n0(e),!0):!1}function rt(e){return typeof e=="function"?e():_t(e)}const vr=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const ep=Object.prototype.toString,tp=e=>ep.call(e)==="[object Object]",en=()=>{},ea=np();function np(){var e,t;return vr&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||((t=window==null?void 0:window.navigator)==null?void 0:t.maxTouchPoints)>2&&/iPad|Macintosh/.test(window==null?void 0:window.navigator.userAgent))}function hc(e,t){function n(...r){return new Promise((o,a)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(a)})}return n}const mc=e=>e();function rp(...e){let t=0,n,r=!0,o=en,a,i,s,c,u;!Ve(e[0])&&typeof e[0]=="object"?{delay:i,trailing:s=!0,leading:c=!0,rejectOnCancel:u=!1}=e[0]:[i,s=!0,c=!0,u=!1]=e;const d=()=>{n&&(clearTimeout(n),n=void 0,o(),o=en)};return v=>{const h=rt(i),b=Date.now()-t,T=()=>a=v();return d(),h<=0?(t=Date.now(),T()):(b>h&&(c||!r)?(t=Date.now(),T()):s&&(a=new Promise((I,E)=>{o=u?E:I,n=setTimeout(()=>{t=Date.now(),r=!0,I(T()),d()},Math.max(0,h-b))})),!c&&!n&&(n=setTimeout(()=>r=!0,h)),r=!1,a)}}function op(e=mc){const t=Y(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...a)=>{t.value&&e(...a)};return{isActive:Ut(t),pause:n,resume:r,eventFilter:o}}function ap(e){let t;function n(){return t||(t=e()),t}return n.reset=async()=>{const r=t;t=void 0,r&&await r},n}function ip(e){return e||On()}function lp(...e){if(e.length!==1)return Pn(...e);const t=e[0];return typeof t=="function"?Ut(Gl(()=>({get:t,set:en}))):Y(t)}function sp(e,t=200,n=!1,r=!0,o=!1){return hc(rp(t,n,r,o),e)}function cp(e,t,n={}){const{eventFilter:r=mc,...o}=n;return ve(e,hc(r,t),o)}function up(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:a,pause:i,resume:s,isActive:c}=op(r);return{stop:cp(e,t,{...o,eventFilter:a}),pause:i,resume:s,isActive:c}}function mo(e,t=!0,n){ip()?ye(e,n):t?e():rn(e)}function dp(e,t,n={}){const{immediate:r=!0}=n,o=Y(!1);let a=null;function i(){a&&(clearTimeout(a),a=null)}function s(){o.value=!1,i()}function c(...u){i(),o.value=!0,a=setTimeout(()=>{o.value=!1,a=null,e(...u)},rt(t))}return r&&(o.value=!0,vr&&c()),an(s),{isPending:Ut(o),start:c,stop:s}}function ll(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=Ve(e),a=Y(e);function i(s){if(arguments.length)return a.value=s,a.value;{const c=rt(n);return a.value=a.value===c?rt(r):c,a.value}}return o?i:[a,i]}function Qe(e){var t;const n=rt(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Pt=vr?window:void 0,gc=vr?window.document:void 0,_c=vr?window.navigator:void 0;function Se(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=Pt):[t,n,r,o]=e,!t)return en;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const a=[],i=()=>{a.forEach(d=>d()),a.length=0},s=(d,p,v,h)=>(d.addEventListener(p,v,h),()=>d.removeEventListener(p,v,h)),c=ve(()=>[Qe(t),rt(o)],([d,p])=>{if(i(),!d)return;const v=tp(p)?{...p}:p;a.push(...n.flatMap(h=>r.map(b=>s(d,h,b,v))))},{immediate:!0,flush:"post"}),u=()=>{c(),i()};return an(u),u}let sl=!1;function fp(e,t,n={}){const{window:r=Pt,ignore:o=[],capture:a=!0,detectIframe:i=!1}=n;if(!r)return en;ea&&!sl&&(sl=!0,Array.from(r.document.body.children).forEach(v=>v.addEventListener("click",en)),r.document.documentElement.addEventListener("click",en));let s=!0;const c=v=>o.some(h=>{if(typeof h=="string")return Array.from(r.document.querySelectorAll(h)).some(b=>b===v.target||v.composedPath().includes(b));{const b=Qe(h);return b&&(v.target===b||v.composedPath().includes(b))}}),d=[Se(r,"click",v=>{const h=Qe(e);if(!(!h||h===v.target||v.composedPath().includes(h))){if(v.detail===0&&(s=!c(v)),!s){s=!0;return}t(v)}},{passive:!0,capture:a}),Se(r,"pointerdown",v=>{const h=Qe(e);s=!c(v)&&!!(h&&!v.composedPath().includes(h))},{passive:!0}),i&&Se(r,"blur",v=>{setTimeout(()=>{var h;const b=Qe(e);((h=r.document.activeElement)==null?void 0:h.tagName)==="IFRAME"&&!(b!=null&&b.contains(r.document.activeElement))&&t(v)},0)})].filter(Boolean);return()=>d.forEach(v=>v())}function pp(){const e=Y(!1),t=On();return t&&ye(()=>{e.value=!0},t),e}function hr(e){const t=pp();return L(()=>(t.value,!!e()))}function bc(e,t={}){const{window:n=Pt}=t,r=hr(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const a=Y(!1),i=u=>{a.value=u.matches},s=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},c=Aa(()=>{r.value&&(s(),o=n.matchMedia(rt(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),a.value=o.matches)});return an(()=>{c(),s(),o=void 0}),a}function cl(e,t={}){const{controls:n=!1,navigator:r=_c}=t,o=hr(()=>r&&"permissions"in r);let a;const i=typeof e=="string"?{name:e}:e,s=Y(),c=()=>{a&&(s.value=a.state)},u=ap(async()=>{if(o.value){if(!a)try{a=await r.permissions.query(i),Se(a,"change",c),c()}catch{s.value="prompt"}return a}});return u(),n?{state:s,isSupported:o,query:u}:s}function vp(e={}){const{navigator:t=_c,read:n=!1,source:r,copiedDuring:o=1500,legacy:a=!1}=e,i=hr(()=>t&&"clipboard"in t),s=cl("clipboard-read"),c=cl("clipboard-write"),u=L(()=>i.value||a),d=Y(""),p=Y(!1),v=dp(()=>p.value=!1,o);function h(){i.value&&E(s.value)?t.clipboard.readText().then(k=>{d.value=k}):d.value=I()}u.value&&n&&Se(["copy","cut"],h);async function b(k=rt(r)){u.value&&k!=null&&(i.value&&E(c.value)?await t.clipboard.writeText(k):T(k),d.value=k,p.value=!0,v.start())}function T(k){const y=document.createElement("textarea");y.value=k??"",y.style.position="absolute",y.style.opacity="0",document.body.appendChild(y),y.select(),document.execCommand("copy"),y.remove()}function I(){var k,y,P;return(P=(y=(k=document==null?void 0:document.getSelection)==null?void 0:k.call(document))==null?void 0:y.toString())!=null?P:""}function E(k){return k==="granted"||k==="prompt"}return{isSupported:u,text:d,copied:p,copy:b}}const Vr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},Mr="__vueuse_ssr_handlers__",hp=mp();function mp(){return Mr in Vr||(Vr[Mr]=Vr[Mr]||{}),Vr[Mr]}function gp(e,t){return hp[e]||t}function _p(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const bp={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},ul="vueuse-storage";function yc(e,t,n,r={}){var o;const{flush:a="pre",deep:i=!0,listenToStorageChanges:s=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=Pt,eventFilter:v,onError:h=x=>{console.error(x)},initOnMounted:b}=r,T=(d?ft:Y)(typeof t=="function"?t():t);if(!n)try{n=gp("getDefaultStorage",()=>{var x;return(x=Pt)==null?void 0:x.localStorage})()}catch(x){h(x)}if(!n)return T;const I=rt(t),E=_p(I),k=(o=r.serializer)!=null?o:bp[E],{pause:y,resume:P}=up(T,()=>w(T.value),{flush:a,deep:i,eventFilter:v});p&&s&&mo(()=>{Se(p,"storage",H),Se(p,ul,J),b&&H()}),b||H();function $(x,X){p&&p.dispatchEvent(new CustomEvent(ul,{detail:{key:e,oldValue:x,newValue:X,storageArea:n}}))}function w(x){try{const X=n.getItem(e);if(x==null)$(X,null),n.removeItem(e);else{const M=k.write(x);X!==M&&(n.setItem(e,M),$(X,M))}}catch(X){h(X)}}function U(x){const X=x?x.newValue:n.getItem(e);if(X==null)return c&&I!=null&&n.setItem(e,k.write(I)),I;if(!x&&u){const M=k.read(X);return typeof u=="function"?u(M,I):E==="object"&&!Array.isArray(M)?{...I,...M}:M}else return typeof X!="string"?X:k.read(X)}function H(x){if(!(x&&x.storageArea!==n)){if(x&&x.key==null){T.value=I;return}if(!(x&&x.key!==e)){y();try{(x==null?void 0:x.newValue)!==k.write(T.value)&&(T.value=U(x))}catch(X){h(X)}finally{x?rn(P):P()}}}}function J(x){H(x.detail)}return T}function yp(e){return bc("(prefers-color-scheme: dark)",e)}function Ep(e,t,n={}){const{window:r=Pt,...o}=n;let a;const i=hr(()=>r&&"ResizeObserver"in r),s=()=>{a&&(a.disconnect(),a=void 0)},c=L(()=>Array.isArray(e)?e.map(p=>Qe(p)):[Qe(e)]),u=ve(c,p=>{if(s(),i.value&&r){a=new ResizeObserver(t);for(const v of p)v&&a.observe(v,o)}},{immediate:!0,flush:"post"}),d=()=>{s(),u()};return an(d),{isSupported:i,stop:d}}function Lp(e,t={width:0,height:0},n={}){const{window:r=Pt,box:o="content-box"}=n,a=L(()=>{var p,v;return(v=(p=Qe(e))==null?void 0:p.namespaceURI)==null?void 0:v.includes("svg")}),i=Y(t.width),s=Y(t.height),{stop:c}=Ep(e,([p])=>{const v=o==="border-box"?p.borderBoxSize:o==="content-box"?p.contentBoxSize:p.devicePixelContentBoxSize;if(r&&a.value){const h=Qe(e);if(h){const b=r.getComputedStyle(h);i.value=Number.parseFloat(b.width),s.value=Number.parseFloat(b.height)}}else if(v){const h=Array.isArray(v)?v:[v];i.value=h.reduce((b,{inlineSize:T})=>b+T,0),s.value=h.reduce((b,{blockSize:T})=>b+T,0)}else i.value=p.contentRect.width,s.value=p.contentRect.height},n);mo(()=>{const p=Qe(e);p&&(i.value="offsetWidth"in p?p.offsetWidth:t.width,s.value="offsetHeight"in p?p.offsetHeight:t.height)});const u=ve(()=>Qe(e),p=>{i.value=p?t.width:0,s.value=p?t.height:0});function d(){c(),u()}return{width:i,height:s,stop:d}}const dl=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function $a(e,t={}){const{document:n=gc,autoExit:r=!1}=t,o=L(()=>{var E;return(E=Qe(e))!=null?E:n==null?void 0:n.querySelector("html")}),a=Y(!1),i=L(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(E=>n&&E in n||o.value&&E in o.value)),s=L(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(E=>n&&E in n||o.value&&E in o.value)),c=L(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(E=>n&&E in n||o.value&&E in o.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(E=>n&&E in n),d=hr(()=>o.value&&n&&i.value!==void 0&&s.value!==void 0&&c.value!==void 0),p=()=>u?(n==null?void 0:n[u])===o.value:!1,v=()=>{if(c.value){if(n&&n[c.value]!=null)return n[c.value];{const E=o.value;if((E==null?void 0:E[c.value])!=null)return!!E[c.value]}}return!1};async function h(){if(!(!d.value||!a.value)){if(s.value)if((n==null?void 0:n[s.value])!=null)await n[s.value]();else{const E=o.value;(E==null?void 0:E[s.value])!=null&&await E[s.value]()}a.value=!1}}async function b(){if(!d.value||a.value)return;v()&&await h();const E=o.value;i.value&&(E==null?void 0:E[i.value])!=null&&(await E[i.value](),a.value=!0)}async function T(){await(a.value?h():b())}const I=()=>{const E=v();(!E||E&&p())&&(a.value=E)};return Se(n,dl,I,!1),Se(()=>Qe(o),dl,I,!1),r&&an(h),{isSupported:d,isFullscreen:a,enter:b,exit:h,toggle:T}}function xo(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function Ec(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}const Br=new WeakMap;function Lc(e,t=!1){const n=Y(t);let r=null;ve(lp(e),i=>{const s=xo(rt(i));if(s){const c=s;Br.get(c)||Br.set(c,c.style.overflow),n.value&&(c.style.overflow="hidden")}},{immediate:!0});const o=()=>{const i=xo(rt(e));!i||n.value||(ea&&(r=Se(i,"touchmove",s=>{Ap(s)},{passive:!1})),i.style.overflow="hidden",n.value=!0)},a=()=>{var i;const s=xo(rt(e));!s||!n.value||(ea&&(r==null||r()),s.style.overflow=(i=Br.get(s))!=null?i:"",Br.delete(s),n.value=!1)};return an(a),L({get(){return n.value},set(i){i?o():a()}})}let Tp=0;function Ip(e,t={}){const n=Y(!1),{document:r=gc,immediate:o=!0,manual:a=!1,id:i=`vueuse_styletag_${++Tp}`}=t,s=Y(e);let c=()=>{};const u=()=>{if(!r)return;const p=r.getElementById(i)||r.createElement("style");p.isConnected||(p.id=i,t.media&&(p.media=t.media),r.head.appendChild(p)),!n.value&&(c=ve(s,v=>{p.textContent=v},{immediate:!0}),n.value=!0)},d=()=>{!r||!n.value||(c(),r.head.removeChild(r.getElementById(i)),n.value=!1)};return o&&!a&&mo(u),a||an(d),{id:i,css:s,unload:d,load:u,isLoaded:Ut(n)}}function wp(e={}){const{window:t=Pt,behavior:n="auto"}=e;if(!t)return{x:Y(0),y:Y(0)};const r=Y(t.scrollX),o=Y(t.scrollY),a=L({get(){return r.value},set(s){scrollTo({left:s,behavior:n})}}),i=L({get(){return o.value},set(s){scrollTo({top:s,behavior:n})}});return Se(t,"scroll",()=>{r.value=t.scrollX,o.value=t.scrollY},{capture:!1,passive:!0}),{x:a,y:i}}function kp(e={}){const{window:t=Pt,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:a=!0}=e,i=Y(n),s=Y(r),c=()=>{t&&(a?(i.value=t.innerWidth,s.value=t.innerHeight):(i.value=t.document.documentElement.clientWidth,s.value=t.document.documentElement.clientHeight))};if(c(),mo(c),Se("resize",c,{passive:!0}),o){const u=bc("(orientation: portrait)");ve(u,()=>c())}return{width:i,height:s}}const Ac=({type:e="info",text:t="",vertical:n,color:r},{slots:o})=>{var a;return l("span",{class:["vp-badge",e,{diy:r}],style:{verticalAlign:n??!1,backgroundColor:r??!1}},((a=o.default)==null?void 0:a.call(o))||t)};Ac.displayName="Badge";var Pp=B({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=L(()=>{const r=["font-icon icon"],o=`iconfont icon-${e.icon}`;return r.push(o),r}),n=L(()=>{const r={};return e.color&&(r.color=e.color),e.size&&(r["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),dt(r).length?r:null});return()=>e.icon?l("span",{key:e.icon,class:t.value,style:n.value}):null}});const Tc=()=>l(ue,{name:"back-to-top"},()=>[l("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),l("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);Tc.displayName="BackToTopIcon";var Op=B({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),n=Sn({"/en/":{backToTop:"Back to top"},"/":{backToTop:"返回顶部"}}),r=ft(),{height:o}=Lp(r),{height:a}=kp(),{y:i}=wp(),s=L(()=>t.value.backToTop!==!1&&i.value>e.threshold),c=L(()=>i.value/(o.value-a.value));return ye(()=>{r.value=document.body}),()=>l(wt,{name:"fade"},()=>s.value?l("button",{type:"button",class:"vp-back-to-top-button","aria-label":n.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:l("svg",{class:"vp-scroll-progress"},l("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),l(Tc)]):null)}});const Rp=Ye({enhance:({app:e})=>{It("Badge")||e.component("Badge",Ac),It("FontIcon")||e.component("FontIcon",Pp)},setup:()=>{Ip(` @import url("https://at.alicdn.com/t/c/font_2410206_5vb9zlyghj.css"); + `)},rootComponents:[()=>l(Op,{})]});function Dp(e,t,n){var r,o,a;t===void 0&&(t=50),n===void 0&&(n={});var i=(r=n.isImmediate)!=null&&r,s=(o=n.callback)!=null&&o,c=n.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var h=Date.now()-u;if(h+t>=c)return c-h}return t}var v=function(){var h=[].slice.call(arguments),b=this;return new Promise(function(T,I){var E=i&&a===void 0;if(a!==void 0&&clearTimeout(a),a=setTimeout(function(){if(a=void 0,u=Date.now(),!i){var y=e.apply(b,h);s&&s(y),d.forEach(function(P){return(0,P.resolve)(y)}),d=[]}},p()),E){var k=e.apply(b,h);return s&&s(k),T(k)}d.push({resolve:T,reject:I})})};return v.cancel=function(h){a!==void 0&&clearTimeout(a),d.forEach(function(b){return(0,b.reject)(h)}),d=[]},v}const Sp=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=Ne(),i=Dp(()=>{var T,I;const s=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(s-0)v.some(k=>k.hash===E.hash));for(let E=0;E=(((T=k.parentElement)==null?void 0:T.offsetTop)??0)-r,$=!y||s<(((I=y.parentElement)==null?void 0:I.offsetTop)??0)-r;if(!(P&&$))continue;const U=decodeURIComponent(o.currentRoute.value.hash),H=decodeURIComponent(k.hash);if(U===H)return;if(p){for(let J=E+1;J{window.addEventListener("scroll",i)}),wa(()=>{window.removeEventListener("scroll",i)})},fl=async(e,t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=n)},xp=".vp-sidebar-link, .toc-link",Cp=".header-anchor",Vp=200,Mp=5,Bp=Ye({setup(){Sp({headerLinkSelector:xp,headerAnchorSelector:Cp,delay:Vp,offset:Mp})}});let Ic=()=>null;const wc=Symbol(""),$p=e=>{Ic=e},Np=()=>he(wc),Hp=e=>{e.provide(wc,Ic)};var Fp=B({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean,indexType:{type:String,default:"ul"},hideHeading:Boolean},setup(e){const t=Np(),n=Sn({"/en/":{title:"Catalog",empty:"No catalog"},"/":{title:"目录",empty:"暂无目录"}}),r=ie(),o=Ne(),a=Hs(),i=u=>{const d=u.I;return typeof d>"u"||d},s=()=>{const u=e.base||r.value.path.replace(/\/[^/]+$/,"/"),d=o.getRoutes(),p=[];return d.filter(({meta:v,path:h})=>{if(!rr(h,u)||h===u)return!1;if(u==="/"){const b=dt(a.value.locales).filter(T=>T!=="/");if(h==="/404.html"||b.some(T=>rr(h,T)))return!1}return(un(h,".html")&&!un(h,"/index.html")||un(h,"/"))&&i(v)}).map(({path:v,meta:h})=>{const b=v.substring(u.length).split("/").length;return{title:h.t||"",icon:h.i,base:v.replace(/\/[^/]+\/?$/,"/"),order:h.O||null,level:un(v,"/")?b-1:b,path:v}}).filter(({title:v,level:h})=>v&&h<=e.level).sort(({title:v,level:h,path:b,order:T},{title:I,level:E,path:k,order:y})=>h-E||(un(b,"/index.html")?-1:un(k,"/index.html")?1:T===null?y===null?v.localeCompare(I):y:y===null?T:T>0?y>0?T-y:-1:y<0?T-y:1)).forEach(v=>{var T;const{base:h,level:b}=v;switch(b){case 1:p.push(v);break;case 2:{const I=p.find(E=>E.path===h);I&&(I.children??(I.children=[])).push(v);break}default:{const I=p.find(E=>E.path===h.replace(/\/[^/]+\/$/,"/"));if(I){const E=(T=I.children)==null?void 0:T.find(k=>k.path===h);E&&(E.children??(E.children=[])).push(v)}}}}),p},c=L(()=>s());return()=>l("div",{class:"vp-catalog"},[e.hideHeading?null:l("h2",{class:"vp-catalog-main-title"},n.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:v},h)=>[l("h3",{id:v,class:["vp-catalog-child-title",{"has-children":u.length}]},[l("a",{href:`#${v}`,class:"header-anchor","aria-hidden":!0},"#"),l(Re,{class:"vp-catalog-title",to:p},()=>[e.index?`${h+1}.`:null,d&&t?l(t,{icon:d}):null,v||p])]),u.length?l(e.index&&e.indexType==="ol"?"ol":"ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:T,path:I,title:E},k)=>l("li",{class:"vp-child-catalog"},[l("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[l("a",{href:`#${E}`,class:"header-anchor"},"#"),l(Re,{class:"vp-catalog-title",to:I},()=>[e.index?`${h+1}.${k+1}`:null,T&&t?l(t,{icon:T}):null,E||I])]),b.length?l("div",{class:"v-sub-catalogs"},b.map(({icon:y,path:P,title:$},w)=>l(Re,{class:"vp-sub-catalog",to:P},()=>[e.index&&e.indexType!=="ol"?`${h+1}.${k+1}.${w+1}`:null,y&&t?l(t,{icon:y}):null,$||P]))):null]))):null]):l("p",{class:"vp-empty-catalog"},n.value.empty)])}}),jp=Ye({enhance:({app:e})=>{Hp(e),It("AutoCatalog",e)||e.component("AutoCatalog",Fp)}});const zp=l("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[l("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),l("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),kc=B({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=bt(),n=L(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>l("span",[zp,l("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}}),qp={},Up=Ye({enhance({app:e}){e.component("ExternalLinkIcon",l(kc,{locales:qp}))}});/** * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress * @license MIT - */const de={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
    '},status:null,set:e=>{const t=de.isStarted();e=Co(e,de.settings.minimum,1),de.status=e===1?null:e;const n=de.render(!t),r=n.querySelector(de.settings.barSelector),o=de.settings.speed,a=de.settings.easing;return n.offsetWidth,Gp(i=>{$r(r,{transform:"translate3d("+pl(e)+"%,0,0)",transition:"all "+o+"ms "+a}),e===1?($r(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){$r(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){de.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),de},isStarted:()=>typeof de.status=="number",start:()=>{de.status||de.set(0);const e=()=>{setTimeout(()=>{de.status&&(de.trickle(),e())},de.settings.trickleSpeed)};return de.settings.trickle&&e(),de},done:e=>!e&&!de.status?de:de.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=de.status;return t?(typeof e!="number"&&(e=(1-t)*Co(Math.random()*t,.1,.95)),t=Co(t+e,0,.994),de.set(t)):de.start()},trickle:()=>de.inc(Math.random()*de.settings.trickleRate),render:e=>{if(de.isRendered())return document.getElementById("nprogress");vl(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=de.settings.template;const n=t.querySelector(de.settings.barSelector),r=e?"-100":pl(de.status||0),o=document.querySelector(de.settings.parent);return $r(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&vl(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{hl(document.documentElement,"nprogress-busy"),hl(document.querySelector(de.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&Wp(e)},isRendered:()=>!!document.getElementById("nprogress")},Co=(e,t,n)=>en?n:e,pl=e=>(-1+e)*100,Gp=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),$r=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(s,c){return c.toUpperCase()})}function r(i){const s=document.body.style;if(i in s)return i;let c=e.length;const u=i.charAt(0).toUpperCase()+i.slice(1);let d;for(;c--;)if(d=e[c]+u,d in s)return d;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function a(i,s,c){s=o(s),i.style[s]=c}return function(i,s){for(const c in s){const u=s[c];u!==void 0&&Object.prototype.hasOwnProperty.call(s,c)&&a(i,c,u)}}}(),Pc=(e,t)=>(typeof e=="string"?e:Na(e)).indexOf(" "+t+" ")>=0,vl=(e,t)=>{const n=Na(e),r=n+t;Pc(n,t)||(e.className=r.substring(1))},hl=(e,t)=>{const n=Na(e);if(!Pc(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},Na=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),Wp=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const Jp=()=>{ye(()=>{const e=Ne(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||de.start()}),e.afterEach(n=>{t.add(n.path),de.done()})})},Qp=Ye({setup(){Jp()}}),Kp=JSON.parse(`{"encrypt":{},"author":{"name":"Yaien Blog","url":"https://yanggl.cn","email":"yaien_6530@163.com"},"logo":"/logo.png","darkmode":"toggle","navbarAutoHide":"always","repo":"https://github.com/yaien6530/yaien6530.github.io","editLink":false,"breadcrumb":false,"breadcrumbIcon":false,"print":false,"blog":{"medias":{"GitHub":"https://example.com","Gmail":"mailto:info@example.com","QQ":"2549597630","Wechat":"https://example.com"},"roundAvatar":true,"articleInfo":["Date","Tag","Original"],"articlePerPage":10},"locales":{"/en/":{"lang":"en-US","navbarLocales":{"langName":"English","selectLangAriaLabel":"Select language"},"metaLocales":{"author":"Author","date":"Writing Date","origin":"Original","views":"Page views","category":"Category","tag":"Tag","readingTime":"Reading Time","words":"Words","toc":"On This Page","prev":"Prev","next":"Next","lastUpdated":"Last update","contributors":"Contributors","editLink":"Edit this page","print":"Print"},"blogLocales":{"article":"Articles","articleList":"Article List","category":"Category","tag":"Tag","timeline":"Timeline","timelineTitle":"Yesterday Once More!","all":"All","intro":"Personal Intro","star":"Star"},"paginationLocales":{"prev":"Prev","next":"Next","navigate":"Jump to","action":"Go","errorText":"Please enter a number between 1 and $page !"},"outlookLocales":{"themeColor":"Theme Color","darkmode":"Theme Mode","fullscreen":"Full Screen"},"routeLocales":{"skipToContent":"Skip to main content","notFoundTitle":"Page not found","notFoundMsg":["There’s nothing here.","How did we get here?","That’s a Four-Oh-Four.","Looks like we've got some broken links."],"back":"Go back","home":"Take me home","openInNewWindow":"Open in new window"},"navbar":["/en/",{"text":"note","icon":"note","link":"/en/note/"},{"text":"essay","icon":"edit","link":"/en/informal-essay/"},{"text":"books","icon":"study","link":"/en/books/"},{"text":"tutorial","icon":"write","link":"/en/tutorial/"}],"sidebar":{"/en/note/":[{"text":"structure","prefix":"structure/","collapsible":true,"children":"structure"},{"text":"algorithm","prefix":"algorithm/","collapsible":true,"children":"structure"},{"text":"Java","prefix":"java/","collapsible":true,"children":"structure"},{"text":"MyBatis","prefix":"mybatis/","collapsible":true,"children":"structure"},{"text":"MySQL","prefix":"mysql/","collapsible":true,"children":[{"text":"base","prefix":"base/","children":"structure"},{"text":"further","prefix":"further/","children":"structure"}]},{"text":"Nacos","prefix":"nacos/","collapsible":true,"children":"structure"},{"text":"Redis","prefix":"redis/","collapsible":true,"children":"structure"},{"text":"Spring","prefix":"spring/","collapsible":true,"children":[{"text":"base","prefix":"base/","children":"structure"},{"text":"further","prefix":"further/","children":"structure"},{"text":"sourcecode","prefix":"sourcecode/","children":"structure"}]},{"text":"SpringBoot","prefix":"springboot/","collapsible":true,"children":"structure"},{"text":"SpringMVC","prefix":"springmvc/","collapsible":true,"children":"structure"},{"text":"other","prefix":"other/","collapsible":true,"children":"structure"}],"/en/tutorial/":"structure","/en/books/":"structure","/en/informal-essay/":"structure"},"footer":"桂ICP备2022011356号-1","displayFooter":true,"blog":{"description":"A JAVA developer who wants to stand at the top of the industry pyramid","intro":"/en/intro.html"}},"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":["/",{"text":"笔记","icon":"code","link":"/note/"},{"text":"资源","icon":"code","prefix":"/resources","children":[{"text":"书籍读物","link":"/books"},{"text":"快速导航","link":"/navigation"}]},{"text":"关于","icon":"code","prefix":"/about","children":[{"text":"我的简介","link":"/me"},{"text":"我的朋友","link":"/friend"},{"text":"更新内容","link":"/update"}]}],"sidebar":{"/note/":[{"text":"数据结构","link":"structure/"},{"text":"算法","link":"algorithm/"},{"text":"网络编程","link":"net/"},{"text":"JAVA","link":"java/"},{"text":"数据库","link":"db/"},{"text":"微服务","link":"microservices/"},{"text":"开发框架","link":"framework/"},{"text":"其他","link":"other/"}],"/note/java/":[{"text":"Jvm","prefix":"jvm/","children":"structure","collapsible":true},{"text":"并发编程","prefix":"concurrency/","children":"structure","collapsible":true}],"/note/db/":[{"text":"MongoDB","prefix":"mongodb/","children":"structure","collapsible":true},{"text":"MySQL","prefix":"mysql/","children":"structure","collapsible":true},{"text":"Redis","prefix":"redis/","children":"structure","collapsible":true}],"/note/microservices/":[{"text":"Dubbo","prefix":"dubbo/","children":"structure","collapsible":true},{"text":"ES","prefix":"elasticsearch/","children":"structure","collapsible":true},{"text":"Gateway","prefix":"gateway/","children":"structure","collapsible":true},{"text":"Kafka","prefix":"kafka/","children":"structure","collapsible":true},{"text":"Nacos","prefix":"nacos/","children":"structure","collapsible":true},{"text":"Rabbitmq","prefix":"rabbitmq/","children":"structure","collapsible":true},{"text":"Rocketmq","prefix":"rocketmq/","children":"structure","collapsible":true},{"text":"Seata","prefix":"seata/","children":"structure","collapsible":true},{"text":"Security","prefix":"security/","children":"structure","collapsible":true},{"text":"Sentinel","prefix":"sentinel/","children":"structure","collapsible":true},{"text":"SkyWalking","prefix":"skywalking/","children":"structure","collapsible":true},{"text":"Zookeeper","prefix":"zookeeper/","children":"structure","collapsible":true}],"/note/framework/":[{"text":"Spring","prefix":"spring/","children":"structure","collapsible":true},{"text":"Spring Boot","prefix":"springboot/","children":"structure","collapsible":true},{"text":"Spring MVC","prefix":"springmvc/","children":"structure","collapsible":true}],"/note/structure/":"structure","/note/algorithm/":"structure","/note/net/":"structure","/note/other/":"structure","/informal-essay/":"structure"},"footer":"桂ICP备2022011356号-1","displayFooter":true,"blog":{"description":"披荆斩棘 勇往直前","intro":"/about/me.html"}}}}`),Yp=Y(Kp),Oc=()=>Yp,Rc=Symbol(""),Xp=()=>{const e=he(Rc);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},Zp=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},e2=Ye({enhance({app:e}){const t=Oc(),n=e._context.provides[Sa],r=L(()=>Zp(t.value,n.value));e.provide(Rc,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}});const t2={provider:"Giscus",lightTheme:"https://unpkg.com/vuepress-theme-hope@2.0.0-beta.242/templates/giscus/light.css",darkTheme:"https://unpkg.com/vuepress-theme-hope@2.0.0-beta.242/templates/giscus/dark.css",repo:"yaien6530/blog-giscus",repoId:"R_kgDOJvOJXQ",category:"Announcements",categoryId:"DIC_kwDOJvOJXc4CXM6Q"};let n2=t2;const Dc=Symbol(""),Sc=()=>he(Dc),r2=Sc,o2=e=>{e.provide(Dc,n2)},ml=["ar","ca","de","en","eo","es","fa","fr","he","id","it","ja","ko","nl","pl","pt","ro","ru","th","tr","uk","vi","zh-CN","zh-TW"];var a2=B({name:"GiscusComment",props:{identifier:{type:String,required:!0},darkmode:Boolean},setup(e){const t=r2(),n=!!(t.repo&&t.repoId&&t.category&&t.categoryId),{repo:r,repoId:o,category:a,categoryId:i}=t,s=Y(!1),c=L(()=>{const d=co().value;if(ml.includes(d))return d;const p=d.split("-")[0];return ml.includes(p)?p:"en"}),u=L(()=>({repo:r,repoId:o,category:a,categoryId:i,lang:c.value,theme:e.darkmode?t.darkTheme||"dark":t.lightTheme||"light",mapping:t.mapping||"pathname",term:e.identifier,inputPosition:t.inputPosition||"top",reactionsEnabled:t.reactionsEnabled===!1?"0":"1",strict:t.strict===!1?"0":"1",loading:t.lazyLoading===!1?"eager":"lazy",emitMetadata:"0"}));return ye(async()=>{await f(()=>import("./giscus-789985da.js"),[]),s.value=!0}),()=>n?l("div",{id:"comment",class:["giscus-wrapper",{"input-top":t.inputPosition!=="bottom"}]},s.value?l("giscus-widget",u.value):l(ec)):null}}),i2=B({name:"CommentService",props:{darkmode:Boolean},setup(e){const t=Sc(),n=ie(),r=Ee(),o=t.comment!==!1,a=L(()=>r.value.comment||o&&r.value.comment!==!1);return()=>l(a2,{identifier:r.value.commentID||n.value.path,darkmode:e.darkmode,style:{display:a.value?"block":"none"}})}}),l2=Ye({enhance:({app:e})=>{o2(e),e.component("CommentService",i2)}});const s2=800,c2=2e3,u2={"/en/":{copy:"Copy code",copied:"Copied",hint:"Copied successfully"},"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},d2=!1,f2=['.theme-hope-content div[class*="language-"] pre'],gl=!1,Vo=new Map,p2=()=>{const{copy:e}=vp({legacy:!0}),t=Sn(u2),n=ie(),r=Zf(),o=s=>{if(!s.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
    ',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),s.parentElement&&s.parentElement.insertBefore(c,s),s.setAttribute("copy-code-registered","")}},a=()=>rn().then(()=>new Promise(s=>{setTimeout(()=>{f2.forEach(c=>{document.querySelectorAll(c).forEach(o)}),s()},s2)})),i=(s,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(s.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(Vo.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),Vo.delete(u)},c2);Vo.set(u,p)})};ye(()=>{(!r.value||gl)&&a(),Se("click",s=>{const c=s.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&i(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&i(d,p,u)}}),ve(()=>n.value.path,()=>{(!r.value||gl)&&a()})})};var v2=Ye({setup:()=>{p2()}});const h2=()=>{const e=Ee(),t=Sn({"/en/":{author:"Copyright by :author",license:"License under :license",link:":link"},"/":{author:"著作权归:author所有",license:"基于:license协议",link:"原文链接::link"}}),n=ie(),r=L(()=>!!e.value.copy||e.value.copy!==!1&&!0),o=L(()=>so(e.value.copy)?e.value.copy:null),a=L(()=>{var p;return((p=o.value)==null?void 0:p.disableCopy)??!1}),i=L(()=>{var p;return r.value?((p=o.value)==null?void 0:p.disableSelection)??!1:!1}),s=L(()=>{var p;return r.value?((p=o.value)==null?void 0:p.maxLength)??0:0}),c=L(()=>{var p;return((p=o.value)==null?void 0:p.triggerLength)??100}),u=()=>{const{author:p="",license:v=""}=n.value.copyright,{author:h,license:b,link:A}=t.value;return[p?h.replace(":author",p):"",v?b.replace(":license",v):"",A.replace(":link",window.location.href)].filter(I=>I).join(` -`)},d=p=>{const v=getSelection();if(v){const h=v.getRangeAt(0);if(r.value){const b=h.toString().length;if(a.value||s.value&&b>s.value)return p.preventDefault();if(b>=c.value){p.preventDefault();const A=u(),I=document.createElement("div");I.appendChild(v.getRangeAt(0).cloneContents()),p.clipboardData&&(p.clipboardData.setData("text/html",`${I.innerHTML}
    `),p.clipboardData.setData("text/plain",`${v.getRangeAt(0).cloneContents().textContent||""} + */const de={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
    '},status:null,set:e=>{const t=de.isStarted();e=Co(e,de.settings.minimum,1),de.status=e===1?null:e;const n=de.render(!t),r=n.querySelector(de.settings.barSelector),o=de.settings.speed,a=de.settings.easing;return n.offsetWidth,Gp(i=>{$r(r,{transform:"translate3d("+pl(e)+"%,0,0)",transition:"all "+o+"ms "+a}),e===1?($r(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){$r(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){de.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),de},isStarted:()=>typeof de.status=="number",start:()=>{de.status||de.set(0);const e=()=>{setTimeout(()=>{de.status&&(de.trickle(),e())},de.settings.trickleSpeed)};return de.settings.trickle&&e(),de},done:e=>!e&&!de.status?de:de.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=de.status;return t?(typeof e!="number"&&(e=(1-t)*Co(Math.random()*t,.1,.95)),t=Co(t+e,0,.994),de.set(t)):de.start()},trickle:()=>de.inc(Math.random()*de.settings.trickleRate),render:e=>{if(de.isRendered())return document.getElementById("nprogress");vl(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=de.settings.template;const n=t.querySelector(de.settings.barSelector),r=e?"-100":pl(de.status||0),o=document.querySelector(de.settings.parent);return $r(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&vl(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{hl(document.documentElement,"nprogress-busy"),hl(document.querySelector(de.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&Wp(e)},isRendered:()=>!!document.getElementById("nprogress")},Co=(e,t,n)=>en?n:e,pl=e=>(-1+e)*100,Gp=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),$r=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(s,c){return c.toUpperCase()})}function r(i){const s=document.body.style;if(i in s)return i;let c=e.length;const u=i.charAt(0).toUpperCase()+i.slice(1);let d;for(;c--;)if(d=e[c]+u,d in s)return d;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function a(i,s,c){s=o(s),i.style[s]=c}return function(i,s){for(const c in s){const u=s[c];u!==void 0&&Object.prototype.hasOwnProperty.call(s,c)&&a(i,c,u)}}}(),Pc=(e,t)=>(typeof e=="string"?e:Na(e)).indexOf(" "+t+" ")>=0,vl=(e,t)=>{const n=Na(e),r=n+t;Pc(n,t)||(e.className=r.substring(1))},hl=(e,t)=>{const n=Na(e);if(!Pc(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},Na=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),Wp=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const Jp=()=>{ye(()=>{const e=Ne(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||de.start()}),e.afterEach(n=>{t.add(n.path),de.done()})})},Qp=Ye({setup(){Jp()}}),Kp=JSON.parse(`{"encrypt":{},"author":{"name":"Yaien Blog","url":"https://yanggl.cn","email":"yaien_6530@163.com"},"logo":"/logo.png","darkmode":"toggle","navbarAutoHide":"always","repo":"https://github.com/yaien6530/yaien6530.github.io","editLink":false,"breadcrumb":false,"breadcrumbIcon":false,"print":false,"blog":{"medias":{"GitHub":"https://example.com","Gmail":"mailto:info@example.com","QQ":"2549597630","Wechat":"https://example.com"},"roundAvatar":true,"articleInfo":["Date","Tag","Original"],"articlePerPage":10},"locales":{"/en/":{"lang":"en-US","navbarLocales":{"langName":"English","selectLangAriaLabel":"Select language"},"metaLocales":{"author":"Author","date":"Writing Date","origin":"Original","views":"Page views","category":"Category","tag":"Tag","readingTime":"Reading Time","words":"Words","toc":"On This Page","prev":"Prev","next":"Next","lastUpdated":"Last update","contributors":"Contributors","editLink":"Edit this page","print":"Print"},"blogLocales":{"article":"Articles","articleList":"Article List","category":"Category","tag":"Tag","timeline":"Timeline","timelineTitle":"Yesterday Once More!","all":"All","intro":"Personal Intro","star":"Star"},"paginationLocales":{"prev":"Prev","next":"Next","navigate":"Jump to","action":"Go","errorText":"Please enter a number between 1 and $page !"},"outlookLocales":{"themeColor":"Theme Color","darkmode":"Theme Mode","fullscreen":"Full Screen"},"routeLocales":{"skipToContent":"Skip to main content","notFoundTitle":"Page not found","notFoundMsg":["There’s nothing here.","How did we get here?","That’s a Four-Oh-Four.","Looks like we've got some broken links."],"back":"Go back","home":"Take me home","openInNewWindow":"Open in new window"},"navbar":["/en/",{"text":"note","icon":"note","link":"/en/note/"},{"text":"essay","icon":"edit","link":"/en/informal-essay/"},{"text":"books","icon":"study","link":"/en/books/"},{"text":"tutorial","icon":"write","link":"/en/tutorial/"}],"sidebar":{"/en/note/":[{"text":"structure","prefix":"structure/","collapsible":true,"children":"structure"},{"text":"algorithm","prefix":"algorithm/","collapsible":true,"children":"structure"},{"text":"Java","prefix":"java/","collapsible":true,"children":"structure"},{"text":"MyBatis","prefix":"mybatis/","collapsible":true,"children":"structure"},{"text":"MySQL","prefix":"mysql/","collapsible":true,"children":[{"text":"base","prefix":"base/","children":"structure"},{"text":"further","prefix":"further/","children":"structure"}]},{"text":"Nacos","prefix":"nacos/","collapsible":true,"children":"structure"},{"text":"Redis","prefix":"redis/","collapsible":true,"children":"structure"},{"text":"Spring","prefix":"spring/","collapsible":true,"children":[{"text":"base","prefix":"base/","children":"structure"},{"text":"further","prefix":"further/","children":"structure"},{"text":"sourcecode","prefix":"sourcecode/","children":"structure"}]},{"text":"SpringBoot","prefix":"springboot/","collapsible":true,"children":"structure"},{"text":"SpringMVC","prefix":"springmvc/","collapsible":true,"children":"structure"},{"text":"other","prefix":"other/","collapsible":true,"children":"structure"}],"/en/tutorial/":"structure","/en/books/":"structure","/en/informal-essay/":"structure"},"footer":"桂ICP备2022011356号-1","displayFooter":true,"blog":{"description":"A JAVA developer who wants to stand at the top of the industry pyramid","intro":"/en/intro.html"}},"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":["/",{"text":"笔记","icon":"code","link":"/note/"},{"text":"资源","icon":"code","prefix":"/resources","children":[{"text":"书籍读物","link":"/books"},{"text":"快速导航","link":"/navigation"}]},{"text":"关于","icon":"code","prefix":"/about","children":[{"text":"我的简介","link":"/me"},{"text":"我的朋友","link":"/friend"},{"text":"更新内容","link":"/update"}]}],"sidebar":{"/note/":[{"text":"数据结构","link":"structure/"},{"text":"算法","link":"algorithm/"},{"text":"网络编程","link":"net/"},{"text":"JAVA","link":"java/"},{"text":"数据库","link":"db/"},{"text":"微服务","link":"microservices/"},{"text":"开发框架","link":"framework/"},{"text":"其他","link":"other/"}],"/note/java/":[{"text":"Jvm","prefix":"jvm/","children":"structure","collapsible":true},{"text":"并发编程","prefix":"concurrency/","children":"structure","collapsible":true}],"/note/db/":[{"text":"MongoDB","prefix":"mongodb/","children":"structure","collapsible":true},{"text":"MySQL","prefix":"mysql/","children":"structure","collapsible":true},{"text":"Redis","prefix":"redis/","children":"structure","collapsible":true}],"/note/microservices/":[{"text":"Dubbo","prefix":"dubbo/","children":"structure","collapsible":true},{"text":"ES","prefix":"elasticsearch/","children":"structure","collapsible":true},{"text":"Gateway","prefix":"gateway/","children":"structure","collapsible":true},{"text":"Kafka","prefix":"kafka/","children":"structure","collapsible":true},{"text":"Nacos","prefix":"nacos/","children":"structure","collapsible":true},{"text":"Rabbitmq","prefix":"rabbitmq/","children":"structure","collapsible":true},{"text":"Rocketmq","prefix":"rocketmq/","children":"structure","collapsible":true},{"text":"Seata","prefix":"seata/","children":"structure","collapsible":true},{"text":"Security","prefix":"security/","children":"structure","collapsible":true},{"text":"Sentinel","prefix":"sentinel/","children":"structure","collapsible":true},{"text":"SkyWalking","prefix":"skywalking/","children":"structure","collapsible":true},{"text":"Zookeeper","prefix":"zookeeper/","children":"structure","collapsible":true}],"/note/framework/":[{"text":"Spring","prefix":"spring/","children":"structure","collapsible":true},{"text":"Spring Boot","prefix":"springboot/","children":"structure","collapsible":true},{"text":"Spring MVC","prefix":"springmvc/","children":"structure","collapsible":true}],"/note/structure/":"structure","/note/algorithm/":"structure","/note/net/":"structure","/note/other/":"structure","/informal-essay/":"structure"},"footer":"桂ICP备2022011356号-1","displayFooter":true,"blog":{"description":"披荆斩棘 勇往直前","intro":"/about/me.html"}}}}`),Yp=Y(Kp),Oc=()=>Yp,Rc=Symbol(""),Xp=()=>{const e=he(Rc);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},Zp=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},e2=Ye({enhance({app:e}){const t=Oc(),n=e._context.provides[Sa],r=L(()=>Zp(t.value,n.value));e.provide(Rc,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}});const t2={provider:"Giscus",lightTheme:"https://unpkg.com/vuepress-theme-hope@2.0.0-beta.242/templates/giscus/light.css",darkTheme:"https://unpkg.com/vuepress-theme-hope@2.0.0-beta.242/templates/giscus/dark.css",repo:"yaien6530/blog-giscus",repoId:"R_kgDOJvOJXQ",category:"Announcements",categoryId:"DIC_kwDOJvOJXc4CXM6Q"};let n2=t2;const Dc=Symbol(""),Sc=()=>he(Dc),r2=Sc,o2=e=>{e.provide(Dc,n2)},ml=["ar","ca","de","en","eo","es","fa","fr","he","id","it","ja","ko","nl","pl","pt","ro","ru","th","tr","uk","vi","zh-CN","zh-TW"];var a2=B({name:"GiscusComment",props:{identifier:{type:String,required:!0},darkmode:Boolean},setup(e){const t=r2(),n=!!(t.repo&&t.repoId&&t.category&&t.categoryId),{repo:r,repoId:o,category:a,categoryId:i}=t,s=Y(!1),c=L(()=>{const d=co().value;if(ml.includes(d))return d;const p=d.split("-")[0];return ml.includes(p)?p:"en"}),u=L(()=>({repo:r,repoId:o,category:a,categoryId:i,lang:c.value,theme:e.darkmode?t.darkTheme||"dark":t.lightTheme||"light",mapping:t.mapping||"pathname",term:e.identifier,inputPosition:t.inputPosition||"top",reactionsEnabled:t.reactionsEnabled===!1?"0":"1",strict:t.strict===!1?"0":"1",loading:t.lazyLoading===!1?"eager":"lazy",emitMetadata:"0"}));return ye(async()=>{await f(()=>import("./giscus-789985da.js"),[]),s.value=!0}),()=>n?l("div",{id:"comment",class:["giscus-wrapper",{"input-top":t.inputPosition!=="bottom"}]},s.value?l("giscus-widget",u.value):l(ec)):null}}),i2=B({name:"CommentService",props:{darkmode:Boolean},setup(e){const t=Sc(),n=ie(),r=Ee(),o=t.comment!==!1,a=L(()=>r.value.comment||o&&r.value.comment!==!1);return()=>l(a2,{identifier:r.value.commentID||n.value.path,darkmode:e.darkmode,style:{display:a.value?"block":"none"}})}}),l2=Ye({enhance:({app:e})=>{o2(e),e.component("CommentService",i2)}});const s2=800,c2=2e3,u2={"/en/":{copy:"Copy code",copied:"Copied",hint:"Copied successfully"},"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},d2=!1,f2=['.theme-hope-content div[class*="language-"] pre'],gl=!1,Vo=new Map,p2=()=>{const{copy:e}=vp({legacy:!0}),t=Sn(u2),n=ie(),r=Zf(),o=s=>{if(!s.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
    ',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),s.parentElement&&s.parentElement.insertBefore(c,s),s.setAttribute("copy-code-registered","")}},a=()=>rn().then(()=>new Promise(s=>{setTimeout(()=>{f2.forEach(c=>{document.querySelectorAll(c).forEach(o)}),s()},s2)})),i=(s,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(s.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(Vo.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),Vo.delete(u)},c2);Vo.set(u,p)})};ye(()=>{(!r.value||gl)&&a(),Se("click",s=>{const c=s.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&i(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&i(d,p,u)}}),ve(()=>n.value.path,()=>{(!r.value||gl)&&a()})})};var v2=Ye({setup:()=>{p2()}});const h2=()=>{const e=Ee(),t=Sn({"/en/":{author:"Copyright by :author",license:"License under :license",link:":link"},"/":{author:"著作权归:author所有",license:"基于:license协议",link:"原文链接::link"}}),n=ie(),r=L(()=>!!e.value.copy||e.value.copy!==!1&&!0),o=L(()=>so(e.value.copy)?e.value.copy:null),a=L(()=>{var p;return((p=o.value)==null?void 0:p.disableCopy)??!1}),i=L(()=>{var p;return r.value?((p=o.value)==null?void 0:p.disableSelection)??!1:!1}),s=L(()=>{var p;return r.value?((p=o.value)==null?void 0:p.maxLength)??0:0}),c=L(()=>{var p;return((p=o.value)==null?void 0:p.triggerLength)??100}),u=()=>{const{author:p="",license:v=""}=n.value.copyright,{author:h,license:b,link:T}=t.value;return[p?h.replace(":author",p):"",v?b.replace(":license",v):"",T.replace(":link",window.location.href)].filter(I=>I).join(` +`)},d=p=>{const v=getSelection();if(v){const h=v.getRangeAt(0);if(r.value){const b=h.toString().length;if(a.value||s.value&&b>s.value)return p.preventDefault();if(b>=c.value){p.preventDefault();const T=u(),I=document.createElement("div");I.appendChild(v.getRangeAt(0).cloneContents()),p.clipboardData&&(p.clipboardData.setData("text/html",`${I.innerHTML}
    `),p.clipboardData.setData("text/plain",`${v.getRangeAt(0).cloneContents().textContent||""} ------ -${A}`))}}}};ye(()=>{const p=document.querySelector("#app");Se(p,"copy",d),Ta(()=>{p.style.userSelect=i.value?"none":"auto"})})};var m2=Ye({setup:()=>{h2()}});const xc=({title:e,desc:t="",logo:n="",color:r="",link:o=""})=>{const a=[l("img",{class:"vp-card-logo",src:Ie(n)}),l("div",{class:"vp-card-content"},[l("div",{class:"vp-card-title",innerHTML:e}),l("hr"),l("div",{class:"vp-card-desc",innerHTML:t})])],i={class:"vp-card"};return r&&(i.style={background:r}),er(o)?l("a",{href:o,target:"_blank",...i},a):l(Re,{to:o,...i},()=>a)};xc.displayName="VPCard";const g2='';var _2=B({name:"Playground",props:{title:{type:String,default:""},link:{type:String,required:!0}},setup(e){return()=>[l("div",{class:"vp-playground"},[l("div",{class:"vp-playground-header"},[e.title?l("div",{class:"vp-playground-title"},decodeURIComponent(e.title)):null,l("div",{class:"vp-playground-actions"},[l("a",{class:"vp-playground-action",href:decodeURIComponent(e.link),target:"_blank",innerHTML:g2})])]),l("div",{class:"vp-playground-container"},l("iframe",{src:decodeURIComponent(e.link)}))])]}});const b2=Ye({enhance:({app:e})=>{e.component("VPCard",xc),e.component("Playground",_2)},setup:()=>{}});let y2={};const Cc=Symbol(""),E2=()=>he(Cc),L2=e=>{e.provide(Cc,y2)};const T2=".theme-hope-content :not(a) > img:not([no-view])",A2={"/en/":{closeTitle:"Close",downloadTitle:"Download Image",fullscreenTitle:"Switch to full screen",zoomTitle:"Zoom in/out",arrowPrevTitle:"Prev (Arrow Left)",arrowNextTitle:"Next (Arrow Right)"},"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},I2=800,w2='
    ',k2=e=>pe(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Vc=e=>new Promise((t,n)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Vc(e)),e.onerror=r=>n(r))}),P2=()=>{const{isSupported:e,toggle:t}=$a(),n=E2(),r=Sn(A2),o=ie();let a;const i=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let v=-1;for(let h=0;h{d.goTo(p.indexOf(A.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{v>=0&&p[v].classList.remove("active"),p[d.currIndex].classList.add("active"),v=d.currIndex})}})})},s=()=>Promise.all([f(()=>import("./photoswipe.esm-060dc2da.js"),[]),rn().then(()=>new Promise(c=>setTimeout(c,I2)).then(()=>k2(T2)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:w2,element:p,msrc:p.src}));u.forEach((p,v)=>{const h=()=>{a=new c({preloaderDelay:0,showHideAnimationType:"zoom",...r.value,...n,dataSource:d,index:v,closeOnVerticalDrag:!0,wheelToZoom:!1}),i(a),a.addFilter("thumbEl",()=>p),a.addFilter("placeholderSrc",()=>p.src),a.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{h()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&h()})}),u.forEach((p,v)=>{Vc(p).then(h=>{d.splice(v,1,h),a==null||a.refreshSlideContent(v)})})});ye(()=>{Se("wheel",()=>{a==null||a.close()}),ve(()=>o.value.path,s,{immediate:!0})})};var O2=Ye({enhance:({app:e})=>{L2(e)},setup:()=>{P2()}});const $e=e=>{const{icon:t="",color:n,size:r}=e,o={};return n&&(o.color=n),r&&(o.height=Number.isNaN(Number(r))?r:`${r}px`),on(t)?l("img",{class:"icon",src:t,"no-view":"",style:o}):vo(t)?l("img",{class:"icon",src:Ie(t),"no-view":"",style:o}):l(Ke("FontIcon"),e)};$e.displayName="HopeIcon";const R2=(e,t)=>{const n=e.replace(t,"/").split("/"),r=[];let o=Da(t);return n.forEach((a,i)=>{i!==n.length-1?(o+=`${a}/`,r.push({link:o,name:a||"Home"})):a!==""&&(o+=a,r.push({link:o,name:a}))}),r};var _e;(function(e){e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O"})(_e||(_e={}));var ta;(function(e){e.article="a",e.home="h",e.slide="s",e.page="p"})(ta||(ta={}));const _n=(e,t,n=!1)=>{let r=In(e,oc(encodeURI(t)));r.name==="404"&&(r=In(e,t));const{fullPath:o,meta:a,name:i}=r;return{text:!n&&a[_e.shortTitle]?a[_e.shortTitle]:a[_e.title]||t,link:i==="404"?t:o,...a[_e.icon]?{icon:a[_e.icon]}:{}}},mr=()=>{const e=Ne(),t=pt();return n=>{if(n)if(vo(n))t.path!==n&&e.push(n);else if(on(n)||Ss(n))window&&window.open(n);else{const r=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${r}/${encodeURI(n)}`)}}},Mc=()=>{const e=ie();return L(()=>e.value.readingTime??null)},na=typeof{"/en/":{word:"About $word words",less1Minute:"Less than 1 minute",time:"About $time min"},"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/en/":{word:"About $word words",less1Minute:"Less than 1 minute",time:"About $time min"},"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Bc=(e,t)=>{const{minutes:n,words:r}=e,{less1Minute:o,word:a,time:i}=t;return{time:n<1?o:i.replace("$time",Math.round(n).toString()),words:a.replace("$word",r.toString())}},_l={words:"",time:""},$c=()=>na?Sn(na):L(()=>null),D2=()=>{if(typeof na>"u")return L(()=>_l);const e=Mc(),t=$c();return L(()=>e.value&&t.value?Bc(e.value,t.value):_l)},Gt=()=>Oc(),le=()=>Xp(),xn=()=>L(()=>!!Gt().value.pure),Nc=()=>{const e=le(),t=Ee();return L(()=>{const{author:n}=t.value;return n?or(n):n===!1?[]:or(e.value.author,!1)})},S2=()=>{const e=Ee();return L(()=>ic(e.value.category).map(t=>{var n,r;return{name:t,path:((r=(n=he(Symbol.for("categoryMap")))==null?void 0:n.value.map[t])==null?void 0:r.path)||""}}))},x2=()=>{const e=Ee();return L(()=>lc(e.value.tag).map(t=>{var n,r;return{name:t,path:((r=(n=he(Symbol.for("tagMap")))==null?void 0:n.value.map[t])==null?void 0:r.path)||""}}))},C2=()=>{const e=Ee(),t=ie();return L(()=>{const n=Va(e.value.date);if(n)return n;const{createdTime:r}=t.value.git||{};return r?new Date(r):null})},V2=()=>{const e=le(),t=ie(),n=Ee(),r=Nc(),o=S2(),a=x2(),i=C2(),s=Mc(),c=D2(),u=L(()=>({author:r.value,category:o.value,date:i.value,localizedDate:t.value.localizedDate,tag:a.value,isOriginal:n.value.isOriginal||!1,readingTime:s.value,readingTimeLocale:c.value,pageview:"pageview"in n.value?n.value.pageview:!0})),d=L(()=>"pageInfo"in n.value?n.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}};let Mo=null,Bn=null;const M2={wait:()=>Mo,pending:()=>{Mo=new Promise(e=>Bn=e)},resolve:()=>{Bn==null||Bn(),Mo=null,Bn=null}},Hc=()=>M2,B2="719px",$2="1440px",N2="false",Ha={mobileBreakPoint:B2,pcBreakPoint:$2,enableThemeColor:N2},{mobileBreakPoint:H2,pcBreakPoint:F2}=Ha,bl=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,gr=()=>{const e=Y(!1),t=Y(!1),n=()=>{e.value=window.innerWidth<=(bl(H2)??719),t.value=window.innerWidth>=(bl(F2)??1440)};return ye(()=>{n(),Se("resize",n,!1),Se("orientationchange",n,!1)}),{isMobile:e,isPC:t}},Fc=Symbol(""),_r=()=>{const e=he(Fc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},j2=e=>{const t=Gt(),n=yp(),r=yc("vuepress-theme-hope-scheme","auto"),o=L(()=>t.value.darkmode||"switch"),a=L(()=>{const s=o.value;return s==="disable"?!1:s==="enable"?!0:s==="auto"?n.value:s==="toggle"?r.value==="dark":r.value==="dark"||r.value==="auto"&&n.value}),i=L(()=>{const s=o.value;return s==="switch"||s==="toggle"});e.provide(Fc,{canToggle:i,config:o,isDarkmode:a,status:r}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>a.value}})},z2=()=>{const{isDarkmode:e}=_r(),t=(n=e.value)=>document.documentElement.setAttribute("data-theme",n?"dark":"light");ye(()=>{ve(e,t,{immediate:!0})})},ze=B({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:n,slots:r}){const o=pt(),a=Hs(),i=Pn(e,"config"),s=L(()=>on(i.value.link)),c=L(()=>Ss(i.value.link)||Ad(i.value.link)),u=L(()=>c.value?void 0:i.value.target||(s.value?"_blank":void 0)),d=L(()=>u.value==="_blank"),p=L(()=>!s.value&&!c.value&&!d.value),v=L(()=>c.value?void 0:i.value.rel||(d.value?"noopener noreferrer":void 0)),h=L(()=>i.value.ariaLabel||i.value.text),b=L(()=>{if(e.exact)return!1;const I=dt(a.value.locales);return I.length?I.every(E=>E!==i.value.link):i.value.link!=="/"}),A=L(()=>p.value?i.value.activeMatch?new RegExp(i.value.activeMatch).test(o.path):b.value?rr(o.path,i.value.link):o.path===i.value.link:!1);return()=>{const{before:I,after:E,default:k}=r,{text:y,icon:P,link:$}=i.value;return p.value?l(Re,{to:$,"aria-label":h.value,...t,class:["nav-link",{active:A.value},t.class],onFocusout:()=>n("focusout")},()=>k?k():[I?I():l($e,{icon:P}),y,E==null?void 0:E()]):l("a",{href:$,rel:v.value,target:u.value,"aria-label":h.value,...t,class:["nav-link",t.class],onFocusout:()=>n("focusout")},k?k():[I?I():l($e,{icon:P}),y,e.noExternalLinkIcon?null:l(kc),E==null?void 0:E()])}}}),wn=(e,t,n=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ba(e,t.link)?!0:t.children&&!n?t.children.some(r=>wn(e,r)):!1,jc=(e,t)=>t.type==="group"?t.children.some(n=>n.type==="group"?jc(e,n):n.type==="page"&&wn(e,n,!0))||"prefix"in t&&Ba(e,t.prefix):!1,zc=(e,t)=>pe(e.link)?l(ze,{...t,config:e}):l("p",t,[l($e,{icon:e.icon}),e.text]),qc=e=>{const t=pt();return e?l("ul",{class:"vp-sidebar-sub-headers"},e.map(n=>{const r=wn(t,n,!0);return l("li",{class:"vp-sidebar-sub-header"},[zc(n,{class:["vp-sidebar-link","vp-heading",{active:r}]}),qc(n.children)])})):null},Fa={"/en/note/structure/":[],"/en/note/algorithm/":[],"/en/note/java/":[],"/en/note/mybatis/":[],"/en/note/mysql/base/":[],"/en/note/mysql/further/":[],"/en/note/nacos/":[],"/en/note/redis/":[],"/en/note/spring/base/":[],"/en/note/spring/further/":[],"/en/note/spring/sourcecode/":[],"/en/note/springboot/":[],"/en/note/springmvc/":[],"/en/note/other/":[],"/en/tutorial/":[{text:"Docker",prefix:"docker/",collapsible:!0,children:["2305081011","2305081101","2305081110"]},{text:"Linux",prefix:"linux/",collapsible:!0,children:["2305081106"]},{text:"Openai",prefix:"openai/",collapsible:!0,children:["2305081112"]},{text:"Util",prefix:"util/",collapsible:!0,children:["2305090946"]}],"/en/books/":[],"/en/informal-essay/":[],"/note/java/jvm/":["2305052159","2305111000","2311091006","2311081011","2311071055","2311221518","2403021055","2403031236","2403031505","2403032005","2311081109"],"/note/java/concurrency/":[{text:"Atomic",prefix:"atomic/",collapsible:!0,children:["2305252031","2305281438","2305281702","2305312110"]},{text:"Lock",prefix:"lock/",collapsible:!0,children:["2305312243","2306011640","2306021120","2306021924"]},{text:"Queue",prefix:"queue/",collapsible:!0,children:["2306041045","2306042021","2306051101","2306052321"]},{text:"线程池",prefix:"线程池/",collapsible:!0,children:["2312130940","2312141621"]}],"/note/db/mongodb/":[],"/note/db/mysql/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["1910011300","1910011301","1910011302","1910011303","1910011304"]},{text:"Further",prefix:"further/",collapsible:!0,children:["2305011002","2305012001","2305020500","2305020501","2305032100"]}],"/note/db/redis/":["2304010120","2305091732","2305091736","2305091734","2305091740","2305091744","2312220952","2312280935","2312291000","2312291432"],"/note/microservices/dubbo/":["2305140023","2305140203","2305140206","2305172157","2305191622","2305232045"],"/note/microservices/elasticsearch/":[],"/note/microservices/gateway/":[],"/note/microservices/kafka/":[],"/note/microservices/nacos/":[],"/note/microservices/rabbitmq/":[],"/note/microservices/rocketmq/":[],"/note/microservices/seata/":[],"/note/microservices/security/":[],"/note/microservices/sentinel/":[],"/note/microservices/skywalking/":[],"/note/microservices/zookeeper/":["2305110857"],"/note/framework/spring/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2101202010","2101202011","2101202012"]},{text:"Sourcecode",prefix:"sourcecode/",collapsible:!0,children:["2304021002"]}],"/note/framework/springboot/":[{text:"Plugin",prefix:"plugin/",collapsible:!0,children:["2306210953","2306211023"]}],"/note/framework/springmvc/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2102202010","2102202011","2102202012"]}],"/note/structure/":["2305090929","2301101200"],"/note/algorithm/":["2301101203","2301101204"],"/note/net/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2401171030"]}],"/note/other/":["2305081011","2305081101","2305081110","2302201400","2302201401","2302201404","2305081106","2305081112","2305090946","2307051931","2307062006"],"/informal-essay/":[]},Bo=(e="",t="")=>vo(t)?t:`${Ed(e)}${t}`,q2=(e,t)=>{const n=ie();return{type:"heading",text:e.title,link:`${n.value.path}#${e.slug}`,children:ja(e.children,t)}},ja=(e,t)=>t>0?e.map(n=>q2(n,t-1)):[],Uc=e=>{const t=ie();return ja(t.value.headers,e)},ra=(e,t,n="")=>{const r=Ne(),o=ie(),a=(i,s=n)=>{var u;const c=pe(i)?_n(r,Bo(s,i)):i.link?{...i,...er(i.link)?{}:{link:_n(r,Bo(s,i.link)).link}}:i;if("children"in c){const d=Bo(s,c.prefix),p=c.children==="structure"?Fa[d]:c.children;return{type:"group",...c,prefix:d,children:p.map(v=>a(v,d))}}return{type:"page",...c,children:c.link===o.value.path?ja(((u=o.value.headers[0])==null?void 0:u.level)===1?o.value.headers[0].children:o.value.headers,t):[]}};return e.map(i=>a(i))},U2=(e,t)=>{const n=ie(),r=dt(e).sort((o,a)=>a.length-o.length);for(const o of r)if(rr(decodeURI(n.value.path),o)){const a=e[o];return a?ra(a==="structure"?Fa[o]:a==="heading"?Uc(t):a,t,o):[]}return console.warn(`${n.value.path} is missing sidebar config.`),[]},G2=(e,t)=>{const n=bt();return e===!1?[]:e==="heading"?Uc(t):e==="structure"?ra(Fa[n.value],t,n.value):ee(e)?ra(e,t):so(e)?U2(e,t):[]},Gc=Symbol(""),W2=()=>{const e=Ee(),t=le(),n=ie(),r=L(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),o=L(()=>e.value.headerDepth??t.value.headerDepth??2),a=ho(()=>[r.value,o.value,n.value.path,null],()=>G2(r.value,o.value));ct(Gc,a)},za=()=>{const e=he(Gc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};const J2=B({name:"PageFooter",setup(){const e=Ee(),t=le(),n=Nc(),r=L(()=>{const{copyright:i,footer:s}=e.value;return s!==!1&&!!(i||s||t.value.displayFooter)}),o=L(()=>{const{footer:i}=e.value;return i===!1?!1:pe(i)?i:t.value.footer||""}),a=L(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:n.value.length?`Copyright © ${new Date().getFullYear()} ${n.value[0].name}`:!1);return()=>r.value?l("footer",{class:"vp-footer-wrapper"},[o.value?l("div",{class:"vp-footer",innerHTML:o.value}):null,a.value?l("div",{class:"vp-copyright",innerHTML:a.value}):null]):null}});var yl=B({name:"EmptyComponent",setup:()=>()=>null});const Wc=B({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const n=ie(),r=Pn(e,"config"),o=L(()=>r.value.ariaLabel||r.value.text),a=Y(!1);ve(()=>n.value.path,()=>{a.value=!1});const i=s=>{s.detail===0&&(a.value=!a.value)};return()=>{var s;return l("div",{class:["dropdown-wrapper",{open:a.value}]},[l("button",{type:"button",class:"dropdown-title","aria-label":o.value,onClick:i},[((s=t.title)==null?void 0:s.call(t))||l("span",{class:"title"},[l($e,{icon:r.value.icon}),e.config.text]),l("span",{class:"arrow"}),l("ul",{class:"nav-dropdown"},r.value.children.map((c,u)=>{const d=u===r.value.children.length-1;return l("li",{class:"dropdown-item"},"children"in c?[l("h4",{class:"dropdown-subtitle"},c.link?l(ze,{config:c,onFocusout:()=>{c.children.length===0&&d&&(a.value=!1)}}):l("span",c.text)),l("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,v)=>l("li",{class:"dropdown-subitem"},l(ze,{config:p,onFocusout:()=>{v===c.children.length-1&&d&&(a.value=!1)}}))))]:l(ze,{config:c,onFocusout:()=>{d&&(a.value=!1)}}))}))])])}}}),Jc=()=>l(ue,{name:"i18n"},()=>[l("path",{d:"M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z"})]);Jc.displayName="I18nIcon";const Qc=(e,t,n="")=>pe(t)?_n(e,`${n}${t}`):"children"in t?{...t,...t.link&&!er(t.link)?_n(e,`${n}${t.link}`):{},children:t.children.map(r=>Qc(e,r,`${n}${t.prefix||""}`))}:{...t,link:er(t.link)?t.link:_n(e,`${n}${t.link}`).link},Kc=()=>{const e=le(),t=Ne(),n=()=>(e.value.navbar||[]).map(o=>Qc(t,o));return ho(()=>e.value.navbar,()=>n())},Q2=()=>{const e=Ne(),t=pt(),n=bt(),r=Rn(),o=Gt(),a=le();return ho(()=>[t.path,r.value.locales,o.value.extraLocales,a.value.navbarLocales],()=>{const i=dt(r.value.locales),s=Dn(o.value.extraLocales??{});if(i.length<2&&!s.length)return null;const{path:c,fullPath:u}=t,{navbarLocales:d}=a.value;return{text:"",ariaLabel:d==null?void 0:d.selectLangAriaLabel,children:[...i.map(v=>{var k,y,P;const h=((k=r.value.locales)==null?void 0:k[v])??{},b=((y=o.value.locales)==null?void 0:y[v])??{},A=h.lang||"",I=((P=b.navbarLocales)==null?void 0:P.langName)??A;let E;if(A===r.value.lang)E=c;else{const $=c.replace(n.value,v);E=e.getRoutes().some(w=>w.path===$)?u.replace(c,$):b.home??v}return{text:I,link:E}}),...s.map(([v,h])=>({text:v,link:h.replace(":route",t.path.replace(n.value,""))}))]}})},K2=()=>{const e=le(),t=L(()=>e.value.repo||null),n=L(()=>t.value?Kf(t.value):null),r=L(()=>t.value?cc(t.value):null),o=L(()=>n.value?e.value.repoLabel??(r.value===null?"Source":r.value):null);return L(()=>!n.value||!o.value||e.value.repoDisplay===!1?null:{type:r.value||"Source",label:o.value,link:n.value})},Y2=B({name:"LanguageDropdown",setup(){const e=Q2();return()=>e.value?l("div",{class:"nav-item"},l(Wc,{class:"i18n-dropdown",config:e.value},{title:()=>{var t;return l(Jc,{"aria-label":(t=e.value)==null?void 0:t.ariaLabel,style:{width:"1rem",height:"1rem",verticalAlign:"middle"}})}})):null}});const X2=B({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ie(),n=Pn(e,"config"),r=L(()=>n.value.ariaLabel||n.value.text),o=Y(!1);ve(()=>t.value.path,()=>{o.value=!1});const a=(i,s)=>s[s.length-1]===i;return()=>[l("button",{type:"button",class:["nav-screen-dropdown-title",{active:o.value}],"aria-label":r.value,onClick:()=>{o.value=!o.value}},[l("span",{class:"title"},[l($e,{icon:n.value.icon}),e.config.text]),l("span",{class:["arrow",o.value?"down":"end"]})]),l("ul",{class:["nav-screen-dropdown",{hide:!o.value}]},n.value.children.map(i=>l("li",{class:"dropdown-item"},"children"in i?[l("h4",{class:"dropdown-subtitle"},i.link?l(ze,{config:i,onFocusout:()=>{a(i,n.value.children)&&i.children.length===0&&(o.value=!1)}}):l("span",i.text)),l("ul",{class:"dropdown-subitem-wrapper"},i.children.map(s=>l("li",{class:"dropdown-subitem"},l(ze,{config:s,onFocusout:()=>{a(s,i.children)&&a(i,n.value.children)&&(o.value=!1)}}))))]:l(ze,{config:i,onFocusout:()=>{a(i,n.value.children)&&(o.value=!1)}}))))]}});const Z2=B({name:"NavScreenLinks",setup(){const e=Kc();return()=>e.value.length?l("nav",{class:"nav-screen-links"},e.value.map(t=>l("div",{class:"navbar-links-item"},"children"in t?l(X2,{config:t}):l(ze,{config:t})))):null}}),Yc=()=>l(ue,{name:"dark"},()=>l("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Yc.displayName="DarkIcon";const Xc=()=>l(ue,{name:"light"},()=>l("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Xc.displayName="LightIcon";const Zc=()=>l(ue,{name:"auto"},()=>l("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Zc.displayName="AutoIcon";const eu=()=>l(ue,{name:"enter-fullscreen"},()=>l("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));eu.displayName="EnterFullScreenIcon";const tu=()=>l(ue,{name:"cancel-fullscreen"},()=>l("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));tu.displayName="CancelFullScreenIcon";const nu=()=>l(ue,{name:"outlook"},()=>[l("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);nu.displayName="OutlookIcon";const ru=B({name:"AppearanceSwitch",setup(){const{config:e,status:t}=_r(),n=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>l("button",{type:"button",id:"appearance-switch",onClick:()=>n()},[l(Zc,{style:{display:t.value==="auto"?"block":"none"}}),l(Yc,{style:{display:t.value==="dark"?"block":"none"}}),l(Xc,{style:{display:t.value==="light"?"block":"none"}})])}}),ev=B({name:"AppearanceMode",setup(){const e=le(),{canToggle:t}=_r(),n=L(()=>e.value.outlookLocales.darkmode);return()=>t.value?l("div",{class:"appearance-wrapper"},[l("label",{class:"appearance-title",for:"appearance-switch"},n.value),l(ru)]):null}});const $o="VUEPRESS_THEME_COLOR",tv=B({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(n="")=>{const r=document.documentElement.classList,o=dt(e.themeColor);if(!n){localStorage.removeItem($o),r.remove(...o);return}r.remove(...o.filter(a=>a!==n)),r.add(n),localStorage.setItem($o,n)};return ye(()=>{const n=localStorage.getItem($o);n&&t(n)}),()=>l("ul",{id:"theme-color-picker"},[l("li",l("span",{class:"theme-color",onClick:()=>t()})),Dn(e.themeColor).map(([n,r])=>l("li",l("span",{style:{background:r},onClick:()=>t(n)})))])}}),bn=Ha.enableThemeColor==="true",nv=bn?Gf(Dn(Ha).filter(([e])=>e.startsWith("theme-"))):{},rv=B({name:"ThemeColor",setup(){const e=le(),t=L(()=>e.value.outlookLocales.themeColor);return()=>bn?l("div",{class:"theme-color-wrapper"},[l("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),l(tv,{themeColor:nv})]):null}});const ou=B({name:"ToggleFullScreenButton",setup(){const e=le(),{isSupported:t,isFullscreen:n,toggle:r}=$a(),o=L(()=>e.value.outlookLocales.fullscreen);return()=>t?l("div",{class:"full-screen-wrapper"},[l("label",{class:"full-screen-title",for:"full-screen-switch"},o.value),l("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:n.value,onClick:()=>r()},n.value?l(tu):l(eu))]):null}}),au=B({name:"OutlookSettings",setup(){const e=Gt(),t=xn(),n=L(()=>!t.value&&e.value.fullscreen);return()=>l(pr,()=>[bn?l(rv):null,l(ev),n.value?l(ou):null])}});const ov=B({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:n}){const r=ie(),{isMobile:o}=gr(),a=ft(),i=Lc(a);return ye(()=>{a.value=document.body,ve(o,s=>{!s&&e.show&&(i.value=!1,t("close"))}),ve(()=>r.value.path,()=>{i.value=!1,t("close")})}),ur(()=>{i.value=!1}),()=>l(wt,{name:"fade",onEnter:()=>{i.value=!0},onAfterLeave:()=>{i.value=!1}},()=>{var s,c;return e.show?l("div",{id:"nav-screen"},l("div",{class:"vp-nav-screen-container"},[(s=n.before)==null?void 0:s.call(n),l(Z2),l("div",{class:"vp-outlook-wrapper"},l(au)),(c=n.after)==null?void 0:c.call(n)])):null})}});const av=B({name:"NavbarBrand",setup(){const e=bt(),t=Rn(),n=le(),r=L(()=>n.value.home||e.value),o=L(()=>t.value.title),a=L(()=>n.value.navTitle??o.value),i=L(()=>n.value.logo?Ie(n.value.logo):null),s=L(()=>n.value.logoDark?Ie(n.value.logoDark):null);return()=>l(Re,{to:r.value,class:"vp-brand"},()=>[i.value?l("img",{class:["vp-nav-logo",{light:!!s.value}],src:i.value,alt:o.value}):null,s.value?l("img",{class:["vp-nav-logo dark"],src:s.value,alt:o.value}):null,a.value?l("span",{class:["vp-site-name",{"hide-in-pad":i.value&&n.value.hideSiteNameOnMobile!==!1}]},a.value):null])}});const iv=B({name:"NavbarLinks",setup(){const e=Kc();return()=>e.value.length?l("nav",{class:"vp-nav-links"},e.value.map(t=>l("div",{class:"nav-item hide-in-mobile"},"children"in t?l(Wc,{config:t}):l(ze,{config:t})))):null}});const lv=B({name:"RepoLink",components:{BitbucketIcon:pc,GiteeIcon:fc,GitHubIcon:uc,GitLabIcon:dc,SourceIcon:vc},setup(){const e=K2();return()=>e.value?l("div",{class:"nav-item vp-repo"},l("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},l(Ke(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const iu=({active:e=!1},{emit:t})=>l("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},l("span",[l("span",{class:"vp-top"}),l("span",{class:"vp-middle"}),l("span",{class:"vp-bottom"})]));iu.displayName="ToggleNavbarButton";const qa=(e,{emit:t})=>l("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},l("span",{class:"icon"}));qa.displayName="ToggleSidebarButton";qa.emits=["toggle"];const sv=B({name:"OutlookButton",setup(){const{isSupported:e}=$a(),t=Gt(),n=xn(),r=ie(),{canToggle:o}=_r(),a=Y(!1),i=L(()=>!n.value&&t.value.fullscreen&&e);return ve(()=>r.value.path,()=>{a.value=!1}),()=>o.value||i.value||bn?l("div",{class:"nav-item hide-in-mobile"},o.value&&!i.value&&!bn?l(ru):i.value&&!o.value&&!bn?l(ou):l("button",{type:"button",class:["outlook-button",{open:a.value}],tabindex:"-1","aria-hidden":!0},[l(nu),l("div",{class:"outlook-dropdown"},l(au))])):null}});const cv=B({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:n}){const r=le(),{isMobile:o}=gr(),a=Y(!1),i=L(()=>{const{navbarAutoHide:d="mobile"}=r.value;return d!=="none"&&(d==="always"||o.value)}),s=L(()=>r.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:av,Language:Y2,Links:iv,Repo:lv,Outlook:sv,Search:It("Docsearch")?Ke("Docsearch"):It("SearchBox")?Ke("SearchBox"):yl},u=d=>c[d]??(It(d)?Ke(d):yl);return()=>{var d,p,v,h,b,A;return[l("header",{id:"navbar",class:["vp-navbar",{"auto-hide":i.value,"hide-icon":r.value.navbarIcon===!1}]},[l("div",{class:"vp-navbar-start"},[l(qa,{onToggle:()=>{a.value&&(a.value=!1),t("toggleSidebar")}}),(d=n.startBefore)==null?void 0:d.call(n),(s.value.start||[]).map(I=>l(u(I))),(p=n.startAfter)==null?void 0:p.call(n)]),l("div",{class:"vp-navbar-center"},[(v=n.centerBefore)==null?void 0:v.call(n),(s.value.center||[]).map(I=>l(u(I))),(h=n.centerAfter)==null?void 0:h.call(n)]),l("div",{class:"vp-navbar-end"},[(b=n.endBefore)==null?void 0:b.call(n),(s.value.end||[]).map(I=>l(u(I))),(A=n.endAfter)==null?void 0:A.call(n),l(iu,{active:a.value,onToggle:()=>{a.value=!a.value}})])]),l(ov,{show:a.value,onClose:()=>{a.value=!1}},{before:()=>{var I;return(I=n.screenTop)==null?void 0:I.call(n)},after:()=>{var I;return(I=n.screenBottom)==null?void 0:I.call(n)}})]}}});const uv=B({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=pt();return()=>[zc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:wn(t,e.config,!0)}],exact:!0}),qc(e.config.children)]}});const dv=B({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const n=pt(),r=L(()=>wn(n,e.config)),o=L(()=>wn(n,e.config,!0));return()=>{const{collapsible:a,children:i=[],icon:s,prefix:c,link:u,text:d}=e.config;return l("section",{class:"vp-sidebar-group"},[l(a?"button":"p",{class:["vp-sidebar-heading",{clickable:a||u,exact:o.value,active:r.value}],...a?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[l($e,{icon:s}),u?l(ze,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):l("span",{class:"vp-sidebar-title"},d),a?l("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!a?l(lu,{key:c,config:i}):null])}}});const lu=B({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=pt(),n=Y(-1),r=o=>{n.value=o===n.value?-1:o};return ve(()=>t.path,()=>{const o=e.config.findIndex(a=>jc(t,a));n.value=o},{immediate:!0,flush:"post"}),()=>l("ul",{class:"vp-sidebar-links"},e.config.map((o,a)=>l("li",o.type==="group"?l(dv,{config:o,open:a===n.value,onToggle:()=>r(a)}):l(uv,{config:o}))))}});const fv=B({name:"SideBar",slots:Object,setup(e,{slots:t}){const n=pt(),r=le(),o=za(),a=ft();return ye(()=>{ve(()=>n.hash,i=>{const s=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${n.path}${i}"]`);if(!s)return;const{top:c,height:u}=a.value.getBoundingClientRect(),{top:d,height:p}=s.getBoundingClientRect();dc+u&&s.scrollIntoView(!1)},{immediate:!0})}),()=>{var i,s,c;return l("aside",{ref:a,id:"sidebar",class:["vp-sidebar",{"hide-icon":r.value.sidebarIcon===!1}]},[(i=t.top)==null?void 0:i.call(t),((s=t.default)==null?void 0:s.call(t))||l(lu,{config:o.value}),(c=t.bottom)==null?void 0:c.call(t)])}}});const Ua=B({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const n=Ne(),r=ie(),o=Ee(),a=le(),{isMobile:i,isPC:s}=gr(),[c,u]=ll(!1),[d,p]=ll(!1),v=za(),h=Y(!1),b=L(()=>e.noNavbar||o.value.navbar===!1||a.value.navbar===!1?!1:!!(r.value.title||a.value.logo||a.value.repo||a.value.navbar)),A=L(()=>e.noSidebar?!1:o.value.sidebar!==!1&&v.value.length!==0&&!o.value.home),I=L(()=>e.noToc||o.value.home?!1:o.value.toc||a.value.toc!==!1&&o.value.toc!==!1),E={x:0,y:0},k=w=>{E.x=w.changedTouches[0].clientX,E.y=w.changedTouches[0].clientY},y=w=>{const U=w.changedTouches[0].clientX-E.x,H=w.changedTouches[0].clientY-E.y;Math.abs(U)>Math.abs(H)*1.5&&Math.abs(U)>40&&(U>0&&E.x<=80?u(!0):u(!1))},P=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let $=0;return Se("scroll",sp(()=>{const w=P();w<=58||w<$?h.value=!1:$+200{w||u(!1)}),ye(()=>{const w=Lc(document.body);ve(c,H=>{w.value=H});const U=n.afterEach(()=>{u(!1)});ur(()=>{w.value=!1,U()})}),()=>l(It("GlobalEncrypt")?Ke("GlobalEncrypt"):tc,()=>l("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!A.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":I.value,"hide-navbar":h.value,"sidebar-collapsed":!i.value&&!s.value&&d.value,"sidebar-open":i.value&&c.value},e.containerClass,o.value.containerClass||""],onTouchStart:k,onTouchEnd:y},[b.value?l(cv,{onToggleSidebar:()=>u()},{startBefore:()=>{var w;return(w=t.navbarStartBefore)==null?void 0:w.call(t)},startAfter:()=>{var w;return(w=t.navbarStartAfter)==null?void 0:w.call(t)},centerBefore:()=>{var w;return(w=t.navbarCenterBefore)==null?void 0:w.call(t)},centerAfter:()=>{var w;return(w=t.navbarCenterAfter)==null?void 0:w.call(t)},endBefore:()=>{var w;return(w=t.navbarEndBefore)==null?void 0:w.call(t)},endAfter:()=>{var w;return(w=t.navbarEndAfter)==null?void 0:w.call(t)},screenTop:()=>{var w;return(w=t.navScreenTop)==null?void 0:w.call(t)},screenBottom:()=>{var w;return(w=t.navScreenBottom)==null?void 0:w.call(t)}}):null,l(wt,{name:"fade"},()=>c.value?l("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),l(wt,{name:"fade"},()=>i.value?null:l("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},l("span",{class:["arrow",d.value?"end":"start"]}))),l(fv,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var w;return(w=t.sidebarTop)==null?void 0:w.call(t)},bottom:()=>{var w;return(w=t.sidebarBottom)==null?void 0:w.call(t)}}),t.default(),l(J2)]))}});const oa=(e,{slots:t})=>{var p,v;const{bgImage:n,bgImageDark:r,bgImageStyle:o,color:a,description:i,image:s,imageDark:c,header:u,features:d=[]}=e;return l("div",{class:"vp-feature-wrapper"},[n?l("div",{class:["vp-feature-bg",{light:r}],style:[{"background-image":`url(${n})`},o]}):null,r?l("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${r})`},o]}):null,l("div",{class:"vp-feature",style:a?{color:a}:{}},[((p=t.image)==null?void 0:p.call(t,e))||[s?l("img",{class:["vp-feature-image",{light:c}],src:Ie(s),alt:u}):null,c?l("img",{class:"vp-feature-image dark",src:Ie(c),alt:u}):null],((v=t.info)==null?void 0:v.call(t,e))||[u?l("h2",{class:"vp-feature-header"},u):null,i?l("p",{class:"vp-feature-description",innerHTML:i}):null],d.length?l("div",{class:"vp-features"},d.map(({icon:h,title:b,details:A,link:I})=>{const E=[l("h3",{class:"vp-feature-title"},[l($e,{icon:h}),l("span",{innerHTML:b})]),l("p",{class:"vp-feature-details",innerHTML:A})];return I?er(I)?l("a",{class:"vp-feature-item link",href:I,role:"navigation","aria-label":b,target:"_blank"},E):l(Re,{class:"vp-feature-item link",to:I,role:"navigation","aria-label":b},()=>E):l("div",{class:"vp-feature-item"},E)})):null])])};oa.displayName="FeaturePanel";const se=B({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const n=o=>{o.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,o.style.transform="translateY(-20px)",o.style.opacity="0"},r=o=>{o.style.transform="translateY(0)",o.style.opacity="1"};return()=>l(e.type==="single"?wt:ld,{name:"drop",appear:e.appear,onAppear:n,onAfterAppear:r,onEnter:n,onAfterEnter:r,onBeforeLeave:n},()=>t.default())}});const pv=B({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const n=Ee(),r=Rn(),o=L(()=>n.value.heroFullScreen??!1),a=L(()=>{const{heroText:u,tagline:d}=n.value;return{text:u??r.value.title??"Hello",tagline:d??r.value.description??"",isFullScreen:o.value}}),i=L(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:v,heroImageStyle:h}=n.value;return{image:d?Ie(d):null,imageDark:p?Ie(p):null,heroStyle:h,alt:v||u||"hero image",isFullScreen:o.value}}),s=L(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=n.value;return{image:kt(u)?Ie(u):null,imageDark:kt(d)?Ie(d):null,bgStyle:p,isFullScreen:o.value}}),c=L(()=>n.value.actions??[]);return()=>{var u,d,p;return l("header",{class:["vp-hero-info-wrapper",{fullscreen:o.value}]},[((u=t.heroBg)==null?void 0:u.call(t,s.value))||[s.value.image?l("div",{class:["vp-hero-mask",{light:s.value.imageDark}],style:[{"background-image":`url(${s.value.image})`},s.value.bgStyle]}):null,s.value.imageDark?l("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${s.value.imageDark})`},s.value.bgStyle]}):null],l("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,i.value))||l(se,{appear:!0,type:"group"},()=>[i.value.image?l("img",{key:"light",class:["vp-hero-image",{light:i.value.imageDark}],style:i.value.heroStyle,src:i.value.image,alt:i.value.alt}):null,i.value.imageDark?l("img",{key:"dark",class:"vp-hero-image dark",style:i.value.heroStyle,src:i.value.imageDark,alt:i.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,a.value))??l("div",{class:"vp-hero-infos"},[a.value.text?l(se,{appear:!0,delay:.04},()=>l("h1",{id:"main-title"},a.value.text)):null,a.value.tagline?l(se,{appear:!0,delay:.08},()=>l("p",{class:"vp-description",innerHTML:a.value.tagline})):null,c.value.length?l(se,{appear:!0,delay:.12},()=>l("p",{class:"vp-actions"},c.value.map(v=>l(ze,{class:["vp-action",v.type||"default"],config:v,noExternalLinkIcon:!0})))):null])])])}}});const su=(e,{slots:t})=>{var v,h,b;const{bgImage:n,bgImageDark:r,bgImageStyle:o,color:a,description:i,image:s,imageDark:c,header:u,highlights:d=[],type:p="un-order"}=e;return l("div",{class:"vp-highlight-wrapper",style:a?{color:a}:{}},[n?l("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${n})`},o]}):null,r?l("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,l("div",{class:"vp-highlight"},[((v=t.image)==null?void 0:v.call(t,e))||[s?l("img",{class:["vp-highlight-image",{light:c}],src:Ie(s),alt:u}):null,c?l("img",{class:"vp-highlight-image dark",src:Ie(c),alt:u}):null],((h=t.info)==null?void 0:h.call(t,e))||[l("div",{class:"vp-highlight-info-wrapper"},l("div",{class:"vp-highlight-info"},[u?l("h2",{class:"vp-highlight-header",innerHTML:u}):null,i?l("p",{class:"vp-highlight-description",innerHTML:i}):null,((b=t.highlights)==null?void 0:b.call(t,d))||l(p==="order"?"ol":p==="no-order"?"dl":"ul",{class:"vp-highlights"},d.map(({icon:A,title:I,details:E,link:k})=>{const y=[l(p==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[A?l($e,{class:"vp-highlight-icon",icon:A}):null,l("span",{innerHTML:I})]),E?l(p==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:E}):null];return l(p==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:k}]},k?zf(k)?l("a",{class:"vp-highlight-item link",href:k,role:"navigation","aria-label":I,target:"_blank"},y):l(Re,{class:"vp-highlight-item link",to:k,role:"navigation","aria-label":I},()=>y):l("div",{class:"vp-highlight-item"},y))}))]))]])])};su.displayName="HighlightPanel";const br=({custom:e})=>l(js,{class:["theme-hope-content",{custom:e}]});br.displayName="MarkdownContent";br.props={custom:Boolean};const vv=B({name:"HomePage",slots:Object,setup(e,{slots:t}){const n=xn(),r=Ee(),o=L(()=>{const{features:i}=r.value;return ee(i)?i:null}),a=L(()=>{const{highlights:i}=r.value;return ee(i)?i:null});return()=>{var i,s,c,u;return l("main",{id:"main-content",class:["vp-project-home ",{pure:n.value}],"aria-labelledby":r.value.heroText===null?"":"main-title"},[(i=t.top)==null?void 0:i.call(t),l(pv),((s=a.value)==null?void 0:s.map(d=>"features"in d?l(oa,d):l(su,d)))||(o.value?l(se,{appear:!0,delay:.24},()=>l(oa,{features:o.value})):null),(c=t.center)==null?void 0:c.call(t),l(se,{appear:!0,delay:.32},()=>l(br)),(u=t.bottom)==null?void 0:u.call(t)])}}});const hv=B({name:"BreadCrumb",setup(){const e=Ne(),t=ie(),n=bt(),r=Ee(),o=le(),a=ft([]),i=L(()=>(r.value.breadcrumb||r.value.breadcrumb!==!1&&o.value.breadcrumb!==!1)&&a.value.length>1),s=L(()=>r.value.breadcrumbIcon||r.value.breadcrumbIcon!==!1&&o.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=R2(t.value.path,n.value).map(({link:p,name:v})=>{const h=u.find(b=>b.path===p);if(h){const{meta:b,path:A}=In(e,h.path);return{title:b[_e.shortTitle]||b[_e.title]||v,icon:b[_e.icon],path:A}}return null}).filter(p=>p!==null);d.length>1&&(a.value=d)};return ye(()=>{ve(()=>t.value.path,c,{immediate:!0})}),()=>l("nav",{class:["vp-breadcrumb",{disable:!i.value}]},i.value?l("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},a.value.map((u,d)=>l("li",{class:{"is-active":a.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[l(Re,{to:u.path,property:"item",typeof:"WebPage"},()=>[s.value?l($e,{icon:u.icon}):null,l("span",{property:"name"},u.title||"Unknown")]),l("meta",{property:"position",content:d+1})]))):[])}});const El=e=>{const t=Ne();return e===!1?!1:pe(e)?_n(t,e,!0):so(e)?e:null},aa=(e,t,n)=>{const r=e.findIndex(o=>o.link===t);if(r!==-1){const o=e[r+n];return o!=null&&o.link?o:null}for(const o of e)if(o.children){const a=aa(o.children,t,n);if(a)return a}return null},mv=B({name:"PageNav",setup(){const e=le(),t=Ee(),n=za(),r=ie(),o=mr(),a=L(()=>{const s=El(t.value.prev);return s===!1?null:s||(e.value.prevLink===!1?null:aa(n.value,r.value.path,-1))}),i=L(()=>{const s=El(t.value.next);return s===!1?null:s||(e.value.nextLink===!1?null:aa(n.value,r.value.path,1))});return Se("keydown",s=>{s.altKey&&(s.key==="ArrowRight"?i.value&&(o(i.value.link),s.preventDefault()):s.key==="ArrowLeft"&&a.value&&(o(a.value.link),s.preventDefault()))}),()=>a.value||i.value?l("nav",{class:"vp-page-nav"},[a.value?l(ze,{class:"prev",config:a.value},()=>{var s,c;return[l("div",{class:"hint"},[l("span",{class:"arrow start"}),e.value.metaLocales.prev]),l("div",{class:"link"},[l($e,{icon:(s=a.value)==null?void 0:s.icon}),(c=a.value)==null?void 0:c.text])]}):null,i.value?l(ze,{class:"next",config:i.value},()=>{var s,c;return[l("div",{class:"hint"},[e.value.metaLocales.next,l("span",{class:"arrow end"})]),l("div",{class:"link"},[(s=i.value)==null?void 0:s.text,l($e,{icon:(c=i.value)==null?void 0:c.icon})])]}):null]):null}}),cu=()=>l(ue,{name:"author"},()=>l("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));cu.displayName="AuthorIcon";const uu=()=>l(ue,{name:"calendar"},()=>l("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));uu.displayName="CalendarIcon";const du=()=>l(ue,{name:"category"},()=>l("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));du.displayName="CategoryIcon";const fu=()=>l(ue,{name:"print"},()=>l("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));fu.displayName="PrintIcon";const pu=()=>l(ue,{name:"tag"},()=>l("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));pu.displayName="TagIcon";const vu=()=>l(ue,{name:"timer"},()=>l("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));vu.displayName="TimerIcon";const hu=()=>l(ue,{name:"word"},()=>[l("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),l("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);hu.displayName="WordIcon";const Wt=()=>{const e=le();return L(()=>e.value.metaLocales)},gv={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},_v=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const a=cc(e);let i;return o?i=o:a!==null&&(i=gv[a]),i?i.replace(/:repo/,on(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,xs(`${Da(n)}/${r}`)):null},bv=()=>{const e=le(),t=ie(),n=Ee();return L(()=>{const{repo:r,docsRepo:o=r,docsBranch:a="main",docsDir:i="",editLink:s,editLinkPattern:c=""}=e.value;if(!(n.value.editLink??s??!0)||!o)return null;const d=_v({docsRepo:o,docsBranch:a,docsDir:i,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return d?{text:e.value.metaLocales.editLink,link:d}:null})},yv=()=>{const e=Rn(),t=le(),n=ie(),r=Ee();return L(()=>{var i,s;return!(r.value.lastUpdated??t.value.lastUpdated??!0)||!((i=n.value.git)!=null&&i.updatedTime)?null:new Date((s=n.value.git)==null?void 0:s.updatedTime).toLocaleString(e.value.lang)})},Ev=()=>{const e=le(),t=ie(),n=Ee();return L(()=>{var o;return n.value.contributors??e.value.contributors??!0?((o=t.value.git)==null?void 0:o.contributors)??null:null})};const Lv=B({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=Wt();return()=>e.author.length?l("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(cu),l("span",e.author.map(n=>n.url?l("a",{class:"page-author-item",href:n.url,target:"_blank",rel:"noopener noreferrer"},n.name):l("span",{class:"page-author-item"},n.name))),l("span",{property:"author",content:e.author.map(n=>n.name).join(", ")})]):null}});const Tv=B({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ne(),n=ie(),r=Wt(),o=(a,i="")=>{i&&n.value.path!==i&&(a.preventDefault(),t.push(i))};return()=>e.category.length?l("span",{class:"page-category-info","aria-label":`${r.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(du),e.category.map(({name:a,path:i})=>l("span",{class:["page-category-item",{[`category${po(a,9)}`]:!e.pure,clickable:i}],role:i?"navigation":"",onClick:s=>o(s,i)},a)),l("meta",{property:"articleSection",content:e.category.map(({name:a})=>a).join(",")})]):null}}),Av=B({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=co(),n=Wt();return()=>e.date?l("span",{class:"page-date-info","aria-label":`${n.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(uu),l("span",l(pr,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),l("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}});const Iv=B({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=Wt();return()=>e.isOriginal?l("span",{class:"page-original-info"},t.value.origin):null}}),wv=B({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Wt(),n=L(()=>{if(!e.readingTime)return null;const{minutes:r}=e.readingTime;return r<1?"PT1M":`PT${Math.round(r)}M`});return()=>{var r,o;return(r=e.readingTimeLocale)!=null&&r.time?l("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(vu),l("span",(o=e.readingTimeLocale)==null?void 0:o.time),l("meta",{property:"timeRequired",content:n.value})]):null}}});const kv=B({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ne(),n=ie(),r=Wt(),o=(a,i="")=>{i&&n.value.path!==i&&(a.preventDefault(),t.push(i))};return()=>e.tag.length?l("span",{class:"page-tag-info","aria-label":`${r.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(pu),e.tag.map(({name:a,path:i})=>l("span",{class:["page-tag-item",{[`tag${po(a,9)}`]:!e.pure,clickable:i}],role:i?"navigation":"",onClick:s=>o(s,i)},a)),l("meta",{property:"keywords",content:e.tag.map(({name:a})=>a).join(",")})]):null}}),Pv=B({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Wt();return()=>{var n,r,o;return(n=e.readingTimeLocale)!=null&&n.words?l("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(hu),l("span",(r=e.readingTimeLocale)==null?void 0:r.words),l("meta",{property:"wordCount",content:(o=e.readingTime)==null?void 0:o.words})]):null}}});const mu=B({name:"PageInfo",components:{AuthorInfo:Lv,CategoryInfo:Tv,DateInfo:Av,OriginalInfo:Iv,PageViewInfo:()=>null,ReadingTimeInfo:wv,TagInfo:kv,WordInfo:Pv},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=xn();return()=>e.items?l("div",{class:"page-info"},e.items.map(n=>l(Ke(`${n}Info`),{...e.info,pure:t.value}))):null}});const Ov=B({name:"PageTitle",setup(){const e=ie(),t=Ee(),n=le(),{info:r,items:o}=V2();return()=>l("div",{class:"vp-page-title"},[l("h1",[n.value.titleIcon===!1?null:l($e,{icon:t.value.icon}),e.value.title]),l(mu,{info:r.value,...o.value===null?{}:{items:o.value}}),l("hr")])}}),gu=()=>l(ue,{name:"edit"},()=>[l("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),l("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);gu.displayName="EditIcon";const Rv=B({name:"PageMeta",setup(){const e=le(),t=bv(),n=yv(),r=Ev();return()=>{const{metaLocales:o}=e.value;return l("footer",{class:"page-meta"},[t.value?l("div",{class:"meta-item edit-link"},l(ze,{class:"label",config:t.value},{before:()=>l(gu)})):null,l("div",{class:"meta-item git-info"},[n.value?l("div",{class:"update-time"},[l("span",{class:"label"},`${o.lastUpdated}: `),l(pr,()=>l("span",{class:"info"},n.value))]):null,r.value&&r.value.length?l("div",{class:"contributors"},[l("span",{class:"label"},`${o.contributors}: `),r.value.map(({email:a,name:i},s)=>[l("span",{class:"contributor",title:`email: ${a}`},i),s!==r.value.length-1?",":""])]):null])])}}});const Dv=B({name:"PrintButton",setup(){const e=Gt(),t=le();return()=>e.value.print===!1?null:l("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},l(fu))}});const Sv=({title:e,level:t,slug:n})=>l(Re,{to:`#${n}`,class:["toc-link",`level${t}`]},()=>e),ia=(e,t)=>{const n=pt();return e.length&&t>0?l("ul",{class:"toc-list"},e.map(r=>{const o=ia(r.children,t-1);return[l("li",{class:["toc-item",{active:Ba(n,`#${r.slug}`)}]},Sv(r)),o?l("li",o):null]})):null},_u=B({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const n=pt(),r=ie(),o=Wt(),a=ft(),i=Y("-1.7rem"),s=u=>{var d;(d=a.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(a.value){const u=document.querySelector(".toc-item.active");u?i.value=`${u.getBoundingClientRect().top-a.value.getBoundingClientRect().top+a.value.scrollTop}px`:i.value="-1.7rem"}else i.value="-1.7rem"};return ye(()=>{ve(()=>n.hash,u=>{if(a.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:v}=a.value.getBoundingClientRect(),{top:h,height:b}=d.getBoundingClientRect();hp+v&&s(a.value.scrollTop+h+b-p-v)}}),ve(()=>n.fullPath,c,{flush:"post",immediate:!0})}),()=>{var d,p;const u=e.items.length?ia(e.items,e.headerDepth):r.value.headers?ia(r.value.headers,e.headerDepth):null;return u?l("div",{class:"toc-place-holder"},[l("aside",{id:"toc"},[(d=t.before)==null?void 0:d.call(t),l("div",{class:"toc-header"},[o.value.toc,l(Dv)]),l("div",{class:"toc-wrapper",ref:a},[u,l("div",{class:"toc-marker",style:{top:i.value}})]),(p=t.after)==null?void 0:p.call(t)])]):null}}});const xv=B({name:"NormalPage",slots:Object,setup(e,{slots:t}){const n=Ee(),r=ie(),{isDarkmode:o}=_r(),a=le(),i=L(()=>n.value.toc||n.value.toc!==!1&&a.value.toc!==!1);return()=>l("main",{id:"main-content",class:"vp-page"},l(It("LocalEncrypt")?Ke("LocalEncrypt"):tc,()=>{var s,c,u,d;return[(s=t.top)==null?void 0:s.call(t),n.value.cover?l("img",{class:"page-cover",src:Ie(n.value.cover),alt:r.value.title,"no-view":""}):null,l(hv),l(Ov),i.value?l(_u,{headerDepth:n.value.headerDepth??a.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),l(br),(u=t.contentAfter)==null?void 0:u.call(t),l(Rv),l(mv),It("CommentService")?l(Ke("CommentService"),{darkmode:o.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}});const Ga=B({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ie(),n=le(),r=ft(),o=({target:a})=>{const i=document.querySelector(a.hash);if(i){const s=()=>{i.removeAttribute("tabindex"),i.removeEventListener("blur",s)};i.setAttribute("tabindex","-1"),i.addEventListener("blur",s),i.focus(),window.scrollTo(0,0)}};return ye(()=>{ve(()=>t.value.path,()=>r.value.focus())}),()=>[l("span",{ref:r,tabindex:"-1"}),l("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:o},n.value.routeLocales.skipToContent)]}});const Cv=B({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:n,pending:r}=Hc();return()=>l(wt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:n,onBeforeLeave:r},()=>{var o;return(o=t.default)==null?void 0:o.call(t)})}}),Vv=B({name:"Layout",setup(){const e=Gt(),t=le(),n=ie(),r=Ee(),{isMobile:o}=gr(),a=L(()=>{var i,s;return((i=t.value.blog)==null?void 0:i.sidebarDisplay)||((s=e.value.blog)==null?void 0:s.sidebarDisplay)||"mobile"});return()=>[l(Ga),l(Ua,{},{default:()=>r.value.home?l(vv):l(Cv,()=>l(xv,{key:n.value.path})),...a.value!=="none"?{navScreenBottom:()=>l(Ke("BloggerInfo"))}:{},...!o.value&&a.value==="always"?{sidebar:()=>l(Ke("BloggerInfo"))}:{}})]}});const Mv=B({name:"NotFoundHint",setup(){const e=le(),t=()=>{const n=e.value.routeLocales.notFoundMsg;return n[Math.floor(Math.random()*n.length)]};return()=>l("div",{class:"not-found-hint"},[l("p",{class:"error-code"},"404"),l("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),l("p",{class:"error-hint"},t())])}});const Bv=B({name:"NotFound",slots:Object,setup(e,{slots:t}){const n=bt(),r=le(),{navigate:o}=Zo({to:r.value.home??n.value});return()=>[l(Ga),l(Ua,{noSidebar:!0},()=>{var a;return l("main",{id:"main-content",class:"vp-page not-found"},((a=t.default)==null?void 0:a.call(t))||[l(Mv),l("div",{class:"actions"},[l("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},r.value.routeLocales.back),l("button",{type:"button",class:"action-button",onClick:()=>o()},r.value.routeLocales.home)])])})]}}),$v={GitHub:'',Gmail:'',QQ:'',Wechat:''},Nv={category:{"/":{path:"/category/",map:{随笔:{path:"/category/%E9%9A%8F%E7%AC%94/",keys:["v-ed1de9c6"]},DDD:{path:"/category/ddd/",keys:["v-ed1de9c6"]},算法:{path:"/category/%E7%AE%97%E6%B3%95/",keys:["v-1dbb5152","v-7ba48b4f","v-79efb2b0"]},地图:{path:"/category/%E5%9C%B0%E5%9B%BE/",keys:["v-1dbb5152"]},微信:{path:"/category/%E5%BE%AE%E4%BF%A1/",keys:["v-1f7029f1"]},前端:{path:"/category/%E5%89%8D%E7%AB%AF/",keys:["v-248eb3ce"]},docker:{path:"/category/docker/",keys:["v-4468c549","v-7763b6e7","v-aad5ccee"]},教程:{path:"/category/%E6%95%99%E7%A8%8B/",keys:["v-7febf202"]},人工智能:{path:"/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",keys:["v-a4026a72"]},工具:{path:"/category/%E5%B7%A5%E5%85%B7/",keys:["v-1ad96fc8"]},kubernetes:{path:"/category/kubernetes/",keys:["v-f0b271fa"]},数据结构:{path:"/category/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/",keys:["v-249319d7","v-0115d5da"]},DB:{path:"/category/db/",keys:["v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-1ae8d368","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-753dfaa7","v-b8674064","v-113cc96c","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3"]},MyBatis:{path:"/category/mybatis/",keys:["v-0b555a5d"]},Java:{path:"/category/java/",keys:["v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-d0e2c786","v-305fe098"]},微服务:{path:"/category/%E5%BE%AE%E6%9C%8D%E5%8A%A1/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-e97c7d16"]},RPC:{path:"/category/rpc/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066"]},TCP:{path:"/category/tcp/",keys:["v-da7165c6"]},IP:{path:"/category/ip/",keys:["v-da7165c6"]},Spring:{path:"/category/spring/",keys:["v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},SpringBoot:{path:"/category/springboot/",keys:["v-26c13b6a","v-3b468bb2"]},SpringMVC:{path:"/category/springmvc/",keys:["v-e65d617a","v-e9c712b8","v-ed30c3f6"]},并发:{path:"/category/%E5%B9%B6%E5%8F%91/",keys:["v-783d7264","v-e12a9278","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415"]}}},"/en/":{path:"/en/category/",map:{docker:{path:"/en/category/docker/",keys:["v-3fc52a21","v-72c01bbf","v-b41d033e"]},教程:{path:"/en/category/%E6%95%99%E7%A8%8B/",keys:["v-460112e6"]},人工智能:{path:"/en/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",keys:["v-6494b51e"]},工具:{path:"/en/category/%E5%B7%A5%E5%85%B7/",keys:["v-0a4a23d6"]}}}},tag:{"/":{path:"/tag/",map:{DDD:{path:"/tag/ddd/",keys:["v-ed1de9c6"]},排序:{path:"/tag/%E6%8E%92%E5%BA%8F/",keys:["v-7ba48b4f","v-79efb2b0"]},多边形:{path:"/tag/%E5%A4%9A%E8%BE%B9%E5%BD%A2/",keys:["v-1dbb5152"]},微信服务号:{path:"/tag/%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%8F%B7/",keys:["v-1f7029f1"]},LayUI:{path:"/tag/layui/",keys:["v-248eb3ce"]},docker:{path:"/tag/docker/",keys:["v-4468c549","v-7763b6e7","v-aad5ccee"]},linux:{path:"/tag/linux/",keys:["v-7febf202"]},OpenAI:{path:"/tag/openai/",keys:["v-a4026a72"]},ChatGPT:{path:"/tag/chatgpt/",keys:["v-a4026a72"]},idea:{path:"/tag/idea/",keys:["v-1ad96fc8"]},kubernetes:{path:"/tag/kubernetes/",keys:["v-f0b271fa"]},栈:{path:"/tag/%E6%A0%88/",keys:["v-249319d7"]},跳表:{path:"/tag/%E8%B7%B3%E8%A1%A8/",keys:["v-0115d5da"]},Redis:{path:"/tag/redis/",keys:["v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3"]},基础:{path:"/tag/%E5%9F%BA%E7%A1%80/",keys:["v-0b555a5d","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550"]},JVM:{path:"/tag/jvm/",keys:["v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-d0e2c786","v-305fe098"]},类加载:{path:"/tag/%E7%B1%BB%E5%8A%A0%E8%BD%BD/",keys:["v-d0e2c786"]},Lock:{path:"/tag/lock/",keys:["v-c5b336ca","v-0dc9bed3","v-53a68847","v-ea9ab6de"]},GC:{path:"/tag/gc/",keys:["v-366969d6","v-64b533de","v-5a83f4b7"]},dubbo:{path:"/tag/dubbo/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066"]},zookeeper:{path:"/tag/zookeeper/",keys:["v-e97c7d16"]},网络编程:{path:"/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/",keys:["v-da7165c6"]},MySQL:{path:"/tag/mysql/",keys:["v-1ae8d368","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-753dfaa7","v-b8674064","v-113cc96c"]},索引:{path:"/tag/%E7%B4%A2%E5%BC%95/",keys:["v-1ae8d368","v-76f2d346","v-753dfaa7","v-b8674064","v-113cc96c"]},源码:{path:"/tag/%E6%BA%90%E7%A0%81/",keys:["v-4f779f12"]},插件:{path:"/tag/%E6%8F%92%E4%BB%B6/",keys:["v-26c13b6a","v-3b468bb2"]},ThreadLocal:{path:"/tag/threadlocal/",keys:["v-55cc2415"]},Atomic:{path:"/tag/atomic/",keys:["v-49daa846"]},线程安全:{path:"/tag/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/",keys:["v-783d7264","v-e12a9278","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-4ee32d34"]},JUC:{path:"/tag/juc/",keys:["v-15eaf018"]},AQS:{path:"/tag/aqs/",keys:["v-3b8d5cd7"]},Queue:{path:"/tag/queue/",keys:["v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4"]}}},"/en/":{path:"/en/tag/",map:{docker:{path:"/en/tag/docker/",keys:["v-3fc52a21","v-72c01bbf","v-b41d033e"]},linux:{path:"/en/tag/linux/",keys:["v-460112e6"]},OpenAI:{path:"/en/tag/openai/",keys:["v-6494b51e"]},ChatGPT:{path:"/en/tag/chatgpt/",keys:["v-6494b51e"]},idea:{path:"/en/tag/idea/",keys:["v-0a4a23d6"]}}}}},Hv={article:{"/":{path:"/article/",keys:["v-da7165c6","v-1dbb5152","v-2d175b02","v-59c0c6b8","v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-783d7264","v-e12a9278","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-f0b271fa","v-26c13b6a","v-3b468bb2","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415","v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-1ad96fc8","v-1ae8d368","v-ed1de9c6","v-1f7029f1","v-248eb3ce","v-7febf202","v-a4026a72","v-249319d7","v-0b555a5d","v-d0e2c786","v-e97c7d16","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-4468c549","v-7763b6e7","v-aad5ccee","v-753dfaa7","v-305fe098","v-b8674064","v-113cc96c","v-7ba48b4f","v-0115d5da","v-79efb2b0","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},"/en/":{path:"/en/article/",keys:["v-5aa3d8ba","v-0a4a23d6","v-460112e6","v-6494b51e","v-3fc52a21","v-72c01bbf","v-b41d033e"]}},star:{"/":{path:"/star/",keys:["v-da7165c6","v-1dbb5152"]},"/en/":{path:"/en/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-2d175b02","v-59c0c6b8","v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-da7165c6","v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-783d7264","v-e12a9278","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-f0b271fa","v-26c13b6a","v-3b468bb2","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415","v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-1ad96fc8","v-1ae8d368","v-ed1de9c6","v-1dbb5152","v-1f7029f1","v-248eb3ce","v-7febf202","v-a4026a72","v-249319d7","v-0b555a5d","v-d0e2c786","v-e97c7d16","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-4468c549","v-7763b6e7","v-aad5ccee","v-753dfaa7","v-305fe098","v-b8674064","v-113cc96c","v-7ba48b4f","v-0115d5da","v-79efb2b0","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},"/en/":{path:"/en/timeline/",keys:["v-5aa3d8ba","v-0a4a23d6","v-460112e6","v-6494b51e","v-3fc52a21","v-72c01bbf","v-b41d033e"]}}},Ll=Y(Nv),bu=(e="")=>{const t=ie(),n=Ne(),r=bt();return L(()=>{var c;const o=e||((c=Ee().value.blog)==null?void 0:c.key)||"";if(!o)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const a=n.getRoutes();if(!Ll.value[o])throw new Error(`useBlogCategory: key ${o} is invalid`);const i=Ll.value[o][r.value],s={path:i.path,map:{}};for(const u in i.map){const d=i.map[u];s.map[u]={path:d.path,items:[]};for(const p of d.keys){const v=a.find(({name:h})=>h===p);if(v){const h=In(n,v.path);s.map[u].items.push({path:h.path,info:h.meta})}}t.value.path===d.path&&(s.currentItems=s.map[u].items)}return s})},Tl=Y(Hv),go=(e="")=>{const t=Ne(),n=bt();return L(()=>{var s;const r=e||((s=Ee().value.blog)==null?void 0:s.key)||"";if(!r)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Tl.value[r])throw new Error(`useBlogType: key ${e} is invalid`);const o=t.getRoutes(),a=Tl.value[r][n.value],i={path:a.path,items:[]};for(const c of a.keys){const u=o.find(({name:d})=>d===c);if(u){const d=In(t,u.path);i.items.push({path:d.path,info:d.meta})}}return i})},yu=Symbol.for("categoryMap"),yr=()=>{const e=he(yu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},Fv=()=>{const e=bu("category");ct(yu,e)},Er=()=>{const e=Gt(),t=le();return L(()=>({...e.value.blog,...t.value.blog}))},Eu=Symbol.for("tagMap"),Lr=()=>{const e=he(Eu);if(!e)throw new Error("useTagMap() is called without provider.");return e},jv=()=>{const e=bu("tag");ct(Eu,e)},zv=e=>{const t=le();return L(()=>{const{[_e.author]:n}=e.value;return n?or(n):n===!1?[]:or(t.value.author,!1)})},qv=e=>{const t=yr();return L(()=>ic(e.value[_e.category]).map(n=>({name:n,path:t.value.map[n].path})))},Uv=e=>{const t=Lr();return L(()=>lc(e.value[_e.tag]).map(n=>({name:n,path:t.value.map[n].path})))},Gv=e=>L(()=>{const{[_e.date]:t}=e.value;return Va(t)}),Wv=e=>{const t=Pn(e,"info"),n=Er(),r=zv(t),o=qv(t),a=Uv(t),i=Gv(t),s=$c(),c=L(()=>({author:r.value,category:o.value,date:i.value,localizedDate:t.value[_e.localizedDate]||"",tag:a.value,isOriginal:t.value[_e.isOriginal]||!1,readingTime:t.value[_e.readingTime]||null,readingTimeLocale:t.value[_e.readingTime]&&s.value?Bc(t.value[_e.readingTime],s.value):null,pageview:e.path})),u=L(()=>n.value.articleInfo);return{info:c,items:u}},Lu=Symbol(""),Tr=()=>{const e=he(Lu);if(!e)throw new Error("useArticles() is called without provider.");return e},Jv=()=>{const e=go("article");ct(Lu,e)},Tu=Symbol(""),Wa=()=>{const e=he(Tu);if(!e)throw new Error("useStars() is called without provider.");return e},Qv=()=>{const e=go("star");ct(Tu,e)},Au=Symbol(""),Ja=()=>{const e=he(Au);if(!e)throw new Error("useTimelines() is called without provider.");return e},Kv=()=>{const e=go("timeline"),t=L(()=>{const n=[];return e.value.items.forEach(({info:r,path:o})=>{const a=Va(r[_e.date]),i=a==null?void 0:a.getFullYear(),s=a?a.getMonth()+1:null,c=a==null?void 0:a.getDate();i&&s&&c&&((!n[0]||n[0].year!==i)&&n.unshift({year:i,items:[]}),n[0].items.push({date:`${s}/${c}`,info:r,path:o}))}),{...e.value,config:n.reverse()}});ct(Au,t)},Yv=()=>{Jv(),Fv(),Qv(),jv(),Kv()};const Xv=B({name:"SocialMedia",setup(){const e=Er(),t=xn(),n=L(()=>{const r=e.value.medias;return r?Dn(r).map(([o,a])=>({name:o,icon:$v[o],url:a})):[]});return()=>n.value.length?l("div",{class:"vp-social-medias"},n.value.map(({name:r,icon:o,url:a})=>l("a",{class:"vp-social-media",href:a,rel:"noopener noreferrer",target:"_blank","aria-label":r,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:o}))):null}});const Qa=B({name:"BloggerInfo",setup(){const e=Er(),t=Rn(),n=le(),r=Tr(),o=yr(),a=Lr(),i=Ja(),s=mr(),c=L(()=>{var v;return e.value.name||((v=or(n.value.author)[0])==null?void 0:v.name)||t.value.title}),u=L(()=>e.value.avatar||n.value.logo),d=L(()=>n.value.blogLocales),p=L(()=>e.value.intro);return()=>{const{article:v,category:h,tag:b,timeline:A}=d.value,I=[[r.value.path,r.value.items.length,v],[o.value.path,dt(o.value.map).length,h],[a.value.path,dt(a.value.map).length,b],[i.value.path,i.value.items.length,A]];return l("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[l("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>s(p.value)}:{}},[u.value?l("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:Ie(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?l("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?l("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?l("meta",{property:"url",content:Ie(p.value)}):null]),l("div",{class:"vp-blog-counts"},I.map(([E,k,y])=>l(Re,{class:"vp-blog-count",to:E},()=>[l("div",{class:"count"},k),l("div",y)]))),l(Xv)])}}}),la=()=>l(ue,{name:"category"},()=>l("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));la.displayName="CategoryIcon";const sa=()=>l(ue,{name:"tag"},()=>l("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));sa.displayName="TagIcon";const Ka=()=>l(ue,{name:"timeline"},()=>l("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Ka.displayName="TimelineIcon";const Iu=()=>l(ue,{name:"slides"},()=>l("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));Iu.displayName="SlideIcon";const wu=()=>l(ue,{name:"sticky"},()=>[l("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const Yr=()=>l(ue,{name:"article"},()=>l("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));Yr.displayName="ArticleIcon";const ku=()=>l(ue,{name:"book"},()=>l("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));ku.displayName="BookIcon";const Pu=()=>l(ue,{name:"link"},()=>l("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));Pu.displayName="LinkIcon";const Ou=()=>l(ue,{name:"project"},()=>l("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));Ou.displayName="ProjectIcon";const Ru=()=>l(ue,{name:"friend"},()=>l("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Ru.displayName="FriendIcon";const ca=()=>l(ue,{name:"slide-down"},()=>l("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));ca.displayName="SlideDownIcon";const Du=()=>l("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Du.displayName="EmptyIcon";const Su=()=>l(ue,{name:"lock"},()=>l("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Su.displayName="LockIcon";const Zv=B({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const n=Pn(e,"info"),{info:r,items:o}=Wv(e);return()=>{var v,h,b;const{[_e.title]:a,[_e.type]:i,[_e.isEncrypted]:s=!1,[_e.cover]:c,[_e.excerpt]:u,[_e.sticky]:d}=n.value,p=r.value;return l("div",{class:"vp-article-wrapper"},l("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((v=t.cover)==null?void 0:v.call(t,{cover:c}))||(c?[l("img",{class:"vp-article-cover",src:Ie(c)}),l("meta",{property:"image",content:Ie(c)})]:[]),d?l(wu):null,l(Re,{to:e.path},()=>{var A;return((A=t.title)==null?void 0:A.call(t,{title:a,isEncrypted:s,type:i}))||l("header",{class:"vp-article-title"},[s?l(Su):null,i===ta.slide?l(Iu):null,l("span",{property:"headline"},a)])}),((h=t.excerpt)==null?void 0:h.call(t,{excerpt:u}))||(u?l("div",{class:"vp-article-excerpt",innerHTML:u}):null),l("hr",{class:"vp-article-hr"}),((b=t.info)==null?void 0:b.call(t,{info:p}))||l(mu,{info:p,...o.value?{items:o.value}:{}})]))}}});const e3=B({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let n;const r=le(),o=Y(""),a=L(()=>r.value.paginationLocales),i=L(()=>Math.ceil(e.total/e.perPage)),s=L(()=>!!i.value&&i.value!==1),c=L(()=>i.value<7?!1:e.current>4),u=L(()=>i.value<7?!1:e.current{const{current:h}=e;let b=1,A=i.value;const I=[];i.value>=7&&(h<=4&&h4&&h>=i.value-3?(A=i.value,b=i.value-4):i.value>7&&(b=h-2,A=h+2));for(let E=b;E<=A;E++)I.push(E);return I}),p=h=>t("updateCurrentPage",h),v=h=>{const b=parseInt(h);b<=i.value&&b>0?p(b):n.pop(`${a.value.errorText.replace(/\$page/g,i.value.toString())}`)};return ye(()=>{n=new Jf}),()=>l("div",{class:"vp-pagination"},s.value?l("div",{class:"vp-pagination-list"},[l("div",{class:"vp-pagination-number "},[e.current>1?l("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},a.value.prev):null,c.value?[l("div",{role:"navigation",onClick:()=>p(1)},1),l("div",{class:"ellipsis"},"...")]:null,d.value.map(h=>l("div",{key:h,class:{active:e.current===h},role:"navigation",onClick:()=>p(h)},h)),u.value?[l("div",{class:"ellipsis"},"..."),l("div",{role:"navigation",onClick:()=>p(i.value)},i.value)]:null,e.currentp(e.current+1)},a.value.next):null]),l("div",{class:"vp-pagination-nav"},[l("label",{for:"navigation-text"},`${a.value.navigate}: `),l("input",{id:"navigation-text",value:o.value,onInput:({target:h})=>{o.value=h.value},onKeydown:h=>{h.key==="Enter"&&(h.preventDefault(),v(o.value))}}),l("button",{class:"vp-pagination-button",role:"navigation",title:a.value.action,onClick:()=>v(o.value)},a.value.action)])]):[])}});const Ya=B({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=pt(),n=Ne(),r=Er(),o=Y(1),a=L(()=>r.value.articlePerPage||10),i=L(()=>e.items.slice((o.value-1)*a.value,o.value*a.value)),s=async c=>{o.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),await n.push({path:t.path,query:u}))};return ye(()=>{const{page:c}=t.query;s(c?Number(c):1),ve(o,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)})}),()=>l("div",{id:"article-list",class:"vp-article-list"},i.value.length?[...i.value.map(({info:c,path:u},d)=>l(se,{appear:!0,delay:d*.04},()=>l(Zv,{key:u,info:c,path:u}))),l(e3,{current:o.value,perPage:a.value,total:e.items.length,onUpdateCurrentPage:s})]:l(Du))}});const Xa=B({name:"CategoryList",setup(){const e=ie(),t=yr();return()=>l("ul",{class:"vp-category-list"},Dn(t.value.map).map(([n,{path:r,items:o}])=>l("li",{class:["vp-category",`vp-category${po(n,9)}`,{active:r===e.value.path}]},l(Re,{to:r},()=>[n,l("span",{class:"count"},o.length)]))))}});const Za=B({name:"TagList",setup(){const e=Ee(),t=Lr(),n=r=>{var o;return r===((o=e.value.blog)==null?void 0:o.name)};return()=>l("ul",{class:"tag-list-wrapper"},Dn(t.value.map).map(([r,{path:o,items:a}])=>l("li",{class:["tag",`tag${po(r,9)}`,{active:n(r)}]},l(Re,{to:o},()=>[r,l("span",{class:"tag-num"},a.length)]))))}});const t3=B({name:"TimelineList",setup(){const e=le(),t=Ja(),n=mr(),r=L(()=>e.value.blogLocales.timeline);return()=>l("div",{class:"timeline-list-wrapper"},[l("div",{class:"timeline-list-title",onClick:()=>n(t.value.path)},[l(Ka),l("span",{class:"num"},t.value.items.length),r.value]),l("hr"),l("div",{class:"timeline-content"},l("ul",{class:"timeline-list"},t.value.config.map(({year:o,items:a},i)=>l(se,{appear:!0,delay:.08*(i+1)},()=>l("li",[l("h3",{class:"timeline-year"},o),l("ul",{class:"timeline-year-wrapper"},a.map(({date:s,info:c,path:u})=>l("li",{class:"timeline-item"},[l("span",{class:"timeline-date"},s),l(Re,{class:"timeline-title",to:u},()=>c[_e.title])])))])))))])}});const xu=B({name:"InfoList",setup(){const e=le(),t=Tr(),n=yr(),r=L(()=>dt(n.value.map).length),o=Wa(),a=Lr(),i=L(()=>dt(a.value.map).length),s=mr(),c=Y("article"),u=L(()=>e.value.blogLocales),d=[["article",Yr],["category",la],["tag",sa],["timeline",Ka]];return()=>l("div",{class:"vp-blog-infos"},[l("div",{class:"vp-blog-type-switcher"},d.map(([p,v])=>l("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},l("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},l(v))))),l(se,()=>c.value==="article"?l("div",{class:"vp-sticky-article-wrapper"},[l("div",{class:"title",onClick:()=>s(t.value.path)},[l(Yr),l("span",{class:"num"},t.value.items.length),u.value.article]),l("hr"),l("ul",{class:"vp-sticky-articles"},o.value.items.map(({info:p,path:v},h)=>l(se,{appear:!0,delay:.08*(h+1)},()=>l("li",{class:"vp-sticky-article"},l(Re,{to:v},()=>p[_e.title])))))]):c.value==="category"?l("div",{class:"vp-category-wrapper"},[r.value?l("div",{class:"title",onClick:()=>s(n.value.path)},[l(la),l("span",{class:"num"},r.value),u.value.category]):null,l("hr"),l(se,{delay:.04},()=>l(Xa))]):c.value==="tag"?l("div",{class:"vp-tag-wrapper"},[i.value?l("div",{class:"title",onClick:()=>s(a.value.path)},[l(sa),l("span",{class:"num"},i.value),u.value.tag]):null,l("hr"),l(se,{delay:.04},()=>l(Za))]):l(se,()=>l(t3)))])}});const _o=B({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:n}=gr();return()=>[l(Ga),l(Ua,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>l(Qa),...n.value?{sidebar:()=>l(xu)}:{}})]}});const Ar=()=>l("aside",{class:"vp-blog-info-wrapper"},[l(se,()=>l(Qa)),l(se,{delay:.04},()=>l(xu))]);Ar.displayName="InfoPanel";const n3=B({name:"BlogPage",components:{CategoryList:Xa,TagList:Za},setup(){const e=ie(),t=Ee(),n=yr(),r=Lr(),o=L(()=>t.value.blog||{}),a=L(()=>{const{key:s=""}=o.value;return s==="category"?"CategoryList":s==="tag"?"TagList":null}),i=L(()=>{const{name:s="",key:c=""}=o.value;return c==="category"?s?n.value.map[s].items:[]:c==="tag"?s?r.value.map[s].items:[]:[]});return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,()=>a.value?l(Ke(a.value)):null),o.value.name?l(se,{appear:!0,delay:.24},()=>l(Ya,{key:e.value.path,items:i.value})):null]),l(se,{delay:.16},()=>l(Ar,{key:"blog"}))])))}}),r3="/assets/hero-197a9d2d.jpg";const o3=B({name:"BlogHero",slots:Object,setup(e,{slots:t}){const n=Ee(),r=Rn(),o=ft(),a=L(()=>n.value.heroFullScreen??!1),i=L(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:v,tagline:h}=n.value;return{text:c??r.value.title??"Hello",image:u?Ie(u):null,imageDark:d?Ie(d):null,heroStyle:v,alt:p||c||"hero image",tagline:h??"",isFullScreen:a.value}}),s=L(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=n.value;return{image:pe(c)?Ie(c):c===!1?null:r3,imageDark:pe(u)?Ie(u):null,bgStyle:d,isFullScreen:a.value}});return()=>{var c,u;return n.value.hero===!1?null:l("div",{ref:o,class:["vp-blog-hero",{fullscreen:a.value,"no-bg":!s.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,s.value))||[s.value.image?l("div",{class:["vp-blog-mask",{light:s.value.imageDark}],style:[{background:`url(${s.value.image}) center/cover no-repeat`},s.value.bgStyle]}):null,s.value.imageDark?l("div",{class:"vp-blog-mask dark",style:[{background:`url(${s.value.imageDark}) center/cover no-repeat`},s.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,i.value))||[l(se,{appear:!0,type:"group",delay:.04},()=>[i.value.image?l("img",{key:"light",class:["vp-blog-hero-image",{light:i.value.imageDark}],style:i.value.heroStyle,src:i.value.image,alt:i.value.alt}):null,i.value.imageDark?l("img",{key:"dark",class:"vp-blog-hero-image dark",style:i.value.heroStyle,src:i.value.imageDark,alt:i.value.alt}):null]),l(se,{appear:!0,delay:.08},()=>i.value.text?l("h1",{class:"vp-blog-hero-title"},i.value.text):null),l(se,{appear:!0,delay:.12},()=>i.value.tagline?l("p",{class:"vp-blog-hero-description",innerHTML:i.value.tagline}):null)],i.value.isFullScreen?l("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:o.value.clientHeight,behavior:"smooth"})}},[l(ca),l(ca)]):null])}}});const Ct=yc("bing-image",{index:0,data:[]}),a3=B({name:"BingHeroBackground",setup(){const e=co(),t=ft(),n=Y(!1),r=L(()=>{const s=Ct.value.data[Ct.value.index],c=e.value.toLowerCase().split("-").shift();if(s){const{url:u,wallpaper:d,downloadable:p,locales:v}=s;return{url:u,wallpaper:d,downloadable:p,...v[c]??v.en}}return null}),o=()=>fetch("https://bing-wallpaper.vuejs.press/api/wallpaper").then(s=>s.json()),a=()=>{Ct.value.index--},i=()=>{Ct.value.index++};return fp(t,()=>{n.value=!1}),ye(()=>{o().then(s=>{Ct.value.data=s})}),()=>{const{title:s,headline:c,url:u,backstage:d,quickFact:p,copyright:v}=r.value||{};return l(pr,()=>u?[l("div",{class:"vp-blog-mask",style:{background:`url(${u}) center/cover no-repeat`}}),l("div",{class:"bing-switch",onClick:()=>{n.value=!0}},[l(wt,{name:"fade"},()=>n.value?l("div",{class:"bing-info",ref:t},[l("a",{href:d,target:"_blank",class:"bing-info-header"},c),l("hr"),l("div",{class:"bing-info-body"},p),l("div",{class:"bing-info-copyright"},v)]):null),l("div",{class:"bing-location"},[l("span",{class:"bing-location-icon"}),s]),l("button",{class:"bing-switch-prev",disabled:Ct.value.index===0,onClick:()=>a()}),l("button",{class:"bing-switch-next",disabled:Ct.value.index===Ct.value.data.length-1,onClick:()=>i()})])]:null)}}});const i3=B({name:"HitokotoBlogHero",props:{text:{type:String,required:!0},image:{type:String,default:null},imageDark:{type:String,default:null},alt:{type:String,required:!0},heroStyle:{type:[String,Object],default:null}},setup(e){const t=Y(""),n=Y(""),r=Y("");let o=!1;const a=()=>fetch("https://v1.hitokoto.cn").then(i=>i.json()).then(({from:i,hitokoto:s})=>{t.value=s,r.value=i});return ye(()=>{o=!0,ve(t,()=>{n.value="";let i=0;const s=()=>(n.value+=t.value[i],i++,rn().then(()=>{i{s()},150):o&&setTimeout(()=>{a()},3e3)}));s()}),a()}),ur(()=>{o=!1}),()=>[l(se,{appear:!0,type:"group",delay:.04},()=>[e.image?l("img",{key:"light",class:["vp-blog-hero-image",{light:e.imageDark}],style:e.heroStyle,src:e.image,alt:e.alt}):null,e.imageDark?l("img",{key:"dark",class:"vp-blog-hero-image dark",style:e.heroStyle,src:e.imageDark,alt:e.alt}):null]),l(se,{appear:!0,delay:.08},()=>e.text?l("h1",{class:"vp-blog-hero-title"},e.text):null),l("div",{class:"hitokoto"},[l("p",{class:"hitokoto-text"},l("span",n.value)),l("p",{class:"hitokoto-author",style:{opacity:n.value.length>4?1:0}},`——「${r.value}」`)])]}}),l3=B({__name:"BlogHero",setup(e){return(t,n)=>(ms(),_s(_t(o3),null,{heroBg:zo(()=>[we(_t(a3))]),heroInfo:zo(r=>[we(_t(i3),Yu(Es(r)),null,16)]),_:1}))}}),s3=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},c3=s3(l3,[["__file","BlogHero.vue"]]);const u3=["link","article","book","project","friend"],d3=B({name:"ProjectPanel",components:{ArticleIcon:Yr,BookIcon:ku,FriendIcon:Ru,LinkIcon:Pu,ProjectIcon:Ou},setup(){const e=Ee(),t=xn(),n=mr(),r=(o="",a="icon")=>u3.includes(o)?l(Ke(`${o}-icon`)):on(o)?l("img",{class:"vp-project-image",src:o,alt:a}):vo(o)?l("img",{class:"vp-project-image",src:Ie(o),alt:a}):l($e,{icon:o});return()=>{var o;return(o=e.value.projects)!=null&&o.length?l("div",{class:"vp-project-panel"},e.value.projects.map(({icon:a,link:i,name:s,desc:c},u)=>l("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>n(i)},[r(a,s),l("div",{class:"vp-project-name"},s),l("div",{class:"vp-project-desc"},c)]))):null}}});const f3=B({name:"BlogHome",setup(){const e=Tr();return()=>l("div",{class:"vp-page vp-blog"},[l(c3),l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,{appear:!0,delay:.16},()=>l(d3)),l(se,{appear:!0,delay:.24},()=>l(Ya,{items:e.value.items}))]),l(se,{appear:!0,delay:.16},()=>l(Ar,{key:"blog"}))]),l(se,{appear:!0,delay:.28},()=>l(br))])}}),p3=B({name:"BlogHome",setup(){return()=>l(_o,()=>l(f3))}});const Cu=B({name:"ArticleType",setup(){const e=ie(),t=bt(),n=le(),r=Tr(),o=Wa(),a=L(()=>{const i=n.value.blogLocales;return[{text:i.all,path:r.value.path},{text:i.star,path:o.value.path},...[].map(({key:s,path:c})=>({text:i[s],path:c.replace(/^\//,t.value)}))]});return()=>l("ul",{class:"vp-article-type-wrapper"},a.value.map(i=>l("li",{class:["vp-article-type",{active:i.path===e.value.path}]},l(Re,{to:i.path},()=>i.text))))}}),v3=B({name:"BlogPage",setup(){const e=go(),t=Ee(),n=ie(),r=Tr(),o=Wa(),a=L(()=>{const{key:i="",type:s}=t.value.blog||{};return i==="star"?o.value.items:s==="type"&&i?e.value.items:r.value.items});return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,()=>l(Cu)),l(se,{appear:!0,delay:.24},()=>l(Ya,{key:n.value.path,items:a.value}))]),l(se,{delay:.16},()=>l(Ar,{key:"blog"}))])))}});const h3=B({name:"TimelineItems",setup(){const e=Er(),t=le(),n=Ja(),r=L(()=>e.value.timeline||t.value.blogLocales.timelineTitle),o=L(()=>n.value.config.map(({year:a})=>({title:a.toString(),level:2,slug:a.toString(),children:[]})));return()=>l("div",{class:"timeline-wrapper"},l("ul",{class:"timeline-content"},[l(se,()=>l("li",{class:"motto"},r.value)),l(_u,{items:o.value}),n.value.config.map(({year:a,items:i},s)=>l(se,{appear:!0,delay:.08*(s+1),type:"group"},()=>[l("h3",{key:"title",id:a,class:"timeline-year-title"},l("span",a)),l("li",{key:"content",class:"timeline-year-list"},[l("ul",{class:"timeline-year-wrapper"},i.map(({date:c,info:u,path:d})=>l("li",{class:"timeline-item"},[l("span",{class:"timeline-date"},c),l(Re,{class:"timeline-title",to:d},()=>u[_e.title])])))])]))]))}}),m3=B({name:"Timeline",components:{ArticleType:Cu,CategoryList:Xa,TagList:Za},setup(){return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,{appear:!0,delay:.24},()=>l(h3))]),l(se,{delay:.16},()=>l(Ar,{key:"blog"}))])))}});$p($e);const g3=Ye({enhance:({app:e,router:t})=>{const{scrollBehavior:n}=t.options;t.options.scrollBehavior=async(...r)=>(await Hc().wait(),n(...r)),j2(e),e.component("HopeIcon",$e),e.component("VPLink",Re),e.component("BloggerInfo",Qa)},setup:()=>{z2(),W2(),Yv()},layouts:{Layout:Vv,NotFound:Bv,BlogCategory:n3,BlogHome:p3,BlogType:v3,Timeline:m3}}),_3=Ye({setup(){ye(()=>{console.log(String.raw` +${T}`))}}}};ye(()=>{const p=document.querySelector("#app");Se(p,"copy",d),Aa(()=>{p.style.userSelect=i.value?"none":"auto"})})};var m2=Ye({setup:()=>{h2()}});const xc=({title:e,desc:t="",logo:n="",color:r="",link:o=""})=>{const a=[l("img",{class:"vp-card-logo",src:Ie(n)}),l("div",{class:"vp-card-content"},[l("div",{class:"vp-card-title",innerHTML:e}),l("hr"),l("div",{class:"vp-card-desc",innerHTML:t})])],i={class:"vp-card"};return r&&(i.style={background:r}),er(o)?l("a",{href:o,target:"_blank",...i},a):l(Re,{to:o,...i},()=>a)};xc.displayName="VPCard";const g2='';var _2=B({name:"Playground",props:{title:{type:String,default:""},link:{type:String,required:!0}},setup(e){return()=>[l("div",{class:"vp-playground"},[l("div",{class:"vp-playground-header"},[e.title?l("div",{class:"vp-playground-title"},decodeURIComponent(e.title)):null,l("div",{class:"vp-playground-actions"},[l("a",{class:"vp-playground-action",href:decodeURIComponent(e.link),target:"_blank",innerHTML:g2})])]),l("div",{class:"vp-playground-container"},l("iframe",{src:decodeURIComponent(e.link)}))])]}});const b2=Ye({enhance:({app:e})=>{e.component("VPCard",xc),e.component("Playground",_2)},setup:()=>{}});let y2={};const Cc=Symbol(""),E2=()=>he(Cc),L2=e=>{e.provide(Cc,y2)};const A2=".theme-hope-content :not(a) > img:not([no-view])",T2={"/en/":{closeTitle:"Close",downloadTitle:"Download Image",fullscreenTitle:"Switch to full screen",zoomTitle:"Zoom in/out",arrowPrevTitle:"Prev (Arrow Left)",arrowNextTitle:"Next (Arrow Right)"},"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},I2=800,w2='
    ',k2=e=>pe(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Vc=e=>new Promise((t,n)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Vc(e)),e.onerror=r=>n(r))}),P2=()=>{const{isSupported:e,toggle:t}=$a(),n=E2(),r=Sn(T2),o=ie();let a;const i=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let v=-1;for(let h=0;h{d.goTo(p.indexOf(T.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{v>=0&&p[v].classList.remove("active"),p[d.currIndex].classList.add("active"),v=d.currIndex})}})})},s=()=>Promise.all([f(()=>import("./photoswipe.esm-060dc2da.js"),[]),rn().then(()=>new Promise(c=>setTimeout(c,I2)).then(()=>k2(A2)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:w2,element:p,msrc:p.src}));u.forEach((p,v)=>{const h=()=>{a=new c({preloaderDelay:0,showHideAnimationType:"zoom",...r.value,...n,dataSource:d,index:v,closeOnVerticalDrag:!0,wheelToZoom:!1}),i(a),a.addFilter("thumbEl",()=>p),a.addFilter("placeholderSrc",()=>p.src),a.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{h()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&h()})}),u.forEach((p,v)=>{Vc(p).then(h=>{d.splice(v,1,h),a==null||a.refreshSlideContent(v)})})});ye(()=>{Se("wheel",()=>{a==null||a.close()}),ve(()=>o.value.path,s,{immediate:!0})})};var O2=Ye({enhance:({app:e})=>{L2(e)},setup:()=>{P2()}});const $e=e=>{const{icon:t="",color:n,size:r}=e,o={};return n&&(o.color=n),r&&(o.height=Number.isNaN(Number(r))?r:`${r}px`),on(t)?l("img",{class:"icon",src:t,"no-view":"",style:o}):vo(t)?l("img",{class:"icon",src:Ie(t),"no-view":"",style:o}):l(Ke("FontIcon"),e)};$e.displayName="HopeIcon";const R2=(e,t)=>{const n=e.replace(t,"/").split("/"),r=[];let o=Da(t);return n.forEach((a,i)=>{i!==n.length-1?(o+=`${a}/`,r.push({link:o,name:a||"Home"})):a!==""&&(o+=a,r.push({link:o,name:a}))}),r};var _e;(function(e){e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O"})(_e||(_e={}));var ta;(function(e){e.article="a",e.home="h",e.slide="s",e.page="p"})(ta||(ta={}));const _n=(e,t,n=!1)=>{let r=In(e,oc(encodeURI(t)));r.name==="404"&&(r=In(e,t));const{fullPath:o,meta:a,name:i}=r;return{text:!n&&a[_e.shortTitle]?a[_e.shortTitle]:a[_e.title]||t,link:i==="404"?t:o,...a[_e.icon]?{icon:a[_e.icon]}:{}}},mr=()=>{const e=Ne(),t=pt();return n=>{if(n)if(vo(n))t.path!==n&&e.push(n);else if(on(n)||Ss(n))window&&window.open(n);else{const r=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${r}/${encodeURI(n)}`)}}},Mc=()=>{const e=ie();return L(()=>e.value.readingTime??null)},na=typeof{"/en/":{word:"About $word words",less1Minute:"Less than 1 minute",time:"About $time min"},"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/en/":{word:"About $word words",less1Minute:"Less than 1 minute",time:"About $time min"},"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Bc=(e,t)=>{const{minutes:n,words:r}=e,{less1Minute:o,word:a,time:i}=t;return{time:n<1?o:i.replace("$time",Math.round(n).toString()),words:a.replace("$word",r.toString())}},_l={words:"",time:""},$c=()=>na?Sn(na):L(()=>null),D2=()=>{if(typeof na>"u")return L(()=>_l);const e=Mc(),t=$c();return L(()=>e.value&&t.value?Bc(e.value,t.value):_l)},Gt=()=>Oc(),le=()=>Xp(),xn=()=>L(()=>!!Gt().value.pure),Nc=()=>{const e=le(),t=Ee();return L(()=>{const{author:n}=t.value;return n?or(n):n===!1?[]:or(e.value.author,!1)})},S2=()=>{const e=Ee();return L(()=>ic(e.value.category).map(t=>{var n,r;return{name:t,path:((r=(n=he(Symbol.for("categoryMap")))==null?void 0:n.value.map[t])==null?void 0:r.path)||""}}))},x2=()=>{const e=Ee();return L(()=>lc(e.value.tag).map(t=>{var n,r;return{name:t,path:((r=(n=he(Symbol.for("tagMap")))==null?void 0:n.value.map[t])==null?void 0:r.path)||""}}))},C2=()=>{const e=Ee(),t=ie();return L(()=>{const n=Va(e.value.date);if(n)return n;const{createdTime:r}=t.value.git||{};return r?new Date(r):null})},V2=()=>{const e=le(),t=ie(),n=Ee(),r=Nc(),o=S2(),a=x2(),i=C2(),s=Mc(),c=D2(),u=L(()=>({author:r.value,category:o.value,date:i.value,localizedDate:t.value.localizedDate,tag:a.value,isOriginal:n.value.isOriginal||!1,readingTime:s.value,readingTimeLocale:c.value,pageview:"pageview"in n.value?n.value.pageview:!0})),d=L(()=>"pageInfo"in n.value?n.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}};let Mo=null,Bn=null;const M2={wait:()=>Mo,pending:()=>{Mo=new Promise(e=>Bn=e)},resolve:()=>{Bn==null||Bn(),Mo=null,Bn=null}},Hc=()=>M2,B2="719px",$2="1440px",N2="false",Ha={mobileBreakPoint:B2,pcBreakPoint:$2,enableThemeColor:N2},{mobileBreakPoint:H2,pcBreakPoint:F2}=Ha,bl=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,gr=()=>{const e=Y(!1),t=Y(!1),n=()=>{e.value=window.innerWidth<=(bl(H2)??719),t.value=window.innerWidth>=(bl(F2)??1440)};return ye(()=>{n(),Se("resize",n,!1),Se("orientationchange",n,!1)}),{isMobile:e,isPC:t}},Fc=Symbol(""),_r=()=>{const e=he(Fc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},j2=e=>{const t=Gt(),n=yp(),r=yc("vuepress-theme-hope-scheme","auto"),o=L(()=>t.value.darkmode||"switch"),a=L(()=>{const s=o.value;return s==="disable"?!1:s==="enable"?!0:s==="auto"?n.value:s==="toggle"?r.value==="dark":r.value==="dark"||r.value==="auto"&&n.value}),i=L(()=>{const s=o.value;return s==="switch"||s==="toggle"});e.provide(Fc,{canToggle:i,config:o,isDarkmode:a,status:r}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>a.value}})},z2=()=>{const{isDarkmode:e}=_r(),t=(n=e.value)=>document.documentElement.setAttribute("data-theme",n?"dark":"light");ye(()=>{ve(e,t,{immediate:!0})})},ze=B({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:n,slots:r}){const o=pt(),a=Hs(),i=Pn(e,"config"),s=L(()=>on(i.value.link)),c=L(()=>Ss(i.value.link)||Td(i.value.link)),u=L(()=>c.value?void 0:i.value.target||(s.value?"_blank":void 0)),d=L(()=>u.value==="_blank"),p=L(()=>!s.value&&!c.value&&!d.value),v=L(()=>c.value?void 0:i.value.rel||(d.value?"noopener noreferrer":void 0)),h=L(()=>i.value.ariaLabel||i.value.text),b=L(()=>{if(e.exact)return!1;const I=dt(a.value.locales);return I.length?I.every(E=>E!==i.value.link):i.value.link!=="/"}),T=L(()=>p.value?i.value.activeMatch?new RegExp(i.value.activeMatch).test(o.path):b.value?rr(o.path,i.value.link):o.path===i.value.link:!1);return()=>{const{before:I,after:E,default:k}=r,{text:y,icon:P,link:$}=i.value;return p.value?l(Re,{to:$,"aria-label":h.value,...t,class:["nav-link",{active:T.value},t.class],onFocusout:()=>n("focusout")},()=>k?k():[I?I():l($e,{icon:P}),y,E==null?void 0:E()]):l("a",{href:$,rel:v.value,target:u.value,"aria-label":h.value,...t,class:["nav-link",t.class],onFocusout:()=>n("focusout")},k?k():[I?I():l($e,{icon:P}),y,e.noExternalLinkIcon?null:l(kc),E==null?void 0:E()])}}}),wn=(e,t,n=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ba(e,t.link)?!0:t.children&&!n?t.children.some(r=>wn(e,r)):!1,jc=(e,t)=>t.type==="group"?t.children.some(n=>n.type==="group"?jc(e,n):n.type==="page"&&wn(e,n,!0))||"prefix"in t&&Ba(e,t.prefix):!1,zc=(e,t)=>pe(e.link)?l(ze,{...t,config:e}):l("p",t,[l($e,{icon:e.icon}),e.text]),qc=e=>{const t=pt();return e?l("ul",{class:"vp-sidebar-sub-headers"},e.map(n=>{const r=wn(t,n,!0);return l("li",{class:"vp-sidebar-sub-header"},[zc(n,{class:["vp-sidebar-link","vp-heading",{active:r}]}),qc(n.children)])})):null},Fa={"/en/note/structure/":[],"/en/note/algorithm/":[],"/en/note/java/":[],"/en/note/mybatis/":[],"/en/note/mysql/base/":[],"/en/note/mysql/further/":[],"/en/note/nacos/":[],"/en/note/redis/":[],"/en/note/spring/base/":[],"/en/note/spring/further/":[],"/en/note/spring/sourcecode/":[],"/en/note/springboot/":[],"/en/note/springmvc/":[],"/en/note/other/":[],"/en/tutorial/":[{text:"Docker",prefix:"docker/",collapsible:!0,children:["2305081011","2305081101","2305081110"]},{text:"Linux",prefix:"linux/",collapsible:!0,children:["2305081106"]},{text:"Openai",prefix:"openai/",collapsible:!0,children:["2305081112"]},{text:"Util",prefix:"util/",collapsible:!0,children:["2305090946"]}],"/en/books/":[],"/en/informal-essay/":[],"/note/java/jvm/":["2305052159","2305111000","2311091006","2311081011","2311071055","2311221518","2403021055","2403031236","2403031505","2403032005","2311081109"],"/note/java/concurrency/":[{text:"Atomic",prefix:"atomic/",collapsible:!0,children:["2305252031","2305281438","2305281702","2305312110"]},{text:"Lock",prefix:"lock/",collapsible:!0,children:["2305312243","2306011640","2306021120","2306021924"]},{text:"Queue",prefix:"queue/",collapsible:!0,children:["2306041045","2306042021","2306051101","2306052321"]},{text:"线程池",prefix:"线程池/",collapsible:!0,children:["2312130940","2312141621"]}],"/note/db/mongodb/":[],"/note/db/mysql/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["1910011300","1910011301","1910011302","1910011303","1910011304"]},{text:"Further",prefix:"further/",collapsible:!0,children:["2305012001","2305020500","2305032100","2305020501","2403061142","2403081801"]}],"/note/db/redis/":["2304010120","2305091732","2305091736","2305091734","2305091740","2305091744","2312220952","2312280935","2312291000","2312291432"],"/note/microservices/dubbo/":["2305140023","2305140203","2305140206","2305172157","2305191622","2305232045"],"/note/microservices/elasticsearch/":[],"/note/microservices/gateway/":[],"/note/microservices/kafka/":[],"/note/microservices/nacos/":[],"/note/microservices/rabbitmq/":[],"/note/microservices/rocketmq/":[],"/note/microservices/seata/":[],"/note/microservices/security/":[],"/note/microservices/sentinel/":[],"/note/microservices/skywalking/":[],"/note/microservices/zookeeper/":["2305110857"],"/note/framework/spring/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2101202010","2101202011","2101202012"]},{text:"Sourcecode",prefix:"sourcecode/",collapsible:!0,children:["2304021002"]}],"/note/framework/springboot/":[{text:"Plugin",prefix:"plugin/",collapsible:!0,children:["2306210953","2306211023"]}],"/note/framework/springmvc/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2102202010","2102202011","2102202012"]}],"/note/structure/":["2305090929","2301101200"],"/note/algorithm/":["2301101203","2301101204"],"/note/net/":[{text:"Basic",prefix:"basic/",collapsible:!0,children:["2401171030"]}],"/note/other/":["2305081011","2305081101","2305081110","2302201400","2302201401","2302201404","2305081106","2305081112","2305090946","2307051931","2307062006"],"/informal-essay/":[]},Bo=(e="",t="")=>vo(t)?t:`${Ed(e)}${t}`,q2=(e,t)=>{const n=ie();return{type:"heading",text:e.title,link:`${n.value.path}#${e.slug}`,children:ja(e.children,t)}},ja=(e,t)=>t>0?e.map(n=>q2(n,t-1)):[],Uc=e=>{const t=ie();return ja(t.value.headers,e)},ra=(e,t,n="")=>{const r=Ne(),o=ie(),a=(i,s=n)=>{var u;const c=pe(i)?_n(r,Bo(s,i)):i.link?{...i,...er(i.link)?{}:{link:_n(r,Bo(s,i.link)).link}}:i;if("children"in c){const d=Bo(s,c.prefix),p=c.children==="structure"?Fa[d]:c.children;return{type:"group",...c,prefix:d,children:p.map(v=>a(v,d))}}return{type:"page",...c,children:c.link===o.value.path?ja(((u=o.value.headers[0])==null?void 0:u.level)===1?o.value.headers[0].children:o.value.headers,t):[]}};return e.map(i=>a(i))},U2=(e,t)=>{const n=ie(),r=dt(e).sort((o,a)=>a.length-o.length);for(const o of r)if(rr(decodeURI(n.value.path),o)){const a=e[o];return a?ra(a==="structure"?Fa[o]:a==="heading"?Uc(t):a,t,o):[]}return console.warn(`${n.value.path} is missing sidebar config.`),[]},G2=(e,t)=>{const n=bt();return e===!1?[]:e==="heading"?Uc(t):e==="structure"?ra(Fa[n.value],t,n.value):ee(e)?ra(e,t):so(e)?U2(e,t):[]},Gc=Symbol(""),W2=()=>{const e=Ee(),t=le(),n=ie(),r=L(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),o=L(()=>e.value.headerDepth??t.value.headerDepth??2),a=ho(()=>[r.value,o.value,n.value.path,null],()=>G2(r.value,o.value));ct(Gc,a)},za=()=>{const e=he(Gc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};const J2=B({name:"PageFooter",setup(){const e=Ee(),t=le(),n=Nc(),r=L(()=>{const{copyright:i,footer:s}=e.value;return s!==!1&&!!(i||s||t.value.displayFooter)}),o=L(()=>{const{footer:i}=e.value;return i===!1?!1:pe(i)?i:t.value.footer||""}),a=L(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:n.value.length?`Copyright © ${new Date().getFullYear()} ${n.value[0].name}`:!1);return()=>r.value?l("footer",{class:"vp-footer-wrapper"},[o.value?l("div",{class:"vp-footer",innerHTML:o.value}):null,a.value?l("div",{class:"vp-copyright",innerHTML:a.value}):null]):null}});var yl=B({name:"EmptyComponent",setup:()=>()=>null});const Wc=B({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const n=ie(),r=Pn(e,"config"),o=L(()=>r.value.ariaLabel||r.value.text),a=Y(!1);ve(()=>n.value.path,()=>{a.value=!1});const i=s=>{s.detail===0&&(a.value=!a.value)};return()=>{var s;return l("div",{class:["dropdown-wrapper",{open:a.value}]},[l("button",{type:"button",class:"dropdown-title","aria-label":o.value,onClick:i},[((s=t.title)==null?void 0:s.call(t))||l("span",{class:"title"},[l($e,{icon:r.value.icon}),e.config.text]),l("span",{class:"arrow"}),l("ul",{class:"nav-dropdown"},r.value.children.map((c,u)=>{const d=u===r.value.children.length-1;return l("li",{class:"dropdown-item"},"children"in c?[l("h4",{class:"dropdown-subtitle"},c.link?l(ze,{config:c,onFocusout:()=>{c.children.length===0&&d&&(a.value=!1)}}):l("span",c.text)),l("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,v)=>l("li",{class:"dropdown-subitem"},l(ze,{config:p,onFocusout:()=>{v===c.children.length-1&&d&&(a.value=!1)}}))))]:l(ze,{config:c,onFocusout:()=>{d&&(a.value=!1)}}))}))])])}}}),Jc=()=>l(ue,{name:"i18n"},()=>[l("path",{d:"M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z"})]);Jc.displayName="I18nIcon";const Qc=(e,t,n="")=>pe(t)?_n(e,`${n}${t}`):"children"in t?{...t,...t.link&&!er(t.link)?_n(e,`${n}${t.link}`):{},children:t.children.map(r=>Qc(e,r,`${n}${t.prefix||""}`))}:{...t,link:er(t.link)?t.link:_n(e,`${n}${t.link}`).link},Kc=()=>{const e=le(),t=Ne(),n=()=>(e.value.navbar||[]).map(o=>Qc(t,o));return ho(()=>e.value.navbar,()=>n())},Q2=()=>{const e=Ne(),t=pt(),n=bt(),r=Rn(),o=Gt(),a=le();return ho(()=>[t.path,r.value.locales,o.value.extraLocales,a.value.navbarLocales],()=>{const i=dt(r.value.locales),s=Dn(o.value.extraLocales??{});if(i.length<2&&!s.length)return null;const{path:c,fullPath:u}=t,{navbarLocales:d}=a.value;return{text:"",ariaLabel:d==null?void 0:d.selectLangAriaLabel,children:[...i.map(v=>{var k,y,P;const h=((k=r.value.locales)==null?void 0:k[v])??{},b=((y=o.value.locales)==null?void 0:y[v])??{},T=h.lang||"",I=((P=b.navbarLocales)==null?void 0:P.langName)??T;let E;if(T===r.value.lang)E=c;else{const $=c.replace(n.value,v);E=e.getRoutes().some(w=>w.path===$)?u.replace(c,$):b.home??v}return{text:I,link:E}}),...s.map(([v,h])=>({text:v,link:h.replace(":route",t.path.replace(n.value,""))}))]}})},K2=()=>{const e=le(),t=L(()=>e.value.repo||null),n=L(()=>t.value?Kf(t.value):null),r=L(()=>t.value?cc(t.value):null),o=L(()=>n.value?e.value.repoLabel??(r.value===null?"Source":r.value):null);return L(()=>!n.value||!o.value||e.value.repoDisplay===!1?null:{type:r.value||"Source",label:o.value,link:n.value})},Y2=B({name:"LanguageDropdown",setup(){const e=Q2();return()=>e.value?l("div",{class:"nav-item"},l(Wc,{class:"i18n-dropdown",config:e.value},{title:()=>{var t;return l(Jc,{"aria-label":(t=e.value)==null?void 0:t.ariaLabel,style:{width:"1rem",height:"1rem",verticalAlign:"middle"}})}})):null}});const X2=B({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ie(),n=Pn(e,"config"),r=L(()=>n.value.ariaLabel||n.value.text),o=Y(!1);ve(()=>t.value.path,()=>{o.value=!1});const a=(i,s)=>s[s.length-1]===i;return()=>[l("button",{type:"button",class:["nav-screen-dropdown-title",{active:o.value}],"aria-label":r.value,onClick:()=>{o.value=!o.value}},[l("span",{class:"title"},[l($e,{icon:n.value.icon}),e.config.text]),l("span",{class:["arrow",o.value?"down":"end"]})]),l("ul",{class:["nav-screen-dropdown",{hide:!o.value}]},n.value.children.map(i=>l("li",{class:"dropdown-item"},"children"in i?[l("h4",{class:"dropdown-subtitle"},i.link?l(ze,{config:i,onFocusout:()=>{a(i,n.value.children)&&i.children.length===0&&(o.value=!1)}}):l("span",i.text)),l("ul",{class:"dropdown-subitem-wrapper"},i.children.map(s=>l("li",{class:"dropdown-subitem"},l(ze,{config:s,onFocusout:()=>{a(s,i.children)&&a(i,n.value.children)&&(o.value=!1)}}))))]:l(ze,{config:i,onFocusout:()=>{a(i,n.value.children)&&(o.value=!1)}}))))]}});const Z2=B({name:"NavScreenLinks",setup(){const e=Kc();return()=>e.value.length?l("nav",{class:"nav-screen-links"},e.value.map(t=>l("div",{class:"navbar-links-item"},"children"in t?l(X2,{config:t}):l(ze,{config:t})))):null}}),Yc=()=>l(ue,{name:"dark"},()=>l("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Yc.displayName="DarkIcon";const Xc=()=>l(ue,{name:"light"},()=>l("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Xc.displayName="LightIcon";const Zc=()=>l(ue,{name:"auto"},()=>l("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Zc.displayName="AutoIcon";const eu=()=>l(ue,{name:"enter-fullscreen"},()=>l("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));eu.displayName="EnterFullScreenIcon";const tu=()=>l(ue,{name:"cancel-fullscreen"},()=>l("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));tu.displayName="CancelFullScreenIcon";const nu=()=>l(ue,{name:"outlook"},()=>[l("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);nu.displayName="OutlookIcon";const ru=B({name:"AppearanceSwitch",setup(){const{config:e,status:t}=_r(),n=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>l("button",{type:"button",id:"appearance-switch",onClick:()=>n()},[l(Zc,{style:{display:t.value==="auto"?"block":"none"}}),l(Yc,{style:{display:t.value==="dark"?"block":"none"}}),l(Xc,{style:{display:t.value==="light"?"block":"none"}})])}}),ev=B({name:"AppearanceMode",setup(){const e=le(),{canToggle:t}=_r(),n=L(()=>e.value.outlookLocales.darkmode);return()=>t.value?l("div",{class:"appearance-wrapper"},[l("label",{class:"appearance-title",for:"appearance-switch"},n.value),l(ru)]):null}});const $o="VUEPRESS_THEME_COLOR",tv=B({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(n="")=>{const r=document.documentElement.classList,o=dt(e.themeColor);if(!n){localStorage.removeItem($o),r.remove(...o);return}r.remove(...o.filter(a=>a!==n)),r.add(n),localStorage.setItem($o,n)};return ye(()=>{const n=localStorage.getItem($o);n&&t(n)}),()=>l("ul",{id:"theme-color-picker"},[l("li",l("span",{class:"theme-color",onClick:()=>t()})),Dn(e.themeColor).map(([n,r])=>l("li",l("span",{style:{background:r},onClick:()=>t(n)})))])}}),bn=Ha.enableThemeColor==="true",nv=bn?Gf(Dn(Ha).filter(([e])=>e.startsWith("theme-"))):{},rv=B({name:"ThemeColor",setup(){const e=le(),t=L(()=>e.value.outlookLocales.themeColor);return()=>bn?l("div",{class:"theme-color-wrapper"},[l("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),l(tv,{themeColor:nv})]):null}});const ou=B({name:"ToggleFullScreenButton",setup(){const e=le(),{isSupported:t,isFullscreen:n,toggle:r}=$a(),o=L(()=>e.value.outlookLocales.fullscreen);return()=>t?l("div",{class:"full-screen-wrapper"},[l("label",{class:"full-screen-title",for:"full-screen-switch"},o.value),l("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:n.value,onClick:()=>r()},n.value?l(tu):l(eu))]):null}}),au=B({name:"OutlookSettings",setup(){const e=Gt(),t=xn(),n=L(()=>!t.value&&e.value.fullscreen);return()=>l(pr,()=>[bn?l(rv):null,l(ev),n.value?l(ou):null])}});const ov=B({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:n}){const r=ie(),{isMobile:o}=gr(),a=ft(),i=Lc(a);return ye(()=>{a.value=document.body,ve(o,s=>{!s&&e.show&&(i.value=!1,t("close"))}),ve(()=>r.value.path,()=>{i.value=!1,t("close")})}),ur(()=>{i.value=!1}),()=>l(wt,{name:"fade",onEnter:()=>{i.value=!0},onAfterLeave:()=>{i.value=!1}},()=>{var s,c;return e.show?l("div",{id:"nav-screen"},l("div",{class:"vp-nav-screen-container"},[(s=n.before)==null?void 0:s.call(n),l(Z2),l("div",{class:"vp-outlook-wrapper"},l(au)),(c=n.after)==null?void 0:c.call(n)])):null})}});const av=B({name:"NavbarBrand",setup(){const e=bt(),t=Rn(),n=le(),r=L(()=>n.value.home||e.value),o=L(()=>t.value.title),a=L(()=>n.value.navTitle??o.value),i=L(()=>n.value.logo?Ie(n.value.logo):null),s=L(()=>n.value.logoDark?Ie(n.value.logoDark):null);return()=>l(Re,{to:r.value,class:"vp-brand"},()=>[i.value?l("img",{class:["vp-nav-logo",{light:!!s.value}],src:i.value,alt:o.value}):null,s.value?l("img",{class:["vp-nav-logo dark"],src:s.value,alt:o.value}):null,a.value?l("span",{class:["vp-site-name",{"hide-in-pad":i.value&&n.value.hideSiteNameOnMobile!==!1}]},a.value):null])}});const iv=B({name:"NavbarLinks",setup(){const e=Kc();return()=>e.value.length?l("nav",{class:"vp-nav-links"},e.value.map(t=>l("div",{class:"nav-item hide-in-mobile"},"children"in t?l(Wc,{config:t}):l(ze,{config:t})))):null}});const lv=B({name:"RepoLink",components:{BitbucketIcon:pc,GiteeIcon:fc,GitHubIcon:uc,GitLabIcon:dc,SourceIcon:vc},setup(){const e=K2();return()=>e.value?l("div",{class:"nav-item vp-repo"},l("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},l(Ke(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const iu=({active:e=!1},{emit:t})=>l("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},l("span",[l("span",{class:"vp-top"}),l("span",{class:"vp-middle"}),l("span",{class:"vp-bottom"})]));iu.displayName="ToggleNavbarButton";const qa=(e,{emit:t})=>l("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},l("span",{class:"icon"}));qa.displayName="ToggleSidebarButton";qa.emits=["toggle"];const sv=B({name:"OutlookButton",setup(){const{isSupported:e}=$a(),t=Gt(),n=xn(),r=ie(),{canToggle:o}=_r(),a=Y(!1),i=L(()=>!n.value&&t.value.fullscreen&&e);return ve(()=>r.value.path,()=>{a.value=!1}),()=>o.value||i.value||bn?l("div",{class:"nav-item hide-in-mobile"},o.value&&!i.value&&!bn?l(ru):i.value&&!o.value&&!bn?l(ou):l("button",{type:"button",class:["outlook-button",{open:a.value}],tabindex:"-1","aria-hidden":!0},[l(nu),l("div",{class:"outlook-dropdown"},l(au))])):null}});const cv=B({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:n}){const r=le(),{isMobile:o}=gr(),a=Y(!1),i=L(()=>{const{navbarAutoHide:d="mobile"}=r.value;return d!=="none"&&(d==="always"||o.value)}),s=L(()=>r.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:av,Language:Y2,Links:iv,Repo:lv,Outlook:sv,Search:It("Docsearch")?Ke("Docsearch"):It("SearchBox")?Ke("SearchBox"):yl},u=d=>c[d]??(It(d)?Ke(d):yl);return()=>{var d,p,v,h,b,T;return[l("header",{id:"navbar",class:["vp-navbar",{"auto-hide":i.value,"hide-icon":r.value.navbarIcon===!1}]},[l("div",{class:"vp-navbar-start"},[l(qa,{onToggle:()=>{a.value&&(a.value=!1),t("toggleSidebar")}}),(d=n.startBefore)==null?void 0:d.call(n),(s.value.start||[]).map(I=>l(u(I))),(p=n.startAfter)==null?void 0:p.call(n)]),l("div",{class:"vp-navbar-center"},[(v=n.centerBefore)==null?void 0:v.call(n),(s.value.center||[]).map(I=>l(u(I))),(h=n.centerAfter)==null?void 0:h.call(n)]),l("div",{class:"vp-navbar-end"},[(b=n.endBefore)==null?void 0:b.call(n),(s.value.end||[]).map(I=>l(u(I))),(T=n.endAfter)==null?void 0:T.call(n),l(iu,{active:a.value,onToggle:()=>{a.value=!a.value}})])]),l(ov,{show:a.value,onClose:()=>{a.value=!1}},{before:()=>{var I;return(I=n.screenTop)==null?void 0:I.call(n)},after:()=>{var I;return(I=n.screenBottom)==null?void 0:I.call(n)}})]}}});const uv=B({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=pt();return()=>[zc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:wn(t,e.config,!0)}],exact:!0}),qc(e.config.children)]}});const dv=B({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const n=pt(),r=L(()=>wn(n,e.config)),o=L(()=>wn(n,e.config,!0));return()=>{const{collapsible:a,children:i=[],icon:s,prefix:c,link:u,text:d}=e.config;return l("section",{class:"vp-sidebar-group"},[l(a?"button":"p",{class:["vp-sidebar-heading",{clickable:a||u,exact:o.value,active:r.value}],...a?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[l($e,{icon:s}),u?l(ze,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):l("span",{class:"vp-sidebar-title"},d),a?l("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!a?l(lu,{key:c,config:i}):null])}}});const lu=B({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=pt(),n=Y(-1),r=o=>{n.value=o===n.value?-1:o};return ve(()=>t.path,()=>{const o=e.config.findIndex(a=>jc(t,a));n.value=o},{immediate:!0,flush:"post"}),()=>l("ul",{class:"vp-sidebar-links"},e.config.map((o,a)=>l("li",o.type==="group"?l(dv,{config:o,open:a===n.value,onToggle:()=>r(a)}):l(uv,{config:o}))))}});const fv=B({name:"SideBar",slots:Object,setup(e,{slots:t}){const n=pt(),r=le(),o=za(),a=ft();return ye(()=>{ve(()=>n.hash,i=>{const s=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${n.path}${i}"]`);if(!s)return;const{top:c,height:u}=a.value.getBoundingClientRect(),{top:d,height:p}=s.getBoundingClientRect();dc+u&&s.scrollIntoView(!1)},{immediate:!0})}),()=>{var i,s,c;return l("aside",{ref:a,id:"sidebar",class:["vp-sidebar",{"hide-icon":r.value.sidebarIcon===!1}]},[(i=t.top)==null?void 0:i.call(t),((s=t.default)==null?void 0:s.call(t))||l(lu,{config:o.value}),(c=t.bottom)==null?void 0:c.call(t)])}}});const Ua=B({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const n=Ne(),r=ie(),o=Ee(),a=le(),{isMobile:i,isPC:s}=gr(),[c,u]=ll(!1),[d,p]=ll(!1),v=za(),h=Y(!1),b=L(()=>e.noNavbar||o.value.navbar===!1||a.value.navbar===!1?!1:!!(r.value.title||a.value.logo||a.value.repo||a.value.navbar)),T=L(()=>e.noSidebar?!1:o.value.sidebar!==!1&&v.value.length!==0&&!o.value.home),I=L(()=>e.noToc||o.value.home?!1:o.value.toc||a.value.toc!==!1&&o.value.toc!==!1),E={x:0,y:0},k=w=>{E.x=w.changedTouches[0].clientX,E.y=w.changedTouches[0].clientY},y=w=>{const U=w.changedTouches[0].clientX-E.x,H=w.changedTouches[0].clientY-E.y;Math.abs(U)>Math.abs(H)*1.5&&Math.abs(U)>40&&(U>0&&E.x<=80?u(!0):u(!1))},P=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let $=0;return Se("scroll",sp(()=>{const w=P();w<=58||w<$?h.value=!1:$+200{w||u(!1)}),ye(()=>{const w=Lc(document.body);ve(c,H=>{w.value=H});const U=n.afterEach(()=>{u(!1)});ur(()=>{w.value=!1,U()})}),()=>l(It("GlobalEncrypt")?Ke("GlobalEncrypt"):tc,()=>l("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!T.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":I.value,"hide-navbar":h.value,"sidebar-collapsed":!i.value&&!s.value&&d.value,"sidebar-open":i.value&&c.value},e.containerClass,o.value.containerClass||""],onTouchStart:k,onTouchEnd:y},[b.value?l(cv,{onToggleSidebar:()=>u()},{startBefore:()=>{var w;return(w=t.navbarStartBefore)==null?void 0:w.call(t)},startAfter:()=>{var w;return(w=t.navbarStartAfter)==null?void 0:w.call(t)},centerBefore:()=>{var w;return(w=t.navbarCenterBefore)==null?void 0:w.call(t)},centerAfter:()=>{var w;return(w=t.navbarCenterAfter)==null?void 0:w.call(t)},endBefore:()=>{var w;return(w=t.navbarEndBefore)==null?void 0:w.call(t)},endAfter:()=>{var w;return(w=t.navbarEndAfter)==null?void 0:w.call(t)},screenTop:()=>{var w;return(w=t.navScreenTop)==null?void 0:w.call(t)},screenBottom:()=>{var w;return(w=t.navScreenBottom)==null?void 0:w.call(t)}}):null,l(wt,{name:"fade"},()=>c.value?l("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),l(wt,{name:"fade"},()=>i.value?null:l("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},l("span",{class:["arrow",d.value?"end":"start"]}))),l(fv,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var w;return(w=t.sidebarTop)==null?void 0:w.call(t)},bottom:()=>{var w;return(w=t.sidebarBottom)==null?void 0:w.call(t)}}),t.default(),l(J2)]))}});const oa=(e,{slots:t})=>{var p,v;const{bgImage:n,bgImageDark:r,bgImageStyle:o,color:a,description:i,image:s,imageDark:c,header:u,features:d=[]}=e;return l("div",{class:"vp-feature-wrapper"},[n?l("div",{class:["vp-feature-bg",{light:r}],style:[{"background-image":`url(${n})`},o]}):null,r?l("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${r})`},o]}):null,l("div",{class:"vp-feature",style:a?{color:a}:{}},[((p=t.image)==null?void 0:p.call(t,e))||[s?l("img",{class:["vp-feature-image",{light:c}],src:Ie(s),alt:u}):null,c?l("img",{class:"vp-feature-image dark",src:Ie(c),alt:u}):null],((v=t.info)==null?void 0:v.call(t,e))||[u?l("h2",{class:"vp-feature-header"},u):null,i?l("p",{class:"vp-feature-description",innerHTML:i}):null],d.length?l("div",{class:"vp-features"},d.map(({icon:h,title:b,details:T,link:I})=>{const E=[l("h3",{class:"vp-feature-title"},[l($e,{icon:h}),l("span",{innerHTML:b})]),l("p",{class:"vp-feature-details",innerHTML:T})];return I?er(I)?l("a",{class:"vp-feature-item link",href:I,role:"navigation","aria-label":b,target:"_blank"},E):l(Re,{class:"vp-feature-item link",to:I,role:"navigation","aria-label":b},()=>E):l("div",{class:"vp-feature-item"},E)})):null])])};oa.displayName="FeaturePanel";const se=B({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const n=o=>{o.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,o.style.transform="translateY(-20px)",o.style.opacity="0"},r=o=>{o.style.transform="translateY(0)",o.style.opacity="1"};return()=>l(e.type==="single"?wt:ld,{name:"drop",appear:e.appear,onAppear:n,onAfterAppear:r,onEnter:n,onAfterEnter:r,onBeforeLeave:n},()=>t.default())}});const pv=B({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const n=Ee(),r=Rn(),o=L(()=>n.value.heroFullScreen??!1),a=L(()=>{const{heroText:u,tagline:d}=n.value;return{text:u??r.value.title??"Hello",tagline:d??r.value.description??"",isFullScreen:o.value}}),i=L(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:v,heroImageStyle:h}=n.value;return{image:d?Ie(d):null,imageDark:p?Ie(p):null,heroStyle:h,alt:v||u||"hero image",isFullScreen:o.value}}),s=L(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=n.value;return{image:kt(u)?Ie(u):null,imageDark:kt(d)?Ie(d):null,bgStyle:p,isFullScreen:o.value}}),c=L(()=>n.value.actions??[]);return()=>{var u,d,p;return l("header",{class:["vp-hero-info-wrapper",{fullscreen:o.value}]},[((u=t.heroBg)==null?void 0:u.call(t,s.value))||[s.value.image?l("div",{class:["vp-hero-mask",{light:s.value.imageDark}],style:[{"background-image":`url(${s.value.image})`},s.value.bgStyle]}):null,s.value.imageDark?l("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${s.value.imageDark})`},s.value.bgStyle]}):null],l("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,i.value))||l(se,{appear:!0,type:"group"},()=>[i.value.image?l("img",{key:"light",class:["vp-hero-image",{light:i.value.imageDark}],style:i.value.heroStyle,src:i.value.image,alt:i.value.alt}):null,i.value.imageDark?l("img",{key:"dark",class:"vp-hero-image dark",style:i.value.heroStyle,src:i.value.imageDark,alt:i.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,a.value))??l("div",{class:"vp-hero-infos"},[a.value.text?l(se,{appear:!0,delay:.04},()=>l("h1",{id:"main-title"},a.value.text)):null,a.value.tagline?l(se,{appear:!0,delay:.08},()=>l("p",{class:"vp-description",innerHTML:a.value.tagline})):null,c.value.length?l(se,{appear:!0,delay:.12},()=>l("p",{class:"vp-actions"},c.value.map(v=>l(ze,{class:["vp-action",v.type||"default"],config:v,noExternalLinkIcon:!0})))):null])])])}}});const su=(e,{slots:t})=>{var v,h,b;const{bgImage:n,bgImageDark:r,bgImageStyle:o,color:a,description:i,image:s,imageDark:c,header:u,highlights:d=[],type:p="un-order"}=e;return l("div",{class:"vp-highlight-wrapper",style:a?{color:a}:{}},[n?l("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${n})`},o]}):null,r?l("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,l("div",{class:"vp-highlight"},[((v=t.image)==null?void 0:v.call(t,e))||[s?l("img",{class:["vp-highlight-image",{light:c}],src:Ie(s),alt:u}):null,c?l("img",{class:"vp-highlight-image dark",src:Ie(c),alt:u}):null],((h=t.info)==null?void 0:h.call(t,e))||[l("div",{class:"vp-highlight-info-wrapper"},l("div",{class:"vp-highlight-info"},[u?l("h2",{class:"vp-highlight-header",innerHTML:u}):null,i?l("p",{class:"vp-highlight-description",innerHTML:i}):null,((b=t.highlights)==null?void 0:b.call(t,d))||l(p==="order"?"ol":p==="no-order"?"dl":"ul",{class:"vp-highlights"},d.map(({icon:T,title:I,details:E,link:k})=>{const y=[l(p==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[T?l($e,{class:"vp-highlight-icon",icon:T}):null,l("span",{innerHTML:I})]),E?l(p==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:E}):null];return l(p==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:k}]},k?zf(k)?l("a",{class:"vp-highlight-item link",href:k,role:"navigation","aria-label":I,target:"_blank"},y):l(Re,{class:"vp-highlight-item link",to:k,role:"navigation","aria-label":I},()=>y):l("div",{class:"vp-highlight-item"},y))}))]))]])])};su.displayName="HighlightPanel";const br=({custom:e})=>l(js,{class:["theme-hope-content",{custom:e}]});br.displayName="MarkdownContent";br.props={custom:Boolean};const vv=B({name:"HomePage",slots:Object,setup(e,{slots:t}){const n=xn(),r=Ee(),o=L(()=>{const{features:i}=r.value;return ee(i)?i:null}),a=L(()=>{const{highlights:i}=r.value;return ee(i)?i:null});return()=>{var i,s,c,u;return l("main",{id:"main-content",class:["vp-project-home ",{pure:n.value}],"aria-labelledby":r.value.heroText===null?"":"main-title"},[(i=t.top)==null?void 0:i.call(t),l(pv),((s=a.value)==null?void 0:s.map(d=>"features"in d?l(oa,d):l(su,d)))||(o.value?l(se,{appear:!0,delay:.24},()=>l(oa,{features:o.value})):null),(c=t.center)==null?void 0:c.call(t),l(se,{appear:!0,delay:.32},()=>l(br)),(u=t.bottom)==null?void 0:u.call(t)])}}});const hv=B({name:"BreadCrumb",setup(){const e=Ne(),t=ie(),n=bt(),r=Ee(),o=le(),a=ft([]),i=L(()=>(r.value.breadcrumb||r.value.breadcrumb!==!1&&o.value.breadcrumb!==!1)&&a.value.length>1),s=L(()=>r.value.breadcrumbIcon||r.value.breadcrumbIcon!==!1&&o.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=R2(t.value.path,n.value).map(({link:p,name:v})=>{const h=u.find(b=>b.path===p);if(h){const{meta:b,path:T}=In(e,h.path);return{title:b[_e.shortTitle]||b[_e.title]||v,icon:b[_e.icon],path:T}}return null}).filter(p=>p!==null);d.length>1&&(a.value=d)};return ye(()=>{ve(()=>t.value.path,c,{immediate:!0})}),()=>l("nav",{class:["vp-breadcrumb",{disable:!i.value}]},i.value?l("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},a.value.map((u,d)=>l("li",{class:{"is-active":a.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[l(Re,{to:u.path,property:"item",typeof:"WebPage"},()=>[s.value?l($e,{icon:u.icon}):null,l("span",{property:"name"},u.title||"Unknown")]),l("meta",{property:"position",content:d+1})]))):[])}});const El=e=>{const t=Ne();return e===!1?!1:pe(e)?_n(t,e,!0):so(e)?e:null},aa=(e,t,n)=>{const r=e.findIndex(o=>o.link===t);if(r!==-1){const o=e[r+n];return o!=null&&o.link?o:null}for(const o of e)if(o.children){const a=aa(o.children,t,n);if(a)return a}return null},mv=B({name:"PageNav",setup(){const e=le(),t=Ee(),n=za(),r=ie(),o=mr(),a=L(()=>{const s=El(t.value.prev);return s===!1?null:s||(e.value.prevLink===!1?null:aa(n.value,r.value.path,-1))}),i=L(()=>{const s=El(t.value.next);return s===!1?null:s||(e.value.nextLink===!1?null:aa(n.value,r.value.path,1))});return Se("keydown",s=>{s.altKey&&(s.key==="ArrowRight"?i.value&&(o(i.value.link),s.preventDefault()):s.key==="ArrowLeft"&&a.value&&(o(a.value.link),s.preventDefault()))}),()=>a.value||i.value?l("nav",{class:"vp-page-nav"},[a.value?l(ze,{class:"prev",config:a.value},()=>{var s,c;return[l("div",{class:"hint"},[l("span",{class:"arrow start"}),e.value.metaLocales.prev]),l("div",{class:"link"},[l($e,{icon:(s=a.value)==null?void 0:s.icon}),(c=a.value)==null?void 0:c.text])]}):null,i.value?l(ze,{class:"next",config:i.value},()=>{var s,c;return[l("div",{class:"hint"},[e.value.metaLocales.next,l("span",{class:"arrow end"})]),l("div",{class:"link"},[(s=i.value)==null?void 0:s.text,l($e,{icon:(c=i.value)==null?void 0:c.icon})])]}):null]):null}}),cu=()=>l(ue,{name:"author"},()=>l("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));cu.displayName="AuthorIcon";const uu=()=>l(ue,{name:"calendar"},()=>l("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));uu.displayName="CalendarIcon";const du=()=>l(ue,{name:"category"},()=>l("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));du.displayName="CategoryIcon";const fu=()=>l(ue,{name:"print"},()=>l("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));fu.displayName="PrintIcon";const pu=()=>l(ue,{name:"tag"},()=>l("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));pu.displayName="TagIcon";const vu=()=>l(ue,{name:"timer"},()=>l("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));vu.displayName="TimerIcon";const hu=()=>l(ue,{name:"word"},()=>[l("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),l("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);hu.displayName="WordIcon";const Wt=()=>{const e=le();return L(()=>e.value.metaLocales)},gv={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},_v=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const a=cc(e);let i;return o?i=o:a!==null&&(i=gv[a]),i?i.replace(/:repo/,on(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,xs(`${Da(n)}/${r}`)):null},bv=()=>{const e=le(),t=ie(),n=Ee();return L(()=>{const{repo:r,docsRepo:o=r,docsBranch:a="main",docsDir:i="",editLink:s,editLinkPattern:c=""}=e.value;if(!(n.value.editLink??s??!0)||!o)return null;const d=_v({docsRepo:o,docsBranch:a,docsDir:i,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return d?{text:e.value.metaLocales.editLink,link:d}:null})},yv=()=>{const e=Rn(),t=le(),n=ie(),r=Ee();return L(()=>{var i,s;return!(r.value.lastUpdated??t.value.lastUpdated??!0)||!((i=n.value.git)!=null&&i.updatedTime)?null:new Date((s=n.value.git)==null?void 0:s.updatedTime).toLocaleString(e.value.lang)})},Ev=()=>{const e=le(),t=ie(),n=Ee();return L(()=>{var o;return n.value.contributors??e.value.contributors??!0?((o=t.value.git)==null?void 0:o.contributors)??null:null})};const Lv=B({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=Wt();return()=>e.author.length?l("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(cu),l("span",e.author.map(n=>n.url?l("a",{class:"page-author-item",href:n.url,target:"_blank",rel:"noopener noreferrer"},n.name):l("span",{class:"page-author-item"},n.name))),l("span",{property:"author",content:e.author.map(n=>n.name).join(", ")})]):null}});const Av=B({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ne(),n=ie(),r=Wt(),o=(a,i="")=>{i&&n.value.path!==i&&(a.preventDefault(),t.push(i))};return()=>e.category.length?l("span",{class:"page-category-info","aria-label":`${r.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(du),e.category.map(({name:a,path:i})=>l("span",{class:["page-category-item",{[`category${po(a,9)}`]:!e.pure,clickable:i}],role:i?"navigation":"",onClick:s=>o(s,i)},a)),l("meta",{property:"articleSection",content:e.category.map(({name:a})=>a).join(",")})]):null}}),Tv=B({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=co(),n=Wt();return()=>e.date?l("span",{class:"page-date-info","aria-label":`${n.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(uu),l("span",l(pr,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),l("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}});const Iv=B({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=Wt();return()=>e.isOriginal?l("span",{class:"page-original-info"},t.value.origin):null}}),wv=B({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Wt(),n=L(()=>{if(!e.readingTime)return null;const{minutes:r}=e.readingTime;return r<1?"PT1M":`PT${Math.round(r)}M`});return()=>{var r,o;return(r=e.readingTimeLocale)!=null&&r.time?l("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(vu),l("span",(o=e.readingTimeLocale)==null?void 0:o.time),l("meta",{property:"timeRequired",content:n.value})]):null}}});const kv=B({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ne(),n=ie(),r=Wt(),o=(a,i="")=>{i&&n.value.path!==i&&(a.preventDefault(),t.push(i))};return()=>e.tag.length?l("span",{class:"page-tag-info","aria-label":`${r.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(pu),e.tag.map(({name:a,path:i})=>l("span",{class:["page-tag-item",{[`tag${po(a,9)}`]:!e.pure,clickable:i}],role:i?"navigation":"",onClick:s=>o(s,i)},a)),l("meta",{property:"keywords",content:e.tag.map(({name:a})=>a).join(",")})]):null}}),Pv=B({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Wt();return()=>{var n,r,o;return(n=e.readingTimeLocale)!=null&&n.words?l("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[l(hu),l("span",(r=e.readingTimeLocale)==null?void 0:r.words),l("meta",{property:"wordCount",content:(o=e.readingTime)==null?void 0:o.words})]):null}}});const mu=B({name:"PageInfo",components:{AuthorInfo:Lv,CategoryInfo:Av,DateInfo:Tv,OriginalInfo:Iv,PageViewInfo:()=>null,ReadingTimeInfo:wv,TagInfo:kv,WordInfo:Pv},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=xn();return()=>e.items?l("div",{class:"page-info"},e.items.map(n=>l(Ke(`${n}Info`),{...e.info,pure:t.value}))):null}});const Ov=B({name:"PageTitle",setup(){const e=ie(),t=Ee(),n=le(),{info:r,items:o}=V2();return()=>l("div",{class:"vp-page-title"},[l("h1",[n.value.titleIcon===!1?null:l($e,{icon:t.value.icon}),e.value.title]),l(mu,{info:r.value,...o.value===null?{}:{items:o.value}}),l("hr")])}}),gu=()=>l(ue,{name:"edit"},()=>[l("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),l("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);gu.displayName="EditIcon";const Rv=B({name:"PageMeta",setup(){const e=le(),t=bv(),n=yv(),r=Ev();return()=>{const{metaLocales:o}=e.value;return l("footer",{class:"page-meta"},[t.value?l("div",{class:"meta-item edit-link"},l(ze,{class:"label",config:t.value},{before:()=>l(gu)})):null,l("div",{class:"meta-item git-info"},[n.value?l("div",{class:"update-time"},[l("span",{class:"label"},`${o.lastUpdated}: `),l(pr,()=>l("span",{class:"info"},n.value))]):null,r.value&&r.value.length?l("div",{class:"contributors"},[l("span",{class:"label"},`${o.contributors}: `),r.value.map(({email:a,name:i},s)=>[l("span",{class:"contributor",title:`email: ${a}`},i),s!==r.value.length-1?",":""])]):null])])}}});const Dv=B({name:"PrintButton",setup(){const e=Gt(),t=le();return()=>e.value.print===!1?null:l("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},l(fu))}});const Sv=({title:e,level:t,slug:n})=>l(Re,{to:`#${n}`,class:["toc-link",`level${t}`]},()=>e),ia=(e,t)=>{const n=pt();return e.length&&t>0?l("ul",{class:"toc-list"},e.map(r=>{const o=ia(r.children,t-1);return[l("li",{class:["toc-item",{active:Ba(n,`#${r.slug}`)}]},Sv(r)),o?l("li",o):null]})):null},_u=B({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const n=pt(),r=ie(),o=Wt(),a=ft(),i=Y("-1.7rem"),s=u=>{var d;(d=a.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(a.value){const u=document.querySelector(".toc-item.active");u?i.value=`${u.getBoundingClientRect().top-a.value.getBoundingClientRect().top+a.value.scrollTop}px`:i.value="-1.7rem"}else i.value="-1.7rem"};return ye(()=>{ve(()=>n.hash,u=>{if(a.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:v}=a.value.getBoundingClientRect(),{top:h,height:b}=d.getBoundingClientRect();hp+v&&s(a.value.scrollTop+h+b-p-v)}}),ve(()=>n.fullPath,c,{flush:"post",immediate:!0})}),()=>{var d,p;const u=e.items.length?ia(e.items,e.headerDepth):r.value.headers?ia(r.value.headers,e.headerDepth):null;return u?l("div",{class:"toc-place-holder"},[l("aside",{id:"toc"},[(d=t.before)==null?void 0:d.call(t),l("div",{class:"toc-header"},[o.value.toc,l(Dv)]),l("div",{class:"toc-wrapper",ref:a},[u,l("div",{class:"toc-marker",style:{top:i.value}})]),(p=t.after)==null?void 0:p.call(t)])]):null}}});const xv=B({name:"NormalPage",slots:Object,setup(e,{slots:t}){const n=Ee(),r=ie(),{isDarkmode:o}=_r(),a=le(),i=L(()=>n.value.toc||n.value.toc!==!1&&a.value.toc!==!1);return()=>l("main",{id:"main-content",class:"vp-page"},l(It("LocalEncrypt")?Ke("LocalEncrypt"):tc,()=>{var s,c,u,d;return[(s=t.top)==null?void 0:s.call(t),n.value.cover?l("img",{class:"page-cover",src:Ie(n.value.cover),alt:r.value.title,"no-view":""}):null,l(hv),l(Ov),i.value?l(_u,{headerDepth:n.value.headerDepth??a.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),l(br),(u=t.contentAfter)==null?void 0:u.call(t),l(Rv),l(mv),It("CommentService")?l(Ke("CommentService"),{darkmode:o.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}});const Ga=B({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ie(),n=le(),r=ft(),o=({target:a})=>{const i=document.querySelector(a.hash);if(i){const s=()=>{i.removeAttribute("tabindex"),i.removeEventListener("blur",s)};i.setAttribute("tabindex","-1"),i.addEventListener("blur",s),i.focus(),window.scrollTo(0,0)}};return ye(()=>{ve(()=>t.value.path,()=>r.value.focus())}),()=>[l("span",{ref:r,tabindex:"-1"}),l("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:o},n.value.routeLocales.skipToContent)]}});const Cv=B({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:n,pending:r}=Hc();return()=>l(wt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:n,onBeforeLeave:r},()=>{var o;return(o=t.default)==null?void 0:o.call(t)})}}),Vv=B({name:"Layout",setup(){const e=Gt(),t=le(),n=ie(),r=Ee(),{isMobile:o}=gr(),a=L(()=>{var i,s;return((i=t.value.blog)==null?void 0:i.sidebarDisplay)||((s=e.value.blog)==null?void 0:s.sidebarDisplay)||"mobile"});return()=>[l(Ga),l(Ua,{},{default:()=>r.value.home?l(vv):l(Cv,()=>l(xv,{key:n.value.path})),...a.value!=="none"?{navScreenBottom:()=>l(Ke("BloggerInfo"))}:{},...!o.value&&a.value==="always"?{sidebar:()=>l(Ke("BloggerInfo"))}:{}})]}});const Mv=B({name:"NotFoundHint",setup(){const e=le(),t=()=>{const n=e.value.routeLocales.notFoundMsg;return n[Math.floor(Math.random()*n.length)]};return()=>l("div",{class:"not-found-hint"},[l("p",{class:"error-code"},"404"),l("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),l("p",{class:"error-hint"},t())])}});const Bv=B({name:"NotFound",slots:Object,setup(e,{slots:t}){const n=bt(),r=le(),{navigate:o}=Zo({to:r.value.home??n.value});return()=>[l(Ga),l(Ua,{noSidebar:!0},()=>{var a;return l("main",{id:"main-content",class:"vp-page not-found"},((a=t.default)==null?void 0:a.call(t))||[l(Mv),l("div",{class:"actions"},[l("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},r.value.routeLocales.back),l("button",{type:"button",class:"action-button",onClick:()=>o()},r.value.routeLocales.home)])])})]}}),$v={GitHub:'',Gmail:'',QQ:'',Wechat:''},Nv={category:{"/":{path:"/category/",map:{随笔:{path:"/category/%E9%9A%8F%E7%AC%94/",keys:["v-ed1de9c6"]},DDD:{path:"/category/ddd/",keys:["v-ed1de9c6"]},算法:{path:"/category/%E7%AE%97%E6%B3%95/",keys:["v-1dbb5152","v-7ba48b4f","v-79efb2b0"]},地图:{path:"/category/%E5%9C%B0%E5%9B%BE/",keys:["v-1dbb5152"]},微信:{path:"/category/%E5%BE%AE%E4%BF%A1/",keys:["v-1f7029f1"]},前端:{path:"/category/%E5%89%8D%E7%AB%AF/",keys:["v-248eb3ce"]},docker:{path:"/category/docker/",keys:["v-4468c549","v-7763b6e7","v-aad5ccee"]},教程:{path:"/category/%E6%95%99%E7%A8%8B/",keys:["v-7febf202"]},人工智能:{path:"/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",keys:["v-a4026a72"]},工具:{path:"/category/%E5%B7%A5%E5%85%B7/",keys:["v-1ad96fc8"]},kubernetes:{path:"/category/kubernetes/",keys:["v-f0b271fa"]},数据结构:{path:"/category/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/",keys:["v-249319d7","v-0115d5da"]},DB:{path:"/category/db/",keys:["v-36bed06a","v-61e42302","v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-1ae8d368","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-753dfaa7","v-b8674064","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3"]},MyBatis:{path:"/category/mybatis/",keys:["v-0b555a5d"]},Java:{path:"/category/java/",keys:["v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-d0e2c786","v-305fe098"]},微服务:{path:"/category/%E5%BE%AE%E6%9C%8D%E5%8A%A1/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-e97c7d16"]},RPC:{path:"/category/rpc/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066"]},TCP:{path:"/category/tcp/",keys:["v-da7165c6"]},IP:{path:"/category/ip/",keys:["v-da7165c6"]},Spring:{path:"/category/spring/",keys:["v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},SpringBoot:{path:"/category/springboot/",keys:["v-26c13b6a","v-3b468bb2"]},SpringMVC:{path:"/category/springmvc/",keys:["v-e65d617a","v-e9c712b8","v-ed30c3f6"]},并发:{path:"/category/%E5%B9%B6%E5%8F%91/",keys:["v-783d7264","v-e12a9278","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415"]}}},"/en/":{path:"/en/category/",map:{docker:{path:"/en/category/docker/",keys:["v-3fc52a21","v-72c01bbf","v-b41d033e"]},教程:{path:"/en/category/%E6%95%99%E7%A8%8B/",keys:["v-460112e6"]},人工智能:{path:"/en/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",keys:["v-6494b51e"]},工具:{path:"/en/category/%E5%B7%A5%E5%85%B7/",keys:["v-0a4a23d6"]}}}},tag:{"/":{path:"/tag/",map:{DDD:{path:"/tag/ddd/",keys:["v-ed1de9c6"]},排序:{path:"/tag/%E6%8E%92%E5%BA%8F/",keys:["v-7ba48b4f","v-79efb2b0"]},多边形:{path:"/tag/%E5%A4%9A%E8%BE%B9%E5%BD%A2/",keys:["v-1dbb5152"]},微信服务号:{path:"/tag/%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%8F%B7/",keys:["v-1f7029f1"]},LayUI:{path:"/tag/layui/",keys:["v-248eb3ce"]},docker:{path:"/tag/docker/",keys:["v-4468c549","v-7763b6e7","v-aad5ccee"]},linux:{path:"/tag/linux/",keys:["v-7febf202"]},OpenAI:{path:"/tag/openai/",keys:["v-a4026a72"]},ChatGPT:{path:"/tag/chatgpt/",keys:["v-a4026a72"]},idea:{path:"/tag/idea/",keys:["v-1ad96fc8"]},kubernetes:{path:"/tag/kubernetes/",keys:["v-f0b271fa"]},栈:{path:"/tag/%E6%A0%88/",keys:["v-249319d7"]},跳表:{path:"/tag/%E8%B7%B3%E8%A1%A8/",keys:["v-0115d5da"]},Redis:{path:"/tag/redis/",keys:["v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3"]},基础:{path:"/tag/%E5%9F%BA%E7%A1%80/",keys:["v-0b555a5d","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550"]},JVM:{path:"/tag/jvm/",keys:["v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-d0e2c786","v-305fe098"]},类加载:{path:"/tag/%E7%B1%BB%E5%8A%A0%E8%BD%BD/",keys:["v-d0e2c786"]},Lock:{path:"/tag/lock/",keys:["v-c5b336ca","v-0dc9bed3","v-53a68847","v-ea9ab6de"]},GC:{path:"/tag/gc/",keys:["v-366969d6","v-64b533de","v-5a83f4b7"]},dubbo:{path:"/tag/dubbo/",keys:["v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066"]},zookeeper:{path:"/tag/zookeeper/",keys:["v-e97c7d16"]},网络编程:{path:"/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/",keys:["v-da7165c6"]},MySQL:{path:"/tag/mysql/",keys:["v-36bed06a","v-61e42302","v-1ae8d368","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-753dfaa7","v-b8674064"]},索引:{path:"/tag/%E7%B4%A2%E5%BC%95/",keys:["v-1ae8d368","v-76f2d346","v-753dfaa7","v-b8674064"]},事务:{path:"/tag/%E4%BA%8B%E5%8A%A1/",keys:["v-36bed06a"]},锁机制:{path:"/tag/%E9%94%81%E6%9C%BA%E5%88%B6/",keys:["v-61e42302"]},源码:{path:"/tag/%E6%BA%90%E7%A0%81/",keys:["v-4f779f12"]},插件:{path:"/tag/%E6%8F%92%E4%BB%B6/",keys:["v-26c13b6a","v-3b468bb2"]},ThreadLocal:{path:"/tag/threadlocal/",keys:["v-55cc2415"]},Atomic:{path:"/tag/atomic/",keys:["v-49daa846"]},线程安全:{path:"/tag/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/",keys:["v-783d7264","v-e12a9278","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-4ee32d34"]},JUC:{path:"/tag/juc/",keys:["v-15eaf018"]},AQS:{path:"/tag/aqs/",keys:["v-3b8d5cd7"]},Queue:{path:"/tag/queue/",keys:["v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4"]}}},"/en/":{path:"/en/tag/",map:{docker:{path:"/en/tag/docker/",keys:["v-3fc52a21","v-72c01bbf","v-b41d033e"]},linux:{path:"/en/tag/linux/",keys:["v-460112e6"]},OpenAI:{path:"/en/tag/openai/",keys:["v-6494b51e"]},ChatGPT:{path:"/en/tag/chatgpt/",keys:["v-6494b51e"]},idea:{path:"/en/tag/idea/",keys:["v-0a4a23d6"]}}}}},Hv={article:{"/":{path:"/article/",keys:["v-da7165c6","v-1dbb5152","v-2d175b02","v-59c0c6b8","v-36bed06a","v-61e42302","v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-783d7264","v-e12a9278","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-f0b271fa","v-26c13b6a","v-3b468bb2","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415","v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-1ad96fc8","v-1ae8d368","v-ed1de9c6","v-1f7029f1","v-248eb3ce","v-7febf202","v-a4026a72","v-249319d7","v-0b555a5d","v-d0e2c786","v-e97c7d16","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-4468c549","v-7763b6e7","v-aad5ccee","v-753dfaa7","v-305fe098","v-b8674064","v-7ba48b4f","v-0115d5da","v-79efb2b0","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},"/en/":{path:"/en/article/",keys:["v-5aa3d8ba","v-0a4a23d6","v-460112e6","v-6494b51e","v-3fc52a21","v-72c01bbf","v-b41d033e"]}},star:{"/":{path:"/star/",keys:["v-da7165c6","v-1dbb5152"]},"/en/":{path:"/en/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-2d175b02","v-59c0c6b8","v-36bed06a","v-61e42302","v-366969d6","v-64b533de","v-5a83f4b7","v-4b158648","v-da7165c6","v-fbc34060","v-78811366","v-47b60065","v-1aa97360","v-783d7264","v-e12a9278","v-3a6c11b6","v-7eeef733","v-91f8f38c","v-10b58660","v-c5b336ca","v-f0b271fa","v-26c13b6a","v-3b468bb2","v-24957b89","v-1b2e8a6c","v-ba51a5ea","v-730879e4","v-0dc9bed3","v-53a68847","v-ea9ab6de","v-3b8d5cd7","v-15eaf018","v-49daa846","v-4ee32d34","v-55cc2415","v-3af4619e","v-73716104","v-82100b6c","v-12216b09","v-173ff4e6","v-a7a8f066","v-1ad96fc8","v-1ae8d368","v-ed1de9c6","v-1dbb5152","v-1f7029f1","v-248eb3ce","v-7febf202","v-a4026a72","v-249319d7","v-0b555a5d","v-d0e2c786","v-e97c7d16","v-01034d18","v-01333213","v-02e80ab2","v-049ce351","v-0651bbf0","v-76f2d346","v-4468c549","v-7763b6e7","v-aad5ccee","v-753dfaa7","v-305fe098","v-b8674064","v-7ba48b4f","v-0115d5da","v-79efb2b0","v-327b6d83","v-2ba80b07","v-00fee380","v-04d59b7c","v-0ba8fdf8","v-1452e9f3","v-e65d617a","v-e9c712b8","v-ed30c3f6","v-07fe3def","v-09b3168e","v-06496550","v-4f779f12"]},"/en/":{path:"/en/timeline/",keys:["v-5aa3d8ba","v-0a4a23d6","v-460112e6","v-6494b51e","v-3fc52a21","v-72c01bbf","v-b41d033e"]}}},Ll=Y(Nv),bu=(e="")=>{const t=ie(),n=Ne(),r=bt();return L(()=>{var c;const o=e||((c=Ee().value.blog)==null?void 0:c.key)||"";if(!o)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const a=n.getRoutes();if(!Ll.value[o])throw new Error(`useBlogCategory: key ${o} is invalid`);const i=Ll.value[o][r.value],s={path:i.path,map:{}};for(const u in i.map){const d=i.map[u];s.map[u]={path:d.path,items:[]};for(const p of d.keys){const v=a.find(({name:h})=>h===p);if(v){const h=In(n,v.path);s.map[u].items.push({path:h.path,info:h.meta})}}t.value.path===d.path&&(s.currentItems=s.map[u].items)}return s})},Al=Y(Hv),go=(e="")=>{const t=Ne(),n=bt();return L(()=>{var s;const r=e||((s=Ee().value.blog)==null?void 0:s.key)||"";if(!r)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Al.value[r])throw new Error(`useBlogType: key ${e} is invalid`);const o=t.getRoutes(),a=Al.value[r][n.value],i={path:a.path,items:[]};for(const c of a.keys){const u=o.find(({name:d})=>d===c);if(u){const d=In(t,u.path);i.items.push({path:d.path,info:d.meta})}}return i})},yu=Symbol.for("categoryMap"),yr=()=>{const e=he(yu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},Fv=()=>{const e=bu("category");ct(yu,e)},Er=()=>{const e=Gt(),t=le();return L(()=>({...e.value.blog,...t.value.blog}))},Eu=Symbol.for("tagMap"),Lr=()=>{const e=he(Eu);if(!e)throw new Error("useTagMap() is called without provider.");return e},jv=()=>{const e=bu("tag");ct(Eu,e)},zv=e=>{const t=le();return L(()=>{const{[_e.author]:n}=e.value;return n?or(n):n===!1?[]:or(t.value.author,!1)})},qv=e=>{const t=yr();return L(()=>ic(e.value[_e.category]).map(n=>({name:n,path:t.value.map[n].path})))},Uv=e=>{const t=Lr();return L(()=>lc(e.value[_e.tag]).map(n=>({name:n,path:t.value.map[n].path})))},Gv=e=>L(()=>{const{[_e.date]:t}=e.value;return Va(t)}),Wv=e=>{const t=Pn(e,"info"),n=Er(),r=zv(t),o=qv(t),a=Uv(t),i=Gv(t),s=$c(),c=L(()=>({author:r.value,category:o.value,date:i.value,localizedDate:t.value[_e.localizedDate]||"",tag:a.value,isOriginal:t.value[_e.isOriginal]||!1,readingTime:t.value[_e.readingTime]||null,readingTimeLocale:t.value[_e.readingTime]&&s.value?Bc(t.value[_e.readingTime],s.value):null,pageview:e.path})),u=L(()=>n.value.articleInfo);return{info:c,items:u}},Lu=Symbol(""),Ar=()=>{const e=he(Lu);if(!e)throw new Error("useArticles() is called without provider.");return e},Jv=()=>{const e=go("article");ct(Lu,e)},Au=Symbol(""),Wa=()=>{const e=he(Au);if(!e)throw new Error("useStars() is called without provider.");return e},Qv=()=>{const e=go("star");ct(Au,e)},Tu=Symbol(""),Ja=()=>{const e=he(Tu);if(!e)throw new Error("useTimelines() is called without provider.");return e},Kv=()=>{const e=go("timeline"),t=L(()=>{const n=[];return e.value.items.forEach(({info:r,path:o})=>{const a=Va(r[_e.date]),i=a==null?void 0:a.getFullYear(),s=a?a.getMonth()+1:null,c=a==null?void 0:a.getDate();i&&s&&c&&((!n[0]||n[0].year!==i)&&n.unshift({year:i,items:[]}),n[0].items.push({date:`${s}/${c}`,info:r,path:o}))}),{...e.value,config:n.reverse()}});ct(Tu,t)},Yv=()=>{Jv(),Fv(),Qv(),jv(),Kv()};const Xv=B({name:"SocialMedia",setup(){const e=Er(),t=xn(),n=L(()=>{const r=e.value.medias;return r?Dn(r).map(([o,a])=>({name:o,icon:$v[o],url:a})):[]});return()=>n.value.length?l("div",{class:"vp-social-medias"},n.value.map(({name:r,icon:o,url:a})=>l("a",{class:"vp-social-media",href:a,rel:"noopener noreferrer",target:"_blank","aria-label":r,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:o}))):null}});const Qa=B({name:"BloggerInfo",setup(){const e=Er(),t=Rn(),n=le(),r=Ar(),o=yr(),a=Lr(),i=Ja(),s=mr(),c=L(()=>{var v;return e.value.name||((v=or(n.value.author)[0])==null?void 0:v.name)||t.value.title}),u=L(()=>e.value.avatar||n.value.logo),d=L(()=>n.value.blogLocales),p=L(()=>e.value.intro);return()=>{const{article:v,category:h,tag:b,timeline:T}=d.value,I=[[r.value.path,r.value.items.length,v],[o.value.path,dt(o.value.map).length,h],[a.value.path,dt(a.value.map).length,b],[i.value.path,i.value.items.length,T]];return l("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[l("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>s(p.value)}:{}},[u.value?l("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:Ie(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?l("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?l("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?l("meta",{property:"url",content:Ie(p.value)}):null]),l("div",{class:"vp-blog-counts"},I.map(([E,k,y])=>l(Re,{class:"vp-blog-count",to:E},()=>[l("div",{class:"count"},k),l("div",y)]))),l(Xv)])}}}),la=()=>l(ue,{name:"category"},()=>l("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));la.displayName="CategoryIcon";const sa=()=>l(ue,{name:"tag"},()=>l("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));sa.displayName="TagIcon";const Ka=()=>l(ue,{name:"timeline"},()=>l("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Ka.displayName="TimelineIcon";const Iu=()=>l(ue,{name:"slides"},()=>l("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));Iu.displayName="SlideIcon";const wu=()=>l(ue,{name:"sticky"},()=>[l("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const Yr=()=>l(ue,{name:"article"},()=>l("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));Yr.displayName="ArticleIcon";const ku=()=>l(ue,{name:"book"},()=>l("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));ku.displayName="BookIcon";const Pu=()=>l(ue,{name:"link"},()=>l("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));Pu.displayName="LinkIcon";const Ou=()=>l(ue,{name:"project"},()=>l("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));Ou.displayName="ProjectIcon";const Ru=()=>l(ue,{name:"friend"},()=>l("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Ru.displayName="FriendIcon";const ca=()=>l(ue,{name:"slide-down"},()=>l("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));ca.displayName="SlideDownIcon";const Du=()=>l("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Du.displayName="EmptyIcon";const Su=()=>l(ue,{name:"lock"},()=>l("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Su.displayName="LockIcon";const Zv=B({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const n=Pn(e,"info"),{info:r,items:o}=Wv(e);return()=>{var v,h,b;const{[_e.title]:a,[_e.type]:i,[_e.isEncrypted]:s=!1,[_e.cover]:c,[_e.excerpt]:u,[_e.sticky]:d}=n.value,p=r.value;return l("div",{class:"vp-article-wrapper"},l("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((v=t.cover)==null?void 0:v.call(t,{cover:c}))||(c?[l("img",{class:"vp-article-cover",src:Ie(c)}),l("meta",{property:"image",content:Ie(c)})]:[]),d?l(wu):null,l(Re,{to:e.path},()=>{var T;return((T=t.title)==null?void 0:T.call(t,{title:a,isEncrypted:s,type:i}))||l("header",{class:"vp-article-title"},[s?l(Su):null,i===ta.slide?l(Iu):null,l("span",{property:"headline"},a)])}),((h=t.excerpt)==null?void 0:h.call(t,{excerpt:u}))||(u?l("div",{class:"vp-article-excerpt",innerHTML:u}):null),l("hr",{class:"vp-article-hr"}),((b=t.info)==null?void 0:b.call(t,{info:p}))||l(mu,{info:p,...o.value?{items:o.value}:{}})]))}}});const e3=B({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let n;const r=le(),o=Y(""),a=L(()=>r.value.paginationLocales),i=L(()=>Math.ceil(e.total/e.perPage)),s=L(()=>!!i.value&&i.value!==1),c=L(()=>i.value<7?!1:e.current>4),u=L(()=>i.value<7?!1:e.current{const{current:h}=e;let b=1,T=i.value;const I=[];i.value>=7&&(h<=4&&h4&&h>=i.value-3?(T=i.value,b=i.value-4):i.value>7&&(b=h-2,T=h+2));for(let E=b;E<=T;E++)I.push(E);return I}),p=h=>t("updateCurrentPage",h),v=h=>{const b=parseInt(h);b<=i.value&&b>0?p(b):n.pop(`${a.value.errorText.replace(/\$page/g,i.value.toString())}`)};return ye(()=>{n=new Jf}),()=>l("div",{class:"vp-pagination"},s.value?l("div",{class:"vp-pagination-list"},[l("div",{class:"vp-pagination-number "},[e.current>1?l("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},a.value.prev):null,c.value?[l("div",{role:"navigation",onClick:()=>p(1)},1),l("div",{class:"ellipsis"},"...")]:null,d.value.map(h=>l("div",{key:h,class:{active:e.current===h},role:"navigation",onClick:()=>p(h)},h)),u.value?[l("div",{class:"ellipsis"},"..."),l("div",{role:"navigation",onClick:()=>p(i.value)},i.value)]:null,e.currentp(e.current+1)},a.value.next):null]),l("div",{class:"vp-pagination-nav"},[l("label",{for:"navigation-text"},`${a.value.navigate}: `),l("input",{id:"navigation-text",value:o.value,onInput:({target:h})=>{o.value=h.value},onKeydown:h=>{h.key==="Enter"&&(h.preventDefault(),v(o.value))}}),l("button",{class:"vp-pagination-button",role:"navigation",title:a.value.action,onClick:()=>v(o.value)},a.value.action)])]):[])}});const Ya=B({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=pt(),n=Ne(),r=Er(),o=Y(1),a=L(()=>r.value.articlePerPage||10),i=L(()=>e.items.slice((o.value-1)*a.value,o.value*a.value)),s=async c=>{o.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),await n.push({path:t.path,query:u}))};return ye(()=>{const{page:c}=t.query;s(c?Number(c):1),ve(o,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)})}),()=>l("div",{id:"article-list",class:"vp-article-list"},i.value.length?[...i.value.map(({info:c,path:u},d)=>l(se,{appear:!0,delay:d*.04},()=>l(Zv,{key:u,info:c,path:u}))),l(e3,{current:o.value,perPage:a.value,total:e.items.length,onUpdateCurrentPage:s})]:l(Du))}});const Xa=B({name:"CategoryList",setup(){const e=ie(),t=yr();return()=>l("ul",{class:"vp-category-list"},Dn(t.value.map).map(([n,{path:r,items:o}])=>l("li",{class:["vp-category",`vp-category${po(n,9)}`,{active:r===e.value.path}]},l(Re,{to:r},()=>[n,l("span",{class:"count"},o.length)]))))}});const Za=B({name:"TagList",setup(){const e=Ee(),t=Lr(),n=r=>{var o;return r===((o=e.value.blog)==null?void 0:o.name)};return()=>l("ul",{class:"tag-list-wrapper"},Dn(t.value.map).map(([r,{path:o,items:a}])=>l("li",{class:["tag",`tag${po(r,9)}`,{active:n(r)}]},l(Re,{to:o},()=>[r,l("span",{class:"tag-num"},a.length)]))))}});const t3=B({name:"TimelineList",setup(){const e=le(),t=Ja(),n=mr(),r=L(()=>e.value.blogLocales.timeline);return()=>l("div",{class:"timeline-list-wrapper"},[l("div",{class:"timeline-list-title",onClick:()=>n(t.value.path)},[l(Ka),l("span",{class:"num"},t.value.items.length),r.value]),l("hr"),l("div",{class:"timeline-content"},l("ul",{class:"timeline-list"},t.value.config.map(({year:o,items:a},i)=>l(se,{appear:!0,delay:.08*(i+1)},()=>l("li",[l("h3",{class:"timeline-year"},o),l("ul",{class:"timeline-year-wrapper"},a.map(({date:s,info:c,path:u})=>l("li",{class:"timeline-item"},[l("span",{class:"timeline-date"},s),l(Re,{class:"timeline-title",to:u},()=>c[_e.title])])))])))))])}});const xu=B({name:"InfoList",setup(){const e=le(),t=Ar(),n=yr(),r=L(()=>dt(n.value.map).length),o=Wa(),a=Lr(),i=L(()=>dt(a.value.map).length),s=mr(),c=Y("article"),u=L(()=>e.value.blogLocales),d=[["article",Yr],["category",la],["tag",sa],["timeline",Ka]];return()=>l("div",{class:"vp-blog-infos"},[l("div",{class:"vp-blog-type-switcher"},d.map(([p,v])=>l("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},l("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},l(v))))),l(se,()=>c.value==="article"?l("div",{class:"vp-sticky-article-wrapper"},[l("div",{class:"title",onClick:()=>s(t.value.path)},[l(Yr),l("span",{class:"num"},t.value.items.length),u.value.article]),l("hr"),l("ul",{class:"vp-sticky-articles"},o.value.items.map(({info:p,path:v},h)=>l(se,{appear:!0,delay:.08*(h+1)},()=>l("li",{class:"vp-sticky-article"},l(Re,{to:v},()=>p[_e.title])))))]):c.value==="category"?l("div",{class:"vp-category-wrapper"},[r.value?l("div",{class:"title",onClick:()=>s(n.value.path)},[l(la),l("span",{class:"num"},r.value),u.value.category]):null,l("hr"),l(se,{delay:.04},()=>l(Xa))]):c.value==="tag"?l("div",{class:"vp-tag-wrapper"},[i.value?l("div",{class:"title",onClick:()=>s(a.value.path)},[l(sa),l("span",{class:"num"},i.value),u.value.tag]):null,l("hr"),l(se,{delay:.04},()=>l(Za))]):l(se,()=>l(t3)))])}});const _o=B({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:n}=gr();return()=>[l(Ga),l(Ua,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>l(Qa),...n.value?{sidebar:()=>l(xu)}:{}})]}});const Tr=()=>l("aside",{class:"vp-blog-info-wrapper"},[l(se,()=>l(Qa)),l(se,{delay:.04},()=>l(xu))]);Tr.displayName="InfoPanel";const n3=B({name:"BlogPage",components:{CategoryList:Xa,TagList:Za},setup(){const e=ie(),t=Ee(),n=yr(),r=Lr(),o=L(()=>t.value.blog||{}),a=L(()=>{const{key:s=""}=o.value;return s==="category"?"CategoryList":s==="tag"?"TagList":null}),i=L(()=>{const{name:s="",key:c=""}=o.value;return c==="category"?s?n.value.map[s].items:[]:c==="tag"?s?r.value.map[s].items:[]:[]});return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,()=>a.value?l(Ke(a.value)):null),o.value.name?l(se,{appear:!0,delay:.24},()=>l(Ya,{key:e.value.path,items:i.value})):null]),l(se,{delay:.16},()=>l(Tr,{key:"blog"}))])))}}),r3="/assets/hero-197a9d2d.jpg";const o3=B({name:"BlogHero",slots:Object,setup(e,{slots:t}){const n=Ee(),r=Rn(),o=ft(),a=L(()=>n.value.heroFullScreen??!1),i=L(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:v,tagline:h}=n.value;return{text:c??r.value.title??"Hello",image:u?Ie(u):null,imageDark:d?Ie(d):null,heroStyle:v,alt:p||c||"hero image",tagline:h??"",isFullScreen:a.value}}),s=L(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=n.value;return{image:pe(c)?Ie(c):c===!1?null:r3,imageDark:pe(u)?Ie(u):null,bgStyle:d,isFullScreen:a.value}});return()=>{var c,u;return n.value.hero===!1?null:l("div",{ref:o,class:["vp-blog-hero",{fullscreen:a.value,"no-bg":!s.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,s.value))||[s.value.image?l("div",{class:["vp-blog-mask",{light:s.value.imageDark}],style:[{background:`url(${s.value.image}) center/cover no-repeat`},s.value.bgStyle]}):null,s.value.imageDark?l("div",{class:"vp-blog-mask dark",style:[{background:`url(${s.value.imageDark}) center/cover no-repeat`},s.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,i.value))||[l(se,{appear:!0,type:"group",delay:.04},()=>[i.value.image?l("img",{key:"light",class:["vp-blog-hero-image",{light:i.value.imageDark}],style:i.value.heroStyle,src:i.value.image,alt:i.value.alt}):null,i.value.imageDark?l("img",{key:"dark",class:"vp-blog-hero-image dark",style:i.value.heroStyle,src:i.value.imageDark,alt:i.value.alt}):null]),l(se,{appear:!0,delay:.08},()=>i.value.text?l("h1",{class:"vp-blog-hero-title"},i.value.text):null),l(se,{appear:!0,delay:.12},()=>i.value.tagline?l("p",{class:"vp-blog-hero-description",innerHTML:i.value.tagline}):null)],i.value.isFullScreen?l("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:o.value.clientHeight,behavior:"smooth"})}},[l(ca),l(ca)]):null])}}});const Ct=yc("bing-image",{index:0,data:[]}),a3=B({name:"BingHeroBackground",setup(){const e=co(),t=ft(),n=Y(!1),r=L(()=>{const s=Ct.value.data[Ct.value.index],c=e.value.toLowerCase().split("-").shift();if(s){const{url:u,wallpaper:d,downloadable:p,locales:v}=s;return{url:u,wallpaper:d,downloadable:p,...v[c]??v.en}}return null}),o=()=>fetch("https://bing-wallpaper.vuejs.press/api/wallpaper").then(s=>s.json()),a=()=>{Ct.value.index--},i=()=>{Ct.value.index++};return fp(t,()=>{n.value=!1}),ye(()=>{o().then(s=>{Ct.value.data=s})}),()=>{const{title:s,headline:c,url:u,backstage:d,quickFact:p,copyright:v}=r.value||{};return l(pr,()=>u?[l("div",{class:"vp-blog-mask",style:{background:`url(${u}) center/cover no-repeat`}}),l("div",{class:"bing-switch",onClick:()=>{n.value=!0}},[l(wt,{name:"fade"},()=>n.value?l("div",{class:"bing-info",ref:t},[l("a",{href:d,target:"_blank",class:"bing-info-header"},c),l("hr"),l("div",{class:"bing-info-body"},p),l("div",{class:"bing-info-copyright"},v)]):null),l("div",{class:"bing-location"},[l("span",{class:"bing-location-icon"}),s]),l("button",{class:"bing-switch-prev",disabled:Ct.value.index===0,onClick:()=>a()}),l("button",{class:"bing-switch-next",disabled:Ct.value.index===Ct.value.data.length-1,onClick:()=>i()})])]:null)}}});const i3=B({name:"HitokotoBlogHero",props:{text:{type:String,required:!0},image:{type:String,default:null},imageDark:{type:String,default:null},alt:{type:String,required:!0},heroStyle:{type:[String,Object],default:null}},setup(e){const t=Y(""),n=Y(""),r=Y("");let o=!1;const a=()=>fetch("https://v1.hitokoto.cn").then(i=>i.json()).then(({from:i,hitokoto:s})=>{t.value=s,r.value=i});return ye(()=>{o=!0,ve(t,()=>{n.value="";let i=0;const s=()=>(n.value+=t.value[i],i++,rn().then(()=>{i{s()},150):o&&setTimeout(()=>{a()},3e3)}));s()}),a()}),ur(()=>{o=!1}),()=>[l(se,{appear:!0,type:"group",delay:.04},()=>[e.image?l("img",{key:"light",class:["vp-blog-hero-image",{light:e.imageDark}],style:e.heroStyle,src:e.image,alt:e.alt}):null,e.imageDark?l("img",{key:"dark",class:"vp-blog-hero-image dark",style:e.heroStyle,src:e.imageDark,alt:e.alt}):null]),l(se,{appear:!0,delay:.08},()=>e.text?l("h1",{class:"vp-blog-hero-title"},e.text):null),l("div",{class:"hitokoto"},[l("p",{class:"hitokoto-text"},l("span",n.value)),l("p",{class:"hitokoto-author",style:{opacity:n.value.length>4?1:0}},`——「${r.value}」`)])]}}),l3=B({__name:"BlogHero",setup(e){return(t,n)=>(ms(),_s(_t(o3),null,{heroBg:zo(()=>[we(_t(a3))]),heroInfo:zo(r=>[we(_t(i3),Yu(Es(r)),null,16)]),_:1}))}}),s3=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},c3=s3(l3,[["__file","BlogHero.vue"]]);const u3=["link","article","book","project","friend"],d3=B({name:"ProjectPanel",components:{ArticleIcon:Yr,BookIcon:ku,FriendIcon:Ru,LinkIcon:Pu,ProjectIcon:Ou},setup(){const e=Ee(),t=xn(),n=mr(),r=(o="",a="icon")=>u3.includes(o)?l(Ke(`${o}-icon`)):on(o)?l("img",{class:"vp-project-image",src:o,alt:a}):vo(o)?l("img",{class:"vp-project-image",src:Ie(o),alt:a}):l($e,{icon:o});return()=>{var o;return(o=e.value.projects)!=null&&o.length?l("div",{class:"vp-project-panel"},e.value.projects.map(({icon:a,link:i,name:s,desc:c},u)=>l("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>n(i)},[r(a,s),l("div",{class:"vp-project-name"},s),l("div",{class:"vp-project-desc"},c)]))):null}}});const f3=B({name:"BlogHome",setup(){const e=Ar();return()=>l("div",{class:"vp-page vp-blog"},[l(c3),l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,{appear:!0,delay:.16},()=>l(d3)),l(se,{appear:!0,delay:.24},()=>l(Ya,{items:e.value.items}))]),l(se,{appear:!0,delay:.16},()=>l(Tr,{key:"blog"}))]),l(se,{appear:!0,delay:.28},()=>l(br))])}}),p3=B({name:"BlogHome",setup(){return()=>l(_o,()=>l(f3))}});const Cu=B({name:"ArticleType",setup(){const e=ie(),t=bt(),n=le(),r=Ar(),o=Wa(),a=L(()=>{const i=n.value.blogLocales;return[{text:i.all,path:r.value.path},{text:i.star,path:o.value.path},...[].map(({key:s,path:c})=>({text:i[s],path:c.replace(/^\//,t.value)}))]});return()=>l("ul",{class:"vp-article-type-wrapper"},a.value.map(i=>l("li",{class:["vp-article-type",{active:i.path===e.value.path}]},l(Re,{to:i.path},()=>i.text))))}}),v3=B({name:"BlogPage",setup(){const e=go(),t=Ee(),n=ie(),r=Ar(),o=Wa(),a=L(()=>{const{key:i="",type:s}=t.value.blog||{};return i==="star"?o.value.items:s==="type"&&i?e.value.items:r.value.items});return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,()=>l(Cu)),l(se,{appear:!0,delay:.24},()=>l(Ya,{key:n.value.path,items:a.value}))]),l(se,{delay:.16},()=>l(Tr,{key:"blog"}))])))}});const h3=B({name:"TimelineItems",setup(){const e=Er(),t=le(),n=Ja(),r=L(()=>e.value.timeline||t.value.blogLocales.timelineTitle),o=L(()=>n.value.config.map(({year:a})=>({title:a.toString(),level:2,slug:a.toString(),children:[]})));return()=>l("div",{class:"timeline-wrapper"},l("ul",{class:"timeline-content"},[l(se,()=>l("li",{class:"motto"},r.value)),l(_u,{items:o.value}),n.value.config.map(({year:a,items:i},s)=>l(se,{appear:!0,delay:.08*(s+1),type:"group"},()=>[l("h3",{key:"title",id:a,class:"timeline-year-title"},l("span",a)),l("li",{key:"content",class:"timeline-year-list"},[l("ul",{class:"timeline-year-wrapper"},i.map(({date:c,info:u,path:d})=>l("li",{class:"timeline-item"},[l("span",{class:"timeline-date"},c),l(Re,{class:"timeline-title",to:d},()=>u[_e.title])])))])]))]))}}),m3=B({name:"Timeline",components:{ArticleType:Cu,CategoryList:Xa,TagList:Za},setup(){return()=>l(_o,()=>l("div",{class:"vp-page vp-blog"},l("div",{class:"blog-page-wrapper"},[l("main",{id:"main-content",class:"vp-blog-main"},[l(se,{appear:!0,delay:.24},()=>l(h3))]),l(se,{delay:.16},()=>l(Tr,{key:"blog"}))])))}});$p($e);const g3=Ye({enhance:({app:e,router:t})=>{const{scrollBehavior:n}=t.options;t.options.scrollBehavior=async(...r)=>(await Hc().wait(),n(...r)),j2(e),e.component("HopeIcon",$e),e.component("VPLink",Re),e.component("BloggerInfo",Qa)},setup:()=>{z2(),W2(),Yv()},layouts:{Layout:Vv,NotFound:Bv,BlogCategory:n3,BlogHome:p3,BlogType:v3,Timeline:m3}}),_3=Ye({setup(){ye(()=>{console.log(String.raw` ███╗ ███╗██████╗ ██╗ ██╗ ██████╗ ██████╗ ███████╗ ████╗ ████║██╔══██╗ ██║ ██║██╔═══██╗██╔══██╗██╔════╝ @@ -43,7 +43,7 @@ __ __ _ _ _ __/ | __/ | |___/ |___/ -`)})}}),Nr=[Vd,Rp,Bp,jp,Up,Qp,e2,l2,v2,m2,b2,O2,g3,_3],b3=[["v-8daa1a0e","/",{y:"h",t:"主页",i:"home"},["/README.md"]],["v-fd1808c0","/about/friend.html",{y:"p",t:"我的好友"},[":md"]],["v-2d175b02","/about/hobby.html",{d:1709466185e3,y:"a",t:""},[":md"]],["v-513bdf66","/about/me.html",{y:"p",t:"个人简介"},[":md"]],["v-0c86f055","/about/update.html",{y:"p",t:"更新内容"},[":md"]],["v-2d0a870d","/en/",{y:"h",t:"home",i:"home"},["/en/README.md"]],["v-5aa3d8ba","/en/intro.html",{d:1709466185e3,v:"/assets/images/cover3.jpg",r:{minutes:.04,words:12},y:"a",t:"Intro Page",i:"info"},[":md"]],["v-ed1de9c6","/informal/2305090935.html",{d:16834176e5,l:"2023年5月7日",c:["随笔","DDD"],g:["DDD"],o:!0,e:`

    DDD 领域驱动设计模型

    +`)})}}),Nr=[Vd,Rp,Bp,jp,Up,Qp,e2,l2,v2,m2,b2,O2,g3,_3],b3=[["v-8daa1a0e","/",{y:"h",t:"主页",i:"home"},["/README.md"]],["v-fd1808c0","/about/friend.html",{y:"p",t:"我的好友"},[":md"]],["v-2d175b02","/about/hobby.html",{d:1709971756e3,y:"a",t:""},[":md"]],["v-513bdf66","/about/me.html",{y:"p",t:"个人简介"},[":md"]],["v-0c86f055","/about/update.html",{y:"p",t:"更新内容"},[":md"]],["v-2d0a870d","/en/",{y:"h",t:"home",i:"home"},["/en/README.md"]],["v-5aa3d8ba","/en/intro.html",{d:1709971756e3,v:"/assets/images/cover3.jpg",r:{minutes:.04,words:12},y:"a",t:"Intro Page",i:"info"},[":md"]],["v-ed1de9c6","/informal/2305090935.html",{d:16834176e5,l:"2023年5月7日",c:["随笔","DDD"],g:["DDD"],o:!0,e:`

    DDD 领域驱动设计模型

    DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
    Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

    `,r:{minutes:5.1,words:1529},y:"a",t:"DDD 领域驱动设计模型"},[":md"]],["v-48e62a82","/informal/",{y:"p",t:"随笔"},["/informal/README.md"]],["v-79efb2b0","/note/algorithm/2301101203.html",{d:16753824e5,l:"2023年2月3日",c:["算法"],g:["排序"],o:!0,e:`

    基数(桶)排序

    @@ -68,7 +68,7 @@ Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和

    Siri ChatGPT 使用教程,将 Siri 接入 ChatGPT,直接语音唤醒,并且支持连续对话

    `,r:{minutes:.17,words:51},y:"a",t:"注册OpenAI账号"},[":md"]],["v-1ad96fc8","/note/other/2305090946.html",{d:16835904e5,l:"2023年5月9日",c:["工具"],g:["idea"],o:!0,e:`

    IDEA 常用插件

    IDEA 开发常用插件汇总,持续更新

    -`,r:{minutes:2.24,words:671},y:"a",t:"IDEA 常用插件"},[":md"]],["v-f0b271fa","/note/other/2307051931.html",{d:16885152e5,l:"2023年7月5日",c:["kubernetes"],g:["kubernetes"],o:!0,r:{minutes:6.65,words:1995},y:"a",t:"K8S + Flannel 公网IP搭建笔记"},[":md"]],["v-59c0c6b8","/note/other/2307062006.html",{d:1709466185e3,r:{minutes:1.89,words:568},y:"a",t:"博客整合Github Actions实现自动发布记录"},[":md"]],["v-249319d7","/note/structure/2301101200.html",{d:16834176e5,l:"2023年5月7日",c:["数据结构"],g:["栈"],o:!0,e:`

    栈(Stack)

    +`,r:{minutes:2.24,words:671},y:"a",t:"IDEA 常用插件"},[":md"]],["v-f0b271fa","/note/other/2307051931.html",{d:16885152e5,l:"2023年7月5日",c:["kubernetes"],g:["kubernetes"],o:!0,r:{minutes:6.65,words:1995},y:"a",t:"K8S + Flannel 公网IP搭建笔记"},[":md"]],["v-59c0c6b8","/note/other/2307062006.html",{d:1709971756e3,r:{minutes:1.89,words:568},y:"a",t:"博客整合Github Actions实现自动发布记录"},[":md"]],["v-249319d7","/note/structure/2301101200.html",{d:16834176e5,l:"2023年5月7日",c:["数据结构"],g:["栈"],o:!0,e:`

    栈(Stack)

    栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。

    `,r:{minutes:1.55,words:465},y:"a",t:"栈(Stack)"},[":md"]],["v-0115d5da","/note/structure/2305090929.html",{d:16771104e5,l:"2023年2月23日",c:["数据结构"],g:["跳表"],o:!0,e:`

    跳表(SkipList)

    跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

    @@ -140,12 +140,10 @@ Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负

    MySQL基础笔记系列

    `,r:{minutes:1.62,words:485},y:"a",t:"MySQL基础(四)",O:4},[":md"]],["v-0651bbf0","/note/db/mysql/basic/1910011304.html",{d:16834176e5,l:"2023年5月7日",c:["DB"],g:["MySQL","基础"],o:!0,e:`

    MySQL基础(五)

    MySQL基础笔记系列

    -`,r:{minutes:2.05,words:614},y:"a",t:"MySQL基础(五)",O:5},[":md"]],["v-113cc96c","/note/db/mysql/further/2305011002.html",{d:16831584e5,l:"2023年5月4日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

    深入理解mysql索引底层数据结构与算法

    -

    mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。

    -`,r:{minutes:5.15,words:1545},y:"a",t:"深入理解mysql索引底层数据结构与算法",O:1},[":md"]],["v-b8674064","/note/db/mysql/further/2305012001.html",{d:16832448e5,l:"2023年5月5日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

    MySQL索引

    +`,r:{minutes:2.05,words:614},y:"a",t:"MySQL基础(五)",O:5},[":md"]],["v-b8674064","/note/db/mysql/further/2305012001.html",{d:16832448e5,l:"2023年5月5日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

    MySQL索引

    索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

    在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

    -`,r:{minutes:9.24,words:2771},y:"a",t:"MySQL索引",O:2},[":md"]],["v-753dfaa7","/note/db/mysql/further/2305020500.html",{d:16833312e5,l:"2023年5月6日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

    Explain详解与索引最佳实践

    +`,r:{minutes:9.61,words:2882},y:"a",t:"MySQL索引",O:2},[":md"]],["v-753dfaa7","/note/db/mysql/further/2305020500.html",{d:16833312e5,l:"2023年5月6日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

    Explain详解与索引最佳实践

    Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置
    一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。

    如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。

    @@ -154,9 +152,15 @@ Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负
`,r:{minutes:4.85,words:1455},y:"a",t:"Explain详解与索引最佳实践",O:3},[":md"]],["v-76f2d346","/note/db/mysql/further/2305020501.html",{d:16834176e5,l:"2023年5月7日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

SQL底层执行原理

介绍SQL底层执行原理以及流程

-`,r:{minutes:5.84,words:1751},y:"a",t:"SQL底层执行原理",O:4},[":md"]],["v-1ae8d368","/note/db/mysql/further/2305032100.html",{d:1683504e6,l:"2023年5月8日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

索引优化实战

+`,r:{minutes:5.84,words:1751},y:"a",t:"SQL底层执行原理",O:5},[":md"]],["v-1ae8d368","/note/db/mysql/further/2305032100.html",{d:1683504e6,l:"2023年5月8日",c:["DB"],g:["MySQL","索引"],o:!0,e:`

索引优化实战

MYSQL 索引优化实战笔记

-`,r:{minutes:10.76,words:3229},y:"a",t:"索引优化实战",O:5},[":md"]],["v-06496550","/note/framework/spring/basic/2101202010.html",{d:155736e7,l:"2019年5月9日",c:["Spring"],g:["基础"],o:!0,e:`

Spring 基础(一)

+`,r:{minutes:10.76,words:3227},y:"a",t:"索引优化实战",O:4},[":md"]],["v-36bed06a","/note/db/mysql/further/2403061142.html",{d:17096832e5,l:"2024年3月6日",c:["DB"],g:["MySQL","事务"],o:!0,e:`

Mysql事务原理

+

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

+`,r:{minutes:6.13,words:1838},y:"a",t:"Mysql事务原理",O:6},[":md"]],["v-61e42302","/note/db/mysql/further/2403081801.html",{d:17096832e5,l:"2024年3月6日",c:["DB"],g:["MySQL","锁机制"],o:!0,e:`

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+`,r:{minutes:7.43,words:2228},y:"a",t:"锁机制",O:7},[":md"]],["v-06496550","/note/framework/spring/basic/2101202010.html",{d:155736e7,l:"2019年5月9日",c:["Spring"],g:["基础"],o:!0,e:`

Spring 基础(一)

Spring 基础笔记系列

`,r:{minutes:5.11,words:1532},y:"a",t:"Spring 基础(一)",O:1},[":md"]],["v-07fe3def","/note/framework/spring/basic/2101202011.html",{d:15574464e5,l:"2019年5月10日",c:["Spring"],g:["基础"],o:!0,e:`

Spring 基础(二)

Spring 基础笔记系列

@@ -214,4 +218,4 @@ Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负

DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

`,r:{minutes:4.42,words:1326},y:"a",t:"DelayQueue(无界阻塞队列)",O:4},[":md"]],["v-e12a9278","/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312130940.html",{d:17024256e5,l:"2023年12月13日",c:["并发"],g:["线程安全"],o:!0,e:`

线程池参数配置

记录线程池参数配置参考,以及配置的理论原理以及思路

-`,r:{minutes:6.28,words:1883},y:"a",t:"线程池参数配置",O:1},["/note/java/concurrency/线程池/2312130940.html","/note/java/concurrency/线程池/2312130940.md",":md"]],["v-783d7264","/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312141621.html",{d:1702512e6,l:"2023年12月14日",c:["并发"],g:["线程安全"],o:!0,r:{minutes:.08,words:23},y:"a",t:"线程池核心原理",O:2},["/note/java/concurrency/线程池/2312141621.html","/note/java/concurrency/线程池/2312141621.md",":md"]],["v-3706649a","/404.html",{y:"p",t:""},[]],["v-74bc627b","/about/",{y:"p",t:"About"},[]],["v-76bd8bc4","/note/algorithm/",{y:"p",t:"Algorithm"},[]],["v-15054f24","/note/",{y:"p",t:"Note"},[]],["v-8428717a","/note/other/",{y:"p",t:"Other"},[]],["v-3dbf9980","/note/structure/",{y:"p",t:"Structure"},[]],["v-7fe15663","/resources/",{y:"p",t:"Resources"},[]],["v-a945652a","/en/tutorial/docker/",{y:"p",t:"Docker"},[]],["v-65b85824","/en/tutorial/",{y:"p",t:"Tutorial"},[]],["v-e2850a42","/en/tutorial/linux/",{y:"p",t:"Linux"},[]],["v-19d7a44e","/en/tutorial/openai/",{y:"p",t:"Openai"},[]],["v-4857a26e","/en/tutorial/util/",{y:"p",t:"Util"},[]],["v-1b5ca3be","/note/db/redis/",{y:"p",t:"Redis"},[]],["v-34d631cd","/note/db/",{y:"p",t:"Db"},[]],["v-115f5fd1","/note/framework/mybatis/",{y:"p",t:"Mybatis"},[]],["v-716fffd6","/note/framework/",{y:"p",t:"Framework"},[]],["v-e025c652","/note/java/jvm/",{y:"p",t:"Jvm"},[]],["v-586621a9","/note/java/",{y:"p",t:"Java"},[]],["v-6a12e6a2","/note/microservices/dubbo/",{y:"p",t:"Dubbo"},[]],["v-3033cd1e","/note/microservices/",{y:"p",t:"Microservices"},[]],["v-5c743294","/note/microservices/zookeeper/",{y:"p",t:"Zookeeper"},[]],["v-7c89fed2","/note/net/basic/",{y:"p",t:"Basic"},[]],["v-65f4a756","/note/net/",{y:"p",t:"Net"},[]],["v-7a09c3ca","/note/db/mysql/basic/",{y:"p",t:"Basic"},[]],["v-2a2b904c","/note/db/mysql/",{y:"p",t:"Mysql"},[]],["v-9dd2bf36","/note/db/mysql/further/",{y:"p",t:"Further"},[]],["v-88fbbbe4","/note/framework/spring/basic/",{y:"p",t:"Basic"},[]],["v-0633ee0d","/note/framework/spring/",{y:"p",t:"Spring"},[]],["v-9a498f0c","/note/framework/spring/sourcecode/",{y:"p",t:"Sourcecode"},[]],["v-3fd38ebe","/note/framework/springboot/plugin/",{y:"p",t:"Plugin"},[]],["v-5b721b4a","/note/framework/springboot/",{y:"p",t:"Springboot"},[]],["v-02880d78","/note/framework/springmvc/basic/",{y:"p",t:"Basic"},[]],["v-557d9e12","/note/framework/springmvc/",{y:"p",t:"Springmvc"},[]],["v-519d06c2","/note/java/concurrency/atomic/",{y:"p",t:"Atomic"},[]],["v-214de1b6","/note/java/concurrency/",{y:"p",t:"Concurrency"},[]],["v-c37cff42","/note/java/concurrency/lock/",{y:"p",t:"Lock"},[]],["v-32cc68a3","/note/java/concurrency/queue/",{y:"p",t:"Queue"},[]],["v-657b4fa8","/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/",{y:"p",t:"线程池"},["/note/java/concurrency/线程池/"]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-60649a06","/category/%E9%9A%8F%E7%AC%94/",{y:"p",t:"随笔 分类",I:0},["/category/随笔/"]],["v-b313b7ce","/tag/ddd/",{y:"p",t:"标签: DDD",I:0},[]],["v-506407f4","/en/article/",{y:"p",t:"Articles",I:0},[]],["v-37a8c5a0","/en/star/",{y:"p",t:"Star",I:0},[]],["v-0379cba1","/en/timeline/",{y:"p",t:"Timeline",I:0},[]],["v-65ee64e3","/category/ddd/",{y:"p",t:"DDD 分类",I:0},[]],["v-5d93e6df","/tag/%E6%8E%92%E5%BA%8F/",{y:"p",t:"标签: 排序",I:0},["/tag/排序/"]],["v-06be9332","/category/%E7%AE%97%E6%B3%95/",{y:"p",t:"算法 分类",I:0},["/category/算法/"]],["v-87044d8a","/tag/%E5%A4%9A%E8%BE%B9%E5%BD%A2/",{y:"p",t:"标签: 多边形",I:0},["/tag/多边形/"]],["v-f56a87fe","/category/%E5%9C%B0%E5%9B%BE/",{y:"p",t:"地图 分类",I:0},["/category/地图/"]],["v-6f232062","/tag/%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%8F%B7/",{y:"p",t:"标签: 微信服务号",I:0},["/tag/微信服务号/"]],["v-4880d88d","/category/%E5%BE%AE%E4%BF%A1/",{y:"p",t:"微信 分类",I:0},["/category/微信/"]],["v-21f6c076","/tag/layui/",{y:"p",t:"标签: LayUI",I:0},[]],["v-e9c7e408","/category/%E5%89%8D%E7%AB%AF/",{y:"p",t:"前端 分类",I:0},["/category/前端/"]],["v-6106c001","/tag/docker/",{y:"p",t:"标签: docker",I:0},[]],["v-27fb5a12","/category/docker/",{y:"p",t:"docker 分类",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: linux",I:0},[]],["v-4f7b1987","/category/%E6%95%99%E7%A8%8B/",{y:"p",t:"教程 分类",I:0},["/category/教程/"]],["v-ae84bf22","/tag/openai/",{y:"p",t:"标签: OpenAI",I:0},[]],["v-72c4ce0d","/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",{y:"p",t:"人工智能 分类",I:0},["/category/人工智能/"]],["v-3a8afeec","/tag/chatgpt/",{y:"p",t:"标签: ChatGPT",I:0},[]],["v-14145d44","/category/%E5%B7%A5%E5%85%B7/",{y:"p",t:"工具 分类",I:0},["/category/工具/"]],["v-2894de8a","/tag/idea/",{y:"p",t:"标签: idea",I:0},[]],["v-209ce691","/category/kubernetes/",{y:"p",t:"kubernetes 分类",I:0},[]],["v-742fbe9b","/tag/kubernetes/",{y:"p",t:"标签: kubernetes",I:0},[]],["v-4f178b9c","/category/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/",{y:"p",t:"数据结构 分类",I:0},["/category/数据结构/"]],["v-b6d14f14","/tag/%E6%A0%88/",{y:"p",t:"标签: 栈",I:0},["/tag/栈/"]],["v-3d183459","/category/db/",{y:"p",t:"DB 分类",I:0},[]],["v-36899bd6","/tag/%E8%B7%B3%E8%A1%A8/",{y:"p",t:"标签: 跳表",I:0},["/tag/跳表/"]],["v-49f5e4d4","/category/mybatis/",{y:"p",t:"MyBatis 分类",I:0},[]],["v-0d1f4c3c","/tag/redis/",{y:"p",t:"标签: Redis",I:0},[]],["v-5831b135","/category/java/",{y:"p",t:"Java 分类",I:0},[]],["v-14748cc9","/tag/%E5%9F%BA%E7%A1%80/",{y:"p",t:"标签: 基础",I:0},["/tag/基础/"]],["v-5bd10ded","/category/%E5%BE%AE%E6%9C%8D%E5%8A%A1/",{y:"p",t:"微服务 分类",I:0},["/category/微服务/"]],["v-b30dba08","/tag/jvm/",{y:"p",t:"标签: JVM",I:0},[]],["v-65f4ef02","/category/rpc/",{y:"p",t:"RPC 分类",I:0},[]],["v-2e8071d8","/tag/%E7%B1%BB%E5%8A%A0%E8%BD%BD/",{y:"p",t:"标签: 类加载",I:0},["/tag/类加载/"]],["v-65f5a886","/category/tcp/",{y:"p",t:"TCP 分类",I:0},[]],["v-28c41ed6","/tag/lock/",{y:"p",t:"标签: Lock",I:0},[]],["v-3d1848d0","/category/ip/",{y:"p",t:"IP 分类",I:0},[]],["v-0da0c1c5","/tag/gc/",{y:"p",t:"标签: GC",I:0},[]],["v-08073caa","/category/spring/",{y:"p",t:"Spring 分类",I:0},[]],["v-3b261482","/tag/dubbo/",{y:"p",t:"标签: dubbo",I:0},[]],["v-0667aa78","/category/springboot/",{y:"p",t:"SpringBoot 分类",I:0},[]],["v-2671299e","/tag/zookeeper/",{y:"p",t:"标签: zookeeper",I:0},[]],["v-424a813a","/category/springmvc/",{y:"p",t:"SpringMVC 分类",I:0},[]],["v-2bce0156","/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/",{y:"p",t:"标签: 网络编程",I:0},["/tag/网络编程/"]],["v-573729ca","/category/%E5%B9%B6%E5%8F%91/",{y:"p",t:"并发 分类",I:0},["/category/并发/"]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-5decfa84","/en/category/",{y:"p",t:"Category",I:0},[]],["v-6ebee387","/tag/%E7%B4%A2%E5%BC%95/",{y:"p",t:"标签: 索引",I:0},["/tag/索引/"]],["v-437f3f0b","/en/category/docker/",{y:"p",t:"docker Category",I:0},[]],["v-07f91f24","/tag/%E6%BA%90%E7%A0%81/",{y:"p",t:"标签: 源码",I:0},["/tag/源码/"]],["v-8d9af0ca","/en/category/%E6%95%99%E7%A8%8B/",{y:"p",t:"教程 Category",I:0},["/en/category/教程/"]],["v-50c2828c","/tag/%E6%8F%92%E4%BB%B6/",{y:"p",t:"标签: 插件",I:0},["/tag/插件/"]],["v-77e741be","/en/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",{y:"p",t:"人工智能 Category",I:0},["/en/category/人工智能/"]],["v-20b8e9fc","/tag/threadlocal/",{y:"p",t:"标签: ThreadLocal",I:0},[]],["v-5fad3f72","/en/category/%E5%B7%A5%E5%85%B7/",{y:"p",t:"工具 Category",I:0},["/en/category/工具/"]],["v-68f34d54","/tag/atomic/",{y:"p",t:"标签: Atomic",I:0},[]],["v-721d1794","/tag/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/",{y:"p",t:"标签: 线程安全",I:0},["/tag/线程安全/"]],["v-b30dc3f6","/tag/juc/",{y:"p",t:"标签: JUC",I:0},[]],["v-b3160ccc","/tag/aqs/",{y:"p",t:"标签: AQS",I:0},[]],["v-0ec4cde8","/tag/queue/",{y:"p",t:"标签: Queue",I:0},[]],["v-075c6c62","/en/tag/",{y:"p",t:"Tag",I:0},[]],["v-6943976d","/en/tag/docker/",{y:"p",t:"Tag: docker",I:0},[]],["v-31c4e89d","/en/tag/linux/",{y:"p",t:"Tag: linux",I:0},[]],["v-9e0b104a","/en/tag/openai/",{y:"p",t:"Tag: OpenAI",I:0},[]],["v-3bced2c4","/en/tag/chatgpt/",{y:"p",t:"Tag: ChatGPT",I:0},[]],["v-2ab8a0f6","/en/tag/idea/",{y:"p",t:"Tag: idea",I:0},[]]];var Al=B({name:"Vuepress",setup(){const e=Rd();return()=>l(e.value)}}),y3=()=>b3.reduce((e,[t,n,r,o])=>(e.push({name:t,path:n,component:Al,meta:r},{path:n.endsWith("/")?n+"index.html":n.substring(0,n.length-5),redirect:n},...o.map(a=>({path:a===":md"?n.substring(0,n.length-5)+".md":a,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:Al}]),E3=pf,L3=()=>{const e=Bf({history:E3(Da("/")),routes:y3(),scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var r;(t.path!==n.path||n===Tt)&&([t.meta._data]=await Promise.all([Lt.resolvePageData(t.name),(r=Cs[t.name])==null?void 0:r.__asyncLoader()]))}),e},T3=e=>{e.component("ClientOnly",pr),e.component("Content",js)},A3=(e,t,n)=>{const r=il(()=>t.currentRoute.value.path),o=il(()=>Lt.resolveRouteLocale(pn.value.locales,r.value)),a=ho(r,()=>t.currentRoute.value.meta._data),i=L(()=>Lt.resolveLayouts(n)),s=L(()=>Lt.resolveSiteLocaleData(pn.value,o.value)),c=L(()=>Lt.resolvePageFrontmatter(a.value)),u=L(()=>Lt.resolvePageHeadTitle(a.value,s.value)),d=L(()=>Lt.resolvePageHead(u.value,c.value,s.value)),p=L(()=>Lt.resolvePageLang(a.value,s.value)),v=L(()=>Lt.resolvePageLayout(a.value,i.value));return e.provide(wd,i),e.provide(Vs,a),e.provide(Ms,c),e.provide(Od,u),e.provide(Bs,d),e.provide($s,p),e.provide(Ns,v),e.provide(Sa,o),e.provide(Fs,s),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>c.value},$head:{get:()=>d.value},$headTitle:{get:()=>u.value},$lang:{get:()=>p.value},$page:{get:()=>a.value},$routeLocale:{get:()=>o.value},$site:{get:()=>pn.value},$siteLocale:{get:()=>s.value},$withBase:{get:()=>Ie}}),{layouts:i,pageData:a,pageFrontmatter:c,pageHead:d,pageHeadTitle:u,pageLang:p,pageLayout:v,routeLocale:o,siteData:pn,siteLocaleData:s}},I3=()=>{const e=Pd(),t=co(),n=Y([]),r=()=>{e.value.forEach(a=>{const i=w3(a);i&&n.value.push(i)})},o=()=>{document.documentElement.lang=t.value,n.value.forEach(a=>{a.parentNode===document.head&&document.head.removeChild(a)}),n.value.splice(0,n.value.length),e.value.forEach(a=>{const i=k3(a);i!==null&&(document.head.appendChild(i),n.value.push(i))})};ct(Sd,o),ye(()=>{r(),o(),ve(()=>e.value,o)})},w3=([e,t,n=""])=>{const r=Object.entries(t).map(([s,c])=>pe(c)?`[${s}=${JSON.stringify(c)}]`:c===!0?`[${s}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(s=>s.innerText===n)||null},k3=([e,t,n])=>{if(!pe(e))return null;const r=document.createElement(e);return so(t)&&Object.entries(t).forEach(([o,a])=>{pe(a)?r.setAttribute(o,a):a===!0&&r.setAttribute(o,"")}),pe(n)&&r.appendChild(document.createTextNode(n)),r},P3=vd,O3=async()=>{var n;const e=P3({name:"VuepressApp",setup(){var r;I3();for(const o of Nr)(r=o.setup)==null||r.call(o);return()=>[l(Zs),...Nr.flatMap(({rootComponents:o=[]})=>o.map(a=>l(a)))]}}),t=L3();T3(e),A3(e,t,Nr);for(const r of Nr)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:pn}));return e.use(t),{app:e,router:t}};O3().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{s3 as _,ys as a,Ls as b,R3 as c,O3 as createVueApp,S3 as d,D3 as e,we as f,Es as g,Yu as n,ms as o,Ke as r,zo as w}; +`,r:{minutes:6.28,words:1883},y:"a",t:"线程池参数配置",O:1},["/note/java/concurrency/线程池/2312130940.html","/note/java/concurrency/线程池/2312130940.md",":md"]],["v-783d7264","/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/2312141621.html",{d:1702512e6,l:"2023年12月14日",c:["并发"],g:["线程安全"],o:!0,r:{minutes:.08,words:23},y:"a",t:"线程池核心原理",O:2},["/note/java/concurrency/线程池/2312141621.html","/note/java/concurrency/线程池/2312141621.md",":md"]],["v-3706649a","/404.html",{y:"p",t:""},[]],["v-74bc627b","/about/",{y:"p",t:"About"},[]],["v-76bd8bc4","/note/algorithm/",{y:"p",t:"Algorithm"},[]],["v-15054f24","/note/",{y:"p",t:"Note"},[]],["v-8428717a","/note/other/",{y:"p",t:"Other"},[]],["v-3dbf9980","/note/structure/",{y:"p",t:"Structure"},[]],["v-7fe15663","/resources/",{y:"p",t:"Resources"},[]],["v-a945652a","/en/tutorial/docker/",{y:"p",t:"Docker"},[]],["v-65b85824","/en/tutorial/",{y:"p",t:"Tutorial"},[]],["v-e2850a42","/en/tutorial/linux/",{y:"p",t:"Linux"},[]],["v-19d7a44e","/en/tutorial/openai/",{y:"p",t:"Openai"},[]],["v-4857a26e","/en/tutorial/util/",{y:"p",t:"Util"},[]],["v-1b5ca3be","/note/db/redis/",{y:"p",t:"Redis"},[]],["v-34d631cd","/note/db/",{y:"p",t:"Db"},[]],["v-115f5fd1","/note/framework/mybatis/",{y:"p",t:"Mybatis"},[]],["v-716fffd6","/note/framework/",{y:"p",t:"Framework"},[]],["v-e025c652","/note/java/jvm/",{y:"p",t:"Jvm"},[]],["v-586621a9","/note/java/",{y:"p",t:"Java"},[]],["v-6a12e6a2","/note/microservices/dubbo/",{y:"p",t:"Dubbo"},[]],["v-3033cd1e","/note/microservices/",{y:"p",t:"Microservices"},[]],["v-5c743294","/note/microservices/zookeeper/",{y:"p",t:"Zookeeper"},[]],["v-7c89fed2","/note/net/basic/",{y:"p",t:"Basic"},[]],["v-65f4a756","/note/net/",{y:"p",t:"Net"},[]],["v-7a09c3ca","/note/db/mysql/basic/",{y:"p",t:"Basic"},[]],["v-2a2b904c","/note/db/mysql/",{y:"p",t:"Mysql"},[]],["v-9dd2bf36","/note/db/mysql/further/",{y:"p",t:"Further"},[]],["v-88fbbbe4","/note/framework/spring/basic/",{y:"p",t:"Basic"},[]],["v-0633ee0d","/note/framework/spring/",{y:"p",t:"Spring"},[]],["v-9a498f0c","/note/framework/spring/sourcecode/",{y:"p",t:"Sourcecode"},[]],["v-3fd38ebe","/note/framework/springboot/plugin/",{y:"p",t:"Plugin"},[]],["v-5b721b4a","/note/framework/springboot/",{y:"p",t:"Springboot"},[]],["v-02880d78","/note/framework/springmvc/basic/",{y:"p",t:"Basic"},[]],["v-557d9e12","/note/framework/springmvc/",{y:"p",t:"Springmvc"},[]],["v-519d06c2","/note/java/concurrency/atomic/",{y:"p",t:"Atomic"},[]],["v-214de1b6","/note/java/concurrency/",{y:"p",t:"Concurrency"},[]],["v-c37cff42","/note/java/concurrency/lock/",{y:"p",t:"Lock"},[]],["v-32cc68a3","/note/java/concurrency/queue/",{y:"p",t:"Queue"},[]],["v-657b4fa8","/note/java/concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0/",{y:"p",t:"线程池"},["/note/java/concurrency/线程池/"]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-60649a06","/category/%E9%9A%8F%E7%AC%94/",{y:"p",t:"随笔 分类",I:0},["/category/随笔/"]],["v-b313b7ce","/tag/ddd/",{y:"p",t:"标签: DDD",I:0},[]],["v-506407f4","/en/article/",{y:"p",t:"Articles",I:0},[]],["v-37a8c5a0","/en/star/",{y:"p",t:"Star",I:0},[]],["v-0379cba1","/en/timeline/",{y:"p",t:"Timeline",I:0},[]],["v-65ee64e3","/category/ddd/",{y:"p",t:"DDD 分类",I:0},[]],["v-5d93e6df","/tag/%E6%8E%92%E5%BA%8F/",{y:"p",t:"标签: 排序",I:0},["/tag/排序/"]],["v-06be9332","/category/%E7%AE%97%E6%B3%95/",{y:"p",t:"算法 分类",I:0},["/category/算法/"]],["v-87044d8a","/tag/%E5%A4%9A%E8%BE%B9%E5%BD%A2/",{y:"p",t:"标签: 多边形",I:0},["/tag/多边形/"]],["v-f56a87fe","/category/%E5%9C%B0%E5%9B%BE/",{y:"p",t:"地图 分类",I:0},["/category/地图/"]],["v-6f232062","/tag/%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%8F%B7/",{y:"p",t:"标签: 微信服务号",I:0},["/tag/微信服务号/"]],["v-4880d88d","/category/%E5%BE%AE%E4%BF%A1/",{y:"p",t:"微信 分类",I:0},["/category/微信/"]],["v-21f6c076","/tag/layui/",{y:"p",t:"标签: LayUI",I:0},[]],["v-e9c7e408","/category/%E5%89%8D%E7%AB%AF/",{y:"p",t:"前端 分类",I:0},["/category/前端/"]],["v-6106c001","/tag/docker/",{y:"p",t:"标签: docker",I:0},[]],["v-27fb5a12","/category/docker/",{y:"p",t:"docker 分类",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: linux",I:0},[]],["v-4f7b1987","/category/%E6%95%99%E7%A8%8B/",{y:"p",t:"教程 分类",I:0},["/category/教程/"]],["v-ae84bf22","/tag/openai/",{y:"p",t:"标签: OpenAI",I:0},[]],["v-72c4ce0d","/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",{y:"p",t:"人工智能 分类",I:0},["/category/人工智能/"]],["v-3a8afeec","/tag/chatgpt/",{y:"p",t:"标签: ChatGPT",I:0},[]],["v-14145d44","/category/%E5%B7%A5%E5%85%B7/",{y:"p",t:"工具 分类",I:0},["/category/工具/"]],["v-2894de8a","/tag/idea/",{y:"p",t:"标签: idea",I:0},[]],["v-209ce691","/category/kubernetes/",{y:"p",t:"kubernetes 分类",I:0},[]],["v-742fbe9b","/tag/kubernetes/",{y:"p",t:"标签: kubernetes",I:0},[]],["v-4f178b9c","/category/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/",{y:"p",t:"数据结构 分类",I:0},["/category/数据结构/"]],["v-b6d14f14","/tag/%E6%A0%88/",{y:"p",t:"标签: 栈",I:0},["/tag/栈/"]],["v-3d183459","/category/db/",{y:"p",t:"DB 分类",I:0},[]],["v-36899bd6","/tag/%E8%B7%B3%E8%A1%A8/",{y:"p",t:"标签: 跳表",I:0},["/tag/跳表/"]],["v-49f5e4d4","/category/mybatis/",{y:"p",t:"MyBatis 分类",I:0},[]],["v-0d1f4c3c","/tag/redis/",{y:"p",t:"标签: Redis",I:0},[]],["v-5831b135","/category/java/",{y:"p",t:"Java 分类",I:0},[]],["v-14748cc9","/tag/%E5%9F%BA%E7%A1%80/",{y:"p",t:"标签: 基础",I:0},["/tag/基础/"]],["v-5bd10ded","/category/%E5%BE%AE%E6%9C%8D%E5%8A%A1/",{y:"p",t:"微服务 分类",I:0},["/category/微服务/"]],["v-b30dba08","/tag/jvm/",{y:"p",t:"标签: JVM",I:0},[]],["v-65f4ef02","/category/rpc/",{y:"p",t:"RPC 分类",I:0},[]],["v-2e8071d8","/tag/%E7%B1%BB%E5%8A%A0%E8%BD%BD/",{y:"p",t:"标签: 类加载",I:0},["/tag/类加载/"]],["v-65f5a886","/category/tcp/",{y:"p",t:"TCP 分类",I:0},[]],["v-28c41ed6","/tag/lock/",{y:"p",t:"标签: Lock",I:0},[]],["v-3d1848d0","/category/ip/",{y:"p",t:"IP 分类",I:0},[]],["v-0da0c1c5","/tag/gc/",{y:"p",t:"标签: GC",I:0},[]],["v-08073caa","/category/spring/",{y:"p",t:"Spring 分类",I:0},[]],["v-3b261482","/tag/dubbo/",{y:"p",t:"标签: dubbo",I:0},[]],["v-0667aa78","/category/springboot/",{y:"p",t:"SpringBoot 分类",I:0},[]],["v-2671299e","/tag/zookeeper/",{y:"p",t:"标签: zookeeper",I:0},[]],["v-424a813a","/category/springmvc/",{y:"p",t:"SpringMVC 分类",I:0},[]],["v-2bce0156","/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/",{y:"p",t:"标签: 网络编程",I:0},["/tag/网络编程/"]],["v-573729ca","/category/%E5%B9%B6%E5%8F%91/",{y:"p",t:"并发 分类",I:0},["/category/并发/"]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-5decfa84","/en/category/",{y:"p",t:"Category",I:0},[]],["v-6ebee387","/tag/%E7%B4%A2%E5%BC%95/",{y:"p",t:"标签: 索引",I:0},["/tag/索引/"]],["v-437f3f0b","/en/category/docker/",{y:"p",t:"docker Category",I:0},[]],["v-26374ab8","/tag/%E4%BA%8B%E5%8A%A1/",{y:"p",t:"标签: 事务",I:0},["/tag/事务/"]],["v-8d9af0ca","/en/category/%E6%95%99%E7%A8%8B/",{y:"p",t:"教程 Category",I:0},["/en/category/教程/"]],["v-64c651c8","/tag/%E9%94%81%E6%9C%BA%E5%88%B6/",{y:"p",t:"标签: 锁机制",I:0},["/tag/锁机制/"]],["v-77e741be","/en/category/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/",{y:"p",t:"人工智能 Category",I:0},["/en/category/人工智能/"]],["v-07f91f24","/tag/%E6%BA%90%E7%A0%81/",{y:"p",t:"标签: 源码",I:0},["/tag/源码/"]],["v-5fad3f72","/en/category/%E5%B7%A5%E5%85%B7/",{y:"p",t:"工具 Category",I:0},["/en/category/工具/"]],["v-50c2828c","/tag/%E6%8F%92%E4%BB%B6/",{y:"p",t:"标签: 插件",I:0},["/tag/插件/"]],["v-20b8e9fc","/tag/threadlocal/",{y:"p",t:"标签: ThreadLocal",I:0},[]],["v-68f34d54","/tag/atomic/",{y:"p",t:"标签: Atomic",I:0},[]],["v-721d1794","/tag/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8/",{y:"p",t:"标签: 线程安全",I:0},["/tag/线程安全/"]],["v-b30dc3f6","/tag/juc/",{y:"p",t:"标签: JUC",I:0},[]],["v-b3160ccc","/tag/aqs/",{y:"p",t:"标签: AQS",I:0},[]],["v-0ec4cde8","/tag/queue/",{y:"p",t:"标签: Queue",I:0},[]],["v-075c6c62","/en/tag/",{y:"p",t:"Tag",I:0},[]],["v-6943976d","/en/tag/docker/",{y:"p",t:"Tag: docker",I:0},[]],["v-31c4e89d","/en/tag/linux/",{y:"p",t:"Tag: linux",I:0},[]],["v-9e0b104a","/en/tag/openai/",{y:"p",t:"Tag: OpenAI",I:0},[]],["v-3bced2c4","/en/tag/chatgpt/",{y:"p",t:"Tag: ChatGPT",I:0},[]],["v-2ab8a0f6","/en/tag/idea/",{y:"p",t:"Tag: idea",I:0},[]]];var Tl=B({name:"Vuepress",setup(){const e=Rd();return()=>l(e.value)}}),y3=()=>b3.reduce((e,[t,n,r,o])=>(e.push({name:t,path:n,component:Tl,meta:r},{path:n.endsWith("/")?n+"index.html":n.substring(0,n.length-5),redirect:n},...o.map(a=>({path:a===":md"?n.substring(0,n.length-5)+".md":a,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:Tl}]),E3=pf,L3=()=>{const e=Bf({history:E3(Da("/")),routes:y3(),scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var r;(t.path!==n.path||n===At)&&([t.meta._data]=await Promise.all([Lt.resolvePageData(t.name),(r=Cs[t.name])==null?void 0:r.__asyncLoader()]))}),e},A3=e=>{e.component("ClientOnly",pr),e.component("Content",js)},T3=(e,t,n)=>{const r=il(()=>t.currentRoute.value.path),o=il(()=>Lt.resolveRouteLocale(pn.value.locales,r.value)),a=ho(r,()=>t.currentRoute.value.meta._data),i=L(()=>Lt.resolveLayouts(n)),s=L(()=>Lt.resolveSiteLocaleData(pn.value,o.value)),c=L(()=>Lt.resolvePageFrontmatter(a.value)),u=L(()=>Lt.resolvePageHeadTitle(a.value,s.value)),d=L(()=>Lt.resolvePageHead(u.value,c.value,s.value)),p=L(()=>Lt.resolvePageLang(a.value,s.value)),v=L(()=>Lt.resolvePageLayout(a.value,i.value));return e.provide(wd,i),e.provide(Vs,a),e.provide(Ms,c),e.provide(Od,u),e.provide(Bs,d),e.provide($s,p),e.provide(Ns,v),e.provide(Sa,o),e.provide(Fs,s),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>c.value},$head:{get:()=>d.value},$headTitle:{get:()=>u.value},$lang:{get:()=>p.value},$page:{get:()=>a.value},$routeLocale:{get:()=>o.value},$site:{get:()=>pn.value},$siteLocale:{get:()=>s.value},$withBase:{get:()=>Ie}}),{layouts:i,pageData:a,pageFrontmatter:c,pageHead:d,pageHeadTitle:u,pageLang:p,pageLayout:v,routeLocale:o,siteData:pn,siteLocaleData:s}},I3=()=>{const e=Pd(),t=co(),n=Y([]),r=()=>{e.value.forEach(a=>{const i=w3(a);i&&n.value.push(i)})},o=()=>{document.documentElement.lang=t.value,n.value.forEach(a=>{a.parentNode===document.head&&document.head.removeChild(a)}),n.value.splice(0,n.value.length),e.value.forEach(a=>{const i=k3(a);i!==null&&(document.head.appendChild(i),n.value.push(i))})};ct(Sd,o),ye(()=>{r(),o(),ve(()=>e.value,o)})},w3=([e,t,n=""])=>{const r=Object.entries(t).map(([s,c])=>pe(c)?`[${s}=${JSON.stringify(c)}]`:c===!0?`[${s}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(s=>s.innerText===n)||null},k3=([e,t,n])=>{if(!pe(e))return null;const r=document.createElement(e);return so(t)&&Object.entries(t).forEach(([o,a])=>{pe(a)?r.setAttribute(o,a):a===!0&&r.setAttribute(o,"")}),pe(n)&&r.appendChild(document.createTextNode(n)),r},P3=vd,O3=async()=>{var n;const e=P3({name:"VuepressApp",setup(){var r;I3();for(const o of Nr)(r=o.setup)==null||r.call(o);return()=>[l(Zs),...Nr.flatMap(({rootComponents:o=[]})=>o.map(a=>l(a)))]}}),t=L3();A3(e),T3(e,t,Nr);for(const r of Nr)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:pn}));return e.use(t),{app:e,router:t}};O3().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{s3 as _,ys as a,Ls as b,R3 as c,O3 as createVueApp,S3 as d,D3 as e,we as f,Es as g,Yu as n,ms as o,Ke as r,zo as w}; diff --git a/assets/friend.html-741bf052.js b/assets/friend.html-3da35bba.js similarity index 81% rename from assets/friend.html-741bf052.js rename to assets/friend.html-3da35bba.js index 089c95b3..64fef47d 100644 --- a/assets/friend.html-741bf052.js +++ b/assets/friend.html-3da35bba.js @@ -1 +1 @@ -import{_ as t,o as a,c as r,a as e,b as c}from"./app-1efcbe9f.js";const n={},o=e("h1",{id:"我的好友",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#我的好友","aria-hidden":"true"},"#"),c(" 我的好友")],-1),s=[o];function _(d,i){return a(),r("div",null,s)}const h=t(n,[["render",_],["__file","friend.html.vue"]]);export{h as default}; +import{_ as t,o as a,c as r,a as e,b as c}from"./app-6a63891c.js";const n={},o=e("h1",{id:"我的好友",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#我的好友","aria-hidden":"true"},"#"),c(" 我的好友")],-1),s=[o];function _(d,i){return a(),r("div",null,s)}const h=t(n,[["render",_],["__file","friend.html.vue"]]);export{h as default}; diff --git a/assets/friend.html-a1c44619.js b/assets/friend.html-80311cd2.js similarity index 69% rename from assets/friend.html-a1c44619.js rename to assets/friend.html-80311cd2.js index fa77a990..9b54fc0e 100644 --- a/assets/friend.html-a1c44619.js +++ b/assets/friend.html-80311cd2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-fd1808c0","path":"/about/friend.html","title":"我的好友","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover1.jpg"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/friend.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-fd1808c0","path":"/about/friend.html","title":"我的好友","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover1.jpg"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/friend.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/hobby.html-32792606.js b/assets/hobby.html-2d902f7a.js similarity index 71% rename from assets/hobby.html-32792606.js rename to assets/hobby.html-2d902f7a.js index 917feb3d..fb1da8b1 100644 --- a/assets/hobby.html-32792606.js +++ b/assets/hobby.html-2d902f7a.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-2d175b02","path":"/about/hobby.html","title":"","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0,"words":0},"filePathRelative":"about/hobby.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-2d175b02","path":"/about/hobby.html","title":"","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0,"words":0},"filePathRelative":"about/hobby.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/hobby.html-e4d044b2.js b/assets/hobby.html-b0beb222.js similarity index 63% rename from assets/hobby.html-e4d044b2.js rename to assets/hobby.html-b0beb222.js index ac8c5be6..ec778978 100644 --- a/assets/hobby.html-e4d044b2.js +++ b/assets/hobby.html-b0beb222.js @@ -1 +1 @@ -import{_ as e,o as c,c as o}from"./app-1efcbe9f.js";const t={};function _(r,n){return c(),o("div")}const a=e(t,[["render",_],["__file","hobby.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as o}from"./app-6a63891c.js";const t={};function _(r,n){return c(),o("div")}const a=e(t,[["render",_],["__file","hobby.html.vue"]]);export{a as default}; diff --git a/assets/index.html-013e7589.js b/assets/index.html-02fae00a.js similarity index 71% rename from assets/index.html-013e7589.js rename to assets/index.html-02fae00a.js index a1de75c9..584f0ce3 100644 --- a/assets/index.html-013e7589.js +++ b/assets/index.html-02fae00a.js @@ -1 +1 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-0cb0f3a4.js b/assets/index.html-06351497.js similarity index 63% rename from assets/index.html-0cb0f3a4.js rename to assets/index.html-06351497.js index 7c6f802a..68cc8d39 100644 --- a/assets/index.html-0cb0f3a4.js +++ b/assets/index.html-06351497.js @@ -1 +1 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c81dddfc.js b/assets/index.html-079ca76b.js similarity index 80% rename from assets/index.html-c81dddfc.js rename to assets/index.html-079ca76b.js index 49ebb4bd..a7e2b5d5 100644 --- a/assets/index.html-c81dddfc.js +++ b/assets/index.html-079ca76b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-48e62a82","path":"/informal/","title":"随笔","lang":"zh-CN","frontmatter":{"title":"随笔","index":false,"article":false},"headers":[{"level":2,"title":"随笔随笔,就是随便比比","slug":"随笔随笔-就是随便比比","link":"#随笔随笔-就是随便比比","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.06,"words":19},"filePathRelative":"informal/README.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-48e62a82","path":"/informal/","title":"随笔","lang":"zh-CN","frontmatter":{"title":"随笔","index":false,"article":false},"headers":[{"level":2,"title":"随笔随笔,就是随便比比","slug":"随笔随笔-就是随便比比","link":"#随笔随笔-就是随便比比","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.06,"words":19},"filePathRelative":"informal/README.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-06d1eb22.js b/assets/index.html-0bf6ed17.js similarity index 71% rename from assets/index.html-06d1eb22.js rename to assets/index.html-0bf6ed17.js index a1de75c9..584f0ce3 100644 --- a/assets/index.html-06d1eb22.js +++ b/assets/index.html-0bf6ed17.js @@ -1 +1 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-165c0158.js b/assets/index.html-0d28deb1.js similarity index 63% rename from assets/index.html-165c0158.js rename to assets/index.html-0d28deb1.js index 7c6f802a..68cc8d39 100644 --- a/assets/index.html-165c0158.js +++ b/assets/index.html-0d28deb1.js @@ -1 +1 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-139c0752.js b/assets/index.html-128c30d4.js similarity index 71% rename from assets/index.html-139c0752.js rename to assets/index.html-128c30d4.js index a1de75c9..584f0ce3 100644 --- a/assets/index.html-139c0752.js +++ b/assets/index.html-128c30d4.js @@ -1 +1 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-16a33c6b.js b/assets/index.html-1529aa11.js similarity index 63% rename from assets/index.html-16a33c6b.js rename to assets/index.html-1529aa11.js index 7c6f802a..68cc8d39 100644 --- a/assets/index.html-16a33c6b.js +++ b/assets/index.html-1529aa11.js @@ -1 +1 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-19e06afe.js b/assets/index.html-19072b4f.js similarity index 63% rename from assets/index.html-19e06afe.js rename to assets/index.html-19072b4f.js index 7c6f802a..68cc8d39 100644 --- a/assets/index.html-19e06afe.js +++ b/assets/index.html-19072b4f.js @@ -1 +1 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-278160ed.js b/assets/index.html-196a39b3.js similarity index 71% rename from assets/index.html-278160ed.js rename to assets/index.html-196a39b3.js index a1de75c9..584f0ce3 100644 --- a/assets/index.html-278160ed.js +++ b/assets/index.html-196a39b3.js @@ -1 +1 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-1f45f6a9.js b/assets/index.html-1f45f6a9.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-1f45f6a9.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-2137cedf.js b/assets/index.html-2137cedf.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-2137cedf.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-21cd7b10.js b/assets/index.html-21cd7b10.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-21cd7b10.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-22ca0620.js b/assets/index.html-22ca0620.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-22ca0620.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e7dd7f4e.js b/assets/index.html-2350b047.js similarity index 85% rename from assets/index.html-e7dd7f4e.js rename to assets/index.html-2350b047.js index 805216d0..6dd78672 100644 --- a/assets/index.html-e7dd7f4e.js +++ b/assets/index.html-2350b047.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-64c61a30","path":"/resources/books/","title":"Books","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover3.jpg"},"headers":[{"level":2,"title":"计算机相关","slug":"计算机相关","link":"#计算机相关","children":[]},{"level":2,"title":"JVM","slug":"jvm","link":"#jvm","children":[]},{"level":2,"title":"持续更新中...","slug":"持续更新中","link":"#持续更新中","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.24,"words":371},"filePathRelative":"resources/books/README.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-64c61a30","path":"/resources/books/","title":"Books","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover3.jpg"},"headers":[{"level":2,"title":"计算机相关","slug":"计算机相关","link":"#计算机相关","children":[]},{"level":2,"title":"JVM","slug":"jvm","link":"#jvm","children":[]},{"level":2,"title":"持续更新中...","slug":"持续更新中","link":"#持续更新中","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.24,"words":371},"filePathRelative":"resources/books/README.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-23968938.js b/assets/index.html-23968938.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-23968938.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-23fbd1f9.js b/assets/index.html-23fbd1f9.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-23fbd1f9.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-24e592b8.js b/assets/index.html-24e592b8.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-24e592b8.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-25ffa926.js b/assets/index.html-25ffa926.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-25ffa926.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-26dacff9.js b/assets/index.html-26dacff9.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-26dacff9.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-0ad43b75.js b/assets/index.html-2785f8b4.js similarity index 71% rename from assets/index.html-0ad43b75.js rename to assets/index.html-2785f8b4.js index 5304e7bd..8d728a77 100644 --- a/assets/index.html-0ad43b75.js +++ b/assets/index.html-2785f8b4.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-2d0a870d","path":"/en/","title":"home","lang":"en-US","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"home","heroImage":"/assets/images/avatar/avatar_3.png","heroText":"Yaien Blog","heroFullScreen":true,"tagline":"Don't waste your life where you will definitely regret it"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.09,"words":27},"filePathRelative":"en/README.md","localizedDate":"March 3, 2024","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}`);export{e as data}; +const e=JSON.parse(`{"key":"v-2d0a870d","path":"/en/","title":"home","lang":"en-US","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"home","heroImage":"/assets/images/avatar/avatar_3.png","heroText":"Yaien Blog","heroFullScreen":true,"tagline":"Don't waste your life where you will definitely regret it"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.09,"words":27},"filePathRelative":"en/README.md","localizedDate":"March 9, 2024","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}`);export{e as data}; diff --git a/assets/index.html-287b3ad3.js b/assets/index.html-287b3ad3.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-287b3ad3.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-2900fd6d.js b/assets/index.html-2900fd6d.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-2900fd6d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-2b491aea.js b/assets/index.html-2b491aea.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-2b491aea.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-2df6a92a.js b/assets/index.html-2df6a92a.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-2df6a92a.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-2e1d318a.js b/assets/index.html-2e1d318a.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-2e1d318a.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-2f222b05.js b/assets/index.html-2f222b05.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-2f222b05.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-2f510971.js b/assets/index.html-2f510971.js new file mode 100644 index 00000000..5e26f09b --- /dev/null +++ b/assets/index.html-2f510971.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-26374ab8","path":"/tag/%E4%BA%8B%E5%8A%A1/","title":"标签: 事务","lang":"zh-CN","frontmatter":{"title":"标签: 事务","dir":{"index":false},"feed":false,"sitemap":false,"blog":{"type":"category","name":"事务","key":"tag"},"layout":"BlogCategory"},"headers":[],"git":{},"readingTime":{"minutes":0,"words":0},"filePathRelative":null,"excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-2fb606ee.js b/assets/index.html-2fb606ee.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-2fb606ee.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-307c2c56.js b/assets/index.html-307c2c56.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-307c2c56.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-30cc402b.js b/assets/index.html-30cc402b.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-30cc402b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-30e58062.js b/assets/index.html-30e58062.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-30e58062.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-32b3b73b.js b/assets/index.html-32b3b73b.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-32b3b73b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-335f0eec.js b/assets/index.html-335f0eec.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-335f0eec.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-35c8c7ba.js b/assets/index.html-35c8c7ba.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-35c8c7ba.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-37792e31.js b/assets/index.html-37792e31.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-37792e31.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-39f0791c.js b/assets/index.html-39f0791c.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-39f0791c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3ab4778c.js b/assets/index.html-3ab4778c.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-3ab4778c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3b8c93dc.js b/assets/index.html-3b8c93dc.js new file mode 100644 index 00000000..e46354cc --- /dev/null +++ b/assets/index.html-3b8c93dc.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-64c651c8","path":"/tag/%E9%94%81%E6%9C%BA%E5%88%B6/","title":"标签: 锁机制","lang":"zh-CN","frontmatter":{"title":"标签: 锁机制","dir":{"index":false},"feed":false,"sitemap":false,"blog":{"type":"category","name":"锁机制","key":"tag"},"layout":"BlogCategory"},"headers":[],"git":{},"readingTime":{"minutes":0,"words":0},"filePathRelative":null,"excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-3c855670.js b/assets/index.html-3c855670.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-3c855670.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3caf49bf.js b/assets/index.html-3caf49bf.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-3caf49bf.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3d8df219.js b/assets/index.html-3d8df219.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-3d8df219.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-3dd01ab6.js b/assets/index.html-3dd01ab6.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-3dd01ab6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-3e2a279f.js b/assets/index.html-3e2a279f.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-3e2a279f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3f217e7b.js b/assets/index.html-3f217e7b.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-3f217e7b.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-3f5a891d.js b/assets/index.html-3f5a891d.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-3f5a891d.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-40b038f9.js b/assets/index.html-40b038f9.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-40b038f9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-41716a03.js b/assets/index.html-41716a03.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-41716a03.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-44013f94.js b/assets/index.html-44013f94.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-44013f94.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-4416aa74.js b/assets/index.html-4416aa74.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-4416aa74.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-463d76d6.js b/assets/index.html-463d76d6.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-463d76d6.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-477f0894.js b/assets/index.html-477f0894.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-477f0894.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-48e53762.js b/assets/index.html-48e53762.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-48e53762.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b72ef4a7.js b/assets/index.html-4eb83f41.js similarity index 87% rename from assets/index.html-b72ef4a7.js rename to assets/index.html-4eb83f41.js index baceb69e..3d03be51 100644 --- a/assets/index.html-b72ef4a7.js +++ b/assets/index.html-4eb83f41.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-db09d350","path":"/resources/navigation/","title":"Navigation","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover1.jpg"},"headers":[{"level":2,"title":"搜索","slug":"搜索","link":"#搜索","children":[]},{"level":2,"title":"在线工具","slug":"在线工具","link":"#在线工具","children":[]},{"level":2,"title":"语言大模型","slug":"语言大模型","link":"#语言大模型","children":[]},{"level":2,"title":"学习","slug":"学习","link":"#学习","children":[]}],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.81,"words":544},"filePathRelative":"resources/navigation/README.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-db09d350","path":"/resources/navigation/","title":"Navigation","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover1.jpg"},"headers":[{"level":2,"title":"搜索","slug":"搜索","link":"#搜索","children":[]},{"level":2,"title":"在线工具","slug":"在线工具","link":"#在线工具","children":[]},{"level":2,"title":"语言大模型","slug":"语言大模型","link":"#语言大模型","children":[]},{"level":2,"title":"学习","slug":"学习","link":"#学习","children":[]}],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":1.81,"words":544},"filePathRelative":"resources/navigation/README.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-4f8e791e.js b/assets/index.html-4f8e791e.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-4f8e791e.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-5032b125.js b/assets/index.html-5032b125.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-5032b125.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-50db02e4.js b/assets/index.html-50db02e4.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-50db02e4.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-51191316.js b/assets/index.html-51191316.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-51191316.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-51e523d0.js b/assets/index.html-51e523d0.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-51e523d0.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-51f89f01.js b/assets/index.html-51f89f01.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-51f89f01.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-5267cfee.js b/assets/index.html-5267cfee.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-5267cfee.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-55ed2d56.js b/assets/index.html-55ed2d56.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-55ed2d56.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-56f1c82e.js b/assets/index.html-56f1c82e.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-56f1c82e.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-56ff77a6.js b/assets/index.html-56ff77a6.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-56ff77a6.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-57d9bf0e.js b/assets/index.html-57d9bf0e.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-57d9bf0e.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-58ed29e7.js b/assets/index.html-58ed29e7.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-58ed29e7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-595541c4.js b/assets/index.html-595541c4.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-595541c4.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-597f253c.js b/assets/index.html-597f253c.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-597f253c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-59c688cd.js b/assets/index.html-59c688cd.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-59c688cd.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-5a95e16e.js b/assets/index.html-5a95e16e.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-5a95e16e.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-5c7bac98.js b/assets/index.html-5c7bac98.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-5c7bac98.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-5d324fd9.js b/assets/index.html-5d324fd9.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-5d324fd9.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-5f68b0d4.js b/assets/index.html-5f68b0d4.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-5f68b0d4.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-5fddaaec.js b/assets/index.html-5fddaaec.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-5fddaaec.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-5ff6fe2a.js b/assets/index.html-5ff6fe2a.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-5ff6fe2a.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-61511692.js b/assets/index.html-61511692.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-61511692.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-62260527.js b/assets/index.html-62260527.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-62260527.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6229fea5.js b/assets/index.html-6229fea5.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6229fea5.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-62a7a99d.js b/assets/index.html-62a7a99d.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-62a7a99d.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-65e9ca5c.js b/assets/index.html-65e9ca5c.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-65e9ca5c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6622e199.js b/assets/index.html-6622e199.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6622e199.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6663db00.js b/assets/index.html-6663db00.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6663db00.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-674b2b83.js b/assets/index.html-674b2b83.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-674b2b83.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-6782fbcf.js b/assets/index.html-6782fbcf.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-6782fbcf.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-67965a53.js b/assets/index.html-67965a53.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-67965a53.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-67b81d61.js b/assets/index.html-67b81d61.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-67b81d61.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6969f8b1.js b/assets/index.html-6969f8b1.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6969f8b1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6bcb739a.js b/assets/index.html-6bcb739a.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6bcb739a.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6c781776.js b/assets/index.html-6c781776.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6c781776.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6d9f98a2.js b/assets/index.html-6d9f98a2.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6d9f98a2.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6dab2702.js b/assets/index.html-6dab2702.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6dab2702.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6e9b5913.js b/assets/index.html-6e9b5913.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6e9b5913.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6f38caf8.js b/assets/index.html-6f38caf8.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6f38caf8.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6f663488.js b/assets/index.html-6f663488.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-6f663488.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6f8c8f56.js b/assets/index.html-6f8c8f56.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-6f8c8f56.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-3fa35adf.js b/assets/index.html-702a3106.js similarity index 97% rename from assets/index.html-3fa35adf.js rename to assets/index.html-702a3106.js index 76575fc1..e328b99c 100644 --- a/assets/index.html-3fa35adf.js +++ b/assets/index.html-702a3106.js @@ -1 +1 @@ -import{_ as r,r as s,o as l,c as d,f as e,n as t,g as c,a as o,b as n}from"./app-1efcbe9f.js";const i={},p=o("h1",{id:"books",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#books","aria-hidden":"true"},"#"),n(" Books")],-1),h=o("h2",{id:"计算机相关",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#计算机相关","aria-hidden":"true"},"#"),n(" 计算机相关")],-1),g=o("h2",{id:"jvm",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#jvm","aria-hidden":"true"},"#"),n(" JVM")],-1),u=o("h2",{id:"持续更新中",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#持续更新中","aria-hidden":"true"},"#"),n(" 持续更新中...")],-1);function m(v,_){const a=s("VPCard");return l(),d("div",null,[p,h,e(a,t(c({title:"计算机组成原理(第二版)",desc:"高等教育出版社",logo:"/cover/计算机组成原理.png",link:"https://pan.baidu.com/s/11GTYY1tcZ_15wnaBQobU2g?pwd=pmoc",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解计算机系统(原书第三版)",desc:"机械工业出版社",logo:"/cover/深入理解计算机系统(原书第三版).png",link:"https://pan.baidu.com/s/1Y6O9pDDO5hapAog9jxiLTg?pwd=eziy",color:"rgba(253, 230, 135, 0.15)"})),null,16),g,e(a,t(c({title:"Java虚拟机基础教程",desc:"中国工信出版社",logo:"/cover/jvm/Java虚拟机基础教程.png",link:"https://pan.baidu.com/s/1oSqfLW_GDK8s3iWWckLKEw?pwd=6x1c",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)",desc:"机械工业出版社",logo:"/cover/jvm/深入理解Java虚拟机&JVM高级特性与最佳实践.png",link:"https://pan.baidu.com/s/1toJWxMLuJHfKOJll-Q8xtQ?pwd=uwkq",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"实战Java虚拟机:JVM故障诊断与性能优化",desc:"电子工业出版社",logo:"/cover/jvm/实战Java虚拟机&JVM故障诊断与性能优化.png",link:"https://pan.baidu.com/s/1FlISoorqk8UezeoGWD9FGw?pwd=u483",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解JVM&G1 GC",desc:"电子工业出版社",logo:"/cover/jvm/深入理解JVM&G1 GC.png",link:"https://pan.baidu.com/s/1ZLssWxZdP5y0eVBWcJrQMQ?pwd=pcjz",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"揭秘Java虚拟机:JVM设计原理与实现",desc:"电子工业出版社",logo:"/cover/jvm/揭秘Java虚拟机&JVM设计原理与实现.png",link:"https://pan.baidu.com/s/1GPpNInmF5ghq89m6keo-ig?pwd=j80c",color:"rgba(253, 230, 135, 0.15)"})),null,16),u])}const J=r(i,[["render",m],["__file","index.html.vue"]]);export{J as default}; +import{_ as r,r as s,o as l,c as d,f as e,n as t,g as c,a as o,b as n}from"./app-6a63891c.js";const i={},p=o("h1",{id:"books",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#books","aria-hidden":"true"},"#"),n(" Books")],-1),h=o("h2",{id:"计算机相关",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#计算机相关","aria-hidden":"true"},"#"),n(" 计算机相关")],-1),g=o("h2",{id:"jvm",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#jvm","aria-hidden":"true"},"#"),n(" JVM")],-1),u=o("h2",{id:"持续更新中",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#持续更新中","aria-hidden":"true"},"#"),n(" 持续更新中...")],-1);function m(v,_){const a=s("VPCard");return l(),d("div",null,[p,h,e(a,t(c({title:"计算机组成原理(第二版)",desc:"高等教育出版社",logo:"/cover/计算机组成原理.png",link:"https://pan.baidu.com/s/11GTYY1tcZ_15wnaBQobU2g?pwd=pmoc",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解计算机系统(原书第三版)",desc:"机械工业出版社",logo:"/cover/深入理解计算机系统(原书第三版).png",link:"https://pan.baidu.com/s/1Y6O9pDDO5hapAog9jxiLTg?pwd=eziy",color:"rgba(253, 230, 135, 0.15)"})),null,16),g,e(a,t(c({title:"Java虚拟机基础教程",desc:"中国工信出版社",logo:"/cover/jvm/Java虚拟机基础教程.png",link:"https://pan.baidu.com/s/1oSqfLW_GDK8s3iWWckLKEw?pwd=6x1c",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)",desc:"机械工业出版社",logo:"/cover/jvm/深入理解Java虚拟机&JVM高级特性与最佳实践.png",link:"https://pan.baidu.com/s/1toJWxMLuJHfKOJll-Q8xtQ?pwd=uwkq",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"实战Java虚拟机:JVM故障诊断与性能优化",desc:"电子工业出版社",logo:"/cover/jvm/实战Java虚拟机&JVM故障诊断与性能优化.png",link:"https://pan.baidu.com/s/1FlISoorqk8UezeoGWD9FGw?pwd=u483",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"深入理解JVM&G1 GC",desc:"电子工业出版社",logo:"/cover/jvm/深入理解JVM&G1 GC.png",link:"https://pan.baidu.com/s/1ZLssWxZdP5y0eVBWcJrQMQ?pwd=pcjz",color:"rgba(253, 230, 135, 0.15)"})),null,16),e(a,t(c({title:"揭秘Java虚拟机:JVM设计原理与实现",desc:"电子工业出版社",logo:"/cover/jvm/揭秘Java虚拟机&JVM设计原理与实现.png",link:"https://pan.baidu.com/s/1GPpNInmF5ghq89m6keo-ig?pwd=j80c",color:"rgba(253, 230, 135, 0.15)"})),null,16),u])}const J=r(i,[["render",m],["__file","index.html.vue"]]);export{J as default}; diff --git a/assets/index.html-70ff954c.js b/assets/index.html-70ff954c.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-70ff954c.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-71240a37.js b/assets/index.html-71240a37.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-71240a37.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-752cb670.js b/assets/index.html-752cb670.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-752cb670.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-758e905d.js b/assets/index.html-758e905d.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-758e905d.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-78f530ac.js b/assets/index.html-78f530ac.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-78f530ac.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7ad1ada8.js b/assets/index.html-7ad1ada8.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-7ad1ada8.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7c3502af.js b/assets/index.html-7c3502af.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-7c3502af.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7cbabdae.js b/assets/index.html-7cbabdae.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-7cbabdae.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7cef443f.js b/assets/index.html-7cef443f.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-7cef443f.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7d32cae3.js b/assets/index.html-7d32cae3.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-7d32cae3.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7d4bebaa.js b/assets/index.html-7d4bebaa.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-7d4bebaa.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-7e2d0ee5.js b/assets/index.html-7e2d0ee5.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-7e2d0ee5.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-80b68195.js b/assets/index.html-80b68195.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-80b68195.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8461bbdf.js b/assets/index.html-8461bbdf.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8461bbdf.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8548e270.js b/assets/index.html-8548e270.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8548e270.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-858cdc31.js b/assets/index.html-858cdc31.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-858cdc31.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-862e8c5c.js b/assets/index.html-862e8c5c.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-862e8c5c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8635b44a.js b/assets/index.html-8635b44a.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8635b44a.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-869af4d9.js b/assets/index.html-869af4d9.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-869af4d9.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8756cc4d.js b/assets/index.html-8756cc4d.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-8756cc4d.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-8819f9aa.js b/assets/index.html-8819f9aa.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8819f9aa.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-885d2d66.js b/assets/index.html-885d2d66.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-885d2d66.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-886738d6.js b/assets/index.html-886738d6.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-886738d6.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-88dd6423.js b/assets/index.html-88dd6423.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-88dd6423.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-891759b6.js b/assets/index.html-891759b6.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-891759b6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-89b3bc4f.js b/assets/index.html-89b3bc4f.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-89b3bc4f.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8a2b8c18.js b/assets/index.html-8a2b8c18.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-8a2b8c18.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8aac1f45.js b/assets/index.html-8aac1f45.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-8aac1f45.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8c38fce5.js b/assets/index.html-8c38fce5.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-8c38fce5.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-8ca57fa5.js b/assets/index.html-8ca57fa5.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8ca57fa5.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8ca5d6d0.js b/assets/index.html-8ca5d6d0.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8ca5d6d0.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8f405e82.js b/assets/index.html-8f405e82.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-8f405e82.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-8f91d0c9.js b/assets/index.html-8f91d0c9.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-8f91d0c9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8f9d8ed5.js b/assets/index.html-8f9d8ed5.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-8f9d8ed5.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8fa9a741.js b/assets/index.html-8fa9a741.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-8fa9a741.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-8ff98832.js b/assets/index.html-8ff98832.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-8ff98832.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-90171d91.js b/assets/index.html-90171d91.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-90171d91.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-90dd2fc6.js b/assets/index.html-90dd2fc6.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-90dd2fc6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-999fe6e7.js b/assets/index.html-999fe6e7.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-999fe6e7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-99d52e98.js b/assets/index.html-99d52e98.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-99d52e98.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-9c2578f6.js b/assets/index.html-9c2578f6.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-9c2578f6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-9c347168.js b/assets/index.html-9c347168.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-9c347168.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-9d8ae846.js b/assets/index.html-9d8ae846.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-9d8ae846.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-9ed6285d.js b/assets/index.html-9ed6285d.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-9ed6285d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-9fd0514e.js b/assets/index.html-9fd0514e.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-9fd0514e.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-9ff5508b.js b/assets/index.html-9ff5508b.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-9ff5508b.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a18de994.js b/assets/index.html-a18de994.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-a18de994.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a326038d.js b/assets/index.html-a326038d.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-a326038d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-a386ae34.js b/assets/index.html-a386ae34.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-a386ae34.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a4585d58.js b/assets/index.html-a4585d58.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-a4585d58.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-a4bc9d27.js b/assets/index.html-a4bc9d27.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-a4bc9d27.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-a544815b.js b/assets/index.html-a544815b.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-a544815b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-1299121d.js b/assets/index.html-a54836e1.js similarity index 69% rename from assets/index.html-1299121d.js rename to assets/index.html-a54836e1.js index 9a507765..81cc1f81 100644 --- a/assets/index.html-1299121d.js +++ b/assets/index.html-a54836e1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"主页","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"主页","heroText":"Yaien Blog","heroFullScreen":false,"tagline":"不要把你的生命浪费在你一定会后悔的地方"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"README.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"主页","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"主页","heroText":"Yaien Blog","heroFullScreen":false,"tagline":"不要把你的生命浪费在你一定会后悔的地方"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"README.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/index.html-a5e9cc70.js b/assets/index.html-a5e9cc70.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-a5e9cc70.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a7377d86.js b/assets/index.html-a7377d86.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-a7377d86.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a89ff6ed.js b/assets/index.html-a89ff6ed.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-a89ff6ed.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-a9e1d4ba.js b/assets/index.html-a9e1d4ba.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-a9e1d4ba.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-aa6f926f.js b/assets/index.html-aa6f926f.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-aa6f926f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ac828602.js b/assets/index.html-ac828602.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-ac828602.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ad37fdd7.js b/assets/index.html-ad37fdd7.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-ad37fdd7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ad8286ac.js b/assets/index.html-ad8286ac.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-ad8286ac.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ad95b869.js b/assets/index.html-ad95b869.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-ad95b869.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-af420c02.js b/assets/index.html-af420c02.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-af420c02.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b106ced5.js b/assets/index.html-b106ced5.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-b106ced5.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-b1355071.js b/assets/index.html-b1355071.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-b1355071.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b1681d44.js b/assets/index.html-b1681d44.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-b1681d44.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b4bcc9f2.js b/assets/index.html-b4bcc9f2.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-b4bcc9f2.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-af94dd98.js b/assets/index.html-b4e4caa4.js similarity index 87% rename from assets/index.html-af94dd98.js rename to assets/index.html-b4e4caa4.js index f7fe10df..c99da81b 100644 --- a/assets/index.html-af94dd98.js +++ b/assets/index.html-b4e4caa4.js @@ -1 +1 @@ -import{_ as t,o as r,c,a as e,b as a}from"./app-1efcbe9f.js";const n={},s=e("h1",{id:"随笔",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#随笔","aria-hidden":"true"},"#"),a(" 随笔")],-1),d=e("h2",{id:"随笔随笔-就是随便比比",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#随笔随笔-就是随便比比","aria-hidden":"true"},"#"),a(" 随笔随笔,就是随便比比")],-1),o=[s,d];function i(_,h){return r(),c("div",null,o)}const f=t(n,[["render",i],["__file","index.html.vue"]]);export{f as default}; +import{_ as t,o as r,c,a as e,b as a}from"./app-6a63891c.js";const n={},s=e("h1",{id:"随笔",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#随笔","aria-hidden":"true"},"#"),a(" 随笔")],-1),d=e("h2",{id:"随笔随笔-就是随便比比",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#随笔随笔-就是随便比比","aria-hidden":"true"},"#"),a(" 随笔随笔,就是随便比比")],-1),o=[s,d];function i(_,h){return r(),c("div",null,o)}const f=t(n,[["render",i],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b58a21c9.js b/assets/index.html-b58a21c9.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-b58a21c9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-b6211302.js b/assets/index.html-b6211302.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-b6211302.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b7052789.js b/assets/index.html-b7052789.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-b7052789.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-b7700f9a.js b/assets/index.html-b7700f9a.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-b7700f9a.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-b8eb1f32.js b/assets/index.html-b8eb1f32.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-b8eb1f32.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-b98a09c4.js b/assets/index.html-b98a09c4.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-b98a09c4.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ba4ac173.js b/assets/index.html-ba4ac173.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-ba4ac173.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ba65114f.js b/assets/index.html-ba65114f.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-ba65114f.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-bab327ad.js b/assets/index.html-bab327ad.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-bab327ad.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-bb69aa58.js b/assets/index.html-bb69aa58.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-bb69aa58.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-bb83e974.js b/assets/index.html-bb83e974.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-bb83e974.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-bd3b3724.js b/assets/index.html-bd3b3724.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-bd3b3724.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-bec33a9f.js b/assets/index.html-bec33a9f.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-bec33a9f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-bfc377fd.js b/assets/index.html-bfc377fd.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-bfc377fd.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-bfd54f12.js b/assets/index.html-bfd54f12.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-bfd54f12.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c029ebd5.js b/assets/index.html-c029ebd5.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-c029ebd5.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c0d34818.js b/assets/index.html-c0d34818.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-c0d34818.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c34502db.js b/assets/index.html-c34502db.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-c34502db.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-c3d62038.js b/assets/index.html-c3d62038.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-c3d62038.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-c544d31c.js b/assets/index.html-c544d31c.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-c544d31c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-c553b57a.js b/assets/index.html-c553b57a.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-c553b57a.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c7522be6.js b/assets/index.html-c7522be6.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-c7522be6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-c865a580.js b/assets/index.html-c865a580.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-c865a580.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-cb81c8ee.js b/assets/index.html-cb81c8ee.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-cb81c8ee.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-cbbd3b7c.js b/assets/index.html-cbbd3b7c.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-cbbd3b7c.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ce1e3055.js b/assets/index.html-ce1e3055.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-ce1e3055.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-cf04d22a.js b/assets/index.html-cf04d22a.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-cf04d22a.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d1cd5458.js b/assets/index.html-d1cd5458.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-d1cd5458.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-d267ecb7.js b/assets/index.html-d267ecb7.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-d267ecb7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d3cd5a3a.js b/assets/index.html-d3cd5a3a.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-d3cd5a3a.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d3d5bda5.js b/assets/index.html-d3d5bda5.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-d3d5bda5.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d5282ff8.js b/assets/index.html-d5282ff8.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-d5282ff8.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-d53f4c2d.js b/assets/index.html-d53f4c2d.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-d53f4c2d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d60f96c1.js b/assets/index.html-d60f96c1.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-d60f96c1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-d9e7528a.js b/assets/index.html-d9e7528a.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-d9e7528a.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-da898e57.js b/assets/index.html-da898e57.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-da898e57.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-dd2aaa1b.js b/assets/index.html-dd2aaa1b.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-dd2aaa1b.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-de623115.js b/assets/index.html-de623115.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-de623115.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e030af3a.js b/assets/index.html-e030af3a.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-e030af3a.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-e1130fb9.js b/assets/index.html-e1130fb9.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-e1130fb9.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e17e5e38.js b/assets/index.html-e17e5e38.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-e17e5e38.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e20e1302.js b/assets/index.html-e20e1302.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-e20e1302.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-e27794d0.js b/assets/index.html-e27794d0.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-e27794d0.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-e4b3035d.js b/assets/index.html-e4b3035d.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-e4b3035d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e6b6057d.js b/assets/index.html-e6b6057d.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-e6b6057d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-e806ccd2.js b/assets/index.html-e806ccd2.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-e806ccd2.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ea30e109.js b/assets/index.html-ea30e109.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-ea30e109.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ec9692d0.js b/assets/index.html-ec9692d0.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-ec9692d0.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ec98ddf6.js b/assets/index.html-ec98ddf6.js deleted file mode 100644 index a1de75c9..00000000 --- a/assets/index.html-ec98ddf6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,r as t,o as n,c,f as a}from"./app-1efcbe9f.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ecfcf913.js b/assets/index.html-ecfcf913.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-ecfcf913.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-eee582b4.js b/assets/index.html-eee582b4.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-eee582b4.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-ef75afa3.js b/assets/index.html-ef75afa3.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-ef75afa3.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-efd678f8.js b/assets/index.html-efd678f8.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-efd678f8.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-f0ad001a.js b/assets/index.html-f0ad001a.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-f0ad001a.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-f0f0b9c9.js b/assets/index.html-f0f0b9c9.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-f0f0b9c9.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-f1467ce1.js b/assets/index.html-f1467ce1.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-f1467ce1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-f2c796cd.js b/assets/index.html-f2c796cd.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-f2c796cd.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-6dc332ee.js b/assets/index.html-f6996b95.js similarity index 98% rename from assets/index.html-6dc332ee.js rename to assets/index.html-f6996b95.js index c6134e40..090d2128 100644 --- a/assets/index.html-6dc332ee.js +++ b/assets/index.html-f6996b95.js @@ -1 +1 @@ -import{_ as n,r as l,o as s,c as r,a as o,f as a,n as t,g as c,b as i}from"./app-1efcbe9f.js";const d={},h=o("h1",{id:"navigation",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#navigation","aria-hidden":"true"},"#"),i(" Navigation")],-1),g=o("h2",{id:"搜索",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#搜索","aria-hidden":"true"},"#"),i(" 搜索")],-1),p={class:"vp-card-container"},_=o("h2",{id:"在线工具",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#在线工具","aria-hidden":"true"},"#"),i(" 在线工具")],-1),u={class:"vp-card-container"},b=o("h2",{id:"语言大模型",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#语言大模型","aria-hidden":"true"},"#"),i(" 语言大模型")],-1),m={class:"vp-card-container"},v=o("h2",{id:"学习",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#学习","aria-hidden":"true"},"#"),i(" 学习")],-1),w={class:"vp-card-container"};function f(k,y){const e=l("VPCard");return s(),r("div",null,[h,g,o("div",p,[a(e,t(c({title:"百度",desc:"中国的互联网搜索引擎,也是全球最大的中文搜索引擎",logo:"/ico/baidu.ico",link:"https://www.baidu.com",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"必应",desc:"由微软公司开发的搜索引擎,以其简洁、干净的界面和强大的搜索能力而闻名",logo:"/ico/bing.ico",link:"https://www.bing.com",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"google",desc:"全球最大的搜索引擎,也是互联网上最知名的品牌之一",logo:"/ico/google.ico",link:"https://www.google.com",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),_,o("div",u,[a(e,t(c({title:"ProcessOn",desc:"免费在线流程图思维导图",logo:"/ico/processon.ico",link:"https://www.processon.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"有道云笔记",desc:"专业强大的编辑器,支持5种文稿类型,随心所欲开启顺滑的创作编辑体验",logo:"/ico/youdao.ico",link:"https://note.youdao.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"GCeasy",desc:"业内首款借助机器学习技术引导的垃圾回收日志分析工具。GCeasy 内置有智能功能,可自动检测 JVM 和 Android GC 日志中的问题并推荐解决方案。",logo:"/ico/gc-easy.ico",link:"https://gceasy.ycrash.cn/",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),b,o("div",m,[a(e,t(c({title:"ChatGpt",desc:"美国人工智能研究实验室OpenAI推出的一种人工智能技术驱动的自然语言处理工具",logo:"/ico/chatgpt.png",link:"https://openai.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"智谱清言",desc:"基于智谱AI自主研发的中英双语对话模型ChatGLM2,经过万亿字符的文本与代码预训练,并采用有监督微调技术,以通用对话的形式为用户提供智能化服务",logo:"/ico/zhipu.svg",link:"https://chatglm.cn/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"文心一言",desc:"百度全新一代知识增强大语言模型,文心大模型家族的新成员",logo:"/ico/wenxin.png",link:"https://yiyan.baidu.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"通义千问",desc:"阿里云推出的一个超大规模的语言模型",logo:"/ico/qianwen.png",link:"https://qianwen.aliyun.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),v,o("div",w,[a(e,t(c({title:"Z-Library",desc:"好用、免费的电子书获取网站",logo:"/ico/Z-Library.png",link:"https://zh.zlibrary-east.se/",color:"rgba(253, 230, 135, 0.15)"})),null,16)])])}const C=n(d,[["render",f],["__file","index.html.vue"]]);export{C as default}; +import{_ as n,r as l,o as s,c as r,a as o,f as a,n as t,g as c,b as i}from"./app-6a63891c.js";const d={},h=o("h1",{id:"navigation",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#navigation","aria-hidden":"true"},"#"),i(" Navigation")],-1),g=o("h2",{id:"搜索",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#搜索","aria-hidden":"true"},"#"),i(" 搜索")],-1),p={class:"vp-card-container"},_=o("h2",{id:"在线工具",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#在线工具","aria-hidden":"true"},"#"),i(" 在线工具")],-1),u={class:"vp-card-container"},b=o("h2",{id:"语言大模型",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#语言大模型","aria-hidden":"true"},"#"),i(" 语言大模型")],-1),m={class:"vp-card-container"},v=o("h2",{id:"学习",tabindex:"-1"},[o("a",{class:"header-anchor",href:"#学习","aria-hidden":"true"},"#"),i(" 学习")],-1),w={class:"vp-card-container"};function f(k,y){const e=l("VPCard");return s(),r("div",null,[h,g,o("div",p,[a(e,t(c({title:"百度",desc:"中国的互联网搜索引擎,也是全球最大的中文搜索引擎",logo:"/ico/baidu.ico",link:"https://www.baidu.com",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"必应",desc:"由微软公司开发的搜索引擎,以其简洁、干净的界面和强大的搜索能力而闻名",logo:"/ico/bing.ico",link:"https://www.bing.com",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"google",desc:"全球最大的搜索引擎,也是互联网上最知名的品牌之一",logo:"/ico/google.ico",link:"https://www.google.com",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),_,o("div",u,[a(e,t(c({title:"ProcessOn",desc:"免费在线流程图思维导图",logo:"/ico/processon.ico",link:"https://www.processon.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"有道云笔记",desc:"专业强大的编辑器,支持5种文稿类型,随心所欲开启顺滑的创作编辑体验",logo:"/ico/youdao.ico",link:"https://note.youdao.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"GCeasy",desc:"业内首款借助机器学习技术引导的垃圾回收日志分析工具。GCeasy 内置有智能功能,可自动检测 JVM 和 Android GC 日志中的问题并推荐解决方案。",logo:"/ico/gc-easy.ico",link:"https://gceasy.ycrash.cn/",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),b,o("div",m,[a(e,t(c({title:"ChatGpt",desc:"美国人工智能研究实验室OpenAI推出的一种人工智能技术驱动的自然语言处理工具",logo:"/ico/chatgpt.png",link:"https://openai.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"智谱清言",desc:"基于智谱AI自主研发的中英双语对话模型ChatGLM2,经过万亿字符的文本与代码预训练,并采用有监督微调技术,以通用对话的形式为用户提供智能化服务",logo:"/ico/zhipu.svg",link:"https://chatglm.cn/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"文心一言",desc:"百度全新一代知识增强大语言模型,文心大模型家族的新成员",logo:"/ico/wenxin.png",link:"https://yiyan.baidu.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16),a(e,t(c({title:"通义千问",desc:"阿里云推出的一个超大规模的语言模型",logo:"/ico/qianwen.png",link:"https://qianwen.aliyun.com/",color:"rgba(253, 230, 135, 0.15)"})),null,16)]),v,o("div",w,[a(e,t(c({title:"Z-Library",desc:"好用、免费的电子书获取网站",logo:"/ico/Z-Library.png",link:"https://zh.zlibrary-east.se/",color:"rgba(253, 230, 135, 0.15)"})),null,16)])])}const C=n(d,[["render",f],["__file","index.html.vue"]]);export{C as default}; diff --git a/assets/index.html-f73ecfb4.js b/assets/index.html-f73ecfb4.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-f73ecfb4.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-f9583176.js b/assets/index.html-f9583176.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-f9583176.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-f97ddf9d.js b/assets/index.html-f97ddf9d.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-f97ddf9d.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-fbb8ee40.js b/assets/index.html-fbb8ee40.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-fbb8ee40.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-fd3d5abd.js b/assets/index.html-fd3d5abd.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-fd3d5abd.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-fd787609.js b/assets/index.html-fd787609.js new file mode 100644 index 00000000..584f0ce3 --- /dev/null +++ b/assets/index.html-fd787609.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as n,c,f as a}from"./app-6a63891c.js";const r={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[a(e)])}const m=o(r,[["render",_],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-fd89aef8.js b/assets/index.html-fd89aef8.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-fd89aef8.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-fe031c38.js b/assets/index.html-fe031c38.js deleted file mode 100644 index 7c6f802a..00000000 --- a/assets/index.html-fe031c38.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as c,c as t}from"./app-1efcbe9f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-fe58a276.js b/assets/index.html-fe58a276.js new file mode 100644 index 00000000..68cc8d39 --- /dev/null +++ b/assets/index.html-fe58a276.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-6a63891c.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/intro.html-def64f28.js b/assets/intro.html-283584de.js similarity index 74% rename from assets/intro.html-def64f28.js rename to assets/intro.html-283584de.js index 549f8264..87a2e348 100644 --- a/assets/intro.html-def64f28.js +++ b/assets/intro.html-283584de.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-5aa3d8ba","path":"/en/intro.html","title":"Intro Page","lang":"en-US","frontmatter":{"icon":"info","cover":"/assets/images/cover3.jpg"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.04,"words":12},"filePathRelative":"en/intro.md","localizedDate":"March 3, 2024","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-5aa3d8ba","path":"/en/intro.html","title":"Intro Page","lang":"en-US","frontmatter":{"icon":"info","cover":"/assets/images/cover3.jpg"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.04,"words":12},"filePathRelative":"en/intro.md","localizedDate":"March 9, 2024","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/intro.html-3701b0ed.js b/assets/intro.html-ddfba02d.js similarity index 84% rename from assets/intro.html-3701b0ed.js rename to assets/intro.html-ddfba02d.js index ad6c0f60..dde2ddb9 100644 --- a/assets/intro.html-3701b0ed.js +++ b/assets/intro.html-ddfba02d.js @@ -1 +1 @@ -import{_ as t,o,c as r,a as e,b as a}from"./app-1efcbe9f.js";const n={},c=e("h1",{id:"intro-page",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#intro-page","aria-hidden":"true"},"#"),a(" Intro Page")],-1),s=e("p",null,"Place your introduction and profile here.",-1),i=[c,s];function _(d,l){return o(),r("div",null,i)}const f=t(n,[["render",_],["__file","intro.html.vue"]]);export{f as default}; +import{_ as t,o,c as r,a as e,b as a}from"./app-6a63891c.js";const n={},c=e("h1",{id:"intro-page",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#intro-page","aria-hidden":"true"},"#"),a(" Intro Page")],-1),s=e("p",null,"Place your introduction and profile here.",-1),i=[c,s];function _(d,l){return o(),r("div",null,i)}const f=t(n,[["render",_],["__file","intro.html.vue"]]);export{f as default}; diff --git a/assets/me.html-b6f2479a.js b/assets/me.html-70c029c8.js similarity index 81% rename from assets/me.html-b6f2479a.js rename to assets/me.html-70c029c8.js index 2acdb1ed..242dfbdd 100644 --- a/assets/me.html-b6f2479a.js +++ b/assets/me.html-70c029c8.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"个人简介",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#个人简介","aria-hidden":"true"},"#"),o(" 个人简介")],-1),n=[s];function _(d,i){return a(),c("div",null,n)}const l=t(r,[["render",_],["__file","me.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"个人简介",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#个人简介","aria-hidden":"true"},"#"),o(" 个人简介")],-1),n=[s];function _(d,i){return a(),c("div",null,n)}const l=t(r,[["render",_],["__file","me.html.vue"]]);export{l as default}; diff --git a/assets/me.html-c3abaf6e.js b/assets/me.html-c71dfce2.js similarity index 68% rename from assets/me.html-c3abaf6e.js rename to assets/me.html-c71dfce2.js index 9de87e46..d5173fe7 100644 --- a/assets/me.html-c3abaf6e.js +++ b/assets/me.html-c71dfce2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-513bdf66","path":"/about/me.html","title":"个人简介","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover2.jpg"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/me.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-513bdf66","path":"/about/me.html","title":"个人简介","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover2.jpg"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/me.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/update.html-0c62b864.js b/assets/update.html-14b8caef.js similarity index 69% rename from assets/update.html-0c62b864.js rename to assets/update.html-14b8caef.js index 1209506d..51156cbf 100644 --- a/assets/update.html-0c62b864.js +++ b/assets/update.html-14b8caef.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-0c86f055","path":"/about/update.html","title":"更新内容","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover3.jpg"},"headers":[],"git":{"createdTime":1709466185000,"updatedTime":1709466185000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/update.md","localizedDate":"2024年3月3日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; +const e=JSON.parse('{"key":"v-0c86f055","path":"/about/update.html","title":"更新内容","lang":"zh-CN","frontmatter":{"article":false,"timeline":false,"pageInfo":false,"toc":false,"comment":false,"lastUpdated":false,"contributors":false,"cover":"/assets/images/cover3.jpg"},"headers":[],"git":{"createdTime":1709971756000,"updatedTime":1709971756000,"contributors":[{"name":"yanggl","email":"yaien6530@gmail.com","commits":1}]},"readingTime":{"minutes":0.07,"words":20},"filePathRelative":"about/update.md","localizedDate":"2024年3月9日","excerpt":"","copyright":{"author":"Yaien","license":"MIT"}}');export{e as data}; diff --git a/assets/update.html-a4f6157b.js b/assets/update.html-74cd8c2b.js similarity index 81% rename from assets/update.html-a4f6157b.js rename to assets/update.html-74cd8c2b.js index 95820663..77046ec1 100644 --- a/assets/update.html-a4f6157b.js +++ b/assets/update.html-74cd8c2b.js @@ -1 +1 @@ -import{_ as t,o as a,c,a as e,b as o}from"./app-1efcbe9f.js";const r={},s=e("h1",{id:"更新内容",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#更新内容","aria-hidden":"true"},"#"),o(" 更新内容")],-1),n=[s];function _(d,i){return a(),c("div",null,n)}const l=t(r,[["render",_],["__file","update.html.vue"]]);export{l as default}; +import{_ as t,o as a,c,a as e,b as o}from"./app-6a63891c.js";const r={},s=e("h1",{id:"更新内容",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#更新内容","aria-hidden":"true"},"#"),o(" 更新内容")],-1),n=[s];function _(d,i){return a(),c("div",null,n)}const l=t(r,[["render",_],["__file","update.html.vue"]]);export{l as default}; diff --git a/category/db/index.html b/category/db/index.html index b7a5544a..a4da9890 100644 --- a/category/db/index.html +++ b/category/db/index.html @@ -31,10 +31,16 @@ } - + -
跳至主要內容
Redis分布式锁实战

Redis分布式锁实战

+
跳至主要內容
Mysql事务原理

Mysql事务原理

+

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

+

MySQL事务原创
锁机制

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+

MySQL锁机制原创
Redis分布式锁实战

Redis分布式锁实战

记录高并发场景下Redis部署、使用、存在的问题以及处理方案等


Redis原创
布隆过滤器

布隆过滤器

布隆过滤器简单实现

@@ -50,11 +56,7 @@

MySQL基础笔记系列


MySQL基础原创
2
- +

MySQL基础原创
2
3
+ diff --git a/category/ddd/index.html b/category/ddd/index.html index 324a7ec3..be578db7 100644 --- a/category/ddd/index.html +++ b/category/ddd/index.html @@ -31,13 +31,13 @@ } - + -
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

+
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

-

DDD原创
- +

DDD原创
+ diff --git a/category/docker/index.html b/category/docker/index.html index b7db2561..cb704461 100644 --- a/category/docker/index.html +++ b/category/docker/index.html @@ -31,16 +31,16 @@ } - + - + diff --git a/category/index.html b/category/index.html index fd442ea5..89905a3b 100644 --- a/category/index.html +++ b/category/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/category/ip/index.html b/category/ip/index.html index 097af598..db222571 100644 --- a/category/ip/index.html +++ b/category/ip/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/category/java/index.html b/category/java/index.html index b27e8b2f..73ad1d76 100644 --- a/category/java/index.html +++ b/category/java/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

+
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器

JVMGC原创
对象创建与内存分配

对象创建与内存分配

@@ -55,7 +55,7 @@ synchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。


JVMLock原创
JVM类加载机制

JVM类加载机制

本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。

-

JVM类加载原创
2
- +

JVM类加载原创
2
+ diff --git a/category/kubernetes/index.html b/category/kubernetes/index.html index e1c9a369..ed3e01ef 100644 --- a/category/kubernetes/index.html +++ b/category/kubernetes/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/category/mybatis/index.html b/category/mybatis/index.html index 1842cc2b..04dbd2fd 100644 --- a/category/mybatis/index.html +++ b/category/mybatis/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/category/rpc/index.html b/category/rpc/index.html index bfe8009a..9026e4cf 100644 --- a/category/rpc/index.html +++ b/category/rpc/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

+
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。


dubbo原创
Dubbo 整合 Spring

Dubbo 整合 Spring

本文主要记录学习Dubbo 整合 Spring 的源码笔记

@@ -44,7 +44,7 @@

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

-

dubbo原创
- +

dubbo原创
+ diff --git a/category/spring/index.html b/category/spring/index.html index 5110b875..4326dc43 100644 --- a/category/spring/index.html +++ b/category/spring/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/category/springboot/index.html b/category/springboot/index.html index fd85113f..8a44bd86 100644 --- a/category/springboot/index.html +++ b/category/springboot/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
spring-boot-maven-plugin 插件详解

spring-boot-maven-plugin 插件详解

+
跳至主要內容
spring-boot-maven-plugin 插件详解

spring-boot-maven-plugin 插件详解

Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring
应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven
插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。

@@ -44,7 +44,7 @@

而如果我们使用一般的打包命令时

mvn clean package
 

不会把依赖的jar包也打进去,这样打出来的包就会很小。

-

插件原创
- +

插件原创
+ diff --git a/category/springmvc/index.html b/category/springmvc/index.html index 59a73e1c..eb194561 100644 --- a/category/springmvc/index.html +++ b/category/springmvc/index.html @@ -31,16 +31,16 @@ } - + - + diff --git a/category/tcp/index.html b/category/tcp/index.html index 4a36cbc4..625d4084 100644 --- a/category/tcp/index.html +++ b/category/tcp/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git "a/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" "b/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" index a634f114..ba6820ae 100644 --- "a/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" +++ "b/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\345\211\215\347\253\257/index.html" "b/category/\345\211\215\347\253\257/index.html" index 8b0face2..3ee18346 100644 --- "a/category/\345\211\215\347\253\257/index.html" +++ "b/category/\345\211\215\347\253\257/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\345\234\260\345\233\276/index.html" "b/category/\345\234\260\345\233\276/index.html" index 4f86defc..2c1bb88d 100644 --- "a/category/\345\234\260\345\233\276/index.html" +++ "b/category/\345\234\260\345\233\276/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\345\267\245\345\205\267/index.html" "b/category/\345\267\245\345\205\267/index.html" index 176850e7..1867c868 100644 --- "a/category/\345\267\245\345\205\267/index.html" +++ "b/category/\345\267\245\345\205\267/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\345\271\266\345\217\221/index.html" "b/category/\345\271\266\345\217\221/index.html" index 705117a1..ee40ef0b 100644 --- "a/category/\345\271\266\345\217\221/index.html" +++ "b/category/\345\271\266\345\217\221/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
线程池参数配置

线程池参数配置

+
跳至主要內容
线程池参数配置

线程池参数配置

记录线程池参数配置参考,以及配置的理论原理以及思路


线程安全原创
LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。

@@ -56,7 +56,7 @@

线程安全Lock原创
AQS

AQS

AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。

-

线程安全AQS原创
2
- +

线程安全AQS原创
2
+ diff --git "a/category/\345\276\256\344\277\241/index.html" "b/category/\345\276\256\344\277\241/index.html" index e1a99235..50510fcd 100644 --- "a/category/\345\276\256\344\277\241/index.html" +++ "b/category/\345\276\256\344\277\241/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\345\276\256\346\234\215\345\212\241/index.html" "b/category/\345\276\256\346\234\215\345\212\241/index.html" index 10bef7d2..d1d085de 100644 --- "a/category/\345\276\256\346\234\215\345\212\241/index.html" +++ "b/category/\345\276\256\346\234\215\345\212\241/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

+
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。


dubbo原创
Dubbo 整合 Spring

Dubbo 整合 Spring

本文主要记录学习Dubbo 整合 Spring 的源码笔记

@@ -44,7 +44,7 @@

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

-

dubbo原创
- +

dubbo原创
+ diff --git "a/category/\346\225\231\347\250\213/index.html" "b/category/\346\225\231\347\250\213/index.html" index 56508054..d09e9ec6 100644 --- "a/category/\346\225\231\347\250\213/index.html" +++ "b/category/\346\225\231\347\250\213/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/category/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" "b/category/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" index da4f0651..2dea888c 100644 --- "a/category/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" +++ "b/category/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" @@ -31,16 +31,16 @@ } - + -
跳至主要內容
栈(Stack)

栈(Stack)

+
跳至主要內容
栈(Stack)

栈(Stack)

栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。


原创
跳表(SkipList)

跳表(SkipList)

跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写
呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。

-

跳表原创
- +

跳表原创
+ diff --git "a/category/\347\256\227\346\263\225/index.html" "b/category/\347\256\227\346\263\225/index.html" index 3e92a59a..d9126b6e 100644 --- "a/category/\347\256\227\346\263\225/index.html" +++ "b/category/\347\256\227\346\263\225/index.html" @@ -31,16 +31,16 @@ } - + -
跳至主要內容
多边形等距离外扩

多边形等距离外扩

+
跳至主要內容
多边形等距离外扩

多边形等距离外扩

实现多边形形成的多边形进行等距外扩或收缩的算法实现


多边形原创
归并排序

归并排序

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。


排序原创
基数(桶)排序

基数(桶)排序

基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。

-

排序原创
- +

排序原创
+ diff --git "a/category/\351\232\217\347\254\224/index.html" "b/category/\351\232\217\347\254\224/index.html" index 73f5acd9..4aea74e9 100644 --- "a/category/\351\232\217\347\254\224/index.html" +++ "b/category/\351\232\217\347\254\224/index.html" @@ -31,13 +31,13 @@ } - + -
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

+
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

-

DDD原创
- +

DDD原创
+ diff --git a/en/article/index.html b/en/article/index.html index cea89ca7..82d710c2 100644 --- a/en/article/index.html +++ b/en/article/index.html @@ -31,10 +31,10 @@ } - + -
Skip to main content
CentOS 8 Linux服务器防火墙常用命令

CentOS 8 Linux服务器防火墙常用命令

+
Skip to main content

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.

@@ -45,6 +45,6 @@

dockerOriginal
Docker搭建Nacos单机

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤


dockerOriginal
- + diff --git a/en/category/docker/index.html b/en/category/docker/index.html index b118f7ae..9941626c 100644 --- a/en/category/docker/index.html +++ b/en/category/docker/index.html @@ -31,7 +31,7 @@ } - +
Skip to main content
Docker搭建Nacos单机

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤


dockerOriginal
- + diff --git a/en/category/index.html b/en/category/index.html index 6a020d27..1a5c3efc 100644 --- a/en/category/index.html +++ b/en/category/index.html @@ -31,10 +31,10 @@ } - +
Skip to main content
- + diff --git "a/en/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" "b/en/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" index 46a5f248..0192842f 100644 --- "a/en/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" +++ "b/en/category/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" @@ -31,12 +31,12 @@ } - +
Skip to main content

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.


OpenAIChatGPTOriginal
- + diff --git "a/en/category/\345\267\245\345\205\267/index.html" "b/en/category/\345\267\245\345\205\267/index.html" index da4f5482..d40bea59 100644 --- "a/en/category/\345\267\245\345\205\267/index.html" +++ "b/en/category/\345\267\245\345\205\267/index.html" @@ -31,10 +31,10 @@ } - + - + diff --git "a/en/category/\346\225\231\347\250\213/index.html" "b/en/category/\346\225\231\347\250\213/index.html" index 825f33b3..a5c12425 100644 --- "a/en/category/\346\225\231\347\250\213/index.html" +++ "b/en/category/\346\225\231\347\250\213/index.html" @@ -31,12 +31,12 @@ } - +
Skip to main content
- + diff --git a/en/index.html b/en/index.html index 314860b5..212586a1 100644 --- a/en/index.html +++ b/en/index.html @@ -31,10 +31,10 @@ } - + -
Skip to main content
Yaien Blog

Yaien Blog

——「」

CentOS 8 Linux服务器防火墙常用命令

CentOS 8 Linux服务器防火墙常用命令

+
Skip to main content
Yaien Blog

Yaien Blog

——「」

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.

@@ -45,6 +45,6 @@

dockerOriginal
Docker搭建Nacos单机

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤


dockerOriginal
- + diff --git a/en/intro.html b/en/intro.html index 8a145ca3..b86b9c04 100644 --- a/en/intro.html +++ b/en/intro.html @@ -31,10 +31,10 @@ } - + -
Skip to main content
Intro Page

Intro Page

Yaien BlogLess than 1 minute

Intro Page

Place your introduction and profile here.

Last update:
Contributors: yanggl
- +
Skip to main content
Intro Page

Intro Page

Yaien BlogLess than 1 minute

Intro Page

Place your introduction and profile here.

Last update:
Contributors: yanggl
+ diff --git a/en/star/index.html b/en/star/index.html index 0fbc5a76..3e7809da 100644 --- a/en/star/index.html +++ b/en/star/index.html @@ -31,10 +31,10 @@ } - +
Skip to main content
- + diff --git a/en/tag/chatgpt/index.html b/en/tag/chatgpt/index.html index 60034aea..49c00d14 100644 --- a/en/tag/chatgpt/index.html +++ b/en/tag/chatgpt/index.html @@ -31,12 +31,12 @@ } - +
Skip to main content

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.


OpenAIChatGPTOriginal
- + diff --git a/en/tag/docker/index.html b/en/tag/docker/index.html index da32394e..587ee06f 100644 --- a/en/tag/docker/index.html +++ b/en/tag/docker/index.html @@ -31,7 +31,7 @@ } - +
Skip to main content
Docker搭建Nacos单机

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤


dockerOriginal
- + diff --git a/en/tag/idea/index.html b/en/tag/idea/index.html index 8eb3bb65..64eb7f53 100644 --- a/en/tag/idea/index.html +++ b/en/tag/idea/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/en/tag/index.html b/en/tag/index.html index 260dfc8f..97f3f427 100644 --- a/en/tag/index.html +++ b/en/tag/index.html @@ -31,10 +31,10 @@ } - +
Skip to main content
- + diff --git a/en/tag/linux/index.html b/en/tag/linux/index.html index ce16638b..255068a5 100644 --- a/en/tag/linux/index.html +++ b/en/tag/linux/index.html @@ -31,12 +31,12 @@ } - +
Skip to main content
- + diff --git a/en/tag/openai/index.html b/en/tag/openai/index.html index ccf47457..cc12112e 100644 --- a/en/tag/openai/index.html +++ b/en/tag/openai/index.html @@ -31,12 +31,12 @@ } - +
Skip to main content

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.


OpenAIChatGPTOriginal
- + diff --git a/en/timeline/index.html b/en/timeline/index.html index 7ad0f592..2dab4a63 100644 --- a/en/timeline/index.html +++ b/en/timeline/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/en/tutorial/docker/2305081011.html b/en/tutorial/docker/2305081011.html index 157f3859..a486d708 100644 --- a/en/tutorial/docker/2305081011.html +++ b/en/tutorial/docker/2305081011.html @@ -31,7 +31,7 @@ } - +
Skip to main content

Docker安装

Yaien BlogOriginalAbout 4 mindockerdocker

Docker安装

本文记录Docker容器安装的详细步骤

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
@@ -77,6 +77,6 @@
 

除了启动Docker,一些其他启动相关的命令:

  • 守护进程重启:systemctl daemon-reload
  • 重启Docker服务:systemctl restart docker / service docker restart
  • 关闭Docker服务:docker service docker stop / docker systemctl stop docker

删除Docker

删除安装包:

yum remove docker-ce
 

删除镜像、容器open in new window、配置文件等内容:

rm -rf /var/lib/docker
 

Docker其他常见命令

安装完成Docker之后,这里汇总列一下常见的Docker操作命令:

  • 搜索仓库镜像:docker search 镜像名
  • 拉取镜像:docker pull 镜像名
  • 查看正在运行的容器:docker ps
  • 查看所有容器:docker ps -a
  • 删除容器:docker rm container_id
  • 查看镜像:docker images
  • 删除镜像:docker rmi image_id
  • 启动(停止的)容器:docker start 容器ID
  • 停止容器:docker stop 容器ID
  • 重启容器:docker restart 容器ID
  • 启动(新)容器:docker run -it ubuntu /bin/bash
  • 进入容器:docker attach 容器IDdocker exec -it 容器ID /bin/bash,推荐使用后者。

更多的命令可以通过docker help命令来查看。

Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/docker/2305081101.html b/en/tutorial/docker/2305081101.html index bbe295da..2fd321a7 100644 --- a/en/tutorial/docker/2305081101.html +++ b/en/tutorial/docker/2305081101.html @@ -31,7 +31,7 @@ } - +
Skip to main content

Docker 安装 Mysql5.7

Yaien BlogOriginalAbout 2 mindockerdocker

Docker 安装 Mysql5.7

本文记录Docker容器安装Mysql5.7的详细步骤

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
@@ -73,6 +73,6 @@
 flush privileges;
 

退出,重启mysql容器

docker restart mysql
 

检查防火墙

  • 检查服务器提供商防火墙是否已经开放3306端口
  • 检查服务器内防火墙是否打开且开放3306端口

使用创建远程连接的账号登录

  • ip为服务器ip地址
  • 端口为服务器映射的端口
Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/docker/2305081110.html b/en/tutorial/docker/2305081110.html index bde3e3dd..439109c9 100644 --- a/en/tutorial/docker/2305081110.html +++ b/en/tutorial/docker/2305081110.html @@ -31,7 +31,7 @@ } - +
Skip to main content

Docker搭建Nacos单机

Yaien BlogOriginalAbout 2 mindockerdocker

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
@@ -82,6 +82,6 @@
 -v /home/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties \
 nacos/nacos-server
 
img
img

七、访问nacos

访问地址:http://域名或ip地址:8848/nacosopen in new window 账号:nacos 密码:nacos

Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/docker/index.html b/en/tutorial/docker/index.html index bc0f671b..a6d35713 100644 --- a/en/tutorial/docker/index.html +++ b/en/tutorial/docker/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/en/tutorial/index.html b/en/tutorial/index.html index 7b52edb0..6b5163b9 100644 --- a/en/tutorial/index.html +++ b/en/tutorial/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/en/tutorial/linux/2305081106.html b/en/tutorial/linux/2305081106.html index b275d001..71a39074 100644 --- a/en/tutorial/linux/2305081106.html +++ b/en/tutorial/linux/2305081106.html @@ -31,7 +31,7 @@ } - +
Skip to main content

CentOS 8 Linux服务器防火墙常用命令

Yaien BlogOriginalLess than 1 minute教程linux

CentOS 8 Linux服务器防火墙常用命令

CentOS 8 Linux服务器防火墙常用命令

查看防火墙某个端口是否开放

firewall-cmd --query-port=3306/tcp
@@ -47,6 +47,6 @@
 

查看被监听(Listen)的端口

netstat -lntp
 

检查端口被哪个进程占用

netstat -lnp|grep 3306
 
Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/linux/index.html b/en/tutorial/linux/index.html index 4a7ac2d7..d8c18986 100644 --- a/en/tutorial/linux/index.html +++ b/en/tutorial/linux/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/en/tutorial/openai/2305081112.html b/en/tutorial/openai/2305081112.html index 3082332c..0ba68616 100644 --- a/en/tutorial/openai/2305081112.html +++ b/en/tutorial/openai/2305081112.html @@ -31,10 +31,10 @@ } - +
Skip to main content

Yaien BlogOriginalLess than 1 minute人工智能OpenAIChatGPT

Registering an OpenAI Account
Tutorial for using Siri ChatGPT: Integrating Siri with ChatGPT for voice-activated commands and continuous conversation support.

Copying the Project

Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/openai/index.html b/en/tutorial/openai/index.html index 9de3812a..053f4358 100644 --- a/en/tutorial/openai/index.html +++ b/en/tutorial/openai/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/en/tutorial/util/2305090946.html b/en/tutorial/util/2305090946.html index 36bb9df0..d9194ff7 100644 --- a/en/tutorial/util/2305090946.html +++ b/en/tutorial/util/2305090946.html @@ -31,10 +31,10 @@ } - +
Skip to main content

Compilation of commonly used plugins for IDEA, continuously updated.

Yaien BlogOriginalAbout 1 min工具idea

Compilation of commonly used plugins for IDEA, continuously updated.

Lombok

Introduction: Automatically generates getters, setters, and toString methods.

You only need to define class properties, let Lombok handle the rest.

Features

@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor, and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val and @var experimental
@var
@UtilityClass

Usage: Apply the aforementioned annotations to your classes.

google-java-format

Introduction: The google-java-format plugin allows automatic code formatting without using specific shortcuts.

Translation

Introduction: Translation plugin supporting Google Translate, Baidu Translate, and Youdao Translate. Google Translate is recommended.

Alibaba Java Coding Guidelines

Introduction: Alibaba code style checker. Non-compliant code sections are highlighted with wave underlines, and corresponding suggestions are displayed when hovering the mouse. Some issues can even be quickly fixed.

Leetcode Editor

Introduction: LeetCode plugin for solving coding problems within IDEA. It's truly convenient for sneaking in some algorithmic exercises while appearing to be working diligently.

You can also visit the official LeetCode website.

Jclasslib Bytecode Viewer

Introduction: Viewing the bytecode files of classes.

CamelCase

Introduction: Switching between several string formats. The following formats are supported:

CamelCase: First word lowercase, subsequent words uppercase.

All lowercase with underscore between words.
All lowercase with space between words.
All lowercase with hyphen between words.
Each word with initial uppercase.
All uppercase with underscore between words.

Usage: Hold Shift + Alt and press U repeatedly to convert the selected content's words between underscore, camel case, and uppercase, until the desired format is achieved.

Free Mybatis Plugin

Introduction: Allows navigation from methods in mapper interfaces to corresponding mapper.xml files.

Auto Filling Java Call Arguments

Introduction: Automatic filling of function arguments. When calling pre-defined functions that require parameter input, it is often the case that the variable names align with the parameter names. Manually filling in individual parameters can be time-consuming, especially with a large number of parameters. This plugin solves this problem.

FindBugs

Introduction: Static code analysis tool. It detects potential issues in your code and provides explanations.

SequenceDiagram

Introduction: Generates sequence diagrams based on the call chain, greatly assisting in viewing class invocations and source code.

Codota

Introduction: Code completion plugin. Competes with IDEA's code suggestion feature and allows searching for third-party usage of a particular function.

Maven Helper

Introduction: Maven dependency management.

JRebel

Introduction: Hot deployment plugin. For detailed instructions, refer to: link to tutorial

Last update:
Contributors: yanggl
- + diff --git a/en/tutorial/util/index.html b/en/tutorial/util/index.html index 2bde5219..6a031e64 100644 --- a/en/tutorial/util/index.html +++ b/en/tutorial/util/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/index.html b/index.html index b0d12fcf..02983d47 100644 --- a/index.html +++ b/index.html @@ -31,21 +31,23 @@ } - +
跳至主要內容

Yaien Blog

——「」

多边形等距离外扩

多边形等距离外扩

实现多边形形成的多边形进行等距外扩或收缩的算法实现

-

多边形原创


Mysql事务原理

Mysql事务原理

+

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

+

MySQL事务原创
锁机制

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+

MySQL锁机制原创
垃圾收集器及原理

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器

JVMGC原创
对象创建与内存分配

对象创建与内存分配

当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。

-

JVM原创
Redis分布式锁实战

Redis分布式锁实战

-

记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

-

Redis原创
2
3
4
5
...
8
- +

JVM原创
2
3
4
5
...
9
+ diff --git a/informal/2305090935.html b/informal/2305090935.html index 19324cc1..17791e1a 100644 --- a/informal/2305090935.html +++ b/informal/2305090935.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

DDD 领域驱动设计模型

Yaien Blog原创大约 5 分钟随笔DDDDDD

DDD 领域驱动设计模型

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

开发目标

依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。1、拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月2、架构出高可用极易符合互联网高速迭代的应用服务3、物料化、组装化、可编排的服务,提高人效

服务架构

  • 应用层{application}
    • 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
    • 应用层的服务包括应用服务和领域事件相关服务。
    • 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
    • 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
  • 领域层{domain}
    • 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
    • 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
    • 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
    • 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
  • 基础层{infrastructrue}
    • 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
    • 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
  • 接口层{interfaces}
    • 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。

开发环境

  • jdk1.8【jdk1.7以下只能部分支持netty】
  • springboot 2.0.6.RELEASE
  • idea + maven

代码结构示例

itstack-demo-ddd-01
@@ -208,6 +208,6 @@
 
 }
 

总结

  1. 以上是基于DDD一个基本入门的结构演示完成,实际开发可以按照此模式进行调整。
  2. 目前这个架构分层还不能很好地进行分离,以及层级关系的引用还不利于扩展。
  3. 后续会持续完善以及可以组合搭建RPC框架等,让整个架构更利于互联网开发。
上次编辑于:
贡献者: yanggl
- + diff --git a/informal/index.html b/informal/index.html index c3d9159b..eaca2214 100644 --- a/informal/index.html +++ b/informal/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容

随笔

Yaien Blog小于 1 分钟

随笔

随笔随笔,就是随便比比

上次编辑于:
贡献者: yanggl
- +
跳至主要內容

随笔

Yaien Blog小于 1 分钟

随笔

随笔随笔,就是随便比比

上次编辑于:
贡献者: yanggl
+ diff --git a/note/algorithm/2301101203.html b/note/algorithm/2301101203.html index ffc7a4dd..bdc0e96b 100644 --- a/note/algorithm/2301101203.html +++ b/note/algorithm/2301101203.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

基数(桶)排序

Yaien Blog原创大约 3 分钟算法排序

基数(桶)排序

基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。

介绍

  1. 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

  2. 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

  3. 基数排序(Radix Sort)是桶排序的扩展

  4. 基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

基本思想

将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

图解

注意:排序的次数取决于最大数值元素的位数

代码实现

package com.ygl.sort;
@@ -101,6 +101,6 @@
 	}
 }
 

总结

  1. 基数排序是对传统桶排序的扩展,速度很快.
  2. 基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。
  3. 基数排序时是稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]
  4. 有负数的数组,我们不用基数排序来进行排序, 如果要支持负数,点击此链接open in new window参考
上次编辑于:
贡献者: yanggl
- + diff --git a/note/algorithm/2301101204.html b/note/algorithm/2301101204.html index 4cb7ce08..9211a55e 100644 --- a/note/algorithm/2301101204.html +++ b/note/algorithm/2301101204.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

归并排序

Yaien Blog原创大约 3 分钟算法排序

归并排序

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。

基本思想

将一个需要排序的数组通过递归进行拆分,当每一个元素都是一个个体的时候,再进行合并,合并时将合并的数据保存到一个临时开辟的空间中,这意味着需要额外的空间来保存数据;这个算法主要分为三步: (一):拆分
将一个初始的数组递归拆分,将每一个元素拆分至单个个体独立存在(这里并不对数据进行操作) (二):合并(核心) 将拆分的当个个体元素进行合并,合并的过程中进行排序,并保存到临时创建的空间内(需要对数据进行排序操作) (三):拷贝
将临时空间内的数据拷贝到原数组中

合并的实现图解如下


代码实现

package com.ygl.sort;
@@ -128,6 +128,6 @@
 	}
 }
 

总结

归并算法的核心在于合并的过程,合并的次数为arr.length()-1次,因此时间复杂度表达式为O(n log n),是一种线性对数阶,处理数据的时间相对快;

上次编辑于:
贡献者: yanggl
- + diff --git a/note/algorithm/index.html b/note/algorithm/index.html index 5ac98e92..b5355d16 100644 --- a/note/algorithm/index.html +++ b/note/algorithm/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/db/index.html b/note/db/index.html index 0c0c4811..153deff5 100644 --- a/note/db/index.html +++ b/note/db/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/note/db/mysql/basic/1910011300.html b/note/db/mysql/basic/1910011300.html index 81887bfd..515db5ee 100644 --- a/note/db/mysql/basic/1910011300.html +++ b/note/db/mysql/basic/1910011300.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

MySQL 基础(一)

Yaien Blog原创大约 2 分钟DBMySQL基础

MySQL 基础(一)

MySQL 基础笔记系列

简介

什么是DBMS: DataBaseManagementSystem,数据库管理系统(数据库管理软件),作用就是负责对数据进行增删改查的软件,常见的DBMS: MySQL、Oracle、DB2、SQLServer、SQLite等

数据库分类

  1. 关系型数据库: 以表为单位保存数据,经过数学理论验证可以保存现实生活中存在的任何关系
  2. 非关系型数据库: 以键值对形式保存数据,一般用于解决特殊场景,如数据缓存。

开源和闭源

  1. 开源:公开源代码,免费。盈利方式:靠卖服务,开源有大拿无偿维护升级
  2. 闭源:不公开源代码,收费。盈利方式:靠卖产品+卖服务,闭源有大拿攻击,但是公司会花钱养一群人维护升级

主流数据库

  1. MySQL:Oracle公司产品, 08年被Sun公司收购,09Sun被Oracle 拉里.埃里森 MariaDB 市场占有率第一
  2. Oracle:Oracle公司产品市场占有率第二,性能最高 价格最贵的数据库
  3. SQLServer:微软公司产品排第三,主要应用在微软整套解决方案中
  4. DB2:IBM公司产品 主要应用在IBM整套解决方案中
  5. sqlite:轻量级数据库,只具备基础的增删改查操作

SQL

Structured Query Language:结构化查询语言,用户程序员和数据库软件进行交流的语言

数据库相关

  1. 连接数据库
mysql -uroot -p
@@ -53,6 +53,6 @@
 
  1. 查看表详情
 # 格式: show create table 表名;
   show create table person;
 

引擎

  1. innodb:默认,支持事物、外键等高级操作
  2. myisam: 只支持基础的增删改查操作,不支持事物、外键等高级操作
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/mysql/basic/1910011301.html b/note/db/mysql/basic/1910011301.html index 24b529e7..bc6c5c5f 100644 --- a/note/db/mysql/basic/1910011301.html +++ b/note/db/mysql/basic/1910011301.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

MySQL基础(二)

Yaien Blog原创大约 3 分钟DBMySQL基础

MySQL基础(二)

MySQL基础笔记系列

主键约束

  • 主键:表示数据唯一性的字段称为主键
  • 约束: 创建表时给表字段添加的限制条件
  • 主键约束: 限制值唯一且非空
# 格式: create table t1(id int primary key,name varchar(10));
@@ -65,6 +65,6 @@
 select ename '姓名' from emp;
 select ename 姓名 from emp;
 
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/mysql/basic/1910011302.html b/note/db/mysql/basic/1910011302.html index 540e4201..092edec2 100644 --- a/note/db/mysql/basic/1910011302.html +++ b/note/db/mysql/basic/1910011302.html @@ -31,7 +31,7 @@ } - + - + diff --git a/note/db/mysql/basic/1910011303.html b/note/db/mysql/basic/1910011303.html index eb066160..92f8dd24 100644 --- a/note/db/mysql/basic/1910011303.html +++ b/note/db/mysql/basic/1910011303.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

MySQL基础(四)

Yaien Blog原创大约 2 分钟DBMySQL基础

MySQL基础(四)

MySQL基础笔记系列

数学相关

  1. 向下取整 floor(num)
select floor(3.85);
@@ -49,6 +49,6 @@
 
  1. 外连接
select * from A left/right join B on A.x=B.x where A.age=20 
 
  1. 多表连接
select 条件 from emp e join emp m on e.mgr=m.empno join dept d on e.deptno=d.deptno join xxx on xxx join xxx on xxx...;
 

总结:如果查询的数据是两张表的交集数据使用内连接,如果查询的数据是一张表的全部和另外一张表的交集使用外连接。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/mysql/basic/1910011304.html b/note/db/mysql/basic/1910011304.html index a5effeac..2dbb88bf 100644 --- a/note/db/mysql/basic/1910011304.html +++ b/note/db/mysql/basic/1910011304.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

MySQL基础(五)

Yaien Blog原创大约 2 分钟DBMySQL基础

MySQL基础(五)

MySQL基础笔记系列

关联关系

外键:用于建立关系的字段称为外键

一对一

  • 什么是一对一: 有AB两张表,A表中一条数据对应B表中的一条数据,同时B表中一条也对应A表中的一条

  • 应用场景: 为了提高查询效率 把原有一张表的数据拆成两张表如:商品表和商品详情表 、 用户表和用户信息扩展表

一对多

  • 什么是一对多:有AB两张表,A表中一条数据对应B表多条数据,同时B表一条对应A表一条,称为一对多。
  • 应用场景: 员工表和部门表 商品表和商品分类表 用户表和地址表
  • 如何建立关系: 在多的表中添加外键指向另外一张表的主键

多对多

  • 什么是多对多: 有AB两张表,A表中一条数据对应B表中的多条数据,同时B表中一体数据对应A表中的多条。
  • 应用场景: 用户表和权限表 老师表和学生表
  • 如何建立关系: 创建关系表,在关系表中添加两个外键指向另外两个表的主键

视图(view)

  • 什么是视图:数据库中表和视图都是其内部的对象,视图本质其实是取代了一段SQL查询语句,视图没有自己独立的数据,数据来自于原表
  • 视图的作用: 1. 重用SQL,提高开发效率 2. 隐藏敏感字段
 create view 视图名 as (子查询);
@@ -39,6 +39,6 @@
 
  1. 创建一个10号部门的视图
create view v_emp_10 as(select * from emp where deptno=10);
 
  1. 创建一个没有工资的员工表视图
create view v_emp_nosal as(select empno,ename,deptno from emp);
 
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/mysql/basic/index.html b/note/db/mysql/basic/index.html index eb610450..390e2665 100644 --- a/note/db/mysql/basic/index.html +++ b/note/db/mysql/basic/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/db/mysql/further/2305011002.html b/note/db/mysql/further/2305011002.html deleted file mode 100644 index 2c942352..00000000 --- a/note/db/mysql/further/2305011002.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - 深入理解mysql索引底层数据结构与算法 | Yaien Blog - - - - - - -
跳至主要內容

深入理解mysql索引底层数据结构与算法

Yaien Blog原创大约 5 分钟DBMySQL索引

深入理解mysql索引底层数据结构与算法

mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。

索引

索引 是帮助mysql高效获取数据的排好序数据结构

索引的数据结构

二叉树

二叉树的右子节点比父节点大,左子节点比父节点小。通过这一特点进行查询时,通过比对可以快速定位节点位置。

缺点:数据形成的是满二叉树或者完全二叉树的时候去查找才能很好的发挥索引的优势。在极端情况下,
例如形成的是一颗线型的二叉树,其实跟没有构建索引是一样的。

红黑树

红黑树又称二叉平衡树,在新增节点时可以自动调整节点的位置。

缺点:当数据的数据量大时,树的高度会变得很高,而且树的高度不可控。如果查询的数据恰好在叶子节点,而树的查询都是从
父节点开始的,执行的IO次数也会很多。

HASH

构建一个HASH桶,将计算好HASH的元素以及地址放到桶的指定位置,再有相同的则追加在元素后面。

查询则先计算好在桶的哪个位置,再遍历链表。在某种情况下要比B+树更高效。

缺点:对范围IN的查询不支持、hash冲突等问题

B树

B树的结构是在一个节点里面放很多的小节点元素,通过节点的横向扩展来解决树的高度问题。小节点元素是从左到右递增排列的。

B树相比B+树,在存储上有所不足。相比B+树,一个h=3可以存两千多万,而B数要存两千多万需要的h=6。使用B+树的结构,
将数据全部记录在叶子结点,非叶子结点就可以存储更多的索引,形成的树高度更小。

B+树(变种)

B+树与B树的结构类似。
B+树只有叶子节点存储数据,其他节点只存储苏索引,且叶子节点包含所有的索引字段和数据。
B+树节点内的索引元素都是排好序的,同时,非叶子节点的子索引元素都是子节点的第一个索引元素(冗余)。
B+树两个叶子节点之间用指针相连,通过指针可以快速定位其他节点的元素,很好的支持范围查找。

查询过程:

  1. 将节点加载到内存,在内存里比对待查询数据与索引字段,比对结果所指向的是叶子节点,则获取索引元素数据或数据地址;
  2. 不是指向叶子节点,再次进行操作1;

一个节点为一页,mysql默认设置的大小为16384(16KB)
按照mysql存储数据的大小,以bigint为例,占8B;指向子结点的索引节点为6B,则每一页可以存储
16384/(8+6)=1170个节点。以一个高度为3的B+树来算,叶子结点由于存储的是索引和数据,假设每一个节点存1K的数据,一页就是16K,
则总共可以记录 1170117016=21902400.两千万个。

存储引擎

存储引擎描述的是数据表而不是数据库,真实起作用的是在表上。

MyISAM

myisam生成三个数据文件:

  1. .frm结尾的是记录数据表相关信息的文件
  2. .MYD结尾的是记录真实数据信息的数据文件
  3. .MYI结尾的是记录索引信息的文件

myisam构建的索引,叶子结点存的是数据在文件中的地址,再通过地址去数据文件获取数据,又称非聚集索引

InnoDB

innodb生成二个数据文件:

  1. .frm结尾的是记录数据表相关信息的文件
  2. .idb结尾的是记录数据和索引信息的文件

innodb构建的索引,叶子结点存的是完整的数据记录,又称聚集索引

  1. 建议建主键,且推荐使用整形自增主键的原因:

    • 如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,
      而不用mysql帮我们去创建
    • 相比UUID等作为主键,推荐整形是比对方便,且节约空间
    • 使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素
  2. 主键索引与非主键索引的区别:

    • 主键索引叶子节点存储完整的数据,非主键索引叶子节点存储的是主键值
  3. 非主键索引叶子节点存主键值的原因:

    • 节约存储空间
    • 一致性

非主键索引查询数据时,需要先获取叶子节点存储的主键,再去主键索引里查完整的记录,这一操作又称为回表

联合索引

联合索引是根据构建索引的字段从左到右的顺序,通过比对字段数据进行排序,如果字段相同,则比对下一个字段的数据,以此来构成了联合索引。

最左前缀原则:在使用联合索引查询数据时,需要按照索引字段从左到右的顺序去添加条件,否则可能会不走索引;

上次编辑于:
贡献者: yanggl
- - - diff --git a/note/db/mysql/further/2305012001.html b/note/db/mysql/further/2305012001.html index 44301a82..2c9a0446 100644 --- a/note/db/mysql/further/2305012001.html +++ b/note/db/mysql/further/2305012001.html @@ -31,12 +31,12 @@ } - + -
跳至主要內容

MySQL索引

Yaien Blog原创大约 9 分钟DBMySQL索引

MySQL索引

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

处理问题

MySQL存储数据是存储在磁盘上的,而且两个相邻数据之间所处的磁盘位置不一定连续,让一条查询sql执行的时候,是需要去磁盘中一条条数据进行加载比较,每一次的加载都是一次磁盘IO,这是非常消耗性能的。处理这个问题的解决方案就是减少磁盘IO,尽可能控制读取次数。索引就是帮我们去整理这些数据,通过建立合适的数据结构,方便查询的时候减少对磁盘的IO。

数据结构对比

二叉树

索引
索引

如果现在要查询一条语句,条件是Col2 = 25,如果没有使用索引,顺序一条条的去查找比对,需要进行5次磁盘IO才能找到,而通过二叉树构成的索引,只需要两次就可以定位到数据,效率会高一些。

如果现在要查询一条Col1 = 6的语句,此时构成的二叉树就有可能是线性的,虽然都是查询6,但其实查找都是执行了六次,索引并没有对查询有任何帮助,索引mysql并不使用二叉树作为底层数据结构。

二叉树
二叉树

红黑树

红黑树其实也是一棵二叉数,又叫二叉平衡树。红黑树可以对新增的元素做平衡处理,比如上面1-6的元素,如果用红黑树实现就会是这样的情况。

红黑树
红黑树

虽然红黑树解决了二叉数线性树的弊端,但是在某些场景下对sql的查询仍然不是很友好,比如:当插入几百万行数据时,树的高度已经达到十几,如果刚好查找的数据在叶子节点,那至少需要遍历十几次才可以查找到。

我们要解决的本质是减少查询次数,如果我们控制了树的高度,就可以很好的解决查询的问题。

B树

B树的特点就是一个节点不在只存一个数据,而是一页数据,一页数据里面存放多个数据节点以及子节点地址,子节点又存储一页数据,这就可以实现将数据横向存储,控制了树的高度,减少了IO次数。

B树
B树

B树做索引的特点:

  • 叶节点具有相同的深度,叶节点的指针为空
  • 所有索引元素不重复
  • 节点中的数据索引从左到右递增排序
  • data里面存储的是索引数据所在行所处磁盘文件的地址

虽然B树解决接红黑树高度的问题,但其实mysql也不是用b树结构做索引,而是使用变种的B+树。

B+树

B+树可以算是B树的进阶版,其数据结构类似,节点都是存储一页数据,并且节点中的索引也是从左到右递增排好序。

B+树与B树的差异:

  1. 非叶子节点不存储data数据,只存储索引,而且是冗余存储,这样处理可以放更多的索引
  2. 所有的data数据都放在叶子节点,并且叶子节点包含所有的索引字段
  3. 叶子节点之间用指针连接,mysql变种后的是实现双指针,这样可以提高区间的访问性能
B+树
B+树

假设现在要通过索引树查找Col = 35的数据,步骤如下:

  • 从根节点出发
  • 将节点整页的数据加载到内存里
  • 如果是非叶子节点,通过查找算法进行数据比对找到35所在的子节点数据页磁盘地址,再次执行操作2
  • 如果是叶子节点,通过匹配到的索引记录的磁盘文件地址去加载所需数据

树高度问题如何解决?

mysql一页在磁盘上默认分配的大小为16kb,通过语句**SHOW GLOBAL STATUS LIKE 'Innodb_page_size'**可以查到这个值。

对于非叶子节点,假设我们使用bigint做主键,占用的是8Byte,记录叶子节点的元素指针在mysql设置的是6Byte,那一页就可以存放 16x1024/(8+6)个元素,计算后为1170个元素。

对于叶子节点,不仅仅存索引还存储data数据,这个data可能存储的是索引所在行的磁盘文件的地址,也有可能存储的是索引所在行的所有列数据。现假设叶子节点存放data为所有列数据,一个元素记录了1K数据,那一页就可以16个元素。

此时如果构建一个 h=3 的B+树,那叶子节点就可以存放 1170x1170x16 个元素,计算后为21902400个元素。

两千多万的数据,高度仅仅是3,查找一个元素的IO次数只用了3次,而如果没有走索引,那有可能扫几百万甚至千万次才可以找到元素,查找效率差的是好几个数量级,这也是索引为什么那么高效的原因。

对于Mysql来说,有可能根节点或者所有非叶子节点常驻内存,对于千万数据级别的表进行查找,直接在内存进行匹配获取索引行数据或者磁盘文件地址,只需要一次IO去获取数据就可以获取到需要的数据,查找的效率更高了。

如果使用B树存储,因为data是跟随索引元素下的,假设还是1kb,那一页数据就存16个元素,如果存储两千万的数据,树的高度大约为7,跟B+树没得比。

B Three 和 B+ Three的区别

  1. B Three的数据是放在节点元素上的,而且所有索引元素都不重复;B+ Three是将数据存放在叶子节点的元素上,并且非叶子节点冗余子节点的首个索引元素,叶子节点记录所有索引元素记录。
  2. B Three 叶子节点之间没有指针相连,当进行范围查询时,获取下一个叶子节点的数据又要从根节点再次检索;B+ Three叶子节点之间记录了双向指针,当进行范围查找时就可以通过这个双向指针快速定位下一个节点的数据。

HASH

mysql还有一种索引结构就是Hash索引,通过一个Hash桶(数组)还有链表形成,通过计算索引行的Hash值确定是在表的哪一个位置,再形成链表去存储数据,当查询的时候只需要对数据进行一次hash计算就可以定位出数据存储的位置,在很多时候Hash索引要比B+树索引更高效。

HASH
HASH

Hash虽然很高效,但是更多时候依旧选择B+树作为索引结构,原因主要有:

  1. Hash冲突问题
  2. 只能满足 = 的条件,对于范围查询 IN、大于、小于 等不支持,B+树通过叶子节点的双向指针可以很好的支持范围查询

存储引擎索引实现

Mysql中有很多的存储引擎实现,存储引擎描述的是数据库表的,真实作用是在表上而不是数据库。早期使用的是MyISAM,现在流行的是InnoDB,现在看看这两个的的实现。

MyISAM

MyISAM存储引擎生成的数据文件是三个

MyISAM数据文件
MyISAM数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.MYD结尾的是记录真实数据信息的数据文件
  3. 以.MYI结尾的是记录索引信息的文件

这就意味着MyISAM存储引擎的主键索引实现是讲数据与索引树分开的,通过两个文件分别存储索引树和数据信息,又叫非聚集索引,MyISAM主键、非主键索引都是非聚集索引

MyISAM
MyISAM

MyISAM如果要查找数据,通过MYI索引树文件,先定位到查找的数据,再通过叶子节点记录的行磁盘文件地址,再去MYD文件获取具体的数据信息。

InnoDB

InnoDB存储引擎生成的数据文件是两个

InnoDB数据文件
InnoDB数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.idb结尾的是记录数据和索引信息的文件

与MyISAM不同,InnoDB主键索引是将索引树和数据写在同一个同一个文件,叶子节点包含了完整列数据,又叫聚集索引,而对于非主键索引,叶子节点记录的是主键索引的主键值而不是完整的列数据,又称非聚集索引

InnoDB
InnoDB

问题:

  1. 为什么建议InnoDB表要建主键?

    答:如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,主键索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,而不用mysql帮我们去创建。

  2. 为什么推荐使用整形的自增主键?

 答:相比UUID等作为主键,推荐整形是比对方便,且节约空间。使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素。
+    
跳至主要內容

MySQL索引

Yaien Blog原创大约 10 分钟DBMySQL索引

MySQL索引

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

处理问题

MySQL存储数据是存储在磁盘上的,而且两个相邻数据之间所处的磁盘位置不一定连续,让一条查询sql执行的时候,是需要去磁盘中一条条数据进行加载比较,每一次的加载都是一次磁盘IO,这是非常消耗性能的。处理这个问题的解决方案就是减少磁盘IO,尽可能控制读取次数。索引就是帮我们去整理这些数据,通过建立合适的数据结构,方便查询的时候减少对磁盘的IO。

数据结构对比

二叉树

索引
索引

如果现在要查询一条语句,条件是Col2 = 25,如果没有使用索引,顺序一条条的去查找比对,需要进行5次磁盘IO才能找到,而通过二叉树构成的索引,只需要两次就可以定位到数据,效率会高一些。

如果现在要查询一条Col1 = 6的语句,此时构成的二叉树就有可能是线性的,虽然都是查询6,但其实查找都是执行了六次,索引并没有对查询有任何帮助,索引mysql并不使用二叉树作为底层数据结构。

二叉树
二叉树

红黑树

红黑树其实也是一棵二叉数,又叫二叉平衡树。红黑树可以对新增的元素做平衡处理,比如上面1-6的元素,如果用红黑树实现就会是这样的情况。

红黑树
红黑树

虽然红黑树解决了二叉数线性树的弊端,但是在某些场景下对sql的查询仍然不是很友好,比如:当插入几百万行数据时,树的高度已经达到十几,如果刚好查找的数据在叶子节点,那至少需要遍历十几次才可以查找到。

我们要解决的本质是减少查询次数,如果我们控制了树的高度,就可以很好的解决查询的问题。

B树

B树的特点就是一个节点不在只存一个数据,而是一页数据,一页数据里面存放多个数据节点以及子节点地址,子节点又存储一页数据,这就可以实现将数据横向存储,控制了树的高度,减少了IO次数。

B树
B树

B树做索引的特点:

  • 叶节点具有相同的深度,叶节点的指针为空
  • 所有索引元素不重复
  • 节点中的数据索引从左到右递增排序
  • data里面存储的是索引数据所在行所处磁盘文件的地址

虽然B树解决接红黑树高度的问题,但其实mysql也不是用b树结构做索引,而是使用变种的B+树。

B+树

B+树可以算是B树的进阶版,其数据结构类似,节点都是存储一页数据,并且节点中的索引也是从左到右递增排好序。

B+树与B树的差异:

  1. 非叶子节点不存储data数据,只存储索引,而且是冗余存储,这样处理可以放更多的索引
  2. 所有的data数据都放在叶子节点,并且叶子节点包含所有的索引字段
  3. 叶子节点之间用指针连接,mysql变种后的是实现双指针,这样可以提高区间的访问性能
B+树
B+树

假设现在要通过索引树查找Col = 35的数据,步骤如下:

  • 从根节点出发
  • 将节点整页的数据加载到内存里
  • 如果是非叶子节点,通过查找算法进行数据比对找到35所在的子节点数据页磁盘地址,再次执行操作2
  • 如果是叶子节点,通过匹配到的索引记录的磁盘文件地址去加载所需数据

树高度问题如何解决?

mysql一页在磁盘上默认分配的大小为16kb,通过语句**SHOW GLOBAL STATUS LIKE 'Innodb_page_size'**可以查到这个值。

对于非叶子节点,假设我们使用bigint做主键,占用的是8Byte,记录叶子节点的元素指针在mysql设置的是6Byte,那一页就可以存放 16x1024/(8+6)个元素,计算后为1170个元素。

对于叶子节点,不仅仅存索引还存储data数据,这个data可能存储的是索引所在行的磁盘文件的地址,也有可能存储的是索引所在行的所有列数据。现假设叶子节点存放data为所有列数据,一个元素记录了1K数据,那一页就可以16个元素。

此时如果构建一个 h=3 的B+树,那叶子节点就可以存放 1170x1170x16 个元素,计算后为21902400个元素。

两千多万的数据,高度仅仅是3,查找一个元素的IO次数只用了3次,而如果没有走索引,那有可能扫几百万甚至千万次才可以找到元素,查找效率差的是好几个数量级,这也是索引为什么那么高效的原因。

对于Mysql来说,有可能根节点或者所有非叶子节点常驻内存,对于千万数据级别的表进行查找,直接在内存进行匹配获取索引行数据或者磁盘文件地址,只需要一次IO去获取数据就可以获取到需要的数据,查找的效率更高了。

如果使用B树存储,因为data是跟随索引元素下的,假设还是1kb,那一页数据就存16个元素,如果存储两千万的数据,树的高度大约为7,跟B+树没得比。

B Three 和 B+ Three的区别

  1. B Three的数据是放在节点元素上的,而且所有索引元素都不重复;B+ Three是将数据存放在叶子节点的元素上,并且非叶子节点冗余子节点的首个索引元素,叶子节点记录所有索引元素记录。
  2. B Three 叶子节点之间没有指针相连,当进行范围查询时,获取下一个叶子节点的数据又要从根节点再次检索;B+ Three叶子节点之间记录了双向指针,当进行范围查找时就可以通过这个双向指针快速定位下一个节点的数据。

HASH

mysql还有一种索引结构就是Hash索引,通过一个Hash桶(数组)还有链表形成,通过计算索引行的Hash值确定是在表的哪一个位置,再形成链表去存储数据,当查询的时候只需要对数据进行一次hash计算就可以定位出数据存储的位置,在很多时候Hash索引要比B+树索引更高效。

HASH
HASH

Hash虽然很高效,但是更多时候依旧选择B+树作为索引结构,原因主要有:

  1. Hash冲突问题
  2. 只能满足 = 的条件,对于范围查询 IN、大于、小于 等不支持,B+树通过叶子节点的双向指针可以很好的支持范围查询

存储引擎索引实现

Mysql中有很多的存储引擎实现,存储引擎描述的是数据库表的,真实作用是在表上而不是数据库。早期使用的是MyISAM,现在流行的是InnoDB,现在看看这两个的的实现。

MyISAM

MyISAM存储引擎生成的数据文件是三个

MyISAM数据文件
MyISAM数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.MYD结尾的是记录真实数据信息的数据文件
  3. 以.MYI结尾的是记录索引信息的文件

这就意味着MyISAM存储引擎的主键索引实现是讲数据与索引树分开的,通过两个文件分别存储索引树和数据信息,又叫非聚集索引,MyISAM主键、非主键索引都是非聚集索引

MyISAM
MyISAM

MyISAM如果要查找数据,通过MYI索引树文件,先定位到查找的数据,再通过叶子节点记录的行磁盘文件地址,再去MYD文件获取具体的数据信息。

InnoDB

InnoDB存储引擎生成的数据文件是两个

InnoDB数据文件
InnoDB数据文件
  1. 以.frm结尾的是记录数据表相关信息的文件
  2. 以.idb结尾的是记录数据和索引信息的文件

与MyISAM不同,InnoDB主键索引是将索引树和数据写在同一个同一个文件,叶子节点包含了完整列数据,又叫聚集索引,而对于非主键索引,叶子节点记录的是主键索引的主键值而不是完整的列数据,又称非聚集索引

InnoDB
InnoDB

问题:

  1. 为什么建议InnoDB表要建主键?

    答:如果表没有建主键,mysql选择一列数据都不相等的或者自动建一列隐藏列作为数据的唯一标识,主键索引则通过该隐藏列来进行构建。为了节约资源,建议加上主键,而不用mysql帮我们去创建。

  2. 为什么推荐使用整形的自增主键?

 答:相比UUID等作为主键,推荐整形是比对方便,且节约空间。使用自增主键时,新插入的元素总是会加到最后一个节点,当节点元素满的时候会开新的节点去存,并不会影响已经构建好的索引,如果是存在中间,就可能会破坏已经构建好的索引,重新调整索引元素。
 
  1. 为什么非主键索引叶子节点存储的是主键值?
 答:InnoDB引擎每一张表都会有一个主键索引,如果我们建表时有指定列为主键,则使用主键去构建主键索引,如果没有指定主键,那会选择一列数据不重复的列或者建一列隐藏列作为主键再去构建主键索引。而非主键索引之所以只记录主键索引的主键值,主要是为了节约存储空间,还有一个就是为了保证插入数据时非主键索引与主键索引的一致性。
-
上次编辑于:
贡献者: yanggl
- +

联合索引

联合索引是根据构建索引的字段从左到右的顺序,通过比对字段数据进行排序,如果字段相同,则比对下一个字段的数据,以此来构成了联合索引。

最左前缀原则:在使用联合索引查询数据时,需要按照索引字段从左到右的顺序去添加条件,否则可能会不走索引;

上次编辑于:
贡献者: yanggl
+ diff --git a/note/db/mysql/further/2305020500.html b/note/db/mysql/further/2305020500.html index 20cae8d6..eb13ed78 100644 --- a/note/db/mysql/further/2305020500.html +++ b/note/db/mysql/further/2305020500.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容

Explain详解与索引最佳实践

Yaien Blog原创大约 5 分钟DBMySQL索引

Explain详解与索引最佳实践

Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置
一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。

如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。

官方地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.htmlopen in new window

字段详解

Explain字段
Explain字段

id

属于select的编号,有几个select就会有几个id,与select出现的顺序一致。

id值越大,执行优先级越高;相同则从上往下执行;为null最后执行。

select_type

标识对应行使简单查询还是复杂查询。

  1. simple:简单查询,查询中不含有子查询和union
  2. primary:复杂查询中最外层select
  3. subquery:包含在select中的子查询(不在from子句中)
  4. derived:包含在from子句中的子查询。Mysql会讲结果存在临时表中。

table

表示行正在访问的表。

当from子句中有子查询,table展示<derivenN>,表示当前查询依赖id = N的查询

当有union时,table展示<union1,2>,1和2表示参与union的行id

type

表示关联类型访问类型,即mysql决定如何查找表中的行,查找数据行记录的大概范围

从优到劣依次为:system > const > eq_ref > ref > range > index > All

  • null:能够在优化阶段分解查询语句,在执行阶段不需要再访问表或索引
  • system:是const的特例,表里只有一条元组匹配时为system
  • const:mysql能对查询的某部分进行优化并转化为一个常量,用于主键索引或唯一索引
    的所有列与常数比较时,所以表最多有一个匹配行
  • eq_ref:主键索引或唯一索引的所有部分都被连接使用,最多只会返回一条符合的记录。
  • ref:与eq_ref类型,使用的是普通索引或唯一索引部分前缀,索引要和某个值比较,可能会找到多个符合条件的行
  • range:通常出现在 IN、between、>、<等操作中。使用一个索引来检索给定范围
  • index:扫描全索引就能拿到结果,一般扫描某个二级索引,这种扫描是直接扫叶子节点,速度较慢,称为覆盖索引
  • all:全表扫描,扫描聚集索引所有叶子节点,数据大,比index更慢

一般得保证查询达到range,最好能达到ref

partitions

如果查询是基于分区表的话,会显示查询将访问的分区

此字段与在5.7以前还需要再加上 partitions 关键字才会展示

possible_keys

显示本次查询可能会使用哪些索引来完成。

key

显示实际采用的索引

key_len

显示索引里使用的字节数,通过这个值可以算出具体使用了索引中的那些列

计算规则如下:

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字或字母占1个字节,
    一个汉字占3个字节
    • char(n):如果存汉字长度就是3n字节
  • varchar(n):如果存汉字则长度是3n+2字节,加的2字节用来存储字符串长度,因为varchar是变长字符串
  • 数值类型
    • tinyint:1字节
  • smallint:1字节
  • int:4字节
    • bigint:8字节
  • 时间类型
    • data:3字节
  • timestamp:4字节
  • datetime:8字节
  • 如果字段允许为null,需要1字节记录是否为null

ref

显示在key列记录的索引中,表查找所用到的列或常量

rows

显示本行查询估计要读取并检测的行数,不是结果集里的行数

filtered

filtered是一个半分比的值.rows*filtered/100可以估算出将要和explain中前一个表进行连接的行数(id值比当前表id值小的表)

此字段与在5.7以前还需要再加上 extended 关键字才会展示

Extra

展示额外信息,重要值如下:

  1. Using index:使用了覆盖索引
  2. Using where:使用where语句来处理结果,并且查询的列未被索引覆盖
  3. Using index condition:查询的列不完全被覆盖,where条件中是一个前导列的范围
  4. Using temporary:mysql建了一张临时表来处理查询
  5. Using filesort:使用外部排序而不是索引排序,数据较小时从内存排,否则需要在磁盘完成
  6. Select tables optimized way:使用了某些聚合函数(min()、max()等)来访问存在索引的某个字段

索引最佳实践

  • 全值匹配
  • 最左前缀原则
  • 在索引列上做操作(计算、函数、自动或手动类型转换)会导致索引失效
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引,减少select * 的语句
  • 使用不等于(!=或<>)、not in、not exists会导致索引失效
  • 大于、小于、大于等于、小于等于会根据检索比例、表大小等因素整体评估是否使用索引
  • is null 、 is not null 一般情况下也会导致索引失效
  • like 以通配符开头('%xa')也会导致索引失效
上次编辑于:
贡献者: yanggl
- +
跳至主要內容

Explain详解与索引最佳实践

Yaien Blog原创大约 5 分钟DBMySQL索引

Explain详解与索引最佳实践

Explain可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。在select之前加上explain关键字,Mysql会在查询上设置
一个标记,执行查询时就不是执行这条SQL而是会返回执行计划的信息。

如果from中包含子查询,仍然会执行子查询,并将结果放入临时表中。在查询中,每个表会输出一行,如果有两个表通过join连接查询,则会输出两行。紧随Explain的语句后,通过show warnings命令可以得到优化后的查询语句,从而看出优化器优化了什么。

官方地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.htmlopen in new window

字段详解

Explain字段
Explain字段

id

属于select的编号,有几个select就会有几个id,与select出现的顺序一致。

id值越大,执行优先级越高;相同则从上往下执行;为null最后执行。

select_type

标识对应行使简单查询还是复杂查询。

  1. simple:简单查询,查询中不含有子查询和union
  2. primary:复杂查询中最外层select
  3. subquery:包含在select中的子查询(不在from子句中)
  4. derived:包含在from子句中的子查询。Mysql会讲结果存在临时表中。

table

表示行正在访问的表。

当from子句中有子查询,table展示<derivenN>,表示当前查询依赖id = N的查询

当有union时,table展示<union1,2>,1和2表示参与union的行id

type

表示关联类型访问类型,即mysql决定如何查找表中的行,查找数据行记录的大概范围

从优到劣依次为:system > const > eq_ref > ref > range > index > All

  • null:能够在优化阶段分解查询语句,在执行阶段不需要再访问表或索引
  • system:是const的特例,表里只有一条元组匹配时为system
  • const:mysql能对查询的某部分进行优化并转化为一个常量,用于主键索引或唯一索引
    的所有列与常数比较时,所以表最多有一个匹配行
  • eq_ref:主键索引或唯一索引的所有部分都被连接使用,最多只会返回一条符合的记录。
  • ref:与eq_ref类型,使用的是普通索引或唯一索引部分前缀,索引要和某个值比较,可能会找到多个符合条件的行
  • range:通常出现在 IN、between、>、<等操作中。使用一个索引来检索给定范围
  • index:扫描全索引就能拿到结果,一般扫描某个二级索引,这种扫描是直接扫叶子节点,速度较慢,称为覆盖索引
  • all:全表扫描,扫描聚集索引所有叶子节点,数据大,比index更慢

一般得保证查询达到range,最好能达到ref

partitions

如果查询是基于分区表的话,会显示查询将访问的分区

此字段与在5.7以前还需要再加上 partitions 关键字才会展示

possible_keys

显示本次查询可能会使用哪些索引来完成。

key

显示实际采用的索引

key_len

显示索引里使用的字节数,通过这个值可以算出具体使用了索引中的那些列

计算规则如下:

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字或字母占1个字节,
    一个汉字占3个字节
    • char(n):如果存汉字长度就是3n字节
  • varchar(n):如果存汉字则长度是3n+2字节,加的2字节用来存储字符串长度,因为varchar是变长字符串
  • 数值类型
    • tinyint:1字节
  • smallint:1字节
  • int:4字节
    • bigint:8字节
  • 时间类型
    • data:3字节
  • timestamp:4字节
  • datetime:8字节
  • 如果字段允许为null,需要1字节记录是否为null

ref

显示在key列记录的索引中,表查找所用到的列或常量

rows

显示本行查询估计要读取并检测的行数,不是结果集里的行数

filtered

filtered是一个半分比的值.rows*filtered/100可以估算出将要和explain中前一个表进行连接的行数(id值比当前表id值小的表)

此字段与在5.7以前还需要再加上 extended 关键字才会展示

Extra

展示额外信息,重要值如下:

  1. Using index:使用了覆盖索引
  2. Using where:使用where语句来处理结果,并且查询的列未被索引覆盖
  3. Using index condition:查询的列不完全被覆盖,where条件中是一个前导列的范围
  4. Using temporary:mysql建了一张临时表来处理查询
  5. Using filesort:使用外部排序而不是索引排序,数据较小时从内存排,否则需要在磁盘完成
  6. Select tables optimized way:使用了某些聚合函数(min()、max()等)来访问存在索引的某个字段

索引最佳实践

  • 全值匹配
  • 最左前缀原则
  • 在索引列上做操作(计算、函数、自动或手动类型转换)会导致索引失效
  • 存储引擎不能使用索引中范围条件右边的列
  • 尽量使用覆盖索引,减少select * 的语句
  • 使用不等于(!=或<>)、not in、not exists会导致索引失效
  • 大于、小于、大于等于、小于等于会根据检索比例、表大小等因素整体评估是否使用索引
  • is null 、 is not null 一般情况下也会导致索引失效
  • like 以通配符开头('%xa')也会导致索引失效
上次编辑于:
贡献者: yanggl
+ diff --git a/note/db/mysql/further/2305020501.html b/note/db/mysql/further/2305020501.html index 4a3a450f..81449a1c 100644 --- a/note/db/mysql/further/2305020501.html +++ b/note/db/mysql/further/2305020501.html @@ -31,15 +31,15 @@ } - + -
跳至主要內容

SQL底层执行原理

Yaien Blog原创大约 6 分钟DBMySQL索引

SQL底层执行原理

介绍SQL底层执行原理以及流程

Mysql结构

大体来说,可以分为Server层和引擎层两部分

server层

server层主要包括连接器、查询缓存、分析器、优化器、执行器等。这些基本涵盖了mysql的大多数核心
服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

连接器

连接器是用来建立Mysql客户端与服务端之间通信的,客户端要想发起通信都必须先跟服务端建立通信连接

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证身份,这个时候用的就是你输入的用户名和密码来进行认证。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。文本中这个图是 show
processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接,关闭连接 kill <id>

客户端如果长时间不发送command到Server端,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。

-- 查看wait_timeout
+    
跳至主要內容

SQL底层执行原理

Yaien Blog原创大约 6 分钟DBMySQL索引

SQL底层执行原理

介绍SQL底层执行原理以及流程

Mysql结构

大体来说,可以分为Server层和引擎层两部分

server层

server层主要包括连接器、查询缓存、分析器、优化器、执行器等。这些基本涵盖了mysql的大多数核心
服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

连接器

连接器是用来建立Mysql客户端与服务端之间通信的,客户端要想发起通信都必须先跟服务端建立通信连接

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证身份,这个时候用的就是你输入的用户名和密码来进行认证。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。文本中这个图是 show
processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接,关闭连接 kill <id>

客户端如果长时间不发送command到Server端,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。

-- 查看wait_timeout
 show global variables like 'wait_timeout';
 
 -- 设置全局服务器关闭非交互连接之前等待活动的秒数
 set global wait_timeout = 28800; 
-

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

开发当中我们大多数时候用的都是长连接,把连接放在Pool内进行管理,但是长连接有些时候会导致 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

  • 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  • 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

连接建立完成后就是写sql然后执行。MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。

如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。

大多数情况下查询缓存就是个鸡肋

  • 查询缓存往往弊大于利。查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。
  • 一般建议大家在静态表里使用查询缓存,什么叫静态表呢?就是一般我们极少更新的表。比如,一个系统配置表、字典表,那这张表上的查询才适合使用查询缓存。好在 MySQL 也提供了这种“按需使用”的方式。你可以将my.cnf参数 query_cache_type 设置成 DEMAND。
  • mysql8.0已经移除了查询缓存功能

分析器

分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。

MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。

做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。

优化器

优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。

执行器

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误;如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

引擎层

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是
InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说如果我们在create table时不指定表的存储引擎类型,默认会给你设置存储引擎为InnoDB。

上次编辑于:
贡献者: yanggl
- +

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

开发当中我们大多数时候用的都是长连接,把连接放在Pool内进行管理,但是长连接有些时候会导致 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

  • 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  • 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

连接建立完成后就是写sql然后执行。MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。

如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。

大多数情况下查询缓存就是个鸡肋

  • 查询缓存往往弊大于利。查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。
  • 一般建议大家在静态表里使用查询缓存,什么叫静态表呢?就是一般我们极少更新的表。比如,一个系统配置表、字典表,那这张表上的查询才适合使用查询缓存。好在 MySQL 也提供了这种“按需使用”的方式。你可以将my.cnf参数 query_cache_type 设置成 DEMAND。
  • mysql8.0已经移除了查询缓存功能

分析器

分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。

MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。

做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。

优化器

优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。

执行器

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误;如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

引擎层

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是
InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说如果我们在create table时不指定表的存储引擎类型,默认会给你设置存储引擎为InnoDB。

上次编辑于:
贡献者: yanggl
+ diff --git a/note/db/mysql/further/2305032100.html b/note/db/mysql/further/2305032100.html index 98f20984..201734b8 100644 --- a/note/db/mysql/further/2305032100.html +++ b/note/db/mysql/further/2305032100.html @@ -31,318 +31,311 @@ } - + -
跳至主要內容

索引优化实战

Yaien Blog原创大约 11 分钟DBMySQL索引

索引优化实战

MYSQL 索引优化实战笔记

优化实战

联合索引第一个字段用范围不会走索引

mysql内部优化规则可能会觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不知直接全表扫

强制走索引

-- 语句
-EXPLAIN
-SELECT *
-FROM user force index(idx)
-

添加强制索引可以让语句走索引,但是最总查找的效率不一定会比扫全表高,因为回表效率不高

覆盖索引优化

对于不走索引的语句,可以尝试使用覆盖索引来进行优化

in和or在表数据大会走索引,反之不会

like语句一般情况下都会走索引

like语句看着其实跟大于小于号差不多,之所以会走索引是因为用到了索引下推

索引下推

联合索引是按照最左前缀原则来进行匹配,正常情况下联合索引首个字段就使用like,那只会匹配到这个字段, 后面的因为不能确保是有序的就无法再利用索引。

5.6版本以前,首个字段就停止索引匹配,那就会拿这些对应索引的主键逐个回表找数据,再比对后序字段是否符合

5.6对此进行优化,在索引便利过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。这就是索引下推。

索引下推回减少回表次数,对于innodb引擎只能适用于二级索引,对主键索引并不适用

trace工具

trace


--- 开启trace
+    
跳至主要內容

索引优化实战

Yaien Blog原创大约 11 分钟DBMySQL索引

索引优化实战

MYSQL 索引优化实战笔记

优化实战

联合索引第一个字段用范围不会走索引

mysql内部优化规则可能会觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不知直接全表扫

强制走索引

-- 语句
+EXPLAIN SELECT * FROM user force index(idx)
+

添加强制索引可以让语句走索引,但是最总查找的效率不一定会比扫全表高,因为回表效率不高

覆盖索引优化

对于不走索引的语句,可以尝试使用覆盖索引来进行优化

in和or在表数据大会走索引,反之不会

like语句一般情况下都会走索引

like语句看着其实跟大于小于号差不多,之所以会走索引是因为用到了索引下推

索引下推

联合索引是按照最左前缀原则来进行匹配,正常情况下联合索引首个字段就使用like,那只会匹配到这个字段, 后面的因为不能确保是有序的就无法再利用索引。

5.6版本以前,首个字段就停止索引匹配,那就会拿这些对应索引的主键逐个回表找数据,再比对后序字段是否符合。5.6对此进行优化,在索引便利过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。这就是索引下推。

索引下推回减少回表次数,对于innodb引擎只能适用于二级索引,对主键索引并不适用

trace工具

trace

-- 开启trace
 set session optimizer_trace = "enabled=on",end_markers_in_json=on;
 
 -- 一起执行查询
-select *
-from employees
-where name > 'a'
-order by position;
-SELECT *
-FROM information_schema.OPTIMIZER_TRACE;
+select * from employees where name > 'a' order by position;
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
 
 -- 关闭 trace
 set session optimizer_trace = "enabled=off";
-

结果集

{
-  "steps": [
-    {
-      "join_preparation": {
+

结果集

{
+  "steps": [
+    {
+      "join_preparation": {
         /* 第一阶段:准备阶段,格式化SQL */
-        "select#": 1,
-        "steps": [
-          {
-            "expanded_query": "/* select#1 */ select `t_menu`.`menu_id` AS `menu_id`,`t_menu`.`menu_name` AS `menu_name`,`t_menu`.`menu_url` AS `menu_url`,`t_menu`.`parent_id` AS `parent_id`,`t_menu`.`level` AS `level`,`t_menu`.`icon` AS `icon`,`t_menu`.`order_by` AS `order_by`,`t_menu`.`hidden` AS `hidden`,`t_menu`.`remark` AS `remark` from `t_menu` where (`t_menu`.`menu_name` > 'a') order by `t_menu`.`parent_id`"
-          }
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `t_menu`.`menu_id` AS `menu_id`,`t_menu`.`menu_name` AS `menu_name`,`t_menu`.`menu_url` AS `menu_url`,`t_menu`.`parent_id` AS `parent_id`,`t_menu`.`level` AS `level`,`t_menu`.`icon` AS `icon`,`t_menu`.`order_by` AS `order_by`,`t_menu`.`hidden` AS `hidden`,`t_menu`.`remark` AS `remark` from `t_menu` where (`t_menu`.`menu_name` > 'a') order by `t_menu`.`parent_id`"
+          }
         ]
         /* steps */
-      }
+      }
       /* join_preparation */
-    },
-    {
-      "join_optimization": {
+    },
+    {
+      "join_optimization": {
         /* 第二阶段:SQL优化阶段 */
-        "select#": 1,
-        "steps": [
-          {
-            "condition_processing": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
               /* 条件处理 */
-              "condition": "WHERE",
-              "original_condition": "(`t_menu`.`menu_name` > 'a')",
-              "steps": [
-                {
-                  "transformation": "equality_propagation",
-                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
-                },
-                {
-                  "transformation": "constant_propagation",
-                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
-                },
-                {
-                  "transformation": "trivial_condition_removal",
-                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
-                }
+              "condition": "WHERE",
+              "original_condition": "(`t_menu`.`menu_name` > 'a')",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(`t_menu`.`menu_name` > 'a')"
+                }
               ]
               /* steps */
-            }
+            }
             /* condition_processing */
-          },
-          {
-            "substitute_generated_columns": {
-            }
+          },
+          {
+            "substitute_generated_columns": {
+            }
             /* substitute_generated_columns */
-          },
-          {
-            "table_dependencies": [
+          },
+          {
+            "table_dependencies": [
               /* 表依赖详情 */
-              {
-                "table": "`t_menu`",
-                "row_may_be_null": false,
-                "map_bit": 0,
-                "depends_on_map_bits": [
+              {
+                "table": "`t_menu`",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
                 ]
                 /* depends_on_map_bits */
-              }
+              }
             ]
             /* table_dependencies */
-          },
-          {
-            "ref_optimizer_key_uses": [
+          },
+          {
+            "ref_optimizer_key_uses": [
             ]
             /* ref_optimizer_key_uses */
-          },
-          {
-            "rows_estimation": [
+          },
+          {
+            "rows_estimation": [
               /* 预估表的访问成本 */
-              {
-                "table": "`t_menu`",
-                "range_analysis": {
-                  "table_scan": {
+              {
+                "table": "`t_menu`",
+                "range_analysis": {
+                  "table_scan": {
                     /* 全表扫描情况 */
-                    "rows": 15,
+                    "rows": 15,
                     /* 扫描行数 */
-                    "cost": 6.1
+                    "cost": 6.1
                     /* 扫描成本 */
-                  }
+                  }
                   /* table_scan */,
-                  "potential_range_indexes": [
+                  "potential_range_indexes": [
                     /* 查询可能使用的索引 */
-                    {
-                      "index": "PRIMARY",
+                    {
+                      "index": "PRIMARY",
                       /* 主键索引 */
-                      "usable": false,
-                      "cause": "not_applicable"
-                    },
-                    {
-                      "index": "idx_test",
+                      "usable": false,
+                      "cause": "not_applicable"
+                    },
+                    {
+                      "index": "idx_test",
                       /* 二级索引 */
-                      "usable": true,
-                      "key_parts": [
+                      "usable": true,
+                      "key_parts": [
                         "menu_name",
                         "menu_url",
                         "parent_id",
                         "menu_id"
                       ]
                       /* key_parts */
-                    }
+                    }
                   ]
                   /* potential_range_indexes */,
-                  "setup_range_conditions": [
+                  "setup_range_conditions": [
                   ]
                   /* setup_range_conditions */,
-                  "group_index_range": {
-                    "chosen": false,
-                    "cause": "not_group_by_or_distinct"
-                  }
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  }
                   /* group_index_range */,
-                  "analyzing_range_alternatives": {
+                  "analyzing_range_alternatives": {
                     /* 分析各个索引使用成本 */
-                    "range_scan_alternatives": [
-                      {
-                        "index": "idx_test",
-                        "ranges": [
+                    "range_scan_alternatives": [
+                      {
+                        "index": "idx_test",
+                        "ranges": [
                           "a < menu_name"
                           /* 索引使用范围 */
                         ]
                         /* ranges */,
-                        "index_dives_for_eq_ranges": true,
-                        "rowid_ordered": false,
+                        "index_dives_for_eq_ranges": true,
+                        "rowid_ordered": false,
                         /* 使用该索引获取的记录是否按照主键排序 */
-                        "using_mrr": false,
-                        "index_only": false,
+                        "using_mrr": false,
+                        "index_only": false,
                         /* 是否使用覆盖索引 */
-                        "rows": 15,
+                        "rows": 15,
                         /* 扫描行数 */
-                        "cost": 19.01,
+                        "cost": 19.01,
                         /* 使用成本 */
-                        "chosen": false,
+                        "chosen": false,
                         /* 是否选择 */
-                        "cause": "cost"
-                      }
+                        "cause": "cost"
+                      }
                     ]
                     /* range_scan_alternatives */,
-                    "analyzing_roworder_intersect": {
-                      "usable": false,
-                      "cause": "too_few_roworder_scans"
-                    }
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    }
                     /* analyzing_roworder_intersect */
-                  }
+                  }
                   /* analyzing_range_alternatives */
-                }
+                }
                 /* range_analysis */
-              }
+              }
             ]
             /* rows_estimation */
-          },
-          {
-            "considered_execution_plans": [
-              {
-                "plan_prefix": [
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "plan_prefix": [
                 ]
                 /* plan_prefix */,
-                "table": "`t_menu`",
-                "best_access_path": {
+                "table": "`t_menu`",
+                "best_access_path": {
                   /* 最有访问路径 */
-                  "considered_access_paths": [
+                  "considered_access_paths": [
                     /* 最终选择的访问路径 */
-                    {
-                      "rows_to_scan": 15,
-                      "access_type": "scan",
+                    {
+                      "rows_to_scan": 15,
+                      "access_type": "scan",
                       /* 访问类型:scan为全表扫描 */
-                      "resulting_rows": 15,
-                      "cost": 4,
-                      "chosen": true,
-                      "use_tmp_table": true
-                    }
+                      "resulting_rows": 15,
+                      "cost": 4,
+                      "chosen": true,
+                      "use_tmp_table": true
+                    }
                   ]
                   /* considered_access_paths */
-                }
+                }
                 /* best_access_path */,
-                "condition_filtering_pct": 100,
-                "rows_for_plan": 15,
-                "cost_for_plan": 4,
-                "sort_cost": 15,
-                "new_cost_for_plan": 19,
-                "chosen": true
-              }
+                "condition_filtering_pct": 100,
+                "rows_for_plan": 15,
+                "cost_for_plan": 4,
+                "sort_cost": 15,
+                "new_cost_for_plan": 19,
+                "chosen": true
+              }
             ]
             /* considered_execution_plans */
-          },
-          {
-            "attaching_conditions_to_tables": {
-              "original_condition": "(`t_menu`.`menu_name` > 'a')",
-              "attached_conditions_computation": [
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "(`t_menu`.`menu_name` > 'a')",
+              "attached_conditions_computation": [
               ]
               /* attached_conditions_computation */,
-              "attached_conditions_summary": [
-                {
-                  "table": "`t_menu`",
-                  "attached": "(`t_menu`.`menu_name` > 'a')"
-                }
+              "attached_conditions_summary": [
+                {
+                  "table": "`t_menu`",
+                  "attached": "(`t_menu`.`menu_name` > 'a')"
+                }
               ]
               /* attached_conditions_summary */
-            }
+            }
             /* attaching_conditions_to_tables */
-          },
-          {
-            "clause_processing": {
-              "clause": "ORDER BY",
-              "original_clause": "`t_menu`.`parent_id`",
-              "items": [
-                {
-                  "item": "`t_menu`.`parent_id`"
-                }
+          },
+          {
+            "clause_processing": {
+              "clause": "ORDER BY",
+              "original_clause": "`t_menu`.`parent_id`",
+              "items": [
+                {
+                  "item": "`t_menu`.`parent_id`"
+                }
               ]
               /* items */,
-              "resulting_clause_is_simple": true,
-              "resulting_clause": "`t_menu`.`parent_id`"
-            }
+              "resulting_clause_is_simple": true,
+              "resulting_clause": "`t_menu`.`parent_id`"
+            }
             /* clause_processing */
-          },
-          {
-            "reconsidering_access_paths_for_index_ordering": {
-              "clause": "ORDER BY",
-              "steps": [
+          },
+          {
+            "reconsidering_access_paths_for_index_ordering": {
+              "clause": "ORDER BY",
+              "steps": [
               ]
               /* steps */,
-              "index_order_summary": {
-                "table": "`t_menu`",
-                "index_provides_order": false,
-                "order_direction": "undefined",
-                "index": "unknown",
-                "plan_changed": false
-              }
+              "index_order_summary": {
+                "table": "`t_menu`",
+                "index_provides_order": false,
+                "order_direction": "undefined",
+                "index": "unknown",
+                "plan_changed": false
+              }
               /* index_order_summary */
-            }
+            }
             /* reconsidering_access_paths_for_index_ordering */
-          },
-          {
-            "refine_plan": [
-              {
-                "table": "`t_menu`"
-              }
+          },
+          {
+            "refine_plan": [
+              {
+                "table": "`t_menu`"
+              }
             ]
             /* refine_plan */
-          }
+          }
         ]
         /* steps */
-      }
+      }
       /* join_optimization */
-    },
-    {
-      "join_execution": {
+    },
+    {
+      "join_execution": {
         /* 第三阶段:执行阶段 */
-        "select#": 1,
-        "steps": [
-          {
-            "filesort_information": [
-              {
-                "direction": "asc",
-                "table": "`t_menu`",
-                "field": "parent_id"
-              }
+        "select#": 1,
+        "steps": [
+          {
+            "filesort_information": [
+              {
+                "direction": "asc",
+                "table": "`t_menu`",
+                "field": "parent_id"
+              }
             ]
             /* filesort_information */,
-            "filesort_priority_queue_optimization": {
-              "usable": false,
-              "cause": "not applicable (no LIMIT)"
-            }
+            "filesort_priority_queue_optimization": {
+              "usable": false,
+              "cause": "not applicable (no LIMIT)"
+            }
             /* filesort_priority_queue_optimization */,
-            "filesort_execution": [
+            "filesort_execution": [
             ]
             /* filesort_execution */,
-            "filesort_summary": {
+            "filesort_summary": {
               /* 文件排序信息 */
-              "rows": 15,
+              "rows": 15,
               /* 预计扫描行数 */
-              "examined_rows": 15,
+              "examined_rows": 15,
               /* 参与排序行 */
-              "number_of_tmp_files": 0,
+              "number_of_tmp_files": 0,
               /* 使用临时文件个数,如果为0表示全部用sort_puffer内存排序 */
-              "sort_buffer_size": 14656,
+              "sort_buffer_size": 14656,
               /* 排序缓存大小 */
-              "sort_mode": "<sort_key, rowid>"
+              "sort_mode": "<sort_key, rowid>"
               /* 排序方式,双路 <sort_key, packed_additional_fields>:单路排序*/
-            }
+            }
             /* filesort_summary */
-          }
+          }
         ]
         /* steps */
-      }
+      }
       /* join_execution */
-    }
+    }
   ]
   /* steps */
-}
-

ORDER BY 与 GROUP BY 优化

order by排序的优化主要是利用索引已经排好序的规律来优化,如果没有使用到会通过文件进行排序消耗性能

  • 使用order by 联合索引,查询条件与排序条件中间字段不能断
  • order by 多个字段时,顺序要与索引字段一致
  • 使用降序排序不会走索引排序
  • in 不会走索引排序,因为结果集不确定是否有序
  • 使用 > 条件的不会走索引排序,可能是数据量太大

总结

  1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2. order by满足两种情况会使用Using index。
    1. order by语句使用索引最左前列。
    2. 使用where子句与order by子句条件列组合满足索引最左前列。
  3. 尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  4. 如果order by的条件不在索引列上,就会产生Using filesort。
  5. 能用覆盖索引尽量用覆盖索引
  6. group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by
    null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。

文件排序原理

排序方式

单路排序

一次性取满足条件的数据的所有字段,然后在sort_buffer中进行排序。

trace工具的sort_mode显示<sort_key, additional_fields>或<sort_key, packed_additional_fields>

双路排序

双路排序又称回表排序。首先根据条件查询出相应的字段,然后取排序字段和主键在sort_buffer中排序,拍完序再去主键索引取信息

trace工具的sort_mode显示<sort_key, rowid>.

MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来 判断使用哪种排序模式。

  • 如果字段的总长度小于max_length_for_sort_data,那么使用单路排序模式;
  • 如果字段的总长度大于max_length_for_sort_data,那么使用双路排序模式;

排序过程

假设条件为 where name = 'a'

单路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出所有字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 返回结果

双路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出ID和排序字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 按照排序好的ID值回到原表取出所欲字段值然后返回

其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer
中进行排序,然后再通过主键回到原表查询需要的字段。

如果 MySQL 排序内存 sort_buffer 配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data
配置小点,让优化器选择使用双路排序算法,可以在sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据。

如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择全字段排序(单路排序),把需要的字段放到 sort_buffer
中,这样排序后就会直接从内存里返回查询结果了。

所以,MySQL通过 max_length_for_sort_data 这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率。

注意:如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。

设计原则

1. 代码先行,索引后上

不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗? 这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

2. 联合索引尽量覆盖条件

比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group
by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

3. 不要在小基数字段上建立索引

索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。
如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。
一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4. 长字符串我们可以采用前缀索引

尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。
当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的。 对于这种varchar(255)
的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY index(name(20)
,age,position)。
此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来完整的name字段值进行比对。
但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排序是没法用上索引的, group by也是同理。所以这里大家要对前缀索引有一个了解。

5. where与order by冲突时优先where

在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?
一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。
因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。

6. 基于慢sql查询做优化

可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。

慢sql查询查阅:http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7Bopen in new window

上次编辑于:
贡献者: yanggl
- +} +

ORDER BY 与 GROUP BY 优化

order by排序的优化主要是利用索引已经排好序的规律来优化,如果没有使用到会通过文件进行排序消耗性能

  • 使用order by 联合索引,查询条件与排序条件中间字段不能断
  • order by 多个字段时,顺序要与索引字段一致
  • 使用降序排序不会走索引排序
  • in 不会走索引排序,因为结果集不确定是否有序
  • 使用 > 条件的不会走索引排序,可能是数据量太大

总结

  1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2. order by满足两种情况会使用Using index。
    1. order by语句使用索引最左前列。
    2. 使用where子句与order by子句条件列组合满足索引最左前列。
  3. 尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  4. 如果order by的条件不在索引列上,就会产生Using filesort。
  5. 能用覆盖索引尽量用覆盖索引
  6. group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order
    by
    null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。

文件排序原理

排序方式

单路排序

一次性取满足条件的数据的所有字段,然后在sort_buffer中进行排序。

trace工具的sort_mode显示<sort_key, additional_fields>或<sort_key, packed_additional_fields>

双路排序

双路排序又称回表排序。首先根据条件查询出相应的字段,然后取排序字段和主键在sort_buffer中排序,拍完序再去主键索引取信息

trace工具的sort_mode显示<sort_key, rowid>.

MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来 判断使用哪种排序模式。

  • 如果字段的总长度小于max_length_for_sort_data,那么使用单路排序模式;
  • 如果字段的总长度大于max_length_for_sort_data,那么使用双路排序模式;

排序过程

假设条件为 where name = 'a'

单路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出所有字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 返回结果

双路排序

  1. 从索引中找到第一个符合条件的主键ID
  2. 根据ID取出ID和排序字段存入sort_buffer中
  3. 查找下一个符合条件的ID
  4. 一直重复2,3直到没有符合的记录
  5. 排序
  6. 按照排序好的ID值回到原表取出所欲字段值然后返回

其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer 中进行排序,然后再通过主键回到原表查询需要的字段。

如果 MySQL 排序内存 sort_buffer 配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data
配置小点,让优化器选择使用双路排序算法,可以在sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据。

如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择全字段排序(单路排序)
,把需要的字段放到 sort_buffer
中,这样排序后就会直接从内存里返回查询结果了。

所以,MySQL通过 max_length_for_sort_data 这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率。

注意:如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。

设计原则

1. 代码先行,索引后上

不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗? 这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

2. 联合索引尽量覆盖条件

比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

3. 不要在小基数字段上建立索引

索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。
如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。
一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4. 长字符串我们可以采用前缀索引

尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。
当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的。

对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于
KEY index(name(20),age,position)。

此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来完整的name字段值进行比对。
但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排序是没法用上索引的, group by也是同理。所以这里要对前缀索引有一个了解。

5. where与order by冲突时优先where

在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?

一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。 因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。

6. 基于慢sql查询做优化

可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。

慢sql查询查阅:http://note.youdao.com/noteshare?id=c71f1e66b7f91dab989a9d3a7c8ceb8e&sub=0B91DF863FB846AA9A1CDDF431402C7Bopen in new window

上次编辑于:
贡献者: yanggl
+ diff --git a/note/db/mysql/further/2403061142.html b/note/db/mysql/further/2403061142.html new file mode 100644 index 00000000..fd3477f7 --- /dev/null +++ b/note/db/mysql/further/2403061142.html @@ -0,0 +1,40 @@ + + + + + + + + Mysql事务原理 | Yaien Blog + + + + + + +
跳至主要內容

Mysql事务原理

Yaien Blog原创大约 6 分钟DBMySQL事务

Mysql事务原理

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

事务

MySQL事务是一组SQL语句的集合,它们作为一个工作单位执行。事务主要用于处理操作量大,复杂度高的数据。在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。

ACID

事务的ACID属性是数据库管理系统在写入或更新数据的过程中,为保证事务是正确可靠的。其具备有原子性、一致性、隔离性、持久性四个属性,简称事务的ACID属性。

  • 原子性(Atomicity): 指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。由Undo Log来实现
  • 一致性(Consistency): 指事务必须使数据库从一个一致性状态变换到另外一个一致性状态。换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
    由其他三个属性以及业务代码正确逻辑来实现
  • 隔离性(Isolation): 指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。由锁机制以及MVCC机制来实现
  • 持久性(Durability): 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。由redo log来实现

并发带来的事务问题

在并发场景下,MySQL事务可能会带来更新丢失、脏读、不可重复读、幻读问题。

  • 更新丢失(Lost Update)或脏写: 两个或者多个事务同时选择同一行数据,都基于最初选定的值更新该行,由于每个事务都不知道其它事务的存在,就会发生更新丢失的问题。最后提交的更新覆盖了之前其它事务所做的更新。
  • 脏读(Dirty Reads): 一个事务正在对一条记录进行修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态。此时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并依据此做了进一步的处理,就会产生对未提交的数据的依赖关系。
  • 不可重复读(Non-Repeatable Reads): 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了。
  • 幻读(Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据。

事务隔隔离级别

脏读、不可重复读、幻读其实都是数据库读一致性问题,可以由数据库提供的隔离机制来解决。数据库提供了读未提交、读已提交、可重复读、可串行化四种隔离机制。

  • 读未提交(Read Uncommitted): 在读未提交隔离级别下,事务A可以读取到事务B修改过但未提交的数据。可能发生脏读、不可重复读和幻读问题,一般很少使用此隔离级别
  • 读已提交(Read Committed): 在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据。读已提交隔离级别解决了脏读的问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别
  • 可重复读(Repeatable Read): 在可重复读隔离级别下,事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务B修改的数据。可重复读隔离级别解决了脏读和不可重复读的问题,但可能发生幻读问题
  • 可串行化(Serializable): 各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。
隔离级别/事务问题更新丢失脏读不可重复读幻读
读未提交可以不可以不可以不可以
读已提交可以可以不可以不可以
可重复读可以可以可以不可以
可串行化可以可以可以可以

数据库隔离级别越严格,并发问题越小,但是付出的代价也就越大,因为事务隔离实际上就是使事务在一定程度上“串行化”,这与“并发”是冲突的。同时,不同应用对读一致性和事务隔离程度的要求也是不同的。

Mysql默认的事务隔离级别是可重复读。对于RC和RR的选择,如果对并发要求高,可以选择RC,如果对同一时期的数据有要求,可以选择RR。

面试题:查询操作方法需要使用事务吗?

答:如果只查询一条语句,可以不加事务。
如果执行多条查询语句,假设现在使用的隔离级别是RR,最好加上事务。对于报表数据,如果使用RR,添加事务可以确保查询出来的数据是同一时间的数据,而对于RC或者不加事务,获取到的值一直都是最新的。
假设报表数据处理耗时1S,这期间有可能已经执行了很多的数据操作了,RR通过事务可以保证查出来的数据集是同一时间的,而RC或者不加事务有可能已经是不同时期的数据集。

大事务的影响

在使用事务的时候,应该尽量避免编写过大的事务,大事务在执行时可能会存在以下问题:

  • 高并发场景下,由于事务操作是需要占用服务器链接的,大事务执行耗时过多,数据库连接池容易被撑爆
  • 事务锁定的数据太多,容易造成大量的阻塞和锁超时
  • 执行耗时过多,容易造成主从延迟
  • 回滚需要的时间过长
  • undo log膨胀
  • 容易死锁

事务使用原则

  • 将查询等数据准备操作放到事务外执行
  • 事务中应避免远程调用或者远程调用设置超时,防止事务等待时间太久
  • 事务中避免一次性处理太多数据,如果可以应拆分多个事务分次处理
  • 更新等设计加锁操作的应尽可能的放在事务靠后的位置
  • 能异步处理的尽量异步处理
  • 应用端(业务代码)保证数据一致性,非事务执行
上次编辑于:
贡献者: yanggl
+ + + diff --git a/note/db/mysql/further/2403081801.html b/note/db/mysql/further/2403081801.html new file mode 100644 index 00000000..e2758af7 --- /dev/null +++ b/note/db/mysql/further/2403081801.html @@ -0,0 +1,43 @@ + + + + + + + + 锁机制 | Yaien Blog + + + + + + +
跳至主要內容

锁机制

Yaien Blog原创大约 7 分钟DBMySQL锁机制

锁机制

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

锁冲突也是影响数据库并发访问性能的一个重要因素。

锁分类

从性能上分,分为乐观锁(用版本对比或者CAS机制)和悲观锁;从数据操作的粒度上分,分为表锁、页锁、行锁;从数据操作的类型上分,分为读锁、写锁和意向锁,读锁和写锁两种锁都属于悲观锁

乐观锁

适合读多写少的场景,如果在写操作多的场景下使用,会导致比对次数过多从而影响性能

悲观锁

适合写操作多的场景

表锁

锁定粒度最大的一种锁,对当前操作的整张表加锁。实现简单,加锁力度大,资源消耗较小,加锁快,不会出现死锁,并发度最低。大部分引擎都支持。一般用于表迁移。

叶锁

粒度介于表锁和行锁之间,锁定的是相邻的一组记录(一页)。基于索引加锁,锁粒度介于表锁和行锁之间,会出现死锁,并发度一般。只有BDB支持叶锁。

行锁

粒度最小的一种锁,只针对当前操作的行加锁。基于索引加锁,锁粒度最小,资源开销大,加锁慢,会出现死锁,并发度最高。Innodb支持行锁。

InnoDB的行锁实际上是针对索引加的锁(在索引对应的索引项上做标记),不是针对整个行记录加的锁。该索引不能失效,否则会从行锁升级为表锁(RR级别会升级为表锁,RC级别不会)。

例如:

select * from user where name='zhangsan' for update;
+

如果where条件中name字段没有索引,其他Session对该表任意一行做修改操作都会被阻塞住。

RR级别行锁升级为表锁的原因分析:

RR级别下,需要解决不可重复读和幻读问题,所以在便利扫描聚集索引时,为了防止扫描过的索引被其他事务修改(不可重复读)或间隙被其它事务插入新数据(幻读),从而导致数据不一致。
所以MySQL的解决方案就是把所有扫描过的索引记录和间隙都上锁(并不是直接将整张表加表锁,因为不一定能加上表锁,可能会有其他事务已经锁住了表里的某些行数据)。

读锁

又称共享锁,简称S锁(Shared)。它允许多个事务对统一数据进行读取,但不能进行修改。对同一个数据,多个读操作可以同时进行,互不干扰。可以通过添加lock in share mode来加读锁。

写锁

又称排他锁,简称X锁(Exclusive)。它允许获取排它锁的事务读取和修改数据,阻止其他事务取得相同数据集的共享锁或排它锁。如果当前写操作没有完毕,则无法进行其他的读、写操作。数据新增、修改都会加写锁,查询也可以通过加for update来加写锁。

读锁会阻塞写,但是不会阻塞读;写锁则会把读、写都阻塞。

意向锁

属于InnoDB引擎中的一种机制。当有事务给表的数据加了锁(读锁或写锁),同时会给表设置一个 标识(意向锁) 表示表已经有了行锁。当其他事务要对表进行加表锁时,就不用便利表数据判断数据有没有行锁会跟表锁冲突了,直接读取这个标识就可以进行判断是否可以加表锁。在表中记录很多时,逐行判断加表锁的方式效率很低。

意向锁由分为:

  • 意向共享锁(IS): 事务有意向对表中的某些行加共享锁(S锁)。
  • 意向排它锁(IX): 事务有意向对表中的某些行加排它锁(X锁)。

间隙锁(Gap Lock)

间隙锁锁的是两个值之间的空隙,间隙锁是在可重复读隔离级别下才会生效。 对于RR级别下幻读的问题,可以通过间隙锁可以解决。

假设user表有如下数据:

idnameage
1lilei5
2zhangsan6
5lisi7
18wangwu8

则此表的间隙有id为(2,5)、(5,18)、(18,+∞)这三个区间(开区间),当某个连接执行

select * from user where id = 3 for update;
+

则其他的连接将没法在(2,5)这个间隙范围里插入任何数据。

如果执行的是

select * from user where id = 20 for update;
+

则其他的连接将没法在(18,+∞)这个间隙范围里插入任何数据。

也就是说,只要在间隙范围内锁了一条不存在的记录就会锁住整个间隙范围,不锁边界记录,这样就能防止其他的连接在这个间隙范围内插入数据,就解决了RR级别的幻读问题。

临建锁(Next-key Locks)

临建锁是行锁于间隙锁的组合,行锁锁间隙两头的行。

在MySQL中,当你执行范围查询时,例如`SELECT…从表value1和value2; '之间的列中,InnoDB不仅锁住了满足查询条件的行(键),还锁住了键之间的间隔或范围。这被称为下一键锁定或间隙锁定。

临建锁目的是防止其他事务插入原本应该包含在原始范围查询中的新行,从而导致幻影读。当一个事务检索一个范围的行,而另一个事务在第一个事务提交或回滚之前将新行插入到范围中时,就会发生幻影读取,导致第一个事务看到一个在最初读取范围时并不存在的“幻影”行。

通过锁定键之间的空隙,InnoDB确保了没有新行被插入到被查询的范围中,保持了事务的一致性和可序列化性。

当您需要在特定的值范围内处理频繁的插入、更新和删除时,下一键锁特别有用。它们保证范围查询的结果集在整个事务执行过程中保持稳定,即使其他事务修改了该范围内的数据。

值得注意的是,下一个键锁定可能会导致锁争用增加和并发性降低,特别是在涉及许多范围查询和相同范围内并发修改的工作负载中。在这种情况下,您可能需要考虑其他技术,如应用程序级别的锁或分区,以平衡并发和一致性需求。

MyISAM与InnoDB锁实现

MyIsAM 在执行查询语句之前,会自动给设计的所有表加读锁,在执行insert、update、delete操作会自动给设计的表加写锁

InnoDB在执行查询语句(非串行化隔离级别),不会加锁,但是insert、update、delete操作会加行锁。

InnoDB由于实现了行级锁,虽然在锁定机制的实现方面锁带来的性能损耗可能会比表锁更高,但整体并发处理能力要远远优于MyISAM的表级锁定。当系统并发量高时,InnoDB的整体性能额MyISAM相比就会有比较明显的优势了。
但是,InnoDB的行级锁定同样有不好的地方,当我们使用不当,可能就会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至有可能会更差。

死锁

大多数情况下MySQL可以自动检测死锁并回滚产生死锁的事务,但是在某些情况MySQL是没法自动检测的,这就需要我们可以通过日志找到对应的事务线程ID,通过ID来kill杀掉事务。

锁优化实践

  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少索引条件范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的SQL尽量放在事务最后执行
  • 尽可能用低的事务隔离级别
上次编辑于:
贡献者: yanggl
+ + + diff --git a/note/db/mysql/further/index.html b/note/db/mysql/further/index.html index a7a2e929..ab925ece 100644 --- a/note/db/mysql/further/index.html +++ b/note/db/mysql/further/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/note/db/mysql/index.html b/note/db/mysql/index.html index a081f787..840feefb 100644 --- a/note/db/mysql/index.html +++ b/note/db/mysql/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/note/db/redis/2304010120.html b/note/db/redis/2304010120.html index 75d8eb50..388a83f3 100644 --- a/note/db/redis/2304010120.html +++ b/note/db/redis/2304010120.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Redis核心数据结构

Yaien Blog原创大约 7 分钟DBRedis

Redis核心数据结构

Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。

Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。

地址

官网:https://redis.io/open in new window

GitHub:https://github.com/redis/redisopen in new window

Redis数据类型简介:https://redis.io/topics/data-types-introopen in new window

命令完整列表:https://redis.io/commandsopen in new window

完整文档地址:https://redis.io/documentationopen in new window

基本特性

  • 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值;
  • 数据是存储在内存中的;
  • 键值对中键的类型可以是字符串、整形、浮点型等,且键是唯一的,相同的键会覆盖值;
  • 键值对中值类型可以是:String、Hash、List、Set、ZSet等 ;
  • 内置了复制、磁盘持久化、LUA脚本、事务、SSL、ACLS、客户端缓存、客户端代理等功能;
  • 提供了Redis哨兵、Redis cluster等高可用性的模式;

核心数据结构

String

String类型的结构对应JAVA的Map,key-val的存储形式

常用操作
命令描述
SET key value存入字符串
MSET [key val ...]批量存入字符串
GET key获取值
MGET [key ...]批量获取值
DEL [key ...]删除一个或多个键
EXPIRE key seconds设置一个键过期时间(s)
SETNX key valkey不存在且保存成功返回1,失败返回0,可用做分布式锁
incrby key val批量生产序列号
SET key val EX 10 NX防止程序意外终止导致死锁
应用场景
  1. 单值缓存
  2. 对象缓存
  3. 分布式锁
  4. 计数器
  5. Web集群session共享
  6. 分布式系统全局系列号等

Hash

Hash类型的结构,存储一个类型的key-val

常用操作
命令简述
HASH key field value存储一个哈希表key的键值
HASHNX key field value存储一个不存在的哈希表key的键值
HMSET key field获取哈希表key对应的field键值
HMGET key [field ...]批量获取field键值
HDEL key [field ...]批量删除
HLEN key返回哈希表key中field的数量
HGETALL key返回哈希表key中所有的键值
HINCRBY key field increment为哈希表key中field键的值加上增量
应用场景
  1. 对象缓存
  2. 电商购物车等
优缺点
  • 优点
    1. 同类数据归类整合储存,方便数据管理
    2. 相比String操作小号的内存与cpu更小
    3. 相比Stirng更节约存储空间
  • 缺点
    1. 过期功能不能使用在field上,只能使用在key上
    2. Redis集群架构下不适合大规模使用

List

List,列表结构,跟JAVA中的List基本相似

命令描述
LPUSH key [value ...]将一个或多个value值插入到key列表的表头
RPUSH key [value ...]将一个或多value值插入多key列表表尾
LPOP key返回并移除key列表的头元素
RPOP key返回并移除key列表的尾元素
LRANGE key start stop返回列表key中制定区域内的元素,区间则以start和stop指定
BLPOP [key...] timeot从key列表表头弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
BRPOP [key...] timeout从key列表尾部弹出元素,若列表为空则阻塞等待,若timeout=0则一直阻塞
应用场景
  1. stack(栈) LPUSH + LPOP
  2. Queue (队列) LPUSH + RPOP
  3. Blocking MQ(阻塞队列)LPUSH + BRPOP

Set

Set集合,val中存储多个不重复的元素

常用命令
命令描述
SADD key [momber ...]往集合key中存入元素,元素存在则忽略
SREM key [momber ...]从集合key中删除元素
SMEMBERS key获取集合key中所有元素
SCARD key获取集合key的元素个数
SISMEMBER key momber判断val元素是否存在于key集合中
SRANDMENMBER key [count]从集合key中选出count个元素,元素不移除
SPOP key [count]从集合key中选出count个元素并移除元素
SINTER [key ..]交集运算
SINTERSTORE newSet [key ...]将交集结果存入新集合newSet中
SUNION [key ...]并集运算
SUNIONSTORE newSet [key ...]将并集结果存入新集合newSet中
SDIFF [key ... ]差集运算
SDIFFSTORE newSet [key ...]将差集结果存入新集合newSet中
应用场景
  1. 抽奖
  2. 去重
  3. 点赞/取消点赞
  4. 集合操作实现关注模型等
  5. 集合操作实现商品赛选等

ZSet

与Set集合类似,ZSet集合是有序的

命令描述
ZADD key [[score member] ...]往有序集合key中加入带分值元素
ZREM key [member ...]从有序集合key中删除元素
ZSCORE key member返回有序集合key中member元素的分值
ZINCRBY key increment member为key中元素member的分值加上increment
ZCARD key返回key中的元素个数
ZRANGE key start stop [withscores]正序获取key中start到stop的元素
ZREVRANGE key start stop [withscores]倒序获取key中start到stop的元素
应用场景
  1. 微博排行榜
  2. 七日排行榜单等

Redis的单线程和高性能

  1. Redis的单线程并非是真正意义上的单线程。单线程主要是指网络IO键值对读写是由一个线程来完成,而这也是Redis对外提供键值存储服务的主要流程。Redis内部的其他功能,例如持久化、 异步删除、数据同步等等是会有额外的线程去执行的。
  2. 单个线程访问之所以还会那么快,是因为数据都存在内存中,所有的运算都是内存级别的运算,单线程同时避免了多线程所带来的上下文切换问题。
  3. 单线程的任务我们需要注意访问的命令,对于那些耗时的命令以及当访问一个bigkey的时候可能会导致Redis卡顿。
  4. 对于客户端的并发连接,Redis底层利用epoll模型来实现IO多路复用,将链接信息和时间放到队列中,依次放到文件事件分派器,事件分派器将事件分发给对应的事件处理器。
IO多路复用
IO多路复用

其他高级命令

keys

全局遍历所有的key,用来列出所有满足特定正则字符串规则的key,当Redis数据量较大时,性能会有所下降,应避免使用

scan

SCAN cursor [MATCH pattern] [COUNT count] 渐进式便利所有的键,相对于keys性能消耗更小,安全性更低

scan 参数提供了三个参数,第一个是 cursor 整数值(hash桶的索引值),第二个是 key 的正则模式,第三个是一次遍历的key的数量(参考值,底层遍历的数量不一定),并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。

⚠️注意:scan并非完美无瑕,如果在scan的过程中如果有键的变化(增加、 删除、 修改),那么遍历效果可能会碰到如下问题:
新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是我们在开发时需要考虑的。

info

查看Redis服务运行信息,分为以下9大块:

  • Server: 服务器运行的环境参数
  • Clients: 客户端相关信息
  • Memory: 服务器运行内存统计数据
  • Persistence: 持久化信息
  • Stats: 通用统计数据
  • Replication: 主从复制相关信息
  • CPU: CPU使用情况
  • Cluster: 集群情况
  • KeySpace: 键值对统计数量信息
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2305091732.html b/note/db/redis/2305091732.html index 3a8f2727..4526b78e 100644 --- a/note/db/redis/2305091732.html +++ b/note/db/redis/2305091732.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Redis数据持久化

Yaien Blog原创大约 5 分钟DBRedis

Redis数据持久化

RDB快照(snapshot)

默认情况下,Redis会将内存数据库快照保存在一个名称为 dump.rdb 的二进制文件中。我们可以通过 .conf 配置文件中的 save 属性进行设置,让它在"N秒内数据集至少有M个改动"这一条件被满足时,自动保存一次数据。

当我们需要关闭RDB只需要将所有的save保存策略注释掉即可:

# ./redis.conf
@@ -58,6 +58,6 @@
 # 开启混合持久化
 aof-use-rdb-preamble yes
 

当开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存的命令存放在一起,都写入到新的AOF文件,新的文件一开始不叫 appendonly.aof ,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。于是在redis重启时,可以先加载RDB的内容,然后再放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率会大幅提高。

⚠️注意:开启混合持久化,必须先开启AOF

Redis备份参考策略

  1. 写crontap定时调度脚本,每小时都copy一份rdb或aof到一个目录中去,仅仅保留最近48小时的备份;
  2. 每天都保留一份当日的数据备份到一个目录中去,可以保留最近一个月的备份;
  3. 每次copy备份的时候,把太久的备份删除;
  4. 每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏数据丢失;
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2305091734.html b/note/db/redis/2305091734.html index 78b9bee3..d2aebd23 100644 --- a/note/db/redis/2305091734.html +++ b/note/db/redis/2305091734.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Redis主从架构

Yaien Blog原创大约 2 分钟DBRedis

Redis主从架构

工作原理

主从复制(全量复制)

  1. 如果你为 master 节点配置了一个 slave ,不管这个 slave 是否第一次连接上 master ,它都会发送一个 PSYNVC 命令给 master 请求复制数据。
  2. master 节点收到 PSYNC 命令后,会在后台通过 bgsave 进行数据持久化生成最新的rdb快照文件,持久化期间, master 会继续接收客户端的请求,他会把这些可能修改数据集的请求缓存在内存中。
  3. 当持久化进行完毕后, maser 会把这份rdb文件数据集发送给 slaveslave 会把己收到的数据进行rdb持久化,然后加载到内存中。若 master 内存中存在后续修改的数据集,再将之前缓存的命令发送给 slave
  4. masterslave 之间的链接由于某种原因断开连接时, slave 能够自动链接 master ,如果 master 收到多个 slave 并发链接请求,它只会进行一次持久化,而不是一次链接持久化一次,会把这一份持久化的数据发送给多个并发连接的 slave
Redis主从复制(全量复制)
Redis主从复制(全量复制)

主从复制(部分复制)

  1. masterslave 断开连接,一般都会对正分数据进行复制。但是从2.8版本开始,redis改用可以支持部分数据复制的命令 PSYNCmaster 同步数据,slavemaster 能够在网络连接断开重连后进行部分数据复制(断电续传);
  2. master 会在内存中创建一个缓存队列,缓存最近一段时间的数据,master 和它所有的 slave 都维护了缓存的数据下标 offsetmaster进程id
  3. 当网络断开,slave 请求 master 继续进行未完成的复制,从所记录的数据下标开始,如果 master 进程ID变了或者 slave 节点数据下标 offset 已经不在 master 缓存队列中时,会进行一次 全量复制
Reids主从复制(部分复制)
Reids主从复制(部分复制)

⚠️注意:repl buffer中存的数据是先进先出的,当偏移量(offset)已经找不到,则全量复制

问题

主从复制风暴

从上述可以看到我们redis主从同步的执行流程,当我们一个主节点存在很多从节点,从节点同时复制主节点会导致主节点压力过大。这就是我们所说的主从复制风暴。

对于主从复制风暴,我们可以通过让部分的从从节点不再从主节点同步数据,而是跟从节点同步数据,具体结构如下:

主从节点同步模型
主从节点同步模型
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2305091736.html b/note/db/redis/2305091736.html index 7b19fb4d..823a9c4b 100644 --- a/note/db/redis/2305091736.html +++ b/note/db/redis/2305091736.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Redis事务

Yaien Blog原创大约 3 分钟DBRedis

Redis事务

事务实现

管道

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完成后再一次性读取服务器的响应,这样可以极大的降低多条命令执行的网络传输开销。管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意的是用pipeline方式打包命令发送,redis必须处理完所有命令前先缓存起所有命令的处理结构。打包的命令越多,缓存消耗的内存也越多,所以并不是打包的命令越多越好。

pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达所有的命令一起成功的语义,管道中前面命令失败并不会影响到后面命令的执行,同时管道的操作并 非原子 的。

Lua脚本

reids 在2.6版本推出的脚本功能,允许开发者使用lua语言编写脚本传到redis中执行。通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。EVAL命令格式如下:

EVAL script numkeys key [key..] arg [arg...]
@@ -54,6 +54,6 @@
 
 #  其中 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 是被求值的Lua脚本,数字2指定了键名参数的数量, key1和key2是键名参数,分别使用 KEYS[1] 和 //KEYS[2] 访问,而最后的 first 和 second 则是附加参数,可以通过 ARGV[1] 和 ARGV[2] 访问它们。
 

注意:redis时单进程、单线程执行脚本,因此不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令,所以使用时要注意不能出现死循环、耗时运算

优势

  • 减少网络开销:这点跟管道类似,使用脚本也可以减少网络往返时间;
  • 原子操作:redis会将脚本作为一个整体去执行,中间不会被其他命令所影响;
  • 替代redis事务:redis自带事务很鸡肋,而lua脚本几乎实现了常规的事务功能,同时官方也推荐使用lua脚本替代redis本身的事务功能;
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2305091740.html b/note/db/redis/2305091740.html index c0bd0a9d..801817f6 100644 --- a/note/db/redis/2305091740.html +++ b/note/db/redis/2305091740.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Redis哨兵(Sentinel)

Yaien Blog原创大约 1 分钟DBRedis

Redis哨兵(Sentinel)

工作原理

sentinel哨兵是特殊的redis服务,不提供读写,主要用来监控redis实例节点。

哨兵架构下client端第一次从哨兵找出redis主节点,后续就直接访问redis主节点,不会每次都通过sentinel代理访问redis主节点,当redis主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端。

client端会实现订阅功能,订阅sentinel发布的节点变动消息。如果redis主节点挂了,哨兵集群会重新选举出新的redis主节点。

Reids哨兵架构
Reids哨兵架构

优缺点

在Redis 3.0以前的版本要实现集群一般是借助哨兵sentinel节点的状态,在高可用高并发等场景下会存在以下问题:

  1. 如果mastr节点异常,则会做主从切换,将某一台slave作为master,消耗时间和性能;
  2. 哨兵的配置略微复杂,并且性能和高可用性等各方面表现一般,特别是在主从切换的瞬间会存在访问瞬断的情况;
  3. 哨兵模式只有一个主节点对外提供服务,没法支持很高的并发;
  4. 当耽搁主节点内存设置过大,否则会导致持久化文件多大,影响数据恢复或主从同步的效率;
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2305091744.html b/note/db/redis/2305091744.html index 4858c429..8cd9d20b 100644 --- a/note/db/redis/2305091744.html +++ b/note/db/redis/2305091744.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Redis集群(Cluster)

Yaien Blog原创大约 5 分钟DBRedis

Redis集群(Cluster)

redis集群是一个又多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片的特性。
redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展。
根据官方文档称,可线性扩展到上万个节点(推荐不超过1W个节点)。
redis集群的性能和高可用性均优于哨兵模式,且配置简单。

原理分析

Redis Cluster将所有数据划分为16384个槽位(slots),每个节点负责其中一部分槽位。槽位信息存储在每一个节点中。

当Redis Cluster的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。如此一来当客户端要查找某个key时,可以直接地你个为到目标节点。
而因为槽位的信息可能会存在客户端与服务器不一致的情况,因此还实现了纠正机制来实现槽位信息的校验调整。

槽位定位

Redis Cluster默认对key使用crc16算法进行hash后得到一个整数值,然后用这个整数值对16384个槽位进行取模后获取具体的槽位。

# JedisClusterCRC16 核心源码实现
@@ -93,6 +93,6 @@
     }
 }
 

跳转重定位

当客户端像一个节点发出指令,而该节点发现key所在槽位已经不归自己管理时,它会向客户端发送一个特殊的跳转指令,指令携带了目标操作的节点地址让客户端去该节点进行操作。
客户端收到指令后连接新的节点进行操作,并且会同步更新本地的槽位映射表缓存,后续所有key使用新槽位映射表数据进行操作。

集群节点间通信

维护继集群元数据有两种方式:集中式gossip,而Redis Cluster节点间采用gossip协议进行通信。

集中式

集中式协议的优点就是对元数据的更新和读取时效性好,一旦元数据出现变更立即更新到集中式的存储中,其他节点读取时立即就可以感知获取最新的元数据。 但由于所有的元数据集中在一个地方,会存在数据更新、存储照成一点的影响。

很多中间件在使用集中式存储元数据时,通常借助zookeeper来实现。

gossip

gossip协议的优点在于元数据更新比较分散,更新的请求是陆陆续续到达各节点,相比集中式协议没有更新上的压力。但是分散式更新是陆陆续续到达的,有一定的延迟,元数据更新延迟有可能导致集群的额一些操作有一些滞后。

gossip协议会包含各种信息,例如ping、pong、meet、fail等

  • meet:节点发送meet给新加入的节点,包含了集群的元数据信息,新节点读取数据就会加入集群中开始与其他节点进行通信;
  • ping:每个节点都会经常经常性的给其他节点发送ping命令,其中包含自己的状态还有自己维护的集群元数据,通过ping互相交换元数据;
  • pong:对ping和meet消息的返回,包含自己的状态和其他信息,也可以用于消息广播和更新;
  • fail:某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点该节点宕机了;
上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2312220952.html b/note/db/redis/2312220952.html index 71309703..5bbfd2ad 100644 --- a/note/db/redis/2312220952.html +++ b/note/db/redis/2312220952.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Redis主从/哨兵/集群架构搭建

Yaien Blog原创大约 5 分钟DBRedis

Redis主从/哨兵/集群架构搭建

记录Redis主从复制架构、哨兵架构以及集群(cluster)架构的搭建。本次搭建使用不同云服务器厂商公网ip搭建。

环境

服务器环境

服务器IP版本节点
腾讯云159.159.159.159CentOS 7.9master
腾讯云139.139.139.139CentOS 7.9slave
华为云101.101.101.101CentOS 7.9slave

开放防火墙端口

  1. 关闭服务器防火墙
  2. 开放云服务提供商服务器实例防火墙端口:6379、6380、6381、16379、16380、16381、26379、26380、26381

Redis版本

redis-7.2.0

# 安装路径
@@ -248,6 +248,6 @@
 # 136ddf18be30a78b91b3a2234c51b6c48f701c7a 159.159.159.159:6381@16381 slave 24ace9daa23390120c6196f2bfd4a4fb012a25db 0 1703232221789 4 connected
 # d81b9ed97af9692bdcb2dc6b7245f53e7ffda0e5 139.139.139.139:6381@16381 slave 9fcbba06ff95de4a5352591d81e0a57b52f7ebe2 0 1703232221000 7 connected
 

自此,Redis集群结构搭建完成。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2312280935.html b/note/db/redis/2312280935.html index 2a950a45..0735934b 100644 --- a/note/db/redis/2312280935.html +++ b/note/db/redis/2312280935.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Redis高并发缓存实战

Yaien Blog原创大约 5 分钟DBRedis

Redis高并发缓存实战

记录高并发场景下Redis部署、使用、存在的问题以及处理方案等

常见问题

在中小并发场景下,我们在使用缓存架构基本的业务流程是:

  1. 查询缓存,缓存存在则返回
  2. 缓存没有,查找数据库,更新缓存
    /**
@@ -169,6 +169,6 @@
     }
     
 

上述代码使用Redisson实现了分布式互斥锁,单线程去存储层获取商品信息并重建缓存,采用双重检查的方式来处理等待线程去获取锁时,会再次判断缓存是否已经重建成功,成功直接返回。

数据不一致

上次编辑于:
贡献者: yanggl
- + diff --git a/note/db/redis/2312291000.html b/note/db/redis/2312291000.html index ee05eef8..d0366a86 100644 --- a/note/db/redis/2312291000.html +++ b/note/db/redis/2312291000.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/db/redis/2312291432.html b/note/db/redis/2312291432.html index 8034e406..3b15b4e6 100644 --- a/note/db/redis/2312291432.html +++ b/note/db/redis/2312291432.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/db/redis/index.html b/note/db/redis/index.html index a4f3d371..ebb9a853 100644 --- a/note/db/redis/index.html +++ b/note/db/redis/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/index.html b/note/framework/index.html index 14c67271..b70dad25 100644 --- a/note/framework/index.html +++ b/note/framework/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/mybatis/1911011300.html b/note/framework/mybatis/1911011300.html index 8b22de2d..82deb0be 100644 --- a/note/framework/mybatis/1911011300.html +++ b/note/framework/mybatis/1911011300.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Mybatis 基础

Yaien Blog原创大约 10 分钟MyBatis基础

Mybatis 基础

Mybatis 基础笔记

作用

简化持久层开发,当需要开发某个增删改查功能时,程序员只需要定义好该功能对应的抽象方法,及该抽象方法的功能对应的SQL语句即可。

创建

与创建SpringMVC项目的步骤相同,另外,增加添加依赖:


@@ -313,6 +313,6 @@
     )
 </delete>
 

在配置<foreach>节点时:

  • collection:需要被遍历的集合或数据,如果抽象方法只有1个参数时,如果参数的类型是List集合,则取值为list,如果参数类型是数组,则取值为array
    ;如果抽象方法有多个参数,则该属性取值为@Param("xx")注解中使用的名称。

  • item:遍历过程中,集合中的元素的名称,在<foreach>子级位置,可以使用#{item值}表示被遍历到的元素的值。

  • separator:分隔符。

  • openclose:遍历生成的SQL语句部分的最左侧字符和最右侧字符。

#{}与${}占位符

在MyBatis中,配置SQL语句时,可以使用#{}${}这2种占位符。

使用#{}占位符,可以用于占位某些值,也就是在SQL中写值的位置,都可以使用这种占位符(此前在学习JDBC时使用?的位置);而${}可以表示SQL语句的任何部分!

在使用#{}对某个值进行占位时,框架对整个SQL语句是有预编译处理的,无需考虑该值的数据类型的问题;而使用${}
占位时,框架的处理方式其实就是非常单纯的字符串拼接,需要考虑数据类型的问题,如果占位的值中包括字符串类型的值,则必须使用''框住值!

由于#{}只能对某个值进行占位,SQL语句本身是相对固定的,所以,这种做法实现的功能的局限性就非常明显,由于是预编译的,没有SQL注入风险,且工作效率较高!而${}
可以随意占位,功能可以非常灵活,但是,不是预编译的,有SQL注入风险,工作效率较低。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/mybatis/index.html b/note/framework/mybatis/index.html index 5f1c30be..17aa0393 100644 --- a/note/framework/mybatis/index.html +++ b/note/framework/mybatis/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/spring/basic/2101202010.html b/note/framework/spring/basic/2101202010.html index 61c09699..8dc2bd60 100644 --- a/note/framework/spring/basic/2101202010.html +++ b/note/framework/spring/basic/2101202010.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Spring 基础(一)

Yaien Blog原创大约 5 分钟Spring基础

Spring 基础(一)

Spring 基础笔记系列

框架

开发人员可以在项目开发过程中,引用某些框架,从而,在开发过程中,就可以不必关心某些功能的开发,而是由框架直接完成!

解决的问题

Spring框架主要解决了创建对象和管理对象的问题!

传统的创建对象的方法例如:

	User user = new User();
@@ -145,6 +145,6 @@
     </bean>
 </beans>
 

可以看到,这种做法就必须先创建工厂类PhoneFactory的对象,然后调用工厂类的方法getInstance(),从而完成类的创建。

这种做法还要求工厂类必须有无参数的构造方法!

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/spring/basic/2101202011.html b/note/framework/spring/basic/2101202011.html index 15c1d156..11e20e10 100644 --- a/note/framework/spring/basic/2101202011.html +++ b/note/framework/spring/basic/2101202011.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Spring 基础(二)

Yaien Blog原创大约 5 分钟Spring基础

Spring 基础(二)

Spring 基础笔记系列

对象的作用域与生命周期(不常用)

由Spring管理的对象,默认都是单例的!并且,都是饿汉式的单例模式。

在配置<bean>节点时,可以添加scope属性其是否单例,当取值为singleton时表示单例,该值也是默认值,当取值为prototype时表示非单例:

<bean id="user" class="cn.tedu.spring.User" scope="prototype"/>
@@ -146,6 +146,6 @@
                  location="classpath:db.properties"/>
 

然后,就可以注入到相应的属性中:

<property name="config" ref="config"/>
 
上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/spring/basic/2101202012.html b/note/framework/spring/basic/2101202012.html index 40e92cf2..93095651 100644 --- a/note/framework/spring/basic/2101202012.html +++ b/note/framework/spring/basic/2101202012.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Spring 基础(三)

Yaien Blog原创大约 4 分钟Spring基础

Spring 基础(三)

Spring 基础笔记系列

Spring表达式

当某个Bean的某些属性值来自于另一个Bean的某些属性,则可以使用Spring表达式,例如:

	public class ValueBean {
@@ -106,6 +106,6 @@
 		System.out.println("UserDao.destroy()");
 	}
 

注意:这2个注解是JavaEE中的注解,并不是Spring的注解,在使用之前,需要添加Tomcat运行环境,以导入JavaEE相关的jar包,才可以使用。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/spring/basic/index.html b/note/framework/spring/basic/index.html index da637189..f1f6dabf 100644 --- a/note/framework/spring/basic/index.html +++ b/note/framework/spring/basic/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/spring/index.html b/note/framework/spring/index.html index fcbcbbbc..e92c8350 100644 --- a/note/framework/spring/index.html +++ b/note/framework/spring/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/spring/sourcecode/2304021002.html b/note/framework/spring/sourcecode/2304021002.html index a6a63669..65b6657e 100644 --- a/note/framework/spring/sourcecode/2304021002.html +++ b/note/framework/spring/sourcecode/2304021002.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Spring 构建与内置功能

Yaien Blog原创大约 9 分钟Spring源码

Spring 构建与内置功能

记录spring的简单内置功能以及使用

创建方式

XML

通过加载xml配置文件,获取配置文件里相关的配置项,例如定义的scan标签、bean标签等可以配置扫描的路径以及进行bean的注入。

   ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
@@ -219,6 +219,6 @@
 public class AppConfig {
 }
 

FilterType分类:

  • ANNOTATION:表示是否包含某个注解
  • ASSIGNABLE_TYPE:表示是否是某个类
  • ASPECTJ:表示否是符合某个Aspectj表达式
  • REGEX:表示是否符合某个正则表达式
  • CUSTOM:自定义

Metadata

在Spring中需要去解析类的信息,比如类名、类中的方法、类上的注解,这些都可以称之为类的元数据,所以Spring中对类的元数据做了抽象,并提供了一些工具类。

MetadataReader表示类的元数据读取器,默认实现类为SimpleMetadataReader。SimpleMetadataReader去解析类时,使用的ASM技术

ClassMetadata表示类的元数据信息。

AnnotationMetadata表示注解的元数据信息。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/spring/sourcecode/index.html b/note/framework/spring/sourcecode/index.html index bf2600fa..4ce4e850 100644 --- a/note/framework/spring/sourcecode/index.html +++ b/note/framework/spring/sourcecode/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/springboot/index.html b/note/framework/springboot/index.html index 0df1368c..528a5dd5 100644 --- a/note/framework/springboot/index.html +++ b/note/framework/springboot/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/springboot/plugin/2306210953.html b/note/framework/springboot/plugin/2306210953.html index 10a9bfda..ddc6ed67 100644 --- a/note/framework/springboot/plugin/2306210953.html +++ b/note/framework/springboot/plugin/2306210953.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

spring-boot-maven-plugin 插件详解

Yaien Blog原创大约 4 分钟SpringBoot插件

spring-boot-maven-plugin 插件详解

Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring
应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven
插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。

spring boot 自带插件的原因

Spring Boot 的目标之一是使开发者能够更快速、更便捷地构建 Spring 应用程序。为了实现这个目标,Spring Boot
提供了一系列的开箱即用的功能和约定,其中就包括了自带的 Maven
插件。通过自带插件,开发者可以在项目中快速配置和打包应用程序,而无需手动编写大量的配置代码。同时,这个插件还提供了一些附加功能,如启动应用程序、运行测试等,可以极大地提高开发效率。

spring boot maven plugin插件详解

插件标签

在 Maven 的 pom.xml 文件中,可以使用 build 标签来配置 Spring Boot Maven 插件。常用的配置选项包括:


@@ -105,6 +105,6 @@
     </plugins>
 </build>
 

上述配置将在构建过程中生成一个名为 build-info.properties 的属性文件,其中包含了应用程序的版本信息。

知识补充

除了以上内容,还有一些补充知识有助于更深入地理解和使用 Spring Boot Maven 插件:

  • Profiles(配置文件): 插件支持使用 Maven 的 profiles 功能,可以根据不同的环境选择不同的配置文件,如
    application-dev.properties、application-prod.properties 等。
  • 插件自定义: 除了插件提供的默认功能外,开发者还可以根据自己的需求进行自定义,编写自己的 Maven 插件或扩展已有的插件。
  • 其他构建工具支持: 虽然本文主要介绍了 Spring Boot Maven 插件,但 Spring Boot 也提供了与 Gradle
    等其他构建工具的集成支持,可以根据具体需求选择适合的构建工具。
上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/springboot/plugin/2306211023.html b/note/framework/springboot/plugin/2306211023.html index 228d468c..a64922a0 100644 --- a/note/framework/springboot/plugin/2306211023.html +++ b/note/framework/springboot/plugin/2306211023.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

spring boot 工程 jar 包瘦身

Yaien Blog原创大约 4 分钟SpringBoot插件

spring boot 工程 jar 包瘦身

Spring Boot项目的pom.xml文件中一般都会带有spring-boot-maven-plugin插件,该插件的作用就是会将依赖的jar包全部打包进去。
但是该文件包含了所有的依赖和资源文件,会导致打出来的包会比较大。

而如果我们使用一般的打包命令时

mvn clean package
@@ -95,6 +95,6 @@
 

调整了配置之后,再次打包我们会发现发现target目录中多了个lib文件夹,里面保存了所有的依赖jar。本项目的jar包只有几百K。

当include标签只填groupId标签时,表示groupId标识下的所有依赖都包含

问题

启动问题

Spring Boot默认打包方式,将所有依赖文件全部打入项目jar包,我们在启动项目时可以通过

java -jar xxx.jar
 

直接运行。

而当进行瘦身之后,因为我们已经将项目依赖到外部lib包,我们就不能再通过以上那种方式启动,而是要通过通过-Dloader.path指定lib的路径:

java -jar -Dloader.path="/lib" xxx.jar
 

依赖问题

如果你有一个项目A,它依赖于项目B,而项目B又依赖于项目C,那么项目A实际上也是依赖于项目C的。

这种情况下,如果使用了spring-boot-maven-plugin并配置了includes标签,实际上是在设置要包含哪些依赖,而不是要排除哪些依赖。
所以,当你把B模块添加到includes标签下时,实际上你只是包含了B模块本身,而不是它的所有依赖,例如C

当想要A也包含C的依赖,则需要显示的添加到includes标签下

lib包更新问题

现在项目A显示的引入了项目B,当项目C更新或者添加了新地依赖D的时候,项目A就需要重新构建以便获取最新版本的
B以及其依赖,并且将最新的lib包更新到A的服务器上。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/springboot/plugin/index.html b/note/framework/springboot/plugin/index.html index d226ccac..1fe7c0a2 100644 --- a/note/framework/springboot/plugin/index.html +++ b/note/framework/springboot/plugin/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/springmvc/basic/2102202010.html b/note/framework/springmvc/basic/2102202010.html index 56a2d7d7..7d4e64b3 100644 --- a/note/framework/springmvc/basic/2102202010.html +++ b/note/framework/springmvc/basic/2102202010.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

SpringMVC 基础(一)

Yaien Blog原创大约 5 分钟SpringMVC基础

SpringMVC 基础(一)

SpringMVC 基础笔记系列

SpringMVC框架

作用

SpringMVC框架解决了V与C的交互问题。

原生的Servlet就是控制器,使用Servlet主要存在的问题是实例太多,配置麻烦,管理难度大等一系列的问题。例如项目中有用户注册功能,则可能需要开发UserRegisterServlet
,如果还有登录功能,则可能需要开发UserLoginServlet,几乎是每个功能需要有1个对应的Servlet,如果一个项目中有200个不同的功能,则需要200个Servlet,在更大的系统中,Servlet
的数量就非常多,在实际运行时,在内存中的Servlet对象就会占据大量的内存空间!由于Servlet的数量很多,进而导致配置文件的配置信息会非常多,配置信息多了以后,就会引发管理难度大的问题。

SpringMVC中的核心组件

  • DispatcherServlet:前端控制器,用于接收所有请求,并负责分发;

  • HandlerMapping:根据请求路径映射控制器或控制器的方法,确定请求路径与控制器或控制器中的方法的对应关系;

  • Controller:实际处理请求的组件;

  • ModelAndView:控制器的返回结果,包括处理完成后的数据,及最终应该响应给客户端的视图名称;

  • ViewResolver:根据视图名称得到具体的视图组件。

具体的执行流程图:

SpringMVC HelloWorld

目标

在浏览器中通过http://localhost:8080/项目名称/hello.do可以访问某个JSP显示的页面,页面中显示**Hello, SpringMVC!!!**字样。

创建

创建Maven ProjectArtifact Idcn.tedu.springArtifact IdSPRINGMVC01Packaing必须选择war

创建完成后,首先生成web.xml文件。

然后,在pom.xml中添加spring-webmvc的依赖。

然后,将Spring的配置文件复制到src/main/resources下。

另外,还需要添加Tomcat运行环境。

配置DispatcherServlet

打开web.xml,在配置文件中对DispatcherServlet进行配置,使之可以处理所有以.do结尾的请求:

<node>
@@ -89,6 +89,6 @@
     <property name="suffix" value=".jsp"></property>
 </bean>
 

然后,控制器的方法必须返回"hello",这样,前缀与返回值与后缀拼接起来,就可以得到/WEB-INF/hello.jsp

最后,在浏览器再次访问,可以看到JSP页面设计的内容。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/springmvc/basic/2102202011.html b/note/framework/springmvc/basic/2102202011.html index c30081ef..d0f5fb28 100644 --- a/note/framework/springmvc/basic/2102202011.html +++ b/note/framework/springmvc/basic/2102202011.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

SpringMVC 基础(二)

Yaien Blog原创大约 8 分钟SpringMVC基础

SpringMVC 基础(二)

SpringMVC 基础笔记系列

接收请求参数

(不推荐) 使用HttpServletRequest

在处理请求的方法的参数列表中添加HttpServletRequest参数,然后,在处理过程中,调用requestgetParameter()方法即可获取各请求参数的值:

	@RequestMapping("handle_register.do")
@@ -198,6 +198,6 @@
 

所以,如果并不强制要求客户端提交该参数,可以:

	@RequestParam(name="uname", required=false)
 

另外,还可以配置defaultValue属性,用于配置默认值,即当客户端没有提交该请求参数时,视为客户端提交了某个值:

	@RequestParam(name="uname", required=false, defaultValue="JSD1902")
 

当然,在设置defaultValue时,需要显式的将required设置为false

所以,通常@RequestParam注解的使用场景:

  • 客户端提交的请求参数名称与服务器端处理请求的方法的参数名称不一致时;

  • 强制要求客户端提交某些参数时;

  • 需要为某些请求参数设置默认值时。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/springmvc/basic/2102202012.html b/note/framework/springmvc/basic/2102202012.html index ce9b729a..13cd94c7 100644 --- a/note/framework/springmvc/basic/2102202012.html +++ b/note/framework/springmvc/basic/2102202012.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

SpringMVC 基础(三)

Yaien Blog原创大约 5 分钟SpringMVC基础

SpringMVC 基础(三)

SpringMVC 基础笔记系列

SpringMVC中的拦截器(Interceptor)

基本概念

在SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。

注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。

开发拦截器

首先,所有的拦截器类都必须实现HandlerInterceptor接口,可以自定义LoginInterceptor

	public class LoginInterceptor implements HandlerInterceptor {
@@ -154,6 +154,6 @@
 		return "error";
 	}
 

@ExceptionHandler注解中,可以配置需要处理的异常的种类,当配置后,仅当指定的异常出现时,才会调用匹配的方法进行处理,而其它异常是不予处理的!如果没有配置需要处理哪些异常,则任何异常出现都会进行处理!

在处理时,@ExceptionHandler只能作用于当前控制器类!

上次编辑于:
贡献者: yanggl
- + diff --git a/note/framework/springmvc/basic/index.html b/note/framework/springmvc/basic/index.html index 5748b153..84440e3c 100644 --- a/note/framework/springmvc/basic/index.html +++ b/note/framework/springmvc/basic/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/framework/springmvc/index.html b/note/framework/springmvc/index.html index cdc070b9..bf6b6b3d 100644 --- a/note/framework/springmvc/index.html +++ b/note/framework/springmvc/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/index.html b/note/index.html index f45c4227..26d53be3 100644 --- a/note/index.html +++ b/note/index.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Note

Yaien Blog小于 1 分钟

- + diff --git a/note/java/concurrency/atomic/2305252031.html b/note/java/concurrency/atomic/2305252031.html index 40d56338..4135b60e 100644 --- a/note/java/concurrency/atomic/2305252031.html +++ b/note/java/concurrency/atomic/2305252031.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

ThreadLocal

Yaien Blog原创大约 11 分钟并发ThreadLocal

ThreadLocal

ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。

ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。

使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。

使用

public class UserContext {
@@ -90,6 +90,6 @@
     }
 } 
 

在上述示例中,我们创建了一个ThreadLocal实例,并在子线程中将大量的数据存储到ThreadLocal中。然而,在子线程结束后,我们没有调用remove()方法来清理ThreadLocal中的数据。 如果运行该示例,主线程结束后,由于没有清理ThreadLocal中的数据,可能会导致内存泄漏。每个线程的ThreadLocal实例会持有对线程的引用,导致线程无法被垃圾回收。 为了避免内存泄漏,应该在合适的时机调用remove()方法,清理ThreadLocal中的数据。在上述示例中,可以在子线程的末尾添加threadLocal.remove()来手动清理ThreadLocal数据。

为了避免ThreadLocal可能引发的内存泄漏问题,可以采取以下措施:

  1. 及时调用remove()方法:在使用完ThreadLocal后,应该在合适的时机调用remove()方法,清理ThreadLocal中的数据。可以使用try-finally块,确保在使用完后无论是否发生异常都能够调用remove()方法。
  2. 使用线程池时注意清理:如果在使用线程池时使用了ThreadLocal,需要特别注意清理ThreadLocal数据。在任务执行结束后,可以通过线程池提供的钩子方法(如afterExecute())来清理ThreadLocal数据。
  3. 使用InheritableThreadLocal时小心传递:如果使用InheritableThreadLocal,它会将数据从父线程传递给子线程。在使用InheritableThreadLocal时,需要注意在子线程中是否需要清理或重置ThreadLocal数据,以防止不必要的数据泄漏。
  4. 避免将ThreadLocal放在静态变量中:在某些情况下,将ThreadLocal实例放在静态变量中可能导致意外的内存泄漏。尽量避免在静态变量中使用ThreadLocal,或者在使用完后及时清理数据。
  5. 使用弱引用的ThreadLocal实现:可以自定义ThreadLocal的子类,使用弱引用(WeakReference)来持有线程本地的值。这样,在没有其他强引用指向ThreadLocal实例时,ThreadLocal的键值对将被垃圾回收。
上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/atomic/2305281438.html b/note/java/concurrency/atomic/2305281438.html index 85211504..676d5849 100644 --- a/note/java/concurrency/atomic/2305281438.html +++ b/note/java/concurrency/atomic/2305281438.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Atomic原子操作详解

Yaien Blog原创大约 7 分钟并发Atomic

Atomic原子操作详解

原子操作是在并发编程中指不能被中断的、不可分割的操作。它要么完全执行,要么完全不执行,不会出现部分执行的情况。

并发原子操作理解

在并发编程中,原子操作是指不可被中断的、不可分割的最小操作单位。原子操作要么完全执行,要么完全不执行,不存在中间状态。

在多线程环境下,多个线程可以同时执行,因此可能出现竞争条件(Race Condition),即多个线程同时访问和修改共享的数据,导致数据不一致或不确定的结果。
原子操作可以帮助我们解决这个问题,通过确保某个操作以原子方式执行,从而避免了竞争条件。

在Java中,你可以通过以下几种方式来实现并发原子操作:

  1. 使用原子类:Java提供了java.util.concurrent.atomic包,其中包含了一系列的原子类,如AtomicInteger、AtomicLong等。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。通过使用这些原子类,你可以在多线程环境中实现对共享数据的安全访问和修改。
  2. 使用锁:通过使用锁机制,比如synchronized关键字或ReentrantLock类,你可以确保同一时间只有一个线程可以访问被锁定的代码块或资源。通过在关键的代码段上加锁,可以保证原子性操作的执行。只有持有锁的线程才能执行被锁定的代码块,其他线程必须等待锁释放。
  3. 使用volatile关键字:将共享变量声明为volatile,可以确保变量的读取和写入操作具有可见性和有序性。volatile关键字保证了变量在多个线程之间的一致性,每次对volatile变量的读取都是从主内存中获取最新值,每次对volatile变量的写入都会立即刷新到主内存。
  4. 使用原子操作工具类:Java还提供了一些原子操作的工具类,如java.util.concurrent.atomic.AtomicIntegerArray和java.util.concurrent.atomic.AtomicReference等。这些工具类提供了对数组和引用类型的原子操作支持,可以在并发环境中实现对数组元素或引用对象的原子操作。

synchronized VS atomic

synchronized

synchronized 是 Java 中的关键字,用于实现线程同步和互斥访问共享资源。
通过使用synchronized关键字修饰代码块或方法,可以确保同一时间只有一个线程可以执行被锁定的代码块或方法。synchronized关键字提供了内置的互斥机制,它会自动获取锁和释放锁,保证了代码块或方法的原子性操作。在使用synchronized时,需要注意锁的粒度和范围,以避免死锁和性能问题。

atomic

atomic 是 Java 并发包 (java.util.concurrent.atomic)中提供的一组原子类。这些原子类提供了一些常见的原子操作,如原子的读取、写入、比较和交换等。它们通过使用底层的CAS(Compare-and-Swap)操作来实现线程安全的原子操作。CAS
是一种乐观锁机制,它利用硬件的原子性操作来实现对共享变量的并发修改。原子类中的方法都是原子的,不需要显式加锁,因此可以在高并发环境中获得较好的性能。使用原子类可以避免使用锁带来的开销和潜在的死锁问题。

选择使用 synchronized 还是 atomic 取决于具体的需求和场景。一般来说:

  • 当只需要在特定的代码块或方法上实现原子操作时,可以选择使用synchronized。它更适合于复杂的同步逻辑和资源访问控制。
  • 当只需要对单个变量进行原子操作时,可以选择使用atomic。它更适合于简单的操作,并且在高并发环境中性能更好。

atomic实现原子操作

java.util.concurrent.atomic 包中的原子类通过底层的CAS(Compare-and-Swap)操作来实现原子操作。CAS 是一种乐观锁机制,它利用硬件原子性操作来实现对共享变量的并发修改。

CAS实现原理

CAS 操作包含三个参数:内存位置(变量)、期望值和新值。CAS 操作的执行过程如下:

  • 通过读取内存位置的值,获取当前的变量值。
  • 比较当前的变量值与期望值是否相等。如果相等,则说明变量值没有被其他线程修改,可以执行更新操作。 将新值写入内存位置,更新变量的值。
  • 如果不相等,说明其他线程已经修改了变量的值,当前线程的更新操作失败。此时可以选择重试或执行其他逻辑。

CAS 操作是原子的,因为在执行比较和写入操作期间,不会被其他线程中断或干扰。它依赖于底层硬件的原子性操作,例如处理器提供的原子指令(例如 Compare-and-Swap、Load-Link/Store-Conditional)。

CAS存在的问题

自旋重试

CAS 操作失败时,线程需要不断地自旋重试,直到成功为止。这会消耗一定的 CPU 资源。在高并发情况下,多个线程同时进行 CAS 操作可能会导致大量的自旋重试,从而增加系统负载。

ABA 问题

CAS 操作只关注变量的值是否与期望值相等,而不考虑变量值在操作期间是否发生了变化。这可能引发 ABA 问题。例如,线程 A 将变量从初始值 A 修改为 B,然后再修改回 A,而线程 B 在此期间执行了一个操作,期望变量的值为 A,CAS操作会认为符合条件,导致操作成功。然而,线程 A 并不知道变量的值已经被修改过。解决 ABA 问题的方法之一是使用带有版本号的原子类(如AtomicStampedReference),以便在比较时同时考虑变量值和版本号。

只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,可以使用循环CAS的方式保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原则性。

从JDk5开始提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里进行CAS操作

竞争条件

在高并发环境下,多个线程同时进行 CAS 操作,竞争同一个变量,可能会导致竞争条件的发生。如果多个线程同时执行 CAS 操作,只有一个线程的操作会成功,其他线程的操作会失败并进行重试。这种竞争会降低整体的性能。

内存模型限制

CAS 操作依赖于底层硬件的原子性指令,因此对内存模型的限制较大。在一些特殊的内存模型或平台上,CAS 操作可能不具备原子性,或者在使用过程中需要特殊的配置或处理。

LongAdder

LongAdder是JDK8时引入的一个原子类。主要解决的是在高并发环境下热点数据读写,对原子属性进行写操作时,通过写热点分散,减少竞争,它可以提供更好的性能和吞吐量。

与AtomicLong相比,LongAdder在高并发环境下通常具有更好的性能其原理就是因为LongAdder内部维护了一组变量,将计数器的值分散到这些变量中,不同线程对不同变量进行累加操作,从而减少了竞争。当需要获取计数器的总和时,LongAdder会将所有变量的值累加起来,得到最终的结果。

需要注意的是,LongAdder在某些情况下可能会产生略微的误差,因为它的结果是根据当前的内部状态计算得出的,并且可能会受到并发更新的影响。如果需要精确的结果,可以使用LongAccumulator或AtomicLong等其他适用的原子类。

import java.util.concurrent.atomic.LongAdder;
@@ -56,6 +56,6 @@
     }
 }
 

以上示例,使用LongAdder时,可以通过调用increment()、decrement()和add()等方法来进行计数的增加、减少和加法操作。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/atomic/2305281702.html b/note/java/concurrency/atomic/2305281702.html index c649ef15..2a2044d5 100644 --- a/note/java/concurrency/atomic/2305281702.html +++ b/note/java/concurrency/atomic/2305281702.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

线程安全

Yaien Blog原创大约 12 分钟并发线程安全

线程安全

线程安全性是指多线程环境下,一个函数、对象或系统的行为是否可以正确地处理多个线程同时访问或修改共享的数据而不会产生不确定的结果或导致数据损坏。
在并发编程中,线程安全性是一个非常重要的概念,因为多线程同时操作共享资源时可能引发竞态条件(race conditions)和其他并发问题。

如果要实现线程安全性,就要保证我们的类是线程安全的的。在《 Java 并发 编程实战》 中, 定义“类是线程安全的”如下:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

理解

原子性(Atomicity)

指一个操作是不可中断的。一个原子操作要么完全执行成功,要么完全不执行,不会出现中间状态。在并发环境中,如果多个线程同时执行某个原子操作,那么该操作的执行结果应当与线程的执行顺序无关,保证数据的一致性。

可见性(Visibility)

指一个线程对共享数据的修改对其他线程是可见的。当一个线程修改了共享数据时,其他线程应当能够立即看到最新的修改结果,而不是看到过期或无效的数据。

实现线程安全

线程封闭

线程封闭就是把对象封装到一个线程里,只有这一个线程能看到此对象。那么这个对象就算不是线程安全的也不会出现任何安全问题。

线程封闭是一种简单有效的并发编程技术,适用于某些场景下的数据隔离需求。它能够减少线程间的竞争和同步开销,提高并发程序的性能和可靠性。然而,需要注意线程封闭可能带来的局限性,如线程安全性和数据一致性的保证,以及对并发性能的影响等。因此,在使用线程封闭时需要仔细评估场景和需求,确保其适用性和正确性。

栈封闭(Stack Confinement)

将数据保存在线程栈的局部变量中。由于局部变量的作用域仅限于所属线程的执行上下文,其他线程无法访问到该数据,因此可以避免并发访问的问题。

ThreadLocal

使用Java中的ThreadLocal类,可以将数据与当前线程关联起来,使得每个线程都有自己的数据副本。ThreadLocal提供了线程级别的数据隔离,每个线程对数据的访问都是独立的,从而避免了并发访问的问题。

单线程模型

某些情况下,可以将任务或资源限制在单个线程中进行处理,从而避免了并发访问的问题。例如,使用单个线程的事件驱动模型,或者使用单个线程的线程池来处理任务。

无状态的类

无状态的类是指不包含任何实例变量(或称为状态)的类,其行为仅依赖于传入的参数。换句话说,无状态类的方法不会受到类级别的状态影响,每次调用方法时,都只关注输入参数和方法的逻辑,而不依赖于类内部的状态信息。

public class StringUtils {
@@ -167,6 +167,6 @@
     }
 }
 

在这个示例中,有两个线程 thread1 和 thread2,它们都试图交替执行某个操作,但由于逻辑上的冲突,导致无法顺利交替执行,最终进入了活锁状态。

产生原因

  • 响应性过度

当线程遇到冲突或竞争时,为了避免死锁,它们试图改变自己的状态或行为。然而,如果所有线程都同时响应并改变自己的行为,就可能导致活锁的发生。

  • 同步策略问题

不恰当的同步策略或竞争条件可能导致线程在忙等状态下相互响应,无法前进。

避免方式

  • 随机化

通过引入随机因素,使得线程在冲突时具有不确定性的行为,减少线程之间的同步冲突,降低活锁的可能性。

  • 退避策略

当线程遇到冲突时,可以使用退避策略,即暂停一段时间后再尝试,避免持续的忙等状态。

  • 合理的调度策略

合理的线程调度策略可以减少线程之间的竞争和冲突,降低活锁的发生概率。

  • 重试次数限制

对于一些可能导致活锁的操作,可以设置重试次数限制,超过限制则采取其他策略。

  • 分布式算法

在分布式系统中,可以采用一些分布式算法,如仲裁者、协调者等,来解决并发冲突和避免活锁的问题。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/atomic/2305312110.html b/note/java/concurrency/atomic/2305312110.html index 144e4565..93e21925 100644 --- a/note/java/concurrency/atomic/2305312110.html +++ b/note/java/concurrency/atomic/2305312110.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

JUC

Yaien Blog原创大约 10 分钟并发线程安全JUC

JUC

JUC 是 Java Util Concurrent(Java 并发工具包)的缩写,是 Java 提供的用于并发编程的工具包。Java 并发工具包位于 java.util.concurrent
包下,提供了一组强大的工具和类,用于简化并发编程、提高性能和可扩展性。

ReentrantLock

ReentrantLock 是 Java 并发工具包中提供的一个可重入锁类。它实现了 Lock 接口,提供了比使用 synchronized 关键字更灵活和可扩展的锁机制。

import java.util.concurrent.locks.ReentrantLock;
@@ -186,6 +186,6 @@
     }
 }
 

在上述示例中,有5个线程执行任务,并在任务完成后调用 countDown() 方法减少计数器的值。主线程通过调用 await() 方法等待计数器的值变为零,以确保所有线程的任务都完成后再继续执行。

CountDownLatch 的主要方法包括:

CountDownLatch(int count)

创建一个指定初始计数值的 CountDownLatch 对象。

void countDown()

计数器减一,表示一个操作已完成。

void await()

线程等待,直到计数器的值变为零。如果计数器的值已经是零,那么该方法会立即返回。

boolean await(long timeout, TimeUnit unit)

在指定的时间范围内等待,直到计数器的值变为零,或等待时间超时。返回值表示是否在等待时间内计数器变为零。

使用场景

  • 并行任务同步

CountDownLatch可以用于协调多个并行任务的完成情况,确保所有任务都完成后再继续执行下一步操作。

  • 多任务汇总

CountDownLatch可以用于统计多个线程的完成情况,以确定所有线程都已完成工作。

  • 资源初始化

CountDownLatch可以用于等待资源的初始化完成,以便在资源初始化完成后开始使用。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/atomic/index.html b/note/java/concurrency/atomic/index.html index 3b306b03..bf5dd63f 100644 --- a/note/java/concurrency/atomic/index.html +++ b/note/java/concurrency/atomic/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/concurrency/index.html b/note/java/concurrency/index.html index 35a46ff1..0af26891 100644 --- a/note/java/concurrency/index.html +++ b/note/java/concurrency/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/concurrency/lock/2305312243.html b/note/java/concurrency/lock/2305312243.html index 00bb0b0e..626043e5 100644 --- a/note/java/concurrency/lock/2305312243.html +++ b/note/java/concurrency/lock/2305312243.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

AQS

Yaien Blog原创大约 8 分钟并发线程安全AQS

AQS

AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。

AQS 的设计思想是使用一个等待队列来管理线程的竞争和等待状态。它维护了一个双向链表的队列,其中的每个节点表示一个等待线程,线程以 FIFO(先进先出)的顺序排队等待。AQS 提供了基于 CAS(Compare and
Swap)操作的方法来管理队列和线程的状态,实现了线程的挂起、唤醒和竞争。

特性

  1. 状态管理

AQS 维护了一个同步状态(synchronization state),它表示同步器的状态信息。同步状态可以是任意的整数值,并且可以被子类用来表示不同的状态和含义。

  1. 线程阻塞和唤醒

AQS 提供了线程阻塞和唤醒的机制,以实现线程的等待和恢复。线程在无法获取同步资源时可以被阻塞,直到其他线程释放资源并唤醒它们。

  1. 等待队列

AQS 使用一个等待队列(wait queue)来管理等待获取同步资源的线程。等待队列是一个双向链表,线程以 FIFO(先进先出)的顺序排队等待。通过等待队列,AQS 实现了公平性和线程的顺序保证。

  1. 独占与共享模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

  1. 可重入性

AQS 支持同一个线程多次获取同步资源,即可重入(Reentrant)特性。同一个线程可以多次获取同步资源,而不会造成死锁或其他并发问题。

  1. 条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

核心结构

同步状态(Synchronization State)

AQS 内部维护了一个同步状态(synchronization state),它是一个整数值,表示同步器的状态信息。同步状态可以用来表示不同的状态和含义,具体取决于具体的同步器实现。

等待队列(Wait Queue)

AQS 使用一个等待队列来管理等待获取同步资源的线程。等待队列是一个双向链表,每个节点表示一个等待线程。等待队列的设计保证了线程的先进先出(FIFO)顺序。

Node 对象

等待队列中的每个节点都由 Node 对象表示,它包含了线程等待状态、线程引用以及等待条件等信息。Node 对象的设计和状态变化对于实现同步的正确性和性能至关重要。

CAS 操作

AQS 使用 CAS(Compare and Swap)操作来实现同步状态的原子性操作。通过 CAS,可以确保同步状态的更新是原子的,避免多个线程同时修改同一状态造成的竞态条件。

共享模式和独占模式

AQS 支持独占模式和共享模式的同步器。独占模式是指一次只能有一个线程获取同步资源,而共享模式是指多个线程可以同时获取同步资源。

条件变量支持

AQS 提供了 Condition 对象,用于支持条件变量的功能。条件变量允许线程在满足特定条件之前等待,并在条件满足时被唤醒。

方法

AQS 的主要方法包括:

  • acquire(int arg):获取同步状态,如果获取不到则进入等待状态。
  • release(int arg):释放同步状态,并唤醒等待队列中的下一个线程。
  • tryAcquire(int arg):尝试获取同步状态,如果成功则返回 true,否则返回 false。
  • tryRelease(int arg):尝试释放同步状态,如果成功则返回 true,否则返回 false。
  • isHeldExclusively():判断当前线程是否独占持有同步状态。

访问方式

Exclusive(独占)

独占模式(Exclusive)是指在同一时刻只允许一个线程持有锁或访问临界区,其他线程必须等待锁的释放才能继续执行。

通过 AQS 的独占模式,多个线程可以竞争同一个锁,但只有一个线程可以持有锁进行临界区的访问,从而实现线程间的互斥和同步。许多基于 AQS 的同步器和锁,如ReentrantLock就是基于 AQS
的独占模式实现的。开发者可以通过继承 AQS 并实现相关的抽象方法来构建自定义的独占模式同步器。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在独占锁模式下,state 的值通常用于表示锁的持有状态,例如 0 表示未锁定状态,1 表示锁定状态。
  2. 获取锁
    • 当一个线程尝试获取独占锁时,它会调用 AQS 的 acquire() 方法。
    • 在 acquire() 方法中,线程首先会尝试通过 CAS(Compare and Swap)操作将 state 从 0 更新为 1,以尝试获取锁。
    • 如果 CAS 操作成功,表示锁获取成功,线程可以继续执行临界区的代码。
    • 如果 CAS 操作失败,表示锁已被其他线程持有,当前线程将被加入到等待队列尾部,进入等待状态。
  3. 释放锁
    • 当持有锁的线程需要释放锁时,它会调用 AQS 的 release() 方法。
    • 在 release() 方法中,线程首先会将 state 的值设置为 0,表示锁已释放,然后,线程会检查等待队列中是否有等待的线程,如果有,则选择一个线程唤醒,使其能够继续尝试获取锁。

Share(共享)

共享模式(Shared Mode)用于实现共享锁的功能。其允许多个线程同时获取同一个锁或访问临界区,以实现并发访问共享资源的能力。

通过 AQS 的共享模式,多个线程可以同时获取同一个共享锁,进入临界区执行共享资源的访问。这种模式适用于一些读多写少的场景,允许多个线程同时读取共享资源,而在写操作时需要独占访问。基于 AQS 的共享锁,如
Semaphore或者CountDownLatch,提供了更高的并发性能,允许多个线程同时读取数据,提高系统的吞吐量。

实现思路

  1. 内部维护一个同步状态变量(state),用于表示锁的状态。在共享模式下,state 的值通常用于表示当前锁被多少个线程持有。
  2. 获取锁
    • 当一个线程尝试获取共享锁时,它会调用 AQS 的 acquireShared() 方法。
    • 在 acquireShared() 方法中,线程会根据当前的同步状态(state)和其他线程的状态,决定是否能够获取共享锁。
    • 如果可以获取共享锁,则线程可以继续执行临界区的代码。
    • 如果无法获取共享锁,线程将进入等待状态,并加入等待队列。
  3. 释放锁
    • 当持有共享锁的线程需要释放锁时,它会调用 AQS 的 releaseShared() 方法。
    • 在 releaseShared() 方法中,线程首先会更新同步状态(state)来释放共享锁,然后,线程会通知等待队列中的其他线程,让它们有机会竞争获取共享锁。

队列

Synchronization Wait Queue(同步等待队列)

同步等待队列是 AQS 中用于管理等待获取锁的线程的队列。当一个线程无法获取锁而需要等待时,它会被加入到同步等待队列中。同步等待队列是一个双向链表,由 Node 对象表示,每个节点对应一个等待线程。

Condition Wait Queue(条件等待队列)

条件等待队列是 AQS 中用于管理等待特定条件的线程的队列。AQS 提供了 Condition 对象来支持条件变量的功能。当一个线程在某个条件上等待时,它会被加入到条件等待队列中。条件等待队列是一个单向链表,由 Node
对象表示,每个节点对应一个等待线程。

区别

这两种队列的区别在于它们所管理的线程的目的和等待条件不同:

  • 同步等待队列用于管理等待获取锁的线程,这些线程都是在同步操作中等待锁的释放。
  • 条件等待队列用于管理等待特定条件的线程,这些线程等待条件满足才能继续执行。

节点状态

  1. 初始值

值为0,表示当前节点在sync队列中,等待着获取锁。

  1. CANCELLED

值为1,表示节点被取消。当一个节点的线程被中断或超时等情况下,节点可能会被取消。

  1. SIGNAL

值为-1,表示后继节点被阻塞。当一个节点的线程需要释放锁或满足某个条件时,它会唤醒其后继节点。

  1. CONDITION

值为-2,表示节点在条件队列中等待。当一个线程调用了 Condition 的 await() 方法后,它会被移动到条件队列中,并处于等待状态。

  1. PROPAGATE

值为-3,表示共享模式传播。用于共享模式同步器中,表示当前线程需要唤醒其后继节点。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/lock/2306011640.html b/note/java/concurrency/lock/2306011640.html index 6f24738c..b0d8b069 100644 --- a/note/java/concurrency/lock/2306011640.html +++ b/note/java/concurrency/lock/2306011640.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

ReentrantLock(独占锁)

Yaien Blog原创大约 5 分钟并发线程安全Lock

ReentrantLock(独占锁)

ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock
接口的一个实现。ReentrantLock
是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。

import java.util.concurrent.locks.ReentrantLock;
@@ -152,6 +152,6 @@
         return free;
     }
 
  1. 调用lock.unlock()
  2. 调用release(1)方法
    • 调用tryRelease(1)方法,首先获取state执行-1操作,如果-1后的值为0,将exclusiveOwnerThread置空,最后将-1后的值赋值到state,释放锁
    • 锁释放成功,唤醒队列中下一个线程节点,当前线程出队

非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序进行的。这样可能造成优先级高的线程因为等待而导致一种称为“饥饿”的现象,但是相对于公平锁,非公平锁的性能上要高。在ReentrantLock内部,如果有线程试图获取锁,且锁当前未被其他线程持有,那么无论等待队列中是否有线程在等待,该线程都能立即获取到锁,即 "
插队"。

实现原理

公平锁的实现与非公平锁实现原理类似,只不过在加锁之后,直接就调用了acquire(1)方法,而非公平锁再进入acquire(1)方法之前还会去尝试加锁。

注意

  1. 在等待队列中的线程时,一定是唤醒下标为1的,因为0是记录当前获取锁的线程
  2. 非公平的实现,只针对于线程入队之前,线程入队后还是会遵循队列的先进先出原则

使用场景

  • 多线程并发控制
  • 超时等待
  • 可中断锁实现
  • 公平锁
  • 多条件锁实现
上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/lock/2306021120.html b/note/java/concurrency/lock/2306021120.html index 615d1ce5..e8de20dc 100644 --- a/note/java/concurrency/lock/2306021120.html +++ b/note/java/concurrency/lock/2306021120.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

ReentrantReadWriteLock(读写锁)

Yaien Blog原创大约 7 分钟并发线程安全Lock

ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLock是一个读写锁,它内部维护了两个锁:ReadLockWriteLock。ReadLock用于只读操作,WriteLock用于写操作。 如果没有写操作,读锁是可以被多个线程同时持有的,即写锁是独占的,读锁是共享的。

import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -206,6 +206,6 @@
         return fullTryAcquireShared(current);
     }
 

性能问题

默认情况下,ReentrantReadWriteLock 是非公平的,这意味着在高并发的情况下,读锁可能会一直占用,导致写锁长时间无法获取,也就是发生了写锁饥饿。虽然可以通过构造函数指定为公平锁来避免这个问题,但是公平锁的性能会稍低一些。

jdk1.8引入了StampedLock,这是一个新的读写锁,设计目标就是为了解决ReentrantReadWriteLock在高并发场景下的一些性能问题。

使用场景

  • 读多写少
  • 需要读写分离
  • 缓存
上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/lock/2306021924.html b/note/java/concurrency/lock/2306021924.html index ab3aceb9..d2c8c5d5 100644 --- a/note/java/concurrency/lock/2306021924.html +++ b/note/java/concurrency/lock/2306021924.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

StampedLock(读写锁)

Yaien Blog原创大约 3 分钟并发线程安全Lock

StampedLock(读写锁)

StampedLock 是 Java 8 引入的一个新的读写锁,其设计目标是为了解决 ReentrantReadWriteLock 的一些性能问题,提供了乐观读锁的机制。

import java.util.concurrent.locks.StampedLock;
@@ -82,6 +82,6 @@
     return Math.sqrt(currentX * currentX + currentY * currentY);
 }
 

以上代码是一个乐观读的例子,在这个例子中,首先尝试获取一个乐观读锁,然后读取数据。如果在读取数据的过程中,有其他线程获取到了写锁,我们就重新获取一个读锁,然后再次读取数据。

基本思想

如果一个线程去读取数据,它假设在读数据过程中不会被其他线程进行写操作,因此他并不会去真正的获取一把锁,而是获取一个stamp时间戳,然后直接读取数据。
读取完成后,这个线程会使用 validate()方法检查在它读取数据的过程中,是否有其他线程获取到了写锁。

  • 如果没有,那么它就可以确信读到的数据是有效的。
  • 如果有其他线程获取到了写锁,那么它就需要使用一种回退策略,通常是尝试重新获取一个读锁或写锁。

优缺点

优点:性能高。乐观读是进行了假设,直接获取值后再去判断获取值是否有变更,并不阻塞写线程
缺点:如果数据频繁发生变更,乐观读可能需要多次回退重试才能读取到有效的数据,这可能就会导致实际性能低于普通读锁

使用场景

StampedLock的使用场景通常在需要高并发读写操作的情况下,而且读操作远大于写操作,这时候使用 StampedLock 可以提高性能。

StampedLock特别适合在数据结构中,如哈希映射和并发数组等。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/lock/index.html b/note/java/concurrency/lock/index.html index dc676e82..e72f365c 100644 --- a/note/java/concurrency/lock/index.html +++ b/note/java/concurrency/lock/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/concurrency/queue/2306041045.html b/note/java/concurrency/queue/2306041045.html index 936fc3c1..213a092f 100644 --- a/note/java/concurrency/queue/2306041045.html +++ b/note/java/concurrency/queue/2306041045.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

BlockingQueue(阻塞队列)

Yaien Blog原创大约 4 分钟并发线程安全Queue

BlockingQueue(阻塞队列)

BlockingQueue是java.util.concurrent包下的一个接口,它是Queue接口的一个子接口。相比于普通的Queue,BlockingQueue的主要特性是,当试图向满的队列中添加元素或从空的队列中获取元素时,队列会阻塞插入/获取操作。这两种操作使得BlockingQueue适合用于生产者-消费者模型,在多线程环境中处理数据共享问题。

Queue接口

  • boolean add(E e): 添加一个元素,添加成功返回true, 如果队列满了,就会抛出异常

  • boolean offer(E e): 添加一个元素,添加成功返回true, 如果队列满了,返回false

  • E remove(): 返回并删除队首元素,队列为空则抛出异常

  • E poll(): 返回并删除队首元素,队列为空则返回null

  • E element(): 返回队首元素,但不移除,队列为空则抛出异常

  • E peek(): 获取队首元素,但不移除,队列为空则返回null

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则等待可用的空间。这是一个阻塞操作。

  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。

  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。

  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间,如果在指定的时间内,队列仍为空,则返回
    null。

  • int remainingCapacity(): 返回队列还剩下多少空间。

  • boolean drainTo(Collection<? super E> c): 移除此队列中所有可用的元素,并将它们添加到给定的集合中。

  • boolean drainTo(Collection<? super E> c, int maxElements): 从此队列中移除最多给定数量的可用元素,并将这些元素添加到给定的集合中。

主要实现

  • ArrayBlockingQueue: 一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
  • DelayQueue: 一个使用优先级队列实现的无界阻塞延迟队列。
  • PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列。
  • SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作。

使用场景

  • 线程池:
    线程池中的任务队列通常是一个阻塞队列。当任务数超过线程池的容量时,新提交的任务将被放入任务队列中等待执行。线程池中的工作线程从任务队列中取出任务进行处理,如果队列为空,则工作线程会被阻塞,直到队列中有新的任务被提交。

  • 生产者-消费者模型:
    在生产者-消费者模型中,生产者向队列中添加元素,消费者从队列中取出元素进行处理。阻塞队列可以很好地解决生产者和消费者之间的并发问题,避免线程间的竞争和冲突。

  • 消息队列:
    消息队列使用阻塞队列来存储消息,生产者将消息放入队列中,消费者从队列中取出消息进行处理。消息队列可以实现异步通信,提高系统的吞吐量和响应性能,同时还可以将不同的组件解耦,提高系统的可维护性和可扩展性。

  • 缓存系统:
    缓存系统使用阻塞队列来存储缓存数据,当缓存数据被更新时,它会被放入队列中,其他线程可以从队列中取出最新的数据进行使用。使用阻塞队列可以避免并发更新缓存数据时的竞争和冲突。

  • 并发任务处理:
    在并发任务处理中,可以将待处理的任务放入阻塞队列中,多个工作线程可以从队列中取出任务进行处理。使用阻塞队列可以避免多个线程同时处理同一个任务的问题,并且可以将任务的提交和执行解耦,提高系统的可维护性和可扩展性。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/queue/2306042021.html b/note/java/concurrency/queue/2306042021.html index b862c17d..56266beb 100644 --- a/note/java/concurrency/queue/2306042021.html +++ b/note/java/concurrency/queue/2306042021.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

ArrayBlockingQueue(有界阻塞队列)

Yaien Blog原创大约 6 分钟并发线程安全Queue

ArrayBlockingQueue(有界阻塞队列)

ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的有界阻塞队列
队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。

原理

  1. 使用有界阻塞队列,队列中元素先进先出,存取元素操作相互排斥
  2. 使用静态数组,容量固定,在构建ArrayBlockingQueue时必须指定长度,并且没有扩容机制
  3. 线程安全使用ReentrantLock来实现,存取的是同一把锁,操作的是同一个数组队形,存取操作相互排斥
  4. 入队是从队首开始添加元素,并记录putIndex,同时唤醒notEmpty(当putIndex到达队尾时设置为0)
  5. 出队也是从队首开始取出元素,并记录takeIndex,同时唤醒notFull(当takeIndex到达队尾时设置为0)

注意:两个Index指针都是从队首像队尾移动,保证队列先进先出原则

ArrayBlockingQueue使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有一个线程可以进行入队或者出队操作;这也就意味着生产者和消费者无法并行操作,在高并发场景下会成为性能瓶颈。

核心属性

  • final Object[] items: 这是一个数组,用来保存队列中的元素。

  • int takeIndex: 表示下一个要被获取(或“take”)的元素在数组中的位置。如果队列为空,则没有具体的意义。

  • int putIndex: 表示下一个要添加(或“put”)的元素在数组中的位置。如果队列已满,则没有具体的意义。

  • int count: 表示队列中当前的元素数量。

  • final ReentrantLock lock: 重入锁,用于控制对队列的并发访问。

  • private final Condition notEmpty: 当队列为空时,获取元素的线程可以在这个条件上等待。

  • private final Condition notFull: 当队列已满时,添加元素的线程可以在这个条件上等待。

核心方法

  • void put(E e): 将指定元素插入此队列中,如果队列已满,则阻塞等待可用的空间。
  • E take(): 从队列中取出并删除一个元素,如果队列为空,当前线程则会阻塞,直到有元素可以获取。
  • boolean offer(E e, long timeout, TimeUnit unit): 尝试将元素插入队列,如果队列已满,则等待指定的等待时间。如果在指定的时间内,队列仍然没有可用空间,那么返回
    false。如果插入成功,则返回 true。
  • E poll(long timeout, TimeUnit unit): 尝试从队列中获取并删除第一个元素,并等待指定的时间。如果在指定的时间内,队列仍为空,则返回
    null。
  • int remainingCapacity(): 返回此队列中剩余的可用空间。

入队源码

    // 添加元素
@@ -110,6 +110,6 @@
         return x;
     }
 

思考:为什么ArrayBlockingQueue对数组操作要设计成双指针?

img
img

使用双指针的好处在于可以避免数组的复制操作。

如果使用单指针,每次删除元素时需要将后面的元素全部向前移动,这样会导致时间复杂度为 O(n)。
而使用双指针,我们可以直接将 takeIndex 指向下一个元素,而不需要将其前面的元素全部向前移动。
同样地,插入新的元素时,我们可以直接将新元素插入到 putIndex 所指向的位置,而不需要将其后面的元素全部向后移动。
这样可以使得插入和删除的时间复杂度都是 O(1) 级别,提高了队列的性能。

使用场景

  • 生产者-消费者模式:ArrayBlockingQueue可以在生产者-消费者模式中使用,这是最常见的一种应用场景。例如,你有一个系统需要处理大量的任务,但是系统的处理能力有限,不能立即处理这些任务。你可以创建一个ArrayBlockingQueue,生产者线程将任务放入队列,消费者线程从队列中取出任务进行处理。当队列已满时,生产者线程会被阻塞,直到队列中有空闲的位置;当队列为空时,消费者线程会被阻塞,直到队列中有新的任务。

  • 资源池:ArrayBlockingQueue可以用来实现一个固定大小的资源池,例如数据库连接池、线程池等。资源被放入一个ArrayBlockingQueue,需要资源时从队列中取出,用完后再放回队列。当队列为空时,如果还需要资源,则需要等待,直到有资源被放回队列。

  • 数据流处理:如果你的系统需要处理一个数据流,例如日志文件、网络数据等,你可以创建一个ArrayBlockingQueue,一个线程负责从数据流中读取数据并放入队列,其他线程从队列中取出数据进行处理。这样可以有效地分离数据读取和数据处理两个环节,提高系统的处理能力。

总的来说,它可以应用在任何需要队列的场景,并且需要队列大小有界,或者需要阻塞操作的场景。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/queue/2306051101.html b/note/java/concurrency/queue/2306051101.html index e9ac94be..3a0f5db0 100644 --- a/note/java/concurrency/queue/2306051101.html +++ b/note/java/concurrency/queue/2306051101.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

LinkedBlockingQueue(链表结构的阻塞队列)

Yaien Blog原创大约 4 分钟并发线程安全Queue

LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。

原理

  1. 实现的是无界阻塞队列,可以指定容量,默认Integer.MAX_VALUE,先进先出,存取互不干扰
  2. 数据结构选用链表,可指定容量,内存存粗Node元素
  3. 实现两把锁,存取互不干扰,存取操作的是不同的Node对象,但是删除元素时存取元素都会加锁
  4. 入队是从队尾入队,有last指针记录
  5. 出队从队首出,有head指针记录

核心属性

  • Node[] items:链表实现,用来存储队列中的元素。每个节点包含一个元素和指向下一个节点的链接。

  • ReentrantLock takeLock:可重入锁,用于控制元素的移除操作。当多个线程试图移除队列中的元素时,这个锁确保了只有一个线程可以执行该操作。

  • ReentrantLock putLock:可重入锁,用于控制元素的插入操作。当多个线程试图向队列中插入元素时,这个锁确保了只有一个线程可以执行该操作。

  • Condition notEmpty:用于协调消费者线程。当队列为空,消费者线程试图移除元素时,它们会等待这个条件变量。

  • Condition notFull:用于协调生产者线程。当队列已满,生产者线程试图插入元素时,它们会等待这个条件变量。

  • AtomicInteger count:用来记录队列中当前的元素数量。

  • capacity (int):队列的容量,如果在创建队列时没有指定容量,那么容量将等于 Integer.MAX_VALUE。

入队源码

    // 添加元素
@@ -110,6 +110,6 @@
         return x;
     }
 

使用场景

  • 任务队列:在多线程编程中,常常需要使用一个任务队列来存储待处理的任务。例如,在一个网页爬虫程序中,可以创建一个LinkedBlockingQueue,用于存储待爬取的网页URL。一个或多个生产者线程负责从网络上发现新的URL并将它们添加到队列中,一个或多个消费者线程负责从队列中取出URL并下载网页内容。

  • 日志处理:在服务器应用中,可能需要处理大量的日志消息。可以创建一个LinkedBlockingQueue,用于存储待处理的日志消息。一个或多个生产者线程负责生成日志消息并将它们添加到队列中,一个或多个消费者线程负责从队列中取出日志消息并写入日志文件或发送到日志服务器。

  • 资源池: LinkedBlockingQueue也可以用于创建资源池,例如数据库连接池、线程池等。当需要一个资源时,可以从队列中取出,当不再需要这个资源时,可以将它放回队列。这样可以有效地复用资源,提高系统的效率。

思考:线程池为什么使用LinkedBlockingQueue而不是ArrayBlockingQueue呢?
因为LinkedBlockingQueue的入队和出队是两把锁,存取元素互不干扰。
ArrayBlockingQueue则是使用的同一把锁,存取元素时相互排斥。
LinkedBlockingQueue这种锁分离的方式可以有效地减少锁竞争,从而提高线程池的并发性能。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/queue/2306052321.html b/note/java/concurrency/queue/2306052321.html index 90b95bb0..bd8213c3 100644 --- a/note/java/concurrency/queue/2306052321.html +++ b/note/java/concurrency/queue/2306052321.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

DelayQueue(无界阻塞队列)

Yaien Blog原创大约 4 分钟并发线程安全Queue

DelayQueue(无界阻塞队列)

DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

原理

  1. 使用优先级队列实现的无界阻塞队列
  2. 优先级队列(PriorityQueue)与PriorityBlockingQueue类似,不过没有阻塞功能
  3. 线程安全使用ReentrantLock来实现,通过Condition available控制阻塞条件
  4. 入队不阻塞,并且队列没有边界,与优先级队列入队相同
  5. 出队为空时阻塞,不为空时检查堆顶元素过期时间,小于等于0则出队,否则表示元素还未过期,阻塞
  6. 阻塞时先判断leader线程是否为空(为了保证优先级),不为空表示已经有线程阻塞了,为空则将当前线程设置为leader,并按照过期时间进行阻塞

特性

  • 队列中的元素必须实现Delayed接口。在创建元素时,可以定义该元素的存活时间,当从队列获取元素时,只有满足该存活时间的元素才能被取出。
  • 向队列中插入元素的操作(例如put和offer)永远不会被阻塞。只有当队列为空,或者队列中的元素没有到达其存活时间时,获取元素的操作(例如take和poll)才会被阻塞。

核心属性

  • final PriorityQueue q: 实际存储队列元素的数据结构。它是一个优先队列,队列中的元素按到期时间排序,最早到期的元素在队列的头部。

  • Thread leader: 用于标记当前是否有线程在排队(仅用于取元素时) leader 指向的是第一个从队列获取元素阻塞的线程。

  • final transient ReentrantLock lock: 锁,用于控制对队列的并发访问。

  • final Condition available: 条件,用于表示现在是否有可取的元素 当新元素到达,或新线程可能需要成为leader时被通知。

入队源码

public void put(E e) {
@@ -99,6 +99,6 @@
     }
 }
 

使用场景

  • 任务调度:DelayQueue可以用于任务调度,例如一个任务需要在10分钟后执行,可以将这个任务放入DelayQueue,并设置延迟时间为10分钟,10分钟后这个任务就能从队列中取出,然后执行。Java的ScheduledThreadPoolExecutor就使用了DelayQueue进行任务的调度。

  • 缓存系统:在一个缓存系统中,DelayQueue可以用于存储缓存项,缓存项在创建时设置一个到期时间,到期后缓存项就能从DelayQueue中取出,然后清除。这种方式可以避免需要定时扫描所有缓存项来查找并清除过期的缓存项。

  • 会话管理:在网络编程中,可以使用DelayQueue来管理用户的会话,当用户的会话在一定时间内没有活动(例如没有发送或接收数据),那么这个会话就认为是过期的,可以从DelayQueue中取出并关闭。这种方式可以防止资源的浪费,提高服务器的处理能力。

  • 消息重发:在消息队列中,如果消息没有被正确处理,可以将消息放入DelayQueue并设置一个延迟时间,延迟时间过后这个消息就能从DelayQueue中取出并重新发送。

总的来说,其实只要是需要延迟处理的任务,都可以使用DelayQueue来实现。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/concurrency/queue/index.html b/note/java/concurrency/queue/index.html index be0e6f2a..239ad886 100644 --- a/note/java/concurrency/queue/index.html +++ b/note/java/concurrency/queue/index.html @@ -31,10 +31,10 @@ } - + - + diff --git "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312130940.html" "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312130940.html" index 8dafc81f..30698d90 100644 --- "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312130940.html" +++ "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312130940.html" @@ -31,7 +31,7 @@ } - +
跳至主要內容

线程池参数配置

Yaien Blog原创大约 6 分钟并发线程安全

线程池参数配置

记录线程池参数配置参考,以及配置的理论原理以及思路

参考理论

工作中对于线程池核心数与最大线程数的配置,需要根据业务实际需求来进行配置。对于业务功能任务,通常可分为三种:CPU密集型、IO密集型、混合型。

CPU密集型

CPU密集型任务,也被称作计算密集型任务,指的是那些在执行过程中,主要依赖于中央处理器(CPU)的计算能力来完成任务的工作负载。这类任务的特点是需要进行大量的数据处理、计算和逻辑判断,如数值计算、图像处理、视频编码等。在执行这些任务时,CPU的利用率通常很高,而其他资源如磁盘、网络等可能处于较空闲的状态。

    static class DefaultRunnable implements Runnable {
@@ -66,6 +66,6 @@
             Executors.defaultThreadFactory(),
             new DiscardPolicy());
 

核心数与最大线程数

创建线程池时,核心线程数(corePoolSize)和最大线程数(maximumPoolSize)的设置对线程池的性能和资源利用有很大影响。

什么时候设置核心数与最大线程数一样呢?

在某些需要快速响应并且并发量高的应用场景中,可以设置核心线程数和最大线程数相同,以确保任务能够立即得到处理。例如,在线客服系统或者实时交易系统,任务一旦产生就需要立即处理,这时设置相同的核心线程数和最大线程数能够保证任务的快速处理。

什么时候又不需要设置一样大呢?

在某些应用场景中,任务的到达可能会出现波动,有时任务量很大,有时任务量较小。在这种情况下,可以设置较大的最大线程数,以便在任务量较大时能够创建更多的线程来处理任务。而核心线程数可以设置得较小,以保持线程池在空闲时占用较少的资源。

阻塞队列容量

线程池还需要配置一个阻塞队列,而阻塞队列的容量大小配置同样值得考虑。

假设现在创建了一个核心线程为10的线程,任务执行耗时需要1s,这就意味着1s可以同时处理10个任务,当我们设置阻塞队列容量为50,在容量填满的时候,最后10个任务就需要等待4s后才可以执行,再加上执行的1s,意味着这10个任务就需要5s之后才能执行完成。
这5s的耗时就是需要考虑的时间,如果项目中不允许耗时如此严重的任务存在,就需要缩小我们的容量来减少任务等待时间。

如果任务数超过了50,达到51个,那第51个会看当先线程数是否达到最大线程数,没达到就创建新线程执行,达到了就走拒绝策略,没有影响到队列中的等待任务。

上次编辑于:
贡献者: yanggl
- + diff --git "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312141621.html" "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312141621.html" index b8ed4497..2de5ff2f 100644 --- "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312141621.html" +++ "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/2312141621.html" @@ -31,10 +31,10 @@ } - +
跳至主要內容

线程池核心原理

Yaien Blog原创小于 1 分钟并发线程安全

线程池核心原理

上次编辑于:
贡献者: yanggl
- + diff --git "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/index.html" "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/index.html" index 960f269d..89a2512a 100644 --- "a/note/java/concurrency/\347\272\277\347\250\213\346\261\240/index.html" +++ "b/note/java/concurrency/\347\272\277\347\250\213\346\261\240/index.html" @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/index.html b/note/java/index.html index 53dafa9d..eab584d1 100644 --- a/note/java/index.html +++ b/note/java/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/jvm/2305052159.html b/note/java/jvm/2305052159.html index 7b96749b..4f95a900 100644 --- a/note/java/jvm/2305052159.html +++ b/note/java/jvm/2305052159.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

JVM简介

Yaien Blog原创大约 1 分钟JavaJVM

JVM简介

Java虚拟机(JVM)是Java程序运行行的关键组件,他负责将Java源代码转换为可执行的机器码。主要由:类加载器、运行时数据区、执行引擎、本地库接口组成。

JVM组成
JVM组成

类加载器(ClassLoad)

类加载器主要负责将Java字节码文件加载到内存中,以便程序运行时使用。

运行时数据区(Runtime Data Area)

运行时数据区是JVM内存的一部分,用于存储程序运行时的数据。它包括以下几个区域:

  • 程序计数器(Program Counter Register): 记录当前线程执行的字节码行号,用于指示下一条需要执行的指令。
  • Java虚拟机栈(Java Virtual Machine Stacks): 描述Java方法执行的内存模型,包括局部变量表、操作数栈、动态链接等信息。
  • 本地方法栈(Native Method Stack): 为虚拟机调用Native方法服务。
  • Java堆(Java Heap): 存放对象实例,是Java虚拟机中内存最大的一块,被所有线程共享。
  • 方法区(Method Area): 存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

执行引擎(Execution Engine)

执行引擎将字节码翻译成底层系统指令,再交由CPU执行

本地库接口(Native Interface)

本地库接口用于扩展Java平台的功能,融合不同编程语言的功能为Java使用。通过本地库接口,Java程序可以调用本地代码库中的功能,实现对特定平台资源的访问和操作。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2305111000.html b/note/java/jvm/2305111000.html index b140a120..b9990281 100644 --- a/note/java/jvm/2305111000.html +++ b/note/java/jvm/2305111000.html @@ -31,7 +31,7 @@ } - + - + diff --git a/note/java/jvm/2311071055.html b/note/java/jvm/2311071055.html index 672e4196..2e23896a 100644 --- a/note/java/jvm/2311071055.html +++ b/note/java/jvm/2311071055.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

深入理解Synchronized

Yaien Blog原创大约 11 分钟JavaJVMLock

深入理解Synchronized

synchronized关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。
synchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。

基本概念

临界资源

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区,其共享资源为临界资源

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

Synchronized

在Java中,所有的对象都有一个内置的锁。当一个线程进入一个synchronized方法或代码块时,它会获取这个锁,并在执行完毕后释放这个锁。其他任何尝试进入这个方法或代码块的线程都会被阻塞,直到当前线程释放锁。

synchronized关键字可以应用于实例方法、静态方法以及代码块。当它应用于实例方法时,锁是与当前对象实例关联的。当它应用于静态方法时,锁是与当前类关联的。当它应用于代码块时,锁是与当前对象实例或类关联的。

基本用法

Synchronized方法

当你声明一个方法为synchronized时,这个方法在同一时刻只能被一个线程访问。例如:

/**
@@ -128,6 +128,6 @@
     }
 }
 

在上述示例中,append方法内部使用了StringBuffer,它是一个线程安全的类。然而,由于在这个方法中没有其他线程访问,JVM会自动消除掉这里的同步锁。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2311081011.html b/note/java/jvm/2311081011.html index 1bf090fc..e811a48b 100644 --- a/note/java/jvm/2311081011.html +++ b/note/java/jvm/2311081011.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

运行时数据区详解(内存模型)

Yaien Blog原创大约 6 分钟JavaJVM

运行时数据区详解(内存模型)

JVM运行时数据区数Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,其中包含:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器

方法区(Method Area)

方法区是线程间共享的区域,在JVM启动时创建,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

方法区可以被实现成大小固定或可动态扩展和收缩,如果内存空间不满足内存分配要求就会抛出OutOfMemoryError异常。

对于HotSpot虚拟机而言,在JDK 1.8以前,方法区被实现为 “永久代”(Permanent Generation),属于堆的逻辑组成部分,并提供了两个参数调节其大小,-XX:PermSize用于设定初始容量,-XX:MaxPermSize用于设定最大容量。JDK 1.8之后,HotSpot不再有“永久代”的概念,类的元信息数据迁移到被称为“元空间”(Metaspace)的新区域,而静态变量、常量等则存储于堆中。元空间没有使用堆内存,而是分配在 本地内存(直接内存) 中,默认情况下其容量只受可用的本地内存大小限制。类似地,HotShot虚拟机也提供了两个参数来调节其大小, -XX:MetaspaceSize用于设定初始容量,-XX:MaxMetaspaceSize用于设定最大容量

运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量符号引用,这部分内容将在类加载之后进入到方法的运行常量池中存放。

字面值常量:

  • 字符串字面量
  • 用final修饰的基础类型成员变量的字面值
  • 由字面值常量相加得到的结果

直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,因此直接内存的分配不受Java堆大小的限制,但是还是会受到本机总内存(RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。

直接内存主要用于NIO类库,实现基于通道(Channel)和缓冲区(Buffer)的IO方式。通常,当需要处理大量数据的读写操作时,可以考虑使用直接内存,例如文件传输、网络编程等。

直接内存优点在于可以避免在Java堆和本地堆之间复制数据,提高IO操作的性能,缺点就是分配和释放代价较高,而且不受JVM垃圾回收的管理,容易造成内存溢出。

直接内存可以通过ByteBuffer类的allocateDirect方法来分配和操作,

堆内存(Heap)

堆内存是Java虚拟机中内存最大的一块,也是被所有线程共享的,在虚拟机启动时创建,Java对唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配。

虚拟机栈(Virtual Machine Stack)

虚拟机栈(线程栈)描述的是Java方法执行的内存模型,是线程私有的,它的生命周期与线程相同。当每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

栈帧在线程栈中属于先进后出(FILO),每个方法从调用知道执行完成的过程,都对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量表(LVT)

局部变量表是一个索引以0开始的字节数组,存储了一个方法的所有入参和局部变量。LVT所存储的类型都是编译期可知的,包括各基础类型(byte、char、short、int、long、float、double、boolean)、对象引用(reference类型)和returnAddress类型(指向一条字节码指令的地址)

LVT有如下特点:

  • 第0个Slot(槽位)固定存储指向方法所属对象的this指针
  • 除了long和double占用连续两个Slot之外,其余类型只占用一个Slot
  • LVT按照变量的声明顺序进行存储

操作数栈(OS)

操作数栈用于在方法运算过程存储其中间的运算结果、方法入参和返回结果,它是一个后进先出(Last-In-First-Out,LIFO)的队列。

JVM提供了对OS出栈和入栈的指令,如load指令属于入栈指令、store指令属于出栈指令。

动态链接

每个栈帧内都包含一个指向当前方法所属类的运行时常量池引用,也称为符号引用(Symbolic Reference),用于在类加载阶段对代码进行动态链接。动态链接所做的就是根据符号引用所表示名字,转换成对方法或变量的实际引用,从而实现运行时绑定(Late Binding)。

本地方法栈(Native Method Stack)

本地方法栈的作用与Java虚拟机栈类似,区别在于后者是为Java方法服务,而本地方法栈则为native方法服务。Java虚拟机规范没有对native方法机制及其实现语言做强制规定,如果JVM不提供native方法,则无需实现本地方法栈。

本地方法栈既可以被实现成固定大小,也可以实现成可动态地扩展和收缩,因此在特定的场景下也会抛出StackOverflowError异常和OutOfMemoryError异常。

程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2311081109.html b/note/java/jvm/2311081109.html index 68b2bf14..b96dfaa2 100644 --- a/note/java/jvm/2311081109.html +++ b/note/java/jvm/2311081109.html @@ -31,11 +31,11 @@ } - +
跳至主要內容

JVM指令手册

Yaien Blog原创大约 11 分钟JavaJVM

JVM指令手册

通过执行jvm的指令

javap -C <classPath>
 

就可以查看编写的java代码是如何一步步执行的,而相对应的含义就如下内容

栈和局部变量操作

将常量压入栈的指令

aconst_null 将null对象引用压入栈
iconst_m1 将int类型常量-1压入栈
iconst_0 将int类型常量0压入栈
iconst_1 将int类型常量1压入操作数栈
iconst_2 将int类型常量2压入栈
iconst_3 将int类型常量3压入栈
iconst_4 将int类型常量4压入栈
iconst_5 将int类型常量5压入栈
lconst_0 将long类型常量0压入栈
lconst_1 将long类型常量1压入栈
fconst_0 将float类型常量0压入栈
fconst_1 将float类型常量1压入栈
dconst_0 将double类型常量0压入栈
dconst_1 将double类型常量1压入栈
bipush 将一个8位带符号整数压入栈
sipush 将16位带符号整数压入栈
ldc 把常量池中的项压入栈
ldc_w 把常量池中的项压入栈(使用宽索引)
ldc2_w 把常量池中long类型或者double类型的项压入栈(使用宽索引)

从栈中的局部变量中装载值的指令

iload 从局部变量中装载int类型值
lload 从局部变量中装载long类型值
fload 从局部变量中装载float类型值
dload 从局部变量中装载double类型值
aload 从局部变量中装载引用类型值(refernce)
iload_0 从局部变量0中装载int类型值
iload_1 从局部变量1中装载int类型值
iload_2 从局部变量2中装载int类型值
iload_3 从局部变量3中装载int类型值
lload_0 从局部变量0中装载long类型值
lload_1 从局部变量1中装载long类型值
lload_2 从局部变量2中装载long类型值
lload_3 从局部变量3中装载long类型值
fload_0 从局部变量0中装载float类型值
fload_1 从局部变量1中装载float类型值
fload_2 从局部变量2中装载float类型值
fload_3 从局部变量3中装载float类型值
dload_0 从局部变量0中装载double类型值
dload_1 从局部变量1中装载double类型值
dload_2 从局部变量2中装载double类型值
dload_3 从局部变量3中装载double类型值
aload_0 从局部变量0中装载引用类型值
aload_1 从局部变量1中装载引用类型值
aload_2 从局部变量2中装载引用类型值
aload_3 从局部变量3中装载引用类型值
iaload 从数组中装载int类型值
laload 从数组中装载long类型值
faload 从数组中装载float类型值
daload 从数组中装载double类型值
aaload 从数组中装载引用类型值
baload 从数组中装载byte类型或boolean类型值
caload 从数组中装载char类型值
saload 从数组中装载short类型值

将栈中的值存入局部变量的指令

istore 将int类型值存入局部变量
lstore 将long类型值存入局部变量
fstore 将float类型值存入局部变量
dstore 将double类型值存入局部变量
astore 将将引用类型或returnAddress类型值存入局部变量
istore_0 将int类型值存入局部变量0
istore_1 将int类型值存入局部变量1
istore_2 将int类型值存入局部变量2
istore_3 将int类型值存入局部变量3
lstore_0 将long类型值存入局部变量0
lstore_1 将long类型值存入局部变量1
lstore_2 将long类型值存入局部变量2
lstore_3 将long类型值存入局部变量3
fstore_0 将float类型值存入局部变量0
fstore_1 将float类型值存入局部变量1
fstore_2 将float类型值存入局部变量2
fstore_3 将float类型值存入局部变量3
dstore_0 将double类型值存入局部变量0
dstore_1 将double类型值存入局部变量1
dstore_2 将double类型值存入局部变量2
dstore_3 将double类型值存入局部变量3
astore_0 将引用类型或returnAddress类型值存入局部变量0
astore_1 将引用类型或returnAddress类型值存入局部变量1
astore_2 将引用类型或returnAddress类型值存入局部变量2
astore_3 将引用类型或returnAddress类型值存入局部变量3
iastore 将int类型值存入数组中
lastore 将long类型值存入数组中
fastore 将float类型值存入数组中
dastore 将double类型值存入数组中
aastore 将引用类型值存入数组中
bastore 将byte类型或者boolean类型值存入数组中
castore 将char类型值存入数组中
sastore 将short类型值存入数组中

wide指令

wide 使用附加字节扩展局部变量索引

通用(无类型)栈操作

nop 不做任何操作
pop 弹出栈顶端一个字长的内容
pop2 弹出栈顶端两个字长的内容
dup 复制栈顶部一个字长内容
dup_x1 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的两个字长的内容压入栈
dup_x2 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
dup2 复制栈顶部两个字长内容
dup2_x1 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入栈
dup2_x2 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的四个字长的内容压入栈
swap 交换栈顶部两个字长内容

类型转换

i2l 把int类型的数据转化为long类型
i2f 把int类型的数据转化为float类型
i2d 把int类型的数据转化为double类型
l2i 把long类型的数据转化为int类型
l2f 把long类型的数据转化为float类型
l2d 把long类型的数据转化为double类型
f2i 把float类型的数据转化为int类型
f2l 把float类型的数据转化为long类型
f2d 把float类型的数据转化为double类型
d2i 把double类型的数据转化为int类型
d2l 把double类型的数据转化为long类型
d2f 把double类型的数据转化为float类型
i2b 把int类型的数据转化为byte类型
i2c 把int类型的数据转化为char类型
i2s 把int类型的数据转化为short类型

整数运算

iadd 执行int类型的加法
ladd 执行long类型的加法
isub 执行int类型的减法
lsub 执行long类型的减法
imul 执行int类型的乘法
lmul 执行long类型的乘法
idiv 执行int类型的除法
ldiv 执行long类型的除法
irem 计算int类型除法的余数
lrem 计算long类型除法的余数
ineg 对一个int类型值进行取反操作
lneg 对一个long类型值进行取反操作
iinc 把一个常量值加到一个int类型的局部变量上

逻辑运算

移位操作

ishl 执行int类型的向左移位操作
lshl 执行long类型的向左移位操作
ishr 执行int类型的向右移位操作
lshr 执行long类型的向右移位操作
iushr 执行int类型的向右逻辑移位操作
lushr 执行long类型的向右逻辑移位操作

按位布尔运算

iand 对int类型值进行“逻辑与”操作
land 对long类型值进行“逻辑与”操作
ior 对int类型值进行“逻辑或”操作
lor 对long类型值进行“逻辑或”操作
ixor 对int类型值进行“逻辑异或”操作
lxor 对long类型值进行“逻辑异或”操作

浮点运算

fadd 执行float类型的加法
dadd 执行double类型的加法
fsub 执行float类型的减法
dsub 执行double类型的减法
fmul 执行float类型的乘法
dmul 执行double类型的乘法
fdiv 执行float类型的除法
ddiv 执行double类型的除法
frem 计算float类型除法的余数
drem 计算double类型除法的余数
fneg 将一个float类型的数值取反
dneg 将一个double类型的数值取反

对象和数组

对象操作指令

new 创建一个新对象
checkcast 确定对象为所给定的类型
getfield 从对象中获取字段
putfield 设置对象中字段的值
getstatic 从类中获取静态字段
putstatic 设置类中静态字段的值
instanceof 判断对象是否为给定的类型

数组操作指令

newarray 分配数据成员类型为基本上数据类型的新数组
anewarray 分配数据成员类型为引用类型的新数组
arraylength 获取数组长度
multianewarray 分配新的多维数组

控制流

条件分支指令

ifeq 如果等于0,则跳转
ifne 如果不等于0,则跳转
iflt 如果小于0,则跳转
ifge 如果大于等于0,则跳转
ifgt 如果大于0,则跳转
ifle 如果小于等于0,则跳转
if_icmpcq 如果两个int值相等,则跳转
if_icmpne 如果两个int类型值不相等,则跳转
if_icmplt 如果一个int类型值小于另外一个int类型值,则跳转
f_icmpge 如果一个int类型值大于或者等于另外一个int类型值,则跳转
if_icmpgt 如果一个int类型值大于另外一个int类型值,则跳转
if_icmple 如果一个int类型值小于或者等于另外一个int类型值,则跳转
ifnull 如果等于null,则跳转
ifnonnull 如果不等于null,则跳转
if_acmpeq 如果两个对象引用相等,则跳转
if_acmpnc 如果两个对象引用不相等,则跳转

比较指令

lcmp 比较long类型值
fcmpl 比较float类型值(当遇到NaN时,返回-1)
fcmpg 比较float类型值(当遇到NaN时,返回1)
dcmpl 比较double类型值(当遇到NaN时,返回-1)
dcmpg 比较double类型值(当遇到NaN时,返回1)

无条件转移指令

goto 无条件跳转
goto_w 无条件跳转(宽索引)

表跳转指令

tableswitch 通过索引访问跳转表,并跳转
lookupswitch 通过键值匹配访问跳转表,并执行跳转操作

异常

athrow 抛出异常或错误
finally子句
jsr 跳转到子例程
jsr_w 跳转到子例程(宽索引)
rct 从子例程返回

方法调用与返回

方法调用指令

invokcvirtual 运行时按照对象的类来调用实例方法
invokespecial 根据编译时类型来调用实例方法
invokestatic 调用类(静态)方法
invokcinterface 调用接口方法

方法返回指令

ireturn 从方法中返回int类型的数据
lreturn 从方法中返回long类型的数据
freturn 从方法中返回float类型的数据
dreturn 从方法中返回double类型的数据
areturn 从方法中返回引用类型的数据
return 从方法中返回,返回值为void

线程同步

montiorenter 进入并获取对象监视器
monitorexit 释放并退出对象监视器

JVM指令助记符

变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_
操作数栈到变量:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_
常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_
加:iadd,ladd,fadd,dadd
减:isub,lsub,fsub,dsub
乘:imul,lmul,fmul,dmul
除:idiv,ldiv,fdiv,ddiv
余数:irem,lrem,frem,drem
取负:ineg,lneg,fneg,dneg
移位:ishl,lshr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位与:iand,land
按位异或:ixor,lxor
类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换)
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换)
创建类实便:new
创建新数组:newarray,anewarray,multianwarray
访问类的域和类实例域:getfield,putfield,getstatic,putstatic
把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload
从操作数栈存存储到数组:
bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
获取数组长度:arraylength
检相类实例或数组属性:instanceof,checkcast
操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
fcmpg,dcmpl,dcmpg
复合条件转移:tableswitch,lookupswitch
无条件转移:goto,goto_w,jsr,jsr_w,ret
调度对象的实便方法:invokevirtual
调用由接口实现的方法:invokeinterface
调用需要特殊处理的实例方法:invokespecial
调用命名类中的静态方法:invokestatic
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
异常:athrow
finally关键字的实现使用:jsr,jsr_w,ret

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2311091006.html b/note/java/jvm/2311091006.html index d4f26f42..143e0411 100644 --- a/note/java/jvm/2311091006.html +++ b/note/java/jvm/2311091006.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

JVM对象内存布局

Yaien Blog原创大约 4 分钟JavaJVM

JVM对象内存布局

Hotspot虚拟机中,将对象在内存中存储的布局分为三块:对象头(Header)实例数据(Instance Data)对齐填充(Padding)

对象内存布局
对象内存布局

对象头

HotSpot主要将对象头划分为:MarkWordKlassPointer数组长度,记录了对象Hash码、对象所属年代、对象锁、锁状态、偏向锁的线程ID、偏向时间、数组长度等等信息

MarkWord

MarkWord用于存储对象自身运行时的数据,例如哈希码(HashCode)、GC分代年龄、锁状态、持有锁的线程、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit。

  • 32位MarkWord
32位MarkWord
32位MarkWord
  • 64位MarkWord
64位MarkWord
64位MarkWord

MarkWord结构搞得那么复杂,是因为需要节省内存,让同一内存区域在不同锁阶段有不同的用处

  • hash:保存对象的哈希码,在运行期间调用System.identityHashCode()进行计算,这是一个延迟计算,并将结果赋值到hash内存中
  • age:保存对象的分代年龄。记录对象被GC的次数,当该次数到达阈值后就会由年轻代转入老年代
  • biased_lock:偏向锁标识位。由于无锁和偏向锁的锁标识都是记01,为了区分引入了一位来标识是否为偏向锁
  • lock:锁状态标识。区分锁状态,比如00时表示轻量锁,只有最后2位锁标识(00)有效
  • JavaThread:保存持有偏向锁的线程ID。当处于偏行模式时,有线程持有对象,则对象的这里就会保存持有线程的ID,后续再获取锁时就无需再进行尝试获取锁的动作
  • epoch:保存偏向时间戳。偏向锁再CAS锁操作过程中,偏向性标识,标识对象更偏向哪个锁

KlassPointer

KlassPointer又称类型指针,是指向对象的类元数据的指针。虚拟机可以通过这个指针来确定这个对象是那个类的实例。

在32位的JVM内,类型指针占4byte
在64位的JVM内,类型指针正常占8byte,若开启指针压缩或者最大堆内存小于32G时占4byte。JDK8后默认开启指针压缩

进行指针压缩的原因:

  • 在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据会占用较大宽带,同时GC也会承受较大压力
  • JVM中32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得JVM只用32位地址就可以支持更大的内存配置(小于等于32G)

需要注意的是:

  • 堆内存小于4G时不需要开启指针压缩,JVM会直接去除32位地址,即使用低虚拟地址空间
  • 堆内存大于32G时,压缩指针会失效,会强制使用64(8字节)来对Java对象进行寻址,所以堆内存最好不要大于32G。

数组长度

如果这个对象是一个数组,则对象头中会有一块4byte长度数数据区用于记录数组的长度,如果不是数组则这部分长度为0

Header内存占用
Header内存占用

内存布局查看实战

为了验证对象内存布局,可使用Java对象的内部布局工具JOL(JAVA OBJECT LAYOUT)
,用此工具可以查看new出来的一个java对象的内部布局,以及一个普通的java对象占用多少字节。

Maven依赖引入

<!--  JAVA对象布局、大小查看  -->
@@ -59,6 +59,6 @@
 Instance size: 16 bytes
 Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
 
  • OFF:偏移地址(Byte)
  • SZ:内存占用大小(Byte)
  • TYPE DESCRIPTION:类型描述,object header为对象头,object alignment gap为对齐补充
  • VALUE:对应内存中当前存储的值
上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2311221518.html b/note/java/jvm/2311221518.html index 4ae2578e..ac74f089 100644 --- a/note/java/jvm/2311221518.html +++ b/note/java/jvm/2311221518.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

JIT(即时编译)深入理解

Yaien Blog原创大约 7 分钟JavaJVM

JIT(即时编译)深入理解

Java是一门解释型语言,通过编译器(javac)将源代码编译成平台无关的Java字节码文件(.class)。然后JVM解释执行这些字节码文件,实现平台无关性。
但是,解释执行的速度相对较慢。为了提高执行速度,引入了JIT技术。JIT是JUST IN TIME的缩写,意味着即时编译

热点探测技术

当虚拟机发现某个方法或代码块运行特别频繁时,就会将这些代码标记为热点代码(Hot Spot Code)。热点探测的目的是识别出这些热点代码,以便进行即时编译(JIT)优化。

这种探测方法周期性地检查各个线程的虚拟机栈的栈顶。如果某个方法经常出现在栈顶,就认为这个方法是热点方法。基于采样的热点探测是一种常见的热点代码判定方式。

对于热点方法,JIT会触发标准编译,将其编译成机器码,以提高执行效率。对于循环体,JIT可能触发OSR(On-Stack
Replacement)栈上替换,直接切换到本地代码执行。

热点探测技术的触发,只需要满足以下一个条件即可:

  • 方法调用计数器
    • 方法计数器用于记录方法被调用的次数。当某个方法被多次调用时,方法计数器的值会逐渐增加
    • 在Client模式下,默认的方法计数器阈值是1500次。
    • 在Server模式下,默认的方法计数器阈值是10000次。
  • 回边计数器
    • 回边计数器用于判断循环是否热点。当循环体被多次执行时,回边计数器的值会增加。
    • 在Client模式下,回边计数器的阈值计算公式为:OSRP = 方法调用计数器阈值 * 933 / 100。
    • 在Server模式下,回边计数器的阈值计算公式为:OSRP = (方法调用计数器阈值 * 140 - 监控解释器比率) / 100。
public class HotSpotDetectionExample {
@@ -93,6 +93,6 @@
     }
 }
 

在这个示例中,每次调用 stringBuffer.append 方法都需要加锁和解锁,如果JVM检测到有一连串的对同一个对象加锁和解锁的操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append 方法时进行加锁,最后一次 append 方法结束后进行解锁。这样就避免了频繁用户态到内核态的状态转换,从而提高了性能。

逃逸分析技术

逃逸分析是编译程序优化理论中的一种方法,用于确定指针动态范围,即分析在程序的哪些地方可以访问到指针。当一个变量(或者对象)在方法中被分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸。

逃逸分析的主要作用是帮助Java Hotspot编译器分析出一个新的对象的引用的使用范围,从而决定是否需要将这个对象分配到堆上。逃逸分析可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。

逃逸分析的优化技术包括:

  • 栈上分配: 如果一个对象不会在方法体内,或线程内发生逃逸,那么可以使用栈上的空间,那么大量的对象将随方法的结束而销毁,减轻了GC压力2。
  • 同步消除: 如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行2。
  • 标量替换: 如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替2。

逃逸的方式主要有两种:

  • 方法逃逸: 在一个方法体内,定义一个局部变量,而它可能被外部方法引用,比如作为调用参数传递给方法,或作为对象直接返回。
  • 线程逃逸: 这个对象被其他线程访问到,比如赋值给了实例变量,并被其他线程访问到了。
上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2403021055.html b/note/java/jvm/2403021055.html index 18eb05d5..60656390 100644 --- a/note/java/jvm/2403021055.html +++ b/note/java/jvm/2403021055.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

对象创建与内存分配

Yaien Blog原创大约 4 分钟JavaJVM

对象创建与内存分配

当我们在Java中创建一个对象时,JVM会执行一系列步骤来完成对象的创建和初始化。本文则记录对象在JVM中完整的创建流程。

当我们去new一个对象的时候,完整的流程如下:类加载检查、内存分配、初始化、设置对象头、执行init()

类加载检查

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等。

内存分配

类加载完成之后,会为新对象进行内存分配。对象所需的内存大小在类加载完成之后便可以确定。此时为对象分配内存空间等于把一块确定大小的内存从JVM堆中划分出来给对象使用。

在进行堆内存划分的时候会存在两个问题:如何划分问题和并发问题

内存划分方式

为新对象在堆中划分内存,划分方式有两种:指针碰撞(默认使用)和空闲列表

指针碰撞(Bump the Pointer)

通过一个指针指向分界点,这个指针表示已分配未分配内存的分界。当需要分配内存时,将指针往空闲一侧移动与对象大小相等的距离即可。

需要注意的是这需要Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边。主要用于Serial和ParNew等不会产生内存碎片的垃圾收集器。

空闲列表(Free List)

JVM维护一张内存列表,记录可用的内存块信息。当分配内存时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。

空闲列表即使是Java堆中的内存并不规整,已分配内存和未分配内存交错也是适用的。最常见使用此方案的垃圾收集器是CMS(Concurrent
Mark-Sweep)

并发问题处理

在并发情况对象在内存划分时,可能会出现正在给A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况,导致数据不一致或内存分配错误。

JVM采用以下方法来解决并发问题:CMS和本地线程分配缓冲(TLAB)

CMS(Compare and Swap)

虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。

本地线程分配缓冲(Thread Local Allocation Buffer)

为每个线程在Java堆中预先分配一小块内存,哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

虚拟机是否使用TLAB,可以通过 -XX:+/-UseTLAB(默认开启) 参数来设定。

栈上分配

通常Java对象都是在堆上分配内存,当对象没有被引用时,光依靠GC进行回收内存,对象创建过多就会给GC带来较大的压力,间接影响应用的性能。

为了减少临时对象在堆内存分配的数量,JVM通过逃逸分析技术确定对象不会被外部访问,则说明对象不会逃逸出改栈帧,可以将对象在栈上分配内存,这样对象占用的内存就会随着栈帧出栈而销毁,达到减轻GC回收的压力。

逃逸分析技术还需要结合热点探测技术,以此为前提,详情可在 JIT(即时编译)深入理解 文章查看。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2403031236.html b/note/java/jvm/2403031236.html index 276a76e7..1d68329e 100644 --- a/note/java/jvm/2403031236.html +++ b/note/java/jvm/2403031236.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

垃圾收集器及原理

Yaien Blog原创大约 4 分钟JavaJVMGC

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器

GC算法

  • 分代收集算法: 这种算法根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代。新生代中的对象一般存活时间较短,所以选择复制算法,而老年代中的对象存活时间长,空间大,所以选择标记-清理或者标记-整理算法。
  • 复制算法: 这种算法将内存分为两块,每次只使用其中一块。当这一块内存用完后,就将还存活的对象复制到另一块上面,然后再把已使用的内存空间一次清理掉。这种算法的主要缺点是内存利用率只有50%。
  • 标记-清除算法: 这种算法首先标记出所有需要回收的对象,然后进行清除。这种算法的主要缺点是效率不高,并且清除后会产生大量的内存碎片。
  • 标记-整理算法: 这种算法是在标记-清除算法的基础上进行的改进,标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

Serial收集器

Serial收集器是Java虚拟机(JVM)中最基本,也是历史最悠久的垃圾收集器。此收集器是一个单线程的收集器,它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束,这种机制被称为Stop-The-World(STW)

Serial收集器的优点在于是单线程运行,没有线程交互的开销,对内存的消耗小,可以专心做垃圾收集,因此可以获得很高的单线程收集效益。缺点就是在垃圾收集过程中其他CPU资源被闲置,必须暂停其他所有的工作线程(STW),这会影响到应用程序的响应性能。

Serial收集器
Serial收集器

Serial Old收集器被用作Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:

  • 在JDK1.5版本及以前的Parallel Scavenge收集器搭配使用
  • 作为CMS收集器的后备方案

Serial收集器主要用于新生代的垃圾回收,采用的是复制算法。 Serial Old收集器使用的是标记-整理算法

Parallel收集器

Parallel收集器是Serial收集器的多线程版本,除了使用多线程进行垃圾收集以外,其行为(参数控制、收集算法、回收策略等)和Serial相似。

Parallel收集器主要用于多核处理器的服务器中,旨在提高垃圾收集的吞吐量(高效的利用CPU),所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总耗时的比值。在注重吞吐量以及CPU资源的场合,都可以优先考虑Parallel收集器配合Parallel Old收集器来进行垃圾收集(JDK1.8默认垃圾收集组合)

Parallel收集器
Parallel收集器

Parallel Old收集器是Parallel收集器的老年代版本,同样是使用多线程来进行垃圾收集。Parallel采用的是复制算法。 Parallel Old收集器使用的是标记-整理算法

ParNew收集器

ParNew垃圾收集器是一种用于新生代的并行垃圾回收器,与Parallel收集器类似,都是并行收集器。它的名字中的 “Par” 是 “Parallel” 的缩写,表示并行,而 “New” 表示它主要处理新生代的垃圾回收。

ParNew收集器
ParNew收集器

ParNew收集器的诞生主要是为了配合CMS(Concurrent Mark-Sweep)垃圾回收器一起使用,用于处理新生代的垃圾回收。这是因为在新生代中,回收次数频繁,使用并行方式更高效。此外,目前只有Serial和ParNew可以与CMS进行配合垃圾收集

上次编辑于:
贡献者: yanggl
- + diff --git a/note/java/jvm/2403031505.html b/note/java/jvm/2403031505.html index dde25488..a5bcab27 100644 --- a/note/java/jvm/2403031505.html +++ b/note/java/jvm/2403031505.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/jvm/2403032005.html b/note/java/jvm/2403032005.html index 25c7690c..961b2c87 100644 --- a/note/java/jvm/2403032005.html +++ b/note/java/jvm/2403032005.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/java/jvm/index.html b/note/java/jvm/index.html index 6d18aa59..49117a17 100644 --- a/note/java/jvm/index.html +++ b/note/java/jvm/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/microservices/dubbo/2305140023.html b/note/microservices/dubbo/2305140023.html index 31c10380..d4b85d00 100644 --- a/note/microservices/dubbo/2305140023.html +++ b/note/microservices/dubbo/2305140023.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Dubbo 基本介绍

Yaien Blog原创大约 3 分钟微服务RPCdubbo

Dubbo 基本介绍

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

Dubbo一开始的定位就是RPC,专注于两个服务之间的调用。但随着微服务的盛行,除开服务调用之外,Dubbo也在逐步的涉猎服务治理、服务监控、服务网关等等,所以现在的Dubbo目标已经不止是RPC框架了,而是和Spring Cloud类似想成为了一个服务框架

核心组件

服务提供者(Provider)

作为服务的提供者,将自己的服务注册到注册中心,接收消费者的调用请求并提供相应的服务实现。提供者通常是一个独立的应用程序,它将自己的服务暴露给消费者。

服务消费者(Consumer)

从注册中心获取服务提供者的地址,并发起远程调用请求

注册中心(Register)

用于服务的注册与发现,提供服务地址的管理

监控中心(Monitor)

实时监控和统计分析服务的运行情况和性能指标,帮助用户更好地管理和优化分布式服务,提高系统的可靠性和可用性

基本原理

基本原理
基本原理

架构

从抽象架构上分为两层:服务治理抽象控制面 和 Dubbo 数据面

架构
架构

服务治理控制面

服务治理控制面不是特指如注册中心类的单个具体组件,而是对 Dubbo 治理体系的抽象表达。控制面包含协调服务发现的注册中心、流量管控策略、Dubbo Admin 控制台等,如果采用了 Service Mesh 架构则还包含 Istio
等服务网格控制面。

Dubbo数据面

数据面代表集群部署的所有 Dubbo 进程,进程之间通过 RPC 协议实现数据交换,Dubbo 定义了微服务应用开发与调用规范并负责完成数据传输的编解码工作

  • 服务消费者 (Dubbo Consumer),发起业务调用或 RPC 通信的 Dubbo 进程
  • 服务提供者 (Dubbo Provider),接收业务调用或 RPC 通信的 Dubbo 进程

通信协议

Dubbo 从设计上不绑定任何一款特定通信协议,HTTP/2、REST、gRPC、JsonRPC、Thrift、Hessian2 等几乎所有主流的通信协议,Dubbo 框架都可以提供支持。

这样的 Protocol 设计模式给构建微服务带来了最大的灵活性,开发者可以根据需要如性能、通用型等选择不同的通信协议,不再需要任何的代理来实现协议转换,甚至你还可以通过 Dubbo 实现不同协议间的迁移。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/dubbo/2305140203.html b/note/microservices/dubbo/2305140203.html index 3748a9d2..8e90848e 100644 --- a/note/microservices/dubbo/2305140203.html +++ b/note/microservices/dubbo/2305140203.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/microservices/dubbo/2305140206.html b/note/microservices/dubbo/2305140206.html index 75208b89..225f5f22 100644 --- a/note/microservices/dubbo/2305140206.html +++ b/note/microservices/dubbo/2305140206.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

SPI扩展及其源码

Yaien Blog原创大约 7 分钟微服务RPCdubbo

SPI扩展及其源码

dubbo的扩展点实现与JAVA中实现的扩展点类似,但是功能比JAVA提供的会强大一些。 本次文章简单记录Dubbo SPI扩展点的使用以及核心原理,通过对源码的解读学习dubbo spi扩展点的核心实现以及内部的使用。

SPI 扩展点的实现,都是基于接口来实现的,所以扩展点都是通过实现某个接口来进行扩展的。

核心对象(ExtensionLoader)

ExtensionLoader表示某个接口的扩展点加载器,可以用来加载某个扩展点实例。

核心属性

  • extensionInstances:用来缓存某个接口类型所对应的ExtensionLoader实例
  • type:表示当前ExtensionLoader实例是那个接口的扩展点加载器
  • objectFactory:扩展点工厂,可以通过工厂获取某个扩展点的具体对象实例

使用

  • 新建maven工程,并引入dubbo相关依赖
  • 在resource包下创建子包:MEAT-INF.dubbo
  • 在dubbo包下创建扩展点文件,文件名为扩展点的全类名(包含包名和接口名)
  • 在文件内填写扩展点实现类的全类名以及映射关系

当以上步骤都完成以后,在启动类main方法中通过dubbo的扩展点加载器来加载扩展点

    // 获取加载扩展点加载器
@@ -257,6 +257,6 @@
         return instance;
     }
 
  • 便利所有实例的方法,跳过不是set方法、方法上有@DisableInject、实现ScopeModelAware接口的方法
  • 获取set方法的第一个参数类型,如果是基本数据类型则跳过
  • 截取set方法名(setCar -> car)
  • 通过参数类型和名称创建实例
    • 通过Spring容器获取
    • dubbo自己去生成一个代理类(方法上要加@Adaptive,否则调用方法的时候会报错)

包装器(AOP)

  • 获取文件解析时存的所有包装器,降序排序,然后再颠倒成升序
  • 便利所有的包装器
  • 判断,需要生成实例的条件如下
    • 类上没有@Wrapper
    • 有@Wrapper注解
      • matches为空或者matches包含扩展点名称
      • 并且mismatches不包含扩展点名称
  • 找包装类上有以扩展类类型为参数的构造方法,通过该构造方法创建包装类
  • 传入包装类和扩展类类名,调用初始化后后置处理器
上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/dubbo/2305172157.html b/note/microservices/dubbo/2305172157.html index 7a102677..405eb2db 100644 --- a/note/microservices/dubbo/2305172157.html +++ b/note/microservices/dubbo/2305172157.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Dubbo 整合 Spring

Yaien Blog原创大约 5 分钟微服务RPCdubbo

Dubbo 整合 Spring

本文主要记录学习Dubbo 整合 Spring 的源码笔记

启动类

public class Application {
@@ -105,6 +105,6 @@
 
     }
 

以上两个类,核心注解是**@EnableDubboConfigBindings**,注解中会通过@Import引入DubboConfigBindingsRegistrar类来解析
@EnableDubboConfigBinding然后结合读取到的dubbo配置文件内容,注册成Bean对象

解析properties

实现流程

  • 获取@EnableDubboConfigBinding列表,使用一个AnnotationAttributes对象集合接收,然后进行遍历
  • 获取AnnotationAttributes的prefix和type属性的值
  • 通过spring提供的environment,获取properties文件中以上一步prefix中的值为前缀的Map数据
  • 判断是否开了multiple多协议支持,然后调用对应的方法生成BeanName的Set集合
    • 开启多协议,调用resolveMultipleBeanNamesS
      • 遍历map的key,截取kay生成beanName,添加到集合中返回:(dubbo.protocols.p1.name=dubbo,则beanName=p1)
    • 没开启,调用resolveSingleBeanName
      • 配置了dubbo.application.id=appl,那么appl就是beanName
      • 没有则有spring自动生成一个beanName并返回
  • 遍历所有的beanName,为每一个beanName注册一个空的BeanDefinition以及DubboConfigBindingBeanPostProcessor的Bean工厂的后置处理器(有问题)
  • 注册一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean,用来把某个XxConfig所对应的beanName设置到name属性中去

流程图

@DubboComponentScan

@DubboComponentScan通过Spring的 @Import 注入了 DubboComponentScanRegistrar
的Bean,当spring启动时会调用registerBeanDefinitions方法,方法内主要是向Spring容器中注册两个Bean:

  • ServiceAnnotationBeanPostProcessor
  • ReferenceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,是用来注册BeanDefinition的。

它的主要作用是扫描Dubbo的@Service注解,一旦扫描到某个@Service注解就把它以及被它注解的类当做一个Dubbo服务,进行服务导出。

实现流程

  • spring启动
  • 创建DubboClassPathBeanDefinitionScanner的bean,用来扫描Dubbo自定义的@Service注解
  • 扫描有@Service注解的类,并生成对应的BeanDefinition
  • 查找被@Service注解的类的BeanDefinition(无论这个类有没有被@ComponentScan注解标注了)
  • 遍历BeanDefinition进行处理
    • 获取@Service标注的实现类、@Service对应的Annotation对象、@Service属性信息、@Service实现类对应的接口、实现类的name
    • 调用buildServiceBeanDefinition生成一个ServiceBean
      • 生成一个ServiceBean.class对应的BeanDefinition
      • 将@Service对应的Annotation对象中的属性赋值到ServiceBean中
      • 将实现类的name赋值到ServiceBean中的ref属性中,关联实现类
      • ...进行属性赋值
    • 生成ServiceBean的名称并进行查重,ServiceBean名称为ServiceBean:cn.yaien.CatService
    • 注册ServiceBean到Spring容器中
  • ServiceBean中监听spring启动完成事件,进行服务导出(服务注册)

服务导出还会处理很多的事情,不在这里继续深入。服务导出笔记可点击前往查看

流程图

ReferenceAnnotationBeanPostProcessor

ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。其父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是一个BeanPostProcessor。

Spring在对Bean进行依赖注入时会调用AnnotationInjectedBeanPostProcessor的postProcessPropertyValues()
方法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻辑进行依赖注入。 在注入之前会查找注入点,被@Reference注解的属性或方法都是注入点。

实现流程

  • spring启动
  • 往spring注册一个ReferenceAnnotationBeanPostProcessor的后置处理器
  • 调用AnnotationInjectedBeanPostProcessor.postProcessPropertyValues方法寻找需要注入的属性(被@Reference标注的Field)
  • 调用doGetInjectedBean方法生成@Reference标注对象的一个代理对象
    • 生成ServiceBean的名称referencedBeanName,用做查询是否有本地服务
    • 通过@Reference注解信息生成referenceBeanName,用做缓存的key,value是对应服务的一个ReferenceBean
    • 查询缓存有没有ReferenceBean,没有就创建一个,将@Reference注解信息赋值到configBean属性中
    • 判断本地spring容器有没有指定的ServiceBean(通过referencedBeanName在spring中找对应的ServiceBean)
      • 如果有,生成一个代理对象(不直接用容器中的bean而是生成代理对象,是考虑到除对应方法需要执行外,还会有很多dubbo逻辑需要处理)
      • 如果没有,将生成的ReferenceBean注入到spring容器

流程图

上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/dubbo/2305191622.html b/note/microservices/dubbo/2305191622.html index f6f4e81a..2295db9c 100644 --- a/note/microservices/dubbo/2305191622.html +++ b/note/microservices/dubbo/2305191622.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

服务导出(服务注册)

Yaien Blog原创大约 6 分钟微服务RPCdubbo

服务导出(服务注册)

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。

服务导出(服务注册)的核心方法是ServiceBean对象中监听器里的export();调用时机是在Spring容器启动完成之后发布的完成事件,
ServiceBean通过监听该事件然后去进行服务导出(服务注册)。

    @Override
@@ -89,6 +89,6 @@
     }
 

export

校验是否执行服务导出,通过属性可进行配置,默认true

delay

校验是否延迟导出,通过delay属性可进行配置,默认0不延迟(毫秒)

doExport()

调用doExport方法进行服务导出

loadRegistries()

loadRegistries方法主要做的事情就是获取配置用户配置的注册中心,最终会返回一个URL列表,一个URL就代表一个注册中心地址,URL的内容如下:

registry://1.12.242.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&logger=log4j&pid=93947&registry=zookeeper&release=2.7.0&timeout=3000&timestamp=1684854503882
 

此时URL并没有标识是使用zookeeper或者nacos,而是先试用registry进行标识

doExportUrlsFor1Protocol()

这个方法主要做的事情就是为每一个协议,都导出一个服务。例如提供者提供了一个UserService服务,但是我配置了两个协议,那此时就会生成两个UserService服务地址,并且都会注册到所有的注册中心上。

具体实现流程

  • 获取协议名称,没有默认就是dubbo
  • 构建一个map,用来存服务url的参数,并往map中填值
  • 构建Token,Token是为了防止服务被消费者直接调用(伪造http请求)
    • token生成规则:如果没有配则没有,配置true或者default自动通过uuid生成,否者使用配置的字符做token
  • 构建服务URL
  • 生成一个当前服务接口的代理对象,使用代理对象生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
  • 封装DelegateProviderMetaDataInvoker,包括了Invoker和服务的配置
  • 导出服务&服务注册
    • 从invoker种获取注册中心URL(registerUrl)和服务提供者URL(providerUrl)
    • 在providerUrl的基础上生成overrideSubscribeUrl,这个是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators,
      这是老版本上的动态配置)s
    • 创建OverrideListener监听器,用来监听overrideSubscribeUrl配置变更(老版本)
    • 添加两个监听器(新版本的监听器)
      • providerConfigurationListener:表示应用级别的动态配置监听器,属于RegistyProtocol的一个属性
      • serviceConfigurationListener:表示服务级别的动态配置监听器,每暴露一个服务时就会生成一个
    • 通过两个监听器,重写providerUrl
    • 使用重写过后的providerUrl,调用doLocalExport()进行服务导出
    • 获取注册中心(通过SPI扩展点机制)
    • 简化服务URL并注册到注册中心

doLocalExport()

此方法就是真正去执行服务导出的逻辑,里面会调用protocol.export(invokerDelegate)去导出服务

具体实现

  • 获取服务URL
  • 通过URL生成一个key
  • 构造一个Exporter进行服务导出
  • 存到exporterMap中
  • 调用openServer启动Netty/Tomcat

服务提供者监听配置更新

服务提供者主要监听动态配置,在服务运行是时可以动态修改服务配置,会监听三个节点,一个是老版本的节点以及两个新节点

补充

当dubbo的某一个服务导出完了之后,会发布一个Spring的时间,如果想知道某个服务是否已经导出完毕,可以监听ServiceBeanExportedEvent实现。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/dubbo/2305232045.html b/note/microservices/dubbo/2305232045.html index 0fc04750..022ce5c7 100644 --- a/note/microservices/dubbo/2305232045.html +++ b/note/microservices/dubbo/2305232045.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

服务发现

Yaien Blog原创小于 1 分钟微服务RPCdubbo

服务发现

上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/dubbo/index.html b/note/microservices/dubbo/index.html index a776972b..f1967f64 100644 --- a/note/microservices/dubbo/index.html +++ b/note/microservices/dubbo/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/microservices/index.html b/note/microservices/index.html index dd8127e2..19616faa 100644 --- a/note/microservices/index.html +++ b/note/microservices/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/microservices/zookeeper/2305110857.html b/note/microservices/zookeeper/2305110857.html index 6f7e1c36..08b28b04 100644 --- a/note/microservices/zookeeper/2305110857.html +++ b/note/microservices/zookeeper/2305110857.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

zookeeper基本使用

Yaien Blog原创大约 10 分钟微服务zookeeper

zookeeper基本使用

基本概念

通常情况下,单个物理节点很容易达到性能,计算或者容量的瓶颈,所以这个时候就需要多个物理节点来共同完成某项任务,一个分布式系统的本质是分布在不同网络或计算机上的程序组件,彼此通过信息传递来协同工作的系统,而Zookeeper正是一个分布式应用协调框架,在分布式系统架构中有广泛的应用场景。

官方文档解释:zookeeper它是一个分布式协调框架,是ApacheHadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等

核心概念

一个基于内存,用于存储少量数据的数据库。核心概念:文件系统存储结构、监听通知机制

文件系统存储结构

Zookeeper维护一个了类似文件系统的数据结构,每个子目录项都被称作为znode (目录节点),和文件系统类似,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode。

zookeeper有以下几种类型的znode:

1、PERSISTENT­持久化目录节点客户端与zookeeper断开连接后,该节点依旧存在,只要不手动删除该节点,他将永远存在

2、PERSISTENT_SEQUENTIAL­持久化顺序编号目录节点客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3、EPHEMERAL­临时目录节点客户端与zookeeper断开连接后,该节点被删除

4、EPHEMERAL_SEQUENTIAL­临时顺序编号目录节点客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

5、Container节点(3.5.3版本新增,如果Container节点下面没有子节点,则Container节点在未来会被Zookeeper自动清除,定时任务默认60s检查一次)

6、TTL节点(默认禁用,只能通过系统配置zookeeper.extendedTypesEnabled=true开启,不稳定

监听通知机制

客户端注册监听它关心的任意节点,或者目录节点及递归子目录节点。

1.如果注册的是对某个节点的监听,则当这个节点被删除,或者被修改时,对应的客户端将被通知。

2.如果注册的是对某个目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对应的客户端将被通知。

3.如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有目录结构的变化(有子节点被创建,或被删除)或者根节点有数据变化时,对应的客户端将被通知。

注意:所有的通知都是 一次性 的,及无论是对节点还是对目录进行的监听,一旦触发,对应的监听即被移除。递归子节点,监听是对所有子节点的,所以,每个子节点下面的事件同样只会被 触发一次

应用场景

  1. 分布式配置中心
  2. 分布式注册中心
  3. 分布式锁
  4. 分布式队列
  5. 分布式屏障
  6. 集群选举
  7. 发布/订阅

安装

Step1:配置JAVA环境,并检查环境(建议JDK1.8)以上版本

java -version
@@ -214,6 +214,6 @@
 // 访问数据
 get /node-1
 

注意:创建时授予权限时,节点数据不能为空

上次编辑于:
贡献者: yanggl
- + diff --git a/note/microservices/zookeeper/index.html b/note/microservices/zookeeper/index.html index 1f71d471..a303a0d9 100644 --- a/note/microservices/zookeeper/index.html +++ b/note/microservices/zookeeper/index.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

Zookeeper

Yaien Blog小于 1 分钟

- + diff --git a/note/net/basic/2401171030.html b/note/net/basic/2401171030.html index 24d34d9e..14698c4a 100644 --- a/note/net/basic/2401171030.html +++ b/note/net/basic/2401171030.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

深入理解网络通信与TCP/IP协议

Yaien Blog原创小于 1 分钟TCPIP网络编程

深入理解网络通信与TCP/IP协议

上次编辑于:
贡献者: yanggl
- + diff --git a/note/net/basic/index.html b/note/net/basic/index.html index bc2a4661..b57a5f34 100644 --- a/note/net/basic/index.html +++ b/note/net/basic/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/net/index.html b/note/net/index.html index d140813a..9245252c 100644 --- a/note/net/index.html +++ b/note/net/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/other/2302201400.html b/note/other/2302201400.html index 96a7c886..6f229f64 100644 --- a/note/other/2302201400.html +++ b/note/other/2302201400.html @@ -31,7 +31,7 @@ } - +
跳至主要內容
多边形等距离外扩

多边形等距离外扩

Yaien Blog原创大约 3 分钟算法地图多边形

多边形等距离外扩

实现多边形形成的多边形进行等距外扩或收缩的算法实现

涉及技术

  • 腾讯地图
  • jdk1.8

问题描述

开发一款应用,前端在地图上标记经纬度坐标点集合形成一个多边形,现需要在后端将多边形做一个等距外扩或者收缩。

在网上查了好多的算法,经过尝试验证,在小经度多边形进行收缩外扩都或多或少存在一些问题,因此自己参考写出了

一个多边形外扩收缩的算法。

JAVA代码实现

坐标点实体(Point)

/**
@@ -256,6 +256,6 @@
 
 }
 

经测试,对于小经度多边形的外扩是能够正常的。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2302201401.html b/note/other/2302201401.html index 35f26dbc..18a5f267 100644 --- a/note/other/2302201401.html +++ b/note/other/2302201401.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

微信服务号推送服务模板消息

Yaien Blog原创大约 2 分钟微信微信服务号

微信服务号推送服务模板消息

记录通过微信服务号推送服务模版消息的实现

业务需求

项目中存在金额待支付,需要实时提醒微信用户;小程序的服务推送是一次性的,且需要用户点击授权才能推送,授权一次可发送一条,不符合业务需求;在查看官方文档后发现服务号的模板推送是可以实现的,具体可查看 接口实现open in new window
以及 模板消息运营规范open in new window

认证的服务号

要使用模板功能,该服务号必须是认证的,且接收的对象必须关注此服务号,否则无法推送

服务号中添加模板

在添加模板之前,需要开通模板消息接口服务,可使用的接口以及限制可在最地下的接口权限查看。

获取access_token

发送模板,需要用到 access_tokenopen in new window
,access_token 的获取需要用到 服务号的 appid 及开发者密码 secret,根据文档调用 GET 接口即可获取到;

也可以前往 服务号接口调试open in new window,填写对应信息获取测试token

在使用是可能会报错 ip 无效,将该ip添加到白名单中即可

调用后台代码,发送模板消息

    @Test
@@ -91,6 +91,6 @@
         return jsonObject;
     }
 

此时,我们就可以在微信上收到一条服务通知

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2302201404.html b/note/other/2302201404.html index 44c74a23..f1ca8417 100644 --- a/note/other/2302201404.html +++ b/note/other/2302201404.html @@ -31,7 +31,7 @@ } - + - + diff --git a/note/other/2305081011.html b/note/other/2305081011.html index 2a855544..e3600bec 100644 --- a/note/other/2305081011.html +++ b/note/other/2305081011.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Docker安装

Yaien Blog原创大约 4 分钟dockerdocker

Docker安装

本文记录Docker容器安装的详细步骤

Docker从17.03版本之后分为CE(Community Edition: 社区版)和EE(Enterprise Edition: 企业版)。相对于社区版本,企业版本强调安全性,但需付费使用。这里我们使用社区版本即可。

Docker支持64位版本的CentOS 7和CentOS 8及更高版本,它要求Linux内核版本不低于3.10。

查看Linux版本的命令这里推荐两种:lsb_release -acat /etc/redhat-release

lsb_release -a查看效果:

[ ~]$ lsb_release -a
@@ -77,6 +77,6 @@
 

除了启动Docker,一些其他启动相关的命令:

  • 守护进程重启:systemctl daemon-reload
  • 重启Docker服务:systemctl restart docker / service docker restart
  • 关闭Docker服务:docker service docker stop / docker systemctl stop docker

删除Docker

删除安装包:

yum remove docker-ce
 

删除镜像、容器open in new window、配置文件等内容:

rm -rf /var/lib/docker
 

Docker其他常见命令

安装完成Docker之后,这里汇总列一下常见的Docker操作命令:

  • 搜索仓库镜像:docker search 镜像名
  • 拉取镜像:docker pull 镜像名
  • 查看正在运行的容器:docker ps
  • 查看所有容器:docker ps -a
  • 删除容器:docker rm container_id
  • 查看镜像:docker images
  • 删除镜像:docker rmi image_id
  • 启动(停止的)容器:docker start 容器ID
  • 停止容器:docker stop 容器ID
  • 重启容器:docker restart 容器ID
  • 启动(新)容器:docker run -it ubuntu /bin/bash
  • 进入容器:docker attach 容器IDdocker exec -it 容器ID /bin/bash,推荐使用后者。

更多的命令可以通过docker help命令来查看。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2305081101.html b/note/other/2305081101.html index fca5e26c..a6345684 100644 --- a/note/other/2305081101.html +++ b/note/other/2305081101.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Docker 安装 Mysql5.7

Yaien Blog原创大约 2 分钟dockerdocker

Docker 安装 Mysql5.7

本文记录Docker容器安装Mysql5.7的详细步骤

创建并启动容器

下载Mysql镜像

# 默认下载MySQL5.7最新版本(其他版本可以指定比如 docker pull mysql:5.7.34)
@@ -73,6 +73,6 @@
 flush privileges;
 

退出,重启mysql容器

docker restart mysql
 

检查防火墙

  • 检查服务器提供商防火墙是否已经开放3306端口
  • 检查服务器内防火墙是否打开且开放3306端口

使用创建远程连接的账号登录

  • ip为服务器ip地址
  • 端口为服务器映射的端口
上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2305081106.html b/note/other/2305081106.html index 02fd8374..9b5c54ce 100644 --- a/note/other/2305081106.html +++ b/note/other/2305081106.html @@ -31,7 +31,7 @@ } - + - + diff --git a/note/other/2305081110.html b/note/other/2305081110.html index d16cf5fc..b61ca370 100644 --- a/note/other/2305081110.html +++ b/note/other/2305081110.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

Docker搭建Nacos单机

Yaien Blog原创大约 2 分钟dockerdocker

Docker搭建Nacos单机

本文记录Docker容器安装Nacos单机环境的详细步骤

使用 docker pull nacos/nacos-server 拉取nacos镜像

我这里没有指定版本所以是拉取latest,你也可以使用 docker pull nacos/nacos-server:版本号 指定拉取的版本

img
img

二、使用命令启动容器

注意:如果只是简单的学习使用直接用下面的命令就好了。 但是nacos所有元数据都会保存在容器内部,如果容器迁移会导致nacos元数据不复存在,
所以通常我们通常会将nacos元数据保存在mysql中,那么请不要用下面这个命令,继续从第三步接着操作。

docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
@@ -82,6 +82,6 @@
 -v /home/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties \
 nacos/nacos-server
 
img
img

七、访问nacos

访问地址:http://域名或ip地址:8848/nacosopen in new window 账号:nacos 密码:nacos

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2305081112.html b/note/other/2305081112.html index 52a29c79..440a80e8 100644 --- a/note/other/2305081112.html +++ b/note/other/2305081112.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/other/2305090946.html b/note/other/2305090946.html index abe6ec76..8e872816 100644 --- a/note/other/2305090946.html +++ b/note/other/2305090946.html @@ -31,10 +31,10 @@ } - +
跳至主要內容

IDEA 常用插件

Yaien Blog原创大约 2 分钟工具idea

IDEA 常用插件

IDEA 开发常用插件汇总,持续更新

Lombok

简介:自动get/set/toString等。

你只需要写好类属性,其他的交给Lombok

Features

@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and
@NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder
@SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var
@UtilityClass

使用方法:将上面的注解写到类上面即可

google-java-format

简介:google-java-format插件可以帮助我们不通过对应的快捷键就可以实现特定方式下自动格式化代码。

Translation

简介:翻译插件,支持google翻译、百度翻译、有道翻译。推荐使用谷歌翻译

Alibaba Java Coding Guidelines

简介:阿里巴巴代码规范检测。不符合代码规范的地方会有波浪线,鼠标移上去就会有相应的提示,有些问题甚至可以快速修复

Leetcode Editor

简介:LeetCode插件,可以在IDEA中在线刷题。上班摸鱼属实方便,表面上我在干活,实际上我在刷算法题。

也可前往 LeetCode 官网;

Jclasslib Bytecode Viewer

简介:看类的字节码文件

CamelCase

简介:在几种字符串格式之间来回切换。有一下几种格式:

  • 驼峰,第一个单词首字母小写,其他单词首字母大写
  • 所有字母小写,单词间下划线分隔
  • 所有字母小写,单词间空格分隔
  • 所有字母小写,单词间短横线分隔
  • 每个单词首字母全部大写
  • 所有字母大写,单词间下划线分隔

使用:按住Shift + Alt再不停的按U,会把选中内容的单词的下划线转驼峰转大写等,不停的转换,直到你想要的。

Free Mybatis Plugin

简介:可以通过mapper接口里的方法跳转到mapper.xml里。

Auto filling Java call arguments

简介:函数参数自动填充。已经编写好的函数,调用后需要填充参数,但是绝大多数情况下,传入的变量名称和该函数的参数名一致,当参数较多时,手动单个填充参数非常浪费时间。该插件就可以帮你解决这个问题。

FindBugs

简介:静态代码检查。可以检查你代码中的隐患,并给出原因。

SequenceDiagram

简介:根据调用链路生成时序图,对查看类调用及查看源码帮助很大;

Codota

简介:代码补全插件。争抢IDEA的代码提示功能,可以搜索第三方对该函数的使用方法

Maven Helper

简介:maven 依赖管理。

JRebel

简介:热部署插件 详情教程:https://www.jianshu.com/p/52e698c57131open in new window

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2307051931.html b/note/other/2307051931.html index a6e96b53..5b39de14 100644 --- a/note/other/2307051931.html +++ b/note/other/2307051931.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

K8S + Flannel 公网IP搭建笔记

Yaien Blog原创大约 7 分钟kuberneteskubernetes

K8S + Flannel 公网IP搭建笔记

159.75.154.193 master 1.12.242.126 node1 139.159.219.202 node2 101.34.229.23 node3

版本简介

软件版本信息

名称版本
docker10.10.21
kubernetes1.21.0-0
Flannel0.20.2

集群角色规划

服务商内核公网IP节点
腾讯云centOS7.6159.75.154.193master
腾讯云centOS7.61.12.242.126node1
华为云centOS7.6139.159.219.202node2
腾讯云centOS7.6101.34.229.23node3

准备工作

修改服务器名称

修改所有的服务器名称,使用命令

# instanceName 处填写每个服务器的名称
@@ -349,6 +349,6 @@
 

查看pod提供的服务

kubectl get pods -o wide
 

访问

使用提供pod服务的公网ip+暴露的端口访问,假设是node1,则为

1.12.242.126:30992
 

参考文档

kubeadm+Flannel基于公网IP搭建k8s集群open in new window

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/2307062006.html b/note/other/2307062006.html index b31a3005..0d5e28b5 100644 --- a/note/other/2307062006.html +++ b/note/other/2307062006.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容

博客整合Github Actions实现自动发布记录

Yaien Blog大约 2 分钟

博客整合Github Actions实现自动发布记录

博客网站结合git提供的快速博客实现,现记录提交文档时实现自动发布,并复制一份到自己的远程服务器,通过域名访问远程服务器进而访问博客

服务器操作

创建密钥

ssh-keygen -t rsa -b 4096
+    
跳至主要內容

博客整合Github Actions实现自动发布记录

Yaien Blog大约 2 分钟

博客整合Github Actions实现自动发布记录

博客网站结合git提供的快速博客实现,现记录提交文档时实现自动发布,并复制一份到自己的远程服务器,通过域名访问远程服务器进而访问博客

服务器操作

创建密钥

ssh-keygen -t rsa -b 4096
 
  • 密钥文件保存位置可以修改,如果不想修改可以直接Enter,默认的文件保存路径在 ~/.ssh/ 文件夹下
  • 命令会生成两个文件,分别为id_rsa.pub(公钥)和id_rsa(私钥)

开启ssh登录

  • 复制公钥内内容到~/.ssh/authorized_keys文件中
cd ~/.ssh/
 cp id_rsa.pub authorized_keys
 
  • 修改文件访问权限
chmod 600 ~/.ssh/authorized_keys
@@ -94,6 +94,6 @@
           BRANCH: blog
           FOLDER: src/.vuepress/dist
 

以上配置需要根据项目调整node版本node-version的值以及本地编译后的路径source和要上传到服务器的路径target的值!

上次编辑于:
贡献者: yanggl
- + diff --git a/note/other/index.html b/note/other/index.html index 8f6790b6..0555af2b 100644 --- a/note/other/index.html +++ b/note/other/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/note/structure/2301101200.html b/note/structure/2301101200.html index 820b241d..aa1080bd 100644 --- a/note/structure/2301101200.html +++ b/note/structure/2301101200.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

栈(Stack)

Yaien Blog原创大约 2 分钟数据结构

栈(Stack)

栈(Stack)是一种线性数据结构,它具有后进先出(Last-In-First-Out,LIFO)的特性。这意味着最后一个进入栈中的元素是第一个被弹出的,而最先进入栈中的元素是最后一个被弹出的。

图形分析

思路分析

  1. 定义一个top来表示入栈的数量(栈顶),当添加一个数据时top会指向新添加的数据;
  2. 设置top = -1;(初始值)默认栈为空,当 top == stack.size() 时表示栈满;
  3. 定义一个数组 stack 模拟栈,保存需要入栈的值;
  4. 入栈:接收一个值,将值保存到 stack 中,并将 top 的位置上移 ;
  5. 出栈:将栈顶top的值取出,并将top下移,重新标记栈顶的位置;

实现代码

//定义一个 ArrayStack 表示栈结构
@@ -92,6 +92,6 @@
 	}
 }
 

总结

栈是一种简单但强大的数据结构,它可以用于许多不同的场景,特别是那些需要后进先出顺序的场景。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/structure/2305090929.html b/note/structure/2305090929.html index 9eae5232..34d6146e 100644 --- a/note/structure/2305090929.html +++ b/note/structure/2305090929.html @@ -31,7 +31,7 @@ } - +
跳至主要內容

跳表(SkipList)

Yaien Blog原创大约 14 分钟数据结构跳表

跳表(SkipList)

跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写
呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。

快速了解跳表

跳跃表(简称跳表)由美国计算机科学家William Pugh发明于1989年。他在论文《Skip lists: a probabilistic alternative to balanced
trees》中详细介绍了跳表的数据结构和插入删除等操作。

跳表(SkipList,全称跳跃表)

是用于有序元素序列快速搜索查找的一个数据结构,跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它在性能上和红黑树,AVL树不相上下,但是跳表的原理非常简单,实现也比红黑树简单很多。

在这里你可以看到一些关键词:链表(有序链表)、索引、二分查找。想必你的脑海中已经有了一个初略的印象,不过你可能还是不清楚这个"会跳的链表"有多diao,甚至还可能会产生一点疑虑:跟随机化有什么关系?你在下文中很快就能得到答案!

回顾链表,我们知道链表和顺序表(数组)通常都是相爱相杀,成对出现,各有优劣。而链表的优势就是更高效的插入、删除。痛点就是查询很慢很慢!每次查询都是一种O(n)复杂度的操作,链表估计自己都气的想哭了 。

这是一个带头结点的链表(头结点相当于一个固定的入口,不存储有意义的值)
,每次查找都需要一个个枚举,相当的慢,我们能不能稍微优化一下,让它稍微跳一跳呢?答案是可以的,我们知道很多算法和数据结构以空间换时间,我们在上面加一层索引,让部分节点在上层能够直接定位到,这样链表的查询时间近乎减少一半,链表自己虽然没有开心起来,但收起了它想哭的脸。

这样,在查询某个节点的时候,首先会从上一层快速定位节点所在的一个范围,如果找到具体范围向下然后查找代价很小,当然在表的结构设计上会增加一个向下的索引(指针)用来查找确定底层节点。平均查找速度平均为O(n/2)
。但是当节点数量很大的时候,它依旧很慢很慢。我们都知道二分查找是每次都能折半的去压缩查找范围,要是有序链表也能这么跳起来那就太完美了。没错跳表就能让链表拥有近乎的接近二分查找的效率的一种数据结构,其原理依然是给上面加若干层索引,优化查找速度。

通过上图你可以看到,通过这样的一个数据结构对有序链表进行查找都能近乎二分的性能。就是在上面维护那么多层的索引,首先在最高级索引上查找最后一个小于当前查找元素的位置,然后再跳到次高级索引继续查找,直到跳到最底层为止,这时候以及十分接近要查找的元素的位置了(

如果查找元素存在的话)。由于根据索引可以一次跳过多个元素,所以跳查找的查找速度也就变快了。

对于理想的跳表,每向上一层索引节点数量都是下一层的1/2.那么如果n个节点增加的节点数量(1/2+1/4+…)<n。
并且层数较低,对查找效果影响不大。但是对于这么一个结构,你可能会疑惑,这样完美的结构真的存在吗?大概率不存在的,因为作为一个链表,少不了增删该查的一些操作。而删除和插入可能会改变整个结构,所以上面的这些都是理想的结构,在插入的时候是否添加上层索引是个概率问题(
1/2的概率),在后面会具体讲解。

跳表操作

上面稍微了解了跳表是个啥,那么在这里就给大家谈谈跳表的增删改查过程。在实现本跳表的过程为了便于操作,我们将跳表的头结点(head)的key设为int的最小值(一定满足左小右大方便比较)。

对于每个节点的设置,设置成SkipNode类,为了防止初学者将next向下还是向右搞混,直接设置right,down两个指针

public class SkipNode<T> {
@@ -183,6 +183,6 @@
         }
     }
 

总结

对于上面,跳表完整分析就结束啦,当然,你可能看到不同品种跳表的实现,还有的用数组方式表示上下层的关系这样也可以,但本文只定义right和down两个方向的链表更纯正化的讲解跳表。

对于跳表以及跳表的同类竞争产品:红黑树,为啥Redis的有序集合(zset)使用跳表呢?

因为跳表除了查找插入维护和红黑树有着差不多的效率,它是个链表,能确定范围区间,而区间问题在树上可能就没那么方便查询啦。而JDK中跳跃表ConcurrentSkipListSet和ConcurrentSkipListMap。
有兴趣的也可以查阅源码。

上次编辑于:
贡献者: yanggl
- + diff --git a/note/structure/index.html b/note/structure/index.html index cd25ab58..7e654e47 100644 --- a/note/structure/index.html +++ b/note/structure/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/resources/books/index.html b/resources/books/index.html index 81c938bd..27811776 100644 --- a/resources/books/index.html +++ b/resources/books/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/resources/index.html b/resources/index.html index f333a7b0..0197793c 100644 --- a/resources/index.html +++ b/resources/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/resources/navigation/index.html b/resources/navigation/index.html index 2b445ada..39a28b7f 100644 --- a/resources/navigation/index.html +++ b/resources/navigation/index.html @@ -31,10 +31,10 @@ } - + - + diff --git a/star/index.html b/star/index.html index 07025a41..5968e0f7 100644 --- a/star/index.html +++ b/star/index.html @@ -31,12 +31,12 @@ } - +
跳至主要內容
多边形等距离外扩

多边形等距离外扩

实现多边形形成的多边形进行等距外扩或收缩的算法实现

-

多边形原创
- +

多边形原创
+ diff --git a/tag/aqs/index.html b/tag/aqs/index.html index 9a14e380..db49623f 100644 --- a/tag/aqs/index.html +++ b/tag/aqs/index.html @@ -31,13 +31,13 @@ } - + -
跳至主要內容
+ diff --git a/tag/atomic/index.html b/tag/atomic/index.html index 4d962db5..06f44db7 100644 --- a/tag/atomic/index.html +++ b/tag/atomic/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/chatgpt/index.html b/tag/chatgpt/index.html index 400cfb24..3b00e4ed 100644 --- a/tag/chatgpt/index.html +++ b/tag/chatgpt/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/ddd/index.html b/tag/ddd/index.html index 5a59c2c0..e5c6c581 100644 --- a/tag/ddd/index.html +++ b/tag/ddd/index.html @@ -31,13 +31,13 @@ } - + -
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

+
跳至主要內容
DDD 领域驱动设计模型

DDD 领域驱动设计模型

DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(
Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。

-

DDD原创
- +

DDD原创
+ diff --git a/tag/docker/index.html b/tag/docker/index.html index d0a2f5aa..939fc700 100644 --- a/tag/docker/index.html +++ b/tag/docker/index.html @@ -31,16 +31,16 @@ } - + -
跳至主要內容
+ diff --git a/tag/dubbo/index.html b/tag/dubbo/index.html index 13a81e96..073da6fc 100644 --- a/tag/dubbo/index.html +++ b/tag/dubbo/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

+
跳至主要內容
服务导出(服务注册)

服务导出(服务注册)

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。


dubbo原创
Dubbo 整合 Spring

Dubbo 整合 Spring

本文主要记录学习Dubbo 整合 Spring 的源码笔记

@@ -44,7 +44,7 @@

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用
Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

-

dubbo原创
- +

dubbo原创
+ diff --git a/tag/gc/index.html b/tag/gc/index.html index 3235954a..4804a200 100644 --- a/tag/gc/index.html +++ b/tag/gc/index.html @@ -31,13 +31,13 @@ } - + -
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

+
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器
-

JVMGC原创
- +

JVMGC原创
+ diff --git a/tag/idea/index.html b/tag/idea/index.html index b6b2be0e..fb42a86e 100644 --- a/tag/idea/index.html +++ b/tag/idea/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/index.html b/tag/index.html index eb4c7fe4..66e1b3a2 100644 --- a/tag/index.html +++ b/tag/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/tag/juc/index.html b/tag/juc/index.html index 04e8492c..5413bb9f 100644 --- a/tag/juc/index.html +++ b/tag/juc/index.html @@ -31,13 +31,13 @@ } - + - + diff --git a/tag/jvm/index.html b/tag/jvm/index.html index c38d7d15..517e204e 100644 --- a/tag/jvm/index.html +++ b/tag/jvm/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

+
跳至主要內容
垃圾收集器及原理

垃圾收集器及原理

JVM垃圾收集器负责回收无用对象,以防止内存泄漏。常见的收集器有Serial、Parallel Scavenge、ParNew、Serial Old、Parallel Old、CMS和G1及ZGC等。 本文主要简单记录垃圾收集器常用的垃圾收集算法、串行以及并行垃圾收集器的相关知识。

垃圾收集器
垃圾收集器

JVMGC原创
对象创建与内存分配

对象创建与内存分配

@@ -55,7 +55,7 @@ synchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。


JVMLock原创
JVM类加载机制

JVM类加载机制

本文主要记录JAVA项目在启动之后,对于我们编写好的JAVA代码是如何加载,以及加载过程中还执行了哪些操作。

-

JVM类加载原创
2
- +

JVM类加载原创
2
+ diff --git a/tag/kubernetes/index.html b/tag/kubernetes/index.html index 9767061a..8ad085f7 100644 --- a/tag/kubernetes/index.html +++ b/tag/kubernetes/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git a/tag/layui/index.html b/tag/layui/index.html index 2cb5e4ff..f5279734 100644 --- a/tag/layui/index.html +++ b/tag/layui/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/linux/index.html b/tag/linux/index.html index 4008f067..c39e1311 100644 --- a/tag/linux/index.html +++ b/tag/linux/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/lock/index.html b/tag/lock/index.html index 01f5c6cb..c7d41881 100644 --- a/tag/lock/index.html +++ b/tag/lock/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
深入理解Synchronized

深入理解Synchronized

+
跳至主要內容
深入理解Synchronized

深入理解Synchronized

synchronized关键字是为了处理在Java编程中多线程环境下的数据一致性和安全性的重要问题。
synchronized关键字可以用于方法或代码块,以确保在同一时刻只有一个线程可以访问被保护的资源(临界资源)。


JVMLock原创
ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLock(读写锁)

@@ -45,7 +45,7 @@

ReentrantLock 是Java中的一个工具类,位于 java.util.concurrent.locks 包下,是一种可重入的互斥锁,是 Lock
接口的一个实现。ReentrantLock
是由java提供的一种能够进行显示同步操作的锁,和synchronized不同的是,它是通过代码的方式来控制锁的获取和释放。

-

线程安全Lock原创
- +

线程安全Lock原创
+ diff --git a/tag/mysql/index.html b/tag/mysql/index.html index 26259a39..c909e124 100644 --- a/tag/mysql/index.html +++ b/tag/mysql/index.html @@ -31,10 +31,16 @@ } - + -
跳至主要內容
索引优化实战

索引优化实战

+
跳至主要內容
Mysql事务原理

Mysql事务原理

+

为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制、日志机制,用一整套机制来解决多事务并发问题。

+

MySQL事务原创
锁机制

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+

MySQL锁机制原创
MySQL索引

MySQL索引

-

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

-

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

-

MySQL索引原创
深入理解mysql索引底层数据结构与算法

深入理解mysql索引底层数据结构与算法

-

mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。

-

MySQL索引原创
- +

MySQL索引原创
2
+ diff --git a/tag/openai/index.html b/tag/openai/index.html index 2fdd3e1f..6c123b58 100644 --- a/tag/openai/index.html +++ b/tag/openai/index.html @@ -31,12 +31,12 @@ } - + - + diff --git a/tag/queue/index.html b/tag/queue/index.html index 45599ad9..0eb3d779 100644 --- a/tag/queue/index.html +++ b/tag/queue/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue(链表结构的阻塞队列)

+
跳至主要內容
LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。


线程安全Queue原创
DelayQueue(无界阻塞队列)

DelayQueue(无界阻塞队列)

DelayQueue是Java并发包java.util.concurrent中的一个类,它实现了BlockingQueue接口。这是一个无界阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

@@ -43,7 +43,7 @@

线程安全Queue原创
ArrayBlockingQueue(有界阻塞队列)

ArrayBlockingQueue(有界阻塞队列)

ArrayBlockingQueue 是 java.util.concurrent 包下的一个类,它是 BlockingQueue 接口的一个实现。这是一个由数组支持的有界阻塞队列
队列按照 FIFO (先进先出) 的规则对元素进行排序,队列的头部是在队列中存在时间最长的元素。新的元素插入到队列的尾部,队列检索操作会获取位于队列头部的元素。

-

线程安全Queue原创
- +

线程安全Queue原创
+ diff --git a/tag/redis/index.html b/tag/redis/index.html index d227b91c..d374ed59 100644 --- a/tag/redis/index.html +++ b/tag/redis/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
Redis分布式锁实战

Redis分布式锁实战

+
跳至主要內容
Redis分布式锁实战

Redis分布式锁实战

记录高并发场景下Redis部署、使用、存在的问题以及处理方案等


Redis原创
布隆过滤器

布隆过滤器

布隆过滤器简单实现

@@ -45,7 +45,7 @@

Redis原创
Redis核心数据结构

Redis核心数据结构

Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。底层使用c语言进行实现。Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。

Redis内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。

-

Redis原创
- +

Redis原创
+ diff --git a/tag/threadlocal/index.html b/tag/threadlocal/index.html index 32f204ca..2e6c6cf6 100644 --- a/tag/threadlocal/index.html +++ b/tag/threadlocal/index.html @@ -31,14 +31,14 @@ } - + -
跳至主要內容
ThreadLocal

ThreadLocal

+
跳至主要內容
ThreadLocal

ThreadLocal

ThreadLocal是Java中的一个线程局部变量,它允许每个线程独立地存储和获取数据,保证线程之间的数据互相独立,避免并发访问带来的竞争条件。

ThreadLocal不是用来解决共享数据的问题,而是为了实现线程隔离的目的。它在某些场景下非常有用,如Web应用中的用户身份信息、数据库连接、事务管理等。

使用ThreadLocal需要注意内存泄漏的问题,因为ThreadLocal会持有线程的引用,如果线程不正确地被管理,可能会导致内存泄漏。在使用完ThreadLocal后,应该及时调用remove()方法清除数据,避免不必要的资源占用。

-

ThreadLocal原创
- +

ThreadLocal原创
+ diff --git a/tag/zookeeper/index.html b/tag/zookeeper/index.html index 631c2fe1..136958ac 100644 --- a/tag/zookeeper/index.html +++ b/tag/zookeeper/index.html @@ -31,10 +31,10 @@ } - + - - + + diff --git "a/tag/\344\272\213\345\212\241/index.html" "b/tag/\344\272\213\345\212\241/index.html" new file mode 100644 index 00000000..3ab49f89 --- /dev/null +++ "b/tag/\344\272\213\345\212\241/index.html" @@ -0,0 +1,42 @@ + + + + + + + + 标签: 事务 | Yaien Blog + + + + + + + + + + diff --git "a/tag/\345\237\272\347\241\200/index.html" "b/tag/\345\237\272\347\241\200/index.html" index 9104ada0..315b383e 100644 --- "a/tag/\345\237\272\347\241\200/index.html" +++ "b/tag/\345\237\272\347\241\200/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
+ diff --git "a/tag/\345\244\232\350\276\271\345\275\242/index.html" "b/tag/\345\244\232\350\276\271\345\275\242/index.html" index ad3ca7b4..70d27c95 100644 --- "a/tag/\345\244\232\350\276\271\345\275\242/index.html" +++ "b/tag/\345\244\232\350\276\271\345\275\242/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/tag/\345\276\256\344\277\241\346\234\215\345\212\241\345\217\267/index.html" "b/tag/\345\276\256\344\277\241\346\234\215\345\212\241\345\217\267/index.html" index a128eb64..dd4aa08c 100644 --- "a/tag/\345\276\256\344\277\241\346\234\215\345\212\241\345\217\267/index.html" +++ "b/tag/\345\276\256\344\277\241\346\234\215\345\212\241\345\217\267/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/tag/\346\216\222\345\272\217/index.html" "b/tag/\346\216\222\345\272\217/index.html" index 0d8c079f..f59e5da3 100644 --- "a/tag/\346\216\222\345\272\217/index.html" +++ "b/tag/\346\216\222\345\272\217/index.html" @@ -31,14 +31,14 @@ } - + -
跳至主要內容
归并排序

归并排序

+
跳至主要內容
归并排序

归并排序

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各个答案“修补”在一起)。


排序原创
基数(桶)排序

基数(桶)排序

基数排序是一种非比较的排序算法,它根据元素的位值进行排序。它的基本思想是将待排序的数据按照位数切割成不同的数字,然后按照每个位数分别进行比较,从而得到最终有序的结果。

-

排序原创
- +

排序原创
+ diff --git "a/tag/\346\217\222\344\273\266/index.html" "b/tag/\346\217\222\344\273\266/index.html" index 148cd9e3..acfeeb1b 100644 --- "a/tag/\346\217\222\344\273\266/index.html" +++ "b/tag/\346\217\222\344\273\266/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
spring-boot-maven-plugin 插件详解

spring-boot-maven-plugin 插件详解

+
跳至主要內容
spring-boot-maven-plugin 插件详解

spring-boot-maven-plugin 插件详解

Spring Boot 是一个用于简化 Spring 应用程序开发的框架,它提供了一种快速构建可独立运行的、生产级别的 Spring
应用程序的方式。为了进一步简化项目的构建和部署过程,Spring Boot 内置了一个名为 spring-boot-maven-plugin 的 Maven
插件。本文将详细介绍这个插件的作用、使用方法以及一些常见的使用场景。

@@ -44,7 +44,7 @@

而如果我们使用一般的打包命令时

mvn clean package
 

不会把依赖的jar包也打进去,这样打出来的包就会很小。

-

插件原创
- +

插件原创
+ diff --git "a/tag/\346\240\210/index.html" "b/tag/\346\240\210/index.html" index 29f0cd4b..65ded965 100644 --- "a/tag/\346\240\210/index.html" +++ "b/tag/\346\240\210/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/tag/\346\272\220\347\240\201/index.html" "b/tag/\346\272\220\347\240\201/index.html" index b2595449..c0415816 100644 --- "a/tag/\346\272\220\347\240\201/index.html" +++ "b/tag/\346\272\220\347\240\201/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/tag/\347\261\273\345\212\240\350\275\275/index.html" "b/tag/\347\261\273\345\212\240\350\275\275/index.html" index eb84a8e4..57cbc633 100644 --- "a/tag/\347\261\273\345\212\240\350\275\275/index.html" +++ "b/tag/\347\261\273\345\212\240\350\275\275/index.html" @@ -31,12 +31,12 @@ } - + - + diff --git "a/tag/\347\264\242\345\274\225/index.html" "b/tag/\347\264\242\345\274\225/index.html" index f380d50a..35a5b964 100644 --- "a/tag/\347\264\242\345\274\225/index.html" +++ "b/tag/\347\264\242\345\274\225/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
索引优化实战

索引优化实战

+
跳至主要內容
SQL底层执行原理

SQL底层执行原理

介绍SQL底层执行原理以及流程

@@ -48,9 +48,7 @@

MySQL索引原创
MySQL索引

MySQL索引

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,简单讲就是一种排好序的数据结构

在千万级别的表中,有索引跟没有索引查询耗时差别是好几个数量级,好的索引设计可以帮助快速的在数据表中找到想要的数据。

-

MySQL索引原创
深入理解mysql索引底层数据结构与算法

深入理解mysql索引底层数据结构与算法

-

mysql数据是存储在磁盘上的,数据表里相邻的两个数据并不一定在磁盘上也是相邻的, 每查询一条记录就会去执行一次磁盘IO,这是一个很耗时的操作。

-

MySQL索引原创
- +

MySQL索引原创
+ diff --git "a/tag/\347\272\277\347\250\213\345\256\211\345\205\250/index.html" "b/tag/\347\272\277\347\250\213\345\256\211\345\205\250/index.html" index 7316cf23..c822dbba 100644 --- "a/tag/\347\272\277\347\250\213\345\256\211\345\205\250/index.html" +++ "b/tag/\347\272\277\347\250\213\345\256\211\345\205\250/index.html" @@ -31,10 +31,10 @@ } - + -
跳至主要內容
线程池参数配置

线程池参数配置

+
跳至主要內容
线程池参数配置

线程池参数配置

记录线程池参数配置参考,以及配置的理论原理以及思路


线程安全原创
LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue(链表结构的阻塞队列)

LinkedBlockingQueue是java.util.concurrent包中的一个类,它实现了BlockingQueue接口,是一个基于链表结构的阻塞队列,按FIFO(先进先出)排序元素,也是一种典型的生产者和消费者模型的阻塞队列。

@@ -56,7 +56,7 @@

线程安全Lock原创
AQS

AQS

AQS(AbstractQueuedSynchronizer)是 Java 并发包中的一个抽象类,用于实现构建同步器的基础框架。它是实现锁、信号量和其他同步器的关键组件。AQS
提供了一套底层的同步机制,供开发者基于其进行扩展和实现各种高级同步器。

-

线程安全AQS原创
2
- +

线程安全AQS原创
2
+ diff --git "a/tag/\347\275\221\347\273\234\347\274\226\347\250\213/index.html" "b/tag/\347\275\221\347\273\234\347\274\226\347\250\213/index.html" index c76f41f6..c4f0a3a8 100644 --- "a/tag/\347\275\221\347\273\234\347\274\226\347\250\213/index.html" +++ "b/tag/\347\275\221\347\273\234\347\274\226\347\250\213/index.html" @@ -31,10 +31,10 @@ } - + - - + + diff --git "a/tag/\350\267\263\350\241\250/index.html" "b/tag/\350\267\263\350\241\250/index.html" index 3b2817bc..a3629f17 100644 --- "a/tag/\350\267\263\350\241\250/index.html" +++ "b/tag/\350\267\263\350\241\250/index.html" @@ -31,14 +31,14 @@ } - + -
跳至主要內容
跳表(SkipList)

跳表(SkipList)

+
跳至主要內容
跳表(SkipList)

跳表(SkipList)

跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们熟知的就有Redis跳表。并且在面试的很多场景可能会问到,偶尔还会让你手写试一试(跳表可能会让手写,红黑树是不可能的)。

对于一个数据结构或算法,人群数量从听过名称、了解基本原理、清楚执行流程、能够手写
呈抖降的趋势。因为很多数据结构与算法其核心原理可能简单,但清楚其执行流程就需要动脑子去思考想明白,但是如果能够把它写出来,那就要自己一步步去设计和实现。可能要花很久才能真正写出来,并且还可能要查阅大量的资料。

-

跳表原创
- +

跳表原创
+ diff --git "a/tag/\351\224\201\346\234\272\345\210\266/index.html" "b/tag/\351\224\201\346\234\272\345\210\266/index.html" new file mode 100644 index 00000000..cf554962 --- /dev/null +++ "b/tag/\351\224\201\346\234\272\345\210\266/index.html" @@ -0,0 +1,44 @@ + + + + + + + + 标签: 锁机制 | Yaien Blog + + + + + + +
跳至主要內容
锁机制

锁机制

+

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。

+

在数据库中,除了传统的计算资源(CPU、RAM、I/O等)的争用外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题。

+

锁冲突也是影响数据库并发访问性能的一个重要因素。

+

MySQL锁机制原创
+ + + diff --git a/timeline/index.html b/timeline/index.html index 32f46bf8..037e391c 100644 --- a/timeline/index.html +++ b/timeline/index.html @@ -31,10 +31,10 @@ } - + -
跳至主要內容
- +
跳至主要內容
+