自增長主鍵Id的另類設計


一、引言

在使用ORM框架時,一個表有一個主鍵是必須的,如果沒有主鍵,就沒有辦法來唯一的更新一條記錄。在Sql Server數據庫和Mysql數據庫設置自增長的主鍵是一件很輕松的事情,如果在Oracle數據庫中設置自增長的主鍵是比較繁瑣的。本文不討論數據庫里單表的自增長問題,探討的是多表自增長唯一Id的設計。
如果各位看官遇到這個多表自增長唯一Id的這個需求,會怎么處理呢?

 

二、GUID的介紹

關於自增長主鍵的問題,有些人可能會想到.Net中的GUID,先對這個GUID進行測試。

 public void GuidTest()
        {
            string guid = Guid.NewGuid().ToString();
            Console.WriteLine(guid);
            Console.WriteLine(guid.Length);
        }
GUID Test

下面是輸出結果:

c8eb1c81-eafa-423b-a1bf-15fd5df829c4
36

可以看到這個GUID是一個字符串,長度36位,還真夠長的,估計在用的時候,如果想減少位數,可以把橫杠-去掉,少了4位,還剩32位,不過還是挺長的。
以我的觀察來看,現在如果誰把數據庫的主鍵設計成這個GUID,還真的是個二貨:)
原因有三,1)太長了,在數據庫里占空間  
              2)排序不方便
              3)檢索時字符串的效率不如整數
本來對GUID只想一筆而過,看來寫得太多了。
 

三、單機版自增長Id實現

針對多表下的自增長的唯一的ID,首先想到的就是時間,把這個時間格式化成整形的數字,就可以解決問題。為了減少位數,把日期的年的部分20去掉,毫秒數取兩位,共15位。
為了防止重復,程序每次生成,如果當前生成的Id大於當前時間下的Id,會保存在在xml里,下次程序啟動,會檢查xml里保存的Id和當前時間下的Id,兩者取其大。這樣做是為了,防止程序重啟,生成的Id重復。當然,為了多線程的安全,順手lock。代碼如下:

public class IdGenerator
    {
        private static string idXml = "id.xml";
        private static long current = 0;
        private static object obj = new object();
        private static string format = "yyMMddHHmmssfff"; //15位

        static IdGenerator()
        {
            var xDoc = XDocument.Load(idXml);
            long last = 0;
            long.TryParse(xDoc.Root.Value, out last);

            //每次啟動檢查最后生成的Id是否大於當前時間下的Id
            long now = long.Parse(DateTime.Now.ToString(format));
            if (last > now)
            {
                current = last;
            }
            else
            {
                current = now;
            }
        }

        public static long GetId()
        {
            lock (obj)
            {
                current += 1;

                //如果當前的生成的Id大於當前時間下的Id,就要保存下來
                if (current > long.Parse(DateTime.Now.ToString(format)))
                {
                    var xDoc = XDocument.Load(idXml);
                    xDoc.Root.Value = current.ToString();
                    xDoc.Save(idXml);
                }
                return current;
            }
        }
    }
IdGenerator


四、分布式下的自增長Id實現
在現實環境下,項目中可能有多個網站或多個應用程序使用這個自增長的Id,這樣就要考慮分布式的情況,這里實現分布式使用WCF技術,WCF實現在本地和服務器通過契約來通信,抽象之后,調用服務器代碼就像調用本地代碼。
首先定義契約:

namespace WCFServiceTest
{
    [ServiceContract]
    public interface IIdGenContract
    {
        [OperationContract]
        long GetIdByWCF();
    }
}
IIdGenContract

該接口里有一個方法GetIdByWCF,這個方法需要服務端來實現。

namespace WCFServiceTest
{
    [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, MaxItemsInObjectGraph = Int32.MaxValue)]
    public class IdGenService : IIdGenContract
    {
        public static IdGenService Instance = new IdGenService();

        public long GetIdByWCF()
        {
            return IdGenerateHelper.IdGenerator.GetId();
        }

    }
}
IdGenService

下一步就是開啟這個服務

namespace WCFServiceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(IdGenService.Instance);
            host.Open();
            Console.WriteLine(typeof(IdGenService) + " Opened");
            Console.WriteLine("服務地址:" + host.Description.Endpoints[0].ListenUri);
          
            Console.Read();
        }
    }
}
Program

這里面需要配置文件,如果是網站就是web.config,桌面程序就是app.config,配置文件的詳細會在下面的demo代碼里。
服務端完成了,下面就是編寫客戶端的代碼來調用該服務了。客戶端通過契約(接口)來和服務端進行通信,也要實現這個接口。

namespace ConsoleApplication2
{
    public class IdService : ClientBase<IIdGenContract>, IIdGenContract
    {
        public long GetIdByWCF()
        {
            return base.Channel.GetIdByWCF();
        }
    }
}
ClientIdService

接下來就是測試代碼了

namespace ConsoleApplication2
{
    /// <summary>
    /// 通過調用WCF服務的方式生成自增長Id
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            long id = new ClientIdService().GetIdByWCF();
            Console.WriteLine(id);
            Console.Read();
        }
    }
}
Program

 

四、結束
一切按計划執行,輸出一個唯一的Id,本文完整Demo下載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM