业务ID如何设计,满足大规模并发

先前业务有大量的id生成,比如充值id,订单id,支付id等,业务id格式一般为 10160824000123 其中10代表业务类型,其中 160824代表2016年08月24日 000123代表自增长的id,当多个业务同时都要生成id的时候,一般会这么设计,先看表。

CREATE TABLE `id_create` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `type` int(11) NOT NULL COMMENT ';业务类型1充值 2订单 3支付';,
  `value` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='生成表';

根据type值去生成各个业务id值,value每次+1 ,有几个问题,生成value值的时候,这个表会被同时很多程序同时在跑,只要一个业务受损,其它业务必然也受损。

改进:

CREATE TABLE `id_create_1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='生成表1';

CREATE TABLE `id_create_2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='生成表2';


CREATE TABLE `id_create_3` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='生成表3';

这样避免业务相互竞争的情况,但还有几个问题没有解决。

1、InnoDB在事务会锁表

2、事务回滚导致业务生成的值有时候大有时候小,

总结,不能采用InnoDB,必须采用MyISAM 引擎,不能 回滚,必须永远成功。

看下面设计的表

CREATE TABLE `id_1` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `stub` tinyint(1) NOT NULL COMMENT ';该值不允许修改变更';,
  PRIMARY KEY (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

sql语句:

REPLACE INTO id_1(stub) VALUES(1)
然后去获取刚刚执行的last_insert_id

字段stub的值设为1,每次replace的值也是为1,这时id值会自增1,完美解决并发和锁表的问题。

后附一个yii2程序做为参考,其它类似:

$db = Yii::$app->db;
$sql = "REPLACE INTO id_1(stub) VALUES(1)";
$flag = $db->createCommand($sql)->execute();
$id = $db->getLastInsertID();