Ik heb in Java een systeem (mee) ontwikkelt die stemmen afvangt van gebruikers (simpel gezegd). Dit is een systeem dat maandelijks enkele tienduizenden stemmen moet afvangen (tot 100.000, waarschijnlijk, het oude systeem (een PHP script) doet meestal tussen de 30.000 en 70.000 per maand).
Daar ik het niet nodig vindt om elke stem apart in de database op te gaan slaan, heb ik het zo ontwikkeld dat er een tabel is met daarin de velden, en een 'aantal stemmen'-kolom.
Hier komt er echter twee andere problemen om de hoek:
* Het uniek houden van de rijen (dwz dat er slechts één rij per unieke keuze geinsert wordt)
* Threading (dwz dat bovenstaand maar één keer uitgevoerd wordt, dat het niet mogelijk is om twee keer een insert te doen terwijl er eigenlijk één insert en één update gedaan moet worden).
Beide problemen dacht ik opgelost te hebben (na wat Google werk) met als resultaat onderstaande query:
Deze query:
* Begint een transactie en zorgt ervoor dat er maar één query tegelijk uitgevoerd wordt
* Probeert een update uit te voeren
* Indien de update mislukt (bijgewerkte rijen = 0):
* Maakt een nieuwe rij aan met 1 stem.
Dit leek goed te werken tijdens de tests (waarin ik met honderd+ threads mbv Apache JMeter duizend+ stemmen gepost heb), echter bij de livegang van dit project ging de hele site offline (pijnlijk). Uit de logs kregen we de volgende melding:
Het lijkt er dus op dat een aantal threads tegelijk de query uit proberen te voeren, dat dit te lang duurt, en dat MSSQL 2000 een deadlock detecteert (of iets in die trant). Nu is / zijn mijn vraag / vragen als volgt:
1. Zit er iets fout in mijn query waardoor hij vast loopt?
2. Kan ik dit 'oplossen' door gewoon de query nogmaals uit te laten voeren?
3. Waarom zorgt dit ervoor dat de hele (database) server (die overigens stevig uitgevallen is) op zijn knieën gaat?
4. Hoe kan ik #3 voorkomen?
5. Is het omvormen naar een 'een rij per stem' structuur toch beter / sneller / veiliger / effectiever?
6. Of zit dit in Java? De stemmen worden dmv een servlet naar de server gestuurd, dus ook meerdere tegelijk.
Het systeem moet sowieso tientallen stemmen in een minuut kunnen verwerken (daar het stemmen elke maand uitgevoerd wordt en in het begin zeer populair zal zijn, verwacht ik).
Daar ik het niet nodig vindt om elke stem apart in de database op te gaan slaan, heb ik het zo ontwikkeld dat er een tabel is met daarin de velden, en een 'aantal stemmen'-kolom.
Hier komt er echter twee andere problemen om de hoek:
* Het uniek houden van de rijen (dwz dat er slechts één rij per unieke keuze geinsert wordt)
* Threading (dwz dat bovenstaand maar één keer uitgevoerd wordt, dat het niet mogelijk is om twee keer een insert te doen terwijl er eigenlijk één insert en één update gedaan moet worden).
Beide problemen dacht ik opgelost te hebben (na wat Google werk) met als resultaat onderstaande query:
SQL:
1
2
3
4
5
6
7
8
9
10
11
| BEGIN tran UPDATE votes WITH (serializable) SET votes = votes + 1 WHERE MijnElfId = @id AND playerId = @playerId AND position = @position IF @@rowcount = 0 BEGIN INSERT INTO votes VALUES (@id, @position, @playerId, 1) END COMMIT tran |
Deze query:
* Begint een transactie en zorgt ervoor dat er maar één query tegelijk uitgevoerd wordt
* Probeert een update uit te voeren
* Indien de update mislukt (bijgewerkte rijen = 0):
* Maakt een nieuwe rij aan met 1 stem.
Dit leek goed te werken tijdens de tests (waarin ik met honderd+ threads mbv Apache JMeter duizend+ stemmen gepost heb), echter bij de livegang van dit project ging de hele site offline (pijnlijk). Uit de logs kregen we de volgende melding:
code:
1
2
3
4
5
| Dec 29, 2009 11:40:48 AM dit.dat.Dao.executeQuery SEVERE: Transaction (Process ID 168) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. java.sql.SQLException: Transaction (Process ID 168) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. |
Het lijkt er dus op dat een aantal threads tegelijk de query uit proberen te voeren, dat dit te lang duurt, en dat MSSQL 2000 een deadlock detecteert (of iets in die trant). Nu is / zijn mijn vraag / vragen als volgt:
1. Zit er iets fout in mijn query waardoor hij vast loopt?
2. Kan ik dit 'oplossen' door gewoon de query nogmaals uit te laten voeren?
3. Waarom zorgt dit ervoor dat de hele (database) server (die overigens stevig uitgevallen is) op zijn knieën gaat?
4. Hoe kan ik #3 voorkomen?
5. Is het omvormen naar een 'een rij per stem' structuur toch beter / sneller / veiliger / effectiever?
6. Of zit dit in Java? De stemmen worden dmv een servlet naar de server gestuurd, dus ook meerdere tegelijk.
Het systeem moet sowieso tientallen stemmen in een minuut kunnen verwerken (daar het stemmen elke maand uitgevoerd wordt en in het begin zeer populair zal zijn, verwacht ik).