ท่านใดเคยเขียนเว็บที่มีการอัพเดทข้อมูลบน SQLถี่ๆเช่นเว็บเกี่ยวกับเงินบ้างครับ +1

เริ่มโดย nat-ns, 28 มิถุนายน 2016, 18:27:26

หัวข้อก่อนหน้า - หัวข้อถัดไป

0 สมาชิก และ 1 ผู้มาเยือน กำลังดูหัวข้อนี้

nat-ns

เนื่องจากเว็บที่กำลังจะทำนั้น มีการอัพเดทข้อมูลที่ถี่มาก เช่นส่วนของการนับแต้ม ที่จะเก็บ Score จากหลายๆที่โดยไม่อาจคาดเดาได้ว่าแต้มเหล่านั้นจะถูกบันทึกมาจากใครและที่ใด

สมมตินาย A มีแต้ม 200 แล้ว นาย B,C,...N ส่งแต้มมาให้ โดยให้แบบพร้อมกันบ้างไม่พร้อมกันบ้าง แต่ให้ตลอดเวลา ปัญหาที่เกิดคือข้อมูล อาจจะชนกันได้ ดังนั้นผมถึงได้หาวิธีแก้ปัญหาโดยใช้ INNODB เพื่อสามารถควบคุมการ Lock ระดับแถวได้ และตอน SELECT SCORE เก่าผมก็ใช้คำสั่ง FOR UPDATE เข้ามาช่วยเพื่อไม่ให้คนอื่นสามารถมาทำรายการได้แล้วคนที่อ่านลำดับถัดไป หรือในเวลาเดียวกันต้องได้ค่า score ล่าสุดที่ถูกต้องนะ

ปัญหาคือผมยังไม่เคยเจอสถานณ์ที่ข้อมูลวิ่งกันขนาดนนี้เลยไม่รู้ว่าวิธีใดเหมาะสมที่สุดในการเขียนคำสั่ง SQL ในการนำเอา SCORE มา UPDATE ดังนั้นผมจึงอยากขอประสบการณ์จากทุกท่าน ช่วยเล่าหรือแนะนำ วิธีที่ผมใช้(ได้มากจากคำแนะนำจากหลายท่านในหลายที่) นั้นเหมาะสมหรือไม่ แล้วมีวิธีใดที่ดีกว่า เพราะผมไม่อยากให้ระบบตอบสนองช้าเนื่องจากคิวที่รอยาวเกินไป และไม่อยากให้ข้อมูลชนกันจนค่า Score ผิดเพี้ยน เพราะเป็นหน่วยที่สำคัญมากๆ เพราะเป็นเรื่องของเงินๆทองๆจริงๆครับ เสียหายมาผมคงกุมขมับแน่ๆ ดังนั้นจึงขอความอนุเคราะห์ จากประสบการณ์ของหลายๆท่าน

ตัวอย่างโค้ดที่ผมจำลองมาทดสอบดูนะครับ มันโอเค ข้อมูลอัพเดทล่าสุด โดยผมเปิดจากคอมหลายๆเครื่อง หน้าหลายๆหน้าพร้อมกันโดยเวลาไม่ต่างกันมาก แต่ยังไม่เยอะครับเลยอาจจะไม่เห็นภาพว่าถ้าคนอัพเดทพร้อมกันจะอืดแล้วผิดพลาดไหม

<?php

// Create connection
//$conn = new mysqli($servername, $username, $password, $dbname);
$conn mysql_connect("localhost","root","") or die("Error Connect to Database");
mysql_select_db("testtran");
//*** Start Transaction ***//
mysql_query("START TRANSACTION");

$sql    "SELECT point FROM test FOR UPDATE";
$result mysql_query$sql,$conn);
$row    mysql_fetch_assoc($result);
print_r($row);
//echo $row['point'];
mysql_query("SET GLOBAL autocommit=0");
Sleep(5);

$point $row['point']+100;
$sql "UPDATE test SET point=".$point." WHERE name='nscyber'";
$objQuery1 mysql_query($sql,$conn);

if(
$objQuery1)
{

mysql_query("COMMIT");
echo "Save Done.";
}
else
{

mysql_query("ROLLBACK");
echo "Error Save";
}
//mysqli_close($conn);
mysql_close($conn);
?>

rapiz

ลองไปเรียนรู้เรื่อง ISOLATION LEVEL ของ mysql นะคับ เพราะว่าสำคัญ

ใช้ในการ lock row หรือ session เพื่อไม่ให้ transaction ใหม่เข้ามายุ่ง ถ้ายังไม่ commit

ควรจะใช้ mysqli แทน mysql ได้แล้วนะคับ
Web Hosting,Reseller Hosting ราคาถูกเริ่มต้นแค่ 200฿ ต่อปีเท่านั้น!
VPS Linux, Windows (*ฟรี Direct Admin*) เริ่มต้นแค่ 700฿ ต่อเดือนเท่านั้น
รับเขียน Application บน Windows, Linux ด้วยทีมงานมืออาชีพ!!
สนใจ สมัครบริการด้านบน http://www.rapizhost.com
ติดต่อ 091-7733660 (24ชม.) Line ID : @rapizhost

jira2712

