發(fā)布一個(gè)基于TokyoTyrant的C#客戶端開源項(xiàng)目
這里開放的是客戶端主要功能代碼,開源的目的一方面是希望更多的人來(lái)學(xué)習(xí)研究TC和TT,同時(shí)大家可以下載本C#源碼繼續(xù)優(yōu)化提升性能,同時(shí)查找BUG,必定本人精力能力有限,而Discuz!NT企業(yè)版的功能點(diǎn)又太多(抽空會(huì)多寫文章進(jìn)行介紹)實(shí)在有些力不從心了,呵呵:)
好了,為了便于使用,下面先對(duì)源碼中的項(xiàng)目文件進(jìn)行說(shuō)明:
源碼包中包括三個(gè)項(xiàng)目:
1.Discuz.EntLib.TokyoTyrant 核心功能代碼(目前名空間暫以產(chǎn)品命名)
2.TTSample 主要用于加載測(cè)試數(shù)據(jù),并對(duì)比SQLSERVER數(shù)據(jù)庫(kù)的創(chuàng)建查詢功能的速度。
3.TTSampleConsole 使用核心功能代碼的例子(本文中會(huì)介紹其中主要功能)
其中Discuz.EntLib.TokyoTyrant中類圖如下:
該客戶端有如下特點(diǎn):
支持TcpClient連接池
支持UTF-8編碼
支持初始化鏈接數(shù),鏈接過(guò)期時(shí)間,最大空閑時(shí)間,最長(zhǎng)工作時(shí)間等設(shè)置
下面介紹一下如何使用:
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操作:
創(chuàng)建一條記錄(以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中有記錄則覆蓋,沒有則創(chuàng)建該記錄
查詢操作:
首先構(gòu)程過(guò)一個(gè)查詢(條件)對(duì)象,比如查詢字段olid = 1的在線用戶信息,則該對(duì)象定義如下:
new Query().NumberEquals("olid", 1)
然后將其放入TokyoTyrantService的QueryRecords方法中(注意綁定鏈接池),如下:
var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("olid", 1));
//遍歷當(dāng)前結(jié)果集
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...數(shù)據(jù)綁定操作
}
更復(fù)雜的查詢,如下(查詢forumid = 16 and userid<1000 ,同時(shí)按userid字段倒序排列的前三條記錄):
qrecords = TokyoTyrantService.QueryRecords(pool, new Query().NumberGreaterThanOrEqual("forumid", 16).
NumberLessThan("userid", 1000).OrderBy("userid", QueryOrder.NUMDESC).LimitTo(3, 0));
這里的比較運(yùn)行符可以參見源碼中的枚舉類型,如下:
public enum QueryOperation
{
STREQ = 0, // # 查詢條件: 表示與操作對(duì)象的文字內(nèi)容完全相同(=)
STRINC = 1, // # 查詢條件: 表示含有操作對(duì)象文字的內(nèi)容(LIKE ‘%文字%')
STRBW = 2, // # 查詢條件: 表示以操作對(duì)象的文字行列開始(LIKE ‘文字%')
STREW = 3, // # 查詢條件: 表示到操作對(duì)象的文字行列結(jié)束(LIKE ‘%文字')
STRAND = 4, // # 查詢條件: 表示包含操作對(duì)象的文字行列中右逗號(hào)分開部分的字段的全部(name LIKE ‘%文字㈠%' AND name LIKE ‘%文字㈡%')
STROR = 5, // # 查詢條件: 表示包含操作對(duì)象文字段中逗號(hào)分開部分的其中一部分(name LIKE ‘%文字㈠%' OR name LIKE ‘%文字㈡%')
STROREQ = 6, // # 查詢條件: 表示與操作對(duì)象文字段中逗號(hào)分開部分的其中某部分完全相同( name = ‘文字㈠' OR name =‘文字㈡')
STRRX = 7, // # 查詢條件: 表與與常規(guī)表達(dá)式匹配
NUMEQ = 8, // # 查詢條件: 表示等于操作對(duì)象的數(shù)值(=)
NUMGT = 9, // # 查詢條件: 表示比操作對(duì)象的數(shù)值要大(>)
NUMGE = 10, // # 查詢條件: 表大于或等于操作對(duì)象的數(shù)值(>=)
NUMLT = 11, // # 查詢條件: 表示比操作對(duì)象的數(shù)值要?。?lt;)
NUMLE = 12, // # 查詢條件: 表示小于或等于操作對(duì)象的數(shù)值(<=)
NUMBT = 13, // # 查詢條件: 表示其大小處于操作對(duì)象文字段中被逗號(hào)分開的兩個(gè)數(shù)值的中間(between 100 and 200)
NUMOREQ = 14, // # 查詢條件: 表示其大小處于操作對(duì)象文字段中被逗號(hào)分開的兩個(gè)數(shù)值的中間(between 100 and 200)
NEGATE = 1 << 24, // # 查詢條件: 負(fù)標(biāo)志negation flag
NOIDX = 1 << 25 // # 查詢條件: 非索引標(biāo)志
}
查詢指定主鍵(如本例中的olid,效率最高)
var qrecords = TokyoTyrantService.GetColumns(pool, new string[]{"1", "2", "3"});
foreach (string key in qrecords.Keys)
{
var column = qrecords[key];
}
更新操作:
因?yàn)門C的TCT結(jié)構(gòu)沒有提供直接更新記錄中某一字段的功能,所以只能全部取出相關(guān)記錄的所有字段,然后再更新全部字段(這種做法的效率不高,但在MONGODB中是可以更新部分字段)。所以要組合使用查詢和創(chuàng)建操作中的語(yǔ)法,即選查出相應(yīng)記錄,然后再使用PutColumns方法更新該記錄,形式如下:
var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("olid", 1));
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...數(shù)據(jù)綁定操作
TokyoTyrantService.PutColumns(TTPool.GetInstance(), column["olid"], columns, true);//column["olid"]為主鍵,類似數(shù)據(jù)庫(kù)里的主鍵,以其為查詢條件,速度最快
}
刪除操作
該操作有兩種執(zhí)行方法,一種是選查詢出符合條件的記錄,然后再刪除(依次刪除),一種是直接給定要?jiǎng)h除的主鍵直接刪除(效率比前者高)。第一種(可以針對(duì)不用字段進(jìn)行查詢,并將相應(yīng)結(jié)果的主鍵做了刪除依據(jù))
var qrecords = TokyoTyrantService.QueryRecords(TTPool.GetInstance(), new Query().NumberEquals("userid", 1));
foreach (var k in qrecords.Keys)
{
var column = qrecords[k];
...數(shù)據(jù)綁定操作
TokyoTyrantService.Delete(TTPool.GetInstance(), column["olid"]);//column["olid"]為主鍵,類似數(shù)據(jù)庫(kù)里的主鍵
}
第二種(刪除olid為1或2或3或4的鍵值記錄,只能刪除以主鍵為條件的記錄):
TokyoTyrantService.DeleteMultiple(pool, new string[] { "1", "2", "3", "4" });
創(chuàng)建索引
TC中支持幾種類型的字段索引如下(經(jīng)常用的是數(shù)值型和字符型):
/// <summary>
/// 索引類型
/// </summary>
public enum IndexOption : int
{
LEXICAL = 0, // # 文本型索引
DECIMAL = 1, // # 數(shù)值型索引
TOKEN = 2, // # 標(biāo)記倒排索引.
QGRAM = 3, // #QGram倒排索引.
OPT = 9998, // # 9998, 對(duì)索引優(yōu)化
VOID = 9999, // # 9999, 移除索引.
KEEP = 1 << 24 // # 16777216, 保持已有索引.
}
比如在線表中經(jīng)常用的字段索引設(shè)置如下:
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則表示跳過(guò)多少條記錄(類似LINQ中的那個(gè)Skip方法)
Vanish(TcpClientIOPool pool);清空所有記錄
QueryRecordsCount(TcpClientIOPool pool, Query query)//查詢指定條件的記錄數(shù)
GetRecordCount(TcpClientIOPool pool)//返回當(dāng)前表中的記錄總數(shù)
GetDatabaseSize(TcpClientIOPool pool);//獲取數(shù)據(jù)庫(kù)(表)信息
IteratorNext(TcpClientIOPool pool)//一個(gè)迭代器,用于遍歷所有記錄
4.因?yàn)槠浼嫒軲emcached,所以提供方法支持(鍵/值對(duì))
Put(TcpClientIOPool pool, string key, string value, bool overwrite)//該操作方法將不像Put那樣獲取服務(wù)器端返回的信息
PutFast(TcpClientIOPool pool, string key, string value)//快速存儲(chǔ)鍵值對(duì)(不再獲取服務(wù)端返回信息). 如鍵值已存在則將被覆蓋
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, // # 排序類型: 表示按照文本型字段內(nèi)的文本內(nèi)容在字典中排列順序的升序
STRDESC = 1, // # 排序類型: 表示按照文本型字段內(nèi)的文本內(nèi)容在字典中排列順序的降序
NUMASC = 2, // # 排序類型: 表示按照數(shù)值大小的升序
NUMDESC = 3 // # 排序類型: 表示按照數(shù)值大小的降序
}
用法(如降序并取前16條記錄):
qrecords = TokyoTyrantService.QueryRecords(pool, new Query().OrderBy("userid", QueryOrder.NUMDESC).LimitTo(16, 0));
注意:盡量避免對(duì)大數(shù)據(jù)集(如100w條記錄)進(jìn)行排序,那樣耗時(shí)會(huì)很嚴(yán)重。所以盡量在OrderBy之前指定查詢條件,從而縮減查詢結(jié)果集的尺寸。
其它說(shuō)明:
TT的啟動(dòng)參數(shù)(這里以TCT類型為例):
注:網(wǎng)上有一些關(guān)于TC+TT與MONGODB,Redis的速度測(cè)試,所以這里我想有必要對(duì)TT的啟動(dòng)參數(shù)做一下介紹,因?yàn)檫@會(huì)關(guān)系到最終的測(cè)試結(jié)果。
因?yàn)閮烧叨际褂昧薓MAP模式,而TC+TT要使用MMAP,就要使用下面參數(shù):
xmsiz:指定了TCHDB的擴(kuò)展MMAP內(nèi)存大小,默認(rèn)值為 67108864,也就是64M,如果數(shù)據(jù)庫(kù)文件超過(guò)64M,則只有前部分會(huì)映射在內(nèi)存中,所以寫入性能會(huì)下降。
bnum: 指定了bucket array的數(shù)量。推薦設(shè)置bnum為預(yù)計(jì)存儲(chǔ)總記錄數(shù)的0.5~4倍,使key的哈希分布更均勻,減少在 bucket內(nèi)二分查找的時(shí)間復(fù)雜度。
比如有100w條記錄,這里可以使用下面命令行啟動(dòng)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)
當(dāng)然TTServer中針對(duì)不同的數(shù)據(jù)庫(kù)(TC中支持6種),都有相應(yīng)的參數(shù)進(jìn)行啟動(dòng)配置(有重復(fù)),這會(huì)導(dǎo)致的查詢和插入數(shù)據(jù)的結(jié)果上有很大的差異,更多的內(nèi)容可以參見這個(gè)鏈接。
下面我將自己對(duì)TC+TT(僅使用TCT文件類型,其它5種類型都比這個(gè)類型快許多)與MONGODB的測(cè)試結(jié)果做一下說(shuō)明:
機(jī)器是一個(gè)普遍臺(tái)式機(jī):1.5g內(nèi)存+1.5gCPU,64位的centos機(jī)器,150g硬盤。
mongodb (centos 64bit) :
插入1000000 條記錄,耗時(shí):250377毫秒
對(duì)1000000條記錄,查詢10000 次記錄,耗時(shí):8100毫秒 (偶爾出現(xiàn)7500毫秒) (查詢"_id"主鍵速度在6995毫秒上下)
對(duì)1000000條記錄,查詢100000 次記錄,耗時(shí):77101毫秒
ttcache(centos 64bit,使用上面的啟動(dòng)參數(shù)):
創(chuàng)建 1000000 條數(shù)據(jù),耗時(shí) 589472毫秒
對(duì)1000000條記錄,查詢 10000 次數(shù)據(jù),耗時(shí) 4843毫秒
對(duì)1000000條記錄,查詢 100000 次數(shù)據(jù),耗時(shí) 47903毫秒
注:查詢條件動(dòng)態(tài)變化,以模擬實(shí)際生產(chǎn)環(huán)境。
比較發(fā)現(xiàn)MONGODB插入速度要比TTCACHE快至少一倍(MONGODB在WINDOWS下也是如此),但10000次查詢速度會(huì)慢大約40%-50%。這里的查詢和插入操作都是每做一次操作就Connect一次服務(wù)器,操作結(jié)束時(shí)則將當(dāng)前鏈接放到鏈接池中,而不是開啟一個(gè)長(zhǎng)鏈接來(lái)做批量操作。其中TTSERVER所使用的客戶端分別是本文的這個(gè)工具, MONGODB則使用的是MongoDB.Driver。
下面是MSSQL數(shù)據(jù)庫(kù)操作結(jié)果:
批量創(chuàng)建 1000000 條數(shù)據(jù),耗時(shí) 9020196毫秒
批量查詢 10000 條數(shù)據(jù),耗時(shí) 106040毫秒
批量查詢 100000 條數(shù)據(jù),耗時(shí) 773867毫秒
我想進(jìn)行這類測(cè)試,還是不要使用什么WINDOWS(盡量MONGODB在WINDOW下插入數(shù)據(jù)的速度已很快)或其它操作系統(tǒng)。而應(yīng)該使用LINUX(盡量是64位)。當(dāng)然內(nèi)存要盡量的大,因?yàn)楸M管TC+TT已很省內(nèi)存(必定符合日本的國(guó)情,資源少還要多辦事),但如果要提升查詢和插入速度,還是建議4g以上的內(nèi)存做測(cè)
試。而MONGODB本來(lái)對(duì)內(nèi)存要求很高(包括CPU)。
因?yàn)閙ongodb的插入速度非常快,且在數(shù)據(jù)庫(kù)大量可以新建文件來(lái)存儲(chǔ)新的數(shù)據(jù)(不像TCT使用一個(gè)數(shù)據(jù)文件),所以在更大級(jí)別的數(shù)據(jù)量插入上依然性能穩(wěn)定??磥?lái)將它視為海量數(shù)據(jù)存儲(chǔ)的分布解決方案還是很有可行性的。當(dāng)然我目前正在考慮一個(gè)架構(gòu),就是將MongoDb和TC/TT組合起來(lái),實(shí)現(xiàn)讀寫分離(即將TC作為讀數(shù)據(jù)庫(kù)slavedb,并發(fā)性和查詢速度快),而將MongoDb作為寫數(shù)據(jù)庫(kù)masterdb(更新和插入速度快)。將分布式的MongoDb數(shù)據(jù)文件與前端TC中的文件依次對(duì)應(yīng)(使用C#代碼實(shí)現(xiàn)兩者之間的數(shù)據(jù)同步和邏輯調(diào)用),這樣融合兩者各自的優(yōu)勢(shì)的結(jié)果。當(dāng)然目前這只是想法,且離文本的內(nèi)容越來(lái)越遠(yuǎn)了,呵呵。
好了,今天的內(nèi)容就選到這里了。
下載鏈接:http://tokyotyrantclient.codeplex.com/
相關(guān)文章
.NET?Core使用Autofac容器的DI依賴注入,IOC控制反轉(zhuǎn)及AOP切面編程
本文詳細(xì)講解了.NET?Core使用Autofac容器的DI依賴注入,IOC控制反轉(zhuǎn)及AOP切面編程,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02C#中efcore-ShardingCore呈現(xiàn)“完美”分表
本文簡(jiǎn)單的介紹了efcore的分表,著重介紹了efcore下最完美的分表組件ShardingCore,可以幫助大家更好的學(xué)習(xí),感興趣的小伙伴可以參考一下2021-08-08關(guān)于多對(duì)多關(guān)系表無(wú)法更新與插入的問(wèn)題
這篇文章主要介紹了關(guān)于多對(duì)多關(guān)系表無(wú)法更新與插入的問(wèn)題 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07ASP.NET中操作數(shù)據(jù)庫(kù)的基本步驟分享
ASP.NET中操作數(shù)據(jù)庫(kù)的基本步驟分享,學(xué)習(xí)asp.net的朋友可以參考下。2011-10-10瀏覽器窗口滾動(dòng)加載數(shù)據(jù)采用異步形式從后臺(tái)加載數(shù)據(jù)
在滾動(dòng)條距頂部距離(頁(yè)面超出窗口的高度)時(shí)采用異步形式從后臺(tái)加載數(shù)據(jù),下面是具體的實(shí)現(xiàn),希望對(duì)大家有所幫助2014-01-01ASP.NET中的無(wú)刷新驗(yàn)證碼的開發(fā)(完整代碼)
ASP.NET中的無(wú)刷新驗(yàn)證碼的開發(fā)(完整代碼),需要的朋友可以參考下。2010-09-09ASP.NET通過(guò)Remoting service上傳文件
ASP.NET通過(guò)Remoting service上傳文件...2006-09-09