About concurrency problems, you have a "simple" way to prevent any concurrency problems in the second method, inside your transaction, a selection is made in the article line (now For update implicit). Any matching insert in the same article will not be able to get the same lock and will be waiting for you.
With the new default isolation levels, even without using the serialization level in the transaction, you will not see any parallel insertion in the voting table until the end of the transaction. Thus, your SUM should remain consistent or look coherent . But if a parallel transaction inserts a vote on the same article and commits in front of you (and this second one does not see your insert), the last transaction to commit will overwrite the counter, and you will lose 1 vote. So lock the rows on the article using the selection before (and, of course, do your job in a transaction). It is easy to test, open 2 interactive sessions in MySQL and start transactions with BEGIN.
If you use a trigger, you are in a transaction by default. But I think you should also make a selection in the article table to make implicit row locking to trigger simultaneous triggers (harder to check).
- Do not forget the delete triggers.
- Do not forget about update triggers.
- If you do not use triggers and remain in the code, be careful to insert / delete / update the request for voting; you must lock the line on the corresponding article in the transaction. It is not very difficult to forget.
Last point: perform more complex transactions before using the transaction:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Thus, you do not need row locks in articles, MySQL detects that a potential record in the same row occurs and blocks the transactions of others until you finish. But do not use what you calculated from the previous query. The update request will wait for the release of the lock for articles, when the lock is released by the first COMMIT transaction, the SUM calculation must be done again to count. Thus, the update request must contain SUM or make an addition.
update articles set nb_votes=(SELECT count(*) from vote) where id=2;
And here you will see that MySQL is smart, a deadlock is detected if 2 transactions try to do this, while the insert was executed at the same time. In serialization levels, I did not find a way to get the wrong value with:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN; insert into vote (... update articles set nb_votes=( SELECT count(*) from vote where article_id=xx ) where id=XX; COMMIT;
But be prepared to process a transaction that you must complete again.