用pt-online-schema-change給大表線上加欄位的時候導致從庫數據丟失的問題

来源:http://www.cnblogs.com/zhulin516114/archive/2017/08/08/7307013.html
-Advertisement-
Play Games

今天同事在主庫在給一個大表的欄位新加了一個索引,因為是大表,所以用了pt-osc工具,在添加完索引沒過多久,開發那邊反應丟數據了。 這個表以前是寫在主庫的,後來不知道是什麼原因改成了寫從庫,也就是說主庫有部分以前的歷史數據,但是新的數據都是寫在從庫上,同事用pt-osc加索引的時候是在主庫加的,於是 ...


今天同事在主庫在給一個大表的欄位新加了一個索引,因為是大表,所以用了pt-osc工具,在添加完索引沒過多久,開發那邊反應丟數據了。

  這個表以前是寫在主庫的,後來不知道是什麼原因改成了寫從庫,也就是說主庫有部分以前的歷史數據,但是新的數據都是寫在從庫上,同事用pt-osc加索引的時候是在主庫加的,於是把主庫的歷史數據同步過來了,從庫上的新數據丟了。

  通過以下例子也以看出為什麼會發生這種情況:

 

>select count(*) from goods;

+----------+

| count(*) |

+----------+

|  1426200 |

+----------+

 

CREATE TABLE `goods` (

  `rec_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `goods_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `add_time` int(11) unsigned NOT NULL DEFAULT '0',

  `is_attention` tinyint(1) NOT NULL DEFAULT '0',

  `wid` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '倉id,預設為1表示中國倉',

  PRIMARY KEY (`rec_id`),

  KEY `user_id` (`user_id`),

  KEY `goods_id` (`goods_id`),

  KEY `is_attention` (`is_attention`)

) ENGINE=InnoDB AUTO_INCREMENT=1721748 DEFAULT CHARSET=utf8  

 

我們先把goods表上的user_id索引刪除:

alter table goods drop key user_id;

 

在主庫上刪除部分數據:

>set sql_log_bin=off;

>delete from goods where rec_id>721747;

Query OK, 848254 rows affected (20.24 sec)

>set sql_log_bin=on;

>select count(*) from test;

+----------+

| count(*) |

+----------+

|   577946 |

+----------+

 

 

測試一:

先直接用alter table語句對goods表的user_id欄位加索引:

>alter table  test add key (user_id);

 

主庫:

show create table test\G

*************************** 1. row ***************************

       Table: test

