mysql 自增 数据迁移_MySQL数据库迁移详细步骤

背景

Oracle数据库提供了sequence的特性,即用户可以通过create sequence seq语法创建一个sequence对象,用来生成一个单机或者RAC环境下,唯一,单调递增/递减,

全局有序(RAC环境)的数字,极大的简化了应用的设计,满足应用对全局唯一的需求。

然而, MySQL 并没有sequence对象类型的存在,为满足用户使用需求, RDS for MySQL移植了Oracle的sequence特性。

语法支持

RDS

for MySQL尽可能的兼容Oracle的sequence语法,

但还是会有一些细微的差别:

1、创建语法

create sequence seq_1

start with 1

minvalue 1

maxvalue 1000000000

increment by 1

cache 10

cycle;

和Oracle的创建sequence语法有一个clause的差别,就是MySQL不支持order clause,原因是MySQL只支持在单机情况下的全局有序,无法在分布式环境下实现,所以,默认在单机环境下就是order的,无需指定,也不支持设置。

2、获取sequence语法

select nextval from seq_1;

select

currval from seq_1;

MySQL原生没有支持dual表,所以获取序列值的方法,沿用了普通的select语法,语法处理起来也比较简单。

设计思路

1.创建一张表表示sequence对象

MySQL使用InnoDB引擎的一张普通的表来表示sequence对象,固定sequence的columns是:

(currval, nextval, start, minvalue,

maxvalue, increment, cache, cycle, used)

其中:

start,

minvalue, maxvalue, increment, cache, cycle

这些字段对应着create sequence语法中指定的值,如果没有指定会有默认值存在。

used

used字段为了区分第一个nextval,如果第一次取是start值,如果超过maxvalue,并且支持cycle,会从minvalue重新开始。

currval, nextval

这两个字段会在select nextval from seq_1执行后,根据cache定义的缓存使用完后,推高其大小。

2.初始化一条记录,表示sequence的值

在创建sequence对象的时候,初始化一条记录(有且仅有一条),记录表示了sequence的定义。

used=0, 表示nextval,currval还没有初始化过。

3.定义缓存

在MySQL server层,在table_share(表定义)对象中,缓存一份Sequence定义和currval,nextval,用来提供cache功能。

4.读取流程

读取select nextval from seq逻辑,参考下面的流程图:

a97e355e528299e4b48124963230bec0.png

一共包含两条路径

路径1:当sequence cache中缓存的内容还没有用完的时候,通过步长increment直接递增nextval,返回结果集给用户。

路径2:当sequence cache中缓存的内容用完的时候,需要推高currval,然后更新sequence表中的记录,最后置换Sequence cache中的缓存,并递增nextval,返回结果集给用户。

常见的两个问题

问题1:sequence 连续性问题

因为cache的原因,实例crash,就会带来nextval的连续性的问题,但sequence的特性本没有支持这个特性,只保留唯一性和单调有序。

问题2:sequence性能问题

sequence的查询,如果cache命中,性能开销基本可以忽略,如果cache未命中,相当update+commit语句的代价,需要性能的问题,依赖用户定义的cache大小。

sequence查询的主流层,就是处理缓存的一般逻辑,看起来并没有多少复杂度,但代码上需要处理的问题,因为事务系统的原因,持久化的操作变得相对比较复杂:

RDS移植sequence解决的问题点

1.

sequence对象类型持久化

Sequence对象虽然底层是使用一张InnoDB表来模拟的,但需要持久化这个类型,以保证系统在读取的时候,识别出来这是一个sequence,我们采用了类似view(视图)的的保存策略,

在MySQL的frm文件中,使用了保留位head[39]=SEQUENCE_TABLE来表示这是一个sequence表。

2.

sequence对象的初始化

在create sequence的过程中,包含了两块处理逻辑:

(1)

创建一张模拟的InnoDB表

(2)插入一条记录表示sequence

因为DDL会默认进行一次隐式提交动作,所以逻辑2中的insert 记录也会一起提交掉。

虽然保证了持久化,但逻辑1和逻辑2中间有一个时间差,所以会有以下两种情况存在问题:

情况1:

如果session2在这个间隙发起select nextval查询,会查询不到记录;

情况2:

如果session2的隔离级别>=repeatable read. 同样看不到记录;

解法:

情况1是存在的,系统会给一个ERROR 1098 (42000): sequence未初始化的错误,重新发起一次就可以了。

