最近对数据库进行了一番优化,增加耗时统计,以及优化现有的sql语句操作,减少读写耗时,这篇文章就从三个方面来讲述下相关的知识点,希望能对你有所帮助。
由于下面的优化操作需要一个操作环境,所以我说下当前我做了哪些准备工作:
class UserInfo { @JvmField var xuhao = 0 @JvmField var name = "" @JvmField var height = 0L @JvmField var weight = 0.0f @JvmField var married = false @JvmField var update_time = ""}
准备工作期间,为了下面的优化对比操作,我已经向该数据库的表中插入了几万条数据:
接下来我们就开始具体的实验工作吧。
操作数据库读写时一定要记得开启事务,事务的好处大家都知道,特别是某次sql指令执行的非常频繁的时候,开启事务能极大减少读写磁盘的次数,减少读写执行耗时。
为了验证上面的结论,我们分别通过开启事务和不开启事务,一次性向数据库执行2500次update操作,对比下这两个条件下具体的执行耗时。
代码如下:
fun updateByCommon() { for (i in 0..2500) { val update = "update user_info set update_time = 'qy2_$i' where age = $i" userDBHelper.writableDatabase.execSQL(update) }}
耗时统计使用的是kotlin官方库提供的扩展函数measureTimeMillis{}非常的方便:
mBinding.tsUpdate.setOnClickListener { measureTimeMillis { updateByTs() }.also { Log.i("SqliteActivity", "tsUpdate: time = $it") }}
接下来我们该场景下的耗时:
我总共执行了4次,差不多不开启事务一次性执行2500次update操作的耗时平均为7.2s左右。可以看到,相当的耗时。
实验环境和上面的相同,除了开启事务来执行2500次update更新操作,先看下实验代码:
fun updateByTs() { writeDb.beginTransaction() for (i in 0..2500) { val update = "update user_info set update_time = 'qy1_$i' where age = $i" userDBHelper.writableDatabase.execSQL(update) } writeDb.setTransactionSuccessful() writeDb.endTransaction()}
最终的执行耗时如下:
执行了4次,每次的平均耗时为5.2s左右,可以看到开启事务相比较于不开启事务执行,2500次的update操作耗时减少了大概2s。
这就的优化就结束了吗?不,我们还可以再减少几百ms,下面的SQLiteStatement会带给你更大的惊喜哈!!
这个SQLiteStatement是什么呢,其实平常我们调用SQLiteDatabase的insert、update等操作,最终都会将sql语句和对应的参数包装成一个SQLiteStatement对象,通过该对象执行最终的增删改查操作:
SQLiteDatabase.update()源码:
SQLiteDatabase.insert()源码:
从源码中都会看到,每一次insert、update操作都会创建一个SQLiteStatement对象,像我们第一小节做的实验,执行了2500次update操作,那就相当于频繁创建了2500个SQLiteStatement对象,想想都可怕。
所以这里的优化就是:在执行update操作之前,显示创建一个SQLiteStatement对象,通过?占位符和bindXXX系列方法实现一次创建、多次update的操作。
说的再多不如数说话,我们直接开启测试,先看下相关代码:
fun updateBySQLiteStatement() { val statement = writeDb.compileStatement("update user_info set update_time = ? where age = ?") writeDb.beginTransaction() for (i in 0..2500) { statement.clearBindings() statement.bindString(1, "qy2_$i") statement.bindLong(2, i.toLong()) statement.executeUpdateDelete() } writeDb.setTransactionSuccessful() writeDb.endTransaction()}
通过compileStatement()方法显示创建一个SQLiteStatement对象,其中clearBindings()是清理上次绑定的数据,接下来我们看下执行耗时:
平均耗时为5.1s,相比较开启事务读写,减少了100ms左右,不多但也是一个优化角度。
PS:显示创建SQLiteStatement的方式在我们项目中的优化很明显,至少减少了百分之50的耗时,不知道这里为啥就优化了那么一丢丢哎!!
如果我们要统计一个query查询操作的耗时,直接在query相关的sql语句执行前和执行后统计耗时是不准确的,因为的查询的耗时还要包括Cursor的读取。最正确的方法是在Cursor执行close()操作后再去统计查询耗时。
所以这里的优化操作是,在基类查询方法向外部暴漏Cursor对象时,对这个暴漏的Cursor借助静态代理的思想增加一层包装,这样就可以监听到Cursor的close()方法执行时机,在这个方法回调中插入我们最终的 耗时统计代码:
class CursorProxy( private val cursor: Cursor, private val mCloseCallback: () -> Unit) : Cursor by cursor { override fun close() { cursor.close() mCloseCallback() }}
这里我们借助kotlin的by类委托关键字实现了更为简单的静态代理,以及通过mCloseCallback这个函数类型传入外部的监听回调代码,比如这里的最终耗时统计代码。
public Cursor query(String sql) { //耗时统计开启 Cursor cursor = db.rawQuery(sql, null); return new CursorProxy(cursor, new Function0<Unit>() { @Override public Unit invoke() { //耗时统计结束代码 return null; } });}
本篇文章主要是讲解了sql操作的一些优化操作,减少执行耗时,同时也给出了准确统计query查询耗时的优化方案,本篇文章不难理解,希望能对你有所帮助。
作者:长安皈故里
链接:
https://juejin.cn/post/7173460152396300295
来源:稀土掘金
按 Esc 退出