MySQL 8.0的原子DDL非事務性DDL,絕大多數情況下,原子DDL仍舊是一個然並卵的特性

来源:https://www.cnblogs.com/wy123/archive/2020/04/28/12792876.html
-Advertisement-
Play Games

首先聲明一下:MySQL 8.0之後,依舊不支持DDL事務。原子性DDL與其說是一個MySQL8.0下的新特性,倒是不如說是修複了MySQL5.7 DDL 執行失敗造成的bug。 MySQL 8.0 前後表的物理文件差異在mysql中,對於表的數據文件,在mysql 8.0之前,是分為一個ifm的表 ...


首先聲明一下:MySQL 8.0之後,依舊不支持DDL事務。原子性DDL與其說是一個MySQL8.0下的新特性,倒是不如說是修複了MySQL5.7 DDL 執行失敗造成的bug。

MySQL 8.0 前後表的物理文件差異
在mysql中,對於表的數據文件,在mysql 8.0之前,是分為一個ifm的表結構文本文件和一個idb數據文件
再執行表結構變更的DDL的時候實際上是一個寫文本文件的過程,因此不支持事務
到了8.0之後,表結構存儲進去了共用表空間文件的數據字典中Data Dictionary Storage Engine(即 InnoDB表中),已經無法直接看到.frm文件了
   
這樣看來,DDL的創建好似乎也是一個事務性的DML(修改元數據),但是MySQL 8.0依舊不支持事務性的DDL。

原子DDL
最多的示例就是這個:DROP TABLE t1,t2;drop VIEW v1,v2,執行時候t1,t2出錯之後會自動回滾。
原子DDL相對MySQL8.0之前的DDL,稍微進步了一點點:alter table 操作,在server crash的情況下,不會遺留.frm,.ibd臨時文件
但是仍舊不支持事務性DDL,最多的示例就是這個,誰特麽天天 DROP TABLE t1,t2;drop VIEW v1,v2 ???
實際對於DB的變更,更多的時候,是基於不同的表加欄位/修改欄位類型/加索引/刪索引,而這種語句,無法書寫成原子性DDL支持的語句,當然MySQL 8.0下也是不支持事務性DDL的。


原子DDL的實現過程

以下原子DDL的實現過程摘自於網路:
不管參數innodb_flush_log_at_trx_commit的值,只要是對mysql.innodb_ddl_log表做的更改的重做日誌都會立即刷新到磁碟。
立即刷新重做日誌可以避免DDL操作修改數據文件的情況,但是由這些操作產生的對mysql.innodb_ddl_log表的更改的重做日誌不會持久保存到磁碟。
這種情況可能會在回滾或恢復期間導致錯誤。“老大要重點保護”

InnoDB存儲引擎分以下幾個階段執行DDL操作:
1)準備:創建所需對象並將DDL日誌寫入mysql.innodb_ddl_log表。 DDL日誌定義瞭如何前滾和回滾DDL操作。
2)執行:執行DDL操作。 例如,為CREATE TABLE操作執行create常式。
3)提交:更新數據字典並提交數據字典事務。
4)DDL後收尾工作:從mysql.innodb_ddl_log表中重播並刪除DDL日誌。 為了確保可以安全地執行回滾而不引入不一致性,在最後階段執行文件操作,例如重命名或刪除數據文件。 此階段還從mysql.innodb_dynamic_metadata數據字典表中刪除DROP TABLE,TRUNCATE TABLE和其他重建表的DDL操作的動態元數據。

無論事務是提交還是回滾,DDL日誌都會在Post-DDL階段重放並從mysql.innodb_ddl_log表中刪除。 如果伺服器在DDL操作期間暫停,則DDL日誌應僅保留在mysql.innodb_ddl_log表中。 在這種情況下,DDL日誌將在恢復後重放並刪除。
在恢復情況下,可以在重新啟動伺服器時提交或回滾DDL事務。 如果在重做日誌和二進位日誌中存在在DDL操作的提交階段期間執行的數據字典事務,則該操作被視為成功並且前滾。 否則,當InnoDB重放數據字典重做日誌並回滾DDL事務時,將回滾未完成的數據字典事務。



