日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

歷史數據遷移

  • 項目地址:https://gitee.com/xl-echo/dataMigration

歷史遷移解決方案。微服務的架構為基礎,使用多種設計模式,如:單利、橋接、工廠、模板、策略等。其中涉及的核心技術有,多線程、過濾器等。致力于解決MySQL大表遷移的問題。提供多種遷移模式,如:庫到庫、庫到文件再到庫等! Historical migration solution. Based on the architecture of microservices, multiple design patterns are used, such as simple interest, bridge, factory, template, strategy, etc. The core technologies involved include multithreading, filters, etc. It is committed to solving the problem of MySQL large table migration. Provide multiple migration modes, such as library to library, library to file, and then to library

開發環境

工具

版本

描述

IDEA

2019.2

 

JDK

1.8

 

MySQL

5.7

5.7 +

技術框架

技術

描述

用途

備注

SpringBoot

基礎框架

構建系統架構

 

MyBatis

ORM框架

構建數據層

 

JDBC

構建外部數據源

用于配置遷移

 

Maven

項目管理

項目管理

 

ExecutorService

線程池

分批執行任務

 

需求分析和結構整理

歷史數據遷移,顧名思義就是將某個地方的歷史數據轉移到另外一個地方。需求明了,就一句話能夠概括完整。其中要涉及的技術,其實也不是很難。無非是設計一個復制數據的流程,設計一個插入數據的流程和一個刪除數據的流程。有三個流程去完成這件事情,節本就能搞定。當然,我們不能僅僅考慮把流程設計的能執行就好,還需要保障流程的健全性。雖然三個流程,就有了可行性,并且在實際應用中基本這三個流程是不可缺少的。但是我們必須保證數據安全,流程安全,并且可以直接,拓展,這樣更有利于我們的項目的接入,使用。關鍵是解決了數據丟失,數據復制不完整的問題。 支付行業,支付行業中對遷移的應用,都是必須保障數據安全的,寧可數據遷移不成功,也必須保障數據不因為遷移二缺少,所以以上這三個流程并不完善。 檢查流程的加入,處理以上說的三個流程,本次需求設計了另外兩個檢查流程,用來檢查查詢出來的數據和插入的數據是否完整,這樣保障了源庫數據和遷移到目標庫的數據一致 事務設計,遷移過程中可能出現的各種系統異常會導致數據復制問題,如果不加入事務的考慮,會導致數據丟失,這也是需要解決的問題。 內存管理,在遷移過成功,如果一次性加載所以需要遷移的數據到項目或者內存中,基本都會導致OOM之類的問題,這也是我們要仔細考慮到的問題。

代碼流程設計如下

數據遷移的辦法有很多種,但是其目的都是一致的,都是將數據從源位置,轉移到目標位置。項目中設計了一個通用流程來專門管理多種遷移辦法,并且用到了多種設計模式來讓代碼更加簡潔和更便于管理遷移。讓代碼靈活,并且方便二開或者多開。通用流程執行示意圖如下,也是該項目主要流程

 

簡潔的代碼和流程,其實得益于原本的流程設計和設計模式的運用。

遷移物理架構圖解

 

數據庫模型設計

數據庫模型的設計,是我們整個流程的核心,如果沒有這個數據庫的模型,我們的代碼將會亂成一團。也正是數據庫先一步做出了規定,讓我們的代碼能夠更加的靈活。

