请教一个 SQLite+Android+Gomobile 的问题

274 天前
 SilenceLL

背景:

使用gomobile+go-sqlite3构建了一个 Android AAR 的库文件。发现性能很差,大数据库文件(200M-1G 的 sqlite 文件)只要执行insert into select xx或者 update xx set where id in (batch id)就会出现 disk I/O error: read-only file system的文件系统错误。但如果使用相同的 sql+sqlite 文件通过 Android 原生调用,就能够执行成功。同时 AAR 的方式使用模拟器也能够执行成功,似乎电脑端的模拟器性能更高一些。

困惑:

从表象上看 Android 似乎对 AAR 的方式做了一些性能限制,可以确定文件权限没有问题,通过 Android Studio 查看文件和目录权限正常。实在没啥排查思路,目前的改法是分批提交,减少单次数据操作量。不知道如何才能够在 AAR 中达到 Android 原生调用的性能,现在新版的 Android 不 root 真是费事,啥也看不了,啥命令行执行也不行。

684 次点击
所在节点    程序员
6 条回复
zhanlanhuizhang
274 天前
gomobile+go-sqlite3 ,直接调试你就可以知道。逐步增加数量,就可以看出哪里是性能瓶颈。但是这种方案肯定是有性能损失的。因为 go 和 sqlite3 交互,比 C 和 sqlite3 交互,肯定更加耗时。
ysc3839
274 天前
为什么要先调用 Golang 再调用 SQLite ?楼主不会 Java ,只会 Golang ?建议先试试用 Java 调用 SQLite 看看性能如何。
SilenceLL
273 天前
@zhanlanhuizhang @ysc3839 有一定的历史原因,因为我们现在有一个库就是用 beego+go-sqlite3 打包成可执行文件当做 android 本地服务器使用的,新版 android 30 限制使用这种方式执行二进制,只能改成 aar 执行。结果性能下降非常严重,相同的数据和 sql 直接调用不管是通过二进制文件( targetVerison<30 )还是通过 android 直接调用都能执行成功,但是影响数据量大的情况下 gomobile+go-sqlite3 构建的 aar 直接报错。

如下例子(打包成 aar ),同一个事务中,修改数据少的 sql 可以执行成功,修改数据多的不能执行成功。

```go
func Test3(dir string, times int) {
fmt.Println("Test3 exec start:", times)
//os.Remove(dir)
os.Chmod(dir, 0777)
//db, err := sql.Open("sqlite3", "file:"+dir+"?mode=rwc")
db, err := sql.Open("sqlite3", dir)
if err != nil {
fmt.Println(err)
}
defer db.Close()

var readOnly string
row := db.QueryRow("PRAGMA query_only")
err = row.Scan(&readOnly)
if err != nil {
fmt.Println(fmt.Sprintf("pragma query_only error:%s", err.Error()))
}
fmt.Println(fmt.Sprintf("pragma query_only:%s", readOnly))

tx, err := db.Begin()
if err != nil {
fmt.Println(err)
}
for i := 0; i < times; i++ {
_, err := tx.Exec(`update table set is_del = 1 where id <101`)
if err != nil {
fmt.Println(fmt.Sprintf("exec1 error:%s", err.Error()))
}
_, err = tx.Exec(`update table set is_del = 1 where is_del = 0 and id not in
(select id from (select max(create_at), id from table where is_del =0 group by a_id,b_idhaving count(*)>0))`)
if err != nil {
fmt.Println(fmt.Sprintf("exec2 error:%s", err.Error()))
}
}
err = tx.Commit()
if err != nil {
fmt.Println(err)
}
fileInfo, err := os.Stat(dir)
if err != nil {
fmt.Println(err)
}
fileMode := fileInfo.Mode()
fmt.Println(fileMode)
//perm := fileMode.Perm()
//fmt.Println("permission:", uint32(perm))
fmt.Println("Test3 exec end:", times)
}
```

```shell
2023-09-22 10:09:28.473 27362-27496 GoLog I Test3 exec start: 1
2023-09-22 10:09:28.476 27362-27493 GoLog I pragma query_only:0
2023-09-22 10:09:28.521 27362-27496 GoLog I exec2 error:disk I/O error: read-only file system
2023-09-22 10:09:28.521 27362-27496 GoLog I cannot commit - no transaction is active
2023-09-22 10:09:28.521 27362-27493 GoLog I -rwxrwxrwx
2023-09-22 10:09:28.521 27362-27493 GoLog I Test3 exec end: 1
2023-09-22 10:09:28.530 27362-27490 Android E Shell Command Output:-rwxrwxrwx 1 u0_a1451 u0_a1451 17772544 2023-09-22 10:03 /data/user/0/packagename/16fc0efec1104515b0244ddae36a4123.db

```
SilenceLL
273 天前
另外还有一个现象,就是如果我每次执行 sql 之前都宠幸 sql.open 一个新的 db ,这种问题出现的概率会大幅下降。有点像是一个连接性能有限,开个新的就能申请一些新的资源占用。
zhanlanhuizhang
273 天前
@SilenceLL 应该是整个 app 只打开一次 db ,你这多次打开,影响很大。是不是调用错误。
SilenceLL
273 天前
@zhanlanhuizhang 现在就是只打开一次,出现写入错误概率高。如果每次执行前打开一个新的链接,写入错误概率就低了,甚至不出现了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/977069

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX