There is a small item to answer the table with the following fields:
ID User ID
Times answers
Questions answers the question ID, which is a string of PHP serialize ()
When the user answers the question like the backend submission results, the built post package is as follows:
{ ' id ': 1, ' QuestionID ': 38}
The background logic is as follows:
$user = User::findfirst ("id =? 1", Array (' ID ' =>$_post[' id ')));//get user Model $questions = unserialize ($user->questions );//deserialization $questionid = (int) $_post[' QuestionID '];//topic idif (In_array ($questionid, $questions)) {//To determine if the user replied, If not answered, add the title ID to the questions field $questions [] = $questionid; $user->times + = 1;//Statistical answer $user->questions = serialize ($questions);//Serialization Questions $user->save () ;//update} else { exit (' Sorry, you have already answered this question);}
It seems that the process is not a problem ah, the user each time the submission will be read their own data, testing whether to answer the question, answer the direct exit.
but there is a problem:
When the event was launched the next day, the background data found that a user's times and questions records did not match, showing times=120 count (unserialize (questions)) = 17.
At that time very strange, the code normal process is no problem, the card for a half-day, has not thought out how the user caused this data, until just now, finally enlightened, as the topic said, the problem in the concurrency , the user may write his own script or the use of third-party tools to simulate the POST request, and using techniques such as multithreading to create concurrent requests, let's simulate the concurrency process:
The user initiates two requests at the same time, PHP also allocates two processes to handle, in front of the two processes almost simultaneously executes this line of code:
$user = User::findfirst ("id =? 1", Array (' ID ' =>$_post[' id '));//Get User model
At this point the $user data in the two processes is the same, let's assume that at this point the value of $user->questions is {.}.
Suppose that two requests were built with a post packet of:
{ ' id ': 1, ' QuestionID ': 3}
And
{ ' id ': 1, ' QuestionID ': 4}
So when the code executes to:
In_array ($questionid, $questions)
is actually returning false when the
They launched the $user->save (), respectively, want to add 3 and 4 into questions, note that this time questions in the database is the value of {"}, and the SQL statement is executed, That is, the database has performed two update operations sequentially, first changing the value of questions to {x-i}, then the second save () is executed and changed to {1,2,4}, two times is incremented by 1, so the final result is:
Times questions
4 {1,2,4}
At this time, if the user continues to submit ' QuestionID ': 3, then change to:
Times questions
5 {1,2,3,4}
This is a two concurrency scenario, which can cause times to be more than the number of questions 1
What if we simulate 10 concurrency? Don't want to forget.
Since the problem has appeared, or to solve Ah, the first thought of using InnoDB row-level lock, unfortunately, the table is MyISAM ...
The final solution is implemented by an exclusive lock on the operating system file, and a friend in front of the screen is interested to see:
http://my.oschina.net/cxz001/blog/281130
PHP when database update encounters concurrency, a small pit