主キーにGUIDを使用する場合、Rails 3.1ではCOMB GUIDは良いアイデアですか?

activerecord guid ruby-on-rails ruby-on-rails-3.1 uuid
主キーにGUIDを使用する場合、Rails 3.1ではCOMB GUIDは良いアイデアですか?

PostgreSQL 8.4でRails 3.1を使用しています。 GUID主キーを使用したい/必要があると仮定しましょう。 潜在的な欠点の1つは、インデックスの断片化です。 MS SQLで推奨される解決策は、特別なシーケンシャルGUIDを使用することです。 順次GUIDへのhttp://www.informit.com/articles/article.aspx?p=25862[approach]は、GUIDの最後のMACアドレス部分を6バイトのタイムスタンプに置き換えるCOMBination GUIDです。 これにはいくつかの主流の採用があります:COMBはNHibernateでネイティブに利用可能です(http://www.symbolsource.org/Public/Metadata/Default/Project/NHibernate/3.0.0.GA/Release/All/NHibernate/NHibernate/Id/GuidCombGenerator .cs [NHibernate / Id / GuidCombGenerator.cs])。

RailsでCOMB GUIDを作成する方法を(UUIDTools 2.1.2 gemの助けを借りて)考え出したと思いますが、未回答の質問がいくつか残っています。

  • PostgreSQLは、PRIMARY KEYのときにインデックスの断片化の影響を受けますか
    タイプ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

  3  2


ベストアンサー

_
* PostgreSQLは、PRIMARY KEYのときにインデックスの断片化の影響を受けますか
タイプUUIDは何ですか?
_

はい、それは予想されることです。 ただし、COMB戦略を使用する場合、それは起こりません。 行は常に順番に並んでいます(完全に正しいわけではありませんが、私に耐えてください)。

また、ネイティブpgsql UUIDとVARCHARの間のパフォーマンスはhttp://jochem.vandieten.net/2008/02/06/postgresql-uuids-and-coldfusion-1/ [すべてではありません]です。 考慮すべきもう1つのポイント。

_
* GUIDの下位6バイトが次の場合、断片化は回避されますか
一連の?
_

私のテストでは、UUID1(RFC 4122)がシーケンシャルであり、生成されたuuidにタイムスタンプが既に追加されていることがわかりました。 ただし、最後の6バイトにタイムスタンプを追加すると、その順序が保証されます。 どうやらそれは私がとにかくやったことです。なぜなら、明らかに既に存在するタイムスタンプは順序を保証するものではないからです。 COMBの詳細http://www.informit.com/articles/article.aspx?p=25862&seqNum=7 [こちら]

_
* 以下に実装されているCOMB GUIDは、許容される信頼できる方法です。
RailsでシーケンシャルGUIDを作成しますか?
_

私はレールを使用していませんが、私はジャンゴでそれをどのようにしたかを紹介します:

import uuid, time

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

ここで、「ノード」はハードウェアアドレスを識別する48ビットの正の整数です。

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

4


タイトルとURLをコピーしました