1. 程式人生 > >Mysql百萬資料量級資料快速匯入Redis

Mysql百萬資料量級資料快速匯入Redis

前言

隨著系統的執行,資料量變得越來越大,單純的將資料儲存在mysql中,已然不能滿足查詢要求了,此時我們引入Redis作為查詢的快取層,將業務中的熱資料儲存到Redis,擴充套件傳統關係型資料庫的服務能力,使用者通過應用直接從Redis中快速獲取常用資料,或者在互動式應用中使用Redis儲存活躍使用者的會話,都可以極大地降低後端關係型資料庫的負載,提升使用者體驗。

傳統命令的缺點

使用傳統的redis client命令在大資料量的匯入場景下存在如下缺陷:

由於redis是單執行緒模型,雖然避免了多執行緒下執行緒切換所耗費的時間,單一順序的執行命令也很快,但是在大批量資料匯入的場景下,傳送命令所花費的時間和接收伺服器響應結果耗費的時間就會被放大。

假如需要匯入100萬條資料,那光是命令執行時間,就需要花費100萬*(t1 + t2)。

除了逐條命令傳送,當然redis設計肯定也會考慮這個問題,所以出現了pipelining管道模式。

但是pipelining在命令列中是沒有的,使得我們又需要編寫新的處理程式碼,來接收批量的響應。但是隻有很少很少的客戶端程式碼支援,比如php-redis的擴充套件就不支援非同步。

pipelining管道模式,其實就是減少了TCP連線的互動時間,當一批命令執行完畢後,一次性發送結果。

其實現原理是採用FIFO(先進先出)的佇列來保證資料的順序性。

只有一小部分客戶端支援非阻塞I/O,並不是所有的客戶端都能夠以一種有效的方式解析應答,以最大化吞吐量。

由於這些原因,將龐大資料匯入到Redis的首選方法是生成一個包含Redis協議資料格式,批量的傳送過去。

資料匯入Redis熱身

採用nc命令匯入資料

nc是netcat的簡寫,nc的作用有:

(1)實現任意TCP/UDP埠的偵聽,增加-l引數後,nc可以作為server以TCP或UDP方式偵聽指定埠

(2)埠的掃描,nc可以作為client發起TCP或UDP連線

(3)機器之間傳輸檔案

(4)機器之間網路測速

採用pipe模式匯入資料

然而,使用nc監聽並不是一個非常可靠的方式來執行大規模的資料匯入,因為netcat並不真正知道何時傳輸了所有資料,也無法檢查錯誤。在2.6或更高版本的Redis中,Redis -cli指令碼支援一種稱為pipe管道模式的新模式,這種模式是為了執行大規模插入而設計的。
使用管道模式的命令執行如下:

由上圖,可以看到pipe命令的返回結果,txt檔案中有多少行命令,返回的replies數就是多少,
errors表示其中執行錯誤的命令條數。

redis協議學習

協議的格式為:

*<引數數量>  \r\n
$<引數 1 的位元組數量>  \r\n
<引數 1 的資料> \r\n
...
$<引數 N 的位元組數量> \r\n
<引數 N 的資料> \r\n

比如:
插入一條hash型別的資料。

HSET  id  book1  book_description1

根據Redis協議,總共有4個部分,所以開頭為*4,其餘內容解釋如下:

內容 長度 協議命令
HSET 4 $4
id 2 $2
book1 5 $5
book_description1 17 $17

注意一下:HSET命令本身也作為協議的其中一個引數來發送。

構造出來的協議資料結構:

*4\r\n$4\r\nHSET\r\n$2\r\nid\r\n$5\r\nbook1\r\n$17\r\nbook_description1\r\n

格式化一下:

*4\r\n
$4\r\n
HSET\r\n
$2\r\n
idvvvv\r\n
$5\r\n
book1\r\n
$17\r\n
book_description1\r\n

RESP協議 bulk

Redis客戶機使用一種稱為RESP (Redis序列化協議)的協議與Redis伺服器通訊。

redis-cli pipe模式需要和nc命令一樣快,並且解決了nc命令不知道何時命令結束的問題。

在傳送資料的同時,它同樣會去讀取響應,嘗試去解析。

一旦輸入流中沒有讀取到更多的資料之後,它就會發送一個特殊的20位元的echo命令,標識最後一個命令已經發送完畢
如果在響應結果中匹配到這個相同資料後,說明本次批量傳送是成功的。

使用這個技巧,我們不需要解析傳送給伺服器的協議來了解我們傳送了多少命令,只需要解析應答即可。

在解析應答時,redis會對解析的應答進行一個計數,在最後能夠告訴使用者大量插入會話向伺服器傳輸的命令的數量。也就是上面我們使用pipe模式實際操作的響應結果。

將輸入資料來源換成mysql

上面的例子中,我們以一個txt文字為輸入資料來源,使用了pipe模式匯入資料。

基於上述協議的學習和理解,我們只需要將mysql中的資料按照既定的協議通過pipe模式匯入Redis即可。

實際案例--從Mysql匯入百萬級資料到Redis

首先造資料

由於環境限制,所以這裡沒有用真實資料來實現匯入,那麼我們就先使用一個儲存過程來造一百萬條資料把。使用儲存過程如下:

DELIMITER $$
USE `cb_mon`$$

DROP PROCEDURE IF EXISTS `test_insert`$$
CREATE DEFINER=`root`@`%` PROCEDURE `test_insert`()
BEGIN
    
        DECLARE i INT DEFAULT 1;
        WHILE i<= 1000000
            DO
            INSERT INTO t_book(id,number,NAME,descrition)
            VALUES (i, CONCAT("00000",i) , CONCAT('book',i)
            , CONCAT('book_description',i));    
            SET i=i+1;
        END WHILE ;
        COMMIT;
    END$$

DELIMITER ;

呼叫儲存過程:

 CALL test_insert();

查看錶資料:

按協議構造查詢語句

按照上述redis協議,我們使用如下sql來構造協議資料

SELECT
  CONCAT(
    "*4\r\n",
    "$",
    LENGTH(redis_cmd),
    "\r\n",
    redis_cmd,
    "\r\n",
    "$",
    LENGTH(redis_key),
    "\r\n",
    redis_key,
    "\r\n",
    "$",
    LENGTH(hkey),
    "\r\n",
    hkey,
    "\r\n",
    "$",
    LENGTH(hval),
    "\r\n",
    hval,
    "\r"
  )
FROM
  (SELECT
    "HSET" AS redis_cmd,
    id AS redis_key,
    NAME AS hkey,
    descrition AS hval
  FROM
    cb_mon.t_book
  ) AS t limit 1000000 

並將內容儲存至redis.sql 檔案中。

編寫指令碼使用pipe模式匯入redis

編寫shell指令碼。由於我在主機上是通過docker安裝的redis和mysql,以下指令碼供參考:

#!/bin/bash
starttime=`date +'%Y-%m-%d %H:%M:%S'`

docker exec -i 899fe01d4dbc mysql --default-character-set=utf8   
--skip-column-names --raw < ./redis.sql
| docker exec -i 4c90ef506acd redis-cli --pipe

endtime=`date +'%Y-%m-%d %H:%M:%S'`
start_seconds=$(date --date="$starttime" +%s);
end_seconds=$(date --date="$endtime" +%s);

echo "指令碼執行耗時: "$((end_seconds-start_seconds))"s"

執行截圖:

可以看到百萬級的資料匯入redis,只花費了7秒,效率非常高。

注意事項

如果mysql表特別大,可以考慮分批匯入,或者將表拆分,否則在匯入過程中可能會發生

lost connection to mysql server during query

由於max_allowed_packed和超時時間限制,查詢資料的過程中,可能會造成連線斷開,所以在資料表的資料量特別大的時候,需要分頁或者將表拆分匯入。

總結

本篇文章主要探討了,Mysql百萬級資料量級下,如何高效的遷移到Redis中去,逐步實現目標的過程中,總結了如下幾點

  1. redis單執行緒執行命令,避免了執行緒切換所消耗的時間,但是在超大資料量級下,其傳送、響應接收的時延不可忽視。
  2. 網路nc命令的應用場景,及在資料匯入時存在的缺點。
  3. redis RESP協議的理解和應用。
  4. 百萬量級Mysql資料的Redis快速匯入案例。

相關推薦

Mysql百萬資料量級資料快速匯入Redis

前言 隨著系統的執行,資料量變得越來越大,單純的將資料儲存在mysql中,已然不能滿足查詢要求了,此時我們引入Redis作為查詢的快取層,將業務中的熱資料儲存到Redis,擴充套件傳統關係型資料庫的服務能力,使用者通過應用直接從Redis中快速獲取常用資料,或者在互動式應用中使用Redis儲存活躍使用者的會話

python生成資料後,快速匯入資料庫

1、使用python生成資料庫檔案內容# coding=utf-8import randomimport timedef create_user():    start = time.time()    count = 1000  # 一千萬條資料    beginId = 200010000    with

Mysql 百萬級別的資料查詢

從實驗可以得出,表中的欄位加上索引之後確實能提升查詢的速度。當單表中的資料量達到百萬之後,我們將資料庫表中的資料從資料庫中搜出來,再加上網路的傳輸,最終顯示在某個網頁上,豈不是更耗時,還有一點,在優化 語句的時候,我們儘量不要查詢沒有用的欄位,不要動不動就將所有欄位都查詢出來,無論是對於資料庫查詢,還是網路傳

通過管道傳輸快速MySQL資料匯入Redis(自己做過測試)

通過管道傳輸快速將MySQL的資料匯入Redis 通過管道傳輸pipe將MySQL資料批量匯入Redis       自Redis 2.6以上版本起,Redis支援快速大批量匯入資料,即官網的Redis Mass Insertion,即

mysql資料Redis快速匯入

