欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mysql遷移到TiDB雙寫數(shù)據(jù)庫兜底方案詳解

 更新時(shí)間:2023年01月03日 14:41:38   作者:石磊  
這篇文章主要為大家介紹了Mysql遷移到TiDB雙寫數(shù)據(jù)庫兜底方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

TiDB 作為開源 NewSQL 數(shù)據(jù)庫的典型代表之一,同樣支持 SQL,支持事務(wù) ACID 特性。在通訊協(xié)議上,TiDB 選擇與 MySQL 完全兼容,并盡可能兼容 MySQL 的語法。因此,基于 MySQL 數(shù)據(jù)庫開發(fā)的系統(tǒng),大多數(shù)可以平滑遷移至 TiDB,而幾乎不用修改代碼。對用戶來說,遷移成本極低,過渡自然。

然而,仍有一些 MySQL 的特性和行為,TiDB 目前暫時(shí)不支持或表現(xiàn)與 MySQL 有差異。除此之外,TiDB 提供了一些擴(kuò)展語法和功能,為用戶提供更多的便利。

TiDB 仍處在快速發(fā)展的道路上,對 MySQL 功能和行為的支持方面,正按 路線圖 的規(guī)劃在前行。

兼容策略

先從總體上概括 TiDB 和 MySQL 兼容策略,如下表:

通訊協(xié)議SQL語法功能和行為
完全兼容兼容絕大多數(shù)兼容大多數(shù)

截至 4.0 版本,TiDB 與 MySQL 的區(qū)別總結(jié)如下表:

?MySQLTiDB
隔離級別支持讀未提交、讀已提交、可重復(fù)讀、串行化,默認(rèn)為可重復(fù)讀樂觀事務(wù)支持快照隔離,悲觀事務(wù)支持快照隔離和讀已提交
鎖機(jī)制悲觀鎖樂觀鎖、悲觀鎖
存儲過程支持不支持
觸發(fā)器支持不支持
事件支持不支持
自定義函數(shù)支持不支持
窗口函數(shù)支持部分支持
JSON支持不支持部分 MySQL 8.0 新增的函數(shù)
外鍵約束支持忽略外鍵約束
字符集?只支持 ascii、latin1、binary、utf8、utf8mb4
增加/刪除主鍵支持通過 alter-primary-key 配置開關(guān)提供
CREATE TABLE tblName AS SELECT stmt支持不支持
CREATE TEMPORARY TABLE支持TiDB 忽略 TEMPORARY 關(guān)鍵字,按照普通表創(chuàng)建
DML affected rows支持不支持
AutoRandom 列屬性不支持支持
Sequence 序列生成器不支持支持

三種方案比較

雙寫方案:同時(shí)往mysql和tidb寫入數(shù)據(jù),兩個(gè)數(shù)據(jù)庫數(shù)據(jù)完全保持同步

•優(yōu)點(diǎn):此方案最安全,作為兜底方案不需擔(dān)心數(shù)據(jù)庫回滾問題,因?yàn)閿?shù)據(jù)完全一致,可以無縫回滾到mysql

•缺點(diǎn):新方案,調(diào)研方案實(shí)現(xiàn),成本較高

讀寫分離:數(shù)據(jù)寫入mysql,從tidb讀,具體方案是切換到線上以后,保持讀寫分離一周時(shí)間左右,這一周時(shí)間用來確定tidb數(shù)據(jù)庫沒有問題,再把寫操作也切換到tidb

•優(yōu)點(diǎn): 切換過程,mysql和tidb數(shù)據(jù)保持同步,滿足數(shù)據(jù)回滾到mysql方案

•缺點(diǎn):mysql和tidb數(shù)據(jù)庫同步存在延時(shí),對部分寫入數(shù)據(jù)要求實(shí)時(shí)查詢的會導(dǎo)致查詢失敗,同時(shí)一旦整體切換到tidb,無法回切到mysql

直接切換:直接一步切換到tidb

•優(yōu)點(diǎn):切換過程最簡單,成本最低

•缺點(diǎn):此方案沒有兜底方案,切換到tidb,無法再回切到mysql或者同步數(shù)據(jù)回mysql風(fēng)險(xiǎn)較大,無法保證數(shù)據(jù)是否可用

Django雙寫mysql與tidb策略

settings.py中新增配置
# Dev Database settings
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'name',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'db',
    },
    'replica': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'name',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'db',
    },
    'bak': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'name',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'db',
    },
}
# 多重寫入數(shù)據(jù)庫配置
MULTI_WRITE_DB = "bak"

