暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Postgresql数据抽样方法

原创 梧桐 2023-08-17
995

##概述

数据抽样在数据处理方面经常用到,特别是当表数据量比较大时,随机查询表中一定数量记录的操作很常见,通常通过order by random()方式实现数据抽样,这种方式虽然在功能上满足随机返回指定行数据, 但性能很低,如下所示:

select * from test order by random() limit 1;

**order by random()方式适合取固定数量的抽样场景。**表test数据量为100万, 从100万随机取一条,上述sql的执行时间为Time: 180.445 ms,这种方法进行了全表扫描和排序,效率非常低,当表数据量更大时,性能几乎无法接受。

以后PostgreSQL支持tablesample数据抽样,语法如下:

select from table name tablesample sampliog_method (argument[,...))[repeatable(seed))

sampling_ method指抽样方法, 主要有两种:system和bernoulli,

接下来详细介绍这两种抽样方式, argument指抽样百分比。

system抽样方式

system抽样方式为随机抽取表上数据块上的数据,理论上被抽样表的每个数据块被检索的概率是一样的,system抽样方式基于数据块级别,后接抽样参数,被选中的块上的所有数据将被检索,例如。
创建test测试表,并插入175万条数据,如下所示:

抽样因子设置成0. 01, 意味着返回175万 × 0.01%=175条记录,执行如下sql:

执行计划如下所示:

explain analyze select * from test tablesample system(0.01);
                                             QUERY PLAN
------------------------------------------------------------------------------------------------------

 Sample Scan on test  (cost=0.00..5.75 rows=175 width=53) (actual time=0.012..0.058 rows=389 loops=1)
   Sampling: system ('0.01'::real)
 Planning time: 0.031 ms
 Execution time: 0.078 ms
(4 rows)

以上执行计划主要有两点,一方面进行了samplescan扫描(抽样方式为system),执行时间为 0.029 ms,性能较好,另一方面优化器预计访问96条记录,实际返回389,为什么会返回175条记录呢?接着查看表占用的数据块数量,如下所示:

select relname,relpages from pg_class where relname='test'; relname | relpages ---------+---------- test | 4919 (1 row) Time: 0.366 ms

表test物理上占用4919个数据块,也就是说每个数据块存储 175万/4919=389条记录。
查看抽样数据的ctid,如下所示:

select ctid,* from test tablesample system(0.01);
 ctid    |  id  | testid | personid |              imgurl              |          rectime           
-----------+------+--------+----------+----------------------------------+----------------------------
 (936,1)   | 2086 |   6877 |     3399 | c9cf294ec186bd458b50124badd6f91e | 2021-03-30 22:12:45.743534
 (936,2)   | 3206 |   8911 |     4121 | 42934ee86a0f73a7618f151e4d5fa502 | 2021-03-30 22:12:45.744686
 (936,3)   | 3429 |    707 |     8722 | e2d9bb0fbdac00940115de31bb85b0d7 | 2021-03-30 22:12:45.745712
 (936,4)   | 1734 |   9222 |     6850 | e61542b9e7d0fcae6b150275c4caf251 | 2021-03-30 22:12:45.746354
 (936,5)   | 2794 |   4940 |     5248 | 9b08f63f0b9fbc869888a927a74ea1cd | 2021-03-30 22:12:45.747393
...

ctid是表的隐藏列,括号里的第一位表示逻辑数据块编号,第二位表示逻辑块上的数据的逻辑编号,从以上看出,这107条记录都存储在逻辑编号为5640的数据块上,也就是说抽样查询返回了一个数据块上的所有数据,抽样因子固定为0.01,多次执行以下查询,如下所示:

select count(*) from test tablesample system(0.01); count ------- 389 (1 row)

再次查询发现返回的记录为214或107,由于一个数据块存储107条记录,因此查询结果有时返回了两个数块上的所有数据,这是因为抽样因子设置成0. 01,意味着返回1750000 × 0.01%=175条记录, 150条记录需要两个数据块存储,这也验证了system抽样方式返回的数据以数据块为单位,被抽样的块上的所有数据被检索。

再次查询发现返回的记录为389或0,由于一个数据块存储107条记录,因此查询结果有时返回了两个数块上的所有数据,这是因为抽样因子设置成0. 01,意味着返回1750000 × 0.01 %=175条记录,175条记录需要两个数据块存储,这也验证了system抽样方式返回的数据以数据块为单位,被抽样的块上的所有数据被检索。

时而为0,不靠谱

bernoulli抽样方式

bernoulli抽样方式随机抽取表的数据行,并返回指定百分比数据,bernoulli抽样方式基于数据行级别,理论上被抽样表的每行记录被检索的概率是一样的,因此bernoulli抽样方式抽取的数据相比system抽样方式具有更好的随机性,但性能上比system抽样方式低很多。例如:
设置抽样方式为bernoulli,抽样因子为0.01,如下所示:

explain analyze select * from test tablesample bernoulli(0.01); query plan Sample Scan on test (cost=0.00..4920.75 rows=175 width=53) (actual time=0.100..19.999 rows=185 loops=1) Sampling: bernoulli ('0.01'::real) Planning time: 0.031 ms Execution time: 20.034 ms (4 rows) Time: 21.139 ms

从以上执行计划看出进行了samplescan扫描(抽样方式为bernoulli), 执行计划预计返回150条记录,实际返回152条, 从返回的记录数来看,非常接近175条
( 17500 000×0.01%),但执行时间却要22. 569毫秒,性能相比system抽样方式0.366毫秒差了57倍。
多次执行以下查询,查看返回记录数的变化,如下所示:

select ctid,* from test tablesample bernoulli(0.01) limit 3; ctid | id | testid | personid | imgurl | rectime ----------+------+--------+----------+----------------------------------+---------------------------- (23,53) | 6684 | 7804 | 6901 | b4201e76e54968516862e174b6a2efea | 2021-03-30 22:12:39.074777 (24,140) | 9760 | 8542 | 9916 | 9597f35e5689d9aa030bbf563f1250de | 2021-03-30 22:12:39.071712 (48,26) | 1414 | 3728 | 895 | f0a9d97f2f71dae6b72c0d9e06bb6ead | 2021-03-30 22:12:39.057819 (3 rows) Time: 0.807 ms

从以上三条记录的ctid信息看出, 三条数据分别位于数据块23、 24、 48上,因此,bernoulli抽样方式随机性相比system抽样方式更好。

抽样方式 基于级别 随机性 性能
system抽样 数据块 不好 性能很高
bernoulli抽样 数据行 好(伯努利抽样基本特性:总体中的所有元素被抽样的可能性均等) 性能相比system差很多

https://en.wikipedia.org/wiki/Bernoulli_distribution

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论