数据库表空间收缩之pg_squeeze,pg_repack

下半年一直忙于NP的sybase,通过大家的共同努力,NP年底比较稳定。很久没有弄过pg相关的知识了,最近经常看到有人问如何用工具自动清理pg的坏元组。

除了我们经常手动使用vacuum之外,生产环境还有两个比较常用的工具一个是pg_squeeze,另外一个是pg_repack

pg_squeeze1.2

项目地址:https://github.com/cybertec-postgresql/pg_squeeze

原理

pg_squeeze是一个扩展,它从表中删除未使用的空间,并且可以选择根据特定索引对元组进行排序,一般当一个表膨胀时一般使用vacuum full或者cluster进行表重建,在这一过程中会加排他锁,导致该表无法进行读写,只有等整个过程完成后才可以进行正常使用

优点

相比pg_repack或者pg_reorg,pg_squeeze不需要建触发器,所以在重组时对原表的DML几乎没有性能影响。pg_squeeze支持自动重组,可以设置定时清理时间以及根据空闲空间比例来进行清理表。该过程会自动启动worker进程,将数据复制到重组表,然后加锁,切换filenode。

安装

1、下载安装包后,解压后修改MakeFile,在MakeFile中加入pg_config

  1. PG_CONFIG =/home/thunisoft5/arterybase/5.0/bin/pg_config

2、安装

  1. make && make install

3、修改postgresql.conf配置文件

  1. wal_level = logical
  2. max_replication_slots = 1 # 大于等于1
  3. shared_preload_libraries = 'pg_squeeze'

4、重启数据库

使用

1、创建扩展

  1. postgres=# create extension pg_squeeze;
  2. CREATE EXTENSION
  3. postgres=# \dx
  4. List of installed extensions
  5. Name | Version | Schema | Description
  6. ------------+---------+------------+------------------------------------------------
  7. pg_squeeze | 1.2 | squeeze | A tool to remove unused space from a relation.
  8. plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
  9. (2 rows)

2、安装完成后会有一个对应的系统表

  1. postgres=# \d squeeze.tables
  2. Table "squeeze.tables"
  3. Column | Type | Collation | Nullable | Default
  4. ------------------+-----------------------+-----------+----------+--------------------------------------------
  5. id | integer | | not null | nextval('squeeze.tables_id_seq'::regclass)
  6. tabschema | name | | not null |
  7. tabname | name | | not null |
  8. clustering_index | name | | |
  9. rel_tablespace | name | | |
  10. ind_tablespaces | name[] | | |
  11. schedule | time with time zone[] | | not null |
  12. free_space_extra | integer | | not null | 50
  13. min_size | real | | not null | 8
  14. vacuum_max_age | interval | | not null | '01:00:00'::interval
  15. max_retry | integer | | not null | 0
  16. skip_analyze | boolean | | not null | false
  17. Indexes:
  18. "tables_pkey" PRIMARY KEY, btree (id)
  19. "tables_tabschema_tabname_key" UNIQUE CONSTRAINT, btree (tabschema, tabname)
  20. Check constraints:
  21. "tables_free_space_extra_check" CHECK (free_space_extra >= 0 AND free_space_extra < 100)
  22. "tables_min_size_check" CHECK (min_size > 0.0::double precision)
  23. Referenced by:
  24. TABLE "squeeze.tables_internal" CONSTRAINT "tables_internal_table_id_fkey" FOREIGN KEY (table_id) REFERENCES squeeze.tables(id) ON DELETE CASCADE
  25. TABLE "squeeze.tasks" CONSTRAINT "tasks_table_id_fkey" FOREIGN KEY (table_id) REFERENCES squeeze.tables(id) ON DELETE CASCADE
  26. Triggers:
  27. tables_internal_trig AFTER INSERT ON squeeze.tables FOR EACH ROW EXECUTE PROCEDURE squeeze.tables_internal_trig_func()

