成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

TiDB 在轉(zhuǎn)轉(zhuǎn)的業(yè)務(wù)實(shí)戰(zhàn)

hover_lew / 1832人閱讀

摘要:而批處理,可以復(fù)用一條簡(jiǎn)單,實(shí)現(xiàn)批量數(shù)據(jù)的寫(xiě)入或更新,為系統(tǒng)帶來(lái)更低更穩(wěn)定的耗時(shí)。批處理的簡(jiǎn)要流程說(shuō)明如下經(jīng)業(yè)務(wù)中實(shí)踐,使用批處理方式的寫(xiě)入或更新,比常規(guī)或性能更穩(wěn)定,耗時(shí)也更低。

作者:陳維,轉(zhuǎn)轉(zhuǎn)優(yōu)品技術(shù)部 RD。
開(kāi)篇

世界級(jí)的開(kāi)源分布式數(shù)據(jù)庫(kù) TiDB 自 2016 年 12 月正式發(fā)布第一個(gè)版本以來(lái),業(yè)內(nèi)諸多公司逐步引入使用,并取得廣泛認(rèn)可。

對(duì)于互聯(lián)網(wǎng)公司,數(shù)據(jù)存儲(chǔ)的重要性不言而喻。在 NewSQL 數(shù)據(jù)庫(kù)出現(xiàn)之前,一般采用單機(jī)數(shù)據(jù)庫(kù)(比如 MySQL)作為存儲(chǔ),隨著數(shù)據(jù)量的增加,“分庫(kù)分表”是早晚面臨的問(wèn)題,即使有諸如 MyCat、ShardingJDBC 等優(yōu)秀的中間件,“分庫(kù)分表”還是給 RD 和 DBA 帶來(lái)較高的成本;NewSQL 數(shù)據(jù)庫(kù)出現(xiàn)后,由于它不僅有 NoSQL 對(duì)海量數(shù)據(jù)的管理存儲(chǔ)能力、還支持傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)的 ACID 和 SQL,所以對(duì)業(yè)務(wù)開(kāi)發(fā)來(lái)說(shuō),存儲(chǔ)問(wèn)題已經(jīng)變得更加簡(jiǎn)單友好,進(jìn)而可以更專注于業(yè)務(wù)本身。而 TiDB,正是 NewSQL 的一個(gè)杰出代表!

站在業(yè)務(wù)開(kāi)發(fā)的視角,TiDB 最吸引人的幾大特性是:

支持 MySQL 協(xié)議(開(kāi)發(fā)接入成本低);

100% 支持事務(wù)(數(shù)據(jù)一致性實(shí)現(xiàn)簡(jiǎn)單、可靠);

無(wú)限水平拓展(不必考慮分庫(kù)分表)。

基于這幾大特性,TiDB 在業(yè)務(wù)開(kāi)發(fā)中是值得推廣和實(shí)踐的,但是,它畢竟不是傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù),以致我們對(duì)關(guān)系型數(shù)據(jù)庫(kù)的一些使用經(jīng)驗(yàn)和積累,在 TiDB 中是存在差異的,現(xiàn)主要闡述“事務(wù)”和“查詢”兩方面的差異。

TiDB 事務(wù)和 MySQL 事務(wù)的差異 MySQL 事務(wù)和 TiDB 事務(wù)對(duì)比

在 TiDB 中執(zhí)行的事務(wù) b,返回影響條數(shù)是 1(認(rèn)為已經(jīng)修改成功),但是提交后查詢,status 卻不是事務(wù) b 修改的值,而是事務(wù) a 修改的值。

可見(jiàn),MySQL 事務(wù)和 TiDB 事務(wù)存在這樣的差異:

MySQL 事務(wù)中,可以通過(guò)影響條數(shù),作為寫(xiě)入(或修改)是否成功的依據(jù);而在 TiDB 中,這卻是不可行的!

作為開(kāi)發(fā)者我們需要考慮下面的問(wèn)題:

同步 RPC 調(diào)用中,如果需要嚴(yán)格依賴影響條數(shù)以確認(rèn)返回值,那將如何是好?

多表操作中,如果需要嚴(yán)格依賴某個(gè)主表數(shù)據(jù)更新結(jié)果,作為是否更新(或?qū)懭耄┢渌淼呐袛嘁罁?jù),那又將如何是好?

