【散300分】由 EXtremeDB 想到的扯淡“技术”——示例怎么自己编写内存数据库

【散300分】由 EXtremeDB 想到的扯淡“技术”——示例如何自己编写内存数据库.
我曾经写过一点短文来介绍如何编写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; }
}