實現批量DDL的可重覆性執行

因此,這裡上可以認為,除了表結構變更時候的臨時文件清理,與DDL事物沒有什麼瓜葛,這個MySQL8.0的原子DDL是然並卵的。
如果真的需要執行一系列DDL語句,如何避免中間發生了錯誤,再次執行時某些語句無法重覆執行造成的錯誤?
其實還是一樣的思路,將執行過的DDL存起來,如果判斷已經執行過了,就跳過,繼續判斷下一個DDL,花了一小會驗證了一下這個思路。

#!/usr/bin/python3
import pymysql
import zlib
'''
-- dll record log
CREATE TABLE ddl_execute_record
(
    id                     INT AUTO_INCREMENT PRIMARY KEY,
    db_name                 VARCHAR(50),
    ddl_statement         LONGTEXT,
    ddl_hash                BIGINT,
    execute_status     TINYINT COMMENT '0:init;1:successful;-1:fail',
    create_datetime     DATETIME(6),
    last_update            DATETIME(6)
);
'''

class mysql_release(object):
    def __init__(self):
        return

    def __init__(self, host=None, port=None,user=None,password=None,db=None,charset=None):
        self.host = host
        self.port = port
        self.user = user
        self.password = password
        self.db = db
        self.charset = charset

    def get_conn(self):
        if not self.db:
            raise(NameError,"no database info")
        conn = pymysql.connect(host=self.host,  port=self.port,  user=self.user,password=self.password, db=self.db, charset=self.charset)
        if not conn:
            raise(NameError,"conn database fail")
        else:
            return conn

    def execute_noquery(self,str_sql):
        try:
            conn = self.get_conn()
            cursor =conn.cursor()
            cursor.execute(str_sql)
            last_rowid = cursor.lastrowid
            conn.commit()
        except  Exception as err:
            raise
        return last_rowid

    def execute_query(self,str_sql):
        list = None
        try:
            conn = self.get_conn()
            cursor =conn.cursor()
            cursor.execute(str_sql)
            list = cursor.fetchall()
        except  Exception as err:
            print(err)
        return list

    def execute_statement(self,sql_statement):
        #尼瑪這個hash不好使,忽略先,與記憶體地址有關,這裡目的是對sql語句的字元串進行固定hash運算,然後基於hash值作比較
        ddl_hash = hash(sql_statement)
        ddl_log_search = '''select * from db_release_audit.ddl_execute_record where ddl_statement = '{0}' and execute_status=1 '''.format(sql_statement.strip())
        ddl_log_insert = '''insert into db_release_audit.ddl_execute_record(db_name,ddl_statement,ddl_hash,execute_status,create_datetime,last_update) 
                    values ('{0}','{1}',{2},{3},now(6),now(6))'''.format('master',sql_statement.strip(),ddl_hash,0)
        ddl_log = self.execute_query(ddl_log_search)
        if not ddl_log:
            last_rowid = self.execute_noquery(ddl_log_insert)
            if last_rowid:
                update_statment = ""
                try:
                    self.execute_noquery(sql_statement)
                    # update sql statement execute successful
                    update_statment = '''update db_release_audit.ddl_execute_record set execute_status = 1,last_update = now(6) where id = {0}'''.format(last_rowid)
                except Exception as err:
                    # update sql statement execute fail
                    update_statment = '''update db_release_audit.ddl_execute_record set execute_status = -1,last_update = now(6) where id = {0}'''.format(last_rowid)
                    print(err)
                finally:
                    self.execute_noquery(update_statment)
            else:
                print('record log fail')
        else:
            print('the statment executed info: '+str(ddl_log[0]))


