美文网首页Android潜修者Android 开发经验集Android面试
Android面试一天一题(Day 18:SQLite数据库)

Android面试一天一题(Day 18:SQLite数据库)

作者: goeasyway | 来源:发表于2016-07-11 20:06 被阅读5197次

鹭岛厦门是个很美丽的海滨城市,给我的感觉很舒适和悠闲,据说政府对到那工作的高新技术人才第一年有10万元的奖励,因为这个原因我很有兴趣的参加了一个厦门公司的面试。他们主要是研发VOIP方面的技术,对手机应用的性能优化和音频算法有较高的要求。

他们招人的薪资半年内涨了30万元,都一直都没有招到合适的人。为什么呢?因为他们要求技术好的同时,英语也要好。什么才叫好呢?就是可以用流利的英语和国外的团队无障碍交流。

这就为难很多程序员了。这里就不再讲英语的励志故事了,我们回到技术面试上。厦门这家公司对技术的要求还是比较高,问了很多对Android机制的理解问题,为什么面试官很在意对机制的理解呢?因为实际的项目中很多性能问题都是由于缺乏对Android运行机制的正确理解的程序员引发的。除了机制问题,现在印象比较深的就是关于SQLite数据库操作的性能优化问题。

面试题:如何对SQLite数据库中进行大量的数据插入?

Android系统内置了SQLite数据库,并且提供了一整套的API用于对数据库进行增删改查操作。SQLite是一个轻量的、跨平台的、开源的数据库引擎。SQLite每个数据库都是以单个文件(.db)的形式存在,这些数据都是以B-Tree的数据结构形式存储在磁盘上。

使用SQLiteDatabase的insert,delete等方法或者execSQL方法默认都开启了事务,如果操作的顺利完成才会更新.db数据库。事务的实现是依赖于名为rollback journal文件,借助这个临时文件来完成原子操作和回滚功能。

大家可以在/data/data/<packageName>/databases/目录下看到一个和数据库同名的.db-journal文件。

SQLite想要执行操作,需要将程序中的SQL语句编译成对应的SQLiteStatement,比如" select * from table1 ",每执行一次都需要将这个String类型的SQL语句转换成SQLiteStatement。如下insert的操作最终都是将ContentValues转成SQLiteStatementi:

    public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
            // 省略部份代码
            SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
            try {
                return statement.executeInsert();
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }

对于批量处理插入或者更新的操作,我们可以重用SQLiteStatement,使用SQLiteDatabase的beginTransaction()方法开启一个事务,样例如下:

    try
    {
        sqLiteDatabase.beginTransaction();
        SQLiteStatement stat = sqLiteDatabase.compileStatement(insertSQL);

        // 插入10000次
        for (int i = 0; i < 10000; i++)
        {
            stat.bindLong(1, 123456);
            stat.bindString(2, "test");
            stat.executeInsert();
        }
        sqLiteDatabase.setTransactionSuccessful();
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
    finally
    {
        // 结束
        sqLiteDatabase.endTransaction();
        sqLiteDatabase.close();
    }

我在华为Nexus 6P上对常见的几种做法做了一下测试。

直接使用SQL语句进行插入
直接使用SQL语句插入,添加事务
使用ContentValues方式,添加事务
使用SQLiteStatement方式,添加事务

结果如下图:


从数据上看,第四种方式使用SQLiteStatement最快,不过只要添加了事务(或者说只需要一个事务,不是每条插入都使用事务),后三种方式的差别并不大。所以针过这个题目的插入的优化可以通过“SQLiteStatement+事务”的方式显著提高效率。

查询方面的优化一般可以通过建立索引。建立索引会对插入和更新的操作性能产生影响,使用索引需要考虑实际情况进行利弊权衡,对于查询操作量级较大,业务对要求查询要求较高的,还是推荐使用索引。所以这会有一个取舍问题,看你的项目是查询频繁还是插入和修改频繁。当然还有一些小的优化细节,如果面试官问到也可以说几点(如limit)。

线程问题

SQLite的同步锁精确到数据库级,粒度比较大,不像别的数据库有表锁,行锁。同一个时间只允许一个连接进行写入操作。

如果有大量的数据处理,那么肯定不合适于在UI线程去操作,这时就要考虑多线程的问题了。我们如果开一个工作线程去操作SQLite数据库,如批量地插入可能需要30秒钟,而这个时间UI线程也要从数据库读取一下数据展示给用户,那么这个时候UI线程能读取到这个数据库吗?大家可以思考一下这个问题。

我们常常在多线程中只使用一个SQLiteDatabase引用,在用SQLiteDataBase.close()的时需要注意调是否还有别的线程在使用这个实例。如果一个线程操作完成后就直接close了,别一个正在使用这个数据库的线程就会异常。所以有些人会直接把SQLiteDatabase的实例放在Application中,让它们的生命周期一致。也有的做法是写一个计数器,当计数器为0时才真正关闭数据库。

使用ORM的问题

目前网上有很多开源的ORM(对象关系数据映射)框架,如greenDAO、ormlite等等。在使用这些框架有必要很了解一下它们的利弊,特别是一些使用反射的框架,对性能的影响会比较大。有些框架在多线程同步方面也会产生一些问题,所以使用时要有所顾虑。

Realm 是最近兴起的一个专注于移动设备数据库的库,其核心是使用C++编写,号称很多时候数据的存取速度比SQLite要快很多。不过在我的一些项目中,发现它读取并不比SQLite快。

小结

在实践中我们总结出一条守则:“不要用Helloworld来测试自己的框架(或代码),要测就要用真实的数据和环境。”

特别是针对数据库方面,如果只用几条简单的数据进行测式,那么你会很容易傲娇和满足,而忽视了很多问题。没有经过真实数据(或大量数据)测试之前,不要对自己的代码太过自信。

Even 原创
简书账号Goeasyway:http://www.jianshu.com/users/f9fbc7a39b36/latest_articles
转载请注明出处。

相关文章

  • Android面试一天一题-goeasyway

    以下文章作者:goeasyway Android面试一天一题(1 Day) Android面试一天一题(2 Day...

  • MS(1):Android之架构篇

    一、架构相关 1、MVC,MVP,MVVM MS思考:Android面试一天一题(Day 33:Android开发...

  • Android面试一天一题(Day 18:SQLite数据库)

    鹭岛厦门是个很美丽的海滨城市,给我的感觉很舒适和悠闲,据说政府对到那工作的高新技术人才第一年有10万元的奖励,因为...

  • 面试题

    最全的BAT大厂面试题整理答案Android面试一天一题(Day 37:一套高级工程师的面试题)Android面试...

  • MS(2):Android之基础知识篇

    二、组件 1、Activity----------1 MS思考:Android面试一天一题(3 Day):Acti...

  • MS(7):非技术问题篇

    一、GM问非技术问题汇总 MS思考:Android面试一天一题(Day 34:常去的Android相关站点) 说下...

  • MS(3):Android之机制原理篇

    五、重点机制原理 1、Handler机制 MS思考:Android面试一天一题(8 Day):Handler相关分...

  • MS(5):android之进阶篇

    七、自定义View MS思考:Android面试一天一题(Day 30:老外的自定义View面试题)MS思考:老外...

  • android Service 一些思考

    看了Android面试一天一题(1 Day),对Service 的一些总结与思考 作者原文章链接: http://...

  • MS(6):Java篇

    MS思考:Android面试一天一题(7 Day):Java相关 1.Switch能否用string做参数? 在J...

网友评论

  • 望北8261:平时开发喜欢用ORM,用的很爽,但是面试的时候问原生的就懵逼的
  • 森林雪峰:楼主好厉害
  • 67798e788591:最近遇到了这方面的问题,使用liteorm,查询5000条数据,框架内的日志显示300ms左右完成,但是我在List<>=query语句后打出来的时间距查询开始要2-3s,也就是说从查询开始到我实际可以操作查询结果要2-3s,这里面是不是主要是io耗时?单看数据库查询时间是可以接受的,但总体耗时就不理想了
  • 8cd249b8ddf2:关于楼主那个一边插入一边读取显示那个问题,我有如下解决方法,望楼主评价一下如何: 首先先根据项目需求,看这个一边读取的数据量有多少,可以采用预插入一个小数量级的数据,然后再读出来(假设数据获取和UI显示在两个不同的进程),满足UI需求,然后后续再用户无法感知的情况下,再完成后续的大量数据插入。这相当于lazy吧,因为移动端的确资源有限,动不动就想并发不太实际,楼主觉得呢?
  • WayneLyn:想问一下一般开发用sqlite好还是orm好?比如说一些公司之类的
    goeasyway:@WayneLyn 小的应用自己比较容易控制的我比较喜欢用orm,其他用原生要保险些。
  • dean550:插入10000条数据 用事务还可以应对过来。之前一项目,要插入50W的数据,开了事务,也吃不消。楼主对整个问题,有没有什么想法?
  • 流穿枫:如果仅仅是简单的增删改查,用greenDAO框架有助于快速开发。对于表关系错综复杂的,楼主建议用sqlite自己写sql语句吗?
  • 码农豹:看作者的面试一天一题有点像打怪升级的快感,升到18级啦,就是不知道顶级是多少 :grin:
  • 860fa2e98755:作者写得很详细,很清晰,逻辑清楚,图文并茂,多谢分享.
  • ab752740d9e1:ringcentral :smiley:
  • 4da100780a44:Sqlite在并发处理上却是有很多可以优化的点,同一时间只允许一个线程写,这就有些尴尬
  • 岁月留痕:有一次项目需要插入2000多条记录,用的xutils的db,结果花了1分多钟的时间,它里面也是开了事物,后来成存其他地方快了500%
    岁月留痕:@gadfly_only 存文件里了
    gadfly_only:@岁月留痕 “后来成存其他地方快了500%”什么意思
  • 捡淑:mark
  • Edward_zhuo:刚好最近项目中用到了sqlite
  • ab72c065186a: :clap: 楼主屌屌的,👍👍👍赞!

本文标题:Android面试一天一题(Day 18:SQLite数据库)

本文链接:https://www.haomeiwen.com/subject/ofdvrttx.html