GBase 8a 空洞率,被删除数据的比例,shring space 释放磁盘空间

GBase 8a数据库在数据被删除时,并没有释放磁盘空间,而是只打了一个【已删除】的标记。,这就会导致有效数据,在磁盘上是不连续的,其比例就是空洞率。 在大数据场景,少量空洞是可以接受的,但如果已经对性能有了实质影响,则需要进行shrink space重整。

警告

在方案1的功能完善前,建议老版本使用方案3,2022年以后的新版本使用方案4

排查方案1,通过元数据

数据库的元数据表,提供了查询空洞率的方法,其中的DELETE_RATIO就是空洞率。

在自测中发现【截止2021-12-30】,如果表进行过shrink space 操作成功并确实删掉了seg文件, 则空洞率查询结果是不正确的。后续版本完善后我会更新本文。

select * from performance_schema.tables where table_schema='DBNAME' and table_name='TBNAME';

如下是一个验证场景

场景

gbase> select * from  performance_schema.tables where table_schema='testdb' and table_name='hr';
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
| TABLE_SCHEMA | TABLE_NAME | MAX_ROWID | DELETE_ROWS | TABLE_ROWS | STORAGE_SIZE | DELETABLE_SIZE | SHRINKABLE_SIZE | DELETE_RATIO |
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
| testdb       | hr         |     31216 |           0 |      31216 |       651264 |              0 |               0 |            0 |
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
1 row in set (Elapsed: 00:00:00.00)

gbase> desc hr;
+----------+---------------+------+-----+---------+-------+
| Field    | Type          | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+-------+
| id       | int(11)       | YES  |     | NULL    |       |
| name     | varchar(100)  | YES  |     | NULL    |       |
| joindate | date          | YES  |     | NULL    |       |
| salary   | decimal(10,2) | YES  |     | NULL    |       |
+----------+---------------+------+-----+---------+-------+

删除一部分数据,再查看

gbase> delete from hr where salary > 0.9;
Query OK, 3114 rows affected (Elapsed: 00:00:00.14)

gbase> select * from  performance_schema.tables where table_schema='testdb' and table_name='hr';
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
| TABLE_SCHEMA | TABLE_NAME | MAX_ROWID | DELETE_ROWS | TABLE_ROWS | STORAGE_SIZE | DELETABLE_SIZE | SHRINKABLE_SIZE | DELETE_RATIO |
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
| testdb       | hr         |     31216 |        3114 |      28102 |       659456 |          65785 |               0 |         9.98 |
+--------------+------------+-----------+-------------+------------+--------------+----------------+-----------------+--------------+
1 row in set (Elapsed: 00:00:00.00)

排查方案2,查rowid

在表的分片(gncli),注意不是gccli,执行如下SQL,根据最小行号(小于它的都是被删除的), 最大-最小-count(*) , 表示数据之间的空洞行数。越大证明空的越多。

select min(rowid), max(rowid),count(*)  from XXXX_nX

排查方案3(老版本推荐)

全部在数据节点,分片级别操作。

评估一个DC需要占用的空间

使用现有的metadump工具评估一个DC占用的空间,如果有多个列,需要每个列累计。

如下是查看第一个列C00000.seg的每个DC占用空间的命令,其中的total_size是一个DC(65536行)占用的空间(75007字节)

[root@rh6-1 zxq]# metadump /opt/gnode/userdata/gbase/testdb/sys_tablespace/t1_n1/C00000.seg
This tool is designed for use at GBase internally,and is unsupported externally.
GBase makes no claims and holds no liability regarding the external use of this tool.
This tool is copyrighted and his redistribution is restricted.
/opt/gnode/userdata/gbase/testdb/sys_tablespace/t1_n1/C00000.seg
total_size,75007
optimal_mode,32
no_obj-1,19270
no_nulls,0
max_val,888887
save_checksum,117988097

表有多个列,那就按照顺序,统计每个列的,注意一个列有多个数据文件时,选择其中一个就行,不要重复了。如下是一个统计的脚本,需要在dbaUser(一般是gbase)操作系统用户下运行。参数是库名和表分片名字(带_nX的)

dbname=$1
tbname=$2
data_path=${GBASE_BASE}/userdata/gbase/${dbname}/sys_tablespace/${tbname}
if [ ! -d ${data_path} ]; then
  echo "${dbname}.${tbname} not exists at ${data_path}"
  exit 1;
fi
# 每个列的一个DC大小占用的磁盘容量总计
one_dc_size=0
for segname in `ls ${data_path}/ |cut -d\. -f 1 |uniq`
do
  echo -n Checking $segname' ';
  # 多个segment选择最新的评估
  tmpname=`ls ${data_path}/${segname}.* -t | head -n 1`
  size=`metadump ${tmpname}|grep total_size|cut -d',' -f 2`;
  echo -e "\t"${size}
#  echo ${size};
  let one_dc_size+=size;
#  echo sum=${total};
done;

