IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> TiDB分表唯一主键ID——sequence 与gorm无法获取主键的解决 -> 正文阅读

[大数据]TiDB分表唯一主键ID——sequence 与gorm无法获取主键的解决

按理说用了TiDB后就不用去考虑分库分表的问题了,但是由于业务场景上同时也要支持MySQL,且确实数据上存在一定隔离性,因此通过数据类型进行水平分表了。

MySQL水平分表后的主键id通过snowflake来写入,而TiDB的主键id决定通过tidb的sequenceID来实现

Sequence

Sequence 是数据库系统按照一定规则自增的数字序列,具有唯一和单调递增的特性。在官方 SQL 2003 标准中,其被定义为"生成连续数值的一种机制,Sequence 既可以是内部对象,也可以是外部对象"。因为原生 MySQL 中并未支持 Sequence,所以 TiDB Sequence 的语法参考了 MariaDB、Oracle 和 IBM Db2

  • Create Sequence 语法
CREATE [TEMPORARY] SEQUENCE [IF NOT EXISTS] sequence_name 
[ INCREMENT [ BY | = ] INCREMENT ] 
[ MINVALUE [=] minvalue | NO MINVALUE | NOMINVALUE ] 
[ MAXALUE [=] maxvalue | NO MAXVALUE | NOMAXVALUE ] 
[ START [ WITH | = ] start ] 
[ CACHE [=] cache | NOCACHE | NO CACHE] 
[ CYCLE | NOCYCLE | NO CYCLE] 
[ ORDER | NOORDER | NO ORDER] 
[table_options]
  • Show Create Sequence 语法
SHOW CREATE SEQUENCE sequence_name
  • Drop Sequence
DROP [TEMPORARY] SEQUENCE [IF NOT EXISTS] sequence_name
  • 获取下一个值
SELECT NEXT VALUE FOR sequence_name; 
SELECT NEXTVAL(sequence_name);

示例

创建同一结构的三张表,表根据业务类型进行分表

CREATE SEQUENCE seq_for_autoid START WITH 1 INCREMENT BY 1 CACHE 1000 NOCYCLE;

SHOW CREATE SEQUENCE seq_for_autoid;

CREATE TABLE `user_1` ( 
 	`autoid` int(11) DEFAULT nextval(seq_for_autoid),
  `userid` varchar(32) NOT NULL,  
 	PRIMARY KEY (`autoid`) 
);
 
CREATE TABLE user_2 LIKE user_1;
CREATE TABLE user_3 LIKE user_1;
  • 插入测试
INSERT INTO user_1(userid) VALUES ('a1'),('b1'),('c1'),('d1'),('e1'),('f1');
INSERT INTO user_2(userid) VALUES ('a1'),('b1'),('c1'),('d1'),('e1'),('f1');
INSERT INTO user_3(userid) VALUES ('a1'),('b1'),('c1'),('d1'),('e1'),('f1');

user_1表:
img
user_2表:
img
user_3表:
img

gorm的批量插入接口插入,无法返回获取到的主键值

import (
	"github.com/sirupsen/logrus"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

type User_1 struct {
	AutoId int    `gorm:"column:autoid;primarykey"`
	UserId string `gorm:"column:userid"`
}

func main() {
	db, err := gorm.Open(mysql.Open("test:test@tcp(127.0.0.1:4000)/apiserver?charset=utf8mb4&parseTime=true&loc=Local")})
	if err != nil {
		return
	}
	var users []User_1
	users = append(users, User_1{UserId: "6"})
	users = append(users, User_1{UserId: "7"})
	users = append(users, User_1{UserId: "8"})
	users = append(users, User_1{UserId: "9"})
	users = append(users, User_1{UserId: "10"})
	db.Debug().Create(&users)
	logrus.Info(users)
}

//运行结果
[14.247ms] [rows:5] INSERT INTO `user_1` (`userid`) VALUES ('6'),('7'),('8'),('9'),('10')
time="2022-04-20T16:47:57+08:00" level=info msg="[{0 6} {0 7} {0 8} {0 9} {0 10}]"
  • 解决方法1:写一个BeforeCreate钩子函数
func (user *User_1) BeforeCreate(tx *gorm.DB) error {
	var sequenceId uint
	if err := tx.Raw("SELECT NEXT VALUE FOR seq_for_autoid;").First(&sequenceId).Error; err != nil {
		return err
	}
	user.AutoId = sequenceId
	return nil
}

func main() {

	db, err := gorm.Open(mysql.Open("test:test@tcp(127.0.0.1:4000)/apiserver?charset=utf8mb4&parseTime=true&loc=Local"), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: false,
		},
	})
	if err != nil {
		return
	}

	//db.AutoMigrate(&User{})

	var users []User_1
	users = append(users, User_1{UserId: "aaa"})
	users = append(users, User_1{UserId: "bbb"})

	db.Debug().Table("user_1").Create(&users)
	db.Debug().Table("user_2").Create(&users)
	db.Debug().Table("user_3").Create(&users)

	logrus.Info(users)
}

//执行结果,但是每次插入都要select一次,写频繁时会很慢
[1.288ms] [rows:1] SELECT NEXT VALUE FOR seq_for_autoid;

[0.999ms] [rows:1] SELECT NEXT VALUE FOR seq_for_autoid;

[15.168ms] [rows:2] INSERT INTO `user_3` (`userid`,`autoid`) VALUES ('aaa',48),('bbb',49)
time="2022-04-20T17:24:14+08:00" level=info msg="[{48 aaa} {49 bbb}]"
  • 解决方法2:批量插入完后查询last_sequence

这种方法只需要查询一次,但是插入和查询lastId一定要放到同一个事务当中,否则会导致lastid获取异常

func main() {

	var users []User
	users = append(users, User{UserId: "oopoo"})
	users = append(users, User{UserId: "nomonno"})

	var lastId uint64
	if err = db.Debug().Transaction(func(tx *gorm.DB) error {
		if err := tx.Table("user_1").Create(&users).Error; err != nil {
			return err
		}
        //这一步查询上一次插入的最后一个sequence_id
		if err := tx.Raw("SELECT lastval(seq_for_autoid);").First(&lastId).Error; err != nil {
			return err
		}
        //手动赋值
		for i := 0; i < len(users); i++ {
			users[len(users)-i-1].AutoId = uint(lastId) - uint(i)
		}
		return nil
	}); err != nil {
		logrus.Error(err)
		return
	}

	logrus.Info(users)
}
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:43:14  更:2022-04-22 18:47:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:06:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码