• 树莓派开发笔记(十七):树莓派4B+上Qt多用户连接操作Mysql数据库同步(单条数据悲观锁)


    前言

      安装了mysq数据库,最终时为了实现在一个树莓派上实现多用户多进程操作的同步问题,避免数据并发出现一些错误,本篇安装了远程服务并且讲述了使用Qt进行悲观锁for update操作,命令行进行同步查询的示例。

     

    其他操作

      这里只是稍微提一下,具体参照博主的树莓派系列博客,非常详细。
      远程登陆界面

    sudo apt-get install tightvncserver
    sudo apt-get install xrdp
    sudo service xrdp restart
    sudo ufw allow 3389
    sudo service ufw restart
    

      然后可以使用window远程桌面登陆了:
      默认用户名:pi
      默认密码:raspberry
      在这里插入图片描述
      在这里插入图片描述

    安装qt5

    sudo apt-get install qt5-default
    sudo apt-get install qtcreator
    

      安装好后,远程桌面的程序里面就多了个qtcreator了:
      在这里插入图片描述
      在这里插入图片描述
      创建一个界面工程,然后运行:
      (编译速度比几年前的3B+快一些,后续开发过程中测试一下,是否可以忽略3B+的交叉编译)
      在这里插入图片描述
      检查数据库驱动:
      在这里插入图片描述
      没有mysql的数据库驱动。

    sudo apt-get install libqt5sql5-mysql
    

      在这里插入图片描述

     

    Qt操作mariadb数据库

    QSqlDatabase db;
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("127.0.0.1");
    db.setPort(3306);
    db.setDatabaseName("data");
    db.setUserName("root");
    db.setPassword("a1234567");
    
    if(db.open())
    {
        LOG << "Succeed to open db";
    }else{
        LOG << "Failed to open db:" << db.lastError().text();
        return;
    }
    
    QString cmd = "select * from student;";
    QSqlQuery query = db.exec(cmd);
    while(query.next())
    {
        LOG << query.value(0).toString()
            << query.value(1).toString()
            << query.value(2).toString()
            << query.value(3).toString();
    }
    

      在这里插入图片描述

     

    多用户操作

      本意是为了多用户操作,那么读的时候需要加读锁,写的时候需要加写锁。
      两个用户同时读取了数据库中的一条记录,此时用户A对其中一个字段的值进行了修改操作并进行了提交,后来用户B也对这个字段进行了修改,用户B的提交将会覆盖用户A提交的值。
      在这里插入图片描述

    锁类型

    悲观锁

      每次去取数据,很悲观,都觉得会被别人修改,所以在拿数据的时候都会上锁。简言之,共享资源每次都只给一个线程使用,其他线程阻塞,等第一个线程用完后再把资源转让给其他线程。synchronized和ReentranLock等都是悲观锁思想的体现。

    乐观锁

      每次去取数据,都很乐观,觉得不会被被人修改。因此每次都不上锁,但是在更新的时候,就会看别人有没有在这期间去更新这个数据,如果有更新就重新获取,再进行判断,一直循环,直到拿到没有被修改过的数据。(mysql需要自己实现乐观锁)。

    for update使用场景(悲观锁)

      for update 可以为数据库中的一行数据加上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
      如果项目对某个数据准确性有要求,并且项目存在并发(不一定高并发),则需要使用 for update。
      比如:用户A使用余额购买商品,此时用户B向用户A发起转账,如果恰好处在同一时间,则可能造成用户A最终余额错误。此时需要使用 for update 进行数据加锁防止出错。
      这种情况下,即使并发很小,但是也会有一定的概率会碰到,而余额的错误即使差一分钱也是不能容忍的,所以这种特定的场景,即使不是高并发,也应该使用 for update 规避问题。

    for update 用法

    begin;
    select * from XXX where XXX for update;
    ...
    commit;
    

      for update 必须在事务中才生效。

     

    Qt测试

    使用127.0.0.1的ip进行连接(本地连接)

    QSqlDatabase db;
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("127.0.0.1");
    db.setPort(3306);
    db.setDatabaseName("data");
    db.setUserName("root");
    db.setPassword("a1234567");
    
    if(db.open())
    {
        LOG << "Succeed to open db";
    }else{
        LOG << "Failed to open db:" << db.lastError().text();
        return;
    }
    
    if(db.transaction())
    {
        QString cmd = "select * from student for update;";
        QSqlQuery query = db.exec(cmd);
        while(query.next())
        {
            LOG << query.value(0).toString()
                << query.value(1).toString()
                << query.value(2).toString()
                << query.value(3).toString();
        }
        for(int index = 0; index < 10; index++)
        {
            QThread::sleep(1);
            LOG << "sleep:" << index;
        }
        if(!db.commit())
        {
            LOG << "Failed to commit";
        }
    }
    

       在这里插入图片描述
      至此,我们的锁加入成功,说清楚原理可以方便大家着手开始开发多用户进程操作数据库的同步开发了。

    使用局域网的ip进行连接(远程连接)

    QSqlDatabase db;
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("192.168.0.103");
    db.setPort(3306);
    db.setDatabaseName("data");
    db.setUserName("root");
    db.setPassword("a1234567");
    
    if(db.open())
    {
        LOG << "Succeed to open db";
    }else{
        LOG << "Failed to open db:" << db.lastError().text();
        return;
    }
    
    if(db.transaction())
    {
        QString cmd = "select * from student for update;";
        QSqlQuery query = db.exec(cmd);
        while(query.next())
        {
            LOG << query.value(0).toString()
                << query.value(1).toString()
                << query.value(2).toString()
                << query.value(3).toString();
        }
        for(int index = 0; index < 10; index++)
        {
            QThread::sleep(1);
            LOG << "sleep:" << index;
        }
        if(!db.commit())
        {
            LOG << "Failed to commit";
        }
    }
    

      连接不上:
      在这里插入图片描述
      这个时候需要修改下配置:

    sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
    

      在这里插入图片描述

      重启服务:

    systemctl restart mysql.service
    

      在这里插入图片描述
      继续测试,下面是用树莓派使用局域网ip(之前也不能连接127.0.0.1):
      在这里插入图片描述
      连接成功,然后在局域网pc上连接,但是不能打开事务:
      在这里插入图片描述
      修改一下:
      在这里插入图片描述

      这个其实是跟mysql驱动有关系,因为笔者是几年前弄得libmysql.dll和libmysqld.dll,这个不深究了,已经可以远程操作了。

  • 相关阅读:
    YOLOV7训练TT100K交通标识符数据集
    SpringBoot整合Redis
    JavaScript Promise
    MySQL学习day01
    Python语法--函数、全局/局部变量
    基于Taro开发京东小程序小记
    黑马程序员最新版JavaWeb综合案例(前后端完整版)
    【C++】类的封装 ① ( 类和对象 | 面向对象三大特征 - 封装 继承 多态 | 类的封装引入 )
    解决mac系统终端无法使用vpn
    Antv G6 拖拽生成节点
  • 原文地址:https://www.cnblogs.com/qq21497936/p/16377278.html