$filename="xxxx.txt";
$fp=fopen($filename,"r");
$number=fread($fp,filesize($filename));
fclose($fp);
$number=$number+1;
$fp=fopen($filename,"w");
fwrite($fp,$number);
fclose($fp);

ผมไม่แน่ใจนะ เพิ่มค่าตัวแปร อีกตัวครับ บนสุดของฐานข้อมูลน่าจะจัดเรียงได้ดีครับ
หัวน้ำหอม ขายส่งหัวน้ำหอม ขายหัวน้ำหอม หัวเชื้อน้ำหอม ขายส่งหัวเชื้อน้ำหอม [direct=https://zateers.com]zateers.com[/direct]
[direct=https://zateer.blogspot.com/]หัวน้ำหอม[/direct]
สารกำจัดเชื้อรา สารกำจัดโรคพืช สารป้องกันโรคพืช [direct= https://zateers.com/greens/]zateers.com/greens/[/direct]
[direct=https://greenszateer.blogspot.com/]สารกำจัดเชื้อรา[/direct]

jira2712

หัวน้ำหอม ขายส่งหัวน้ำหอม ขายหัวน้ำหอม หัวเชื้อน้ำหอม ขายส่งหัวเชื้อน้ำหอม [direct=https://zateers.com]zateers.com[/direct]
[direct=https://zateer.blogspot.com/]หัวน้ำหอม[/direct]
สารกำจัดเชื้อรา สารกำจัดโรคพืช สารป้องกันโรคพืช [direct= https://zateers.com/greens/]zateers.com/greens/[/direct]
[direct=https://greenszateer.blogspot.com/]สารกำจัดเชื้อรา[/direct]

nat-ns

อ้างถึงจาก: rapiz ใน 28 มิถุนายน 2016, 18:29:48
ลองไปเรียนรู้เรื่อง ISOLATION LEVEL ของ mysql นะคับ เพราะว่าสำคัญ

ใช้ในการ lock row หรือ session เพื่อไม่ให้ transaction ใหม่เข้ามายุ่ง ถ้ายังไม่ commit

ควรจะใช้ mysqli แทน mysql ได้แล้วนะคับ

ครับ ผมลองดูไประยะนึงแล้วแต่ว่ายังไม่แน่ใจเรื่องคำสั่งในการเขียน เช่น SELECT สามารถ Lock ได้โดยใช้ FOR UPDATE ถ้ามีคนอื่นที่เรียกในแถวเดียวกันอยู่มันจะรอจนกว่าคนที่มาก่อนทำเสร็จก่อน(จากที่ทดสอบนะครับแต่ไม่รุ้เข้าใจถูกไหม) แล้วจึงนำค่านั้นไปใส่ใน UPDATE

แต่ว่าบางท่านแนะนำว่าให้ใส่ตัวเลขไปตรงๆเลยเช่นปกติใช้ แบบ SELECT ก่อนแล้วเอาค่าที่ SELECT ได้ไปบวกค่าใหม่ให้เรียบร้อยแล้วค่อย UPDATE ดังโค้ด


$sql    = "SELECT point FROM test FOR UPDATE";
$result = mysql_query( $sql,$conn);
$row    = mysql_fetch_assoc($result);

$point = $row['point']+100;
$sql = "UPDATE test SET point=".$point." WHERE name='nscyber'";
$objQuery1 = mysql_query($sql,$conn);


แต่บางท่านบอกว่าทำไมไม่ใส่ใน UPDATE ไปเลยละแบบโค้ดด้านล่าง จะไป SELECT ทำไม เพื่อลดข้อผิดพลาดของ SELECT


$sql = "UPDATE test SET point=point+100 WHERE name='nscyber'";
$objQuery1 = mysql_query($sql,$conn);


แต่ปัญหาคือถ้าใช้แบบแรก SELECT ก่อน มันจะมีคำสั่ง FOR UPDATE มาช่วยในการป้องกันการชนกัน แต่สำหรับ UPDATE เราจะใช้คำสั่งอะไรในการป้องกัน

------------
ส่วนในเรื่องของ  ISOLATION LEVEL ซึ่งก็ต้องระวังในเรื่องของ Deadlock ตรงนี้ก็เป็นอีกปัญหาหนึ่งที่ อยากขอคำแนะนำครับว่ามีวิธีการป้องกันไหนที่เหมาะสมกับงานลักษณะนี้ไหมครับ หรือจุดที่ต้องมองเป็นพิเศษ เพราะข้อมูลมีการอัพเดทตลอดเวลา

:wanwan019:


nat-ns

อ้างถึงจาก: jira2712 ใน 28 มิถุนายน 2016, 18:41:29
$filename="xxxx.txt";
$fp=fopen($filename,"r");
$number=fread($fp,filesize($filename));
fclose($fp);
$number=$number+1;
$fp=fopen($filename,"w");
fwrite($fp,$number);
fclose($fp);

ผมไม่แน่ใจนะ เพิ่มค่าตัวแปร อีกตัวครับ บนสุดของฐานข้อมูลน่าจะจัดเรียงได้ดีครับ

อันนี้คือบันทึกอะไรหรอครับ  :P

xvlnw.com

ถ้าค่านั้นๆที่ต้องการอัพเดตเข้าไป ไม่จำเป็นที่จะต้อง Query ออกมาเพื่อคำนวนบางอย่าง ใช้วิธี point=point+100 แบบนี้ผมคิดว่าไม่น่าจะเกิดการซ้ำซ้อนของข้อมูลนะครับ #NotSure #NotTest
[direct=https://cloudhost.in.th/wordpress-hosting]ツ ⓌⓄⓇⒹⓅⓇⒺⓈⓈ ⒽⓄⓈⓉⒾⓃⒼ [/direct] :wanwan014:  :D
[direct=https://cloudhost.in.th/cloudhosting.html]Cloud Hosting[/direct] [direct=https://cloudhost.in.th/cloudvps.html]Cloud Server[/direct] [direct=https://cloudhost.in.th/vpshosting.html]Cloud VPS Hosting[/direct] [direct=https://cloudhost.in.th/windowsvps.html]Cloud Windows[/direct] Tel: 080-348-0843 LINE: ixvlnw

MapTwoZa

อ้างถึงจาก: xvlnw.com ใน 28 มิถุนายน 2016, 20:48:43
ถ้าค่านั้นๆที่ต้องการอัพเดตเข้าไป ไม่จำเป็นที่จะต้อง Query ออกมาเพื่อคำนวนบางอย่าง ใช้วิธี point=point+100 แบบนี้ผมคิดว่าไม่น่าจะเกิดการซ้ำซ้อนของข้อมูลนะครับ #NotSure #NotTest

ซ้ำได้ครับ มันไม่ thread safe



TO จขกท
ทำไมต้องทำ Pessimistic Read ครับ
เวลา select ถ้าข้อมูลไม่ตรง มัน sensitive ขนาดนั้นเลยหรอ refresh ใหม่ก็ตรงละมั้งเพราะ Pessimistic Read มันกินมาก
ผมมองว่า Pessimistic Write ก็พอครับ เอาให้มัน update ถูกพอละครับ ได้ไม่เปลืองมาก (ถ้าไม่เน้น performance ก็ไม่เป็นไร)

1. เวลา lock ก็ lock เฉพาะ row ที่จะ update พอ
2. update เสร็จก็ Unlock เลย
หลักการมันมีแค่นั้นแหละครับ

อ้างถึงแต่ปัญหาคือถ้าใช้แบบแรก SELECT ก่อน มันจะมีคำสั่ง FOR UPDATE มาช่วยในการป้องกันการชนกัน แต่สำหรับ UPDATE เราจะใช้คำสั่งอะไรในการป้องกัน
ก็ Select for update แหละครับ ไว้ใช้ lock ก่อน Update -*-

ปล. ถ้าเน้น concurrency + performance ไม่ควร lock ที่ DB Level ครับ มันคอขวด ควร lock ที่ Thread Level (ซึ่ง PHP ทำไม่ได้ 555+)
Good code quality Developer :D

kingofdollars

แนะนำว่าทำระบบ ตรวจทาน คล้ายๆที่ธนาคารเขาทำครับ  หาเวลา run ตอน traffic ต่ำๆครับ


นั้นคือ ทุก transaction ที่เข้ามาเก็บลงระบบให้หมดครับ  แล้วก็ update ไปตามเรื่อง

พอถึงเวลาที่กำหนด ก็ run crone ซึ่งทำวันละครั้ง

เอา balance ล่าสุดของวันที่แล้วมา แล้ววิ่ง process ทุก transaction ใหม่อีกที แล้วเทียบกับ balance สุดท้ายของที่ดำเนินการมาแล้ว ถ้าตรงก็รอดตัว

ถ้าไม่ตรงก็เอาของเก่าออกหมด จับตัวใหม่ยัดเข้าไป  ระหว่างนี้อย่าลืมปิดระบบการรับข้อมูลใหม่เข้ามาด้วยนะครับ

หลายๆ ธนาคาร เขารันระบบแนวนี้ราวๆ ห้าทุ่ม ก่อนจะเริ่มวันใหม่นะครับ  จะเห็นว่าช่วงเวลานี้บางที ตู้ ATM จะถอนเงินไม่ได้ ธนาคารออนไลน์เข้าไม่ได้ หรือเข้าไปเจอตัวเลขมั่วตั้วมาก


วิธีแบบนี้ทำให้เราสามารถเอาข้อมูล transaction เก่าเกิน 6 เดือนไป backup เอาไว้เผื่อมีปัญหา หลังจากนั้นก็  clean table นั้นใหม่ทำให้ DB ไม่หนักอีกด้วย

เคยทำระบบ payment processor มา  แค่ใช้ระบบ mysql update ธรรมดา ก็ยังไม่เคยเจอปัญหา transaction ซ้อนกันมากนัก อาศัยว่าเก็บทุก transaction ที่เข้ามาลง DB ก่อน แล้วเวลาคำนวน ยอดเงิน ก็คำนวนจาก ยอด balance ล่าสุด หักลบใน transaction แล้วบันทึกค่าใหม่ลงที่แต่ละ user พร้อม encryption เรียบร้อย  แล้วก็เก็บตัวเลข balance รวมเอาไว้สอบทาน ว่าทุก user รวมกันต้องตรงกับ balance รวม  หักค่าธรรมเนียม ฝั่งถอน ฝั่งฝาก ก็ไม่เคยเจอที่ว่ายอดไม่ตรงกัน

ไว้เป็นแนวทางนะครับ  เพราะหากเราเก็บแค่ balance รวมอย่างเดียว เวลาเจอปัญหา แก้ยากครับ ไม่รู้ว่าของใครซ้ำไม่ซ้ำ แต่แบบนี้เราสอบทานความสอดคล้องของข้อมูลฝั่งจ่ายกับฝั่งรับได้เสมอ drill down ลงไปได้ทางฝั่ง admin


postmunnet

ผมทำ 2 table

table แรก ผมเก็บข้อมูลว่าใครส่งให้ใครส่งไปเท่าไหร่ ร่วมกับเวลา และ status check ว่าเอามาคำนวณแล้ว
table สอง ผมเก็บค่า total

จากนั้นผมจะอัพเดตด้วย cronjob ทุก 1 นาที เอาค่าที่ยังไม่คำนวณมาคำนวณ total ตามที่บันทึกไว้ แล้ว update status check
ไม่แน่ใจว่าที่คิดนี่ถูกหรือดีป่าวนะแต่ถ้าตัวเองทำจะประมาณนี้พร้อมแจ้ง user ว่าต้องรอประมาณ 1 นาทีถึงจะเห็นการเปลี่ยนแปลง

nat-ns

อ้างถึงจาก: MapTwoZa ใน 28 มิถุนายน 2016, 23:55:18
อ้างถึงจาก: xvlnw.com ใน 28 มิถุนายน 2016, 20:48:43
ถ้าค่านั้นๆที่ต้องการอัพเดตเข้าไป ไม่จำเป็นที่จะต้อง Query ออกมาเพื่อคำนวนบางอย่าง ใช้วิธี point=point+100 แบบนี้ผมคิดว่าไม่น่าจะเกิดการซ้ำซ้อนของข้อมูลนะครับ #NotSure #NotTest

ซ้ำได้ครับ มันไม่ thread safe



TO จขกท
ทำไมต้องทำ Pessimistic Read ครับ
เวลา select ถ้าข้อมูลไม่ตรง มัน sensitive ขนาดนั้นเลยหรอ refresh ใหม่ก็ตรงละมั้งเพราะ Pessimistic Read มันกินมาก
ผมมองว่า Pessimistic Write ก็พอครับ เอาให้มัน update ถูกพอละครับ ได้ไม่เปลืองมาก (ถ้าไม่เน้น performance ก็ไม่เป็นไร)

1. เวลา lock ก็ lock เฉพาะ row ที่จะ update พอ
2. update เสร็จก็ Unlock เลย
หลักการมันมีแค่นั้นแหละครับ

อ้างถึงแต่ปัญหาคือถ้าใช้แบบแรก SELECT ก่อน มันจะมีคำสั่ง FOR UPDATE มาช่วยในการป้องกันการชนกัน แต่สำหรับ UPDATE เราจะใช้คำสั่งอะไรในการป้องกัน
ก็ Select for update แหละครับ ไว้ใช้ lock ก่อน Update -*-

ปล. ถ้าเน้น concurrency + performance ไม่ควร lock ที่ DB Level ครับ มันคอขวด ควร lock ที่ Thread Level (ซึ่ง PHP ทำไม่ได้ 555+)


ขอบคุณครับ

อ้างถึงเวลา select ถ้าข้อมูลไม่ตรง มัน sensitive ขนาดนั้นเลยหรอ refresh ใหม่ก็ตรงละมั้ง
sensitive มากครับ ระบบจะ select ในสคริปครับสมาชิกจะมองไม่เห็น ไม่มีการโชว์ยอดหลังคำนวณก่อนบันทึกครับ มันเป็นยอดเงินของสมาชิกที่เก็บไว้ในระบบน่ะครับ ถ้าเป็นแบบเว็บขายสิ้นค้าอันี้ใช้แบบนั้นได้ครับ

แล้วก็ Select for update ผมชอบครับตรงตามที่ต้องการเพียงแต่ถ้าคิวแรกๆอืดเมื่อไรคอวต่อๆไป จะรอนานมากครับ


nat-ns

อ้างถึงจาก: kingofdollars ใน 29 มิถุนายน 2016, 06:09:49
แนะนำว่าทำระบบ ตรวจทาน คล้ายๆที่ธนาคารเขาทำครับ  หาเวลา run ตอน traffic ต่ำๆครับ


นั้นคือ ทุก transaction ที่เข้ามาเก็บลงระบบให้หมดครับ  แล้วก็ update ไปตามเรื่อง

พอถึงเวลาที่กำหนด ก็ run crone ซึ่งทำวันละครั้ง

เอา balance ล่าสุดของวันที่แล้วมา แล้ววิ่ง process ทุก transaction ใหม่อีกที แล้วเทียบกับ balance สุดท้ายของที่ดำเนินการมาแล้ว ถ้าตรงก็รอดตัว

ถ้าไม่ตรงก็เอาของเก่าออกหมด จับตัวใหม่ยัดเข้าไป  ระหว่างนี้อย่าลืมปิดระบบการรับข้อมูลใหม่เข้ามาด้วยนะครับ

หลายๆ ธนาคาร เขารันระบบแนวนี้ราวๆ ห้าทุ่ม ก่อนจะเริ่มวันใหม่นะครับ  จะเห็นว่าช่วงเวลานี้บางที ตู้ ATM จะถอนเงินไม่ได้ ธนาคารออนไลน์เข้าไม่ได้ หรือเข้าไปเจอตัวเลขมั่วตั้วมาก


วิธีแบบนี้ทำให้เราสามารถเอาข้อมูล transaction เก่าเกิน 6 เดือนไป backup เอาไว้เผื่อมีปัญหา หลังจากนั้นก็  clean table นั้นใหม่ทำให้ DB ไม่หนักอีกด้วย

เคยทำระบบ payment processor มา  แค่ใช้ระบบ mysql update ธรรมดา ก็ยังไม่เคยเจอปัญหา transaction ซ้อนกันมากนัก อาศัยว่าเก็บทุก transaction ที่เข้ามาลง DB ก่อน แล้วเวลาคำนวน ยอดเงิน ก็คำนวนจาก ยอด balance ล่าสุด หักลบใน transaction แล้วบันทึกค่าใหม่ลงที่แต่ละ user พร้อม encryption เรียบร้อย  แล้วก็เก็บตัวเลข balance รวมเอาไว้สอบทาน ว่าทุก user รวมกันต้องตรงกับ balance รวม  หักค่าธรรมเนียม ฝั่งถอน ฝั่งฝาก ก็ไม่เคยเจอที่ว่ายอดไม่ตรงกัน

ไว้เป็นแนวทางนะครับ  เพราะหากเราเก็บแค่ balance รวมอย่างเดียว เวลาเจอปัญหา แก้ยากครับ ไม่รู้ว่าของใครซ้ำไม่ซ้ำ แต่แบบนี้เราสอบทานความสอดคล้องของข้อมูลฝั่งจ่ายกับฝั่งรับได้เสมอ drill down ลงไปได้ทางฝั่ง admin

เอิ่มเดี๋ยวนะครับผมอ่านยังงงๆนิดๆ  :P

ก็คือทุกครั้งที่มีการทำรายการ ให้บันทึก transaction ไว้ด้วย พอถึงเวลาให้หาสักช่วงเวลา มาตรวจทานก่อนขึ้นวันใหม่ โดยเอา balance ของวันเก่ามารันกับ transaction วันใหม่โว่ายอดที่คำนวณสุดท้ายนั้นตรงกับ balance ล่าสุดหรือไม่ ถ้าตรงก็โอเค

ถ้าไม่ตรงก็ "ถ้าไม่ตรงก็เอาของเก่าออกหมด จับตัวใหม่ยัดเข้าไป " อันนี้ผมยังไม่เข้าใจครับคือ "เอาของเก่าออกหมด" นี่คือเอาอะไรออกรอครับ ค่าเงินล่าสุดหรอครับ ที่ผมเข้าใจคือสมมติตรวจ transaction  แล้วยอดต้องเป็น 3500 แต่ balance ล่าสุดเป็น 3000 ก็เอา 3500 เข้าไปแทน อย่างนี้หรือป่าวครับ
-----------------------------
อ้างถึงเวลาคำนวน ยอดเงิน ก็คำนวนจาก ยอด balance ล่าสุด หักลบใน transaction แล้วบันทึกค่าใหม่ลงที่แต่ละ user

ทำไมถึงนำค่า ยอด balance ล่าสุด หักลบใน transaction หรอครับ ทำไมไม่หักลบกับ ยอดที่ระบบรับมา ณ ตอนนั้นแล้วบันทึกเลย
-----------------------------
อ้างถึงพร้อม encryption เรียบร้อย 

ปกติท่าน encryption อะไรบ้างครับ อยากขอเป็นความรู้นิดนึงครับ ปกติผมจะ encryption พวกรหัสผ่านมากกว่า พวกยอดเงินหรืออะไรผมไม่รุ็ว่าทางระบบใหญ่ๆ เขา encryption ข้อมูลสำคัญอะไรบ้าง

ขอขอบคุณครับ

nat-ns

อ้างถึงจาก: postmunnet ใน 29 มิถุนายน 2016, 08:30:35
ผมทำ 2 table

table แรก ผมเก็บข้อมูลว่าใครส่งให้ใครส่งไปเท่าไหร่ ร่วมกับเวลา และ status check ว่าเอามาคำนวณแล้ว
table สอง ผมเก็บค่า total

จากนั้นผมจะอัพเดตด้วย cronjob ทุก 1 นาที เอาค่าที่ยังไม่คำนวณมาคำนวณ total ตามที่บันทึกไว้ แล้ว update status check
ไม่แน่ใจว่าที่คิดนี่ถูกหรือดีป่าวนะแต่ถ้าตัวเองทำจะประมาณนี้พร้อมแจ้ง user ว่าต้องรอประมาณ 1 นาทีถึงจะเห็นการเปลี่ยนแปลง

ขอบคุณครับ เหมือนวิธีนี้น่าจะติดปัญหาตรงถ้ามีสมาชิกทำรายการเยอะๆ หลายๆคน ณ เวลานั้น อาจจะต้องรอมากกว่า 1 นาที เพราะระบบอาจจะต้องเช็คทีละคน

kingofdollars

ผมอธิบายใหม่ในประเด็นที่คุณสงสัยนะครับ

เนื่องจากเป็นระบบปิด เงินทุกยอดรวมกันต้องได้ ตัวเลขคงที่เสมอ ดังนั้นจึงมีบัญชีระบบอยู่ด้วยคือ

SYSTEM = 1,000,000 ล้านเป็นต้น

เริ่มแรกถ้าใครฝากเงิน Admin จะต้องโอนเงินจากระบบเข้า A เช่น A ฝาก 1000
B ฝาก 2000  ก็จะได้

SYSTEM -> A 1,000
SYSTEM -> B 2,000

เกิดรายการ
1. SYSTEM -1000 
2 A +1000
3 SYSTEM -2000
4 B +2000

หาก A ถอนเงิน 500 ก็จะเกิด
A -> SYSTEM 500
เกิดรายการ
1. A - 500
2 SYSTEM + 500

แต่ละ  User คือ A,B... System  ก็จะมี  Balance เกาะติดอยู่ด้วย จำนวน 2 balance คือ Balance ปัจจุบันตอนนี้ กับ Balance ล่าสุดจากเที่ยงคืนเมื่อวาน

ดังนั้นรวมทุก Balance ก็จะเป็น 1,000,000 เท่าเดิม

คราวนี้ถึงจุดหนึ่งในเวลาผ่านไป

สมมุติมีรายการเข้ามาติดๆ กัน 5 รายการ ตัวอย่างง่ายๆนะครับ

1 A -> B 20
2 C -> D 10
3 B -> C 10
4 B -> A 10
5 A -> D 10


จะเกิด transaction ที่ต้องใส่ DB ที่ชื่อว่า transaction  โดยจะต้องใส่ Hash จับคู่ฝั่งโอนเข้า โอนออกในแต่ละรายการ
หนึ่งรายการ (2 row) จะต้องมี hash เดียวกันไม่ซ้ำกับอย่างอื่น โดยการเอา DB ID ของคนโอนออกมาใช้ในการ hash
1 A - 20
2 B + 20
3 C - 10
4 D -10
5 B -10
6 C +10
7 B -10
8 A +10
9 A -10
10 D + 10

การเกิดแต่ละ transaction ก็จะมีการหักลบ Balance ปัจจุบันไปเรื่อยๆ เพื่อจะได้แสดงยอดเงินได้อย่างรวดเร็ว
แต่ละคนเองจะเห็นรายการที่เกิดขึ้นในฝั่งตัวเองเท่านั้น ดังนั้นใน table transaction จะมีว่า จำนวนเงิน สถานะโอนเข้าหรือออก หรือจะใส่สองคอลัมน์ เข้า 0 ออก 10 ถ้าโอนออก หรือ เข้า 10 ออก 0 ถ้าโอนเข้า แล้วแต่สะดวก

เวลาเราแสดงยอดเงินให้กับ ลูกค้าก็เอาผลการคำนวนสุดท้ายหรือ Balance สุดท้ายมาแสดง


พอถึงสิ้นวัน เริ่มตรวจสอบระบบใหม่ ว่ายอดเงินทุกคนรวมกัน รวมกับ SYSTEM เท่าเดิมหรือไม่
ถ้าเท่าก็รอดตัว แต่ถ้าไม่เท่ากัน เราจะเห็นว่ารายการไหนมันเพิ่มเข้ามาผิดปกติ

พวกรายการเดียว ข้ามไป
พวกหลายรายการ ตรวจสอบว่า Hash transaction ID ซ้ำกันหรือไม่
ถ้าซ้ำรายการไหน ก็มาทำ algorithm ปรับค่าแสดงผลให้ admin ทราบอีกที  แล้วก็ปรับค่า Balance ใหม่
ตอนนี้ Balance เมื่อวาน ก็จะเท่ากับวันนี้แล้ว

ประมาณนี้นะครับ 
เพราะถ้าไม่ทำแบบนี้ จะไม่มีวันรู้ว่าใครแอบเติมเงินเข้าฐานข้อมูลบ้าง

-----

การ encrypt ยอดเงินในฐานข้อมูลเก็บทั้งตัวเลข และ hash
การ  hash เอาทั้ง user id + ยอดเงิน + เกลือ แล้วเก็บในฐานข้อมูลใน transaction นั้นๆ
เมื่อไหร่ที่ยอดเงินถูกแก้ hash ไม่ตรง ก็ห้ามโอน ให้แจ้งเตือน

นั่นแสดงว่า ทุกครั้งก่อนโอนตร้องตรวจ hash ก่อน
รับเงินมาแล้ว สร้าง hash ใหม่ทุกครั้งไป

พบว่า ใน host บางที แฮกเกอร์เข้ามาเปลียนข้อมูลใน DB เฉยเลยครับ


xvlnw.com

[direct=https://cloudhost.in.th/wordpress-hosting]ツ ⓌⓄⓇⒹⓅⓇⒺⓈⓈ ⒽⓄⓈⓉⒾⓃⒼ [/direct] :wanwan014:  :D
[direct=https://cloudhost.in.th/cloudhosting.html]Cloud Hosting[/direct] [direct=https://cloudhost.in.th/cloudvps.html]Cloud Server[/direct] [direct=https://cloudhost.in.th/vpshosting.html]Cloud VPS Hosting[/direct] [direct=https://cloudhost.in.th/windowsvps.html]Cloud Windows[/direct] Tel: 080-348-0843 LINE: ixvlnw

thanarack

รับงานเขียนโปรแกรมทั่วราชอาณาจักรติดต่อว่าจ้างได้ที่
Line: thanarackk

kingofdollars

อ้างถึงจาก: thanarack ใน 29 มิถุนายน 2016, 22:46:52
ปกติ mysql มันต่อคิวเข้าทำงานอยู่แล้วนะครับ หรือยังไง

ใช่แล้วครับ มันไม่ใช่ปัญหาที่ SQL แต่เป็นปัญหาขอวการออกแบบครับ ในเชิงวิขาการเขาเรียกว่า Last Seat Problem ครับ

ตัวอย่างเช่น สายการบินหนึ่งมีที่ว่าง เหลืออีก 1 ที่นั่งเท่านั้น

ระหว่างนั้น Agent A เช็คเข้ามาก็ว่าง 1 ที่
ขณะเดียวกัน Agent B ก็เช็คเข้ามาเห็นว่า 1 ที่  ซึ่งเป็นอันเดียวกันกับที่ A เห็น

ลูกค้าของทั้งสองฝั่ง ยืนยันจองทันที ในเวลาพร้อมๆ กัน

ปัญหาคือ ใครจะได้ที่สุดท้ายนั้นไป

กรณีนี้ก็คล้ายๆ กัน  คือมีเงินอยู่ 100 นึง แล้วเปิด 2 หน้าเว็บพร้อมๆกัน แล้วกดโอนออกไปคนละ 100 เท่ากัน เอ้ะจะเกิดอะไรขึ้น

จะพบว่าถ้าเขียนโค้ดธรรมดา รายการแรก หักไป 100 เงินเหลือ 0 รายการต่อมาหักอีก 100 เงินก็ติดลบ -100

ปัญหานี้ทำให้โปรแกรมเมอร์สมัครเล่นโดนด่ามานักต่อนักแล้ว


คราวนี้จินตนาการว่า คนนี้สั่งโอนแต้มให้คนโน้น มั่วตั้วไปหมด จะออกแบบอย่างไรไม่ให้เกิดปัญหาแบบข้างต้นนั่นเอง

nat-ns

อ้างถึงจาก: kingofdollars ใน 29 มิถุนายน 2016, 16:47:01
ผมอธิบายใหม่ในประเด็นที่คุณสงสัยนะครับ

เนื่องจากเป็นระบบปิด เงินทุกยอดรวมกันต้องได้ ตัวเลขคงที่เสมอ ดังนั้นจึงมีบัญชีระบบอยู่ด้วยคือ

SYSTEM = 1,000,000 ล้านเป็นต้น

เริ่มแรกถ้าใครฝากเงิน Admin จะต้องโอนเงินจากระบบเข้า A เช่น A ฝาก 1000
B ฝาก 2000  ก็จะได้

SYSTEM -> A 1,000
SYSTEM -> B 2,000

เกิดรายการ
1. SYSTEM -1000 
2 A +1000
3 SYSTEM -2000
4 B +2000

หาก A ถอนเงิน 500 ก็จะเกิด
A -> SYSTEM 500
เกิดรายการ
1. A - 500
2 SYSTEM + 500

แต่ละ  User คือ A,B... System  ก็จะมี  Balance เกาะติดอยู่ด้วย จำนวน 2 balance คือ Balance ปัจจุบันตอนนี้ กับ Balance ล่าสุดจากเที่ยงคืนเมื่อวาน

ดังนั้นรวมทุก Balance ก็จะเป็น 1,000,000 เท่าเดิม

คราวนี้ถึงจุดหนึ่งในเวลาผ่านไป

สมมุติมีรายการเข้ามาติดๆ กัน 5 รายการ ตัวอย่างง่ายๆนะครับ

1 A -> B 20
2 C -> D 10
3 B -> C 10
4 B -> A 10
5 A -> D 10


จะเกิด transaction ที่ต้องใส่ DB ที่ชื่อว่า transaction  โดยจะต้องใส่ Hash จับคู่ฝั่งโอนเข้า โอนออกในแต่ละรายการ
หนึ่งรายการ (2 row) จะต้องมี hash เดียวกันไม่ซ้ำกับอย่างอื่น โดยการเอา DB ID ของคนโอนออกมาใช้ในการ hash
1 A - 20
2 B + 20
3 C - 10
4 D -10
5 B -10
6 C +10
7 B -10
8 A +10
9 A -10
10 D + 10

การเกิดแต่ละ transaction ก็จะมีการหักลบ Balance ปัจจุบันไปเรื่อยๆ เพื่อจะได้แสดงยอดเงินได้อย่างรวดเร็ว
แต่ละคนเองจะเห็นรายการที่เกิดขึ้นในฝั่งตัวเองเท่านั้น ดังนั้นใน table transaction จะมีว่า จำนวนเงิน สถานะโอนเข้าหรือออก หรือจะใส่สองคอลัมน์ เข้า 0 ออก 10 ถ้าโอนออก หรือ เข้า 10 ออก 0 ถ้าโอนเข้า แล้วแต่สะดวก

เวลาเราแสดงยอดเงินให้กับ ลูกค้าก็เอาผลการคำนวนสุดท้ายหรือ Balance สุดท้ายมาแสดง


พอถึงสิ้นวัน เริ่มตรวจสอบระบบใหม่ ว่ายอดเงินทุกคนรวมกัน รวมกับ SYSTEM เท่าเดิมหรือไม่
ถ้าเท่าก็รอดตัว แต่ถ้าไม่เท่ากัน เราจะเห็นว่ารายการไหนมันเพิ่มเข้ามาผิดปกติ

พวกรายการเดียว ข้ามไป
พวกหลายรายการ ตรวจสอบว่า Hash transaction ID ซ้ำกันหรือไม่
ถ้าซ้ำรายการไหน ก็มาทำ algorithm ปรับค่าแสดงผลให้ admin ทราบอีกที  แล้วก็ปรับค่า Balance ใหม่
ตอนนี้ Balance เมื่อวาน ก็จะเท่ากับวันนี้แล้ว

ประมาณนี้นะครับ 
เพราะถ้าไม่ทำแบบนี้ จะไม่มีวันรู้ว่าใครแอบเติมเงินเข้าฐานข้อมูลบ้าง

-----

การ encrypt ยอดเงินในฐานข้อมูลเก็บทั้งตัวเลข และ hash
การ  hash เอาทั้ง user id + ยอดเงิน + เกลือ แล้วเก็บในฐานข้อมูลใน transaction นั้นๆ
เมื่อไหร่ที่ยอดเงินถูกแก้ hash ไม่ตรง ก็ห้ามโอน ให้แจ้งเตือน

นั่นแสดงว่า ทุกครั้งก่อนโอนตร้องตรวจ hash ก่อน
รับเงินมาแล้ว สร้าง hash ใหม่ทุกครั้งไป

พบว่า ใน host บางที แฮกเกอร์เข้ามาเปลียนข้อมูลใน DB เฉยเลยครับ


โหขอบคุณจริงๆครับ พอเห็นภาพระดับนึงครับ ตอนแรกก็งงๆอ่านไปสามสี่รอบ ก็คือเราต้องมี SYSTEM ไว้สำหรับคอยตรวจเช็คเงิน หากวันดีคืนดีมีแฮกเกอร์แอบย่องเข้ามาเติมตัวเลขเข้าไปยอดมันจะเกิน ตามที่ตั้งไว้
- ถ้าอย่างงี้แสดงว่าถ้าคิดว่าอนาคตผู้ใช้บริการจะเยอะขึ้น ควรตั้ง SYSTEM ให้มียอดสูงๆไว้
- ถ้ากรณีที่มีผู้ใช้มากจริงๆแล้วทะลุ SYSTEM มันจะปรับอย่างไรในอนาคตครับเพื่อไม่ให้กระทบกับ SYSTEM ที่เคยตั้งไว้ตอนแรก หรือเพิ่มได้เลย
---------------------------------------
ผมสงสัยว่า
อ้างถึงพวกรายการเดียว ข้ามไป
พวกหลายรายการ ตรวจสอบว่า Hash transaction ID ซ้ำกันหรือไม่
ถ้าซ้ำรายการไหน ก็มาทำ algorithm ปรับค่าแสดงผลให้ admin ทราบอีกที  แล้วก็ปรับค่า Balance ใหม่
ตอนนี้ Balance เมื่อวาน ก็จะเท่ากับวันนี้แล้ว
อันนี้มันจะซ้ำอย่างไรหรอครับ ในเมื่อเราต้องเก็บ Hash transaction ID ไว้กรณีผู้โอนและผู้รับต้องระบุ Hash transaction ID ของผู้โอนไว้ ทั้งสองที่ อย่างงี้มันจะซ้ำกันนิครับหรือผมเข้าใจผิด คือที่ผมเข้าใจก็คือ เอาไว้เช็คว่ามีเงินเกินมั้ยถ้ามีก็หาว่าใครที่ผิดปกติ แล้วให้ระบบทำการปรับให้ตรง
---------------------------------------
อันนี้เป็นภาพที่ผมสรุปจากที่ท่านแนะนำมานะครับ หากผมเข้าใจผิดส่วนใดฝากแนะนำด้วยนะครับ โดยเฉพาะส่วนของ hash ที่ท่านบอกนั้น ที่เอาผู้โอนมาใส่ของทั้งรับและส่ง ให้เหมือนกัน ส่วนคนที่ฝากหรอถอนปกติไม่ได้โอนให้ใคร จำเป็นต้องใส่ Hash transaction ID ไหมครับ

---------------------------------------
อีกคำถามครับ เคยพบว่าในส่วนของ transaction โดนแก้ไขไหมครับ ถ้าโดนแก้ทั้งสองที่มีโอกาสแค่ไหน แล้วท่านรับมืออย่างไร

:P

ผมต้องขอขอบคุณสำหรับคำแนะนำอีกรอบนะครับ และผมต้องขออภัยที่ต้องถามเยอะๆจนบางครั้งอาจจะดูเยอะเกินไป แต่เพื่อให้เคลียที่สุดในฐานะผู้พัฒนาก็เลยคิดว่าถามให้เคลียไปเลย จะได้ไม่ค้างคา แล้วพัฒนากับต่อยอดได้ไม่ติดขัด
:-[