一、URP 数据对象ID生成策略

1.1 数据拆分和合并原则

Openurp在部署阶段支持单个学校业务的独立部署,也支持几个学校在一起共同部署,甚至采用大集中的方式进行云化部署。因此在数据层面,要求各个业务部分的数据能够

  • 在拆分的时候具有明显的特征,可以很轻易的将共同部署的系统转为独立部署
  • 在合并的时候不能存在冲突,可以将独立部署的系统转换为共同部署或云化部署

合并和拆分的过程中不能更改任何数据。

由于在数据方面的考虑兼容性考虑,因此在数据的唯一标识上要不依赖于某个序列化的机制。而是在一定的协议基础上,各个学校产生的ID自然而然不会冲突。 协议如下:

  • 与学校有关的数据均在ID的尾部增加学校标识,该标识为5位数字。
  • 与学期有关的数据在学校标识基础上增加学期标识,例如每学期的教学任务,格式为seq+year[4]+term[1]+school_id[5],seq标识这一学期在教学任务上的序列。
  • 与学校无关的数据,例如人员信息,均使用独立的ID生成策略,如sha1,uuid等。

1.2 ID 类型

OpenURP系统支持数字和字符串两种ID类型。数字ID的生成策略如下:

  • code 支持将代码直接转换成数字作为ID,这种策略适用于基础代码、字典类数据
  • auto_increment 支持自增长
  • date 支持根据实体中的年份数据,生成ID。例如2014年的教学任务,保存时,jpa层会执行next_id(teach.lessons,2014) 会得到2014年teach.lessons表中Id的下一个值。

1.3 基本类型数据

OpenURP中的基本信息,校区、教学楼等内容都是基本类型数据,这些数据会在很长一段时间内使用,而不是特定学期内产生后,局限在特定学期内使用,具有跨学期的长久生命力。 但是其增长很慢。考虑到这两个特征,要求ID有如下特征:

  • 体现学校信息
  • 自身能够自增长

这些ID的形式可以为school_id[5]+seq,其中school_id为5为数字,这种ID基本为整形。 能够采用这种形式的数据包括:学校基础代码、专业、方向、教室、教学楼、校区、部门等

1.4 有时间特征的,固定增长速度数据

类似学年学期等数据每年会增长一些,但是不会很多。这种数据可以采用笃定长度的id。 例如学年学期为year[4]+term[1]+school_id[5],这种数据也采用整形表示。

1.5 有学期特征的快数据

有学期特征数据采用seq+year[4]+term[1]+school_id[5]的形式表示,这种形式允许每学期数据增长的空间为9亿。 改类数据采用长整形作为数据类型。例如:

  • 每学期的教学任务
  • 每学期的选课记录
  • 每学期的成绩

1.6 有时间特征的快数据

有时间特征数据采用seq+year[4]+school_id[5]。这种形式允许每学期数据增长的空间为90亿,也采用长整形作为数据类型。例如:

  • 每学年入学的学生
  • 每年入职的教工
  • 每年的考勤数据

1.7 无时间特征的其他数据

其他数据基本上采用seq+school_id[5]的形式表示,采用长整形作为数据类型。

二、数据分区策略

一旦数据量过大,考虑数据分区的策略。数据分区时可以在schema内部建立新表,然后建立合适的路由即可。 如果分区牵涉的数据表较多,可以建立具有时间阶梯的schema组进行处理。这里以教务为例。

如果以学年划分数据比较合适,教务的很多表都需要进行分表切分,因此我们建立如下策略

  • edu_teach2005_2010 表示2005~2006到2010~2011学年的数据
  • edu_teach2011_2015 表示2011~2012到2015~2016学年的数据

尝试以每五年做一次数据切分和归档处理。

数据库分表策略尽量按照数据库支持的技术进行构建。例如在Postgresql中支持表继承和检查约束(Check Constraint),可以使用函数触发器 实现增删改查的分表功能。以教务中的成绩为例:

course_grades 表示成绩表,teach2014.course_grades表示2014学年生成的成绩表,可以如此创建这张表:

CREATE TABLE teach2014.course_grades
(
  CONSTRAINT yearid CHECK (id >= 201400000000000001::bigint AND id <= 201500000000000000::bigint)
)
INHERITS (teach.course_grades)

然后在course_grades增加触发器: