扳手中是否可能有高效的时间戳顺序查询?

问题描述 投票:0回答:3

我关注了本文https://cloud.google.com/blog/products/gcp/sharding-of-timestamp-ordered-data-in-cloud-spanner,并创建了一个有点相似的模式,只是没有companyID:

CREATE TABLE Foo (
  random_id       STRING(22) NOT NULL,
  shard_id              INT64 NOT NULL,
  timestamp_order    TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
) PRIMARY KEY(random_id);
CREATE INDEX OrderIndex ON Foo(shard_id, timestamp_order);

shard_id是一个从0到49的随机数。然后我对其进行了一系列选择:

1: SELECT * FROM Foo@{FORCE_INDEX=OrderIndex} where shard_id=0 order by timestamp_order limit 1;
# this correctly scans 1 row

2: SELECT * FROM Foo@{FORCE_INDEX=OrderIndex} where shard_id<1 order by timestamp_order limit 1;
# this scans 192 rows

3: SELECT * FROM Foo@{FORCE_INDEX=OrderIndex} where shard_id BETWEEEN 1 AND 1 order by timestamp_order limit 1;
# this scans 185 rows

4: SELECT * FROM Foo@{FORCE_INDEX=OrderIndex} where shard_id BETWEEN 0 AND 1 order by timestamp_order limit 1;
# this scans 377 rows

我期望这样的事情:

Query #2 should scan 1 row
Query #3 should scan 1 row
Query #4 should scan 2 rows.

问题:我在这里做错了什么?扳手中是否可能有高效的时间戳顺序查询?

sql google-cloud-spanner
3个回答
3
投票

您可以使用HAVING MIN构造高效地执行此查询。

重写#2:

SELECT *
FROM
    (
    SELECT shard_id, ANY_VALUE(random_id HAVING MIN timestamp_order) AS random_id, MIN(timestamp_order) AS timestamp_order
    FROM Foo@{FORCE_INDEX=OrderIndex} 
    GROUP BY shard_id
    WHERE shard_id<1
    )
ORDER BY timestamp_order
LIMIT 1;

效率将来自内部子查询。它应该只为每个shard_id扫描一行,然后从这些行中选择最小值。如果您发现情况并非如此,则可以使用hint来强制执行该行为。

SELECT *
FROM
    (
    SELECT shard_id, ANY_VALUE(random_id HAVING MIN timestamp_order) AS random_id, MIN(timestamp_order) AS timestamp_order
    FROM Foo@{FORCE_INDEX=OrderIndex, GROUPBY_SCAN_OPTIMIZATION=true} 
    GROUP BY shard_id
    WHERE shard_id<1
    )
ORDER BY timestamp_order
LIMIT 1;

对于其他查询,只需替换内部子查询中的过滤条件。


0
投票

[当您指定多个分片ID(或可能产生多个分片ID的表达式)时,此时的理论结果集不再按时间戳排序(而单个分片则是),因此在时间戳上使用(并且必须考虑每一行)。当您指定一个限制时,理论上的优化是从每个分片中获取前N个并合并,但是看起来该优化尚未到位。

您可以通过在每个相关分片上并行运行限制1查询并合并结果,在应用程序层实现此目标。


0
投票

我看到了有关任意LIMIT的评论。这个时间太长,无法跟进评论(不允许),因此我添加了另一个答案。

对于任意LIMIT,需要更复杂的查询才能获得最高效率。这是使用过滤器“ shard_id <1000”和LIMIT x的模板。

联接的第一面将有效地提取合格的shard_id值,就像使用LIMIT 1的原始查询一样。联接的第二面将返回表并为每个shard_id获取前x行。然后,父级将在所有合格shard_is值中选择顶部的x。如果每个shard_id有很多时间戳,那么这将非常有效。

SELECT ff.*
FROM
(
  SELECT shard_id
  FROM Foo@{FORCE_INDEX=OrderIndex, 
  GROUPBY_SCAN_OPTIMIZATION=true}
  WHERE shard_id < 1000
  GROUP BY shard_id
) shards
JOIN UNNEST(ARRAY
(
  SELECT *
  FROM Foo@{FORCE_INDEX=OrderIndex} AS f
  WHERE f.shard_id = shards.shard_id
  ORDER BY timestamp_order
  LIMIT x
)) AS ff
ORDER BY ff.timestamp_order
LIMIT x;
© www.soinside.com 2019 - 2024. All rights reserved.