PostgreSQL 随机记录返回 - 300倍提速实践 (随机数组下标代替order by random())

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 标签PostgreSQL , 数组 , 随机 , order by random()背景在业务系统中,有些场景会用到随机返回的功能,例如论坛,有很多帖子(比如有100万贴),有些是精华帖(比如有5万贴),为了让精华帖可以均衡的被访问,需要将5万贴随机的分页返回给用户。

标签

PostgreSQL , 数组 , 随机 , order by random()


背景

在业务系统中,有些场景会用到随机返回的功能,例如论坛,有很多帖子(比如有100万贴),有些是精华帖(比如有5万贴),为了让精华帖可以均衡的被访问,需要将5万贴随机的分页返回给用户。

通常的做法是这样的

select xx from tbl where xx order by random() limit xx;    

传统做法,由于需要随机排序,性能会比较差。

传统做法

1、建表

create table tbl_doc (id int primary key, info text, crt_time timestamp, tag int);     

2、写入测试数据

tag=1表示精选帖子

insert into tbl_doc select id, md5(random()::text), clock_timestamp(), 0 from generate_series(1,1000000) t(id);    
insert into tbl_doc select id, md5(random()::text), clock_timestamp(), 1 from generate_series(1000001,1050000) t(id);    

3、测试

vi test1.sql    
    
begin;    
declare abc cursor for select * from tbl_doc where tag=1 order by random() ;    
fetch 10 from abc;    
end;    

QPS不过200.

pgbench -M prepared -n -r -P 1 -f ./test1.sql -c 64 -j 64 -T 120    
progress: 1.0 s, 190.9 tps, lat 272.908 ms stddev 42.231    
progress: 2.0 s, 212.1 tps, lat 284.494 ms stddev 44.371    
progress: 3.0 s, 211.9 tps, lat 325.806 ms stddev 58.453    
progress: 4.0 s, 172.0 tps, lat 340.460 ms stddev 71.527    

优化方法 - 数组随机

周期性将帖子ID放入数组,查询时从数组取随机位置的ID,再从主表反查,避免排序。

1、创建一个存储周期性最新精选帖子的表(数组ID)。

create table tbl_hot_doc (id serial primary key, ids int[], crt_time timestamp);     

通过这种方法写入当前最新帖子

insert into tbl_hot_doc (ids,crt_time)     
      select array_agg(id), now() from tbl_doc where tag=1     

2、创建一个函数,用于自动的更新最新帖子(根据超时时间),同时返回游标,用户通过游标分页。

create or replace function get_ids (    
  ts_timeout interval,   -- 超时时间    
  rands int,   -- 返回随机多少条    
  refname name  -- 游标名    
) returns refcursor as $$    
declare    
  len int;  -- 数组长度,有多少精华帖    
  hotid int;  -- tbl_hot_id表的最新状态ID    
  ts timestamp;  -- 最新状态的时间    
  hotids int[];  -- 最新状态的数组    
  res refcursor := refname;  -- 游标名    
begin    
-- 获取最新状态    
select array_length(ids,1),id,crt_time,ids into len,hotid,ts,hotids from tbl_hot_doc order by id desc limit 1;     
    
-- 如果没有最新状态,或者最新状态已超时    
if now()-ts >= ts_timeout or not found then    
  -- 如果不存在则设置HOTID=0     
  if not found then hotid := 0; end if;    
  -- 使用ad lock抢锁,只有一个会话拿锁,从原始表tbl_doc生成最新数据并写入tbl_hot_doc    
  if pg_try_advisory_xact_lock(hotid) then     
    insert into tbl_hot_doc (ids,crt_time)     
      select array_agg(id), now() from tbl_doc where tag=1     
      returning array_length(ids,1),id,crt_time,ids into len,hotid,ts,hotids;     
        
    -- 使用这条SQL,用数组下标随机,代替之前的order by random()    
    open res for select * from tbl_doc where id in (select hotids[(random()*(len-1))::int+1] from generate_series(1,rands));    
    return res;    
  end if;     
end if;    
    
open res for select * from tbl_doc where id in (select hotids[(random()*(len-1))::int+1] from generate_series(1,rands));    
return res;     
end;    
$$ language plpgsql strict;    

3、优化后的性能测试

vi test.sql    
begin;    
select * from get_ids(interval '5 min', 1000, 'abc');    
fetch 10 from abc;    
end;    

4、QPS上到6万,提升了300倍。

pgbench -M prepared -n -r -P 1 -f ./test.sql -c 64 -j 64 -T 120    
progress: 1.0 s, 57260.4 tps, lat 1.045 ms stddev 0.211    
progress: 2.0 s, 60444.9 tps, lat 1.059 ms stddev 0.050    
progress: 3.0 s, 60467.5 tps, lat 1.058 ms stddev 0.045    
progress: 4.0 s, 60520.2 tps, lat 1.057 ms stddev 0.039    
progress: 5.0 s, 60561.8 tps, lat 1.057 ms stddev 0.045    

5、如果需要在修改tbl_doc原始数据后,立即反映(不等查询get_ids输入的超时参数),实时更新tbl_hot_doc的值,可以采用类似如下方法

begin;     
update tbl_doc set tag=0 where id=?;     
update tbl_hot_doc set ids=array_remove(ids,?) where id=(select max(id) from tbl_hot_doc) and ids<>array_remove(ids,?);     
end;     

小结

使用本文提供的方法,使得随机数据的查询,性能提升了300倍。

其他方法

