我有一张包含大量数据的表格。数据来源是外部api。每隔几个小时,我需要同步数据库,以便从外部api更新更新。我正在进行完全同步(api不允许增量同步)。
同步发生时,我想确保数据库中的数据也可供读取。所以,我遵循以下步骤:
表有大约5000万行,预计会增长。表中有一个customerId字段。同步通常基于customerId将其传递给api。
我的问题是,上面的第3步和第4步花了很多时间。查询类似于:
步骤3 - > delete from foo where customer_id=12345678 and flag=1
步骤4 - > update foo set flag=1 where customer_id=12345678
我已尝试基于customer_id对表进行分区,并且在customer_id行数较少的情况下效果很好但对于某些customer_id,每个分区本身的行数大约为500万。
大约90%的数据在两次同步之间不会发生变化。我怎样才能做到这一点?
我在考虑只使用更新查询而不是插入查询,然后检查是否有任何更新。如果没有,我可以为同一行发出插入查询。这样,任何更新都将与插入一起处理。但我不确定该操作是否会在更新过程中阻止对此进行读取查询。
答案 0 :(得分:0)
对于您的设置(只读数据,完全同步),更新表的最快方法是根本不更新,而是将数据导入另一个表并在之后重命名以使其成为新表。
像原始表格一样创建一个表格,例如使用
create table foo_import like foo;
如果您有例如触发器,也可以添加它们。
从现在开始,让导入api将其(完整)同步写入此新表。
同步完成后,交换两个表:
RENAME TABLE foo TO foo_tmp,
foo_import TO foo,
foo_tmp to foo_import;
它(确切地说)只需要一秒钟。
这个命令是原子的:它将等待访问这些表的事务完成,它不会出现没有表foo
的情况,并且它将完全失败(并且不做任何事情),如果其中一个表格不存在或foo_tmp
已存在。
最后一步,清空导入表(现在包含旧数据),为下次导入做好准备:
truncate foo_import;
这又需要一秒钟。
其余的查询可能会假设为flag=1
。直到(如果有的话)您更新代码以不再使用该标志,您可以将其默认值设置为1
以保持其兼容,例如使用
alter table foo modify column flag tinyint default 1;
由于你没有外键,它不必打扰你,但对于有类似问题的其他人来说,知道外键会被调整可能会有用,所以外键引用{{1重命名表后将引用foo
。为了使它们再次指向新表foo_import
,必须删除它们并重新创建它们。其他所有内容(例如视图,查询,程序)都将按当前名称解析,因此他们将始终访问当前的foo
。
答案 1 :(得分:0)
CREATE TABLE new LIKE real;
Load `new` by whatever means you have; take as long as needed.
RENAME TABLE real TO old, new TO real;
DROP TABLE old;
RENAME
是原子的,"瞬时&#34 ;; real
始终是"可用。
(我不认为需要flag
。)
或... 强>
由于您实际上正在更新表的一部分,请考虑这些......
如果块很小......
如果大块很大,并且你不想锁定表太长",还有其他一些技巧。但首先,客户的每一行都有某种形式的唯一行号吗? (我考虑一次批量移动一堆或多行,但在拼写之前需要更多细节。)