<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Sharding & IDs at Instagram

      原文:http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram

      Instagram的存儲量非常大,差不多每秒25-90張照片。為了保證我們的重要的數據能夠合理的存儲以便快速的提取應用,我們對數據進行了分片 -- 換句話說,將數據放到很多小的桶(buckets)中,每個桶存儲一部分的數據。

      我們的應用服務器跑的是Django ,后端數據庫采用PostgreSQL 。我們決定采用分片后,第一個問題是我們是否還保留我們的主數據庫,是否應該轉換到其他的存儲方案。我們評估了一些不同的NOSQL的解決方案,但是最后覺得最適合我們的需求的還是通過PostgreSQL server集群實現分片。

      在將數據寫入數據庫集群前,我們必須解決如何分配數據唯一標識的問題(例如,上傳上來的每一張照片)。在一個單數據的情況下,典型的解決方案是使用數據庫原有的自增主鍵特性即可,但是這種方法在同時插入多個數據庫的情況下不可行。這篇博客其他的部分將講述我們如何解決這個問題的。

      開始之前,我們列出了我們系統的基本特性:

      生成的ID應該按時間排序(這樣一個照片ID 的列表就足夠了,并不需要照片的其他信息)

      ID應該是64位的(為了更小的索引以及更好的存儲在像Redis這樣的系統中)

      該系統應引入盡可能少的考慮新的“移動部件”(這里指額外的技術部件) --- 我們如何能夠一直用很少的工程師擴展Instagram是因為選擇簡單、易于理解的解決方案。(The system should introduce as few new ‘moving parts’ as possible—a large part of how we’ve been able to scale Instagram with very few engineers is by choosing simple, easy-to-understand solutions that we trust. )

       

      現成的解決方案

      關于ID生成有很多現成的解決方案,這些是我們曾經考慮過的:

       

      在web程序中生成ID

      這個方案是將整個ID生成邏輯放到應用程序層而非數據庫層。例如:MongoDB的ObjectId,采用12個字節的長度,并且將時間戳進行編碼。另外一種流行的方案是使用UUIDs。

       

      贊成者:

      每個應用程序線程獨立生成ID,這樣最大限度減小了生成ID沖突的概率

      如果你采用時間戳編碼,得到的ID將保持按時間排序、


      反對者:

      通常需要更多的存儲空間(96位或更高)以保證唯一性約束

      一些UUID類型是完全隨機的,沒有自然順序

       

      通過專門的服務生成ID;

      例如:Twitter的 Snowflake,一個Thrift 服務使用Apache ZooKeeper 去協調節點并生成64位的唯一標識ID

       

      贊成者:

      Snowflake ID是64位的,只有UUID的一般大小;
      可以使用時間編碼,保持有序性

      適用于分布式系統(Distributed system that can survive nodes dying )

      反對者:

      將引入額外的復雜性和更多的“移動部件”(ZooKeeper, Snowflake servers)


      DB Ticket Servers

      使用數據庫的自增長技術保證唯一性。Flickr 采用這種方案,但是使用了兩個ticket DBs(一個生成奇數,一個生成偶數),用于避免單點的失敗情況;

      贊成者:

      數據庫比較好理解,也有很好的可擴展性
      反對者:

      最后可能會變成寫的瓶頸(雖然據Flickr,已經有很大的規模,也沒有發現問題 )
      需要管理一對額外的機器(或EC2實例)

      如果使用單個數據庫,將無法避免單點沖突。如果使用多個數據庫,將不再能保證按時間排序。


      上面這些方法,Twitter的 Snowflake 是最接近的,但是額外的復雜性是需要跑一個ID服務。取而代之,我們采用了一種概念類似,但是內建于PostgreSQL中的方案。

       

      我們的方案

      我們的分片系統由數千個通過代碼映射到少量物理分片的邏輯分片組成。使用這種方案,我們可以剛開始使用少量是的數據庫服務器,最后逐漸增加,也很容易實現將一些邏輯分片從一個數據庫轉移到另一個數據庫,而不需要re-bucket 數據。我們通過Postgres的schemas特性使腳本化和管理工作變得很簡單。

      Schemas(不要與單個表的SQL schema 概念混淆)在Postgres中是一個邏輯組。每個Postgres數據庫可以有幾個schemas,每個schema包含一個或多個表。表名在每個schema中必須唯一。而不是在每個數據庫中。默認Postgres 會將創建的東西放到一個叫‘public’的schema 中。

      在我們的系統中,每個“邏輯分片”就是一個Postgres 的schema ,每個分片的表(例如,像我們的照片表)存在于每個schema中;

      我們通過PL/PGSQL(Postgres’ 的內部編程語言)和Postgres的自增長功能完成每個分片內每個表的ID創建工作。

       

      我們每個ID有以下幾部分組成:

      1、41位時間的毫秒部分(gives us 41 years of IDs with a custom epoch / 使我們擁有41年的自定義紀元?)

      2、13位表示邏輯分片的ID

      3、10位表示自增長序列,最大1024.這意味著每個分片,每毫秒我們可以生成1024個ID。

       

      讓我們來看一個例子:假設現在是2011年9月9日,上午5:00,我們的紀元從2011年1月,那么從紀元開始到現在就有1387263000毫秒。所以我們生成ID 的時候,通過左移符號將這個值填入最左面的41位:

      id = 1387263000 << (64-41)

      接下來,我們將要為需要插入的數據進行ID 切片,我們使用user ID,假設有2000個邏輯分片,而我們的user ID是31341,這樣分片ID是31341% 2000 -> 1341,將1341填入到接下來的13位中;


      id |= 1341 << (64-41-13)

       

      Finally, we take whatever the next value of our auto-increment sequence (this sequence is unique to each table in each schema) and fill out the remaining bits. Let’s say we’d generated 5,000 IDs for this table already; our next value is 5,001, which we take and mod by 1024 (so it fits in 10 bits) and include it too:

      id |= (5001 % 1024)

      最后,我們將通過自增長序列(這個序列的唯一性針對在每一個schema的每一個表,即每個schema的每個表有自己的序列)獲取下一個值,并填入到余下的位中。假設我們已經為這個表生成了5000個序列ID,我們下一個序列ID應該是5001,我們用5001模1024,即為余下的ID值:

      id |= (5001 % 1024)

      我們現在已經有了我們的ID,我們可以通過INSERT語句的RETURNING 關鍵字,將ID返回給應用程序;

      這里是the PL/PGSQL的完整例子(例子的schema :insta5):

       

      CREATE OR REPLACE FUNCTION insta5.next_id(OUT result bigint) AS $$
      DECLARE
          our_epoch bigint := 1314220021721;
          seq_id bigint;
          now_millis bigint;
          shard_id int := 5;
      BEGIN
          SELECT nextval('insta5.table_id_seq') %% 1024 INTO seq_id;

          SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
          result := (now_millis - our_epoch) << 23;
          result := result | (shard_id << 10);
          result := result | (seq_id);
      END;
      $$ LANGUAGE PLPGSQL;
      And when creating the table, we do:

      CREATE TABLE insta5.our_table (
          "id" bigint NOT NULL DEFAULT insta5.next_id(),
          ...rest of table schema...
      )

       

      就這樣!主鍵在我們的應用程序中是唯一的(額外的好處,包含在其中切分ID為更易于映射)。我們已經將這一方案應用到我們的產品中了,到目前為止效果良好。有興趣幫助我們找出問題嗎?我們正在招聘!
      Mike Krieger, co-founder

      posted @ 2012-07-10 11:02  紅心李  閱讀(2203)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产成人欧美日韩在线电影 | 大伊香蕉在线精品视频75| 宜兰县| 亚洲中文字幕av天堂| 国产精品久久无中文字幕| 国产99视频精品免费专区| 无码内射中文字幕岛国片| 欧美巨大极度另类| 奶头好大揉着好爽视频| 亚洲av永久一区二区| 毛片无码一区二区三区| 精品人妻一区二区三区四区在线| 人妻少妇精品视频三区二区| 国内精品一区二区不卡| 国产婷婷色一区二区三区| 亚洲精品美女久久久久9999| 永久免费在线观看蜜桃视频| 国产中文字幕精品在线| 天堂网国产| 強壮公弄得我次次高潮A片| 国产精品无遮挡猛进猛出| 少妇人妻偷人精品一区二| 久久久久久久一线毛片| 二区中文字幕在线观看| 亚洲国产午夜福利精品| 色老头在线一区二区三区| 日韩精品区一区二区三vr| 午夜国产理论大片高清| 亚洲综合小综合中文字幕| 人妻激情一区二区三区四区| 国产欧美精品aaaaaa片| 扒开双腿猛进入喷水高潮叫声| 亚洲成A人片在线观看无码不卡| 巨爆乳中文字幕爆乳区| 日韩中文字幕v亚洲中文字幕| а∨天堂一区中文字幕| 韩国无码av片在线观看| 麻豆国产成人AV在线播放| 精品久久久久久无码免费| 乱60一70归性欧老妇| 成人国产精品三上悠亚久久 |