原因分析及解決方案

對(duì)于 MySQL,當(dāng)更新某條記錄時(shí),會(huì)先獲取該記錄對(duì)應(yīng)的行級(jí)鎖(排他鎖),獲取成功則進(jìn)行后續(xù)的事務(wù)操作,獲取失敗則阻塞等待。

對(duì)于 TiDB,使用 Percolator 事務(wù)模型:可以理解為樂(lè)觀鎖實(shí)現(xiàn),事務(wù)開(kāi)啟、事務(wù)中都不會(huì)加鎖,而是在提交時(shí)才加鎖。參見(jiàn) 這篇文章(TiDB 事務(wù)算法)。

其簡(jiǎn)要流程如下:

在事務(wù)提交的 PreWrite 階段,當(dāng)“鎖檢查”失敗時(shí):如果開(kāi)啟沖突重試,事務(wù)提交將會(huì)進(jìn)行重試;如果未開(kāi)啟沖突重試,將會(huì)拋出寫(xiě)入沖突異常。

可見(jiàn),對(duì)于 MySQL,由于在寫(xiě)入操作時(shí)加上了排他鎖,變相將并行事務(wù)從邏輯上串行化;而對(duì)于 TiDB,屬于樂(lè)觀鎖模型,在事務(wù)提交時(shí)才加鎖,并使用事務(wù)開(kāi)啟時(shí)獲取的“全局時(shí)間戳”作為“鎖檢查”的依據(jù)。

所以,在業(yè)務(wù)層面避免 TiDB 事務(wù)差異的本質(zhì)在于避免鎖沖突,即,當(dāng)前事務(wù)執(zhí)行時(shí),不產(chǎn)生別的事務(wù)時(shí)間戳(無(wú)其他事務(wù)并行)。處理方式為事務(wù)串行化。

TiDB 事務(wù)串行化

在業(yè)務(wù)層,可以借助分布式鎖,實(shí)現(xiàn)串行化處理,如下:

基于 Spring 和分布式鎖的事務(wù)管理器拓展

在 Spring 生態(tài)下,spring-tx 中定義了統(tǒng)一的事務(wù)管理器接口:PlatformTransactionManager,其中有獲取事務(wù)(getTransaction)、提交(commit)、回滾(rollback)三個(gè)基本方法;使用裝飾器模式,事務(wù)串行化組件可做如下設(shè)計(jì):

其中,關(guān)鍵點(diǎn)有:

超時(shí)時(shí)間:為避免死鎖,鎖必須有超時(shí)時(shí)間;為避免鎖超時(shí)導(dǎo)致事務(wù)并行,事務(wù)必須有超時(shí)時(shí)間,而且鎖超時(shí)時(shí)間必須大于事務(wù)超時(shí)時(shí)間(時(shí)間差最好在秒級(jí))。

加鎖時(shí)機(jī):TiDB 中“鎖檢查”的依據(jù)是事務(wù)開(kāi)啟時(shí)獲取的“全局時(shí)間戳”,所以加鎖時(shí)機(jī)必須在事務(wù)開(kāi)啟前。

事務(wù)模板接口設(shè)計(jì)

隱藏復(fù)雜的事務(wù)重寫(xiě)邏輯,暴露簡(jiǎn)單友好的 API:

TiDB 查詢和 MySQL 的差異

在 TiDB 使用過(guò)程中,偶爾會(huì)有這樣的情況,某幾個(gè)字段建立了索引,但是查詢過(guò)程還是很慢,甚至不經(jīng)過(guò)索引檢索。

索引混淆型(舉例)

表結(jié)構(gòu):

CREATE TABLE `t_test` (
      `id` bigint(20) NOT NULL DEFAULT "0" COMMENT "主鍵id",
      `a` int(11) NOT NULL DEFAULT "0" COMMENT "a",
      `b` int(11) NOT NULL DEFAULT "0" COMMENT "b",
      `c` int(11) NOT NULL DEFAULT "0" COMMENT "c",
      PRIMARY KEY (`id`),
      KEY `idx_a_b` (`a`,`b`),
      KEY `idx_c` (`c`)
    ) ENGINE=InnoDB;