if [ ${one_dc_size} -le 0 ]; then
  echo "no data file found in ${data_path}";
  exit 2
fi

echo one_dc_disk_size=${one_dc_size}

计算表应占用多少空间

再次强调,这个是分片基本的,不是集群级别的。然后根据当前表的行数,计算出DC数量,根据上一步得到的一个DC占用的磁盘空间,计算出当前DC数,需要占用的空间。

# 表总行数
count=`gncli -uroot -e"select count(*) from ${dbname}.${tbname}" -N`
echo record_count=${count}

# 计算理论磁盘占用
let calc_size=count*one_dc_size/65536
echo calc_disk_size=${calc_size}

计算当前占用空间

# 当前数据文件大小合计
current_Size=`ls  -lt ${data_path} | head -n 1 | cut -d' ' -f 2`
let current_Size*=1024
echo current_disk_size=${current_Size}

计算可释放空间和比例

# 可释放空间
let release=current_Size-calc_size
echo can_release_disk_size=${release}

# 比例
let rate=release*100/current_Size
echo can_relese_disk_rate=${rate} %

完整代码

dbname=$1
tbname=$2
data_path=${GBASE_BASE}/userdata/gbase/${dbname}/sys_tablespace/${tbname}
if [ ! -d ${data_path} ]; then
  echo "${dbname}.${tbname} not exists at ${data_path}"
  exit 1;
fi
# 每个列的一个DC大小占用的磁盘容量总计
one_dc_size=0
for segname in `ls ${data_path}/ |cut -d\. -f 1 |uniq`
do
  echo -n Checking $segname' ';
  # 多个segment选择最新的评估
  tmpname=`ls ${data_path}/${segname}.* -t | head -n 1`
  size=`metadump ${tmpname}|grep total_size|cut -d',' -f 2`;
  echo -e "\t"${size}
#  echo ${size};
  let one_dc_size+=size;
#  echo sum=${total};
done;

if [ ${one_dc_size} -le 0 ]; then
  echo "no data file found in ${data_path}";
  exit 2
fi

echo one_dc_disk_size=${one_dc_size}


# 当前数据文件大小合计
current_Size=`ls  -lt ${data_path} | head -n 1 | cut -d' ' -f 2`
let current_Size*=1024
echo current_disk_size=${current_Size}

# 表总行数
count=`gncli -uroot -e"select count(*) from ${dbname}.${tbname}" -N`
echo record_count=${count}

# 计算理论磁盘占用
let calc_size=count*one_dc_size/65536
echo calc_disk_size=${calc_size}

# 可释放空间
let release=current_Size-calc_size
echo can_release_disk_size=${release}

# 比例
let rate=release*100/current_Size
echo can_relese_disk_rate=${rate} %

运行效果

[root@rh6-1 zxq]# sh 1.sh testdb t1_n1
Checking C00000         131106
one_dc_disk_size=131106
current_disk_size=11206656
record_count=5500007
calc_disk_size=11002867
can_release_disk_size=203789
can_relese_disk_rate=1 %
[root@rh6-1 zxq]#

解决方案4(新版本工具)

新版本的metadump工具,提供了内置的方案3的评估功能,但预计最少是2022年2月份以后的新版本才会版本自带。

命令介绍

metadump -d dbname -t tbname

输出信息包括 表名字、可以清理的空间大小,当前磁盘数据大小,可清理比例

使用样例

[gbase@rh6-1 ~]$ ./metadump -d testdb -t t1_10m_n1
This tool is designed for use at GBase internally,and is unsupported
 externally.

GBase makes no claims and holds no liability regarding the external use of
 this tool.

This tool is copyrighted and his redistribution is restricted.

+------------------------------------------+--------------+
| table_name                               | testdb.t1_10m_n1 |
| The size of data which can be cleaned    | 1966590      |
| The total size of data                   | 11125744     |
| The percent of data which can be cleaned | 17.7 %       |
+------------------------------------------+--------------+

解决方案

解决空洞,唯一的方案就是把数据重新整理一遍,无论哪个方案都避免不了该有的性能消耗,差距就是易用性或者可控性。

比如V95新版本通过重分布的方法,原始表可以继续追加数据和查询,前天的都属于DML/DDL操作,表在操作期间是要持有独占锁,不能在对外DML服务,在V8的早期版本,连查询服务都会被独占锁卡住,在V95版本里不会阻塞查询。

通过重建表

该方案最简单也最安全,建1个新表,把老的表的数据全部搬过去,然后rename表名字。

详情可以参考 GBase 8a安全重建表的一个方案

通过shink space

可以通过shrink space [full] 来由数据库内部进行重建。

其中带FULL的功能,在早期的版本里有BUG,有可能造成数据文件损坏。 建议咨询GBase 技术支持人员确认使用的版本是否已经解决。 建议升级到V95新版本,采用rebalance的原理重建。

有关Shrink space的功能,请参考 GBase 8a集群删除数据后释放磁盘空间shrink space