不忘初心,砥砺前行
作者 | 陌无崖
转载请联系授权
今天在写项目的时候学习了一个用代码编写的自增的数据库ID,其实是一个ID缓冲池。使用了golang中chan类型。
我们希望该ID缓冲池可以为我们其他不同的数据表进行ID的生成,因此需要建一个如下表:
CREATE TABLE `uid` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`business_id` varchar(128) COLLATE utf8mb4_bin NOT NULL COMMENT '业务id',
`max_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '最大id',
`step` int(10) unsigned NOT NULL DEFAULT '1000' COMMENT '步长',
`description` varchar(255) COLLATE utf8mb4_bin NOT NULL COMMENT '描述',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_business_id` (`business_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='分布式自增主键';
-- ----------------------------
-- Records of uid
-- ----------------------------
INSERT INTO `uid` VALUES ('1', 'device_id', '1689', '10', '设备id', '2019-10-15 16:42:05', '2020-02-05 06:57:29');
INSERT INTO `uid` VALUES ('2', 'test', '20', '10', '测试', '2020-02-05 06:59:44', '2020-02-05 07:05:12');
type Uid struct {
db *sql.DB
businessId string
ch chan int64//缓冲池的大小
min, max int64
}
// 新建一个Uid并一直生产ID
func NewUid(db *sql.DB, businessId string, len int) (*Uid, error) {
lid := Uid{
db: db,
businessId: businessId,
ch: make(chan int64, len),
}
go lid.produceId()
return &lid, nil
}
首先从数据库中加载获得当前数据的最大值
循环生成自增ID
func (u *Uid) produceId() {
// 从数据库中获取id
u.reload()
for {
if u.min >= u.max {
// 从数据库中获取id
u.reload()
}
u.min++
u.ch <- u.min
}
}
在上述代码中当ch中达到了最大容量,会发生阻塞。
获得数据库中的ID,如果获取失败,将停顿一秒,继续尝试获取
func (u *Uid) reload() error {
var err error
for {
err = u.getFromDB()
if err == nil {
return nil
}
// 如果获取失败,等待
time.Sleep(time.Second)
}
}
操作数据库
func (u *Uid) getFromDB() error {
var (
maxId int64
step int64
)
tx, err := u.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
//sql语句
sqlquery := "select max_id,step from uid where business_id = ? FOR UPDATE "
err = tx.QueryRow(sqlquery, u.businessId).Scan(&maxId, &step)
if err != nil {
return err
}
// 更新数据库中uid的最大值
update := "update uid set max_id = ? where business_id = ?"
_, err = tx.Exec(update, maxId+step, u.businessId)
if err != nil {
return err
}
err = tx.Commit()
if err != nil {
return err
}
u.min = maxId
u.max = maxId + step
return nil
}
有了这个数据库自增ID的管理,当我们分布式操作数据库时,就可以保证不会发生冲突了
END
今日推荐阅读
RabbitMQ系列笔记广播模式和路由模式
RabbitMQ系列笔记入门篇
RabbitMQ系列笔记work模式
RabbitMQ系列笔记work模式
protoc语法详解及结合grpc定义服务
Golang中Model的使用
基于Nginx和Consul构建高可用及自动发现的Docker服务架构
▼关注我,一起成长
主要分享 学习心得、笔记、随笔▼