情况2可以用下面提到的【5.摒弃MVCC】来解决,也就是sequence的读取,虽然是使用支持MVCC的InnoDB,但sequence value的查询实际上都是read uncommitted,即不需要多版本的。

3.

sequence查询的侵入

因为Sequence的查询没有增加和Oracle一致的select seq.nextval from dual;而是沿用了正常表的查询方式,所以,在handler接口层,对查询进行了侵入,如果缓存命中,就直接从InnoDB层返回,如果未命中,会从select语句转换层update语句,并进行了两阶段提交动作。

4.并发的控制

Sequence的查询,使用了每一个sequence 一个mutex的方法,维护缓存和记录的一致性。如果命中的话,mutex只是保护了currval的内存运算,并不会引起太大的并发问题。

5.摒弃MVCC

sequence的currval,nextval,是不维护多版本的,以此来保证sequence的特性,实现这个可以有两种方式:

方式1:

可以临时设置当前session,或者这个语句的隔离级别设置成read uncommited。

方式2:

在语句读取的时候,临时创建一个readview,读取最新的数据,因为有4.的并发控制方法,新的readview一定会读到最新的数据。

这里的移植,使用的是方式2,

因为方式2,还要解决[6. 自治事务的处理]

6.自治事务的处理

考虑当前的一个场景

update t1 set col1=1 where id=1;

select nextval from seq_1;

rollback;

也就是当当前事务回滚的时候, select nextval from seq_1不回滚,也不能回滚,否则破坏了sequence的特性。

那这里就需要一个自治事务,也就是select nextval from seq_1因为缓存不命中,而退化成update 语句的时候,这个update语句产生的事务必须和当前的事务脱离。

变成了下面的这种场景:

父事务

子事务, 并提交

回滚

子事务,是永远提交,并且不影响父事务的自治事务。

innoDB可以通过innobase_trx_allocate(user_thd);

临时分配一个transaction来实现。

7.主备复制

因为sequence的select nextval from seq语句如果缓存不命中,会退化成一个自治事务,包括了一个update seq语句,所以,自治事务需要记录binlog,来同步到备库,完成备库的sequence 更新。所以自治事务同样需要binlog和innoDB参与的内部XA事务,参考8.两阶段提交协议处理

8.两阶段提交协议处理

sequence所产生的XA事务和原本MySQL的内部XA事务并没有什么区别,同样需要两阶段提交协议进行处理,即

1. 分配一个全局XA id

2. binlog prepare

准备begin--table_map--update_row--commit

一个event group,包含了sequence的更新。

3. InnoDB prepare

持久化redo

4. binlog commit

持久化binlog

event

5. InnoDB commit

更改事务为commited状态,并flush

redo。

当MySQL crash的时候,sequence按照相同的崩溃恢复方式来完成recovery。

9.主备环境的切换

对于sequence的查询,主库和备库走了完全不同的路径,即:

如果是主库:

查询会通过sequence cache,并在cache用完的情况下,变成update语句。

如果是备库:

查询会跳过sequence cache,完全的只读。

10.缓存的失效处理

缓存失效,即sequence cache和InnoDB保存的记录不一致的情况下,就会产生这个问题,

比如一个场景:

如果当前的DB的角色切换 [主库-->备库-->主库],在从主库-->备库切换的时候,sequence cache就会失效,因为,备库会接受binlog event来更新InnoDB保存的记录,这样cache和db就会不一致。所以当又变成主库的时候,就会失去了sequence的order 特性。但注意,cache既然已经分配,虽然失效,但仍然会保持sequence的唯一性的特性。

所以,我们这里在set global readonly=[on/off]的时候,递增了一个global_readonly_version, 当sequence cache中保存的version和当前系统中的version不一致的情况下,需要重新load数据,这样就可以避免这个问题。但频繁设置readonly的时候,会导致没有使用完的cache被丢弃掉,但这个副作用,显然没有问题。

11.数据字典查询

Sequence作为一个新的对象类型,需要一些字典信息来查询,我们提供了两类查询

11.1 对象的查询:

可以通过select * from information_schema.tables where TABLE_TYPE= ’Sequence

table‘来查询所有的sequence。

11.2 参数的查询:

可以直接使用select * from seq,就可以查询这个seq的定义了。

但注意:这里因为有nextval,会推高nextval的值,如果不系统增加currval,就指定columns,

比如: select start, minvalue,maxvalue, increment from seq,

这样就不会推高currval。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值