成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

FriendFeed 如何使用 MySQL 來存儲無模式的數(shù)據(jù)

數(shù)據(jù)庫 MySQL
我們使用MySQL存儲了FriendFeed的所有數(shù)據(jù)。數(shù)據(jù)庫隨著用戶基數(shù)的增長而增長了很多。現(xiàn)在已經(jīng)存儲了超過2.5億條記錄與一堆涵蓋了從評論和“喜歡”到好友列表的其他數(shù)據(jù)。

背景

我們使用MySQL存儲了FriendFeed的所有數(shù)據(jù)。數(shù)據(jù)庫隨著用戶基數(shù)的增長而增長了很多?,F(xiàn)在已經(jīng)存儲了超過2.5億條記錄與一堆涵蓋了從評論和“喜歡”到好友列表的其他數(shù)據(jù)。

隨著數(shù)據(jù)的增長,我們也曾迭代地解決了隨著如此迅猛的增長而帶來的擴(kuò)展性問題。我們的嘗試很有代表性,例如使用只讀mysql從節(jié)點和memcache來增加讀取吞吐量,對數(shù)據(jù)庫進(jìn)行分片來提高寫入吞吐量。然而,隨著業(yè)務(wù)的增長,添加新功能比擴(kuò)展既有功能以迎合更多的流量變得更加困難。

特別的,對 schema 做改動或為超過 1000-2000 萬行記錄的數(shù)據(jù)庫添加索引會將數(shù)據(jù)庫鎖住幾個小時。刪除舊索引也要占用這么多時間,但不刪除它們會影響性能;因為數(shù)據(jù)庫要持續(xù)地在每個INSERT上讀寫這些沒用的區(qū)塊,并將重要的區(qū)塊擠出了內(nèi)存。為避免這些問題需要采取一些復(fù)雜的措施(例如在從節(jié)點上設(shè)置新的索引,然后將從節(jié)點與主節(jié)點對調(diào)),但這些措施會引發(fā)錯誤并且實施起來比較困難,它們阻礙了需要改動 schema/索引才能實現(xiàn)的新功能。由于數(shù)據(jù)庫的嚴(yán)重分散,MySQL 的關(guān)系特性(如join)對我們沒用,所以我們決定脫離 RDBMS。

雖然已有許多用于解決靈活 schema 數(shù)據(jù)存儲和運行時構(gòu)建索引的問題(例如 CouchDB)的項目。但在大站點中卻沒有足夠廣泛地用到來說服人們使用。在我們看到和運行的測試中,這些項目要么不穩(wěn)定,要么缺乏足夠的測試(參見這個有點過時的關(guān)于 CouchDB 的文章)。MySQL 不錯,它不會損壞數(shù)據(jù);復(fù)制也沒問題,我們已經(jīng)了解了它的局限。我們喜歡將 MySQL 用于存儲,僅僅是非關(guān)系型的存儲。

幾經(jīng)思量,我們決定在 MySQL 上采用一種無模式的存儲系統(tǒng),而不是使用一個完全沒接觸過的存儲系統(tǒng)。本文試圖描述這個系統(tǒng)的高級細(xì)節(jié)。我們很好奇其他大型網(wǎng)站是如何處理這些問題的,另外也希望我們完成的某些設(shè)計會對其他開發(fā)者有所幫助。

綜述

我們在數(shù)據(jù)庫中存儲的是無模式的屬性集(例如JSON對象或python字典)。存儲的記錄只需一個名為id的16字節(jié)的UUID屬性。對數(shù)據(jù)庫而言實體的其他部分是不可見的。我們可以簡單地存入新屬性來改變schema(可以簡單理解為數(shù)據(jù)表中只有兩個字段:id,data;其中data存儲的是實體的屬性集)。

我們通過保存在不同表中的索引來檢索數(shù)據(jù)。如果想檢索每個實體中的三個屬性,我們就需要三個數(shù)據(jù)表-每個表用于檢索某一特定屬性。如果不想再用某一索引了,我們要在代碼中停止該索引對應(yīng)表的寫操作,并可選地刪除那個表。如果想添加個新索引,只需要為該索引新建個MySQL表,并啟動一個進(jìn)程異步地為該表添加索引數(shù)據(jù)(不影響運行中的服務(wù))。

