發布一個基於TokyoTyrant的C#用戶端開源項目

來源:互聯網
上載者:User

這裡開放的是用戶端主要功能代碼,開源的目的一方面是希望更多的人來學習研究TC和TT,同時大家可以下載本C#源碼繼續最佳化提升效能,同時尋找BUG,必定本人精力能力有限,而Discuz!NT企業版的功能點又太多(抽空會多寫文章進行介紹)實在有些力不從心了,呵呵:)

好了,為了便於使用,下面先對源碼中的專案檔進行說明:

源碼包中包括三個項目:
1.Discuz.EntLib.TokyoTyrant 核心功能代碼(目前名空間暫以產品命名)
2.TTSample 主要用於載入測試資料,並對比SQLSERVER資料庫的建立查詢功能的速度。
3.TTSampleConsole 使用核心功能代碼的例子(本文中會介紹其中主要功能)

其中Discuz.EntLib.TokyoTyrant中類圖如下:

該用戶端有如下特點:
支援TcpClient串連池
支援UTF-8編碼
支援初始化連結數,連結到期時間,最大空閑時間,最長工作時間等設定

下面介紹一下如何使用:
1.初始化連結池:

複製代碼 代碼如下:pool = TcpClientIOPool.GetInstance("dnt_online");//連結池名稱(即DNT線上表)
pool.SetServers(new string[] { "10.0.4.66:11211"});
pool.InitConnections = 8;
pool.MinConnections = 8;
pool.MaxConnections = 8;
pool.MaxIdle = 30000;
pool.MaxBusy = 50000;
pool.MaintenanceSleep = 300000;
pool.TcpClientTimeout = 3000;
pool.TcpClientConnectTimeout = 30000;
pool.Initialize();

2.CRUD操作:
建立一條記錄(以DISCUZ!NT線上表欄位為例):

複製代碼 代碼如下:IDictionary<string, string> columns = new System.Collections.Generic.Dictionary<string, string>();
columns.Add("olid", i.ToString());
columns.Add("userid", i.ToString());
columns.Add("ip", "10.0.7." + i);
columns.Add("username", "使用者" + i);
columns.Add("nickname", "使用者" + i);
columns.Add("password", "");
columns.Add("groupid", "5");
columns.Add("olimg", "");
columns.Add("adminid", "0");
columns.Add("invisible", "0");
columns.Add("action", "0");
columns.Add("lastactivity", "1");
columns.Add("lastposttime", DateTime.Now.ToString());
columns.Add("lastpostpmtime", DateTime.Now.ToString());
columns.Add("lastsearchtime", DateTime.Now.ToString());
columns.Add("lastupdatetime", DateTime.Now.ToString());
columns.Add("forumid", "0");
columns.Add("forumname", "");
columns.Add("titleid", "0");
columns.Add("title", "");
columns.Add("verifycode", "");
columns.Add("newpms", "0");
columns.Add("newnotices", "0");
TokyoTyrantService.PutColumns(TTPool.GetInstance(), i.ToString(), columns, true);//true表示如tc中有記錄則覆蓋,沒有則建立該記錄

查詢操作:
首先構程過一個查詢(條件)對象,比如查詢欄位olid = 1的線上使用者資訊,則該對象定義如下:
new Query().NumberEquals("olid", 1)

然後將其放入TokyoTyrantService的QueryRecords方法中(注意綁定連結池),如下:

var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("olid", 1));
//遍曆當前結果集
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...資料繫結操作
}

更複雜的查詢,如下(查詢forumid = 16 and userid<1000 ,同時按userid欄位倒序排列的前三條記錄):

qrecords = TokyoTyrantService.QueryRecords(pool, new Query().NumberGreaterThanOrEqual("forumid", 16).
NumberLessThan("userid", 1000).OrderBy("userid", QueryOrder.NUMDESC).LimitTo(3, 0));
這裡的比較運行符可以參見源碼中的枚舉類型,如下:

