![]() ![]() ![]() ![]() |
|||||
|
|||||
樓主 暗黑 ![]()
![]() |
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已經自動決解了 因問題比較多,點數我設高一點,來感謝 先進的幫忙; 謝謝 |
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樓
不錯的參考
迷路 ![]() ![]() |
用複合唯一鍵+錯誤判斷的作法不行嗎?
本篇文章回覆於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
== 簽名檔 ==
--未登入的會員無法查看對方簽名檔-- |
回覆 |
如要回應,請先登入. |