【散300分】由 EXtremeDB 想到的扯淡“技术”——示例怎么自己编写内存数据库
【散300分】由 EXtremeDB 想到的扯淡“技术”——示例如何自己编写内存数据库.
我曾经写过一点短文来介绍如何编写ORM接口以及实现接口,甚至还包括触发器等。今天正好看到了介绍 EXtremeDB 等的文章,于是想到给我们死水一潭的 csdn 提供一份扯淡文章,借助我以前介绍的 ORM 接口来让我们的 .net 程序员感受一下如何开始实现一个不一样的DAL。
好了,先写出 DAL 的接口规范:
一个数据库操作就这几条功能,包括用来查询的Cast方法,用来更新数据的Save和Delete方法,用来指挥事务结束的Commit和Rollback方法!而Active操作是为了完成延迟加载关联对象使用的,如果你(像大多数人一样)从来没有想过从数据库读取对象要延迟加载一些关联对象,那么你可以从来不去实现它。
我们这个示例的目的是要自己打造一个“内存数据库”实现。我们的asp.net等服务程序中经常要跨程序,甚至跨线程地使用许多缓存数据,有时候我们不是简单地创建一个 private static List<object> 类型的变量来保存全局对象数据库,而是使用更像一个数据库的实现方式,或许更好。例如我就经常在我实际去用SQL Server、MySQL等来部署服务器程序之前,首先使用内存数据库(我使用一种比这里示例的更加复杂一点的实现)来快速测试。反正这些数据库 ORM 都是实现 IDomain 接口,切换它们只需要在配置文件中改一行 Assembly 定义就可以了。
说道内存数据库,可能大家已经很清楚这个 IDomain 其实实现起来很简单,不过就是把一堆数据放在一个 static 的变量中!是的,但不是全部。因为做为数据库,就要支持事务的并发控制。我们在下面就是用几行代码来实现一个 ReadCommitted 级别的控制,这个级别可以避免多个事务胡乱写数据,但是不阻止事务读数据。(如果你愿意,稍微修改以下的程序,很容易完成 Repeatable 级别的控制)
这个对象类的实现是:
非常非常简单的一段代码,所以所说这段代码“扯淡”——没有什么技术含量!其中很大一部分想法是用来实现事务的——可惜我懒得实现 Rollback 功能了。
下面写一段简单的测试:
我曾经写过一点短文来介绍如何编写ORM接口以及实现接口,甚至还包括触发器等。今天正好看到了介绍 EXtremeDB 等的文章,于是想到给我们死水一潭的 csdn 提供一份扯淡文章,借助我以前介绍的 ORM 接口来让我们的 .net 程序员感受一下如何开始实现一个不一样的DAL。
好了,先写出 DAL 的接口规范:
- C# code
using System; using System.Linq; namespace MyName { public interface IDomain : IDisposable { IQueryable<T> Cast<T>() where T : class; void Activate<T>(T obj); void Save<T>(T obj); void Delete<T>(T obj); void Commit(); void Rollback(); } }
一个数据库操作就这几条功能,包括用来查询的Cast方法,用来更新数据的Save和Delete方法,用来指挥事务结束的Commit和Rollback方法!而Active操作是为了完成延迟加载关联对象使用的,如果你(像大多数人一样)从来没有想过从数据库读取对象要延迟加载一些关联对象,那么你可以从来不去实现它。
我们这个示例的目的是要自己打造一个“内存数据库”实现。我们的asp.net等服务程序中经常要跨程序,甚至跨线程地使用许多缓存数据,有时候我们不是简单地创建一个 private static List<object> 类型的变量来保存全局对象数据库,而是使用更像一个数据库的实现方式,或许更好。例如我就经常在我实际去用SQL Server、MySQL等来部署服务器程序之前,首先使用内存数据库(我使用一种比这里示例的更加复杂一点的实现)来快速测试。反正这些数据库 ORM 都是实现 IDomain 接口,切换它们只需要在配置文件中改一行 Assembly 定义就可以了。
说道内存数据库,可能大家已经很清楚这个 IDomain 其实实现起来很简单,不过就是把一堆数据放在一个 static 的变量中!是的,但不是全部。因为做为数据库,就要支持事务的并发控制。我们在下面就是用几行代码来实现一个 ReadCommitted 级别的控制,这个级别可以避免多个事务胡乱写数据,但是不阻止事务读数据。(如果你愿意,稍微修改以下的程序,很容易完成 Repeatable 级别的控制)
这个对象类的实现是:
- C# code
public class XiaoHuiDomain : IDomain { private static Dictionary<int, object> _datas = new Dictionary<int, object>(); public IQueryable<T> Cast<T>() where T : class { return _datas.Values.OfType<T>().AsQueryable(); } public void Activate<T>(T obj) { } private Dictionary<int, object> Locked = new Dictionary<int, object>(); public void Save<T>(T obj) { if (!Monitor.TryEnter(obj, 20000)) throw new TimeoutException(); var key = obj.GetHashCode(); if (!Locked.ContainsKey(key)) Locked.Add(key, obj); if (!_datas.ContainsKey(key)) _datas.Add(key, obj); } public void Delete<T>(T obj) { _datas.Remove(obj.GetHashCode()); } public void Commit() { } public void Rollback() { throw new NotImplementedException(); } public void Dispose() { foreach (var x in Locked) Monitor.Exit(x.Value); } }
非常非常简单的一段代码,所以所说这段代码“扯淡”——没有什么技术含量!其中很大一部分想法是用来实现事务的——可惜我懒得实现 Rollback 功能了。
下面写一段简单的测试:
- C# code
class Program { static void Main(string[] args) { var datas = new List<UU>(); var n = 50; var value = Encoding.Default.GetBytes("1234567890123456"); Debug.Assert(value.Length == 16); { var tm = new Stopwatch(); tm.Start(); for (var x = 0; x < n; x++) { for (var y = 0; y < 10000; y++) datas.Add(new UU { dt = value }); } tm.Stop(); Console.WriteLine("内存{0}个对象共用时{1},平均{2}条记录/秒,{3}微妙/条。", n * 10000, tm.Elapsed, (int)((n * 10000) / tm.Elapsed.TotalSeconds), tm.ElapsedMilliseconds / 10 / n); } using (var dd = new XiaoHuiDomain()) { var tm = new Stopwatch(); tm.Start(); for (var x = 0; x < n; x++) { for (var y = 0; y < 10000; y++) dd.Save(new UU { dt = value }); } dd.Commit(); tm.Stop(); Console.WriteLine("写入{0}个对象共用时{1},平均{2}条记录/秒,{3}微妙/条。", n * 10000, tm.Elapsed, (int)((n * 10000) / tm.Elapsed.TotalSeconds), tm.ElapsedMilliseconds / 10 / n); } using (var dd = new SqlDomain()) { var tm = new Stopwatch(); tm.Start(); for (var x = 0; x < n; x++) { for (var y = 0; y < 10000; y++) dd.Save(new UU { dt = value }); } dd.Commit(); tm.Stop(); Console.WriteLine("写入{0}个对象共用时{1},平均{2}条记录/秒,{3}微妙/条。", n * 10000, tm.Elapsed, (int)((n * 10000) / tm.Elapsed.TotalSeconds), tm.ElapsedMilliseconds / 10 / n); } using (var dd = new MySQLDomain()) { var cn = dd.Cast<UU>().Count(); UU x; var tm = new Stopwatch(); tm.Start(); dd.Cast<UU>().ForEach(u => { x = u; }); tm.Stop(); Console.WriteLine("读取{0}个对象共用时{1},平均{2}条记录/秒,{3}微妙/条。", n, tm.Elapsed, (int)(cn / tm.Elapsed.TotalSeconds), tm.ElapsedMilliseconds * 1000 / cn); } Console.ReadKey(); } } class UU { public byte[] dt { get; set; } }