Rdis是内存数据库,在很多地方中,是作为关系型数据库的缓存来使用的,类似MmoryCach。 它支持存储的valu类型相对更多,包括string(字符串)、list(链表)、st(集合)、zst(sortdst--有序集合)和hash(哈希类型),rdis的出现,很大程度补偿了mmcachd这类ky/valu存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Prl,Objct-C,Python,Ruby,Erlang等客户端,使用很方便。 但Rdis也有持久化的功能,所以有的地方也会抛弃关系型数据库直接使用Rdis。 比如开发一个网站,有些频繁更新的数据,使用关系型数据库会不太合适。遇到这种情况,通常,可以自己做一层内存缓存或者使用其他的缓存工具。 自己做内存缓存的话,那么数据就被锁定在缓存它的进程中了,如果是分布式系统,就不好同步。而放在Rdis之类的内存数据库中的话,需要通过通信交互才能获得数据。 两种方式各有利弊,抛开这层不谈,直说使用Rdis的情况,假设有这样一款应用使用Rdis作为数据容器,作为一个基础应用的案例,一步步体验Rdis中的功能,最后封装出一个方便使用Rdis存取的类库,就是本文的内容了。 首先这个应用是数据相关的,也就没有了界面,可以当做是Wb后端,或者是网络游戏的游戏服务器部分。因为是只处理数据,所以不引入网络通信,只是假设有这样一个入口,能够接收符合接口的各种输入。 我就直接设定这是一个游戏,语言使用的是C#。 一使用VS创建解决方案包含两个项目,一个ConsolApplication名为RdisTst,一个类库RoyEngin,RdisTst添加项目引用RoyEngin。RoyEngin就是目标封装的Rdis的快速使用类库了。 二创建类Playr作为一个数据应用,第一步,是要能保存数据,最先的数据就是用户信息。所以我首先在RdisTst中创建一个类Playr。 非常简单,只有名字和战斗力两个属性。 然后在Main函数中varplayr=nwPlayr();并给属性赋值。 我的目的是要把这个playr实例保存到Rdis中去。 所以我需要这样一个方法,Sav(playr); 三添加Rdis快速访问类Roy在RoyEngin中创建classRoy,添加函数voidSavT(Tntity) 毫无疑问,这必须是一个泛型函数。感觉有点像是关系型数据库的ORM。 现在实现这个函数就需要连接Rdis,这里需要说明的是Rdis读写是单线程的,也就是说不管创建几个连接,数据到了Rdis都会依序读写,所以,Roy并不会创建多个对Rdis的连接。 四连接RdisC#对Rdis的对接已经有很多类库了,通过NuGt搜索Rdis,你将看到一堆如StackExchang.Rdis、SrvicStack.Rdis等等,这里我使用的是SrvicStack.Rdis的V3版本,因为之后的V4是要收费的,所以分出了一个分支叫NSrvicKit.Rdis,可以在GitHub上找到它的源码。 这里通过Nugt安装到RoyEngin中,点开引用,可以看到如下图: 增加了4个引用,值得一提的是其中的NSrvicKit.Txt其实是有JSON功能的。 有了这个类库,就可以给Roy添加一个私有字段RdisClint_rdis; 然后在构造函数中传入host,port,password和db四个参数,并对_rdis进行实例化了。 我重载了两个构造函数,做完这一步,可以到RdisTst项目的Main函数中添加对应实例化的代码 Royroy=nwRoy(“localhost”,); 那么Rdis的连接就告成了。 五实现SavT()函数其实对这个Sav的实现有一个很简单方式,就是直接Json序列化保存。 Rdis是一个键值对数据库,存取都是通过键值对匹配的方式,只要有一个Id作为Ky就能保存各种各样的数据。 上面提及过,NSrvicKit.Txt其实是带有JSON功能的,所以,如果只想使用JSON保存,可以直接使用_rdis.St(id,ntity);就搞定了。 这里使用的St是对应rdis的SET命令,对应的读取命令是Gt,也有对应的函数实现。 NSK.Rdis在St时会对ntity自动json序列化保存。 当然我不这么做,Rdis有一个结构叫做Hash,本身具有一个键,而保存的值类型是键值对的Hash表,非常适合做这种实例的浅序列化(只保存一层基础类型)。 那么Id怎么获取呢? Rdis有一个incr的命令,其实就是维护一个ky对应的incrmnt值。把这个ky设定为classNam,那么每一个class获取到的自增值肯定都隔离了。 在自增的下,设定实例的Id={ClassNam}:{incrId},那就可以保证每一个保存的class实例都是唯一的了。 有了Hash的Id,就可以对这个Hash进行HMSt命令了,这个命令是HSET的multi版,可以设置多个kyValu对。 通过typOf(T).GtProprtis()获得属性,然后通过ProprtyInfo.GtValu()获得属性值,进行设置保存。 由于这里获得的所有valu都是objct的,而HashSt需要的是byt[],这里就需要转换为字节数组。其实,因为只是浅保存,所以只支持几个基本数据类型,可以设定一个支持的基本类型枚举,然后分别ConvrtToByts,最后保存到一个byt[][]中,一口气进行HMSt。 六优化上面部分主要是反射的应用。当然反射是比较伤性能的,不过也有部分可以优化的地方,比如GtProprtis。 因为所有需要保存的类,在编译的时候肯定是可以确定了的,所以,在写代码的时候就可以加上一些标记,那么可以使用C#的Attribut特性。 我想过给Class打上一个特性来标识它是需要被存档的。但是之后,实际编写的时候发现还有一个Id这个大头的东西很重要。每一个需要存档的Class都需要一个Id,而这个Id在刚才被我设定成了string类型。那么仅仅是特性的约束就不太够了,干脆就做成基类好了。 所以,就有了这么一个基类RoyDataBas。只有继承自RoyDataBas的类型才能被保存。 那就给刚才的Sav泛型加上whrT:RoyDataBas的约束吧。 然后还可以写一个静态方法,在程序启动时执行,反射出所有RoyDataBas的子类和他们的Proprtis,保存到一个Dictionary里面,那么下次至少这部分不需要反射了。实际测试一下,做了这个之后,Sav的速度提高了一倍。 七忽略那些不需要保存的字段属性有时候为了方便,会在需要持久化的域类型中添加一些其他的属性或字段,而这些是不需要保存也不想被保存的,那么需要给他们打上标记。那就干脆给想保存的打上标记吧。 于是增加RoyValuAttribut这个特性,在程序初始化反射的时候忽略那些不带该特性的ProprtyInfo。现在的Playr就像这样: 八读取既然保存了,也就需要有相应的读取,同样的读取也是一个泛型方法: publicTGtT(id)whrT:RoyDataBas,nw(){ Tt=nwT(); //读取Rdis //找到初始化时保存好的元数据,用ProprtyInfo.StValu()方法进行赋值 } 这里读取Rdis需要用到的是Hash的另一个命令HMGt,全部读取之后遍历反射赋值,这个函数也就实现完毕了。 九测试终于到了最后一步,实验下刚才完成的两个函数吧。哦,对了,Rdis还没有吧。 可以在我最下方提供的链接里面找到rdis的文件夹,里面有两个cmd文件,双击其中的srvr.cmd就能运行一个rdis实例。 运行起rdis之后,就在RdisTst的Main函数中保存然后读取一万个不同的Playr试试看吧。 然后你还可以改一下Sav和Gt的实现,对比下用JSON保存和Hash保存的不同。 当然也可以改成sqlinsrt试试,看看速度差多少。 附上代码和Rdis工具的北京最好白癜风正规医院北京治疗白癜风哪里医院好
|