squeeze.tables表字段说明

  • tabschema:表的模式名。
  • tabname:表名。
  • clustering_index:表示重建表时,表数据的物理顺序按照该索引进行聚簇。
  • rel_tablespace:表示表重建时,移动到哪个表空间中。
  • ind_tablespace:这个一个二维数组,表示索引和表空间的映射关系。
  • schedule:vacuum在一天中运行的时间范围
  • free_space_extra:表示空闲空间超过多少时就会对表进行重建,默认是50。
  • min_size:表必须占用的最小磁盘空间(兆字节)才有资格进行处理,默认值为8。
  • vacuum_max_age:当进行一次vacuum后,认为fsm是有效的最大时间,默认1小时。
  • max_retry:当重建表失败时最大的重新尝试的次数,默认是0.
  • skip_analyse:跳过对表进行analyse,默认是false。

3、创建测试表

  1. --创建表
  2. postgres=# create table test(n_id int,c_name varchar(300),primary key(n_id));
  3. CREATE TABLE
  4. --初始化数据
  5. postgres=# insert into test select generate_series(1,4000000),'zhangsan';
  6. INSERT 0 4000000
  7. --查看表大小:169MB
  8. postgres=# \dt+ test
  9. List of relations
  10. Schema | Name | Type | Owner | Size | Description
  11. --------+------+-------+-------+--------+-------------
  12. public | test | table | sa | 169 MB |
  13. (1 row)

4、给表test创建squeeze任务

  1. --需要在表squeeze.tables插入一条记录。添加后,将定期检查表的统计信息。只要满足‘压缩’的太偶见,就会将‘任务’添加到队列中,任务按照创建爱女顺序依次处理
  2. --schedule标识该任务在晚上八点到24点执行,并且free_space_extra表示空闲空间超过10时就会对表进行重建
  3. postgres=# insert into squeeze.tables (tabschema, tabname, schedule, free_space_extra) values ('public', 'test', '{20:00, 24:00}', '10');
  4. INSERT 0 1
  5. --如果需要取消注册表,只需要从‘squeeze.tables’表删除响应的行即可
  6. --查看任务
  7. postgres=# select * from squeeze.tables;
  8. id | tabschema | tabname | clustering_index | rel_tablespace | ind_tablespaces | schedule | free_space_extra | min_size | vacuum_max_age | max_retry | skip_analyze
  9. ----+-----------+---------+------------------+----------------+-----------------+---------------------------+------------------+----------+----------------+-----------+--------------
  10. 2 | public | test | | | | {20:00:00+08,24:00:00+08} | 10 | 8 | 01:00:00 | 0 | f
  11. (1 row)

5、启动和关闭pg_squeeze进程

  1. select squeeze.start_worker();
  2. select squeeze.stop_worker();

6、验证

  1. --更新数据
  2. postgres=# update test set c_name = '张三-1' where n_id <2000000;
  3. UPDATE 1999999
  4. --更新后表大小
  5. postgres=# \dt+ test
  6. List of relations
  7. Schema | Name | Type | Owner | Size | Description
  8. --------+------+-------+-------+--------+-------------
  9. public | test | table | sa | 253 MB |
  10. (1 row)
  11. --查看空闲空间已经30
  12. postgres=# select * from squeeze.tables_internal;
  13. table_id | class_id | class_id_toast | free_space | last_task_created | last_task_finished
  14. ----------+----------+----------------+------------------+-------------------------------+--------------------
  15. 2 | 16528 | 0 | 30.2095497833996 | 2021-01-05 20:57:10.874252+08 |
  16. (1 row)
  17. --启动pg_squeeze
  18. postgres=# select squeeze.start_worker();
  19. start_worker
  20. --------------
  21. 53433
  22. (1 row)
  23. --清理完成后查看表大小:
  24. postgres=# \dt+ test
  25. List of relations
  26. Schema | Name | Type | Owner | Size | Description
  27. --------+------+-------+-------+--------+-------------
  28. public | test | table | sa | 169 MB |
  29. (1 row)
  30. --处理的结束时间last_task_finished时间已经更新了
  31. postgres=# select * from squeeze.tables_internal;
  32. table_id | class_id | class_id_toast | free_space | last_task_created | last_task_finished
  33. ----------+----------+----------------+------------+-------------------------------+-------------------------------
  34. 2 | | | | 2021-01-05 20:57:10.874252+08 | 2021-01-05 20:57:10.916349+08
  35. (1 row)