查詢:如果需要查詢 (a=1 且 b=1)或 c=2 的數(shù)據(jù),在 MySQL 中,sql 可以寫(xiě)為:SELECT id from t_test where (a=1 and b=1) or (c=2);,MySQL 做查詢優(yōu)化時(shí),會(huì)檢索到 idx_a_bidx_c 兩個(gè)索引;但是在 TiDB(v2.0.8-9)中,這個(gè) sql 會(huì)成為一個(gè)慢 SQL,需要改寫(xiě)為:

SELECT id from t_test where (a=1 and b=1) UNION SELECT id from t_test where (c=2);

小結(jié):導(dǎo)致該問(wèn)題的原因,可以理解為 TiDB 的 sql 解析還有優(yōu)化空間。

冷熱數(shù)據(jù)型(舉例)

表結(jié)構(gòu):

CREATE TABLE `t_job_record` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT "主鍵id",
      `job_code` varchar(255) NOT NULL DEFAULT "" COMMENT "任務(wù)code",
      `record_id` bigint(20) NOT NULL DEFAULT "0" COMMENT "記錄id",
      `status` tinyint(3) NOT NULL DEFAULT "0" COMMENT "執(zhí)行狀態(tài):0 待處理",
      `execute_time` bigint(20) NOT NULL DEFAULT "0" COMMENT "執(zhí)行時(shí)間(毫秒)",
      PRIMARY KEY (`id`),
      KEY `idx_status_execute_time` (`status`,`execute_time`),
      KEY `idx_record_id` (`record_id`)
    ) ENGINE=InnoDB COMMENT="異步任務(wù)job"

數(shù)據(jù)說(shuō)明

a. 冷數(shù)據(jù),status=1 的數(shù)據(jù)(已經(jīng)處理過(guò)的數(shù)據(jù));

b. 熱數(shù)據(jù),status=0 并且 execute_time<= 當(dāng)前時(shí)間 的數(shù)據(jù)。

慢查詢:對(duì)于熱數(shù)據(jù),數(shù)據(jù)量一般不大,但是查詢頻度很高,假設(shè)當(dāng)前(毫秒級(jí))時(shí)間為:1546361579646,則在 MySQL 中,查詢 sql 為:

SELECT * FROM t_job_record where status=0 and execute_time<= 1546361579646

這個(gè)在 MySQL 中很高效的查詢,在 TiDB 中雖然也可從索引檢索,但其耗時(shí)卻不盡人意(百萬(wàn)級(jí)數(shù)據(jù)量,耗時(shí)百毫秒級(jí))。

原因分析:在 TiDB 中,底層索引結(jié)構(gòu)為 LSM-Tree,如下圖:

當(dāng)從內(nèi)存級(jí)的 C0 層查詢不到數(shù)據(jù)時(shí),會(huì)逐層掃描硬盤(pán)中各層;且 merge 操作為異步操作,索引數(shù)據(jù)更新會(huì)存在一定的延遲,可能存在無(wú)效索引。由于逐層掃描和異步 merge,使得查詢效率較低。

優(yōu)化方式:盡可能縮小過(guò)濾范圍,比如結(jié)合異步 job 獲取記錄頻率,在保證不遺漏數(shù)據(jù)的前提下,合理設(shè)置 execute_time 篩選區(qū)間,例如 1 小時(shí),sql 改寫(xiě)為:

SELECT * FROM t_job_record  where status=0 and execute_time>1546357979646  and execute_time<= 1546361579646

優(yōu)化效果:耗時(shí) 10 毫秒級(jí)別(以下)。

關(guān)于查詢的啟發(fā)

在基于 TiDB 的業(yè)務(wù)開(kāi)發(fā)中,先摒棄傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)帶來(lái)的對(duì) sql 先入為主的理解或經(jīng)驗(yàn),謹(jǐn)慎設(shè)計(jì)每一個(gè) sql,如 DBA 所提倡:設(shè)計(jì) sql 時(shí)務(wù)必關(guān)注執(zhí)行計(jì)劃,必要時(shí)請(qǐng)教 DBA。

和 MySQL 相比,TiDB 的底層存儲(chǔ)和結(jié)構(gòu)決定了其特殊性和差異性;但是,TiDB 支持 MySQL 協(xié)議,它們也存在一些共同之處,比如在 TiDB 中使用“預(yù)編譯”和“批處理”,同樣可以獲得一定的性能提升。