if __name__ == '__main__':
    dict_conn = {'host': '***。***。***。***', 'port': 3306, 'user': 'root', 'password': '***', 'db': '***', 'charset':'utf8mb4'}
    # parse db name and sql statement from sql file or anywhere
    db_name = 'user_database_name'
    sql_statement = ''' 
    create table t_1 (id int primary key);
    '''
    release_obj = mysql_release(host=dict_conn['host'], port=dict_conn['port'], user=dict_conn['user'],password=dict_conn['password'], db=dict_conn['db'], charset=dict_conn['charset'])
    try:
        release_obj.execute_statement(sql_statement)
    except Exception as err:
        print(err)
        raise

1,這裡的DDL和DML一樣無法實現一個事務性操作,除非DDL執行完,記錄日誌的過程中發生了錯誤,造成實際DDL與日誌不一致的情況
2,這裡不允許重覆執行同一條DDL語句:如果真的存在1加欄位 ,2 刪欄位,3 繼續加同樣的欄位,這種情況可以通過增加時間條件來規避錯誤的判斷,因為不太可出現上述的連續三個步驟
3,被執行的多個DDL或者是DML語句,可能基於sql文本文件解析或者其他方式,這個過程並沒有什麼難點

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Ansible playbook Vault 加密詳解與使用案例 主機規劃 添加用戶賬號 說明: 1、 運維人員使用的登錄賬號; 2、 所有的業務都放在 /app/ 下「yun用戶的家目錄」,避免業務數據亂放; 3、 該用戶也被 ansible 使用,因為幾乎所有的生產環境都是禁止 root 遠程登 ...
  • Ansible-自動化運維工具 ansible簡介 ansible是新出現的自動化運維工具,基於Python開發,集合了眾多運維工具(puppet、chef、func、fabric)的優點,實現了批量系統配置、批量程式部署、批量運行命令等功能。 ansible是基於模塊工作的,本身沒有批量部署能力, ...
  • 從別人哪裡Clone來的項目 git clone https://github.com/XXX/SimpleUI 進入該文件內 cd SimpleUI 刪除原有的.git信息,建議sudo sudo rm -r .git 初始化.git git init 將本地代碼添加到倉庫 git add .// ...
  • 這篇只是簡單記錄自己在Win10下另安裝Ubuntu系統。 不是教程,因為不會。 推薦一個教程:https://blog.csdn.net/weixin_37029453/article/details/80526732 因為想更好的學習使用Linux,便想到在電腦上另外安裝一個Linux系統(wi ...
  • 在這之前肯定很多人都接觸過Linux管理面板:寶塔,寶塔的確非常方便而且好用,安裝也簡單,複製粘貼幾句命令即可安裝完成,且提供免費版。今天呢,民工哥向大家介紹另一個Linux的伺服器管理面板——AppNode,功能豐富,也提供免費版,且是永久免費! 官方地址:https://www.appnode. ...
  • "Linux動態頻率調節系統CPUFreq之二:核心(core)架構與API" 上一節中,我們大致地講解了一下CPUFreq在用戶空間的sysfs介面和它的幾個重要的數據結構,同時也提到,CPUFreq子系統把一些公共的代碼邏輯組織在一起,構成了CPUFreq的核心部分,這些公共邏輯向CPUFreq ...
  • "Linux動態頻率調節系統CPUFreq之一:概述" 隨著技術的發展,我們對CPU的處理能力提出了越來越高的需求,晶元廠家也對製造工藝不斷地提升。現在的主流PC處理器的主頻已經在3GHz左右,就算是智能手機的處理器也已經可以工作在1.5GHz以上,可是我們並不是時時刻刻都需要讓CPU工作在最高的主 ...
  • 1.Linux環境下安裝Redis (1)下載Redis安裝包,並解壓到安裝路徑【沒有wget就使用yum install wget安裝】 [root@localhost ~]#wget http://download.redis.io/releases/redis-5.0.7.tar.gz [ro ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...