10
votes

La base de données Android SQLite est corrompue

Ce lien décrit mon problème exactement: http: //old.nnlamble .com / Android-Base de données-corruption-td28044218.html # A28044218

Il y a environ 300 personnes à l'aide de mon application Android en ce moment et de chaque fois que je reçois un rapport de crash sur le serveur avec cette trace de la pile: p>

public class KeyValueTableAdapter extends BaseTableAdapter {

    private String tableName;
    private String keyColumnName;
    private String valueColumnName;

    public KeyValueTableAdapter(Context context, String tableName, String keyColumnName, String valueColumnName) {
        super(context);
        this.tableName = tableName;
        this.keyColumnName = keyColumnName;
        this.valueColumnName = valueColumnName;
    }

    protected String getStringValue(int key) {
        Cursor cursor = null;
        SQLiteDatabase db = null;
        String value;

        try {
            db = dbOpenHelper.getReadableDatabase();
            cursor = db.query(true, tableName, new String[] { valueColumnName }, keyColumnName + "=" + key, null, null, null, null, null);

            if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
                value = null;
            } else {
                value = cursor.getString(0);
            }
        } finally {
            if (cursor != null) cursor.close();
            if (db != null) db.close();
            dbOpenHelper.close();
        }

        return value;
    }
}


public abstract class BaseTableAdapter {

    protected DbOpenHelper dbOpenHelper;

    public BaseTableAdapter(Context context) {
        this.dbOpenHelper = new DbOpenHelper(context, DatabaseSettings.DATABASE_NAME, null, DatabaseSettings.DATABASE_VERSION);
    }

}


5 commentaires

Utilisez-vous des déclencheurs sur votre base de données?


Nope, cette table particulière n'est qu'une "Tableau de clé / valeur". Deux colonnes: une clé primaire (entier) et une autre non-NULL (texte). Pensez-vous qu'utiliser du texte pourrait être le problème? Je suis un peu inconnu avec la dactylographie de Sqlite.


SQLite est sans faille, cela ne compte pas. Vous pouvez appuyer sur le texte dans un champ de date :)


C'est ce que j'ai pensé, plus il échoue au curseur.MoveTofirst (), pas de get ou de getint ou autre chose.


J'ai eu un problème de DB corrompu lorsque je publiéais une table / create de la table / des vues / des déclencheurs alors que j'étais dans une transaction. Comme il s'avère que la transaction SQLite ne peut contenir que des requêtes spécifiques à la table, pas le schéma lié.


5 Réponses :


2
votes

Utilisation de plusieurs instances de SQLITEDATABASE pourrait entraîner votre problème si vous avez deux instances à mettre à jour le même fichier de base de données simultanément.


1 commentaires

Je fais juste que dans l'une de mes applications sans aucun problème ... SQLITE verrouille le fichier de base de données pendant les opérations d'écriture afin qu'ils soient réellement atomiques (et ainsi de fil de sécurité) au niveau du système de fichiers.



9
votes

Très probablement Le processus de base de données (ES) est tué lors d'une E / S. Par exemple, par un tueur de tâche, ou si vous autorisez les opérations d'écriture DB à continuer pendant une heure où l'application doit arrêter ou dormir ...

Voir si vous pouvez reproduire le problème en mettant votre application dans une boucle d'écriture DB et en utilisant un tueur de tâche dessus.

Scénario: 32 octets étant écriés dans la base de données, la tâche de l'écrivain est tuée après seulement 10, résultat: la base de données laissée dans l'état incohérent et éventuellement corrompu.

Voir aussi: Traiteur de processus Android

EDIT: Ouverture et fermeture de la DB pour chaque lecture / écriture? arrête ça! :)


9 commentaires

Merci, je suis revenu ici pour signaler exactement cela. Je peux le recréer avec une application de tâche tueur. Selon cet article, il s'agit d'un bogue dans SQL Lite: PUBBS.net/201003/SQLITE/.../a>


En outre, la raison pour laquelle j'essaie d'ouvrir et de fermer la DB pour chaque écriture est d'éviter ce problème. Cela ne résoudrait pas le problème? L'utilisateur devrait avoir vraiment de la chance et le tuer entre l'ouverture et la fermeture.


En fonction de la fréquence à laquelle vous écrivez sur la DB, cela pourrait vous aider ou vous blesser. L'ouverture / fermeture peut ajouter suffisamment de frais généraux que les écritures fréquentes et les feuilles ouvertes / fermées sont une fenêtre plus grande pour des problèmes. D'autre part, si vous écrivez rarement, l'ouverture / fermeture peut rincer les octets sur "disque" pour vous afin qu'ils ne soient pas suspendus dans la mémoire à risque d'être perdu. J'ajouterais probablement un gros message laid qui affiche si l'utilisateur corrompt la dB. "Hé idiot, vous ruinez votre téléphone en tuant des tâches!" :)


C'est un bug dans SQLite (par votre premier commentaire à cette réponse), puis-je avoir la prime maintenant: p


