【问题标题】:Improving performance for huge table. Partition? Dropping an id and keeping a unique constraint instead? mysql提高大表的性能。分割?删除 id 并保持唯一约束? mysql
【发布时间】:2022-01-20 23:11:40
【问题描述】:

在我的公司,我们有一张太大的桌子。它只有 4 个字段,索引在下面共享。请注意,我们的数据是按地区分隔的,因此我们在 2 个单独的数据库中具有相同的架构(一个用于 NA,一个用于 EU)。以下数据适用于欧盟,但北美的数据通常是欧盟的 10 倍。

Field Type Null Key Default Extra
id bigint(20) unsigned NO PRI NULL auto_increment
other_table_id int(11) NO MUL NULL
time_index int(11) NO NULL
value decimal(18,6) YES NULL
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
my_table 0 PRIMARY 1 id A 833155696 NULL NULL BTREE
my_table 0 index_other_table_and_time_index 1 other_table_id A 29755560 NULL NULL BTREE
my_table 0 index_other_table_and_time_index 2 time_index A 833155696 NULL NULL BTREE

请注意,other_table_id 是另一个表的 id,但它不是在 db 级别强制执行的。 (在 Rails 应用程序级别强制执行)。另请注意,非主索引对其具有唯一性约束。

我们对这张表只有 2 个查询。我们的插入是以下形式的批量插入:

INSERT INTO my_table (value, time_index, other_table_id) 
VALUES (1,2,3),(4,5,6)...<5000-10000 tuples later>... (7,8,9) 
ON DUPLICATE KEY UPDATE 
my_table.value=VALUES(value), 
my_table.time_index=VALUES(time_index), 
my_table.other_table_id=VALUES(other_table_id);

我们的查询如下:

select other_table_id, time_index, value from my_table where
(other_table_id in (<3000 values>) and time_index between 5800 and 6050 )
or (other_table_id in (<300 values>) and time_index between 800 and 830)
order by other_table_id, time_index;

此表自 2006 年以来不断增长。我们经常看到查询和插入时间 > 60 秒。 (可以在mysql慢查询日志中看到)。我们正在使用 AWS 在 RDS 中提供的最大实例,并且我们已经调整了 IO/内存。

因此,我正在尝试考虑其他方法来提高性能。

我想知道我是否会从删除 id 字段中获得显着的好处?在这种情况下,插入期间所需的值将少一个。我还考虑过基于时间索引的分区,因为大多数查询都使用最近的时间索引。但是我读过分区会使数据库脱机,我会害怕对这么大的数据库进行分区。目前还不清楚数据库会关闭多长时间。

删除 id 字段会给我带来什么重大好处吗?分区会给我带来很大的好处吗?如果是这样,有没有办法在不停机的情况下进行分区?注意我必须在分区之前删除 id 字段,因为 mysql 要求所有索引键都在分区中。我正在考虑在 time_index 上进行分区。

还有哪些其他选择?我们没有只读副本,因此另一种选择是将读取查询移动到只读副本。但是,我不清楚这是否会获得重大胜利,因为我们的问题是更大的表大小而不是加载。请注意,欧盟有约 8.33 亿行,而 NA 有约 3.9B 行。

编辑:澄清价值是一种科学测量。我不确定公司是否允许我对其进行编辑。我还在下面的读取查询中添加了解释查询:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE my_table range index_other_table_and_time_index index_other_table_and_time_index 8 NULL 3432 Using index condition

还在联合建议上添加解释查询:

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY my_table range index_other_table_and_time_index index_other_table_and_time_index 8 NULL 3118 Using index condition
2 UNION my_table range index_other_table_and_time_index index_other_table_and_time_index 8 NULL 314 Using index condition
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL Using temporary; Using filesort

按照要求解释为json:

{
    "query_block": {
        "select_id": 1,
        "ordering_operation": {
            "using_filesort": false,
            "table": {
                "table_name": "my_table",
                "access_type": "range",
                "possible_keys": [
                    "index_other_table_and_time_index"
                ],
                "key": "index_other_table_and_time_index",
                "used_key_parts": [
                    "other_table_id",
                    "time_index"
                ],
                "key_length": "8",
                "rows": 3432,
                "filtered": 100,
                "index_condition": "(((`mydb`.`my_table`.`other_table_id` in (...list of values)) and (`mydb`.`my_table`.`time_index` between 5800 and 6050)) or ((`mydb`.`my_table`.`other_table_id` in (...list of values...)) and (`mydb`.`my_table`.`time_index` between 800 and 830)))"
            }
        }
    }
}

【问题讨论】:

    标签: mysql performance amazon-rds


    【解决方案1】:

    所以,你有UNIQUE(other_table_id, time_index)。是的,放弃 auto_increment 并将其提升为 PRIMARY

    是有意义的

    value 的语义是什么?如果是钱,那你真的需要持有一万亿美元/欧元,精确到小数点后 6 位吗?

    如果value 是“科学”,您需要保留多少个“有效数字”? FLOAT(4 个字节)将为您提供大约 7 个,具有广泛的“范围”。这比你的 9 字节 DECIMAL 小得多。

    IODKU 的措辞似乎“不正确”。如果它决定“更新”(而不是“插入”),则不需要这对:

    my_table.time_index=VALUES(time_index), 
    my_table.other_table_id=VALUES(other_table_id);
    

    这样您的查询会运行得更快:

        (   SELECT  other_table_id, time_index, value
                 from  my_table
                WHERE  other_table_id in (<3000 values>)
                  and  time_index between 5800 AND 6050 
        )
        UNION  
        (   SELECT  other_table_id, time_index, value
                 from  my_table
                WHERE  other_table_id in (<300 values>)
                  and  time_index between 800 AND 830 
        )
        order by  other_table_id, time_index;
    

    因为它可以使用索引两次而不是进行全表扫描(近十亿行)。由于PK的变化,它也会更快。

    (我认为分区不会有帮助。)

    【讨论】:

    • 价值不是货币价值。这是一个科学的测量。我认为企业不会允许这种情况发生变化。关于插入,这是一个很好的捕获。另外关于读取查询,这是另一个很好的收获。我会试试这些,看看它是否会有所改变。还有,更新主键,是不是像ALTER TABLE my_table DROP COLUMN id, ADD PRIMARY KEY (other_table_id, time_index);这么简单?只是想知道添加主键后是否需要删除唯一约束。感谢您的建议!
    • 我对读取查询进行了解释,有无联合。我认为不会有任何好处。请看一下,如果您同意,请告诉我。我编辑了问题以添加解释查询
    • @TerenceChow - 我很惊讶OR 生成了一行EXPLAIN。如果允许,请提供确切的查询和EXPLAIN FORMAT=JSON SELECT ...(您可以省略大部分“3000 值”。)同时,我添加到我的答案中,回复value
    • 增加了json格式的解释
    • 添加INDEX(time_index)应该不会有什么坏处;它可能会有所帮助。
    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 2021-08-03
    • 2015-05-07
    • 2012-09-02
    • 2012-01-20
    相关资源
    最近更新 更多