台灣最大程式設計社群網站
線上人數
1632
 
會員總數:246087
討論主題:189663
歡迎您免費加入會員
討論區列表 >> MySQL >> 如何處理 一次多筆資料 新增不重覆 避免併發的問題
[]  
[我要回覆]
回應主題 加入我的關注話題 檢舉此篇討論 將提問者加入個人黑名單
如何處理 一次多筆資料 新增不重覆 避免併發的問題
價值 : 500 QP  點閱數:902 回應數:16
樓主

暗黑
初學者
50 34
1335 127
發送站內信

MYsQL 版本:10
MYsQL 類型: MariaDB
PHP 版本:7.1.33

各位先進,我是個PHP新手,懇請幫忙一下,整理思路
找了整整一天的資料,愈看愈亂

前提 是這樣的
我有一個資料表,要一次新增 多筆資料(沒辨法設 主鍵,比對資料 需由 多欄位組成)
,但在新增前 需確定 這些資料 是不是重覆的,有重覆資料 則 回傳重覆的資料,不重覆 則新增資料

目前我的流程是
一次多筆查詢(Select Union Select) >> 重覆 則 回傳 ( echo json_encode($result, JSON_UNESCAPED_UNICODE); return; )
>> 沒有重覆 則 新增 (INSERT INTO market_po c1,c2 VALUES ('C1','C2'),('C3','C4') )
以上 基本上流程沒有問題

考慮到併發,我就亂了,網路上查到基本上 兩類解決方式
1-可以用 INSERT INTO Table FROM DUAL WHERE NOT EXISTS(SELECT)>>可是查到的資料,都是單筆的,
問1:是沒有一次寫入的嗎?
問2:是要用迴圈路跑嗎?
問3:這樣,真沒有併發的問題嗎?效率的問題嗎?

2-可以用 鎖; 什麼表、行鎖、共享鎖...,然後 又會 死鎖...,然後又說 基本上 會自動上鎖
問1:會自動上鎖,那應該沒有併發的問題,怎會有這麼多討論呢?
問2:我的狀況,應該是加上表鎖(因為沒主鍵),那應該加在那? 查詢前 還是 新增前
問3:查詢前就鎖,會有什麼 延伸問題? 我又應該如何鎖?
問4:多人連線,是如何區分先後順序??

3-有其他方式解決嗎?
4.還是 併發是我多想的,MYSQL已經自動決解了

因問題比較多,點數我設高一點,來感謝 先進的幫忙; 謝謝





搜尋相關Tags的文章: [ 多筆新增 ] , [ 多筆查詢 ] , [ 併發 ] , [ 鎖 ] ,
本篇文章發表於2020-04-10 23:47
1樓
回應

香帥
檢舉此回應
請參考
http://www.blueshop.com.tw/board/FUM20041006161839LRJ/BRD20120204164125KKX.html
我一樓的解答解決併發問題。
多筆建議以陣列迴圈方式寫入。
本篇文章回覆於2020-04-11 00:01
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
2樓
作者回應

暗黑
檢舉此回應
請教 香帥 所以我問題 只要 在 PHP裡
流程: 查詢 >> Application.Lock() >> 寫入 >> Application.UnLock()
寫入: FOR(){ INSERT INTO market_po c1,c2 VALUES ('C1','C2') } //直接新增


還是: Application.Lock() >> 寫入 >> Application.UnLock()
寫入: FOR(){ INSERT INTO Table FROM DUAL WHERE NOT EXISTS(SELECT) } //查詢後新增
可是這樣,就沒有重覆資料回傳...

另外
Application.Lock() 機制是什麼? (剛查了一下,看不懂 Q_Q; 有ASP 的 ,有 UDF 接口的 有....)
要考慮鎖定前檢查、 回滚 嗎?


本篇文章回覆於2020-04-11 00:57
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
3樓
回應

香帥
檢舉此回應
Application.Lock() 那是asp的管制,若是php請參考
https://www.zhengrongshuo.com/code/show/347237
去做一個類似的lock
若要重費資料回傳
就用兩段式程式碼
1.先select 判斷是否有資料。
2.有資料則回傳重複資料,無資料就寫入資料庫,但也要建議您也是要回傳一個已經成功寫入的信息給使用者。
本篇文章回覆於2020-04-11 08:26
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
4樓
作者回應

