日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

AliSQL开源Sequence Engine

發布時間:2024/2/28 数据库 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AliSQL开源Sequence Engine 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Introduction

單調遞增的唯一值,是在持久化數據庫系統中常見的需求,無論是單節點中的業務主鍵,還是分布式系統中的全局唯一值,亦或是多系統中的冪等控制。不同的數據庫系統有不同的實現方法,比如MySQL提供的AUTO_INCREMENT,Oracle,SQL Server提供SEQUENCE等。

在MySQL數據庫中,如果業務系統希望封裝唯一值,比如增加日期,用戶等信息,AUTO_INCREMENT的方法會帶來很大的不便,在實際的系統設計的時候, 也存在不同的折中方法,比如:

  • 序列值由Application或者Proxy來生成,不過弊端很明顯,狀態帶到應用端,增加了擴容和縮容的復雜度。
  • 序列值由數據庫通過模擬的表來生成,但需要中間件來封裝和簡化獲取唯一值的邏輯。

AliSQL自主實現了SEQUENCE ENGINE,通過引擎的設計方法,盡可能的兼容其他數據庫的使用方法,簡化獲取序列值復雜度。

Github開源地址:https://github.com/alibaba/AliSQL

Description

AliSQL開源的SEQUENCE,實現了MySQL存儲引擎的設計接口,但底層的數據仍然使用現有的存儲引擎,比如InnoDB或者MyISAM來保存持久化數據,以便盡可能的保證現有的外圍工具比如XtraBackup等工具的兼容,所以SEQUENCE ENGINE僅僅是一個邏輯引擎。

對sequence對象的訪問通過SEQUENCE handler接口,這一層邏輯引擎主要實現NEXTVAL的滾動,CACHE的管理等,最后透傳給底層的基表數據引擎,實現最終的數據訪問。

下面我們透過語法來看下AliSQL SEQUENCE的使用。

Syntax

1. CREATE SEQUENCE Syntax:

CREATE SEQUENCE [IF NOT EXISTS] schema.sequence_name[START WITH <constant>][MINVALUE <constant>][MAXVALUE <constant>][INCREMENT BY <constant>][CACHE <constant> | NOCACHE][CYCLE | NOCYCLE];

SEQUENCE OPTIONS:

  • START
    Sequence的起始值

  • MINVALUE
    Sequence的最小值,如果這一輪結束并且是cycle的,那么下一輪將從MINVALUE開始

  • MAXVALUE
    Sequence的最大值,如果到最大值并且是nocycle的,那么將會得到以下報錯:
    ERROR HY000: Sequence 'db.seq' has been run out.

  • INCREMENT BY
    Sequence的步長

  • CACHE/NOCACHE
    Cache的大小,為了性能考慮,可以設置cache的size比較大,但如果遇到實例重啟,cache內的值會丟失

  • CYCLE/NOCYCLE
    表示sequence如果用完了后,是否允許從MINVALUE重新開始

例如:

create sequence sstart with 1minvalue 1maxvalue 9999999increment by 1cache 20cycle;

2. SHOW SEQUENCE Syntax

SHOW CREATE [TABLE|SEQUENCE] schema.sequence_name;CREATE SEQUENCE schema.sequence_name (`currval` bigint(21) NOT NULL COMMENT 'current value',`nextval` bigint(21) NOT NULL COMMENT 'next value',`minvalue` bigint(21) NOT NULL COMMENT 'min value',`maxvalue` bigint(21) NOT NULL COMMENT 'max value',`start` bigint(21) NOT NULL COMMENT 'start value',`increment` bigint(21) NOT NULL COMMENT 'increment value',`cache` bigint(21) NOT NULL COMMENT 'cache size',`cycle` bigint(21) NOT NULL COMMENT 'cycle state',`round` bigint(21) NOT NULL COMMENT 'already how many round' ) ENGINE=InnoDB DEFAULT CHARSET=latin1

由于SEQUENCE是通過真正的引擎表來保存的,所以SHOW COMMAND看到仍然是engine table。

3. QUERY STATEMENT Syntax

SELECT [NEXTVAL | CURRVAL | *] FROM schema.sequence_name; SELECT [NEXTVAL | CURRVAL | *] FOR schema.sequence_name;

這里支持兩種訪問方式,FROM和FOR:

  • FROM clause: 兼容正常的SELECT查詢語句,返回的結果是基表的數據,不迭代NEXTVAL。
  • FOR clause:兼容SQL Server的方法,返回的結果是迭代后NEXTVAL的值。