表名詳解

  • transfer_data_task: 遷移任務。在該表中配置對應的遷移任務。該表僅做任務管理,并且指定遷移的執行模式
  • transfer_database_config: 數據源配置。一個遷移任務對應兩個數據源配置,一個源數據庫配置,一個目標數據庫配置,由database_direction字段控制是需要源庫還是目標,字段有兩個值source,target
  • transfer_select_config: 源庫表配置。遷移任務通過task_id關聯到源庫查詢配置,其中主要配置信息就是我們需要查詢的表,表字段有哪些,查詢條件是什么,一次性查詢多少條。最終會在代碼中拼接成為查詢源庫的SQL語句。 拼接規則:```select 配置的字段 from 配置的表名 where 配置的條件 limit 配置的限制條數````,目前一個定時任務僅支持配置一個表的遷移。
  • transfer_insert_config: 目標庫表配置。遷移任務通過task_id關聯到目標庫配置,其中主要配置信息就是我們需要插入的表,需要插入的字段有哪些,查詢的條件是什么,一次性插入多少條。最終會在代碼中拼接成為插入目標庫的SQL語句。 拼接規則: insert into 配置的表名 (配置的字段) values (...),(),()...配置的插入條數據控制value后面有多少個值
  • transfer_log: 日志記錄表。在整個遷移過程中,log是不可或缺的,這里設計了任務遷移的日志跟蹤。

日志的選型,日志設計(鏈路追蹤)

  • 日志框架用的是:Log4j 這是一個由JAVA編寫可靠、靈活的日志框架,是Apache旗下的一個開源項目。最開始沒有做過多的考慮,選用的原則就一個,市面上實際使用多的,并且開源的框架就行。不過日志的輸出確實精心設計的。 在我們很多的項目當中,一個接口的日志,從前到后可能會有很多。在排查的過程中,我們也基本是跟著日志從代碼最前面往代碼最后面推。這個流程在實際應用當中會略微有一些問題,特別是有新線程,或者并發、大流量的情況基本就很難能一句一句排查了。因為你看到的上一句和下一句,并不一定是一個線程寫出來的。所以這個時候我們需要去精心設計整個日志輸出,讓他能夠在各種環境中一目了然。 TLog為啥要去自己設計,不直接使用TLog這樣的鏈路最終日志框架呢?這里有一個問題,TLog鏈路追蹤會在我們的項目中為每一次執行創建一個唯一鍵,不管傳遞多少個服務,只要都整合了TLog就能按照唯一鍵一路追蹤下去。這里沒有使用的原因只有一個,那就是為了方便管理,并且能夠更加快捷的訪問到日志。我們對日志的輸出設計不僅僅做了鏈路追蹤的流程,還講唯一鍵直接存入了庫里面,這樣我們更加的便捷去追蹤我們的遷移任務。 自己設計的日志追蹤流程,確實會更加的靈便,而且由于放入了庫中與log表關聯起來,更便于我們查詢。當然他也會有缺點,對于日志這一塊我們如果忘記這條規則,會導致我們鏈路追蹤在鏈路中斷裂 按照鏈路追蹤的模式我們設計了日志,其中最關鍵的環節就兩個
  • 唯一鍵的設計(雪花算法、UUID) 雪花算法和UUID的選型,剛開始自己實現了一個雪花算法
package com.echo.one.utils.uuid;

/**
 * 雪花算法
 *
 * @author echo
 * @date 2022/11/16 11:09
 */
public class SnowflakeIdWorker {
    /**
     * 開始時間截 (2015-01-01)
     */
    private final long twepoch = 1420041600000L;
    /**
     * 機器id所占的位數
     */
    private final long workerIdBits = 5L;
    /**
     * 數據標識id所占的位數
     */
    private final long datacenterIdBits = 5L;
    /**
     * 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /**
     * 支持的最大數據標識id,結果是31
     */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    /**
     * 序列在id中占的位數
     */
    private final long sequenceBits = 12L;
    /**
     * 機器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;
    /**
     * 數據標識id向左移17位(12+5)
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    /**
     * 時間截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    /**
     * 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 工作機器ID(0~31)
     */
    private long workerId;
    /**
     * 數據中心ID(0~31)
     */
    private long datacenterId;
    /**
     * 毫秒內序列(0~4095)
     */
    private long sequence = 0L;
    /**
     * 上次生成ID的時間截
     */
    private long lastTimestamp = -1L;

    /**
     * 構造函數
     *
     * @param workerId     工作ID (0~31)
     * @param datacenterId 數據中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 獲得下一個ID (該方法是線程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        // 如果當前時間小于上一次ID生成的時間戳,說明系統時鐘回退過這個時候應當拋出異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        // 如果是同一時間生成的,則進行毫秒內序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒內序列溢出
            if (sequence == 0) {
                //阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        // 時間戳改變,毫秒內序列重置
        else {
            sequence = 0L;
        }
        // 上次生成ID的時間截
        lastTimestamp = timestamp;
        // 移位并通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     *
     * @param lastTimestamp 上次生成ID的時間截
     * @return 當前時間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒為單位的當前時間
     *
     * @return 當前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
}
復制代碼

這個算法其實還是很優秀的,其中的分布式id保障唯一設計也確實能夠很有效的幫組我們獲取鏈路追蹤的唯一鍵。不過在項目中也暴露出來了兩個問題: 1、ID重復(雪花算法就是獲取唯一id的,為啥會出現重復的呢?后面解釋) 2、性能瓶頸

在使用過程中雪花算法出現重復,導致唯一鍵報錯。 雪花算法出現重復的可能就幾種: 1)、因為雪花算法強依賴機器時鐘,所以難以避免受到時鐘回撥的影響,有可能產生ID重復。 2)、同一臺機器同一毫秒需要生成多個Id,并且對long workerId, long datacenterId控制不完善導致 第一種情況這基本沒法避免,不過時鐘回撥很久才發生一次。所以問題還是出現在第二種看情況,主要還是對代碼的不完善導致。后面也正是因為代碼的不完善直接改用UUID。UUID就一行代碼就能夠解決的事情,這里還要不斷的去完善設計的代碼。

UUID確實簡潔,選型UUID看中的也是這一點,同時也還有其他因素。在整個項目中由于代碼的靈活性和整個代碼的開放性,在執行的過程中可能會出現多個線程,并行執行,并且為了資源能夠更高效的執行,那么對鎖的應用是要慎重考慮的。雪花算法中,使用了sync同步鎖,當我們使用多線程去執行代碼的時候,同步鎖會成為重量級鎖,并且成為我們項目執行的性能瓶頸。這樣是最終選擇UUID的重要因素

  • 鏈路的設計

鏈路的設計目前還是極為簡單的手動將需要加入到整個鏈路中的日志加上唯一鍵。 鏈路的設計比較簡單,而且整個流程鏈路中,只需要在相應流程模式中進行鏈路的完善即可,比如:在多線程的環境下,怎么去保證當前線程是我原本的前一任務的鏈路線;執行模式中循環執行回來的時候,怎么保證新的流程能夠用原本的唯一件

遷移模式

遷移模式總共三種,分別對應transfer_data_task中transfer_mode的0、1、2,推薦使用模式1

  • 0、庫 -> 文件 -> 庫(不推薦使用)

模式流程圖

 

該模式,可以有效的安全保障數據在遷移過程中,不會出現數據的錯漏,文件相當于備份操作,后期可以直接追溯遷移過程。唯一的缺點就是大表遷移中,如果使用這種模式需要兩個東西,一個是具備localtemp讀寫權限的賬號,一個是足夠的磁盤。大表數據寫入磁盤很暫用空間,在測試中間,嘗試了100w數據,寫入文件完畢后,發現有10G(大表,150字段左右)

  • 1、庫 -> 庫(推薦使用)
  • 2、source.dump copy target.dump(有風險)

模塊設計

dataMigration
-----bin
---------startup.sh
-----doc
---------blog.sql
---------blogbak.sql
---------community.sql
-----src
---------main
-------------java
-----------------com.echo.one
---------------------common
-------------------------base
-----------------------------TransferContext.java
-----------------------------TransferDataContext.java
-----------------------------BeanUtilsConfig.java
-------------------------constant
-----------------------------DataMigrationConstant.java
-------------------------enums
-----------------------------DatabaseColumnType.java
-----------------------------DatabaseDirection.java
-----------------------------FormatPattern.java
-----------------------------StatusCode.java
-----------------------------TaskStatus.java
-------------------------exception
-----------------------------DataMigrationException.java
-------------------------factory
-----------------------------DataMigrationBeanFactory.java
-------------------------framework
-----------------------------config
---------------------------------RemoveDruidAdConfig.java
-----------------------------filter
---------------------------------WebMvcConfig.java
-----------------------------handler
---------------------------------GlobalExceptionHandler.java
-----------------------------result
---------------------------------Result.java
---------------------controller
-------------------------TransferDatabaseConfigController.java
-------------------------TransferDataTaskController.java
-------------------------TransferHealthy.java
-------------------------TransferInsertConfigController.java
-------------------------TransferLogController.java
-------------------------TransferSelectConfigController.java
---------------------dao
-------------------------TransferDatabaseConfigMApper.java
-------------------------TransferDataTaskMapper.java
-------------------------TransferInsertConfigMapper.java
-------------------------TransferLogMapper.java
-------------------------TransferSelectConfigMapper.java
---------------------DataMigrationApplication.java
---------------------job
-------------------------DataTaskThread.java
-------------------------TransferDataTaskJob.java
-------------------------TransferLogJob.java
---------------------po
-------------------------TransferDatabaseConfig.java
-------------------------TransferDataTask.java
-------------------------TransferInsertConfig.java
-------------------------TransferLog.java
-------------------------TransferSelectConfig.java
---------------------processer
-------------------------CheckInsertDataProcessModeOne.java
-------------------------CheckInsertDataProcessModeTwo.java
-------------------------CheckInsertDataProcessModeZero.java
-------------------------CheckInsertDataTransactionalProcessModeThree.java
-------------------------CheckSelectDataProcessModeOne.java
-------------------------CheckSelectDataProcessModeTwo.java
-------------------------CheckSelectDataProcessModeZero.java
-------------------------CheckSelectDataTransactionalProcessModeThree.java
-------------------------DeleteDataProcessModeOne.java
-------------------------DeleteDataProcessModeTwo.java
-------------------------DeleteDataProcessModeZero.java
-------------------------DeleteDataTransactionalProcessModeThree.java
-------------------------InsertDataProcessModeOne.java
-------------------------InsertDataProcessModeTwo.java
-------------------------InsertDataProcessModeZero.java
-------------------------InsertDataProcess.NEThread.java
-------------------------InsertDataTransactionalProcessModeThree.java
-------------------------ProcessMode.java
-------------------------SelectDataProcessModeOne.java
-------------------------SelectDataProcessModeTwo.java
-------------------------SelectDataProcessModeZero.java
-------------------------SelectDataTransactionalProcessModeThree.java
---------------------service
-------------------------imp
-----------------------------TransferDatabaseConfigServiceImpl.java
-----------------------------TransferDataTaskServiceImpl.java
-----------------------------TransferHealthyServiceImpl.java
-----------------------------TransferInsertConfigServiceImpl.java
-----------------------------TransferLogServiceImpl.java
-----------------------------TransferSelectConfigServiceImpl.java
-------------------------TransferDatabaseConfigService.java
-------------------------TransferDataTaskService.java
-------------------------TransferHealthyService.java
-------------------------TransferInsertConfigService.java
-------------------------TransferLogService.java
-------------------------TransferSelectConfigService.java
---------------------utils
-------------------------DateUtils.java
-------------------------jdbc
-----------------------------JdbcUtils.java
-------------------------SpringContextUtils.java
-------------------------thread
-----------------------------CommunityThreadFactory.java
-------------------------thread
-----------------------------DataMigrationRejectedExecutionHandler.java
-------------------------thread
-----------------------------ExecutorServiceUtil.java
-------------------------uuid
-----------------------------SnowflakeIdWorker.java
---------main
-------------resources
-----------------application.yml
-----------------banner.txt
-----------------logback-spring.xml
-----------------mapper
---------------------TransferDatabaseConfigMapper.xml
---------------------TransferDataTaskMapper.xml
---------------------TransferInsertConfigMapper.xml
---------------------TransferLogMapper.xml
---------------------TransferSelectConfigMapper.xml
-----LICENSE
-----pom.xml
-----README.md
-----dataMigration.iml
復制代碼

設計模式

如果不考慮設計模式,直接硬編碼在我們項目當中會有很多的藕合,也會導致整個代碼的可讀性很差。如下面的代碼: 首先來看項目類的代碼執行的步驟: 1、查詢源庫數據 2、檢查查詢出的源庫數據 3、插入目標數據庫 4、檢查插入 5、刪除源庫數據

    if(mode = 1) ...
        selectData();
        checkSelectData();
        insertData();
        checkInsertData();
        deleteData();
    if(mode = 2) ...
        selectData();
        checkSelectData();
        insertData();
        checkInsertData();
        deleteData();
    if(mode = 3) ...
        selectData();
        checkSelectData();
        insertData();
        checkInsertData();
        deleteData();
    ...
復制代碼

每次新增一個mode,我們就需要手動去修改代碼,增加一個if,極度不易于拓展,而且if的多少影響閱讀和美觀。當我們加入設計模式之后,這樣的if可以直接被取消。

  • 橋接模式的應用 什么是橋接模式?橋接(Bridge)是用于把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬于結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立于接口實現類。這兩種類型的類可被結構化改變而互不影響。

可以在上面的代碼中看到,假若我們能消除if else很多的代碼就不會重復,比如其中的固定執行流程。然后每一個流程代碼,我們設計成為一個固定的模板,每個模板都有一個固定的方法,這個時候,對于具體的實現獨立出來就完美的解決了這樣的問題。

首先解決第一個問題:將步驟代碼抽象出來,不管哪個模式,都要走定義好的步驟方法。但是具體實現每個模式互不干擾

public abstract class ProcessMode {

    public final void invoke(TransferContext transferContext) {
        try {
            handler(transferContext);
        } catch (DataMigrationException e) {
            throw e;
        } catch (Exception e) {
            throw new DataMigrationException("system error, msg: " + e.getMessage());
        }
    }

    protected abstract void handler(TransferContext transferContext);

}
復制代碼

這里直接抽象出來一個類,保障每個流程步驟都屬于他的實現類,這樣解決了流程步驟的多實現互不干擾。

然后解決if else:有了抽象類了,我們這里直接使用mode去定位對應的具體實現。當然,這里要配合一個小巧的類的注入名的修改。原本靠if else檢索位置,現在直接采用類型來定位,這樣就不需要使用判斷來做了。類加載到容器中都有自己固定的名字,這里我們將每一個mode對應的實現類設計成為帶有mode的類,設計規則:process + 功能 + mode號, 然后我們按照類的實現自動加載使用對應的實現類

ProcessMode sourceBean = SpringContextUtils.getBean("process.selectData." + transferContext.getTransferDataTask().getTransferMode(), ProcessMode.class);
復制代碼

在橋接模式中,我們可以看到需要一個橋(類)來連接對應的實現

 

在我們的代碼中其實也正是使用了這種思想,來讓我們的代碼每個mode方式之間互不干擾,但又能和代碼緊密相連接。

 

ProcessMode完美的銜接了我們代碼,充當了我們的橋

  • 單例模式的應用 單利模式很好被理解,單例(Singleton)模式的定義:指一個類只有一個實例,且該類能自行創建這個實例的一種模式。單例模式有 三個特點:1、類只有一個實例對象; 2、該單例對象必須由單例類自行創建; 3、類對外提供一個訪問該單例的全局訪問點;

為什么用單利? 單利最常見的應用場景有 1、windows的Task Manager(任務管理器)就是很典型的單例模式 2、項目中,讀取配置文件的類,一般也只有一個對象。沒有必要每次使用配置文件數據,每次new一個對象去讀取。 3、數據庫連接池的設計一般也是采用單例模式,因為數據庫連接是一種數據庫資源。 4、在Spring中,每個Bean默認就是單例的,這樣做的優點是Spring容器可以管理 。 5、在servlet編程中/spring MVC框架,每個Servlet也是單例 /控制器對象也是單例. 在我們項目中,到哪里的場景很顯然就是第一個,他就是一個任務管理器。作為一個開源的遷移數據項目,我們需要考慮不單單是每次僅執行一個任務,而是每次執行多個,并且每次可能會并發執行很多的任務,當然隨之而來的就是數據安全為題,和任務重復的問題。這個我們后面在做解釋

單利模式又分為:懶漢式、餓漢式。為了保證數據的安全,我們這里直接選用了餓漢式

具體實現如下:

public class ExecutorServiceUtil {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorServiceUtil.class);

    private static ThreadPoolExecutor executorService;

    private static final int INT_CORE_POOL_SIZE = 50;
    private static final int MAXIMUM_POOL_SIZE = 100;
    private static final int KEEP_ALIVE_TIME = 60;
    private static final int WORK_QUEUE_SIZE = 2000;

    static {
        executorService = new ThreadPoolExecutor(
                INT_CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(WORK_QUEUE_SIZE),
                new CommunityThreadFactory(),
                new DataMigrationRejectedExecutionHandler());
    }

    private ExecutorServiceUtil() {
        if(executorService != null){
            throw new DataMigrationException("Reflection call constructor terminates execution!!!");
        }
    }

    public static ExecutorService getInstance() {
        logger.info("queue size: {}", executorService.getQueue().size());
        logger.info("Number of active threads: {}", executorService.getActiveCount());
        logger.info("Number of execution completion threads: {}", executorService.getCompletedTaskCount());
        return executorService;
    }
}
復制代碼

將我們的線程池進行單利化,為的就是解決任務多發或者高發的問題,有序的控制任務內存和執行。既然提到了多發或者高發,肯定需要解決的就是線程安全問題。這里我們不僅使用了單例,還對部分數據操作的類做了數據安全處理

類關系圖

 

對關鍵類都去定義了一個:@Scope(value = "prototype"),使用原型作用域,每個任務過來都會生成一個獨有的類,這樣有效的防止數據共享。

事務的管理

在數據遷移過程中,事務的執行是很重要的,如不能保證數據一致,可能在遷移過程中直接導致數據丟失。

項目中也有簡單的對事務的應用。由于設計模式的問題,拓展能力很強,代碼靈活度也很高。不過接踵而來的就是部分安全代碼的問題。比如:事務。當我們獨立了插入和刪除的時候,我們的事務就被拆分了。那我們需要使用分布式事務,或者事務傳播嗎?

項目中并沒有!

 

原本設計模式之前,直接包含在一個事務內即可,這里我們使用了設計模式之后,行不通了。

現有的事務邏輯

 

不難發現,如果insertData出現問題,insertData的事務會回滾對應的插入數據,當時deleteData繼續執行,這個時候肯定會來帶數據一致性問題。不過這里采用了流程上的控制來解決這個問題。

假若:一個遷移任務只執行一次insertData,deleteData,為了保證數據一致性,我們將這兩個流程設計了固定的執行先后順序,并且,前面一個報錯,后面不在繼續執行。

 

報錯如果出現在刪除,那有什么影響,就一個:那就是源庫的數據有一部分并未被刪除掉,但是肯定是已經遷移到了目標庫,這個時候嚴格意義上來講,數據的遷移并不影響,僅僅只是多出來一份數據,沒有數據安全問題。

總結

每個設計方案都不是完美的,都是巧妙的去設計,靈活的去適應。 假若不斷迭代,問題肯定會不斷出現,新的方案肯定也會不斷的替代老方案。

分享到:
標簽:MySQL
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定