上一篇
安卓开发数据库优化
- 行业动态
- 2025-04-22
- 7
数据库结构设计优化
数据表设计原则
- 范式化与反范式化平衡:
遵循第三范式(3NF)减少数据冗余,但针对高频查询场景可适当反范式化(如冗余字段)提升读取效率。 - 字段类型精简:
使用最小存储空间的字段类型(如INTEGER
替代TEXT
存储数字,REAL
替代DOUBLE
)。
字段类型 | 适用场景 | 存储大小 |
---|---|---|
INTEGER | 整数ID/计数 | 4字节 |
TEXT | 短字符串(<2K) | 动态分配 |
REAL | 浮点数计算 | 8字节 |
BLOB | 二进制数据(如图片) | 动态分配 |
索引优化
- 主键索引:必须为
PRIMARY KEY
,类型建议INTEGER PRIMARY KEY AUTOINCREMENT
。 - 唯一索引:对频繁查询的唯一字段(如用户名)添加
UNIQUE
约束。 - 复合索引:针对多条件查询(如
WHERE A=? AND B=?
)创建联合索引。
示例:
CREATE INDEX idx_user_name ON users(name); -单字段索引 CREATE INDEX idx_order_date_status ON orders(date, status); -复合索引
查询性能优化
避免全表扫描
- 强制使用索引:在
WHERE
子句中优先使用索引字段。 - 参数化查询:防止SQL注入并复用编译好的SQL语句。
Room框架示例:
@Query("SELECT FROM user WHERE id = :userId") User getUserById(int userId); // 参数化查询
减少数据量返回
- 选择性字段:避免
SELECT
,仅查询必要字段。 - 分页加载:使用
LIMIT
和OFFSET
(或基于主键的分页)。
分页方案对比:
| 方案 | 优点 | 缺点 |
|—————|————————|————————–|
| LIMIT+OFFSET
| 实现简单 | 大偏移量时性能差 |
| 基于主键分页 | 高效定位数据 | 需维护连续主键或时间戳 |
写入性能优化
事务管理
- 批量操作:将多次写入合并为一个事务,减少IO次数。
- 异步处理:在子线程执行数据库操作,避免阻塞主线程。
示例(使用sqlite
):
SQLiteDatabase db = getWritableDatabase(); db.beginTransaction(); try { // 多次insert/update操作 db.setTransactionSuccessful(); } finally { db.endTransaction(); }
批量插入优化
- 预编译语句复用:减少SQL编译开销。
- 使用第三方库:如
GreenDAO
的insertInTx
方法自动批量插入。
GreenDAO示例:
DaoSession session = ...; UserDao userDao = session.getUserDao(); List<User> users = ...; userDao.insertInTx(users); // 自动开启事务批量插入
内存与资源管理
Cursor优化
- 及时关闭Cursor:使用
try-finally
或CursorLoader
自动管理。 - 按需加载数据:仅查询必要列,避免内存浪费。
示例(Kotlin协程优化):
val result = database.query("SELECT id, name FROM user WHERE id = ?", arrayOf(userId)) .useLineSequence() // 自动关闭Cursor并转换为序列 .firstOrNull()
数据库加密与压缩
- 加密存储:使用
SQLCipher
加密数据库文件,避免明文存储敏感数据。 - 数据库压缩:定期执行
VACUUM
命令压缩数据库文件,防止文件碎片化。
工具与监控
性能分析工具
- Stetho:Facebook开源的调试工具,支持实时查看数据库内容。
- Android Profiler:监控数据库操作耗时和线程状态。
慢查询日志
- 启用SQLite日志:通过
PRAGMA busy_timeout
和PRAGMA journal_mode
调整日志模式。 - 分析查询计划:使用
EXPLAIN QUERY PLAN
查看SQL执行路径。
相关问题与解答
问题1:如何选择索引类型?
解答:
- 单字段高频查询:创建单字段索引(如用户手机号)。
- 多条件组合查询:使用复合索引,字段顺序按查询频率排序(如
WHERE status=? AND date>?
)。 - 频繁更新字段:避免过度索引,因每次更新会触发索引重构。
问题2:分页方案如何选择?
解答:
- 小数据量分页:
LIMIT+OFFSET
简单易用,但数据量大时性能下降。 - 大数据量分页:基于主键或时间戳的分页(如
WHERE id > ? ORDER BY id LIMIT 10
),性能更稳定。 - 复杂场景:结合
INDEX
和覆盖索引
(返回字段包含在索引中)提升效率