检查和插入记录的最佳方法

时间:2010-05-21 18:32:17

标签: mysql performance

编辑:澄清最初来自平面文件数据库且不在MySQL数据库中的记录。

在我们现有的一个C程序中,其目的是从平面文件中获取数据并将它们(基于标准)插入到MySQL表中:

Open connection to MySQL DB
for record in all_record_of_my_flat_file:
  if record contain a certain field:
    if record is NOT in sql_table A: // see #1
      insert record information into sql_table A and B // see #2
Close connection to MySQL DB
  1. 从sql_table A中选择字段,其中field = XXX
  2. 2个插入
  3. 我认为管理层认为添加功能并不值得,因此当创建平面文件中的字段时,它将被插入到数据库中。这是特定于一个客户(我知道)。我也觉得奇怪,我们使用这样的工具来“同步”数据。我被赋予了使用和维护这个脚本的职责,所以我没有听到太多关于整个过程的内容。目的是主要处理其他记录,因此这不是第一次使用。

    这通常每X个月完成,以便同步所有内容,或者我被告知。我也被告知这个过程大约需要几天时间。 (目前)有至多250万条记录(虽然不一定都会插入所有2.5米的记录,而且很可能更少)。其中一个表包含10个字段,另外5个字段。迭代记录没有太多工作要做,因为此时此部分无法更改。我想做的是加快我查询MySQL的部分。

    我不确定我是否遗漏了任何重要细节 - 请告诉我!我也不是SQL专家所以请随意指出显而易见的事情。

    我想到了:

    1. 将所有插入内容放入一个事务中(目前我不确定该事务对于全部或全部或者如果这会影响性能有多重要)
    2. 使用插入X不存在Y
    3. LOAD DATA INFILE(但这需要我创建一个(可能的)大型临时文件)
    4. 我读过(希望有人可以确认)我应该删除索引,这样就不会重新计算它们了。

      mysql Ver 14.7 Distrib 4.1.22, for sun-solaris2.10 (sparc) using readline 4.3

3 个答案:

答案 0 :(得分:1)

以下是我对您的实用程序脚本的看法......

1)无论如何,这只是一种很好的做法,无论如何,我都会这样做。

2)可以为您节省大量的执行时间。如果您可以在不使用C程序中的迭代的情况下在直接SQL中解决问题,则可以节省相当多的时间。您必须首先对其进行分析,以确保它在测试环境中确实存在。

3)LOAD DATA INFILE是插入大量数据时使用的策略。如果您要插入大量记录(我会编写一个查询来进行分析,以确定您需要在表B中插入多少条记录),那么您可能需要以这种方式加载它们。

在插入之前删除索引可以帮助减少运行时间,但是你需要确保在完成后将它们放回去。

虽然......为什么不是表B中的所有记录?你没有提到处理是如何工作的,但我认为(在你的应用程序中)确保记录到达那里而没有你的服务脚本干预是有利的。当然,你比我更了解你的情况,所以如果它偏离基础,请忽略这一段。我从经验中知道,实用程序清理脚本需要存在的原因很多。


编辑:阅读修改后的帖子后,您的问题域已更改:您在(可搜索?)平面文件中有一堆记录,您需要根据特定条件加载到数据库中。我认为尽快做到这一点的诀窍是确定C应用程序实际上最慢的地方并且花费最多的时间来旋转其众所周知的轮子:

  • 如果它正在读取磁盘,你就会被卡住,除非你得到更快的磁盘,否则你无法做任何事情。
  • 如果它正在进行SQL查询插入操作,你可以尝试优化它,但是你要在两个数据库(平面文件和MySQL数据库)之间进行比较。

快速思考:通过执行LOAD DATA INFILE批量插入来快速填充临时表(如果MySQL允许,甚至可能是内存中的表),然后执行INSERT IF NOT EXISTS可能比你更快'目前正在做。

简而言之,进行分析,找出减速的​​位置。除此之外,请与经验丰富的DBA讨论如何做好这方面的建议。

答案 1 :(得分:1)

为什么不将MySQL服务器升级到5.0(或5.1),然后使用触发器,以便它始终是最新的(不需要每月脚本)?

DELIMITER //
CREATE TRIGGER insert_into_a AFTER INSERT ON source_table
FOR EACH ROW 
BEGIN
    IF NEW.foo > 1 THEN
        SELECT id AS @testvar FROM a WHERE a.id = NEW.id;
        IF @testvar != NEW.id THEN
            INSERT INTO a (col1, col2) VALUES (NEW.col1, NEW.col2);
            INSERT INTO b (col1, col2) VALUES (NEW.col1, NEW.col2);
        END IF
    END IF
END //
DELIMITER ;

然后,您甚至可以设置更新和删除触发器,以便表始终保持同步(如果源表col1已更新,它将自动传播到a和b)...

答案 2 :(得分:0)

我与另一位同事讨论过,以下是我们提出的一些改进措施:

有关:

SELECT X FROM TABLE_A WHERE Y=Z;

更改为(目前正在等待验证X是否始终是唯一的):

SELECT X FROM TABLE_A WHERE X=Z LIMIT 1;

这是一个很容易的改变,我们看到了一些轻微的改进。我不能很好地量化它,但我做了:

SELECT X FROM TABLE_A ORDER BY RAND() LIMIT 1

并比较前两个查询。对于一些测试,大约有0.1秒的改进。也许它缓存了一些东西,但LIMIT 1应该有所帮助。

然后另一个(尚未实施)改进(?):

for record number X in entire record range:
  if (no CACHE)
    CACHE = retrieve Y records (sequentially) from the database
  if (X exceeds the highest record number in cache)
    CACHE = retrieve the next set of Y records (sequentially) from the database
  search for record number X in CACHE
  ...etc

我不确定要将Y设置为什么,是否有任何方法可以确定要尝试的大小合适的数字?该表有200万条目。完成实施后,我将编辑一些结果。