还有一些方法,例如将满足条件的数据使用独立分区来存储,然后使用采样的方法来避免排序,但是精确度和性能还是没有办法与先前的方法相比。

postgres=# select * from tbl_doc tablesample BERNOULLI (0.1) where tag=1 limit 10;
   id    |               info               |          crt_time          | tag 
---------+----------------------------------+----------------------------+-----
 1000398 | 7f232df084bf24a71ccaac9f496639bb | 2018-10-09 23:32:45.761598 |   1
 1000806 | 97095bdfdd0ef1d209515728a078ffc3 | 2018-10-09 23:32:45.762841 |   1
 1001949 | d7733a8b4bc5b5b26bb147fab3cc62d6 | 2018-10-09 23:32:45.766116 |   1
 1002227 | 1feb7e972b7206a0ab909cee0a07e41d | 2018-10-09 23:32:45.766917 |   1
 1003609 | 60223d7a74c51cde4a8b017bf9a3e712 | 2018-10-09 23:32:45.770809 |   1
 1006387 | 89c9cb8b4dbd46b3e0c0606e7e1e6947 | 2018-10-09 23:32:45.778873 |   1
 1006533 | f57808f0f15140bbc5429929a4b8d4b5 | 2018-10-09 23:32:45.779258 |   1
 1007279 | 03fedf609f2e85658a98195daabac2b1 | 2018-10-09 23:32:45.781323 |   1
 1007880 | 298bee6992ca255cbc8d0a299f00166a | 2018-10-09 23:32:45.783055 |   1
 1008073 | bf1b8354692cde77765babc56a9f06a2 | 2018-10-09 23:32:45.783605 |   1
(10 rows)

Time: 11.125 ms

分区表时,块级采样无法满足业务需求,因为随机性不够。而非分区时,则引入了IO放大并且可能访问不到足够的满足条件的记录。

《PostgreSQL 任意列组合条件 行数估算 实践 - 采样估算》

《秒级任意维度分析1TB级大表 - 通过采样估值满足高效TOP N等统计分析需求》

《PostgreSQL Oracle 兼容性 之 - 数据采样与脱敏》

《PostgreSQL 巧妙的数据采样方法》

《PostgreSQL 9.5 new feature - table | mview data sample》

参考

《PostgreSQL 随机查询优化》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
存储 SQL Cloud Native
深入了解云原生数据库CockroachDB的概念与实践
作为一种全球领先的分布式SQL数据库,CockroachDB以其高可用性、强一致性和灵活性等特点备受关注。本文将深入探讨CockroachDB的概念、设计思想以及实践应用,并结合实例演示其在云原生环境下的优越表现。
|
3月前
|
Cloud Native 关系型数据库 大数据
CockroachDB:云原生数据库的新概念与实践
本文将介绍CockroachDB,一种先进的云原生数据库,它具备分布式、强一致性和高可用性等特点。我们将探讨CockroachDB的基本原理、架构设计以及在实际应用中的种种优势和挑战。
|
15天前
|
缓存 关系型数据库 MySQL
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
|
15天前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
|
15天前
|
存储 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(避免使用SELECT *、分页查询的优化、合理使用连接、子查询的优化)(上)
MySQL 查询优化:提速查询效率的13大秘籍(避免使用SELECT *、分页查询的优化、合理使用连接、子查询的优化)(上)
|
4月前
|
存储 关系型数据库 MySQL
存储成本最高降至原来的5%,PolarDB分布式冷数据归档的业务实践
国内某家兼具投资理财、文化旅游、票务为一体的大型综合型集团公司,2015年成立至今,由于业务高速发展,业务数据增长非常快,数据库系统屡次不堪重负。该公司数据库运维总监介绍,他们目前业务压力比较大的是票务和订单系统,他们的平台每天新增几千万的订单数据,订单的数据来自于各个终端,近几年每个月以300G的数据规模在高速增长,由于数据不断增加,数据库系统迄今为止迭代过了3次。
|
5月前
|
关系型数据库 PostgreSQL
postgreSQL获取随机数ID
postgreSQL获取随机数ID
|
6月前
|
SQL 缓存 关系型数据库
PolarDB-X 混沌测试实践:如何衡量数据库索引选择能力
随着PolarDB分布式版的不断演进,功能不断完善,新的特性不断增多,整体架构扩大的同时带来了测试链路长,出现问题前难发现,出现问题后难排查等等问题。原有的测试框架已经难以支撑实际场景的复杂模拟测试。因此,我们实现了一个基于业务场景面向优化器索引选择的混沌查询实验室,本文之后简称为CEST(complex environment simulation test)。
|
7月前
|
存储 SQL 关系型数据库
AnalyticDB PostgreSQL构建一站式实时数仓实践
本文介绍通过 AnalyticDB PostgreSQL 版基于实时物化视图,构建流批一体的一站式实时数仓解决方案,实现一套系统、一份数据、一次写入,即可在数仓内完成实时数据源头导入到实时分析全流程。
1874 5
AnalyticDB PostgreSQL构建一站式实时数仓实践
|
8月前
|
分布式数据库 调度 数据库
直播预告 | PolarDB-X 备份恢复原理与实践
备份恢复是生产级数据库必不可少的功能,而PolarDB-X 作为一款分布式数据库,备份数据的全局一致也是最基本的要求。本期分享将介绍PolarDB-X 开源版备份恢复功能的背景与原理,以及如何使用 PolarDB-X Operator 实现备份调度。
直播预告 | PolarDB-X 备份恢复原理与实践

相关产品

  • 云原生数据库 PolarDB