雙寫中間件 basemodel.py

import copy
import logging
import traceback
from django.db import models, transaction, router
from django.db.models.deletion import Collector
from django.db.models import sql
from django.db.models.sql.constants import CURSOR
from jcdp.settings import MULTI_WRITE_DB, DATABASES
multi_write_db = MULTI_WRITE_DB
# 重寫QuerySet
class BaseQuerySet(models.QuerySet):
    def create(self, **kwargs):
        return super().create(**kwargs)
    def update(self, **kwargs):
        try:
            rows = super().update(**kwargs)
            if multi_write_db in DATABASES:
                self._for_write = True
                query = self.query.chain(sql.UpdateQuery)
                query.add_update_values(kwargs)
                with transaction.mark_for_rollback_on_error(using=multi_write_db):
                    query.get_compiler(multi_write_db).execute_sql(CURSOR)
        except Exception:
            logging.error(traceback.format_exc())
            raise
        return rows
    def delete(self):
        try:
            deleted, _rows_count = super().delete()
            if multi_write_db in DATABASES:
                del_query = self._chain()
                del_query._for_write = True
                del_query.query.select_for_update = False
                del_query.query.select_related = False
                collector = Collector(using=multi_write_db)
                collector.collect(del_query)
                collector.delete()
        except Exception:
            logging.error(traceback.format_exc())
            raise
        return deleted, _rows_count
    def raw(self, raw_query, params=None, translations=None, using=None):
        try:
            qs = super().raw(raw_query, params=params, translations=translations, using=using)
            if multi_write_db in DATABASES:
                super().raw(raw_query, params=params, translations=translations, using=multi_write_db)
        except Exception:
            logging.error(traceback.format_exc())
            raise
        return qs
    def bulk_create(self, objs, batch_size=None, ignore_conflicts=False):
        try:
            for obj in objs:
                obj.save()
        except Exception:
            logging.error(traceback.format_exc())
            raise
        # objs = super().bulk_create(objs, batch_size=batch_size, ignore_conflicts=ignore_conflicts)
        # if multi_write_db in DATABASES:
        #     self._db = multi_write_db
        #     super().bulk_create(objs, batch_size=batch_size, ignore_conflicts=ignore_conflicts)
        return objs
    def bulk_update(self, objs, fields, batch_size=None):
        try:
            super().bulk_update(objs, fields, batch_size=batch_size)
            if multi_write_db in DATABASES:
                self._db = multi_write_db
                super().bulk_update(objs, fields, batch_size=batch_size)
        except Exception:
            logging.error(traceback.format_exc())
            raise
class BaseManager(models.Manager):
    _queryset_class = BaseQuerySet
class BaseModel(models.Model):
    objects = BaseManager()
    class Meta:
        abstract = True
    def delete(
            self, using=None, *args, **kwargs
    ):
        try:
            instance = copy.deepcopy(self)
            super().delete(using=using, *args, **kwargs)
            if multi_write_db in DATABASES:
                super(BaseModel, instance).delete(using=multi_write_db, *args, **kwargs)
        except Exception:
            logging.error(traceback.format_exc())
            raise
    def save_base(self, raw=False, force_insert=False,
                  force_update=False, using=None, update_fields=None):
        try:
            using = using or router.db_for_write(self.__class__, instance=self)
            assert not (force_insert and (force_update or update_fields))
            assert update_fields is None or update_fields
            cls = self.__class__
            # Skip proxies, but keep the origin as the proxy model.
            if cls._meta.proxy:
                cls = cls._meta.concrete_model
            meta = cls._meta
            # A transaction isn't needed if one query is issued.
            if meta.parents:
                context_manager = transaction.atomic(using=using, savepoint=False)
            else:
                context_manager = transaction.mark_for_rollback_on_error(using=using)
            with context_manager:
                parent_inserted = False
                if not raw:
                    parent_inserted = self._save_parents(cls, using, update_fields)
                self._save_table(
                    raw, cls, force_insert or parent_inserted,
                    force_update, using, update_fields,
                )
            if multi_write_db in DATABASES:
                super().save_base(raw=raw,
                                  force_insert=raw,
                                  force_update=force_update,
                                  using=multi_write_db,
                                  update_fields=update_fields)
            # Store the database on which the object was saved
            self._state.db = using
            # Once saved, this is no longer a to-be-added instance.
            self._state.adding = False
        except Exception:
            logging.error(traceback.format_exc())
            raise

上述配置完成以后,在每個(gè)應(yīng)用的models.py中引用新的BaseModel類作為模型基類即可實(shí)現(xiàn)雙寫目的