複製代碼 代碼如下:public enum QueryOperation
{
STREQ = 0, // # 查詢條件: 表示與操作對象的文字內容完全相同(=)
STRINC = 1, // # 查詢條件: 表示含有操作物件常值的內容(LIKE ‘%文字%')
STRBW = 2, // # 查詢條件: 表示以操作對象的文字行列開始(LIKE ‘文字%')
STREW = 3, // # 查詢條件: 表示到操作對象的文字行列結束(LIKE ‘%文字')
STRAND = 4, // # 查詢條件: 表示包含操作對象的文字行列中右逗號分開部分的欄位的全部(name LIKE ‘%文字㈠%' AND name LIKE ‘%文字㈡%')
STROR = 5, // # 查詢條件: 表示包含操作物件常值段中逗號分開部分的其中一部分(name LIKE ‘%文字㈠%' OR name LIKE ‘%文字㈡%')
STROREQ = 6, // # 查詢條件: 表示與操作物件常值段中逗號分開部分的其中某部分完全相同( name = ‘文字㈠' OR name =‘文字㈡')
STRRX = 7, // # 查詢條件: 表與與常規運算式匹配
NUMEQ = 8, // # 查詢條件: 表示等於操作對象的數值(=)
NUMGT = 9, // # 查詢條件: 表示比操作對象的數值要大(>)
NUMGE = 10, // # 查詢條件: 表大於或等於操作對象的數值(>=)
NUMLT = 11, // # 查詢條件: 表示比操作對象的數值要小(<)
NUMLE = 12, // # 查詢條件: 表示小於或等於操作對象的數值(<=)
NUMBT = 13, // # 查詢條件: 表示其大小處於操作物件常值段中被逗號分開的兩個數值的中間(between 100 and 200)
NUMOREQ = 14, // # 查詢條件: 表示其大小處於操作物件常值段中被逗號分開的兩個數值的中間(between 100 and 200)
NEGATE = 1 << 24, // # 查詢條件: 負標誌negation flag
NOIDX = 1 << 25 // # 查詢條件: 非索引標誌
}

查詢指定主鍵(如本例中的olid,效率最高) 複製代碼 代碼如下:var qrecords = TokyoTyrantService.GetColumns(pool, new string[]{"1", "2", "3"});
foreach (string key in qrecords.Keys)
{
var column = qrecords[key];
}

更新操作:
因為TC的TCT結構沒有提供直接更新記錄中某一欄位的功能,所以只能全部取出相關記錄的所有欄位,然後再更新全部欄位(這種做法的效率不高,但在MONGODB中是可以更新部分欄位)。所以要組合使用查詢和建立操作中的文法,即選查出相應記錄,然後再使用PutColumns方法更新該記錄,形式如下: 複製代碼 代碼如下:var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("olid", 1));
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...資料繫結操作
TokyoTyrantService.PutColumns(TTPool.GetInstance(), column["olid"], columns, true);//column["olid"]為主鍵,類似資料庫裡的主鍵,以其為查詢條件,速度最快
}

刪除操作
該操作有兩種執行方法,一種是選查詢出合格記錄,然後再刪除(依次刪除),一種是直接給定要刪除的主鍵直接刪除(效率比前者高)。第一種(可以針對不用欄位進行查詢,並將相應結果的主鍵做了刪除依據)

複製代碼 代碼如下:var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("userid", 1));
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...資料繫結操作
TokyoTyrantService.Delete(TTPool.GetInstance(), column["olid"]);//column["olid"]為主鍵,類似資料庫裡的主鍵
}

第二種(刪除olid為1或2或3或4的索引值記錄,只能刪除以主鍵為條件的記錄):
TokyoTyrantService.DeleteMultiple(pool, new string[] { "1", "2", "3", "4" });

