自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL億級(jí)數(shù)據(jù)數(shù)據(jù)庫優(yōu)化方案測(cè)試-銀行交易流水記錄的查詢

數(shù)據(jù)庫 MySQL
對(duì)MySQL的性能和億級(jí)數(shù)據(jù)的處理方法思考,以及分庫分表到底該如何做,在什么場(chǎng)景比較合適?

 [[267270]]

對(duì)MySQL的性能和億級(jí)數(shù)據(jù)的處理方法思考,以及分庫分表到底該如何做,在什么場(chǎng)景比較合適?

比如銀行交易流水記錄的查詢

限鹽少許,上實(shí)際實(shí)驗(yàn)過程,以下是在實(shí)驗(yàn)的過程中做一些操作,以及踩過的一些坑,我覺得坑對(duì)于讀者來講是非常有用的。

首先:建立一個(gè)現(xiàn)金流量表,交易歷史是各個(gè)金融體系下使用率***,歷史存留數(shù)據(jù)量***的數(shù)據(jù)類型。現(xiàn)金流量表的數(shù)據(jù)搜索,可以根據(jù)時(shí)間范圍,和個(gè)人,以及金額進(jìn)行搜索。

-- 建立一張 現(xiàn)金流量表 

  1. DROP TABLE IF EXISTS `yun_cashflow`;  
  2. CREATE TABLE `yun_cashflow` (  
  3.   `id` bigint(20) NOT NULL AUTO_INCREMENT,  
  4.   `userid` int(11) DEFAULT NULL,  
  5.   `type` int(11) DEFAULT NULL COMMENT '1、入賬,2提現(xiàn)',  
  6.   `operatoruserid` int(11) DEFAULT NULL COMMENT '操作員ID',  
  7.   `withdrawdepositid` bigint(20) DEFAULT NULL COMMENT '提現(xiàn)ID',  
  8.   `money` double DEFAULT NULL COMMENT '錢數(shù)',  
  9.   `runid` bigint(20) DEFAULT NULL COMMENT '工單ID',  
  10.   `createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  11.   PRIMARY KEY (`id`)  
  12. ENGINE=InnoDB AUTO_INCREMENT=63 DEFAULT CHARSET=utf8

然后開始造1個(gè)億的數(shù)據(jù)進(jìn)去。

-- 循環(huán)插入 

  1. drop PROCEDURE test_insert;  
  2. DELIMITER;;  
  3. CREATE PROCEDURE test_insert()  
  4. begin   
  5. declare num int;   
  6. set num=0 
  7.         while num < 10000 do  
  8.             insert into yun_cashflow(userid,type,operatoruserid,withdrawdepositid,money) values(FLOOR(7 + (RAND() * 6))+FLOOR(22 + (RAND() * 9)),1,FLOOR(97 + (RAND()  
  9.  
  10. * 6))+FLOOR(2 + (RAND() * 9)),FLOOR(17 + (RAND() * 6))+FLOOR(2 + (RAND() * 9)),FLOOR(5 + (RAND() * 6))+FLOOR(2 + (RAND() * 9)));  
  11.             set numnum=num+1;  
  12.         end while;  
  13.   END;;  
  14. call test_insert(); 

 坑一:

這個(gè)存儲(chǔ)過程建立好了之后,發(fā)現(xiàn)插入數(shù)據(jù)特別的慢,一天一晚上也插入不到100萬條數(shù)據(jù),平均每秒40~60條數(shù)據(jù),中間我停過幾次,以為是隨機(jī)函數(shù)的問題,都變成常數(shù),但效果一樣,還是很慢,當(dāng)時(shí)讓我對(duì)這個(gè)MySQL數(shù)據(jù)庫感覺到悲觀,畢竟Oracle用慣了,那插速是真的很快,不過功夫不負(fù)有心人,原來可以用另外一種寫法造數(shù)據(jù),速度很快,上代碼。 

  1. INSERT INTO example  
  2. (example_id, name, value, other_value)  
  3. VALUES  
  4. (100, 'Name 1', 'Value 1', 'Other 1'),  
  5. (101, 'Name 2', 'Value 2', 'Other 2'),  
  6. (102, 'Name 3', 'Value 3', 'Other 3'),  
  7. (103, 'Name 4', 'Value 4', 'Other 4'); 

就是在循環(huán)里,用這種格式造很多數(shù)據(jù),VALUES后面以,隔開,然后把數(shù)據(jù)寫上去,我用Excel造了1萬條數(shù)據(jù),按照語句格式粘貼了出來,就變成每循環(huán)一次,就1萬條數(shù)據(jù),這樣沒多久1億數(shù)據(jù)就造好了。 

  1. select count(*) from yun_cashflow 

我還比較好奇,8個(gè)字段1億條數(shù)據(jù),到底占了多大的地方,通過以下語句找到數(shù)據(jù)的路徑。 

  1. show global variables like "%datadir%"; 

通過查看文件,是7.78GB,看來如果字段不是很多,數(shù)據(jù)量大的話,其實(shí)不是什么問題,這其實(shí)作為架構(gòu)師來講,在估算機(jī)器配置硬盤冗余的時(shí)候,這是最簡單直接粗暴的換算思路。

行了,表建完了,各種實(shí)驗(yàn)開始

首先,啥條件不加看看咋樣。

呵呵了,Out of memory,看來這個(gè)查詢是真往內(nèi)存里整,內(nèi)存整冒煙了,看來7.8G的數(shù)據(jù)是往內(nèi)存里放,我內(nèi)存沒那么大導(dǎo)致的。

資金流水一般會(huì)按照時(shí)間進(jìn)行查詢,看看這速度到底怎樣。 

  1. select * from yun_cashflow  where createtime between '2018-10-23 09:06:58' and '2018-10-23 09:06:59'  

我去,腦補(bǔ)一下,當(dāng)你拿這支付寶查歷史資金明細(xì)的時(shí)候,56條信息,103.489秒,也就是將近2分鐘的查詢速度,你會(huì)是怎樣的體驗(yàn)。哦 哦,不對(duì),這個(gè)還沒加用條件,那下面單獨(dú)試試某個(gè)用戶不限時(shí)間范圍的條件是怎樣的。 

  1. select count(*) from yun_cashflow where userid=21 

也是將近1分半的速度,那在試試金額的條件。 

  1. select count(*) from yun_cashflow where money<62 and userid=32 

同樣都是將近一分半的時(shí)間。

那把兩個(gè)條件做下級(jí)聯(lián),看看效果會(huì)是怎樣。

一樣,也是將近1分半的時(shí)間。

小總結(jié)一:在不加索引的情況下,無論單獨(dú),還是聯(lián)合條件查詢,結(jié)果都是1分多鐘不到2分鐘。

好吧,那就加上索引試試,看看到底會(huì)有啥樣奇跡發(fā)生。

給用戶加索引 

  1. ALTER TABLE yun_cashflow ADD INDEX index_userid (userid) `

給金額加索引 

  1. ALTER TABLE yun_cashflow ADD INDEX index_money (money) 

給時(shí)間加索引 

  1. ALTER TABLE yun_cashflow ADD INDEX index_createtime (createtime) 

小總結(jié)二: 建立索引的時(shí)間平均在1400秒左右,大概在23分鐘左右。

索引都建立完了,在開始以前的條件查詢,看看效果。

1、時(shí)間范圍查詢 

  1. select * from yun_cashflow  where createtime between '2018-10-23 09:06:58' and '2018-10-23 09:06:59' 

2、用戶查詢與錢的聯(lián)合查詢

3、用戶查詢與錢與時(shí)間三個(gè)條件的聯(lián)合查詢 

  1. select * from yun_cashflow where money<62 and userid=32 and  createtime between '2018-10-22 09:06:58' and '2018-10-23 09:06:59' 

小總結(jié)三:建立完索引后,這種級(jí)聯(lián)性質(zhì)的查詢,速度基本都很快,數(shù)據(jù)量不大的情況下,基本不會(huì)超過一秒。

由于時(shí)間的范圍返回是56條數(shù)據(jù),數(shù)據(jù)量比較小,所以速度快可能與這個(gè)有關(guān),那實(shí)驗(yàn)下條件多的數(shù)據(jù)效果會(huì)是什么樣。

先試試加完索引, 金額條件的效果。

2千5百萬的數(shù)據(jù),返回時(shí)間為11.460秒。

加一個(gè)用戶數(shù)量比較多的條件 UserID=21

返回1000多萬的數(shù)據(jù),用了6秒

在找一個(gè)用戶數(shù)量比較少的userid=34

返回4000多條,用不到1秒。

小總結(jié)四:條件返回的數(shù)據(jù)統(tǒng)計(jì)量越多,速度就越慢,超過1000萬就慢的離譜,1秒左右就是100萬的量才行。

那。。。。。。。。。。。。咱們程序猿都知道,我們?cè)谧鰯?shù)據(jù)的時(shí)候,都要用到分頁。分頁一般會(huì)用到LIMIT,比如每頁10行,第二頁就是LIMIT 10,10,得試試在分頁的時(shí)候,哪些頁的情況下,會(huì)是什么樣的效果呢?

  •  limit在1千時(shí)候速度
  •  limit在1百萬時(shí)候速度
  •  limit在1千萬時(shí)候速度

