代码之家  ›  专栏  ›  技术社区  ›  Terence Chow

提高巨大桌子的性能。隔断是否删除id并保留唯一约束?mysql

  •  0
  • Terence Chow  · 技术社区  · 3 年前

    在我的公司,我们有一张桌子变得太大了。它只有4个字段,下面共享索引。请注意,我们的数据是按地区分开的,因此我们在两个独立的数据库中有相同的模式(一个用于NA,一个用于EU)。以下数据是针对欧盟的,但北美的数据通常是欧盟的10倍。

    领域 类型 无效的 钥匙 违约 额外的
    id bigint(20)unsigned 没有 PRI NULL 自动更正
    其他表_id int(11) 没有 MUL NULL
    时间索引 int(11) 没有 NULL
    价值 十进制(18,6) 是的 NULL
    桌子 非唯一(_U) 密钥名称 序列索引 列名称 排序规则 基数 子部件(_P) 拥挤的 无效的 索引类型 议论 索引_注释
    我的表(_T) 0 初级 1. id a. 833155696 NULL NULL BTREE
    我的表(_T) 0 索引其他表和时间索引 1. 其他表_id a. 29755560 NULL NULL BTREE
    我的表(_T) 0 索引其他表和时间索引 2. 时间索引 a. 833155696 NULL NULL BTREE

    请注意,other_table_id是另一个表的id,但它不是在数据库级别强制执行的。(在rails应用程序级别强制执行)。还要注意,非主索引有一个唯一的约束。

    我们在这个表上实际上只有两个查询。我们的插页是以下形式的批量插页:

    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慢速查询日志中看到)。我们正在使用RDS中提供的最大实例AWS,并且我们已经调整了IO/内存。

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

    我想知道删除id字段是否会给我带来重大好处?在这种情况下,插入过程中所需的值会减少一个。我还考虑了基于时间索引的分区,因为大多数查询都使用最近的时间索引。然而,我读到分区会使数据库离线,我会害怕对这么大的数据库进行分区。目前还不清楚数据库会关闭多久。

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

    还有什么其他选择?我们没有读取副本,所以另一种选择是将读取查询移动到读取副本。然而,我不清楚这是否会取得重大胜利,因为我们的问题是更多的桌子大小而不是负载。请注意,EU约有8.33亿行,而NA约有3.9亿行。

    编辑:对于澄清来说,价值是一种科学的衡量标准。我不确定业务是否允许我编辑它。我还在下面的阅读查询中添加了解释查询:

    id select_type 桌子 类型 可能的密钥 钥匙 key_len 参考 额外的
    1. 简单的 我的表(_T) 范围 索引其他表和时间索引 索引其他表和时间索引 8. NULL 3432 使用索引条件

    还添加了对联合建议的解释查询:

    id select_type 桌子 类型 可能的密钥 钥匙 key_len 参考 额外的
    1. 初级 我的表(_T) 范围 索引其他表和时间索引 索引其他表和时间索引 8. NULL 3118 使用索引条件
    2. 联合 我的表(_T) 范围 索引其他表和时间索引 索引其他表和时间索引 8. NULL 314 使用索引条件
    NULL 并集结果 <uni1,2> 所有 NULL NULL NULL NULL NULL 使用临时;使用文件排序

    根据请求,将语句解释为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)))"
                }
            }
        }
    }
    
    0 回复  |  直到 3 年前
        1
  •  0
  •   Rick James diyism    3 年前

    所以,你有 UNIQUE(other_table_id, time_index) 。是的,抛弃auto_increment并将其推广到 PRIMARY

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

    如果 价值 是“科学的”,你需要保留多少个“有效数字”? 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;
    

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

    (我认为分区没有帮助。)