为什么 SQLite 数据库文件被锁定使用 WAL 模式并启用池化?

Why the SQLite database file is locked using WAL Mode and Pooling enabled?

我在我的 C# 程序中使用 SQLite 数据库。

程序每秒在数据库中保存值,并且每次都检查表是否已经存在:

class DataManager : IDataManager
{
    private bool machineTableIsCreated = false;
    private bool machineAlreadyInDb = false;
    private bool machineValueTableAlreadyExists = false;

    public DataManager()
    {
        machineTableIsCreated = CheckIfMachineTableExists();
    }

    //Writing the Values in a CSV-File
    public void WriteInCsv(dynamic value, string name)
    {
        string strFilePath = @"C:\SVN\" + name + ".csv";
        File.AppendAllText(strFilePath, Convert.ToString(value) + "\n");
    }

    //Writing the Values into the Database
    public void WriteInDb(string name, string netId, int port, List<BeckhoffVariable> variables)
    {
        BeckhoffMachineDto machine = new BeckhoffMachineDto
        {
            Name = name,
            NetId = netId,
            Port = port,
            Variables = variables
        };

        
        if (!machineTableIsCreated)
        {
            CreateMachineTable();
            machineTableIsCreated = true;
        }

        machineAlreadyInDb = CheckIfMachineIsAlreadyInDb(machine);
        if (!machineAlreadyInDb)
        {
            InsertMachineInDb(machine);
        }

        machineValueTableAlreadyExists = CheckIfMachineValueTableExists(machine.Name);
        if (!machineValueTableAlreadyExists)
        {
            //Creates Table for the Machine where its values are stored
            CreateTable(machine.Name);
        }

        //Inserts the Value read from the Machine into the DB
        InsertData(machine.Name, machine.Variables);
    }

