InnoDB — это механизм хранения, который поддерживает безопасность транзакций, а также является механизмом хранения по умолчанию для mysql. В этой статье в основном представлен формат записи строки InnoDB и принцип реализации страниц данных с точки зрения структуры данных, а также рассматривается механизм хранения InnoDB снизу.
Основное содержание этой статьи основано на буклете Nuggets «Понимание MySQL с самого начала». Если вы хотите узнать больше, рекомендуется купить буклет Nuggets для чтения.
Введение в InnoDB
Всем известно, что данные в mysql хранятся на физических дисках, а реальная обработка данных выполняется в памяти. Поскольку скорость чтения и записи диска очень низкая, если диск часто читается и записывается для каждой операции, производительность должна быть очень низкой. Для вышеуказанной проблемыInnoDB делит данные на несколько страниц и использует страницу как основную единицу взаимодействия между диском и памятью.Размер общей страницы составляет 16 КБ.. В этом случае по крайней мере 1 страница данных читается в памяти за один раз или 1 страница данных записана на диск. Улучшает производительность за счет уменьшения количества взаимодействий на диске памяти.
На самом деле, это типичная идея дизайна кэша, а общий кэш в основном из时间维度
или空间维度
Обдуманный:
-
时间维度
: если часть данных используется, существует высокая вероятность того, что она будет использоваться снова в следующий период времени. Это можно считать热点数据缓存
Все относятся к реализации этой идеи. -
空间维度
: если часть данных используется, данные, хранящиеся рядом с ней, скорее всего, скоро будут использованы.InnoDB的数据页
и操作系统的页缓存
Это воплощение этой идеи.
Формат строки InnoDB
MySQL вставляет данные в таблицу данных в единицах записей (одна строка данных), а способ хранения этих записей на диске называется行格式
. MySQL поддерживает 4 различных типа форматов строк:Compact
,Redundant
(Раньше эта статья не будет вводить его подробно),Dynamic
,Compressed
.
我们可以在创建或修改表的语句中指定行格式:
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称
ALTER TABLE 表名 ROW_FORMAT=行格式名称
Например, мы хотим создать формат строкиCompact
, набор символовascii
техническая спецификацияrecord_format_demo
, sql выглядит следующим образом:
mysql> CREATE TABLE record_format_demo (
-> c1 VARCHAR(10),
-> c2 VARCHAR(10) NOT NULL,
-> c3 CHAR(10),
-> c4 VARCHAR(10)
-> ) CHARSET=ascii ROW_FORMAT=COMPACT;
Query OK, 0 rows affected (0.03 sec)
Предположим, мыrecord_format_demo
В таблицу вставлены 2 строки данных:
mysql> SELECT * FROM record_format_demo;
+------+-----+------+------+
| c1 | c2 | c3 | c4 |
+------+-----+------+------+
| aaaa | bbb | cc | d |
| eeee | fff | NULL | NULL |
+------+-----+------+------+
2 rows in set (0.00 sec)
КОМПАКТНЫЙ формат строки
Как видно из рисунка выше, полная запись содержит记录的额外信息
и记录的真实数据
две части.
Записана дополнительная информация
Регистрируемая дополнительная информация в основном включает 3 категории:变长字段列表
,NULL值列表
и记录头信息
.
список полей переменной длины
MySQL поддерживает некоторые типы данных переменной длины (например,VARCHAR(M)
,TEXT
д.), объем памяти, занимаемый ими для хранения данных, не является фиксированным, а будет меняться при изменении содержимого хранилища. Чтобы точно описать этот тип данных, объем памяти, занимаемый этим полем переменной длины, должен включать:
- реальное содержание данных
- Занятые байты
В линейном формате CompactДлина байтов, занимаемых реальными данными всех полей переменной длины, сохраняется в начале записи, таким образом формируется список длин полей переменной длины.Количество байтов, занимаемых данными каждого поля переменной длины, равно в порядке столбцов.逆序
хранить.
мы начинаем сrecord_format_demo
Возьмите первую строку данных в качестве примера. так какc1
,c2
иc4
все преобразуются в типы данных (VARCHAR(10)
), поэтому сохраните длину этих трех столбцов в начале записи.
Еще одна вещь, которую следует отметить, это то, чтоВ списке длин полей переменной длины сохраняется только длина, занятая содержимым столбца, значение которого не равно NULL, а длина столбца, значение которого равно NULL, не сохраняется.. То есть для второй записи, поскольку значение столбца c4 равно NULL, список длин полей переменной длины второй записи должен хранить только длины столбцов c1 и c2.
Список значений NULL
Для столбцов, которые могут быть нулевыми, для экономии места для хранения, MySQL не будетNULL
значение хранится в记录的真实数据
часть. вместо этого он будет храниться в记录的额外信息
внутриNULL值列表
середина.
Конкретный метод заключается в том, чтобы сначала разрешить хранение в таблице статистики.NULL
столбец значений, затем сохраните каждый разрешенныйNULL
Столбец значений соответствует двоичному биту (1: значение равноNULL
0: значение неNULL
) используется для указания, следует ли хранитьNULL
значения в обратном порядке. Правила MySQLСписок значений NULL должен быть представлен целым числом байт битов, если количество используемых двоичных битов не является целым числом байтов, добавьте 0 к старшему биту байта.
соответствоватьrecord_format_demo
В таблице,c1
,c3
,c4
Всем разрешено хранить значения NULL. Первые две записи заполненыNULL
Схема после списка значений выглядит так:
запись информации в заголовке
Информация заголовка записи состоит из фиксированных 5 байтов (40 бит), и разные биты представляют разные значения:Пока не подробно.
зарегистрированы реальные данные
В дополнение к конкретным данным каждого столбца записанные реальные данные также автоматически добавят некоторые скрытые данные столбца.
имя столбца | Это необходимо | занимать место | описывать |
---|---|---|---|
row_id | нет | 6 байт | Идентификатор строки, который однозначно идентифицирует запись |
transaction_id | да | 6 байт | номер транзакции |
roll_pointer | да | 7 байт | указатель отката |
На самом деле настоящие имена этих столбцов на самом деле: DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR, которые для красоты пишутся как row_id, transaction_id и roll_pointer.
только если база данных не определена主键
или唯一键
, скрыть столбецrow_id
будет существовать и использовать его как таблицу данных主键
.
потому что столrecord_format_demo
Первичный ключ не определен, поэтому сервер MySQL добавит указанные выше три столбца к каждой записи. Теперь посмотри на это плюс记录的真实数据
Структура данных двух записей:
Формат хранения столбца CHAR(M)
Для столбца типа CHAR(M), когда столбец использует набор символов фиксированной длины, количество байтов, занимаемых столбцом, не будет добавлено в список длин полей переменной длины, ноЕсли используется набор символов переменной длины, количество байтов, занимаемых столбцом, также будет добавлено в список длин полей переменной длины..
Следует также отметить, что набор символов переменной длиныCHAR(M)
Столбцы типа должны занимать не менееM
байт, покаVARCHAR(M)
Но такого требования нет. Например, для использованияutf8
Набор символовCHAR(10)
Для столбца диапазон длины байта данных, хранящихся в этом столбце,10~30
байт, даже если мы храним пустую строку в столбце10
байт.
данные переполнения строки
Максимальные данные, которые может хранить VARCHAR(M)
MySQL имеет ограничение на максимальный объем памяти, занимаемый записью, за исключениемBLOB
илиTEXT
вне столбца типа,Общая длина байтов, занимаемых всеми остальными столбцами (за исключением скрытых столбцов и информации заголовка записи), не может превышать 65 535 байт.. Можно предположить, что,Объем памяти, занимаемый строкой записей MySQL, не может превышать 65535 байт.. Помимо данных самого столбца, эти 65535 байт также включают в себя некоторые другие данные (накладные расходы на хранение), например, чтобы хранить столбец типа VARCHAR(M), нам фактически нужно занимать 3 части дискового пространства:
- реальные данные
- Реальные данные занимают байты в длину
- Идентификация значения NULL, если столбец имеет атрибут NOT NULL, в этой части памяти нет необходимости
Предположениеvarchar_size_demo
только одинVARCHAR
type поле, то максимально занимаемое полем 65532 байта. Поскольку длина реальных данных может занимать 2 байта,NULL值标识
Требуется 1 байт. еслиVARCHAR
Тип столбца неNOT NULL
свойства, которые могут хранить не более65532
байт данных. если столбецascii
Набор символов, соответствующее максимальное количество символов до65532
;еслиutf8
набор символов, соответствующее максимальное количество символов равно21844
.
Переполнение, вызванное слишком большим количеством данных в записи
Мы используем набор символов ascii подvarchar_size_demo
В качестве примера вставьте запись:
mysql> CREATE TABLE varchar_size_demo(
-> c VARCHAR(65532)
-> ) CHARSET=ascii ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO varchar_size_demo(c) VALUES(REPEAT('a', 65532));
Query OK, 1 row affected (0.00 sec)
Базовой единицей взаимодействия между диском и памятью в mysql является страница, обычно 16 КБ, 16384 байта, и строка записей может занимать максимум65535
байт, что приводит кКогда одна страница не может хранить следующую строку данных.在Compact和Redundant行格式中,对于占用存储空间非常大的列,在记录的真实数据处只会存储该列的一部分数据,把剩余的数据分散存储在几个其他的页中,然后记录的真实数据处用20个字节存储指向这些页的地址,从而可以找到剩余数据所在的页,如图所示:этоТолько первые 768 байт данных в столбце и адрес, указывающий на другие страницы, хранятся в реальных данных этой записи, а затем остальные данные хранятся в других страницах.
行溢出
, те страницы, которые хранят более 768 байт, также называются溢出页
.
критическая точка переполнения линии
MySQL требует, чтобы на странице хранились как минимум две строки записей.. Вышеvarchar_size_demo
Таблица, например, имеет только один столбецc
, мы вставляем две записи в эту таблицу, сколько байт данных вставляется в каждую запись хотя бы до того, как произойдет переполнение строки? Это должно проанализировать, как используется пространство на странице.
- Помимо хранения наших записей, каждая страница также должна хранить некоторую дополнительную информацию, около 132 байт.
- Дополнительная информация, необходимая для каждой записи, составляет 27 байт.
Предполагая, что количество байтов данных, хранящихся в столбце, равно n, если вы хотите убедиться, что столбец не переполняется, вам необходимо выполнить:
132 + 2×(27 + n) < 16384
оказатьсяn < 8099
.Это означает, что если данные, хранящиеся в столбце, меньше 8099 байт, то столбец не становится столбцом переполнения.. Это значение меньше, если в таблице несколько столбцов.
Динамические и сжатые форматы строк
Формат строки по умолчанию в mysql:Dynamic
.Dynamic
иCompressed
формат строки иCompact
Формат очень похож, только в обработке行溢出
Есть расхождения в данных.Dynamic
иCompressed
формат строки не будет в记录的真实数据
Вместо хранения первых 768 байтов сохраните все байты на других страницах.Compressed
Формат строки использует алгоритм сжатия для сжатия страницы для экономии места.
Структура страницы данных InnoDB
Мы уже знаем, что страница является основной единицей пространства хранения управления InnoDB, а размер страницы обычно составляет 16 КБ. В InnoDB есть много разных типов страниц, предназначенных для разных целей.存储数据记录
Пейдж, официально известный как索引页
. Поскольку индекс еще не введен, назовем его пока数据页
Бар.
Краткий обзор структуры страницы данных
Страница данных может быть разделена на несколько частей по структуре, и разные части имеют разные функции, как показано на следующем рисунке:
Страница данных InnoDB разделена на 7 частей, и содержание этих 7 частей примерно описано ниже.
название | китайское имя | Размер занимаемой площади | Краткое описание |
---|---|---|---|
File Header | заголовок файла | 38 байт | Немного общей информации на странице |
Page Header | Заголовок страницы | 56 байтов | Некоторая информация, относящаяся к странице данных |
Infimum + Supremum | Минимальная запись и максимальная запись | 26 байт | две фиктивные записи строки |
User Records | Запись пользователя | неуверенный | Контент фактически хранится в строках |
Free Space | свободное место | неуверенный | неиспользуемое место на странице |
Page Directory | каталог страниц | неуверенный | Относительное положение некоторых записей на странице |
File Trailer | конец файла | 8 байт | Проверьте, заполнена ли страница |
Хранение записей на страницах
Собственные сохраненные данные пользователя будут храниться в соответствии с行格式
существуетUser Records
середина. На самом деле вновь сгенерированная страница неUser Records
, только когда мы вставляем данные в первый раз, будетFree Space
Выделить место рекордного размера дляUser Records
. когдаFree Space
После того, как он израсходован, это означает, что текущая страница данных также израсходована.чтобы иметь возможность
User Records
Чтобы прояснить, мы должны сначала понять вышеупомянутое记录头信息
.
Информация о заголовке записи
Сначала кратко представим описание каждого атрибута информации заголовка записи:
название | размер (единица измерения: бит) | описывать |
---|---|---|
зарезервированный бит 1 | 1 | не использовал |
Зарезервированный слот 2. | 1 | не использовал |
delete_mask | 1 | отметьте, удалена ли запись |
min_rec_mask | 1 | Эта метка будет добавлена к наименьшей записи в нелистовом узле каждого уровня дерева B+. |
n_owned | 4 | Указывает количество записей, принадлежащих текущей записи |
heap_no | 13 | Указывает информацию о местоположении, записанную в настоящее время в куче записей. |
record_type | 3 | Указывает тип текущей записи, 0 представляет собой обычную запись, 1 представляет собой запись неконечного узла дерева B+, 2 представляет наименьшую запись, а 3 представляет наибольшую запись. |
next_record | 16 | Представляет относительное положение следующей записи |
Далее сpage_demo
Таблица в качестве примера и вставьте некоторые данные, чтобы подробно описать информацию заголовка записи.
mysql> CREATE TABLE page_demo(
-> c1 INT,
-> c2 INT,
-> c3 VARCHAR(10000),
-> PRIMARY KEY (c1)
-> ) CHARSET=ascii ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO page_demo VALUES(1, 100, 'aaaa'), (2, 200, 'bbbb'), (3, 300, 'cccc'), (4, 400, 'dddd');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
Формат строки этих четырех записей в InnoDB следующий (отображается только заголовок записи и реальные данные), а все данные в столбцах выражены в десятичном формате:Сосредоточимся на деталях нескольких свойств по отношению к этой фигуре:
-
delete_mask
: Отмечает, удалена ли текущая запись, 0 означает, что запись не удалена, 1 означает, что она удалена. Неудаленные записи не будут удалены с диска сразу, а сначала будут помечены для удаления, а все удаленные записи сформируют垃圾链表
. Вновь вставленные записи могут быть повторно использованы позже垃圾链表
Занятое пространство, поэтому пространство для хранения, занимаемое списком мусора, также называется可重用空间
. -
heap_no
: указывает положение текущей записи на этой странице.Например, позиции четырех записей выше на этой странице:2、3、4、5
. Фактически, InnoDB автоматически добавит две виртуальные записи на каждую страницу, одна из которых最小记录
, другой最大记录
. Конструкция этих двух записей очень проста.5字节大小的记录头信息
и8字节大小的固定部分
(На самом деле содержание состоит из инфимума или супремума). Эти две записи размещаются отдельноInfimum + Supremum
часть.Из рисунка видно, что минимальная и максимальная записи
heap_no
Значения равны 0 и 1 соответственно, что означает, что они находятся впереди. -
next_record
: означает изАдресное смещение реальных данных текущей записи к реальным данным следующей записи. Его можно просто понимать как односвязный список, следующая запись наименьшей записи является первой записью, а следующая из последней записи является самой большой записью. Для более наглядного отображения мы можем использовать стрелки для замены смещения адреса в следующей_записи:Как видно из рисунка,Пользовательские записи фактически сортируются в положительном порядке в соответствии с размером первичного ключа в односвязный список.. Если удалить из него запись, то изменится и связанный список.Например удаляем вторую запись:
- Вторая запись не удаляется из хранилища, но запись
delete_mask
Значение устанавливается равным 1. - Статья 2 протокола
next_record
Значение становится равным 0, что означает, что для этой записи нет следующей записи. - Статья 1 протокола
next_record
Указывает на 3-ю запись.
- Вторая запись не удаляется из хранилища, но запись
Каталог страниц
Мы уже знаем, что записи объединяются в односвязный список в положительном порядке размера первичного ключа на странице. Что, если мы хотим найти конкретную запись по первичному ключу, самый простой способ — пройтись по связанному списку. Однако в случае относительно большого объема данных этот метод явно слишком неэффективен. Итак, mysql используетPage Directory(页目录)
Для решения этой проблемы.Page Directory(页目录)
Общий принцип таков:
- Разделите все обычные записи (включая самые большие и самые маленькие записи, исключая записи, помеченные как удаленные) на группы. Как разделить первое не обращайте внимания.
- Атрибут n_owned в заголовке последней записи каждой группы (т. е. самой большой записи в группе) указывает количество записей в группе.
- Извлеките смещение адреса последней записи каждой группы отдельно и сохраните его по порядку ближе к концу страницы.Это место называется
Page Directory
.
MySQL предусматривает, что может быть только 1 запись для группы, в которой находится самая маленькая запись, количество записей в группе, где находится самая большая запись, может быть только между 1-8, а количество записей в остальных группах может быть быть только в диапазоне от 4 до 8.
Например, текущийpage_demo
В таблице 18 обычных записей, и InnoDB разделит их на группы 5. В первой группе есть только одна минимальная запись, как показано ниже:
пройти черезPage Directory
Процесс поиска записи с указанным значением первичного ключа на странице данных делится на два этапа:
- Определите слот, в котором находится запись, с помощью дихотомии и найдите запись с наименьшим значением первичного ключа в группе, где находится слот.
- по записи
next_record
Свойство перебирает записи в группе, в которой находится слот.
Для оптимизации производительности запроса связанного списка идея в основном заключается в
二分法
осуществленный. описано вышеPage Directory
,跳跃表
и查找树
Это все так.
Заголовок страницы
Page Header
Он специально используется для хранения различной информации о состоянии, связанной со страницами данных, например, сколько записей было сохранено на этой странице, каков адрес первой записи, сколько слотов хранится в каталоге страниц и так далее. Он занимает фиксированно 56 байт, а значения байтовых атрибутов каждой части следующие:
название | Размер занимаемой площади | описывать |
---|---|---|
PAGE_N_DIR_SLOTS | 2 байта | Количество слотов в каталоге страниц |
PAGE_HEAP_TOP | 2 байта | Наименьший адрес неиспользованного пространства, то есть после этого адресаFree Space
|
PAGE_N_HEAP | 2 байта | Количество записей на этой странице (включая минимальное и максимальное количество записей и записей, помеченных для удаления) |
PAGE_FREE | 2 байта | Адрес первой записи, помеченной для удаления (каждая удаленная запись также будет формировать односвязный список через next_record, и записи в этом односвязном списке можно использовать повторно) |
PAGE_GARBAGE | 2 байта | Количество байтов, занимаемых удаленными записями |
PAGE_LAST_INSERT | 2 байта | Позиция последней вставленной записи |
PAGE_DIRECTION | 2 байта | Направление, в котором была вставлена последняя запись |
PAGE_N_DIRECTION | 2 байта | Количество записей, непрерывно вставляемых в одном направлении. Если изменить направление вставки последней записи, значение этого состояния будет очищено и подсчитано заново. |
PAGE_N_RECS | 2 байта | Количество записей на этой странице (исключая минимальные и максимальные записи и записи, помеченные для удаления) |
PAGE_MAX_TRX_ID | 8 байт | Измените максимальный идентификатор транзакции текущей страницы, это значение определяется только во вторичном индексе. |
PAGE_LEVEL | 2 байта | Уровень текущей страницы в дереве B+ |
PAGE_INDEX_ID | 8 байт | Идентификатор индекса, указывающий, к какому индексу принадлежит текущая страница. |
PAGE_BTR_SEG_LEAF | 10 байт | Информация заголовка дочернего сегмента дерева B+ определяется только на корневой странице дерева B+. |
PAGE_BTR_SEG_TOP | 10 байт | Информация заголовка неконечного сегмента дерева B+ определяется только на корневой странице дерева B+. |
Это только перечислено здесь, и пока нет необходимости понимать их все.
Заголовок файла
File Header
используется для описания некоторой общей информации, применимой к различным страницам, и состоит из следующего:
название | Размер занимаемой площади | описывать |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 байта | Контрольная сумма страницы (значение контрольной суммы) |
FIL_PAGE_OFFSET | 4 байта | номер страницы |
FIL_PAGE_PREV | 4 байта | номер предыдущей страницы |
FIL_PAGE_NEXT | 4 байта | номер следующей страницы |
FIL_PAGE_LSN | 8 байт | Соответствующая позиция в последовательности журнала при последнем изменении страницы (английское название: Log Sequence Number) |
FIL_PAGE_TYPE | 2 байта | тип страницы |
FIL_PAGE_FILE_FLUSH_LSN | 8 байт | Определен только на одной странице системного табличного пространства, что означает, что файл был сброшен как минимум до соответствующего значения LSN. |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 байта | к какому табличному пространству принадлежит страница |
Это только перечислено здесь, и пока нет необходимости понимать их все. Мы сосредоточимся на нескольких свойствах:
FIL_PAGE_SPACE_OR_CHKSUM
Контрольная сумма текущей страницы. Для очень длинной строки байтов мы можем вычислить относительно короткое значение, чтобы представить эту очень длинную строку байтов с помощью некоторого алгоритма, и это относительно короткое значение называется校验和
. пройти через校验和
Это может значительно повысить эффективность сравнения строк на равенство.
2.FIL_PAGE_OFFSET
Каждая страница имеет уникальный номер страницы,InnoDB
通过页号来可以定位一个页。
3.FIL_PAGE_TYPE
Представляет тип текущей страницы Как мы уже говорили ранее, InnoDB делит страницы на разные типы для разных целей.
имя типа | шестигранник | описывать |
---|---|---|
FIL_PAGE_TYPE_ALLOCATED | 0x0000 | Последнее распределение, еще не использовано |
FIL_PAGE_TYPE_ALLOCATED | 0x0000 | Последнее распределение, еще не использовано |
FIL_PAGE_UNDO_LOG | 0x0002 | Отменить страницу журнала |
FIL_PAGE_INODE | 0x0003 | информационный узел сегмента |
FIL_PAGE_IBUF_FREE_LIST | 0x0004 | Вставить список свободных буферов |
FIL_PAGE_IBUF_BITMAP | 0x0005 | Вставить растровое изображение буфера |
FIL_PAGE_TYPE_SYS | 0x0006 | системная страница |
FIL_PAGE_TYPE_TRX_SYS | 0x0007 | данные системы транзакций |
FIL_PAGE_TYPE_FSP_HDR | 0x0008 | Информация о заголовке табличного пространства |
FIL_PAGE_TYPE_XDES | 0x0009 | Страница расширенного описания |
FIL_PAGE_TYPE_BLOB | 0x000A | страница переполнения |
FIL_PAGE_INDEX | 0x45BF | Страницы индекса, также известные как страницы данных |
4. FIL_PAGE_PREV и ФIL_PAGE_NEXT
|
||
Указывает номер страницы предыдущей и следующей страницы этой страницы, каждая страница пропускаетсяFIL_PAGE_PREV и ФIL_PAGE_NEXT Сформируйте двусвязный список. |
||
File Trailer
Основной единицей взаимодействия между памятью и диском в MySQL является страница. Если страница в памяти изменена, страница памяти должна быть синхронизирована с диском в какой-то момент. Если в процессе синхронизации возникает проблема с системой, это может привести к тому, что данные страницы на диске не будут полностью синхронизированы, т.脏页
Случай. Чтобы избежать этой проблемы, mysql добавляет в конце каждой страницыFile Trailer
для проверки целостности страницы.File Trailer
Состоит из 8 байт:
- Первые 4 байта представляют контрольную сумму страницы
Этот раздел соответствует контрольному заказу в заголовке файла. Простое понимание,
File Header
иFile Trailer
У обоих есть контрольные суммы, и если они совпадают, страница данных завершена. В противном случае страница данных脏页
. - Последние 4 байта представляют собой позицию последовательности журнала (LSN), соответствующую последней модификации страницы. Эта часть также предназначена для проверки целостности страницы, поэтому я не знаю ее в деталях.
Нелегко быть оригинальным. Если вы считаете, что статья написана хорошо, пожалуйста, поставьте лайк 👍 и поддержите ее~
Добро пожаловать в мой проект с открытым исходным кодом:Облегченная среда вызовов HTTP для SpringBoot