Create Table: CREATE TABLE `test` (

  `rec_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `goods_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `add_time` int(11) unsigned NOT NULL DEFAULT '0',

  `is_attention` tinyint(1) NOT NULL DEFAULT '0',

  `wid` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '倉id,預設為1表示中國倉',

  PRIMARY KEY (`rec_id`),

  KEY `goods_id` (`goods_id`),

  KEY `is_attention` (`is_attention`),

  KEY `user_id` (`user_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1721748 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

 

>select count(*) from test;

+----------+

| count(*) |

+----------+

|   577946 |

+----------+

1 row in set (0.22 sec)

 

從庫:

show create table test\G

*************************** 1. row ***************************

       Table: test

Create Table: CREATE TABLE `test` (

  `rec_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `goods_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `add_time` int(11) unsigned NOT NULL DEFAULT '0',

  `is_attention` tinyint(1) NOT NULL DEFAULT '0',

  `wid` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '倉id,預設為1表示中國倉',

  PRIMARY KEY (`rec_id`),

  KEY `goods_id` (`goods_id`),

  KEY `is_attention` (`is_attention`),

  KEY `user_id` (`user_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1721748 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

 

>select count(*) from test;

+----------+

| count(*) |

+----------+

|  1426200 |

+----------+

1 row in set (0.47 sec)

 

可以看到直接用alter table加索引的時候從庫的數據沒有變化。

 

測試二:

用pt-osc工作在主庫goods表的user_id列加索引

先刪除user_id列的索引,在主庫上執行:

alter table  test drop index user_id;

 

pt-osc加索引:

pt-online-schema-change --nocheck-replication-filters --recursion-method=none --charset=utf8 --alter "add index (user_id) " h=localhost,P=3306,u=root,p=123456,D=test,t=test --print --execute

 

Successfully altered `test`.`test`. 提示索引加成功。

主庫:

>select count(*) from test;

+----------+

| count(*) |

+----------+

|   577946 |

+----------+

從庫:

>select count(*) from test;

+----------+

| count(*) |

+----------+

|   577946 |

+----------+

可以看到,從庫的數據被主庫覆蓋。

 

為什麼會出現這個原因呢,我們可以具體看一下加索引的過程:

# pt-online-schema-change --nocheck-replication-filters --recursion-method=none --charset=utf8 --alter "add index (user_id) " h=localhost,P=3306,u=root,p=123456,D=test,t=test --print --execute

No slaves found.  See --recursion-method if host master has slaves.

Not checking slave lag because no slaves were found and --check-slave-lag was not specified.

Operation, tries, wait:

  copy_rows, 10, 0.25

  create_triggers, 10, 1

  drop_triggers, 10, 1

  swap_tables, 10, 1

  update_foreign_keys, 10, 1

Altering `test`.`test`...

Creating new table...

CREATE TABLE `test`.`_test_new` (

  `rec_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `goods_id` mediumint(8) unsigned NOT NULL DEFAULT '0',

  `add_time` int(11) unsigned NOT NULL DEFAULT '0',

  `is_attention` tinyint(1) NOT NULL DEFAULT '0',

  `wid` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '倉id,預設為1表示中國倉',

  PRIMARY KEY (`rec_id`),

  KEY `goods_id` (`goods_id`),

  KEY `is_attention` (`is_attention`)

) ENGINE=InnoDB AUTO_INCREMENT=1721748 DEFAULT CHARSET=utf8

Created new table test._test_new OK.

Altering new table...

ALTER TABLE `test`.`_test_new` add index (user_id) 

Altered `test`.`_test_new` OK.

2016-02-01T18:11:48 Creating triggers...

CREATE TRIGGER `pt_osc_test_test_del` AFTER DELETE ON `test`.`test` FOR EACH ROW DELETE IGNORE FROM `test`.`_test_new` WHERE `test`.`_test_new`.`rec_id` <=> OLD.`rec_id`

CREATE TRIGGER `pt_osc_test_test_upd` AFTER UPDATE ON `test`.`test` FOR EACH ROW REPLACE INTO `test`.`_test_new` (`rec_id`, `user_id`, `goods_id`, `add_time`, `is_attention`, `wid`) VALUES (NEW.`rec_id`, NEW.`user_id`, NEW.`goods_id`, NEW.`add_time`, NEW.`is_attention`, NEW.`wid`)

CREATE TRIGGER `pt_osc_test_test_ins` AFTER INSERT ON `test`.`test` FOR EACH ROW REPLACE INTO `test`.`_test_new` (`rec_id`, `user_id`, `goods_id`, `add_time`, `is_attention`, `wid`) VALUES (NEW.`rec_id`, NEW.`user_id`, NEW.`goods_id`, NEW.`add_time`, NEW.`is_attention`, NEW.`wid`)

2016-02-01T18:11:48 Created triggers OK.

2016-02-01T18:11:48 Copying approximately 578405 rows...

INSERT LOW_PRIORITY IGNORE INTO `test`.`_test_new` (`rec_id`, `user_id`, `goods_id`, `add_time`, `is_attention`, `wid`) SELECT `rec_id`, `user_id`, `goods_id`, `add_time`, `is_attention`, `wid` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`rec_id` >= ?)) AND ((`rec_id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 30910 copy nibble*/

SELECT /*!40001 SQL_NO_CACHE */ `rec_id` FROM `test`.`test` FORCE INDEX(`PRIMARY`) WHERE ((`rec_id` >= ?)) ORDER BY `rec_id` LIMIT ?, 2 /*next chunk boundary*/

2016-02-01T18:12:09 Copied rows OK.

2016-02-01T18:12:09 Swapping tables...

RENAME TABLE `test`.`test` TO `test`.`_test_old`, `test`.`_test_new` TO `test`.`test`

2016-02-01T18:12:09 Swapped original and new tables OK.

2016-02-01T18:12:09 Dropping old table...

DROP TABLE IF EXISTS `test`.`_test_old`

2016-02-01T18:12:09 Dropped old table `test`.`_test_old` OK.

2016-02-01T18:12:09 Dropping triggers...

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_test_del`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_test_upd`;

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_test_ins`;

2016-02-01T18:12:09 Dropped triggers OK.

Successfully altered `test`.`test`.

 

在建索引的過程中,pt-osc會新將原表的數據拷貝到一張臨時表裡面,創建三個存儲過程來同步有變更的數據,先在臨時表上面加索引,加完索引後再將臨時表rename。

 

其實如果可以儘量只寫主庫不寫從庫,這樣可以避免很多坑。

 


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

-Advertisement-
Play Games
更多相關文章
  • 原文地址:http://www.roncoo.com/article/detail/124725 1、檢出svn co http://路徑(目錄或文件的全路徑) [本地目錄全路徑] --username 用戶名 --password 密碼 svn co svn://路徑(目錄或文件的全路徑) [本地 ...
  • 2. 獲取磁碟可用空間大小 1 //磁碟可用空間 2 + (CGFloat)diskOfFreeSizeMBytes{ 3 CGFloat size = 0.0; 4 NSError *error; 5 NSDictionary *dic = [[NSFileManager defaultManag ...
  • UIToolbar *keyboardDoneButtonView = [[UIToolbar alloc] init]; [keyboardDoneButtonView sizeToFit]; UIBarButtonItem *doneButton = [[UIBarButtonItem allo... ...
  • 一,效果圖。 二,工程圖。 三,代碼。 ViewController.m ...
  • 一、通過企業賬號申請證書 1 Certificate Signing Request (CSR)文件 在Mac系統中進入“鑰匙串訪問”,選擇“鑰匙串訪問”-“證書助理”-“從證書頒發機構請求證書…”,如圖1所示: 鑰匙串請求證書 —>填寫前兩項,並選擇“存儲到磁碟”,如圖2所示: 2 請求Certi ...
  • 摘自原文:http://www.jianshu.com/p/cea762105f7c 在上架App之前想要 真機測試的同學 請查看iOS- 最全的真機測試教程 裡面包含怎麼讓多臺電腦同時 上架App和同時真機調試。P12文件的使用詳解 因為最近更新了Xcode 8 ,證書的創建都大同小異,只是在Xc ...
  • ImageView顯示圖像控制項 一、簡介 1、 2、 ImageView,圖像視圖,直接繼承自View類,它的主要功能是用於顯示圖片,實際上它不僅僅可以用來顯示圖片,任何Drawable對象都可以使用ImageView來顯示。ImageView可以適用於任何佈局中,並且Android為其提供了縮放和 ...
  • SeekBar拖動條控制項 一、簡介 1、 二、SeekBar拖動條控制項使用方法 1、創建SeekBar控制項 2、添加setOnSeekBarChangeListener監聽 三、代碼實例 1、效果圖: 2、代碼 fry.Activity01 /SeekBarDemo1/res/layout/acti ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...