前言
安裝了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
sudo apt-get install qt5-default
sudo apt-get install qtcreator
安裝好後,遠端桌面的程式裡面就多了個qtcreator了:
建立一個介面工程,然後執行:
(編譯速度比幾年前的3B+快一些,後續開發過程中測試一下,是否可以忽略3B+的交叉編譯)
檢查資料庫驅動:
沒有mysql的資料庫驅動。
sudo apt-get install libqt5sql5-mysql
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。
比如:使用者A使用餘額購買商品,此時使用者B向使用者A發起轉賬,如果恰好處在同一時間,則可能造成使用者A最終餘額錯誤。此時需要使用 for update 進行資料加鎖防止出錯。
這種情況下,即使併發很小,但是也會有一定的概率會碰到,而餘額的錯誤即使差一分錢也是不能容忍的,所以這種特定的場景,即使不是高併發,也應該使用 for update 規避問題。
begin;
select * from XXX where XXX for update;
...
commit;
for update 必須在事務中才生效。
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";
}
}
至此,我們的鎖加入成功,說清楚原理可以方便大家著手開始開發多使用者程式運算元據庫的同步開發了。
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,這個不深究了,已經可以遠端操作了。