mysql> select * from s; +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+ | currval | nextval | minvalue | maxvalue | start | increment | cache | cycle | round | +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+ | 0 | 30004 | 1 | 9223372036854775807 | 1 | 1 | 10000 | 0 | 0 | +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+ 1 row in set (0.00 sec)mysql> select * for s; +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+ | currval | nextval | minvalue | maxvalue | start | increment | cache | cycle | round | +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+ | 0 | 20014 | 1 | 9223372036854775807 | 1 | 1 | 10000 | 0 | 0 | +---------+---------+----------+---------------------+-------+-----------+-------+-------+-------+

4. 兼容性

因為要兼容MYSQLDUMP的備份方式,所以支持另外一種CREATE SEQUENCE方法,即:通過創建SEQUENCE表和INSERT一行初始記錄的方式, 比如:

CREATE SEQUENCE schema.sequence_name (`currval` bigint(21) NOT NULL COMMENT 'current value',`nextval` bigint(21) NOT NULL COMMENT 'next value',`minvalue` bigint(21) NOT NULL COMMENT 'min value',`maxvalue` bigint(21) NOT NULL COMMENT 'max value',`start` bigint(21) NOT NULL COMMENT 'start value',`increment` bigint(21) NOT NULL COMMENT 'increment value',`cache` bigint(21) NOT NULL COMMENT 'cache size',`cycle` bigint(21) NOT NULL COMMENT 'cycle state',`round` bigint(21) NOT NULL COMMENT 'already how many round' ) ENGINE=InnoDB DEFAULT CHARSET=latin1INSERT INTO schema.sequence_name VALUES(0,0,1,9223372036854775807,1,1,10000,1,0); COMMIT;

但強烈建議使用native的CREATE SEQUENCE方法。

5. 語法限制

  • Sequence不支持subquery和join
  • FOR clause只支持sequence表,普通引擎表不支持
  • 可以使用SHOW CREATE TABLE或者SHOW CREATE SEQUENCE來訪問SEQUENCE結構,但不能使用SHOW CREATE SEQUENCE訪問普通表
  • 不支持CREATE TABLE的時候指定SEQUENCE引擎,sequence表只能通過CREATE SEQUENCE的語法來創建

High level architecture

1. Sequence initialization

Sequence對象的創建,會轉化成擁有固定[CURRVAL, NEXTVAL, MINVALUE, MAXVALUE, START, INCREMENT, CACHE, CYCLE, ROUND]這9個字段的引擎表,并根據CREATE SEQUENCE clause的定義,初始化了一條數據,所以sequence對象實質上是擁有一條記錄的存儲引擎表,SLAVE復制的BINLOG使用CREATE SEQUENCE ...語句生成的QUERY EVENT來完成。

2. Sequence interface

SEQUENCE handler實現了一部分的handler interface,并定義了兩個重要的屬性,SEQUENCE_SHARE和BASE_TABLE_FILE,SEQUENCE_SHARE保存著共享的sequence對象屬性和CACHE的值,NEXTVAL的值首先從cache中獲取,只有在cache使用完了,才會查詢基表。 BASE_TABLE_FILE是基表的handler,對持久化的數據的訪問和修改,都通過BASE_TABLE_FILE handler進行訪問。

3. Sequence cache

Sequence對象的CACHE值保存在SEQUENCE_SHARE中,使用SEQUENCE_SHARE::MUTEX進行保護,所有對cache的訪問是串行的。比如cache size是20,那么SEQUENCE_SHARE中只是保存一個cache_end值,當訪問的NEXTVAL到了cache_end,就會從基表中獲取下一個batch放到cache中。NEXTVAL根據INCREMENT BY設置的步長進行迭代。

4. Sequence update