小總結(jié)五:LIMIT 參數(shù)1,參數(shù)2  在隨著參數(shù)1(開始索引)增大時(shí)候,這個(gè)速度就會(huì)越來越慢,如果要求1秒左右返回時(shí)候的速度是100萬數(shù)據(jù),在多在大就慢了,也就是,如果10條一頁,當(dāng)你到第10萬頁之后,就會(huì)越來越慢。如果到30萬頁之后,可能就會(huì)到不到一般系統(tǒng)的3秒要求了。

數(shù)據(jù)庫都建上索引了,那我插數(shù)據(jù)速度有沒有影響呢,那試試

也就是說100條數(shù)據(jù)插了將近5秒,平均每秒插20條。

小總結(jié)六:也就是說,按照這樣的速度插入,并發(fā)量一但大的情況下,操作起來會(huì)很慢。所以在有索引的條件下插入數(shù)據(jù),要么索引失效,要么插入會(huì)特別慢。

分庫分表的思維,一個(gè)大表返回那么多數(shù)據(jù)慢,那我把它變成若干張表,然后每張表count(*)后,我統(tǒng)計(jì)累加一下,一合計(jì),就是所有數(shù)據(jù)的查詢結(jié)果的條數(shù),然后就是到第多少頁,我先算一下這頁在哪個(gè)庫,哪張表,在從那張表讀不就完了。通過之前 的總結(jié),100萬數(shù)據(jù)返回為1秒,所以就一張表里放100萬個(gè)數(shù)據(jù),1億的數(shù)據(jù)就100張表。 

  1. BEGIN   
  2.         DECLARE `@i` int(11);      
  3.         DECLARE `@createSql` VARCHAR(2560);   
  4.         DECLARE `@createIndexSql1` VARCHAR(2560);   
  5.         DECLARE `@createIndexSql2` VARCHAR(2560);  
  6.         DECLARE `@createIndexSql3` VARCHAR(2560);  
  7.         set `@i`=0;   
  8.         WHILE  `@i`< 100 DO                  
  9.                             SET @createSql = CONCAT('CREATE TABLE IF NOT EXISTS yun_cashflow_',`@i`,'(  
  10. `id` bigint(20) NOT NULL AUTO_INCREMENT,  
  11.                                 `userid` int(11) DEFAULT NULL,  
  12.                                 `type` int(11) DEFAULT NULL  ,  
  13.                                 `operatoruserid` int(11) DEFAULT NULL  ,  
  14.                                 `withdrawdepositid` bigint(20) DEFAULT NULL  ,  
  15.                                 `money` double DEFAULT NULL  ,  
  16.                                 `runid` bigint(20) DEFAULT NULL  ,  
  17.                                 `createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  18.                                 PRIMARY KEY (`id`)  
  19.                                 )'  
  20.                             );   
  21.                             prepare stmt from @createSql;   
  22.                             execute stmt;           

-- 創(chuàng)建索引   

  1.   set @createIndexSql1  = CONCAT('create index `t_money` on yun_cashflow_',`@i`,'(`money`);');  
  2.                             prepare stmt1 from @createIndexSql1;   
  3.                             execute stmt1;   
  4.                             set @createIndexSql2  = CONCAT('create index `t_userid` on yun_cashflow_',`@i`,'(`userid`);');  
  5.                             prepare stmt2 from @createIndexSql2;   
  6.                             execute stmt2;   
  7. SET `@i`= `@i`+1;   
  8.             END WHILE;  
  9. END 

表建完了,庫里的效果是醬樣的。

是不是很酷,這表分的,絕了,滿庫全是表。那還得往每張表里整100萬的數(shù)據(jù)。這部分代碼就不寫了,可以參考前面的改,相信能把文章看到這的都是懂行的人,也是對(duì)這方面有一腚追求的人。

坑二:我高估了我的計(jì)算機(jī)的并行計(jì)算能力,當(dāng)我啟用100個(gè)線程同時(shí)玩我自己電腦的數(shù)據(jù)庫連接的時(shí)候,到后期給我反饋的結(jié)果是這樣的。

說白了,連接滿了,超時(shí),數(shù)據(jù)庫都不給我返回值了,所以這種實(shí)驗(yàn),不找100臺(tái)機(jī)器,也別可一臺(tái)機(jī)器去霍霍,因?yàn)槿绻芸?,那個(gè)1個(gè)億的大表,返回的也不會(huì)慢。這時(shí)候拼的就是計(jì)算能力了,都在一臺(tái)機(jī)器上去做實(shí)驗(yàn),會(huì)讓你懷疑人生的。

那咋辦, 這地方我就假裝返回都是1000毫秒,也就1秒,然后每個(gè)線程都在1秒的時(shí)候都給我返回值,這個(gè)值我寫死,可以看看多線程分布式統(tǒng)計(jì)count的效果。

***總體耗時(shí),就是***那個(gè)返回時(shí)間最長的線程返回的時(shí)間,所以理論上100個(gè)線程同時(shí)啟動(dòng),應(yīng)該在1秒完成,但線程這玩意有快有慢,所以1秒多一點(diǎn),也是可以接受的。如果碰上都是機(jī)器性能好的時(shí)候,所有數(shù)據(jù)庫返回都在1秒以內(nèi),那么也就是1秒了。

這個(gè)多線程編程可以試試類似Java的countDownLatch/AKKA 將異步多線程結(jié)果同步返回。

***是在數(shù)據(jù)庫數(shù)據(jù)量比較大的時(shí)候,通過MySQL以上的特性,進(jìn)行不同場(chǎng)景應(yīng)用的思考。

場(chǎng)景:銀行交易流水記錄的查詢

  1.  根據(jù)小總結(jié)六的特性,操作表和歷史查詢表一定要時(shí)間可以分開,由于帶索引的歷史表,插入會(huì)很慢,所以要插入到操作表內(nèi),操作表和歷史表的字段是一樣的。
  2.  根據(jù)小總結(jié)二特性,然后固定某個(gè)時(shí)間點(diǎn),比如半夜12點(diǎn),或者固定日期,或者選擇非交易查詢活躍的時(shí)間,把操作表里的數(shù)據(jù)往歷史表里插一下,由于重建索引也用不了太久,一樣半個(gè)小時(shí)左右。讓兩種表并存。還有另外一種策略,由于流水主要以時(shí)間做為排序?qū)ο螅梢园凑諘r(shí)間順序,也就是ID自增長的順序進(jìn)行分庫分表,就像試驗(yàn)的那樣,100萬左右條數(shù)據(jù)一張表,另外在做一張時(shí)間范圍的索引表,如下: 
  1. CreateTimeIndexTable  
  2. ID  TableName   CreateTimeStart CreateTimeEnd  
  3. 1   yun_cashflow_1  2018-10-22 09:06:58 2018-10-26 09:06:58  
  4. 2   yun_cashflow_2  2018-10-26 09:06:58 2018-10-29 09:06:58  
  5. 3   yun_cashflow_3  2018-11-12 09:06:58 2018-11-22 09:06:58  
  6. 4   yun_cashflow_4  2018-11-22 09:06:58 2018-11-26 09:06:58 

當(dāng)遇見這樣語句需求的時(shí)候: 

  1. select * from yun_cashflow where money<62 and userid=32 and  createtime between '2018-10-27 09:06:58' and '2018-10-28 09:06:59' 

1)、就改寫成這樣的順序 

  1. select TableName from CreateTimeIndexTable where CreateTimeStart>  '2018-10-27 09:06:58' and CreateTimeEnd < '2018-10-28 09:06:59' 

2)、當(dāng)?shù)玫絋ableName的時(shí)候,結(jié)果是yun_cashflow_2,在進(jìn)行語句的查詢 

  1. select * from yun_cashflow_2 where money<62 and userid=32 and  createtime between '2018-10-27 09:06:58' and '2018-10-28 09:06:59' 

這樣,兩遍就可以查詢到結(jié)果。

不過也有可能查詢的結(jié)果是多個(gè),比如 

  1. select TableName from CreateTimeIndexTable where CreateTimeStart>  '2018-10-27 09:06:58' and CreateTimeEnd < '2018-11-13 09:06:59' 

yun_cashflow_2,和yun_cashflow_3,這個(gè)時(shí)候,就需要把兩個(gè)表的結(jié)果都查詢出來,進(jìn)行merge。相信程序員們對(duì)兩個(gè)表的結(jié)果集合并邏輯都不是什么難事,這地方不多解釋。

這樣做的好處,主要是每次重建索引的時(shí)候,就不用整個(gè)1個(gè)億的大表進(jìn)行重建,而是只重建最近的1百萬的那張分出來的表,速度會(huì)很快的。

    3.  根據(jù)小總結(jié)一和小總結(jié)三的特性,把關(guān)鍵的字段加上索引,用戶,時(shí)間,這樣保證查詢的速度。

    4.  根據(jù)小總結(jié)四的特性,盡量限制查詢結(jié)果的數(shù)量范圍,比如,單個(gè)人查自己的交易明細(xì),可以限制范圍,比如查詢時(shí)間范圍不超過三個(gè)月,或半年,或一年。 

責(zé)任編輯:龐桂玉 來源: 數(shù)據(jù)庫開發(fā)
相關(guān)推薦

2011-04-20 14:28:38

SQL優(yōu)化

2021-06-29 08:12:22

MySQL數(shù)據(jù)分頁數(shù)據(jù)庫

2014-07-18 09:33:53

數(shù)據(jù)庫數(shù)據(jù)庫優(yōu)化

2013-01-04 10:00:12

MySQL數(shù)據(jù)庫數(shù)據(jù)庫查詢優(yōu)化

2019-03-05 10:16:54

數(shù)據(jù)分區(qū)表SQLserver

2011-03-03 10:32:07

Mongodb億級(jí)數(shù)據(jù)量

2010-05-13 10:47:44

MySQL數(shù)據(jù)庫查詢

2010-06-10 10:15:50

MySQL數(shù)據(jù)庫查詢

2010-05-20 18:12:37

MySQL數(shù)據(jù)庫查詢

2024-08-22 14:16:08

2010-06-11 12:32:57

MySQL數(shù)據(jù)庫查詢

2010-06-17 09:15:02

MySQL數(shù)據(jù)庫查詢

2021-03-11 10:55:41

MySQL數(shù)據(jù)庫索引

2011-07-06 10:49:50

MySQL優(yōu)化

2011-07-06 14:12:20

MySQLPercona

2011-03-09 08:53:02

MySQL優(yōu)化集群

2010-05-27 17:16:20

MySQL數(shù)據(jù)庫

2011-03-08 08:49:55

MySQL優(yōu)化單機(jī)

2022-06-20 05:40:25

數(shù)據(jù)庫MySQL查詢

2019-05-28 09:31:05

Elasticsear億級(jí)數(shù)據(jù)ES
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)