移动端orm框架性能测评

发表于:2019-7-26 13:41

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:温智宁    来源:思否

  flutter_orm_plugin 发布以来,不少团队试用了,我发现大家对这类数据库相关的库,第一反应就是性能如何,之前确实没做太多行业对比,最近觉得还是有必要做一下性能测试,给大家一个交代的。
  在ios端,业界比较常用的orm框架应该是苹果官方推出的coredata,还有就是realm了。在android端orm框架我挑了三个比较常用的,greendao,realm和activeandroid。我会用flutter_orm_plugin跟上面提到的ios和android端orm框架做对比。 下面我会分别给出测试用例,测试代码,还有最终数据比较的结果。
  测试用例
  测试用例我列了以下这些
  10000次插入数据
  使用批量接口10000次插入数据
  10000次读取数据
  10000次修改数据
  使用批量接口10000次修改数据
  10000次删除数据
  使用批量接口10000次删除数据
  为什么会有普通插入数据和使用批量接口插入数据的区别,大部分orm框架都会对批量操作有一定的优化,所以需要对批量操作进行测试,但是在平时使用,不一定都能用上批量接口(例如多次数据操作不在同一代码块,或者在不同的模块中都要操作数据),所以我们会分别对普通操作和批量操作进行测试。
  android 测试代码
  首先我们给出flutter_orm_plugin 的测试代码,由于不想因为flutter和原生channel通讯产生误差,我们直接用Luakit来写lua代码做测试(greendao、realm、activeandroid、coredata都不涉及flutter和原生channel通讯),flutter_orm_plugin其实底层就是luakit的orm框架,这个不影响测试准确性。
  循环插入
  Luakit定义orm模型结构并做10000次插入,下面的代码是ios和android通用的。
   local Student = {
  __dbname__ = "test.db",
  __tablename__ = "Student",
  studentId = {"TextField",{primary_key = true}},
  name = {"TextField",{}},
  claName = {"TextField",{}},
  teacherName = {"TextField",{}},
  score = {"RealField",{}},
  }
  local params = {
  name = "Student",
  args = Student,
  }
  Table.addTableInfo(params,function ()
  local studentTable = Table("Student”)
  for i=1,10000 do
  local s = {
  studentId = "studentId"..i,
  name = "name"..i,
  claName = "claName"..i,
  teacherName = "teacherName"..i,
  score = 90,
  }
  studentTable(s):save()
  end
  end)
  activeandroid定义orm模型结构并做10000次插入
   @Table(name = "Students")
  public class Student extends Base {
  @Column(name = "studentId")
  public String studentId;
  @Column(name = "name")
  public String name;
  @Column(name = "claName")
  public String claName;
  @Column(name = "teacherName")
  public String teacherName;
  @Column(name = "score")
  public float score;
  public Student() {
  super();
  }
  @Override
  public String toString() {
  return this.studentId;
  }
  }
  for (int i=0 ; i<10000 ;i++) {
  ActiveAndroid.beginTransaction();
  Student s = new Student();
  s.studentId = "studentId"+i;
  s.name = "name"+i;
  s.teacherName = "teacherName"+i;
  s.claName = "claName"+i;
  s.score = 90;
  s.save();
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  }
  realm android 定义orm模型结构并做10000次插入
   public class StudentRealm extends RealmObject {
  @PrimaryKey
  private String studentId;
  @Required
  private String name;
  @Required
  private String teacherName;
  @Required
  private String claName;
  private float score;
  public String getName() {
  return name;
  }
  public void setName(String name) {
  this.name = name;
  }
  public String getClaName() {
  return claName;
  }
  public void setClaName(String ClaName) {
  this.claName = claName;
  }
  public String getTeacherName() {
  return teacherName;
  }
  public void setTeacherName(String teacherName) {
  this.teacherName = teacherName;
  }
  public String getStudentId() {
  return studentId;
  }
  public void setStudentId(String id) {
  this.studentId = id;
  }
  public float getScore() {
  return score;
  }
  public void setScore(float score) {
  this.score = score;
  }
  }
  for (int i=0 ; i<10000 ;i++) {
  realm.beginTransaction();
  StudentRealm realmStudent = realm.createObject(StudentRealm.class,"studentId"+i);
  realmStudent.setName("name"+i);
  realmStudent.setTeacherName("setTeacherName"+i);
  realmStudent.setClaName("setClaName"+i);
  realmStudent.setScore(90);
  realm.commitTransaction();
  }
  GreenDao定义orm模型结构并做10000次插入
   @Entity()
  public class Student {
  @Id
  private String studentId;
  @NotNull
  private String name;
  private String claName;
  private String teacherName;
  private float score;
  @Generated(hash = 1491230551)
  public Student(String studentId, @NotNull String name, String claName, String teacherName,
  float score) {
  this.studentId = studentId;
  this.name = name;
  this.claName = claName;
  this.teacherName = teacherName;
  this.score = score;
  }
  @Generated(hash = 1556870573)
  public Student() {
  }
  public String getStudentId() {
  return studentId;
  }
  public void setStudentId(String studentId) {
  this.studentId = studentId;
  }
  @NotNull
  public String getName() {
  return name;
  }
  /** Not-null value; ensure this value is available before it is saved to the database. */
  public void setName(@NotNull String name) {
  this.name = name;
  }
  public String getClaName() {
  return claName;
  }
  public void setClaName(String claName) {
  this.claName = claName;
  }
  public String getTeacherName() {
  return teacherName;
  }
  public void setTeacherName(String teacherName) {
  this.teacherName = teacherName;
  }
  public float getScore() {
  return score;
  }
  public void setScore(float score) {
  this.score = score;
  }
  }
  DaoSession daoSession = ((App) getApplication()).getDaoSession();
  StudentDao sd = daoSession.getStudentDao();
  for (int i = 0; i < 10000; i++) {
  Student s = new Student();
  s.setStudentId("StudentId"+i);
  s.setClaName("getClaName"+i);
  s.setScore(90);
  s.setName("name"+i);
  s.setTeacherName("tn"+i);
  sd.insertOrReplace(s);
  }
  批量插入
  Luakit没有提供批量插入接口。
  active android批量插入10000条数据。
   ActiveAndroid.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  Student s = new Student();
  s.studentId = "studentId"+i;
  s.name = "name"+i;
  s.teacherName = "teacherName"+i;
  s.claName = "claName"+i;
  s.score = 90;
  s.save();
  }
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  realm android批量插入10000条数据。
   Realm realm = Realm.getDefaultInstance();
  realm.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  StudentRealm realmStudent = realm.createObject(StudentRealm.class,"studentId"+i);
  realmStudent.setName("name"+i);
  realmStudent.setTeacherName("setTeacherName"+i);
  realmStudent.setClaName("setClaName"+i);
  realmStudent.setScore(90);
  }
  realm.commitTransaction();
  GreenDao批量插入10000条数据
   DaoSession daoSession = ((App) getApplication()).getDaoSession();
  StudentDao sd = daoSession.getStudentDao();
  ArrayList<Student> ss = new ArrayList<Student>();
  for (int i = 0; i < 10000; i++) {
  Student s = new Student();
  s.setStudentId("StudentId"+i);
  s.setClaName("getClaName"+i);
  s.setScore(90);
  s.setName("name"+i);
  s.setTeacherName("tn"+i);
  ss.add(s);
  }
  sd.insertOrReplaceInTx(ss);
  数据查询
  Luakit做10000次查询,下面的代码是ios和android通用的。
   local studentTable = Table("Student")
  for i=1,10000 do
  local result = studentTable.get:where({"studentId"..i},"studentId = ?"):all()
  end
  active android做10000次查询。
   for (int i=0 ; i<10000 ;i++) {
  List<Student> student = new Select()
  .from(Student.class)
  .where("studentId = ?", "studentId"+i)
  .execute();
  }
  realm android 做10000次查询。
   for (int i=0 ; i<10000 ;i++) {
  RealmResults<StudentRealm> students = realm.where(StudentRealm.class).equalTo("studentId", "studentId"+i).findAll();
  List<StudentRealm> list = realm.copyFromRealm(students);
  }
  GreenDao 做10000次查询
   DaoSession daoSession = ((App) getApplication()).getDaoSession();
  StudentDao sd = daoSession.getStudentDao();
  for (int i = 0; i < 10000; i++) {
  List<Student> s = sd.queryBuilder()
  .where(StudentDao.Properties.StudentId.eq("StudentId"+i))
  .list();
  }
  循环更新
  Luakit做10000次更新。
  local studentTable = Table("Student")
  for i=1,10000 do
  local result = studentTable.get:where({"studentId"..i},"studentId = ?"):update({name = "name2”})
  end
  active android做10000次更新。
   for (int i=0 ; i<10000 ;i++) {
  ActiveAndroid.beginTransaction();
  Update update = new Update(Student.class);
  update.set("name = ?","name2")
  .where("studentId = ?", "studentId"+i)
  .execute();
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  }
  realm android做10000次更新。
   for (int i=0 ; i<10000 ;i++) {
  realm.beginTransaction();
  StudentRealm student = realm.where(StudentRealm.class).equalTo("studentId", "studentId"+i).findFirst();
  student.setClaName("ClaName"+(i+1));
  realm.copyToRealmOrUpdate(student);
  realm.commitTransaction();
  }
  GreenDao做10000次更新。
   for (int i = 0; i < 10000; i++) {
  List<Student> s = sd.queryBuilder()
  .where(StudentDao.Properties.StudentId.eq("StudentId"+i))
  .list();
  s.get(0).setName("name2");
  sd.update(s.get(0));
  }
  批量更新
  Luakit没有批量更新接口。
  active android批量更新10000条数据。
   ActiveAndroid.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  Update update = new Update(Student.class);
  update.set("name = ?","name2")
  .where("studentId = ?", "studentId"+i)
  .execute();
  }
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  realm android批量更新10000条数据。
   realm.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  StudentRealm student = realm.where(StudentRealm.class).equalTo("studentId", "studentId"+i).findFirst();
  student.setClaName("ClaName"+(i+1));
  realm.copyToRealmOrUpdate(student);
  }
  realm.commitTransaction();
  GreenDao批量更新10000条数据
   ArrayList<Student> ss = new ArrayList<Student>();
  for (int i = 0; i < 10000; i++) {
  List<Student> s = sd.queryBuilder()
  .where(StudentDao.Properties.StudentId.eq("StudentId"+i))
  .list();
  s.get(0).setName("name2");
  ss.add(s.get(0));
  }
  sd.updateInTx(ss);
  循环删除
  Luakit做10000次删除操作。
   ocal studentTable = Table("Student")
  for i=1,10000 do
  studentTable.get:where({"studentId"..i},"studentId = ?"):delete()
  end
  active android做10000次删除操作。
   for (int i=0 ; i<10000 ;i++) {
  ActiveAndroid.beginTransaction();
  new Delete().from(Student.class).where("studentId = ?", "studentId"+i).execute();
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  }
  realm android做10000次删除操作。
   for (int i=0 ; i<10000 ;i++) {
  realm.beginTransaction();
  StudentRealm student = realm.where(StudentRealm.class).equalTo("studentId", "studentId"+i).findFirst();
  student.deleteFromRealm();
  realm.commitTransaction();
  }
  GreenDao做10000次删除操作。
   for (int i = 0; i < 10000; i++) {
  List<Student> s = sd.queryBuilder()
  .where(StudentDao.Properties.StudentId.eq("StudentId"+i))
  .list();
  s.get(0).setName("name2");
  sd.delete(s.get(0));
  }
  批量删除
  Luakit没有批量删除接口。
  active android批量删除10000条数据。
   ActiveAndroid.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  new Delete().from(Student.class).where("studentId = ?", "studentId"+i).execute();
  }
  ActiveAndroid.setTransactionSuccessful();
  ActiveAndroid.endTransaction();
  realm android批量删除10000条数据。
   realm.beginTransaction();
  for (int i=0 ; i<10000 ;i++) {
  StudentRealm student = realm.where(StudentRealm.class).equalTo("studentId", "studentId"+i).findFirst();
  student.deleteFromRealm();
  }
  realm.commitTransaction();
  GreenDao批量删除10000条数据。
   ArrayList<Student> ss = new ArrayList<Student>();
  for (int i = 0; i < 10000; i++) {
  List<Student> s = sd.queryBuilder()
  .where(StudentDao.Properties.StudentId.eq("StudentId"+i))
  .list();
  ss.add(s.get(0));
  }
  sd.deleteInTx(ss);
  android 测试结果及分析
  下面给出测试结果,表格中所有数据的单位是秒,即做10000次操作需要的秒数。
 

  可以看到,active android各项性能都一般。
  在使用批量接口的情况下GreenDao和Realm的性能比较好。
  在使用批量接口的情况下Realm的性能尤其好,批量插入、查询、批量更改、批量删除都是Realm的性能最好,但是Realm的非批量接口性能较差,所有可以这样总结,如果代码高内聚,可以把数据操作代码入口都统一使用,Realm性能是最好的,但这对代码质量、模块设计有要求,当操作数据的代码到处都有,不能使用批量接口时,Realm的性能是不好的。
  Luakit没有提供批量接口,但从图中可以看出,Luakit的各项性能指标都是比较好的,而且对代码没有要求,即使操作数据的代码不内聚,也不会对性能有影响。
  
      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号