删除200w数据

  1. --会自动清理
  2. postgres=# \dt+ test
  3. List of relations
  4. Schema | Name | Type | Owner | Size | Description
  5. --------+------+-------+-------+-------+-------------
  6. public | test | table | sa | 85 MB |
  7. (1 row)

如果执行vacuum full表还会变小吗?

  1. postgres=# vacuum full test;
  2. VACUUM
  3. postgres=# \dt+ test
  4. List of relations
  5. Schema | Name | Type | Owner | Size | Description
  6. --------+------+-------+-------+-------+-------------
  7. public | test | table | sa | 84 MB |
  8. (1 row)

执行vacuum full后表的大小没有实质性改变,说明pg_squeeze清理比较彻底。

pgstattuple

pgstattuple返回一个关系的物理长度、"死亡"元组的百分比以及其他信息。

类型 描述
table_len bigint 物理关系长度,以字节计
tuple_count bigint 存活元组的数量
tuple_len bigint 存活元组的总长度,以字节计
tuple_percent float8 存活元组的百分比
dead_tuple_count bigint 死亡元组的数量
dead_tuple_len bigint 死亡元组的总长度,以字节计
dead_tuple_percent float8 死亡元组的百分比
free_space bigint 空闲空间总量,以字节计
free_percent float8 空闲空间的百分比
  1. postgres=# create extension pgstattuple;
  2. CREATE EXTENSION
  3. postgres=# select * from pgstattuple('test');
  4. table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percen
  5. t | free_space | free_percent
  6. -----------+-------------+-----------+---------------+------------------+----------------+------------------
  7. --+------------+--------------
  8. 88563712 | 2000001 | 74000037 | 83.56 | 0 | 0 |
  9. 0 | 260960 | 0.29
  10. (1 row)

临时处理

还可以手动“压缩”表,而无需注册,跳过任何时间和膨胀检查。

功能签名: squeeze.squeeze_table(tabchema name, tabname name, clustering_index name, rel_tablespace name, ind_tablespaces name[])

示例执行:

  1. SELECT squeeze.squeeze_table('public', 'test', null, null, null);

监控方式

  1. 'squeeze.log'表在每个成功压缩的表中包含一个条目。
  2. 'squeeze.errors'包含在压缩期间发生的错误。这里报告的一个常见问题是有人更改了正在处理表的定义(例如,添加或删除的列)。

注意事项

pg_squeeze需要使用logical replication,所以需要设置足够的slots,而且必须注意可能与standby或者使用了逻辑复制功能争抢slots,要保证slots够用。

pg_squeeze可以自动收缩,对于比较繁忙的数据库,建议不要在业务高峰期启用,避免带来性能风险

首先,确保您的表具有主键或唯一约束。这是处理“ pg_squeeze”工作时其他事务可能进行的更改所必需的。

squeeze1.2和低版本的区别

新版本的squeeze有个更好的功能是:

  • squeeze.tables表可以指定schedule:也就是指定气你的时间范围。你可以放到晚上来运行。

低版本pg_squeeze支持时间间隔的

  • task_interval:表示检查表膨胀是否超过阀值的时间间隔

  • first_check:表示第一次检查时间

    相对来说直接在晚上定时执行vacuum full的方式更加简便

pg_repack

自述文件:和pg_squeeze一样pg_repack也是一个扩展,可以从表和索引中消除膨胀,并且可以选择恢复簇索引的物理顺序,与cluster和vacuum full不同,该工具可以在线工作,并且在处理过程中不需要在表上面持有排它锁(vacuum full工作需要access exclusive lock,导致任何操作都不能执行),pg_repack的启动效率很高,其性能与直接使用cluster相当

pg_repack老版本叫pg_reorg

原理

pg_repack原理和vacuum full原理类似,都是新建一个文件,然后将老文件拷贝过来,然后进行文件切换。不阻塞读写的秘诀就是新建文件和拷贝的过程是在线做的,在没有完成拷贝之前,原来的文件还是可以读写,只有在切表的一瞬间会有影响。

源库的数据文件一直在变化,pg_repack是如何拷贝的呢?表文件分为两部分,一部分是基础数据,一部分是增量数据,基础数据的拷贝是正常拷贝,增量数据是通过创建触发器来捕获该表上的读写操作来实现,基础数据拷贝完之后再将触发器捕获的增量sql进行应用,完成切换。