建立索引
TC中支援幾種類型的欄位索引如下(經常用的是數值型和字元型): 複製代碼 代碼如下:/// <summary>
/// 索引類型
/// </summary>
public enum IndexOption : int
{
LEXICAL = 0, // # 文本型索引
DECIMAL = 1, // # 數值型索引
TOKEN = 2, // # 標記倒排索引.
QGRAM = 3, // #QGram倒排索引.
OPT = 9998, // # 9998, 對索引最佳化
VOID = 9999, // # 9999, 移除索引.
KEEP = 1 << 24 // # 16777216, 保持已有索引.
}

比如線上表中經常用的欄位索引設定如下: 複製代碼 代碼如下:TokyoTyrantService.SetIndex(pool, "olid", IndexOption.DECIMAL);
TokyoTyrantService.SetIndex(pool, "userid", IndexOption.DECIMAL);
TokyoTyrantService.SetIndex(pool, "password", IndexOption.LEXICAL);
TokyoTyrantService.SetIndex(pool, "ip", IndexOption.LEXICAL);
TokyoTyrantService.SetIndex(pool, "forumid", IndexOption.DECIMAL);
TokyoTyrantService.SetIndex(pool, "lastupdatetime", IndexOption.DECIMAL);

3.其它常用操作

複製代碼 代碼如下:LimitTo(int max, int skip):類似於MYSQL中的LIMIT方法,其中max如同mssql中的TOP,而skip則表示跳過多少條記錄(類似LINQ中的那個Skip方法)
Vanish(TcpClientIOPool pool);清空所有記錄
QueryRecordsCount(TcpClientIOPool pool, Query query)//查詢指定條件的記錄數
GetRecordCount(TcpClientIOPool pool)//返回當前表中的記錄總數
GetDatabaseSize(TcpClientIOPool pool);//擷取資料庫(表)資訊
IteratorNext(TcpClientIOPool pool)//一個迭代器,用於遍曆所有記錄

4.因為其相容Memcached,所以提供方法支援(鍵/值對)

複製代碼 代碼如下:Put(TcpClientIOPool pool, string key, string value, bool overwrite)//該操作方法將不像Put那樣擷取伺服器端返回的資訊
PutFast(TcpClientIOPool pool, string key, string value)//快速儲存索引值對(不再擷取服務端返回資訊). 如索引值已存在則將被覆蓋
PutMultiple(TcpClientIOPool pool, IDictionary<string, string> items) //一次添加多值
Delete(TcpClientIOPool pool, string key)//刪除指定鍵的記錄
DeleteMultiple(TcpClientIOPool pool, string[] keys)//刪除指定鍵組的記錄
Get(TcpClientIOPool pool, string key)//擷取指定鍵的記錄(單條)
GetSize(TcpClientIOPool pool, string key)//擷取指定鍵的大小
GetColumns(TcpClientIOPool pool, string[] keys)//擷取指定鍵組的記錄(多條)

5.排序

public enum QueryOrder
{
STRASC = 0, // # 排序類型: 表示按照文本型欄位內的常值內容在字典中排列順序的升序
STRDESC = 1, // # 排序類型: 表示按照文本型欄位內的常值內容在字典中排列順序的降序
NUMASC = 2, // # 排序類型: 表示按照數值大小的升序
NUMDESC = 3 // # 排序類型: 表示按照數值大小的降序
}

用法(如降序並取前16條記錄):

qrecords = TokyoTyrantService.QueryRecords(pool, new Query().OrderBy("userid", QueryOrder.NUMDESC).LimitTo(16, 0));
注意:盡量避免對大資料集(如100w條記錄)進行排序,那樣耗時會很嚴重。所以盡量在OrderBy之前指定查詢條件,從而縮減查詢結果集的尺寸。

其它說明:
TT的啟動參數(這裡以TCT類型為例):
註:網上有一些關於TC+TT與MONGODB,Redis的速度測試,所以這裡我想有必要對TT的啟動參數做一下介紹,因為這會關係到最終的測試結果。
因為兩者都使用了MMAP模式,而TC+TT要使用MMAP,就要使用下面參數:

