2016-10-22 68 views
2

我們正在開發的應用程序每天要編寫大約4-5百萬行數據。而且,我們需要在過去90天內保存這些數據。存儲舊數據以更快訪問的更好方式

user_data具有以下結構(簡化):

id INT PRIMARY AUTOINCREMENT 
dt TIMESTAMP CURRENT_TIMESTAMP 
user_id varchar(20) 
data varchar(20) 

關於應用程序:

  • 數據是舊超過7天將不會被寫入/更新。
  • 數據大多基於user_id訪問(即所有查詢將具有WHERE user_id = XXX
  • 目前大約有13000個用戶。
  • 用戶仍然可以訪問較舊的數據。但是,在訪問舊數據時,我們可以限制他/她只能獲取全天數據而不是時間範圍。 (例如,如果用戶試圖獲取2016-10-01的數據,他/她將獲取全天的數據,並且無法獲取2016-10-01 13:00 - 2016-10的數據-01 14:00)。

目前,我們正在使用MySQL InnoDB存儲的最新數據(即7天,較新的),它工作正常,並在innodb_buffer_pool適合。

至於較舊的數據,我們以user_data_YYYYMMDD的形式創建了較小的表格。過了一段時間,我們發現這些表格不適合innodb_buffer_pool,它開始放慢速度。

我們認爲基於日期分離/分片,基於user_ids的分片會更好(即使用基於用戶和日期的較小數據集,例如user_data_[YYYYMMDD]_[USER_ID])。這將使桌子保持更小的數量(最多隻有10K左右)。

圍繞研究後,我們發現有出有幾個選項:

  • 使用MySQL表每日期的用戶(即user_data_[YYYYMMDD]_[USER_ID])來存儲。
  • 使用MongoDB的集合每個user_data_[YYYYMMDD]_[USER_ID]
  • 寫舊數據(JSON編碼)到[USER_ID]/[YYYYMMDD].txt

最大的騙子我在這看到的是,我們將擁有的表/收藏/文件數量巨大的時候,我們這樣做(即13000 x 90 = 1.170.000)。我想知道我們是否在未來的可擴展性方面接近正確的方式。或者,如果有其他標準化的解決方案。

回答

0

100萬個表格聽起來像一個壞主意。在運行時通過應用程序代碼通過動態表命名進行分片對於我來說也不是一個有利的模式。我對這類問題的第一次嘗試是分區。您可能不希望單個未分區表中的400M +行。在MySQL 5.7中,你甚至可以進行子分區(但這會變得更復雜)。我首先會在日期字段上劃分分區,每天分區一次。在user_id上索引。如果你在5.7版本並且想要涉及子分區,我會建議按日期進行範圍分區,然後通過user_id散列子分區。作爲一個起點,嘗試16到32個散列桶。仍然索引user_id字段。

編輯:這裏的東西一起玩:

CREATE TABLE user_data (
    id INT AUTO_INCREMENT 
    , dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
    , user_id VARCHAR(20) 
    , data varchar(20) 
    , PRIMARY KEY (id, user_id, dt) 
    , KEY (user_id, dt) 
) PARTITION BY RANGE (UNIX_TIMESTAMP(dt)) 
    SUBPARTITION BY KEY (user_id) 
    SUBPARTITIONS 16 (
    PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-25')), 
    PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-26')), 
    PARTITION p3 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-27')), 
    PARTITION p4 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-28')), 
    PARTITION pMax VALUES LESS THAN MAXVALUE 
); 

-- View the metadata if you're interested 
SELECT * FROM information_schema.partitions WHERE table_name='user_data'; 
+0

謝謝,約書亞。一定會嘗試探索更多關於PARTITION的內容。 –

1

縮放數據庫是一個獨特的問題到應用程序。大多數時候別人的方法都不能使用,因爲幾乎所有的應用程序都以自己的方式寫入數據。所以你必須弄清楚你將如何管理你的數據。

話雖如此,如果你的數據繼續增長,最好的解決辦法是shadring在那裏你可以在不同的服務器上分配數據。只要綁定到單個服務器上,像創建不同的表,就會受到內存,存儲和處理能力等資源限制的影響。那些不能無限增加的方式。

如何分配數據,你必須根據自己的業務使用情況弄清楚。正如你所提到的,如果你沒有對舊數據提出更多請求,那就是按日期分發數據庫的最佳方式。像2016年的數據庫,2015年的數據庫等。稍後,您可以清除或關閉擁有更多舊數據的服務器。

0

這是一張大桌子,但不是難以管理。

如果USER_ID + DT是獨一無二的,使之成爲主鍵,擺脫如果id,從而節省了空間。 (更多在一分鐘內...)

user_id標準化爲SMALLINT UNSIGNED(2字節)或更安全MEDIUMINT UNSIGNED(3字節)。這將節省大量的空間。

節省空間對於大型表格的速度(I/O)很重要。

PARTITION BY RANGE(TO_DAYS(dt)) 

與92分區 - 你需要的90,加上1等待DROPped和一個正在填補。看詳情here

ENGINE=InnoDB 

得到PRIMARY KEY集羣。

PRIMARY KEY(user_id, dt) 

如果這是「唯一」,那麼它允許對單個用戶的任何時間範圍進行有效訪問。注意:您可以刪除「只需一天」的限制。但是,您必須必須制定查詢而不隱藏dt在函數中。我建議:

WHERE user_id = ? 
    AND dt >= ? 
    AND dt < ? + INTERVAL 1 DAY 

此外,

PRIMARY KEY(user_id, dt, id), 
INDEX(id) 

也將是有效的,即使(USER_ID,DT)不是唯一的。 PK的加入id就是讓它獨一無二; INDEX(id)的補充是保持AUTO_INCREMENT高興。 (不,UNIQUE(id)不是必需的。)

INT --> BIGINT UNSIGNED ?? 

INT(這是SIGNED)將在大約2十億排在前列。這將在幾年內發生。這可以嗎?如果不是,您可能需要BIGINT(8字節與4)。

此分區設計不關心您的7天規則。您可以選擇保留規則並在您的應用中執行該規則。

BY HASH 

工作爲好。

SUBPARTITION 

一般沒用。

還有其他疑問嗎?如果是這樣,他們必須同時考慮

如果單個服務器的流量過多,則通過user_id進行分片將非常有用。 MySQL本身並不具備分片解決方案。

+0

謝謝你的詳細解釋。我一定會考慮PARTITION。我很好奇,如果所有的用戶都在一個表中(PARTITION),當同時閱讀說同一日期範圍內的不同用戶時,它會有什麼樣的鎖定? –

+0

鎖定在InnoDB的行級。因此,對單獨用戶的查詢之間沒有干擾(除了整個系統忙)。 –

+0

在使用InnoDB時,我正在尋找表格大小(每天大約2GB x 90 = 180GB)不能適應'innodb_buffer_pool'的可能性。這是否會影響分區的查詢速度,因爲我需要基於'user_id'在'dt'上進行搜索? –

相關問題