最終,雖然我們的數(shù)據(jù)表增多了,但添加和刪除索引卻變得簡單了。我們大力改善了添加索引數(shù)據(jù)的進(jìn)程(我們稱之為“清潔工")使其在快速添加索引的同時不會影響站點。我們可以在一天內(nèi)完成新屬性的保存和索引,并且我們不需要對調(diào)主從MySQL數(shù)據(jù)庫,也不需要任何其他可怕的操作。

細(xì)節(jié)

MySQL 使用表保存我們的實體,一個表就像這樣 :

  1. CREATE TABLE entities ( 
  2. added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
  3. id BINARY(16) NOT NULL
  4. updated TIMESTAMP NOT NULL
  5. body MEDIUMBLOB, 
  6. UNIQUE KEY (id), 
  7. KEY (updated) 
  8. ) ENGINE=InnoDB; 

之所以使用 added_id 個字段是因為 InnoDB 按物理主鍵順序存儲數(shù)據(jù),自增長主鍵確保新實例在磁盤上按順序?qū)懙嚼蠈嶓w之后,這樣有助于分區(qū)讀寫(相對老的實體,新實體往往讀操作更頻繁,因為 FriendFeed 的 pages 是按時間逆序排列)。實體本身經(jīng) python 字典序列化后使用 zlib 壓縮存儲。

索引單獨存在一張表里,如果要創(chuàng)建索引,我們創(chuàng)建一張新表存儲我們想要索引的數(shù)據(jù)分片的所有屬性。例如,一個 FriendFeed 實體通過看上去是這樣的:

  1.     "id""71f0c4d2291844cca2df6f486e96e37c"
  2.     "user_id""f48b0440ca0c4f66991c4d5f6a078eaf"
  3.     "feed_id""f48b0440ca0c4f66991c4d5f6a078eaf"
  4.     "title""We just launched a new backend system for FriendFeed!"
  5.     "link""http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c"
  6.     "published": 1235697046, 
  7.     "updated": 1235697046, 

我們索引實體的屬性 user_id,這樣我們可以渲染一個頁面,包含一個已提交用戶的所有屬性。我們的索引表看起來是這樣的:

  1. CREATE TABLE index_user_id ( 
  2. user_id BINARY(16) NOT NULL
  3. entity_id BINARY(16) NOT NULL UNIQUE
  4. PRIMARY KEY (user_id, entity_id) 
  5. ) ENGINE=InnoDB; 

我們的數(shù)據(jù)存儲會自動為你維護(hù)索引,所以如果你要在我們存儲上述結(jié)構(gòu)實體的數(shù)據(jù)存儲里開啟一個實例,你可以寫一段代碼(用 python):

  1. user_id_index = friendfeed.datastore.Index
  2. table="index_user_id", properties=["user_id"], shard_on="user_id"
  3. datastore = friendfeed.datastore.DataStore( 
  4. mysql_shards=["127.0.0.1:3306""127.0.0.1:3307"], 
  5. indexes=[user_id_index]) 
  6.  
  7. new_entity = { 
  8. "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"), 
  9. "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"), 
  10. "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"), 
  11. "title": u"We just launched a new backend system for FriendFeed!"
  12. "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c"
  13. "published": 1235697046, 
  14. "updated": 1235697046, 
  15. datastore.put(new_entity) 
  16. entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c")) 
  17. entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf")) 

上面的 Index 類在所有實體中查找 user_id,自動維護(hù) index_user_id 表的索引。我們的數(shù)據(jù)庫是切分的,參數(shù) shard_on 是用來確定索引是存儲在哪個分片上(這種情況下使用 entity["user_id"] % num_shards)。

你可以使用索引實例(見上面的 user_id_index.get_all)查詢一個索引,使用 python 寫的數(shù)據(jù)存儲代碼將表 index_user_id 和表 entities 合并。首先在所有數(shù)據(jù)庫分片中查詢表 index_user_id 獲取實體 ID 列,然后在 entities 提出數(shù)據(jù)。

新建一個索引,比如,在屬性 link 上,我們可以創(chuàng)建一個新表:

  1. CREATE TABLE index_link ( 
  2. link VARCHAR(735) NOT NULL
  3. entity_id BINARY(16) NOT NULL UNIQUE
  4. PRIMARY KEY (link, entity_id) 
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

我們可以修改數(shù)據(jù)存儲的初始化代碼以包含我們的新索引:

  1. user_id_index = friendfeed.datastore.Index
  2. table="index_user_id", properties=["user_id"], shard_on="user_id"
  3. link_index = friendfeed.datastore.Index
  4. table="index_link", properties=["link"], shard_on="link"
  5. datastore = friendfeed.datastore.DataStore( 
  6. mysql_shards=["127.0.0.1:3306""127.0.0.1:3307"], 
  7. indexes=[user_id_index, link_index]) 

我可以異步構(gòu)建索引(特別是實時傳輸服務(wù)):

  1. ./rundatastorecleaner.py --index=index_link 

#p#

一致性與原子性

由于采用分區(qū)的數(shù)據(jù)庫,實體的索引可能存儲在與實體不同的分區(qū)中,這引起了一致性問題。如果進(jìn)程在寫入所有索引表前崩潰了會怎樣?

許多有野心的 FriendFeed 工程師傾向于構(gòu)建一個事務(wù)性協(xié)議,但我們希望盡可能地保持系統(tǒng)的簡潔。我們決定放寬限制:

  • 保存在主實體表中的屬性集是規(guī)范完整的
  • 索引不會對真實實體值產(chǎn)生影響

因此,往數(shù)據(jù)庫中寫入實體時我們采用如下步驟:

  1. 使用 InnoDB 的 ACID 屬性將實體寫入 entities 表。
  2. 將索引寫入所有分區(qū)中的索引表。

我們要記住從索引表中取出的數(shù)據(jù)可能是不準(zhǔn)確的(例如如果寫操作沒有完成步驟2可能會影響舊屬性值)。為確保采用上面的限制能返回正確的實體,我們用索引表來決定要讀取哪些實體,但不要相信索引的完整性,要使用查詢條件對這些實體進(jìn)行再過濾:

1.根據(jù)查詢條件從索引表中取得 entity_id

2.根據(jù) entity_id 從 entities 表中讀取實體

3.根據(jù)實體的真實屬性(用 Python)過濾掉不符合查詢條件的實體

為保證索引的持久性和一致性,上文提到的“清潔工”進(jìn)程要持續(xù)運行,寫入丟失的索引,清理失效的舊索引。它優(yōu)先清理最近更新的實體,所以實際上維護(hù)索引的一致性非???幾秒鐘).