暗黑
檢舉此回應
早上 查了一下 Application.Lock
Application 是線程機制:
LOOK 是加鎖:那..問題 又回到 MYSQL 加鎖機制上 還是 要如連接所說的
( 如果做多个用户间的数据通信一致性(类似application),可以采用文本文件作为中间存储介质,自己编写类似application 的函数,如果对效率要求很高,建议采用共享内存和信号量的操作,不过php默认安装是不支持的,可能要重新安装php。)

是這樣嗎? 還是我理解錯誤?
本篇文章回覆於2020-04-11 11:04
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
5樓
回應

香帥
檢舉此回應
譬如這麼說
畫面有三張票,100個人同時要搶買
當滑鼠有人點下去時候,較好的做法是像ajax機制即時取得server的回應
還沒到mysql時候,就要鎖住,畫面就要產生變化。
就像發口罩一樣,有的藥局作法是發號碼牌,可以讓客戶提早知道,號碼牌發完,再等下去也沒有了。
因為mysql在處理時候,就像購買口罩,要用健保卡一樣,要多花些時間,還要有收錢動作,整個才處理完,但若鎖在前面,就不會浪費客戶時間,
輪到他在櫃台前面才告訴他已經沒有了,客戶就容易發火。
因次建議就鎖在前面,讓客戶提早知道,到mysql時候只是判斷有沒重複,沒重複就寫入,重複的話,則回傳重複資料。


本篇文章回覆於2020-04-11 12:31
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
6樓
回應

香帥
檢舉此回應
另外補充5樓談到的ajax功能的php的範例程式碼
可參考
http://www.blueshop.com.tw/board/FUM20140918113025PVD/BRD20140303182125981.html
提問者的程式碼,及我幫他修改的程式碼,(不過我的測試網址,因換電腦,舊資料沒再補上,無法測試)
本篇文章回覆於2020-04-11 13:01
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
7樓
回應

香帥
檢舉此回應
另外可參考
http://www.blueshop.com.tw/board/FUM20041006152627A9N/BRD20140321232331N81.html
這位提問者用陣列更新的方法,也希望對您有幫助,當然陣列多筆要還是可以加上判斷是否重複去處理。
本篇文章回覆於2020-04-11 13:19
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
8樓
作者回應

暗黑
檢舉此回應
同意 5樓的說法,我在APP 端 已有對 資料庫 已存在資料 進行重覆驗證通知,
但因不能控制使用者輸入時間的長短(使用者需一次,可輸入多筆資料),或 他們突然有外務界入(常常發生),造成上傳PHP時 "已驗証 沒重覆的資料",被別人寫入了,

就好比是搶坐位,
我目前遇到的狀況(先天條件):我無去控制 位子什麼是候出來(要等使用者 拋出),而位子是 多使用者擁有(所以誰有位子,位子那天屬於誰,我也都不知道)(我只知道有位子),慘的是,使用者有很大的可能會記錯今天位子不屬於他(機率偏高),造成重覆發位
因為,位子絕對不能重覆(機率要無限,趨近於0%),所以 才會再PHP >>MYSQL 內 再次驗證位子的重覆性,但考慮到 併發問題。我就整個混亂了

所以需在PHP 對 MYSQL 端再進行 驗証一次
所以延伸出來的問題就是:一次多筆資料 新增不重覆 避免併發的問題

PS:
先 謝謝 香帥的指導,您提供的兩篇連接,我再來去研究下,感謝 Orz
本篇文章回覆於2020-04-11 15:49
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
9樓
作者回應

暗黑
檢舉此回應
補充一下
我是 app >> php >> mysql 流程,app 並不直接 對 資料庫進行 直接操作
本篇文章回覆於2020-04-11 15:57
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
10樓
作者回應

暗黑
檢舉此回應
再補充下
位子的擁有人,可以再把位子拋出,所以會形成 1 對多 ,再加上日期、時間區段,位子雖然還是一個,可是在資料上,會是 多對多
所以 無法 直接用主鍵 鎖定 只能用 多欄位鎖定

另外,目前階段是,把 位子 拋出,還未到 接(搶) 位子

本篇文章回覆於2020-04-11 16:20
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
11樓
不錯的參考