服務(wù)端預(yù)編譯

在 MySQL 中,可以使用 PREPARE stmt_name FROM preparable_stm 對(duì) sql 語(yǔ)句進(jìn)行預(yù)編譯,然后使用 EXECUTE stmt_name [USING @var_name [, @var_name] ...] 執(zhí)行預(yù)編譯語(yǔ)句。如此,同一 sql 的多次操作,可以獲得比常規(guī) sql 更高的性能。

mysql-jdbc 源碼中,實(shí)現(xiàn)了標(biāo)準(zhǔn)的 StatementPreparedStatement 的同時(shí),還有一個(gè)ServerPreparedStatement 實(shí)現(xiàn),ServerPreparedStatement 屬于PreparedStatement的拓展,三者對(duì)比如下:

容易發(fā)現(xiàn),PreparedStatementStatement 的區(qū)別主要區(qū)別在于參數(shù)處理,而對(duì)于發(fā)送數(shù)據(jù)包,調(diào)用服務(wù)端的處理邏輯是一樣(或類似)的;經(jīng)測(cè)試,二者速度相當(dāng)。其實(shí),PreparedStatement 并不是服務(wù)端預(yù)處理的;ServerPreparedStatement 才是真正的服務(wù)端預(yù)處理,速度也較 PreparedStatement 快;其使用場(chǎng)景一般是:頻繁的數(shù)據(jù)庫(kù)訪問(wèn),sql 數(shù)量有限(有緩存淘汰策略,使用不宜會(huì)導(dǎo)致兩次 IO)。

批處理

對(duì)于多條數(shù)據(jù)寫(xiě)入,常用 sql 為 insert … values (…),(…);而對(duì)于多條數(shù)據(jù)更新,亦可以使用 update … case … when… then… end 來(lái)減少 IO 次數(shù)。但它們都有一個(gè)特點(diǎn),數(shù)據(jù)條數(shù)越多,sql 越加復(fù)雜,sql 解析成本也更高,耗時(shí)增長(zhǎng)可能高于線性增長(zhǎng)。而批處理,可以復(fù)用一條簡(jiǎn)單 sql,實(shí)現(xiàn)批量數(shù)據(jù)的寫(xiě)入或更新,為系統(tǒng)帶來(lái)更低、更穩(wěn)定的耗時(shí)。

對(duì)于批處理,作為客戶端,java.sql.Statement 主要定義了兩個(gè)接口方法,addBatchexecuteBatch 來(lái)支持批處理。

批處理的簡(jiǎn)要流程說(shuō)明如下:

經(jīng)業(yè)務(wù)中實(shí)踐,使用批處理方式的寫(xiě)入(或更新),比常規(guī) insert … values(…),(…)(或 update … case … when… then… end)性能更穩(wěn)定,耗時(shí)也更低。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/17883.html

相關(guān)文章

  • TiDB 分布式數(shù)據(jù)庫(kù)轉(zhuǎn)轉(zhuǎn)公司應(yīng)用實(shí)踐

    摘要:業(yè)務(wù)延遲和錯(cuò)誤量對(duì)比接入數(shù)據(jù)庫(kù)后業(yè)務(wù)邏輯層服務(wù)接口耗時(shí)穩(wěn)定無(wú)抖動(dòng),且沒(méi)有發(fā)生丟棄的情況上圖錯(cuò)誤大多由數(shù)據(jù)訪問(wèn)層服務(wù)隊(duì)列堆積發(fā)生請(qǐng)求丟棄造成。 作者:孫玄,轉(zhuǎn)轉(zhuǎn)公司首席架構(gòu)師;陳東,轉(zhuǎn)轉(zhuǎn)公司資深工程師;冀浩東,轉(zhuǎn)轉(zhuǎn)公司資深 DBA。 公司及業(yè)務(wù)架構(gòu)介紹 轉(zhuǎn)轉(zhuǎn)二手交易網(wǎng) —— 把家里不用的東西賣了變成錢,一個(gè)幫你賺錢的網(wǎng)站。由騰訊與 58 集團(tuán)共同投資。為海量用戶提供一個(gè)有擔(dān)保、便捷的二手...

    Gu_Yan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<