當cache用完了之后,會從基表中獲取下一個batch,這樣會更新基表中的記錄,查詢會轉化成更新語句,
其更新的主要步驟如下:

  • 升級SEQUENCE的MDL_SHARE_READ METADATA LOCK 到 MDL_SHARE_WRITE級別
  • 持有GLOBAL MDL_INTENSIVE_EXCLUSIVE METADATA LOCK
  • 開啟AUTONOMOUS TRANSACTION
  • 更新記錄并生成BINLOG EVENT
  • 持有COMMIT METADATA LOCK
  • XA提交AUTONOMOUS TRANSACTION 并釋放MDL鎖
  • 5. Autonomous transaction

    因為nextval不支持ROLLBACK重用,所以必須重啟一個自治事務來脫離事務上下文, 其步驟如下:

  • 備份當前基表引擎的事務上下文
  • 備份當前BINLOG引擎的上下文
  • SEQUENCE和BINLOG分別注冊AUTONOMOUS TRANSACTION
  • 等更新完成,XA提交AUTONOMOUS TRANSACTION
  • 還原當前事務上下文
  • 6. Sequence read only

    因為SEQUENCE的SELECT語句會轉換成UPDATE語句,所以SELECT NEXTVAL FOR s?statement須持有 MDL_SHARE_WRITE 和 GLOBAL MDL_INTENSIVE_EXCLUSIVE METADATA LOCK 進行,以便在READ ONLY的時候,阻塞對sequence對象的訪問。

    7. Skip cache

    這里指兩種CACHE:

    • 一種是SEQUENCE的CACHE,可以使用SELECT NEXTVAL FORM Sequence_name來skip。
    • 另外一種是QUERY CACHE,所有的SEQUENCE都設置了不支持QUERY CACHE,這樣避免由于QUERY CACHE導致NEXTVAL沒有迭代。

    8. Sequence backup

    由于SEQUENCE是通過真正的引擎表來保存的,所以類似XtraBackup這樣的物理備份可以直接使用,而類似于MYSQLDUMP這樣的邏輯備份,SEQUENCE會備份成CREATE SEQUENCE語句和INSERT語句的組合來完成。

    Next Release

    本次開源了部分功能,下一次release將繼續開源SEQUENCE的部分功能:

    • 支持CURRVAL的訪問,CURRVAL表示當前session的上一次的NEXTVAL訪問的值。
    • 兼容更多數據庫的訪問方法,比如:
    Oracle Syntax:SELECT sequence_name.nextval FROM DUAL; PostgreSQL Syntax:nextval(regclass);currval(regclass);setval(regclass, bigint);

    Usage Scenario

    1. 更具有業務含義的主鍵設計?.

    例如:[八位日期 + 四位USER ID + sequence_number]的流水業務單據號的設計格式,可以通過SELECT NEXTVAL FOR Sequence和應用封裝的方式實現,相比較無意義的id數字,這種格式會帶來幾個優勢:

    • 保持和時間同步的有序性,有利于數據的歸檔,比如可以直接使用這種ID來進行按日/月/年RANGE分區, 無縫使用MySQL的partition特性
    • 增加USER的id信息,可以作為天然的分庫分表邏輯位, 提升數據節點可擴展性
    • 保持數字的有序性,保證InnoDB這種聚簇索引表的插入性能穩定

    業界目前采用的設計方法:

    • Booking使用了AUTO_INCREMENT的方法, 先插入一個無業務含義的數字,然后使用last_insert_id()方法獲取ID值,最后在業務邏輯中使用這個ID值。 其劣勢就是必須先插入,并沒有辦法再修改這個無業務含義的id。
    • Twitter采用了另外一種格式,[41 bits timestamp + 10 bits configured machine ID + 12 bits sequence number], sequence number的生成機制沒有透露,machine ID的的設計,使用Zookeeper來管理的machine ID或者機器的MAC address。
    • UUID的方法,這種方式生成了一個隨機的唯一值,嚴重影響了插入的性能,并且增大了索引大小,降低了命中率,沒有任何優勢。

    2. 分布式節點的唯一值設計

    分布式SEQUENCE生成:

    • 可以為每一個節點設計sequence,比如為每個節點設計不同的INCREMENT BY步長來達到MySQL AUTO_INCREMENT中,設置auto_increment_increment和auto_increment_offset的效果,但相比較auto increment的全局配置,并且保存在my.cnf中的方法,SEQUENCE可以把這些配置當做sequence對象的屬性持久化保存下來,優勢明顯。但不推薦使用這種方法來設計唯一值,會給運維留下不少坑。
    • 使用類似twitter的方法,每一個節點上創建sequence,然后增加節點信息到sequence number中,生成唯一值。

    集中式SEQUENCE生成:

    • 對于分布式節點中的ID需求,使用獨立的集中式的sequence服務來生成,但如果要保證持續可用,sequence服務仍然需要設計成多節點的,比如Flickr的Ticket Servers設計:

    Sequence服務節點上創建Ticket表:

    CREATE TABLE `Tickets64` (`id` bigint(20) unsigned NOT NULL auto_increment,`stub` char(1) NOT NULL default '',PRIMARY KEY (`id`),UNIQUE KEY `stub` (`stub`) ) ENGINE=MyISAM+-------------------+------+ | id | stub | +-------------------+------+ | 72157623227190423 | a | +-------------------+------+

    使用以下語句,生成ID值:

    REPLACE INTO Tickets64 (stub) VALUES ('a'); SELECT LAST_INSERT_ID();

    因為PHOTOS,COMMENTS,FAVORITES,TAGS都需要ID, 所以會建不同的ticket表來完成,為了保持持續可用,采用了:

    TicketServer1: auto-increment-increment = 2 auto-increment-offset = 1TicketServer2: auto-increment-increment = 2 auto-increment-offset = 2

    來保證高可用。
    如果使用sequence對象,可以大大簡化ID的獲取邏輯,并更加安全。

    總結

    以上是生活随笔為你收集整理的AliSQL开源Sequence Engine的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。