性能

我們對新系統(tǒng)的主索引進(jìn)行了優(yōu)化,對結(jié)果也很滿意。以下是上個月 FriendFeed 頁面的加載延時統(tǒng)計圖(我們在前幾天啟動了新的后端,你可以根據(jù)延時的顯著回落找到那一天)。

 

特別地,系統(tǒng)的延時現(xiàn)在也很穩(wěn)定(哪怕是在午高峰期間)。如下是過去24小時FriendFeed頁面加載延時圖。

與上周的某天相比較:

系統(tǒng)到目前為止使用起來很方便。我們在部署之后也改動了幾次索引,并且我們也開始將這種模式應(yīng)用于 MySQL 中那些較大的表,這樣我們在以后可以輕松地改動它們的結(jié)構(gòu)。

原文鏈接:http://www.oschina.net/translate/friendfeed-schemaless-mysql-new

責(zé)任編輯:Ophira 來源: 開源中國社區(qū)
相關(guān)推薦

2020-07-07 07:47:07

Java無鎖技術(shù)

2024-12-16 17:02:58

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

2009-11-06 13:40:07

2012-06-29 17:46:41

BMCIT服務(wù)管理SaaS

2010-05-13 15:54:56

MySQL分頁查詢

2016-03-18 09:44:05

隊列分析應(yīng)用留存用戶行為

2024-10-14 15:04:15

C++std::any存儲

2009-05-04 10:25:36

XML.NET數(shù)據(jù)

2024-01-15 16:28:42

ChatGPTGenAI人工智能

2018-04-18 10:04:56

數(shù)字化 CIO

2020-11-25 08:00:37

MySQL存儲

2010-10-26 14:19:04

oracle存儲過程

2010-04-16 11:03:02

Oracle存儲過程

2024-01-18 17:39:00

數(shù)據(jù)庫存儲索引

2010-04-06 11:39:17

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

2023-08-03 08:00:00

數(shù)據(jù)湖模式驗證

2011-03-16 09:05:29

iptablesNAT

2020-10-19 06:47:05

爬蟲數(shù)據(jù)Jsoup

2016-11-09 08:35:24

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

2021-03-30 11:06:29

數(shù)據(jù)驅(qū)動項目經(jīng)理CIO
點贊
收藏

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

主站蜘蛛池模板: 久久夜视频 | 亚洲精品91| 成人中文网 | 国产美女精品视频 | av在线免费观看网站 | 国产精品美女久久久久久免费 | 免费黄色的视频 | 高清人人天天夜夜曰狠狠狠狠 | 国产一级网站 | 午夜视频精品 | 国产精品视频一区二区三区不卡 | 欧产日产国产精品视频 | 国产久| 亚洲三级视频 | 91免费在线视频 | 色在线看 | 操操日| 中文字幕亚洲欧美 | 亚洲国产日韩欧美 | 午夜网站视频 | 欧美综合色 | aacc678成免费人电影网站 | 亚洲国产高清高潮精品美女 | 亚洲一区二区三区视频在线 | 久久国产一区二区 | 欧美日韩在线精品 | 91av精品| av色站 | 国产区在线观看 | 欧美久久天堂 | 一区二区在线观看av | 国产一区二区影院 | 久久久久久国产 | 国内精品久久久久久久影视简单 | 99视频免费在线 | 羞羞网站在线观看 | 亚洲精品视频在线播放 | 免费看一区二区三区 | 草久网 | 91一区二区在线观看 | 久久国产欧美日韩精品 |