考虑下表,该表记录了属于不同类别的不同公司的不同产品的价格变化。
Column | Type | Modifiers
-----------------+--------+-----------
category_id | bigint | not null
product_id | bigint | not null
industry_id | bigint | not null
time | bigint | not null
price | bigint | not null
product_info | json | not null
Indexes:
"price_change_pk" PRIMARY KEY, btree (category_id, product_id, price, "time")
Foreign-key constraints:
"orders_industry_id" FOREIGN KEY (industry_id) REFERENCES industry_info(industry_id)
"orders_product_id" FOREIGN KEY (product_id) REFERENCES device_info(product_id)
"orders_category_id" FOREIGN KEY (categoy_id) REFERENCES category_info(category_id)
要明确的列值将是:
category_id
- 一个单独的表将 id(唯一的 bigint 值)映射到类别名称 - 100 个类别
(电子、时尚、健康、运动、玩具、书籍)
industry_id
- 一个单独的表将 id(唯一的 bigint 值)映射到行业名称 - 一个类别中的数千个行业
(诺基亚、苹果、微软、PeterEngland、Rubik、Nivia、中远)
product_id
- 一个单独的表格将 id(唯一的 bigint 值)映射到产品名称 - 一个行业中的数百万种产品
time
(unix time as bigint) - 价格被修改的时间,
price
- 数千个不同的值 - (200, 10000, 14999, 30599, 450)
product_info
- 包含产品额外详细信息的 json(键/值对的数量可能会有所不同)
{seller:"ABC Assured", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon}
以多种方式查询该表以分析产品价格在一天/周/月作为小时/天/周/月范围内变化的趋势,作为图表。趋势可能基于否。的产品,正在修改的独特产品。
例如谷歌样本趋势
按原样(as string
)存储 JSON 会占用更多存储空间。因此,我尝试在 json 中存储键值,并在单独的表中使用递增的序列 ID,并使用这些 ID。
喜欢
Keys (citext, bigint)
seller - 1
discount - 2
model - 3
EMIoption - 4
EMIvalue - 5
festival_offer - 6
...
...
currency - 25
Values (citext, bigint)
ABC Assured - 1
10 - 2
XYZ - 3
true - 4
12 - 5
28 - 6
comingsoon - 7
...
...
ZYX - 106
rupees - 107
american dollars - 108
canadian dollars - 109
Prime seller - 110
{seller:"ABC Assured", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon, curreny: rupees}
变成
{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":107}
{seller:"Prime seller", discount:10, model:XYZ, EMIoption:true, EMIvalue:12, festival_offer:28, market_stat:comingsoon, curreny: "canadian dollars"}
变成
{"1":110, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":109}
对于大约 20M 的数据集,它减少了大约 1.5GB。
增加键值基数,增加序列号。所以我尝试将十进制存储为十六进制。
{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "25":107}
变成
{"1":1, "2":2", "3":3, "4":4, "5":5, "6":6, "7":7, "19":"6B"}
{"1":110, "2":2", "3":106, "4":4, "5":5, "6":6, "7":7, "25":109}
变成
{"1":, "2":2", "3":"6A", "4":4, "5":5, "6":6, "7":7, "19":"6D"}
将这些十进制整数存储为十六进制整数也是如此。
- 进一步节省存储空间?(因为在视觉上它似乎被压缩了)
- JSON是保留key-value的数据类型,还是存储为字符串?
- 使数据压缩?
- 提高读取性能?
- 或者无论如何它可以改进?(索引,或任何?)
在普通的 psql 应用程序中,查询需要几分钟才能完成。由于它符合时间序列数据,我们使用 TimescaleDB 扩展,它的分片机制提高了查询执行,但我们需要亚秒级的结果。
查询样本: 每天检查给定类别中的所有产品的价格更改为 500 的次数,按月分组。
select count(*), to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as unit, price
from price_change
where category_id = 1000000010 and time between 1514745000000 and 1517423400000
and price = 500
group by price, unit;
检查在过去 10 个月中按月分组的给定类别中的所有产品的价格更改为 (100,200,300,400,500,600,700,800,900,1000) 中的任何一个的次数。
select count(*), to_char(date_trunc('month', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as unit, price
from price_change
where category_id = 1000000010 and time between 1514745000000 and 1517423400000
and price in (100,200,300,400,500,600,700,800,900,1000) group by price, unit;
在给定的类别中选择在给定时间范围内价格已更改的产品详细信息
select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp
from price_change
join products using product_id
where price_change.category_id = 1000000010
and price_change.time between 1514745000000 and 1517423400000;
在给定类别中选择价格在给定时间范围内发生变化的行业和产品 ID 详细信息
select industry_id, product_id, price
from price_change
join industries using industry_id
where price_change.category_id = 1000000010
and price_change.time between 1514745000000 and 1517423400000;
选择产品价格变化详情,在折扣 10% 的时间范围内,在特定类别中
select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp
from price_change
join products using product_id
where price_change.category_id = 1000000010
and price_change.time between 1514745000000 and 1517423400000
and product_info->>'discount'=10;
选择产品价格变化详情,在特定卖家销售的时间范围内,在特定类别中
select product_id, product_name, price, to_char(date_trunc('day', to_timestamp(time/1000) at time zone 'Asia/Kolkata'), 'YYYY/MM/DD') as timestamp
from price_change
join products using product_id
where price_change.category_id = 1000000010
and price_change.time between 1514745000000 and 1517423400000
and product_info->>'seller'='ABC Assured';
在大多数情况下,查询不会包含category_id
在选择列中。