Redis協議 *<args><cr><lf> 引數個數 $<len><cr><lf> 第一個引數長度 <arg0><cr><lf> 第一個引數 $<len&g

3.MySQL快速匯入資料LOAD DATA INFILE(帶詳細優化引數)

介紹 LOAD DATA INFILE語句以非常高的速度將文字檔案中的行讀入表中。 LOAD DATA INFILE是SELECT … INTO OUTFILE的補充。要將表中的資料寫入檔案,請使用SELECT … INTO OUTFILE。 要將檔案讀回表中,請使用LOAD DATA

Mysql --學習:大量資料快速匯入匯出

宣告:此文供學習使用,原文:https://blog.csdn.net/xiaobaismiley/article/details/41015783  【實驗背景】 專案中需要對資料庫中一張表進行重新設計,主要是之前未分割槽,考慮到資料量大了以後要設計成分割槽表,同時要對資料庫中其他表做好備份恢

Docker執行的MySQL,如何快速匯入資料

       之前匯入資料都是用的資料庫連線工具自帶的匯入功能,有時候匯入資料需要兩個小時,簡直沒辦法忍受。後來有了一個更快的方式匯入資料,把他記錄下來,希望大家不再忍受匯入資料的煎熬。。。。        1、備份資料庫表結構,暫時還沒有遇到特別有效的方法,我都是用sho

mysql百萬資料匯入

--建立MyISAM模式表方便批量跑資料 CREATE TABLE `logs1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `logtype` varchar(255) DEFAULT NULL, `logurl` va

mysql百萬資料快速建立索引

測試資料本機一張表users有100百萬條記錄。在建立此表前沒有未相應欄位新增索引,所以此時需要為表新增索引。但是因為資料量大的原因,索引新增可能不成功,想了很多辦法,終於挖坑成功。開始準備工作,user表結構:CREATE TABLE `users` ( `id` in

Excel資料快速匯入mysql的幾個辦法

有時候需要批量插入一批資料到資料庫,有很多種辦法,這裡我用到過三種辦法: 1、通過Excel直接生成insert語句 =CONCATENATE("insert into aisee_pingfen_fengcai(id,order_n,departm

Mysql 大量資料快速匯入匯出

【實驗背景】 專案中需要對資料庫中一張表進行重新設計,主要是之前未分割槽,考慮到資料量大了以後要設計成分割槽表,同時要對資料庫中其他表做好備份恢復的工作。 【實驗環境】 MySQL版本:mysql-5.6.19 作業系統:Ubuntu 12.04

mysql】使用 if() 函式快速交換資料

條件:給定一張表 user,有欄位 id,name,vip, 要求使用一條SQL把 vip=y 的使用者改為 vip=n,反之亦然。 update user set user = if(vip='y','n','y') SQL的邏輯說明: 設定user 

mysql資料庫命令列匯出匯入資料基本操作

一。mysql命令列連線資料庫 格式: mysql -h主機地址 -u使用者名稱 -p使用者密碼 二,mysql 通過命令列匯入sql檔案 先確保這個資料庫已經建立 mysql -u root -p database_name(資料庫名稱) < dump.txt pass

MysqlRedis資料協議(可以按照寫redis的協議,自測已經成功,key值可以自己變化,不一定非要是id)

redis-cli命令列工具有一個批量插入模式,是專門為批量執行命令設計的。這第一步就是把Mysql查詢的內容格式化成redis-cli可用的資料格式。 原理是把要插入到Redis的資料直接轉成Redis協議資料流,通過pipe mode 匯入到Redis. Redis協議: *<

Hbase通過BulkLoad快速匯入資料

HBase是一個分散式的、面向列的開源資料庫,它可以讓我們隨機的、實時的訪問大資料。大量的資料匯入到Hbase中時速度回很慢,不過我們可以使用bulkload來匯入。 BulkLoad的過程主要有以下部分: 1. 從資料來源提取資料並上傳到HDFS中。 2. 使用MapReduce作

mysql常識:SQL的分類、匯入演示資料

1、SQL的分類 資料查詢語言(DQL-Data Query Language)                             代表關鍵

C#解析Mysql的sql指令碼實現批量匯入資料

最近老大對我們做的資料匯入功能意見挺大,資料量一上來,匯入時間就很長,嚴重影響使用者體驗。因此,不得不花時間搗鼓了一下資料匯入的效能優化問題 原始程式碼: MySqlCommand command = new MySqlCommand(); command.Connection = conn

linux下mysql資料的匯出和匯入

原文 匯出整個資料庫中的所有資料 1、在linux命令列下輸入: mysqldump -u userName -p dabaseName > fileName.sql fileName.sql最好加上路徑名 匯出資料庫中的某個表的資料

使用load data方式將xlsx表格百萬行檔案快速匯入mysql

目錄 一、需求 二、解決方法 三、三種方式的實現 navicat直接匯入 python指令碼 load data命令 四、三種方式效率比較 五、總結 navicat直接匯入 我是同時使用Navicat和mysql-front作為視覺化介面的,