Cachalot是一个高性能的分布式数据库,适用于.NET应用程序。它具有分布式特性,能够随着节点数量的增加而线性扩展。在单个节点上,即使是在中等配置的系统上,也可以实现每秒持久化插入五万个对象。Cachalot提供了强大的LINQ支持和管理员控制台。此外,它还可以作为具有独特特性的事务性分布式缓存使用。
Cachalot的完全开源代码可以在以下地址找到:
https://github.com/usinesoft/Cachalot
预编译的二进制文件和完整文档可以在以下地址找到:
https://github.com/usinesoft/Cachalot/releases/latest
客户端代码以NuGet包的形式提供,可以在nuget.org上找到。要安装,请使用以下命令:
Install-Package Cachalot.Client
在详细介绍之前,让先展示一些代码。从一个允许个人之间出租房屋的玩具网站开始。一个简单的房地产属性描述如下:
public class Home
{
public string CountryCode { get; set; }
public string Town { get; set; }
public string Adress { get; set; }
public string Owner { get; set; }
public string OwnerEmail { get; set; }
public string OwnerPhone { get; set; }
public int Rooms { get; set; }
public int Bathrooms { get; set; }
public int PriceInEuros { get; set; }
}
业务对象的第一个要求是必须有一个主键。在这种情况下没有“自然”的主键,所以将添加一个数值ID。
public class Home
{
[PrimaryKey(KeyDataType.IntKey)]
public int Id { get; set; }
}
现在,该对象可以存储在数据库中了。第一步是实例化一个Connector,它需要客户端配置。稍后会有更多关于配置的信息,但目前它需要包含集群中的服务器列表。首先,只运行一个本地服务器。配置通常从外部文件读取。现在,手动构建它。
var config = new ClientConfig
{
Servers = {
new ServerConfig
{
Host = "localhost",
Port = 4848
}
}
};
using (var connector = new Cachalot.Linq.Connector(config))
{
var homes = connector.DataSource();
// 其余的代码在这里
}
在将对象存储到数据库之前的最后一步。需要为主键生成一个唯一值。一次调用可以生成多个唯一值。与其他数据库不同,不需要显式创建一个唯一值生成器。使用新生成器名称的第一个调用将自动创建它。
var ids = connector.GenerateUniqueIds("home_id", 1);
var home = new Home
{
Id = ids[0],
Adress = "14 rue du chien qui fume",
Bathrooms = 1,
CountryCode = "FR",
PriceInEuros = 125,
Rooms = 2,
Town = "Paris"
};
homes.Put(home);
现在,第一个对象已安全地存储在数据库中。目前,只能通过主键检索它。这可以通过两种等效的方式完成。
var reloaded = homes[home.Id];
// 或者使用LINQ表达式。
var reloaded = homes.First(h => h.Id == home.Id);
第一个更快,因为不需要解析表达式树。在大多数关系数据库中,使用两个不同的操作:INSERT和UPDATE。在Cachalot DB中,只暴露了一个操作:PUT。它将插入新项目(新的主键)并将更新现有项目。
可能对现代数据库有更高的期望,而不仅仅是通过主键存储和检索对象。是对的。
需要了解索引的三个特征。
在Cachalot DB中,.NET属性需要可以转换为Int64或string才能被索引。使用整数类型可以使搜索稍微快一些。
自动转换为Int64提供给:所有数值类型、DateTime和DateTimeOffset、枚举类型、布尔值。转换为string是通过调用属性值的ToString()完成的。
三种类型的索引属性可用:主键(唯一强制的)、唯一键:可以在类型上定义零个或多个、索引键:可以在类型上定义零个或多个。
在任何索引上,可以应用等式运算符。如果索引声明为“有序”的,所有比较运算符都可以同样应用:<、<=、>、>=。这种类型的索引对于大多数现代系统至关重要,但要注意它有成本,因为有序索引必须始终保持排序。
大量插入/更新操作(DataStore.PutMany方法)经过良好优化。达到阈值后(默认为50项),操作将被视为“批量插入”。有序索引仅在最后排序一次。
public class Home
{
[PrimaryKey(KeyDataType.IntKey)]
public int Id { get; set; }
[Index(KeyDataType.StringKey)]
public string CountryCode { get; set; }
[Index(KeyDataType.StringKey)]
public string Town { get; set; }
public string Adress { get; set; }
public string Owner { get; set; }
public string OwnerEmail { get; set; }
public string OwnerPhone { get; set; }
[Index(KeyDataType.IntKey, ordered: true)]
public int Rooms { get; set; }
[Index(KeyDataType.IntKey)]
public int Bathrooms { get; set; }
[Index(KeyDataType.IntKey, ordered: true)]
public decimal PriceInEuros { get; set; }
}
使用这样索引的对象,现在可以执行一些有用的查询:
var results = homes.Where(p => p.PriceInEuros <= 200 && p.Rooms > 1 && p.Town == "Paris").Take(10);
当然,查询是在服务器端执行的,包括take运算符。通过网络发送给客户端的对象最多为十个。
“Contains”扩展方法也受到支持。
var towns = new[] { "Paris", "Nice" };
var one = homes.First(p => p.PriceInEuros < 150 && towns.Contains(p.Town));
这相当于SQL:
SELECT * from HOME where PriceInEuros < 150 and Town IN ("Paris", "Nice")
“Contains”扩展的另一个用途在传统SQL中没有等效项,将在下一节中解释。
让丰富业务对象。拥有每个房屋的可用日期列表将是有用的。添加这个新属性可以启用一些有趣的功能。这是一个集合属性,可以像标量属性一样进行索引。
[Index(KeyDataType.IntKey)]
public List AvailableDates { get; set; } = new List();
希望能够搜索特定日期可用的房屋。
var availableNextWeek = homes.Where(p => p.Town == "Paris" && p.AvailableDates.Contains(DateTime.Today.AddDays(7))).ToList();
这在经典SQL数据库中没有直接等效项。它方便地取代了大多数经典JOIN运算符的用途。
可能需要动态创建查询。例如,在搜索屏幕中,添加标准来限制结果。这可以通过链接Where方法来完成。
var query = homes.Where(p => p.Town == "Paris");
query = query.Where(p => p.PriceInEuros < 200);
query = query.Where(p => p.Rooms > 1);
query = query.Where(p => p.AvailableDates.Contains(DateTime.Today));
var result = query.ToList();
这相当于一个查询,其中所有标准都使用&&(AND)运算符连接。