xmsiz:指定了TCHDB的擴充MMAP記憶體大小,預設值為 67108864,也就是64M,如果資料庫檔案超過64M,則只有前部分會映射在記憶體中,所以寫入效能會下降。
bnum: 指定了bucket array的數量。推薦設定bnum為預計儲存總記錄數的0.5~4倍,使key的雜湊分布更均勻,減少在 bucket內二分尋找的時間複雜度。

比如有100w條記錄,這裡可以使用下面命令列啟動ttserver:
ttserver -host 10.0.4.66 -port 11211 -thnum 1024 -dmn -pid /ttserver/ttserver.pid -log /ttserver/ttserver.log -le -ulog /ttserver/ -ulim 256m -sid 1 -rts /ttserver/ttserver.rts /ttserver/database.tct#bnum=1000000#rcnum=1000000#xmsiz=1073741824 (注:1073741824=1G)

當然TTServer中針對不同的資料庫(TC中支援6種),都有相應的參數進行啟動配置(有重複),這會導致的查詢和插入資料的結果上有很大的差異,更多的內容可以參見這個連結。

下面我將自己對TC+TT(僅使用TCT檔案類型,其它5種類型都比這個類型快許多)與MONGODB的測試結果做一下說明:
機器是一個普遍台式機:1.5g記憶體+1.5gCPU,64位的centos機器,150g硬碟。

mongodb (centos 64bit) :
插入1000000 條記錄,耗時:250377毫秒
對1000000條記錄,查詢10000 次記錄,耗時:8100毫秒 (偶爾出現7500毫秒) (查詢"_id"主鍵速度在6995毫秒上下)
對1000000條記錄,查詢100000 次記錄,耗時:77101毫秒

ttcache(centos 64bit,使用上面的啟動參數):
建立 1000000 條資料,耗時 589472毫秒
對1000000條記錄,查詢 10000 次資料,耗時 4843毫秒
對1000000條記錄,查詢 100000 次資料,耗時 47903毫秒

注:查詢條件動態變化,以類比實際生產環境。
比較發現MONGODB插入速度要比TTCACHE快至少一倍(MONGODB在WINDOWS下也是如此),但10000次查詢速度會慢大約40%-50%。這裡的查詢和插入操作都是每做一次操作就Connect一次伺服器,操作結束時則將當前連結放到連結池中,而不是開啟一個長連結來做大量操作。其中TTSERVER所使用的用戶端分別是本文的這個工具, MONGODB則使用的是MongoDB.Driver。
下面是MSSQL資料庫操作結果:
大量建立 1000000 條資料,耗時 9020196毫秒
批量查詢 10000 條資料,耗時 106040毫秒
批量查詢 100000 條資料,耗時 773867毫秒

我想進行這類測試,還是不要使用什麼WINDOWS(盡量MONGODB在WINDOW下插入資料的速度已很快)或其它作業系統。而應該使用LINUX(盡量是64位)。當然記憶體要盡量的大,因為儘管TC+TT已很省記憶體(必定符合日本的國情,資源少還要多辦事),但如果要提升查詢和插入速度,還是建議4g以上的記憶體做測
試。而MONGODB本來對記憶體要求很高(包括CPU)。

因為mongodb的插入速度非常快,且在資料庫大量可以建立檔案來儲存新的資料(不像TCT使用一個資料檔案),所以在更大層級的資料量插入上依然效能穩定。看來將它視為海量資料存放區的分布解決方案還是很有可行性的。當然我目前正在考慮一個架構,就是將MongoDb和TC/TT組合起來,實現讀寫分離(即將TC作為讀資料庫slavedb,並發性和查詢速度快),而將MongoDb作為寫資料庫masterdb(更新和插入速度快)。將分布式的MongoDb資料檔案與前端TC中的檔案依次對應(使用C#代碼實現兩者之間的資料同步和邏輯調用),這樣融合兩者各自的優勢的結果。當然目前這隻是想法,且離文本的內容越來越遠了,呵呵。

好了,今天的內容就選到這裡了。
下載連結:http://tokyotyrantclient.codeplex.com/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.