暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

TDSQL 序列功能介绍

数据库开发者 2021-04-15
1652

Mysql一直以来都是使用innodb的自增列功能产生表级别的唯一id序列,innodb把自增列值存储在该列所在的索引中(必然是其第一列),每次mysqld启动后首次打开这个表时候,把这个索引的最大值取出来作为自增列的起始值并且存储在内存中,然后mysqld运行期间即可维护和使用这个自增列值。

 

这个功能有不少缺陷,包括:

1. 每个表只能有一个自增列,并且这个自增列必须是一个索引  的第一列。同时,一个自增序列无法给多个表使用。

2. 自增列可能会成为一个性能瓶颈。当innodb_autoinc_lock_mode0时,一个自增列会在一个插入语句执行期间被一直锁住,这样它就成为一个严重的性能瓶颈,对该表的插入是在语句级别排队执行的。后来较新的版本支持了1模式后,问题大大减轻,但是对于insert into ... Select from 这样的语句仍然有性能瓶颈。使用2的话无法保证insert into ... Select from 这样的语句在SBR复制模式下的主备数据一致。

3. 无法独立引用(refer to)一个表的自增列的当前值(last_insert_id无法指定某个表)

4. 其他存储引擎需要实现自己的自增列功能。

 

由于TDSQL的不少金融业用户之前使用Oracle数据库,他们希望我们支持Oracle sequence,因此,我在TDSQLpercona数据库内核中实现了Oracle sequence功能,该功能与oracle原版sequence功能完全相同,没有任何省略和遗漏。Oracle sequence的详细功能介绍在这里(https://docs.oracle.com/cd/B28359_01/server.111/b28310/views002.htm#ADMIN11792) 

TDSQL sequence 完全解决了上述mysql 自增列的缺陷,sequence完全独立与表而存在,不需要作为表的索引第一列;一个表的不同字段可以使用多个sequence产生序列值;一个sequence可以给任意多个表使用产生自增序列值,也可以脱离表的insert/update语句而单独使用。所有存储引擎上面的表都可以使用sequence功能。当然,对于SBR复制模式来说,与mysql自增列一样,使用sequence仍然会导致主备数据不一致。TDSQL使用RBR复制,所以不存在这个问题。

 

在本文文末我会介绍一下TDSQL sequence的性能,下面我先把sequence的常用功能的执行截图贴出来供读者参考。

TDSQL sequence的功能展示


TDSQL sequence的性能


影响sequence性能的因素主要是sequencecache数值,cache越大,每次修改mysql.tdsql_sequence表中该sequence的元数据后可以产生越多的sequence值,因而性能越好。当然,与Oraclesequence一样,如果mysqld crash了,那么下次重启后,sequence将从mysql.tdsql_sequences表的curval字段记录的值开始增长,可能就跳过了很多个sequence值。因此,用户通常需要选择一个合适的cache值。另外,事务回滚也不会归还占用的sequence值,这也与oracle sequence相同。

 

这个性能测试机器使用一个48核的intel cpusysbenchmysqld在同一台机器运行。我预先执行create sequence mysql.seq1;创建一个sequence,然后使用sysbench500个连接,每个连接执行下面这个简单的测试脚本如下:

 

-bash-4.2$ cat tests/db/select-seq.lua 

pathtest = string.match(test, "(.*/)") or ""

 

dofile(pathtest .. "common.lua")

 

function thread_init(thread_id)

   set_vars()

end

 

function event(thread_id)

   rs = db_query("SELECT mysql.seq1.nextval ");

end

 

sysbench测试命令如下:

-bash-4.2$ cat seq.sh 

./sysbench --max-time=300 --test=tests/db/select-seq.lua  --mysql-host=xxx  --mysql-port=xx --mysql-db=test1 --oltp_tables_count=10 --oltp-table-size=50000000 --oltp-read-only=off --init-rng=on --num-threads=500 --max-requests=0 --oltp-dist-type=uniform --mysql-user='abc' --mysql-password='abc'  --oltp_auto_inc=off --report-interval=1 --db-driver=mysql $1

 

Cache

QPS

CPU idle

95% 延时

20

40w/s

35%

2ms

500

35w/s

30%

1.8ms

2000

54w/s

<3%

1.5ms

10000

60w/s

<3%

1.5ms

 

这几个cache值设定下,sysbench期间QPS和延时都非常稳定。有趣的是500个连接时,cache值是500qpscache值为20还低一些,我反复试过几次都是这样。似乎发生了某种共振导致的。这个95%延时值是包含了数据包往返(sysbenchmysqld在同一个机器上面),以及线程池调度开销的,并不只是执行select seq1.nextval的耗时,也就是说如果这个seq1.nextval是一个insert语句的一部分的话,那么执行seq1.nextval这个部分的耗时是远远低于2ms的。

 


文章转载自数据库开发者,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论