class DirectoryStructure(BaseModel):
    """
    目錄結(jié)構(gòu)
    """
    view = models.CharField(max_length=128, db_index=True)  # 視圖名稱 eg:部門視圖 項(xiàng)目視圖
    sub_view = models.CharField(max_length=128, unique=True, db_index=True)  # 子視圖名稱
    sub_view_num = models.IntegerField()  # 子視圖順序號

注:目前該方法尚不支持多對多模型的雙寫情景,如有業(yè)務(wù)需求,還需重寫ManyToManyField類,方法參考猴子補(bǔ)丁方式

遷移數(shù)據(jù)庫過程踩坑記錄

TIDB配置項(xiàng)差異:確認(rèn)數(shù)據(jù)庫配置:ONLY_FULL_GROUP_BY 禁用 (mysql默認(rèn)禁用)

TIDB不支持事務(wù)savepoint,代碼中需要顯式關(guān)閉savepoint=False

TIDB由于是分布式數(shù)據(jù)庫,對于自增主鍵字段的自增策略與mysq有差異,若業(yè)務(wù)代碼會與主鍵id關(guān)聯(lián),需要注意

以上就是Mysql遷移到TiDB雙寫數(shù)據(jù)庫兜底方案詳解的詳細(xì)內(nèi)容,更多關(guān)于Mysql遷移TiDB雙寫數(shù)據(jù)庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mysql數(shù)據(jù)庫值的添加、修改、刪除及清空操作實(shí)例

    Mysql數(shù)據(jù)庫值的添加、修改、刪除及清空操作實(shí)例

    這篇文章主要給大家介紹了關(guān)于Mysql數(shù)據(jù)庫值的添加、修改、刪除及清空操作的相關(guān)資料,文中通過示例代碼以及圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2021-06-06
  • MySQL中CONCAT和GROUP_CONCAT方法的區(qū)別詳解

    MySQL中CONCAT和GROUP_CONCAT方法的區(qū)別詳解

    本文主要介紹了MySQL中CONCAT和GROUP_CONCAT方法的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Mysql啟動報(bào)ERROR:2002的分析與解決

    Mysql啟動報(bào)ERROR:2002的分析與解決

    這篇文章主要給大家介紹了關(guān)于Mysql啟動時(shí)報(bào)ERROR:2002問題的分析與解決方法,文中通過示例代碼介紹將該問題分析的非常詳細(xì),對同樣遇到這個(gè)問題的朋友們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • 淺談MySQL的性能優(yōu)化

    淺談MySQL的性能優(yōu)化

    這篇文章主要介紹了淺談MySQL的性能優(yōu)化,MySQL性能優(yōu)化是通過對數(shù)據(jù)庫的配置、查詢優(yōu)化以及索引優(yōu)化等手段提高數(shù)據(jù)庫的響應(yīng)速度和處理能力,本文從多個(gè)層面對mysql性能優(yōu)化進(jìn)行了小結(jié),需要的朋友可以參考下
    2023-08-08
  • CentOS 7下mysql 5.7 安裝教程

    CentOS 7下mysql 5.7 安裝教程

    這篇文章主要為大家詳細(xì)介紹了CentOS 7下mysql 5.7 安裝教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡單實(shí)例

    mysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡單實(shí)例

    下面小編就為大家?guī)硪黄猰ysql 搜尋附近N公里內(nèi)數(shù)據(jù)的簡單實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • MYSQL與SQLserver之間存儲過程的轉(zhuǎn)換方式

    MYSQL與SQLserver之間存儲過程的轉(zhuǎn)換方式

    這篇文章主要介紹了MYSQL與SQLserver之間存儲過程的轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • MySQL的主鍵命名策略相關(guān)

    MySQL的主鍵命名策略相關(guān)

    這篇文章主要介紹了MySQL的主鍵命名策略的的相關(guān)資料,幫助大家更好的理解和使用MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下
    2021-01-01
  • Linux下卸載MySQL數(shù)據(jù)庫

    Linux下卸載MySQL數(shù)據(jù)庫

    如何在Linux平臺卸載MySQL呢?這篇文章主要介紹了Linux下卸載MySQL數(shù)據(jù)庫的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • MySQL安裝配置以及安裝失敗解決過程

    MySQL安裝配置以及安裝失敗解決過程

    我們在下載完MYSQL時(shí),安裝可能會遇到或大或小的問題,下面這篇文章主要給大家介紹了關(guān)于MySQL安裝配置以及安裝失敗解決的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04

最新評論