迷路
捐贈 VP 給 迷路 檢舉此回應
用複合唯一鍵+錯誤判斷的作法不行嗎?
本篇文章回覆於2020-04-13 09:41
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
12樓
作者回應

暗黑
檢舉此回應
感謝 迷路 的回覆
目前理解到的 是mysql 會自動加鎖,但是只針對 個別的 sql語句,
如果 是一次處理多筆資料的話,會 是先查詢 有無重覆 然後 再加入/修改 資料,可是這樣 mysql 會各別 加鎖等待,如果這時候剛好有人也做了同樣的事(併發),就有可能會因為時間差,造成資料重覆處理
所以,必須用 事務 的寫法,
然而 事務的寫法,因手動加鎖的方式、資料庫架構、sql語句的寫法...,又 innoDB引擎 的隔離級別,
所以會產生 髒讀 幻讀 不可重複讀 等狀況,造成問題
簡單來說,一切都是因為效率的問題,要高併發(多人處理)就低效能,
所以 迷路 提供的 思路,我目前 有 想過 就是 用 迴圈的 方式 select ... updata; 組成單一語句 執行(自已不用處理 加鎖),但使用者 需等待較長時間(另外程式架構也要修改)
用事務 不用改寫架構,可是要清楚 要怎麼寫 事務 來避免 髒讀 幻讀 不可重複讀 等狀況

目前 查到後,我目前理解的狀況是這樣。
本篇文章回覆於2020-04-13 17:43
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
13樓
作者回應

暗黑
檢舉此回應
補充 回答迷路的問題
我目前 就是 用複合唯一鍵 + 錯誤判斷的作法
但 因多筆資料一次性處理,延伸出來的問題,相關問題已寫在 12樓
本篇文章回覆於2020-04-13 17:46
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
14樓
最有價值解答

香帥
檢舉此回應
只要做索引速度就會很快,至於是否要用複合唯一鍵,端看個人需要,只要使用者覺得速度可以,介面又容易登打,不須知道後端是如何處理的。
本篇文章回覆於2020-04-14 00:05
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
15樓
作者回應

暗黑
檢舉此回應
非常同意 14樓 香帥的說法 只要做索引速度就會很快,只要使用者覺得速度可以,不須知道後端是如何處理的。
所以,結合香帥之前提供的 資訊 Application.Lock() >> mysql操作 >> Application.UnLock()

在 mysql 中 只要用 Lock tables orders read local, order_detail read local >> mysql操作 >> Unlock tables; 直接鎖表 就可以了

如果要更貪心一點的話(因為 鎖表 是當下只能一個人使用,其他人不能用(簡單比喻)),
要同時多人可以操作,就要在sql程式裡 下功夫了,mysql 現在 預設 innoDB 引擎(預設 行鎖, 表鎖是 MyISAM在處理的),
innodb 為了,讓更多人可以同時操作 資料庫,所以只鎖你要操作的那 行,其他 行 讓別人也可以操作
所以,又分鎖定的 行,別人可以 讀 還是 不可以 讀 (在這裡,就有可能產生,你在更新資料前 查詢驗證的資料,可能你剛 查完 別人剛好馬上變更,而你又變更了一次,所延伸出來的問題)
所以,在 查詢時 就要先 上鎖 (SELECT * FROM table_name WHERE ... FOR UPDATE),避免 別人可以同時操作。造成的更新問題

又因為,不是一個語句就可以執行完的事,如何讓 mysql 知道 加上鎖了還可以 允許 操作更新,就要用 事務 來表示
SET AUTOCOMMIT = 0;
BEGIN;
SELECT book_number FROM book WHERE book_id = 123 FOR UPDATE ;
UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;
COMMIT;

其實 行鎖的觀念 跟 表鎖的關念 差不多,只是 為了高併發 只鎖 行,可能會有比較多的問題影響(問題最大的可能來自於,程式加錯鎖、鎖的時機不對、加鎖順序有問題、select 查詢的方式 ...),

以上 是我這幾天 查資料 彙整後的 心得(若有問題煩請不吝,告知)
來去,睡覺,明天繼續查找資料


本篇文章回覆於2020-04-14 02:34
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔--
   

回覆
如要回應,請先登入.