【问题标题】:Partitioning by range columns unexpected behavior按范围列分区意外行为
【发布时间】:2021-10-24 16:27:25
【问题描述】:

我的 MySQL 表按范围列(c_id 和 created_at)分区 我创建了 2 个分区:

  1. logs_1_2020(c_id 小于 2 且创建时间小于 2021-01-01 00:00:00)

  2. logs_1_2021(c_id 小于 2 且创建时间小于 2022-01-01 00:00:00)

当我跑步时

INSERT INTO example_log_table (c_id, data, created)
    VALUES (1, 'test', '2021-10-24 18:16:08')

我应该找到存储在logs_1_2021中的结果,但是当我在logs_1_2020中找到她时,我很震惊。

有人对此有解释吗?

此表 SQL 生成器:

CREATE TABLE example_log_table (
                        id int auto_increment ,
                        c_id int,
                        data TEXT NOT NULL,
                        created DATETIME NOT NULL,
                        primary key (id,c_id,created)
) PARTITION BY RANGE columns (c_id,created)(
    PARTITION logs_1_2020 VALUES LESS THAN  (2,'2021-01-01 00:00:00'),
    PARTITION logs_1_2021 VALUES LESS THAN  (2,'2022-01-01 00:00:00')
);

【问题讨论】:

    标签: mysql partitioning data-partitioning


    【解决方案1】:

    当您使用多列作为分区键时,放置基于元组比较。您可以通过这种方式测试一个元组是否小于另一个元组(MySQL 8.0):

    select row(1, '2021-10-24 18:16:08') < row(2, '2021-01-01 00:00:00');
    +---------------------------------------------------------------+
    | row(1, '2021-10-24 18:16:08') < row(2, '2021-01-01 00:00:00') |
    +---------------------------------------------------------------+
    |                                                             1 |
    +---------------------------------------------------------------+
    

    元组不等式比较的规则很棘手。我建议你仔细阅读https://dev.mysql.com/doc/refman/8.0/en/partitioning-columns-range.htmlhttps://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_less-than

    对于行比较,(a, b) &lt; (x, y) 相当于:

    (a < x) OR ((a = x) AND (b < y))
    

    在这种情况下,1 小于2,因此您插入的元组小于定义分区上限的元组logs_1_2020

    如果您要使用ORDER BY c_id,created 查询一组行,您还可以考虑如何对数据进行排序。它将首先按c_id 排序,然后仅在c_id 上的平局情况下才会按created 对平局进行排序。

    【讨论】:

    • 哇,它正在使用元组比较!有没有办法让它根据列值将数据插入分区?示例:我希望“logs_1_2021”分区的所有数据 c_id 小于 2 并且创建的数据小于“2022-01-01 00:00:00”
    • 在 PostgreSQL 中,当我使用 PARTITION BY RANGE (c_id, created_at) 并创建分区 FOR VALUES FROM (1, '2020-01-01 00:00:00') TO (1, '2021-01-01 00:00:00') 和另一个 FOR VALUES FROM (1, '2021-01-01 00:00:00') TO (1, '2022-01-01 00:00: 00'') 尝试插入时 (1,'2021-10-01 00:00:00') 将插入第二个分区而不是其他分区,因为该分区是为这些值创建的,我需要在 Mysql 上进行此行为
    【解决方案2】:

    除非您有充分的理由进行分区,否则将其删除并将索引更改为

    PRIMARY KEY(c_id, created, id),
    INDEX(id)
    

    如果您希望拥有大量数据并希望删除“旧数据”,PARTITION BY RANGE 只需 created;这有助于定期DROP PARTITION。并且上面的两个索引仍然有效且有用。

    【讨论】:

      【解决方案3】:

      在搜索了很多之后有没有办法让Mysql根据列值而不是元组比较将数据插入分区 我决定让我的分区像这样:

              PARTITION logs_1_2020 VALUES LESS THAN  (1,'2021-01-01'),
              PARTITION logs_2_2020 VALUES LESS THAN  (2,'2021-01-01'),
              PARTITION logs_2_2021 VALUES LESS THAN  (2,'2022-01-01')
      

      并且在插入时,我会插入精确的第一个参数,以使 Mysql 比较第二个参数是否更少。

      所以当运行时:

      INSERT INTO example_log_table (c_id, created) VALUES (2, '2021-10-21')
      

      将在 logs_2_2021 插入,因为 c_id 匹配并且创建的小于第二行的创建

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多