從作業系統角度看錶空間計算方式
來源:PostgreSQL學徒
前言
今天在某個群裡看到這樣一個問題
“有大佬知道下圖中表空間變化的原因嗎,我新建立了一個表空間,統計大小為0,我將一個表遷移到新表空間,再將表從新表空間遷移到其他表空間,再次統計新表空間的大小為4096,按道理也應該是0,個人認為是Linux的inode的後設資料,剛好是一個塊4096,大佬們怎麼看?
這引起了我的興趣,簡單分析一下。
復現
復現方式很簡單,將某張表挪到指定表空間再挪回去即可,大小是 4096 位元組。
postgres=# create tablespace myspc location '/home/postgres/mytablespace';
CREATE TABLESPACE
postgres=# create table test(id int,info text);
CREATE TABLE
postgres=# insert into test select n,md5(random()::text) from generate_series(1,10) as n;
INSERT 0 10
postgres=# select pg_size_pretty(pg_tablespace_size('myspc'));
pg_size_pretty
----------------
0 bytes
(1 row)
postgres=# alter table test set tablespace myspc ;
ALTER TABLE
postgres=# select pg_size_pretty(pg_tablespace_size('myspc'));
pg_size_pretty
----------------
20 kB
(1 row)
postgres=# alter table test set tablespace pg_default ;
ALTER TABLE
postgres=# select pg_size_pretty(pg_tablespace_size('myspc'));
pg_size_pretty
----------------
4096 bytes
(1 row)
現在表空間下面什麼都沒有了,用 du -sh 可以看到也有個 4KB 大小,看樣子極有可能就是這個!
[postgres@xiongcc ~]$ ls -l mytablespace/PG_16_202307071/
total 0
[postgres@xiongcc ~]$ du -sh mytablespace/PG_16_202307071/
4.0K mytablespace/PG_16_202307071/
[postgres@xiongcc ~]$ du -sh mytablespace/
8.0K mytablespace/
讓我們瞅瞅程式碼,確認一下。
原始碼分析
呼叫邏輯:pg_tablespace_size_oid → calculate_tablespace_size,calculate_tablespace_size 的程式碼很簡單,寥寥十幾行
/*
* Calculate total size of tablespace. Returns -1 if the tablespace directory
* cannot be found.
*/
static int64
calculate_tablespace_size(Oid tblspcOid)
{
char tblspcPath[MAXPGPATH];
char pathname[MAXPGPATH * 2];
int64 totalsize = 0;
DIR *dirdesc;
struct dirent *direntry;
AclResult aclresult;
/*
* User must have privileges of pg_read_all_stats or have CREATE privilege
* for target tablespace, either explicitly granted or implicitly because
* it is default for current database.
*/
if (tblspcOid != MyDatabaseTableSpace &&
!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS))
{
aclresult = pg_tablespace_aclcheck(tblspcOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_TABLESPACE,
get_tablespace_name(tblspcOid));
}
if (tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "base");
else if (tblspcOid == GLOBALTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "global");
else
snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u/%s", tblspcOid,
TABLESPACE_VERSION_DIRECTORY);
dirdesc = AllocateDir(tblspcPath);
if (!dirdesc)
return -1;
while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL)
{
struct stat fst;
CHECK_FOR_INTERRUPTS();
if (strcmp(direntry->d_name, ".") == 0 ||
strcmp(direntry->d_name, "..") == 0)
continue;
snprintf(pathname, sizeof(pathname), "%s/%s", tblspcPath, direntry->d_name);
if (stat(pathname, &fst) < 0)
{
if (errno == ENOENT)
continue;
else
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m", pathname)));
}
if (S_ISDIR(fst.st_mode))
totalsize += db_dir_size(pathname);
totalsize += fst.st_size;
}
FreeDir(dirdesc);
return totalsize;
}
大概流程如下:
檢查使用者是否有讀取表空間的許可權。如果使用者沒有足夠的許可權,函式會報錯。 根據傳入的 tblspcOid
,構造表空間的路徑。這部分考慮了預設表空間和全域性表空間的特殊情況使用 AllocateDir
開啟表空間目錄,並使用ReadDir
遍歷目錄。對於每個目錄項(direntry
),還會做
中斷檢查: CHECK_FOR_INTERRUPTS()
用於處理可能的中斷請求。忽略特殊目錄: 跳過 "." 和 ".." 目錄。 構建完整路徑: 使用 snprintf
構建檔案或目錄的完整路徑。檔案狀態檢查:使用 stat 獲取檔案或目錄的狀態。 如果 stat
失敗且錯誤不是ENOENT
(檔案不存在),則報錯。累計大小:如果是目錄,遞迴計算目錄大小。最後,累加檔案大小到 totalsize
。
那讓我們看下這 4KB 是什麼大小,其實到這裡各位已經知道答案了。
各位可以看到,該檔案的 inode 是 2504420,st_size 是 4096,那麼這個 inode 是什麼呢?讓我們也用 stat 命令看看,沒錯正是 "5" 這個目錄,這個 5 就是資料庫的 oid
[postgres@xiongcc PG_16_202307071]$ stat 5
File: ‘5’
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd01h/64769d Inode: 2504420 Links: 2
Access: (0700/drwx------) Uid: ( 1000/postgres) Gid: ( 1000/postgres)
Access: 2023-11-25 22:54:15.269934346 +0800
Modify: 2023-11-25 22:52:00.034105361 +0800
Change: 2023-11-25 22:52:00.034105361 +0800
Birth: -
因此這個 4096 位元組就是這麼來的。Linux 一切皆檔案吶!
那麼假如你將這個表挪回去,各位應該就能理解為什麼計算出來是 20KB 了吧。
postgres=# select pg_size_pretty(pg_tablespace_size('myspc'));
pg_size_pretty
----------------
20 kB
(1 row)
[root@xiongcc 5]# du -sh *
8.0K 58100
0 58101
8.0K 58102
[root@xiongcc 5]# du -sh ../5
20K ../5
發散
其實這裡還涉及到一些作業系統相關的知識,我們不妨思考一下:
為什麼目錄佔用的空間是 4096?也就是本例的現象? 為什麼空檔案佔用的空間卻是 0? 如果空檔案真佔用 0 byte 空間,那麼該檔案的檔名、建立者以及許可權等資料夾相關的資訊都存到哪兒去了?
[postgres@xiongcc 5]$ touch empty.file ---建立一個空檔案
[postgres@xiongcc 5]$ du -sh empty.file ---大小是0
0 empty.file
可以看到我們使用 du -sh 看到的大小是 0位元組,但是注意,其實空檔案大小並非是"0",而是 du -sh 的計算方式"欺騙"了我們,實際上仍會佔用一個 inode 的大小。
[root@xiongcc 5]# df -i /
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 2621440 242516 2378924 10% /
[root@xiongcc 5]# touch empty.file
[root@xiongcc 5]# df -i / ---inode加1
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 2621440 242517 2378923 10% /
具體 inode 大小可以透過 dumpe2fs 檢視,可以看到是 256 位元組 ??
[root@xiongcc 5]# dumpe2fs -h /dev/vda1 | grep Inode
dumpe2fs 1.42.9 (28-Dec-2013)
Inode count: 2621440
Inodes per group: 8192
Inode blocks per group: 512
Inode size: 256
而對於目錄,目錄也是一個特殊的檔案 (Linux一切皆檔案~) 也會佔用一個 inode + 一個 block size,至於 block size 也可以使用 dumpe2fs 檢視,可以看到,正是 4096 位元組。
[root@xiongcc 5]# dumpe2fs -h /dev/vda1 | grep 'Block size'
dumpe2fs 1.42.9 (28-Dec-2013)
Block size: 4096
[root@xiongcc PG_16_202307071]# stat 5
File: ‘5’
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd01h/64769d Inode: 2504420 Links: 2
Access: (0700/drwx------) Uid: ( 1000/postgres) Gid: ( 1000/postgres)
Access: 2023-11-25 22:57:15.645308113 +0800
Modify: 2023-11-25 23:02:19.443251339 +0800
Change: 2023-11-25 23:02:19.443251339 +0800
Birth: -
那麼空目錄為啥一開始就消耗block了呢,那是因為其必須預設帶兩個目錄項 "." 和 ".." (至少一個 block,就和 PostgreSQL 的 block 一樣,哪怕寫 1 個位元組也會分配一個資料塊),可以看到 PostgreSQL 的程式碼"巧妙"地處理了這個 "." 和 ".."。
小結
其實這個案例更多和作業系統有關,小結一下:
一個空資料夾,首先要消耗掉一個inode,具體大小取決於具體機器,然後加上一個 block 一個空檔案,並不消耗 block 目錄下的檔案/子目錄越多,目錄就需要申請越多的 block
em,一個有趣的案例 ~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2997325/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 計算機作業系統|作業系統引論計算機作業系統
- 計算機作業系統計算機作業系統
- 從區塊鏈的角度看企業協作區塊鏈
- 計算機的作業系統計算機作業系統
- 作業系統(一)作業系統歷史:從標準函式庫到雲端計算作業系統函式
- 聊聊從邏輯閘到作業系統的計算機作業系統計算機
- 計算機重灌Windows作業系統計算機Windows作業系統
- 計算機導論作業系統計算機作業系統
- 計算機作業系統掃盲計算機作業系統
- 作業系統:計算機的生態系統作業系統計算機
- 計算機作業系統之程式掃盲計算機作業系統
- 961計算機作業系統-2021大綱計算機作業系統
- oracle 檢視錶空間Oracle
- 從程式設計師的角度看 12306程式設計師
- 從設計模式角度看OkHttp原始碼設計模式HTTP原始碼
- [譯] 從設計師的角度看 ReduxRedux
- 對於計算機作業系統的認識計算機作業系統
- 從模運算的角度看原碼和補碼
- 從空間角度聊聊如何設計出一個好玩的競技遊戲遊戲
- 【scipy 基礎】--空間計算
- 從網際網路+角度看雲端計算的現狀與未來
- 計算機面試重難點 之 作業系統計算機面試作業系統
- 從原始碼角度看ContentProvider原始碼IDE
- Windows作業系統的基本攻擊方式Windows作業系統
- CIC:從悅刻招股書看電子煙行業增長空間行業
- 計算機作業系統第一章複習計算機作業系統
- Linux EXT4檔案系統TF卡空間容量顯示和計算Linux
- 直播系統程式碼,系統時間從長倒短之間的換算
- 從NewSQL的角度看Apache ShardingSphereSQLApache
- 從JDK原始碼角度看LongJDK原始碼
- 從JDK原始碼角度看IntegerJDK原始碼
- 從JDK原始碼角度看FloatJDK原始碼
- 從 JDK 原始碼角度看 BooleanJDK原始碼Boolean
- 從JNOS商業作業系統,看京東零售商業化之路新探索作業系統
- 臥看雲起時-作業系統篇02作業系統
- 作業系統(一):作業系統概述作業系統
- 作業系統(1)——作業系統概述作業系統
- 計算機體系結構 - 作業1計算機