    private bool CheckIfMachineValueTableExists(string name)
    {
        //Open connection to DB 
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            //Creating Command to execute
            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"SELECT * FROM SQLITE_MASTER WHERE type ='table' AND name ='{name}';";

                //read result of command
                using (var reader = command.ExecuteReader())
                {
                    //Check if result isnt null
                    if (reader.HasRows)
                    {
                        //connection.Close();
                        return true;
                    }
                    //connection.Close();
                    return false;
                }
            }
        }
    }

    private bool CheckIfMachineIsAlreadyInDb(BeckhoffMachineDto machine)
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"SELECT * FROM machines WHERE Name = '{machine.Name}' AND NetId = '{machine.NetId.Replace('.', '-')}' AND Port = '{machine.Port}';";
                command.CommandTimeout = 1;

                using (var reader = command.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        //connection.Close();
                        return true;
                    }
                    //connection.Close();
                    return false;
                }
            }
        }
    }

    private bool CheckIfMachineTableExists()
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = "SELECT name FROM sqlite_master WHERE type='table' AND name='machines';";
                command.CommandTimeout = 1;

                using (var reader = command.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        //connection.Close();
                        return true;
                    }
                    //connection.Close();
                    return false;
                }
            }
        }
    }

    private void InsertMachineInDb(BeckhoffMachineDto machine)
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = "INSERT INTO machines (Name, NetId, Port, VariableCoolingFlow, VariableCoolingIn, VariableCoolingOut, " +
                "VariableTempAc, VariableTempDc, VariableTempAcBelow, VariableTempDcBelow) VALUES(@machineName, @machineNetId," +
                " @machinePort, @machineVar0, @machineVar1, @machineVar2, @machineVar3, @machineVar4, @machineVar5, @machineVar6); ";

                command.CommandType = CommandType.Text;
                command.Parameters.Add(new SQLiteParameter("@machineName", machine.Name));
                command.Parameters.Add(new SQLiteParameter("@machineNetId", machine.NetId.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machinePort", machine.Port));
                command.Parameters.Add(new SQLiteParameter("@machineVar0", machine.Variables[0].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar1", machine.Variables[1].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar2", machine.Variables[2].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar3", machine.Variables[3].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar4", machine.Variables[4].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar5", machine.Variables[5].Name.Replace('.', '-')));
                command.Parameters.Add(new SQLiteParameter("@machineVar6", machine.Variables[6].Name.Replace('.', '-')));

                command.ExecuteNonQuery();
            }
            //connection.Close();
        }
    }

    private static void CreateMachineTable()
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = "CREATE TABLE machines (Name VARCHAR(40), NetId VARCHAR(30), Port VARCHAR(40), VariableCoolingFlow VARCHAR(45)," +
                " VariableCoolingIn VARCHAR(45), VariableCoolingOut VARCHAR(45), VariableTempAc VARCHAR(45), VariableTempDc VARCHAR(45)," +
                " VariableTempAcBelow VARCHAR(45), VariableTempDcBelow VARCHAR(45));";
                command.ExecuteNonQuery();
            }
            //connection.Close();
        }
    }

    //Creates The Tables for the Values a Machine has
    private static void CreateTable(string name)
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"CREATE TABLE {name} (TemperatureCoolingFlow VARCHAR(20), TemperatureCoolingOut VARCHAR(20), TemperatureCoolingIn VARCHAR(20)," +
                " TemperatureDTSAc VARCHAR(20), TemperatureDTSDc VARCHAR(20), TemperatureDTSAcBelow VARCHAR(20), TemperatureDTSDcBelow VARCHAR(20))";

                command.ExecuteNonQuery();
            }
            //connection.Close();
        }
    }

    //Inserts the Machine Values in the Table of the Machine
    private static void InsertData(string name, List<BeckhoffVariable> variables)
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"INSERT INTO {name} (TemperatureCoolingFlow, TemperatureCoolingOut, TemperatureCoolingIn, TemperatureDTSAc," +
                $" TemperatureDTSDc, TemperatureDTSAcBelow, TemperatureDTSDcBelow) VALUES(@machineVar0, @machineVar1, @machineVar2, " +
                $" @machineVar3, @machineVar4, @machineVar5, @machineVar6); ";

                command.CommandType = CommandType.Text;
                command.Parameters.Add(new SQLiteParameter("@machineVar0", Convert.ToString(variables[0].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar1", Convert.ToString(variables[1].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar2", Convert.ToString(variables[2].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar3", Convert.ToString(variables[3].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar4", Convert.ToString(variables[4].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar5", Convert.ToString(variables[5].Value)));
                command.Parameters.Add(new SQLiteParameter("@machineVar6", Convert.ToString(variables[6].Value)));

                command.ExecuteNonQuery();
            }
            //connection.Close();
        }
    }

    //Returns a List of all Machines in the Database
    public List<BeckhoffMachineDto> getAllMachines()
    { 
        List<BeckhoffMachineDto> machines = new List<BeckhoffMachineDto>();

        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            machineTableIsCreated = CheckIfMachineTableExists();
            if (!machineTableIsCreated)
            {
                CreateMachineTable();
                machineTableIsCreated = true;
            }

            using (var command = connection.CreateCommand())
            {
                command.CommandText = "SELECT * FROM machines";

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        BeckhoffMachineDto currMachine = new BeckhoffMachineDto();
                        currMachine.Name = (string)reader[0];
                        currMachine.NetId = reader[1].ToString().Replace('-', '.');
                        currMachine.Port = Convert.ToInt32(reader[2].ToString());
                        currMachine.Variables = new List<BeckhoffVariable>();

                        for (int i = 3; i <= 9; i++)
                        {
                            currMachine.Variables.Add(new BeckhoffVariable(reader[i].ToString().Replace('-', '.')));
                        }
                        machines.Add(currMachine);
                    }
                    //connection.Close();
                }
            }
        }
        return machines;
    }

    //Deletes the Machine from machines Table and deletes the whole Table from the given machine
    public void DeleteMachine(BeckhoffMachineDto selectedMachine)
    {
        using (var connection = new SQLiteConnection("Data Source=C:/SVN/trunk/database.db;PRAGMA journal_mode=WAL;Pooling=True;"))
        {
            connection.Open();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = $"DROP TABLE {selectedMachine.Name}";
                command.ExecuteNonQuery();

                command.CommandText = $"DELETE FROM machines WHERE Name = '{selectedMachine.Name}' AND NetId = '{selectedMachine.NetId.Replace('.', '-')}' AND Port = '{selectedMachine.Port}'";
                command.ExecuteNonQuery();
            }
            //connection.Close();
        }
    }
}

问题是在 运行 程序中它被锁定了好几次。

我已经启用了 WAL 模式和 True 池。

我真的不明白它是如何被锁定的。

SQLite error (5): database is locked in "SELECT * FROM machines WHERE Name = 'Machine1' AND NetId = '10.10.10.1.1.1' AND Port = '111';"

SQLite error (5): database is locked in "SELECT * FROM machines WHERE Name = 'Machine1' AND NetId = '10.10.10.1.1.1' AND Port = '111';"

SQLite error (5): database is locked in "SELECT * FROM machines WHERE Name = 'Machine1' AND NetId = '10.10.10.1.1.1' AND Port = '111';"

SQLite error (5): database is locked in "SELECT * FROM machines WHERE Name = 'Machine1' AND NetId = '10.10.10.1.1.1' AND Port = '111';"

SQLite error (5): database is locked in "SELECT * FROM machines WHERE Name = 'Machine1' AND NetId = '10.10.10.1.1.1' AND Port = '111';"

问题是,当我将值写入数据库时​​,它会从数据库中选择一些内容并抛出数据库锁定错误,但 WAL 和池化应该会阻止这些。

有人知道我可以做些什么来解决我的问题吗?

看来,解决这个问题的方法是将sqlite数据库的同步pragma改为NORMAL!当使用 WAL 模式时,同步 pragma 的最佳设置是 NORMAL,但 db 的默认设置是 FULL。但是 FULL 不能很好地与 WAL 一起工作。当我更改它时,不再有数据库锁定错误。