Même si la base de données écrit est annulée au hasard, elle ne devrait pas corrompre - c'est un peu le point. Donc, il y a un bogue SQLITE dans la logique de validation / récupération, ou il existe un bogue OS concernant FSYNC (ou équivalent) - et l'ouverture et la fermeture de la DB pour chaque écriture de lecture ne doit pas être problématique, bien qu'elle puisse réduire les performances ( Là encore, cela pourrait ne pas, surtout si vous avez un tas d'écritures)


Il a déjà déclaré qu'il était capable de reproduire le problème en utilisant le tueur de tâche. Cette question a été répondue à 10 fois.


Cela arrive également à mes utilisateurs qui ne sont pas des tueurs de tâches d'utilisateur cependant. J'apprécie définitivement votre réponse, mais j'essaie également de comprendre une solution de contournement dans le cas où un tueur de tâche n'est pas utilisé. Je suis proche d'abandonner SQLite et de rouler ma propre couche de persistance à l'aide de fichiers ou peut-être des préférences partagées.


@Brandon, je comprends. Je remarque un peu de baratte sur la question afin qu'il puisse être utile de rediriger notre attention sur des cas spécifiques et de travailler vers un ensemble d'étapes menant à la question. À l'heure actuelle, nous devinons vraiment.


Au fait, y a-t-il un moyen de rendre le code plus "Kill-Kill" en sécurité?



1
votes

Implémentez chaque jour un processus de sauvegarde DB, et si le DB est corrompu, vous ne remplacez simplement la base de données avec une sauvegarde. Vous pouvez utiliser des méthodes de pâte de copie de fichiers simples pour maintenir une sauvegarde chaque jour.


2 commentaires

Merci, mais la base de données contient des informations de session afin que ce ne soit pas très réalisable de faire une sauvegarde. Les données changent de la minute et sont cruciales pour l'application à exécuter.


C'est toujours une voie de sauvegarde d'aller si vous ne voulez pas tout perdre. Je sais que c'est la dernière chose sur terre à faire.



-4
votes

3 commentaires

Le problème est lié à la base de données et donc une solution à utiliser les préfs partagés n'est pas une solution.


Le problème est lié à la base de données et que l'utilisation de la base de données ne peut donc pas être une solution, en particulier lorsqu'il est basé sur les commentaires de Brandon, ce n'est même pas la meilleure façon de stocker ses données (petites et variées de changements de paires de clés).


Point ici est de résoudre le problème et de ne pas fonctionner autour de lui. Habituellement, les solutions de contournement ont un moyen de vous revenir à vous.



1
votes

Depuis que je ne peux pas commenter - -Yet- sur la poste de Brad.

Je dois être d'accord avec les frais généraux supplémentaires.

J'ai une magie HTC comme mon téléphone quotidien et Ram est toujours un problème.

Les téléphones Android sont à des extrémités très différentes du $$$

Certains sont super bon marché et certains sont super chers, cela revient fondamentalement à la RAM et à la CPU.

Les personnes qui exécutent des tueurs de tâches font ruiner leurs téléphones Android.

En tant que développeur, vous devez suggérer des personnes de ne pas les utiliser, ni de refuser le soutien aux personnes qui utilisent des tueurs de tâches, car Android n'a pas besoin de ces "améliorations" (Demandez Steve (cyanogen))

Aussi le Nouvelle instruction dans Android est très coûteux.

Vous voulez limiter la quantité de Nouveau Appels lors de la programmation pour Android.

La programmation pour Android est tout à propos de la réutilisation de la précieuse mémoire. (HTC Magics / Dreams n'a que 96 Mo à la disposition des applications et la plupart d'entre elles sont déjà utilisées)

Quant à votre SQLITHB ... L'API dit que votre SQLITEDB est privé à votre application.

Je ne vois pas pourquoi vous devez ouvrir et fermer une nouvelle connexion à chaque fois que vous souhaitez lire ou écrire.

Je préférerais garder la connexion ouverte jusqu'à ce que l'utilisateur perd la mise au point de celui-ci.

Cependant, si vous écrivez un fournisseur de contenu, c'est une histoire différente.


2 commentaires

Il se passe toujours environ 5 fois par jour, alors je suis assez certain que ce n'est pas que des personnes utilisant des tueurs de tâches. Je viens de constater que je peux parfois causer la même exception à l'aide d'un tueur de tâche. En ce qui concerne l'ouverture et la fermeture de la connexion de base de données à chaque fois - je faisais simplement le point que je suis prêt à ajouter les frais généraux supplémentaires tant que DB ne se corrompre pas (bien que je ne suis pas sûr si l'ouverture / la fermeture résoudra vraiment le problème, je vais juste tout essayer)


Ce pourrait être un bogue SQLITE dans Android, mais les appareils Android ont normalement des ressources très limitées. Donc, créer aussi peu de frais généraux que possible améliorera la vitesse et la stabilité de toute application Android. On dirait que vous créez des ordures inutiles par requête. Le LowMemorykiller du système d'exploitation peut probablement créer les mêmes problèmes qu'un tueur de tâches. Comme votre demande n'est pas aussi importante que les autres services système.