具体步骤:

  1. 创建一个日志表来记录对原始表所做的更改
  2. 在原始表上添加触发器,将INSERT,UPDATE和DELETE记录到我们的日志表中
  3. 创建一个新表,包含旧表中所有的行
  4. 在这个新表上建立索引
  5. 将日志表中产生的所有更改应用到新表中
  6. 使用系统目录交换表,包括索引和Toast表
  7. 删除原始表

当然我们在执行过程中从pg_stat_activity中也可以看到一些

  • 执行过程中会给对应的表加上ACCESS SHARE MODE

  • 然后执行数据拷贝的工作:INSERT INTO repack.table_16588 SELECT n_id,c_name FROM ONLY public.repack_test

  • 最后创建索引:CREATE UNIQUE INDEX index_16595 ON repack.table_16588 USING btree (n_id) TABLESPACE pg_default

安装

  1. wget https://github.com/reorg/pg_repack/archive/ver_1.4.4.zip
  2. [thunisoft5@localhost pg_repack-ver_1.4.4]$ make && make install
  3. create extension pg_repack;

使用方法

选项:

参数 描述
-a, --all 重新包装所有数据库
-t, --table=TABLE 仅重新包装特定表
-I, --parent-table=TABLE 重新打包特定的父表及其继承者
-c, --schema=SCHEMA 仅在特定架构中重新打包表
-s, --tablespace=TBLSPC 将重新打包的表移动到新表空间
-S, --moveidx 将重新打包的索引也移动到TBLSPC
-o, --order-by=COLUMNS 按列而不是集群键排序
-n, --no-order 真空吸尘而不是吸尘
-N, --dry-run 打印将重新包装的内容并退出
-j, --jobs=NUM 每个表使用这么多并行作业
-i, --index=INDEX 仅移动指定的索引
-x, --only-indexes 仅移动指定表的索引
-T, --wait-timeout=SECS 超时以取消冲突中的其他后端
-D, --no-kill-backend 超时时不要杀死其他后端
-Z, --no-analyze 最后不要分析
-k, --no-superuser-check 跳过客户端中的超级用户检查
-C, --exclude-extension 不要重新打包属于特定扩展名的表

连接选项:

参数 描述
-d, --dbname=DBNAME 数据库连接
-h, --host=HOSTNAME 数据库服务器主机或套接字目录
-p, --port=PORT 数据库服务器端口
-U, --username=USERNAME 连接的用户名
-w, --no-password 从不提示输入密码
-W, --password 强制输入密码提示

通用选项:

参数 描述
-e, --echo 回显查询
-E, --elevel=LEVEL 设置输出消息级别
--help 显示此帮助,然后退出
--version 输出版本信息,然后退出

测试

  1. postgres=# create table repack_test(n_id int,c_name varchar(3000));
  2. CREATE TABLE
  3. --初始化数据
  4. postgres=# insert into repack_test select generate_series(1,4000000),'张三';
  5. INSERT 0 4000000
  6. --使用pg_stattuple查看表情况
  7. postgres=# select * from pgstattuple('repack_test');
  8. table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percen
  9. t | free_space | free_percent
  10. -----------+-------------+-----------+---------------+------------------+----------------+------------------
  11. --+------------+--------------
  12. 177127424 | 4000000 | 140000000 | 79.04 | 0 | 0 |
  13. 0 | 522008 | 0.29
  14. (1 row)
  15. --查看表大小
  16. postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') );
  17. pg_size_pretty
  18. ----------------
  19. 169 MB
  20. (1 row)
  21. --查看表文件路径
  22. postgres=# select pg_relation_filepath('repack_test');
  23. pg_relation_filepath
  24. ----------------------
  25. base/13214/16588
  26. (1 row)
  1. --表必须有主键或者唯一约束,这快和pg_squeeze一样
  2. [thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --table repack_test
  3. WARNING: relation "public.repack_test" must have a primary key or not-null unique keys
  4. --添加主键
  5. postgres=# alter table repack_test add primary key(n_id);
  6. ALTER TABLE
  7. --更新200w数据
  8. postgres=# update repack_test set c_name = '张三-1' where n_id <=2000000;
  9. UPDATE 2000000
  10. 更新后表达小变大了
  11. postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') );
  12. pg_size_pretty
  13. ----------------
  14. 425 MB
  15. (1 row)
  16. --再次执行pg_repack
  17. [thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --table repack_test --elevel=info
  18. INFO: repacking table "public.repack_test"
  19. --更新后查看表大小,表已经缩小了
  20. postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') );
  21. pg_size_pretty
  22. ----------------
  23. 255 MB
  24. (1 row)
  25. --并且数据文件的路径也发生了变化
  26. postgres=# select pg_relation_filepath('repack_test');
  27. pg_relation_filepath
  28. ----------------------
  29. base/13214/16659
  30. (1 row)

系统表

repack.primary_keys

  • indrelid代表表的oid,第二列indexrelid代表主键或者唯一索引的oid

repack.tables

  • tables表记录了创建trigger以及捕获的相关语句,语句按一条条的record进行记录
  1. postgres=# select * from repack.primary_keys limit 10;
  2. indrelid | indexrelid
  3. ----------+------------
  4. 826 | 828
  5. 1136 | 1137
  6. 1213 | 2697
  7. 1247 | 2703
  8. 1249 | 2658
  9. 1255 | 2690
  10. 1259 | 2662
  11. 1260 | 2677
  12. 1261 | 2694
  13. 1262 | 2672
  14. (10 rows)

在线pg_repack

repack数据库

  1. [thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --jobs 8 --elevel=info

repack模式

  1. pg_repack -p 8543 -d postgres --schema=public --no-order --jobs 8 --elevel=info

repack表和索引

  1. pg_repack -p 8543 -d postgres --no-order --table public.repack_test --elevel=info

repack所有索引

  1. pg_repack -p 8543 -d postgres --no-order --only-indexes --table public.repack_test --elevel=info

repack指定索引

  1. pg_repack -p 8543 -d postgres --index public.repack_test_pkey --elevel=info

pg_repack限制

1、无法重组临时表

2、不能通过gist索引集群表

3、如果使用1.1.8或者更早的版本,则在运行pg_repack时,切勿尝试在目标表上面执行任何ddl命令。许多情况下,pg_repack会失败并正确回滚,但是在早期版本中,有一些情况可能会导致数据损坏

总结

pg_squeeze和pg_repack都需要表有主键或者非空唯一约束才行

pg_repack重组时,触发器会带来一定的开销,对被重组的表,有一定的DML性能影响。

pg_squeeze不需要建触发器,所以在重组时对原表的DML几乎没有性能影响。

pg_squeeze支持自动的重组,即通过设置阈值、比较用户表与阈值,自动启动WORKER进程,将数据复制到重组表,最后加锁,切换FILENODE。

pg_squeeze需要清理的表都需要在squeeze.tables表中插入对应的记录,并且可以对不同的表设置阈值和清理时间段。pg_repack可以针对库,schema以及表和索引分别清理

两个工具都可圈可点,pg_squeeze对系统的性能影响更小一些。当然也可以在晚上系统空闲时间直接使用vacuum full的方式来清理。

数据库表空间收缩之pg_squeeze,pg_repack的更多相关文章

  1. oracle数据库表空间追加数据库文件方法

    oracle数据库表空间追加数据库文件方法   针对非大文件方式表空间,允许追加文件进行表空间的扩展,单个文件最大大小是32G  第一种方式:表空间增加数据文件    www.2cto.com   1 ...

  2. Oracle数据库表空间与数据文件的关系描述正确的是( )

    Oracle数据库表空间与数据文件的关系描述正确的是( ) A.一个表空间只能对应一个数据文件 B.一个表空间可以对应多个数据文件 C.一个数据文件可以对应多个表空间 D.表空间与数据文件没任何对应关 ...

  3. Oracle数据库表空间与用户的关系是 ( )

    Oracle数据库表空间与用户的关系是 ( )? A.一对一 B.一对多 C.多对一 D.多对多 解答: D 一个用户可以使用一个或多个表空间,一个表空间也可以供多个用户使用.

  4. 查看Oracle数据库表空间大小(空闲、已使用),是否要增加表空间的数据文件

    查看Oracle数据库表空间大小(空闲.已使用),是否要增加表空间的数据文件 1.查看表空间已经使用的百分比 Sql代码 select a.tablespace_name,a.bytes/1024/1 ...

  5. 创建oracle数据库表空间并分配用户

    我们在本地的oracle上或者virtualbox的oracle上 创建新的数据库表空间操作:通过system账号来创建并授权/*--创建表空间create tablespace YUJKDATAda ...

  6. 当前数据库表空间达到32G,需要扩容

    表空间名:cwy_init 操作:给cwy_init增加数据文件,分配5G的空间,达到瓶颈自动增长1G,如下: alter tablespace cwy_init add datafile '/u01 ...

  7. oracle数据库表空间文件收缩实例

    Oracle数据文件收缩实例 数据文件的作用 HWM的基本概念 查看数据文件的使用情况 包括内容:数据文件大小,已经used空间,free空间,hwm信息 select /*+ ordered use ...

  8. 解决sqlserver数据库表空间不自动释放问题

    在项目中遇到了sql server数据库经过频繁地删减数据后,查询变慢的问题. 我把数据导到另一个库中,发现查询就很快. 查了下原因,根本原因是删除数据并不释放表空间,日志文件太过巨大的原因. 网上查 ...

  9. oracle数据库表空间扩容方法

    1. 先查询表空间在物理磁盘上存放的位置,注意使用sysdba的账号登陆. SELECT tablespace_name, file_id, file_name, ), ) total_space F ...

随机推荐

  1. 自己整理了一个 Dapper的Helper助手类

    链接字符串配置: <connectionStrings> <add name="db" connectionString="server=.;datab ...

  2. SpringCloud-服务间通信方式

    接下来在整个微服务架构中,我们比较关心的就是服务间的服务改如何调用,有哪些调用方式? 总结:在springcloud中服务间调用方式主要是使用 http restful方式进行服务间调用 1. 基于R ...

  3. 熊猫全球站视点关注,韩国投入47亿韩元开发区块链等ICT技术

    韩国科学技术信息通信部评选出18个包括区块链项目在内的政府扶持项目,为信息通信(ICT)创新企业提供技术开发支持. 熊猫全球站获悉,韩国政府计划分期选拔第一阶段(前期策划阶段3个月)和第二阶段(技术开 ...

  4. 我的第一次shell

    我的第一次shell 最近我们的项目需要进行优化,整体架构进行改造. 然后我们红超哥就看我骨骼惊奇,说小伙子你想不想当做掌门人呀.(我说不想哈哈) 想不想也没用了,红超哥说我们现在的架构有所改变,需要 ...

  5. Element-ui 实现table的合计功能

    Element-UI是饿了么前端团队推出的一款基于Vue.js 2.0 的桌面端UI框架,其功能较为完善,根据其文档与demo学习,非常容易上手,但是我在使用其tabel组件时,发现我的功能 需求并不 ...

  6. java web简单的增删改查

    1.主要的文件,运行结果,运行界面,数据库创建的表等图片. 所要创建的文件和要导入的包: 主页面: 显示界面: 数据库的信息: 删除.查找.修改就不一 一列出来,自己可以运行看看.哈哈 2.接下来我将 ...

  7. YZMCMS V5.3后台 SSRF

    当改变命运的时刻降临,犹豫就会败北. 前言 此前在测试过程中遇到过此CMS,久攻不下,于是便尝试代码审计,不得不说这套CMS还是挺安全的,读起来也简单,也适合初学代码审计的同学去阅读,不过漏洞真的不多 ...

  8. RabbitMQ史上最全文章

    老规矩,本篇文章 不做 RabbitMQ 的 编码讲解 ,只介绍 文章学习的一些优秀文章 重点在于不要循规蹈矩,教程 这样走,你不一定要按他这样走,按自己的方式来,学习效率会更高,网上的教程有很多,今 ...

  9. 神奇的BFC

    BFC是什么? 块格式化上下文(Block/box Formatting Context,BFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域 ...

  10. 精尽Spring MVC源码分析 - RequestToViewNameTranslator 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...