2011-10-12 5 views
3

私はPostgreSQL 8.4でRails 3.1を使用しています。 GUIDプライマリキーを使用する必要があると仮定しましょう。 1つの潜在的な欠点は、インデックスの断片化です。 MS SQLでは、特別なシーケンシャルGUIDを使用することをお勧めします。シーケンシャルGUIDへの1つのapproachは、GUIDの最後のMACアドレス部分に6バイトのタイムスタンプを代入するCOMBination GUIDです。これにはいくつかの主流が採用されています.COMBはNHibernate(NHibernate/Id/GuidCombGenerator.cs)でネイティブに利用できます。主キーのGUIDを使用する場合、COMB GUIDはRails 3.1で良いアイデアですか?

私は(UUIDToolsの助けを借り2.1.2宝石に)Railsの中でCOMBのGUIDを作成する方法を考え出したが、それはいくつかの未回答の質問を残し思う:

  • んPostgreSQLはインデックスの断片化に苦しみますプライマリキーがタイプUUIDの場合
  • GUIDの下位6バイトがシーケンシャルの場合、フラグメンテーションが回避されますか?
  • COMB GUIDは、RailsでシーケンシャルGUIDを作成するための許容可能で信頼できる方法の下に実装されていますか?

あなたのご意見ありがとうございます。


create_contacts.rbマイグレーション

class CreateContacts < ActiveRecord::Migration 
    def up 
    create_table :contacts, :id => false do |t| 
     t.column :id, :uuid, :null => false # manually create :id with underlying DB type UUID 
     t.string :first_name 
     t.string :last_name 
     t.string :email 

     t.timestamps 
    end 
    execute "ALTER TABLE contacts ADD PRIMARY KEY (id);" 
    end 

    # Can't use reversible migration because it will try to run 'execute' again 
    def down 
    drop_table :contacts # also drops primary key 
    end 
end 

/app/models/contact.rb

class Contact < ActiveRecord::Base 
    require 'uuid_helper' #rails 3 does not autoload from lib/* 
    include UUIDHelper 

    set_primary_key :id 
end 

/lib/uuid_tools.rb

require 'uuidtools' 

module UUIDHelper 
    def self.included(base) 
    base.class_eval do 
     include InstanceMethods 
     attr_readonly :id  # writable only on a new record 
     before_create :set_uuid 
    end 
    end 

    module InstanceMethods 
    private 
    def set_uuid 
     # MS SQL syntax: CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER) 

     # Get current Time object 
     utc_timestamp = Time.now.utc 

     # Convert to integer with milliseconds: (Seconds since Epoch * 1000) + (6-digit microsecond fraction/1000) 
     utc_timestamp_with_ms_int = (utc_timestamp.tv_sec * 1000) + (utc_timestamp.tv_usec/1000) 

     # Format as hex, minimum of 12 digits, with leading zero. Note that 12 hex digits handles to year 10889 (*). 
     utc_timestamp_with_ms_hexstring = "%012x" % utc_timestamp_with_ms_int 

     # If we supply UUIDTOOLS with a MAC address, it will use that rather than retrieving from system. 
     # Use a regular expression to split into array, then insert ":" characters so it "looks" like a MAC address. 
     UUIDTools::UUID.mac_address = (utc_timestamp_with_ms_hexstring.scan /.{2}/).join(":") 

     # Generate Version 1 UUID (see RFC 4122). 
     comb_guid = UUIDTools::UUID.timestamp_create().to_s 

     # Assign generted COMBination GUID to .id 
     self.id = comb_guid 

     # (*) A note on maximum time handled by 6-byte timestamp that includes milliseconds: 
     # If utc_timestamp_with_ms_hexstring = "FFFFFFFFFFFF" (12 F's), then 
     # Time.at(Float(utc_timestamp_with_ms_hexstring.hex)/1000).utc.iso8601(10) = "10889-08-02T05:31:50.6550292968Z". 
    end 
    end 
end 
+0

Re: "レール3はlib/*から自動ロードされません";あなたが 'config.autoload_paths + =%W(#{config.root}/lib)'を実行した場合はそれが実行されます。 – qerub

答えて

4
  • PRIMARY KEYがタイプUUIDの場合、PostgreSQLはインデックスの断片化を受けますか?

はい、予想されます。しかし、もしあなたが起こらないCOMB戦略を使用しようとするならば。行は常に順番に表示されます(これは完全に真実ではありませんが、私には負担してください)。

また、ネイティブpgsql UUIDとVARCHARの間のパフォーマンスはnot all that differentです。考慮すべき別のポイント。 GUIDの下位6つのバイトが連続している場合

  • は、断片化は避けていますか?私はUUID1(RFC 4122)がシーケンシャルであることがわかりました私のテストでは

は、すでに生成されたUUIDで追加のタイムスタンプがあります。しかし、はい、最後の6バイトにタイムスタンプを追加すると、その順序が保証されます。とにかくそれは、既に存在しているタイムスタンプが注文の保証ではないようだからです。 Railsの中でシーケンシャルGUIDを作成するための許容可能な、信頼性の高い方法の下に実施されるようなCOMB here

  • についての詳細は、COMB GUIDですか?

私はレールを使用していないが、私はジャンゴでそれをやった方法を紹介します:

nodeは、ハードウェアアドレスを特定する48ビットの正の整数である
import uuid, time 

def uuid1_comb(obj): 
    return uuid.uuid1(node=int(time.time() * 1000)) 

実装について、uuidを使用する主な利点の1つは、データベース外で安全に生成できることです。そのため、ヘルパークラスを使用することが有効です。 snowflakeのような外部サービスをいつでも使用できますが、この時点では時期尚早の最適化が行われている可能性があります。

+0

これは私が「タンブルウィード」のバッジを手に入れたほどには未回答です!私はあなたの興味と答えに感謝し、それはCOMB GUIDのコンセプトへの私の自信を助けます。 (興味深いことに、私たちは両方とも同じ[Informitの記事](http://www.informit.com/articles/article.aspx?p=25862)を参考にしています)。あなたのdjangoの実装はおそらく素晴らしいですが、ヘルパーIとはまったく異なります使用しています。一つは、私はUUID1を提供するUUIDToolsヘルパーを思い出さない。私はまだRailsを使っている人から何か確認や訂正を受けたいと思う。 –

+0

私は、このPythonコードの変形を使用して、最初の部分が最後の部分ではなく連続しているときに、Postgresがより良く挿入するので、画期的な順序の部分がUUIDの先頭にあるようにしました。また、別のサーバーにGUIDを作成する場合は、衝突を避けるためにクロックが同期している必要があります。有益な答えをありがとう! – mVChr

関連する問題