优化拥有近 1.5 亿条记录的 Postgres 数据库
Optimize Postgress DB having nearly 150 mil records
我正在使用 Azure Postgress DB 存储大约 1.5 亿条记录。数据库的规格是:160GB 存储和大约 4GB RAM。加载到数据库中时,数据集约为 80GB。数据集不会增长,但会保持在 80 - 85 GB 左右。
这是 table 的定义:
CREATE TABLE properties(
PropertyId bigint,
Address text,
Latitude double precision,
Longitude double precision,
Rooms int,
BathRooms int
)
还有几列
大部分查询都基于以下 4 个字段:
Address (text)
Longitude (double)
Latitude (double)
PropertyId (big int)
我已经在所有这些字段上实现了索引。对于地址 - GIN,其他 B-Tree。
CREATE INDEX address_idx ON properties USING GIN (Address gin_trgm_ops);
CREATE INDEX propertyid_idx ON properties(PropertyId);
CREATE INDEX latitude_idx ON properties(Latitude);
CREATE INDEX longitude_idx ON properties(Longitude);
但问题仍然是查询速度很慢,即
select * from my_table
where Latitude between x and y
and Longitude between p and q
and address like '%address%';
需要几分钟...
我使用 explain analyse 分析了查询,结果显示查询确实使用了索引。
"Bitmap Heap Scan on properties (cost=34256.04..34901.54 rows=10 width=561) (actual time=24664.562..32007.752 rows=35 loops=1)"
" Recheck Cond: ((Address ~~ '%3365%'::text) AND (Longitude >= '-90.5'::double precision) AND (Longitude <= '-90'::double precision))"
" Rows Removed by Index Recheck: 1123"
" Filter: ((propertylatitude >= '38'::double precision) AND (propertylatitude <= '39'::double precision))"
" Rows Removed by Filter: 64"
" Heap Blocks: exact=1213"
" Buffers: shared hit=181 read=6478"
" I/O Timings: read=31160.388"
" -> BitmapAnd (cost=34256.04..34256.04 rows=161 width=0) (actual time=24660.058..24660.059 rows=0 loops=1)"
" Buffers: shared hit=169 read=5277"
" I/O Timings: read=23836.224"
" -> Bitmap Index Scan on address_idx (cost=0.00..135.75 rows=12233 width=0) (actual time=6892.077..6892.077 rows=12973 loops=1)"
" Index Cond: (Address ~~ '%3365%'::text)"
" Buffers: shared hit=168 read=321"
" I/O Timings: read=6815.544"
" -> Bitmap Index Scan on longitude_idx (cost=0.00..34120.04 rows=1627147 width=0) (actual time=17763.265..17763.265 rows=1812752 loops=1)"
" Index Cond: ((Longitude >= '-90.5'::double precision) AND (Longitude <= '-90'::double precision))"
" Buffers: shared hit=1 read=4956"
" I/O Timings: read=17020.681"
"Planning Time: 0.267 ms"
"Execution Time: 32008.085 ms"
所以我的问题是,
- 有什么方法可以提高性能(SQL 明智)?不同的索引算法或策略?
- 在给定数据大小的情况下计算内存和存储要求时是否有经验法则?在 2 秒内获得结果的最低硬件要求是什么?
查询使用了索引,估计没问题。
你的时间几乎都花在了 I/O 上,而你的 I/O 速度大约是每秒 2MB,这太糟糕了。也许您的 I/O 子系统超载了;你可能想检查一下。
使用这种存储方式,您唯一的机会就是将整个 table 及其索引缓存在 RAM 中。或者您可以获得更快的存储空间。
longitude_idx 上的扫描速度慢得惊人。为 btree 索引上的简单范围扫描完成的 IO 通常大部分是顺序的,除非该索引随着时间的推移发生了非常大的周转率,因此变得非常碎片化。但是也许您拥有此数据库的任何存储 class 都没有实施预读优化,因此顺序扫描没有随机读取的特权。
您可以尝试重新索引 table 以查看是否有所不同。将 table 聚类在纬度或经度上可能是有意义的,这应该会自动重新索引它,但您可能没有足够的 scratch space 来执行此操作。
我正在使用 Azure Postgress DB 存储大约 1.5 亿条记录。数据库的规格是:160GB 存储和大约 4GB RAM。加载到数据库中时,数据集约为 80GB。数据集不会增长,但会保持在 80 - 85 GB 左右。
这是 table 的定义:
CREATE TABLE properties(
PropertyId bigint,
Address text,
Latitude double precision,
Longitude double precision,
Rooms int,
BathRooms int
)
还有几列
大部分查询都基于以下 4 个字段:
Address (text)
Longitude (double)
Latitude (double)
PropertyId (big int)
我已经在所有这些字段上实现了索引。对于地址 - GIN,其他 B-Tree。
CREATE INDEX address_idx ON properties USING GIN (Address gin_trgm_ops);
CREATE INDEX propertyid_idx ON properties(PropertyId);
CREATE INDEX latitude_idx ON properties(Latitude);
CREATE INDEX longitude_idx ON properties(Longitude);
但问题仍然是查询速度很慢,即
select * from my_table
where Latitude between x and y
and Longitude between p and q
and address like '%address%';
需要几分钟...
我使用 explain analyse 分析了查询,结果显示查询确实使用了索引。
"Bitmap Heap Scan on properties (cost=34256.04..34901.54 rows=10 width=561) (actual time=24664.562..32007.752 rows=35 loops=1)"
" Recheck Cond: ((Address ~~ '%3365%'::text) AND (Longitude >= '-90.5'::double precision) AND (Longitude <= '-90'::double precision))"
" Rows Removed by Index Recheck: 1123"
" Filter: ((propertylatitude >= '38'::double precision) AND (propertylatitude <= '39'::double precision))"
" Rows Removed by Filter: 64"
" Heap Blocks: exact=1213"
" Buffers: shared hit=181 read=6478"
" I/O Timings: read=31160.388"
" -> BitmapAnd (cost=34256.04..34256.04 rows=161 width=0) (actual time=24660.058..24660.059 rows=0 loops=1)"
" Buffers: shared hit=169 read=5277"
" I/O Timings: read=23836.224"
" -> Bitmap Index Scan on address_idx (cost=0.00..135.75 rows=12233 width=0) (actual time=6892.077..6892.077 rows=12973 loops=1)"
" Index Cond: (Address ~~ '%3365%'::text)"
" Buffers: shared hit=168 read=321"
" I/O Timings: read=6815.544"
" -> Bitmap Index Scan on longitude_idx (cost=0.00..34120.04 rows=1627147 width=0) (actual time=17763.265..17763.265 rows=1812752 loops=1)"
" Index Cond: ((Longitude >= '-90.5'::double precision) AND (Longitude <= '-90'::double precision))"
" Buffers: shared hit=1 read=4956"
" I/O Timings: read=17020.681"
"Planning Time: 0.267 ms"
"Execution Time: 32008.085 ms"
所以我的问题是,
- 有什么方法可以提高性能(SQL 明智)?不同的索引算法或策略?
- 在给定数据大小的情况下计算内存和存储要求时是否有经验法则?在 2 秒内获得结果的最低硬件要求是什么?
查询使用了索引,估计没问题。
你的时间几乎都花在了 I/O 上,而你的 I/O 速度大约是每秒 2MB,这太糟糕了。也许您的 I/O 子系统超载了;你可能想检查一下。
使用这种存储方式,您唯一的机会就是将整个 table 及其索引缓存在 RAM 中。或者您可以获得更快的存储空间。
longitude_idx 上的扫描速度慢得惊人。为 btree 索引上的简单范围扫描完成的 IO 通常大部分是顺序的,除非该索引随着时间的推移发生了非常大的周转率,因此变得非常碎片化。但是也许您拥有此数据库的任何存储 class 都没有实施预读优化,因此顺序扫描没有随机读取的特权。
您可以尝试重新索引 table 以查看是否有所不同。将 table 聚类在纬度或经度上可能是有意义的,这应该会自动重新索引它,但您可能没有足够的 scratch space 来执行此操作。