MySQL:auto_increment的奇怪行为

MySQL: auto_increment's strange behaviour

我需要将数据从 TSV 文件加载到具有 7 个外键的 table 中。我在网上找到了一个建议,它可以通过一个代理 table 来完成,该代理具有与输入文件匹配的列和一个插入事件触发器,该事件将数据填充到目标 table 及其具有 FK 约束的父级。我开始测试它,我注意到父 tables 的 PK ID 增加了超过 1,我的意思是不是预期的 1、2、3 序列……我得到的是 1 , 63, 539, ...看看我的触发代码:

create trigger trig1 before insert on temp_table
for each row
begin
  -- Set FKs
  declare ts_id BIGINT UNSIGNED;
  declare cluster_id BIGINT UNSIGNED;
  declare queue_id BIGINT UNSIGNED;
  declare service_class_id BIGINT UNSIGNED;
  declare project_id BIGINT UNSIGNED;
  declare job_group_id BIGINT UNSIGNED;
  declare user_id BIGINT UNSIGNED;
  insert into timeline (ts) values (NEW.ts) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into ts_id;
  insert into clusters (name) values (NEW.cluster_name) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into cluster_id;  
  insert into queues (name) values (NEW.queue_name) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into queue_id;
  insert into service_classes (name) values (NEW.service_class) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into service_class_id;
  insert into projects (code) values (NEW.project_code) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into project_id;
  insert into job_groups (name) values (NEW.job_group) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into job_group_id;
  insert into users (name) values (NEW.user_name) on duplicate key update id=last_insert_id(id);
  select last_insert_id() into user_id;
  -- Insert a new row to the dest table
  insert into dest_table values(ts_id, cluster_id, queue_id, service_class_id, project_id, job_group_id, user_id, NEW.job_count, NEW.slot_count, NEW.mem_req, NEW.mem_used);
end; 

在我看来,在语句 'insert into ... on duplicate key update id=last_insert_id(id);' 中,无论何时发生重复插入,'update' 都会影响最后一个插入 ID,即使父 table 没有变化。请让我知道您的想法 - 我对这种 auto_increment 行为的猜测是否正确以及如何防止此类问题。在生产中,这个问题可能会导致父 table 中的 PK 比预期更快地达到最大值,所以我想避免这种情况。

这些丢失的值是一个已知的记录限制。

请参阅以下文档: http://dev.mysql.com/doc/refman/5.6/en/innodb-auto-increment-configurable.html

我想看看这个问题之前的讨论,它提出了一些避免这个问题的替代方法: prevent autoincrement on MYSQL duplicate insert

在@TrentLloyd 指向我的 MySQL 文档 http://dev.mysql.com/doc/refman/5.6/en/innodb-auto-increment-configurable.html 中,我发现这种行为取决于系统变量的值 innodb_autoinc_lock_mode:

  • 当 innodb_autoinc_lock_mode=0 重复键更新时 增加主键;
  • 当 innodb_autoinc_lock_mode=(1|2) on-duplicate-key-updates 确实增加了主键。

所以 innodb_autoinc_lock_mode=0 满足了我的需求。不幸的是,这个参数的默认值是 1,它不能在会话中更改,但只能在 my.cnf 中或作为启动命令参数,所以它需要重新配置并重新启动 MySQL 服务。

我想出的解决方法是从所有父表的 ID 中删除 auto_increment 并重写触发器:

create trigger jrun_trig before insert on jrun_tmp
for each row
begin
  -- Set FKs
  declare ts_id INT UNSIGNED;
  declare cluster_id INT UNSIGNED;
  declare queue_id INT UNSIGNED;
  declare service_class_id INT UNSIGNED;
  declare project_id INT UNSIGNED;
  declare job_group_id INT UNSIGNED;
  declare user_id INT UNSIGNED;
  if NEW.ts is NULL then set ts_id = NULL;
  else
      select id into ts_id from timeline where ts = NEW.ts limit 1;
      if ts_id is NULL then 
        select ifnull(max(id), 0) + 1 into ts_id from timeline;
        insert into timeline values (ts_id, NEW.ts);
      end if;
  end if;
  if NEW.cluster_name is NULL then set cluster_id = NULL;
  else
      select id into cluster_id from clusters where name = NEW.cluster_name limit 1;
      if cluster_id is NULL then 
        select ifnull(max(id), 0) + 1 into cluster_id from clusters;
        insert into clusters values (cluster_id, NEW.cluster_name);
      end if;
  end if;
  if NEW.queue_name is NULL then set queue_id = NULL;
  else
      select id into queue_id from queues where name = NEW.queue_name limit 1;
      if queue_id is NULL then 
        select ifnull(max(id), 0) + 1 into queue_id from queues;
        insert into queues values (queue_id, NEW.queue_name); 
      end if;
  end if;
  if NEW.service_class is NULL then set service_class_id = NULL;
  else
      select id into service_class_id from service_classes where name = NEW.service_class limit 1;
      if service_class_id is NULL then 
        select ifnull(max(id), 0) + 1 into service_class_id from service_classes;
        insert into service_classes values (service_class_id, NEW.service_class); 
      end if;
  end if;
  if NEW.project_code is NULL then set project_id = NULL;
  else
      select id into project_id from projects where code = NEW.project_code limit 1;
      if project_id is NULL then 
        select ifnull(max(id), 0) + 1 into project_id from projects;
        insert into projects (id, code) values (project_id, NEW.project_code); 
      end if;
  end if;
  if NEW.job_group is NULL then set job_group_id = NULL;
  else
      select id into job_group_id from job_groups where name = NEW.job_group limit 1;
      if job_group_id is NULL then 
        select ifnull(max(id), 0) + 1 into job_group_id from job_groups;
        insert into job_groups values (job_group_id, NEW.job_group); 
      end if;
  end if;
  if NEW.user_name is NULL then set user_id = NULL;
  else
      select id into user_id from users where name = NEW.user_name limit 1;
      if user_id is NULL then 
        select ifnull(max(id), 0) + 1 into user_id from users;
        insert into users values (user_id, NEW.user_name); 
      end if;
  end if;
  -- Insert a new row to the dest table
  insert ignore into jrun values(ts_id, cluster_id, queue_id, service_class_id, project_id, job_group_id, user_id, NEW.job_count, NEW.slot_count, NEW.mem_req, NEW.mem_used);
end; 

触发器现在有更多行,但它完全解决了我的 PK 列间隙问题。