事件与TTL
正如介绍页面所述,SmartEDB 提供了通过生存时间(TTL)特性自动删除过时数据库对象的能力,并且能够管理数据库对象的新增、删除、更新和检查点事件。这些功能的 Java API 在以下各节中进行了说明。
Autoid
自动标识符是由 SmartEDB 运行时生成的保证唯一的值,可用于快速访问数据库中的唯一对象。通常,自动标识符引用字段将用于在类之间建立关系。有关详细信息,请参阅“类关系”页面。在 Java 类中,自动标识符通过 @Persistent 注解进行定义,例如:
@Persistent(autoid=true)
class Department
{
...
String name;
}
在 Java 应用程序中,当对象的新实例被“存储”时,即当包含对象实例化的事务被提交时,会生成 autoid 值。生成的 autoid 值由 Connection 方法 insert() 返回。例如:
Connection con = new Connection(db);
con.startTransaction(Database.TransactionType.ReadWrite);
Department dept = new Department();
dept.code = DD[i].code;
dept.name = DD[i].name;
long autoid = con.insert(dept);
con.commitTransaction();
对象的autoid可用于快速查找。例如,假设Employee类包含一个引用员工部门的autoid字段。
Employee类的定义可能如下所示:
@Persistent
class Employee
{
// Declare unique tree index by "name" field
@Indexable(Type = Database.IndexType.BTree, Unique = true)
public String name;
public long dept;
}
根据这个定义,Employee 对象会在引用字段 dept 中存储相应的 Department 自动标识符,那么查找员工所属部门的代码可能如下所示:
Employee emp;
// Find the Employee object of interest, then lookup its Department
con.startTransaction(Database.TransactionType.ReadOnly);
Cursor<Department> cursor = new Cursor<Department>(con);
Department d = cursor.find(emp.dept);
...
con.commitTransaction();
Time-To-Live
生存时间(TTL)机制能够根据 TTL 策略自动删除对象。支持两种 TTL 策略:最大计数和最大时间。前者设置对象数量阈值,后者设置对象年龄阈值。这两种策略可以同时为单个类设置。 TTL 策略通过类注解进行设置,就像设置其他 SmartEDB 属性一样。(请注意,在 Java 中,对象年龄阈值始终以微秒为单位指定。)
@TTL(maxCount=10, maxTime=5000000)
class A
{
// ...
}
请注意,maxTime 策略依赖于当前系统时间。更改系统时钟将会影响此策略。在分布式数据库中,这可能会产生重要影响,详情请见下文。
分布式环境中的副作用
在使用 SmartEDB 集群的分布式数据库中,当启用 maxTime 策略时,必须确保集群内各机器的时钟同步。以下是关键点:
TTL 时钟仅在事务发起方的提交阶段开始时验证。远程节点不验证时钟时间,无论其实际时钟如何,都会应用事务。只要事务在远程节点成功应用,通知将返回给发起方并完成提交。所有节点的内容保持一致。
如果远程节点的时钟超前于本地节点,可能会导致数据违反 TTL 要求。例如,记录按远程节点的时钟应已被删除,但仍然保留在该节点的数据库中。
即使对象所在节点的 TTL 尚未到期,如果其他节点的 TTL 已过期,该对象仍可能被移除。例如,节点 1 的时钟为下午 1 点,节点 2 的时钟为下午 2 点。事务在节点 1 发起并提交到两个节点,尽管记录在节点 2 上的 TTL 已过期。不久后,节点 2 发起新事务并根据其时钟移除了该记录,传播删除操作至整个集群,导致记录生命周期比预期短。
事件接口
“事件”属性定义触发应用程序通知的事件类型。Java 应用程序仅支持异步事件处理,每个事件由单独线程处理。事件线程调用 waitEvent() 方法等待事件发生,SmartEDB 释放线程后继续处理。处理程序线程与其他线程并行运行,直到完成处理并再次调用 waitEvent()。
为了避免未处理事件的风险,可以将事件处理委托给另一个线程,或维护一个未处理事件表。异步事件在事务提交后激活,多个对象的添加、删除或字段更新会同时触发所有相关处理程序。
private class EventThread extends Thread
{
public Database db;
public String event_name;
public EventThread(Database db, String EventName)
{
this.db = db;
this.event_name = EventName;
}
public void run()
{
Connection con = new Connection(db);
try
{
while (!exit)
{
con.con.waitEvent(event_name);
}
}
catch (DatabaseError x)
{
if (x.errorCode >= 50)
{
// Errors
}
else
{
// Normal return codes including MCO_S_EVENT_RELEASED
}
}
con.disconnect();
}
}
主应用程序线程将休眠若干毫秒,然后继续进行正常的数据库处理。在终止时,应用程序将调用 Connection 方法 releaseAllEvents(),然后停止事件处理线程。当事件被释放时,事件处理程序将捕获带有错误代码 MCO_S_EVENT_RELEASED 的异常。
有关进一步的实现细节,请参阅 SDK 示例 samples/java/events/asyncbasic。