简体   繁体   中英

PHP + MySQL voting system limited by IP

I need to implement an anonymous voting system (user registration is a no go). I have decided that the best option is to limit votes on a single item to 10 per IP (to account for Schools etc).

What is the best way to go around this. I'm using PHP + MySQL. During peak times there could be up to 20 votes per second. I'm using load balanced front ends with a dedicated MySQL server.

My worry is inserting a row in the database for every vote and then querying that data to see if they have reached their limit may be too much for the server to handle?

Would I be better off looking at MongoDB or something?

Any other ideas?

I would suggest saving the "voted" status in a cookie. This will allow whole schools & offices to vote. Doing it 10 per IP will allow a single user at an address to vote 10 times.

There are obviously ways around it, like clearing cookies etc, but I think its a good option.

I think key/value databases will be better here.
Also, you need not row for every vote, you need only 1 row per IP and use queryes LIKE

INSERT INTO .. ON DUPLICATE KEY UPDATE

I need to implement an anonymous voting system (user registration is a no go)

IP's are not the way to solve this problem, because a lot of companies/schools have thousand of people mapped to just a couple of IP addresses. If you don't want users to login because of anonymous voting, I would advise you to use CAPTCHA(recaptcha) to protect mass-voting because all other techniques can be bypassed by a skilled programmer. It is even possible to spoof IP address . I believe that in a lot of Linux distros you can spoof IPs easily.

alfred@alfred-laptop:~/bash$ apt-cache search ^fake$
fake - IP address takeover tool

http://en.wikipedia.org/wiki/IP_address_spoofing#Defense_against_spoofing :

It is also recommended to design network protocols and services so that they do not rely on the IP source address for authentication.

But a skilled programmer can not bypass well-tested CAPTCHAs like recaptcha. It is a little bit harder to vote, but in my opinion this is the only way to countermeasure fake votings. Also captcha can not make a voting system invulnerable to faulty votes. The only way to make such a system is by using authentication. Keep a list of users(identities) who are allowed to vote.

What is the best way to go around this. I'm using PHP + MySQL. During peak times there could be up to 20 votes per second.

That would not even sweat Redis , because it is insanely fast.

Redis is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets.

First my system information. I like it, but it is already pretty old.

-Computer-
Processor       : 2x Intel(R) Core(TM)2 Duo CPU     T7100  @ 1.80GHz
Memory      : 2051MB (1403MB used)
Operating System        : Ubuntu 10.10
User Name       : alfred (alfred)
Date/Time       : Sat 16 Jul 2011 07:53:20 PM CEST
-Display-
Resolution      : 1280x800 pixels
OpenGL Renderer     : Unknown
X11 Vendor      : The X.Org Foundation
-Multimedia-
Audio Adapter       : HDA-Intel - HDA Intel
-Input Devices-
 Power Button
 Lid Switch
 Sleep Button
 Power Button
 AT Translated Set 2 keyboard
 Dell Dell USB Keyboard
 Logitech Trackball
 PS/2 Logitech Wheel Mouse
 Video Bus
-Printers (CUPS)-
Canon-MP150     : <i>Default</i>
HP-Photosmart-b110
-SCSI Disks-
HL-DT-ST DVDRAM GSA-T20N
ATA WDC WD1600BEVS-2

Next I am going to benchmark my redis-server:

alfred@alfred-laptop:~/database/redis-2.2.0-rc4/src$ ./redis-server --version
Redis server version 2.1.12 (00000000:0)

alfred@alfred-laptop:~/database/redis-2.2.0-rc4/src$ ./redis-benchmark 
====== PING (inline) ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

94.11% <= 1 milliseconds
97.77% <= 2 milliseconds
98.97% <= 3 milliseconds
99.02% <= 4 milliseconds
99.51% <= 6 milliseconds
99.88% <= 7 milliseconds
100.00% <= 7 milliseconds
44052.86 requests per second

====== PING ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

87.97% <= 1 milliseconds
97.44% <= 2 milliseconds
98.83% <= 3 milliseconds
99.41% <= 4 milliseconds
99.51% <= 5 milliseconds
99.70% <= 6 milliseconds
100.00% <= 6 milliseconds
43478.26 requests per second

====== MSET (10 keys) ======
  10000 requests completed in 0.37 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

11.02% <= 1 milliseconds
82.00% <= 2 milliseconds
93.94% <= 3 milliseconds
97.18% <= 4 milliseconds
98.17% <= 5 milliseconds
98.89% <= 6 milliseconds
99.44% <= 7 milliseconds
99.51% <= 9 milliseconds
99.52% <= 10 milliseconds
100.00% <= 10 milliseconds
26881.72 requests per second

====== SET ======
  10000 requests completed in 0.24 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

86.50% <= 1 milliseconds
96.08% <= 2 milliseconds
97.45% <= 3 milliseconds
97.87% <= 4 milliseconds
99.02% <= 5 milliseconds
99.51% <= 6 milliseconds
99.52% <= 7 milliseconds
100.00% <= 7 milliseconds
40983.61 requests per second

====== GET ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

86.06% <= 1 milliseconds
97.51% <= 2 milliseconds
98.89% <= 3 milliseconds
99.65% <= 4 milliseconds
100.00% <= 4 milliseconds
42553.19 requests per second

====== INCR ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

90.72% <= 1 milliseconds
96.92% <= 2 milliseconds
98.12% <= 3 milliseconds
98.33% <= 4 milliseconds
99.27% <= 5 milliseconds
99.51% <= 7 milliseconds
100.00% <= 7 milliseconds
43103.45 requests per second

====== LPUSH ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

87.92% <= 1 milliseconds
96.35% <= 2 milliseconds
98.26% <= 3 milliseconds
99.51% <= 7 milliseconds
100.00% <= 7 milliseconds
42735.04 requests per second

====== LPOP ======
  10000 requests completed in 0.24 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

87.75% <= 1 milliseconds
96.67% <= 2 milliseconds
97.77% <= 3 milliseconds
98.64% <= 4 milliseconds
98.65% <= 5 milliseconds
99.80% <= 6 milliseconds
100.00% <= 6 milliseconds
41841.00 requests per second

====== SADD ======
  10000 requests completed in 0.23 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

89.55% <= 1 milliseconds
96.56% <= 2 milliseconds
97.80% <= 3 milliseconds
98.76% <= 4 milliseconds
99.50% <= 5 milliseconds
99.63% <= 6 milliseconds
100.00% <= 6 milliseconds
42553.19 requests per second

====== SPOP ======
  10000 requests completed in 0.25 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

88.12% <= 1 milliseconds
96.21% <= 2 milliseconds
97.45% <= 3 milliseconds
97.99% <= 4 milliseconds
98.53% <= 5 milliseconds
99.51% <= 6 milliseconds
100.00% <= 6 milliseconds
40322.58 requests per second

====== LPUSH (again, in order to bench LRANGE) ======
  10000 requests completed in 0.24 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

89.41% <= 1 milliseconds
96.05% <= 2 milliseconds
97.76% <= 3 milliseconds
98.76% <= 4 milliseconds
99.01% <= 5 milliseconds
99.51% <= 7 milliseconds
99.96% <= 8 milliseconds
100.00% <= 8 milliseconds
42016.81 requests per second

====== LRANGE (first 100 elements) ======
  10000 requests completed in 0.40 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

11.56% <= 1 milliseconds
76.23% <= 2 milliseconds
91.93% <= 3 milliseconds
94.47% <= 4 milliseconds
97.80% <= 5 milliseconds
99.23% <= 6 milliseconds
99.87% <= 9 milliseconds
100.00% <= 9 milliseconds
24937.66 requests per second

====== LRANGE (first 300 elements) ======
  10000 requests completed in 0.86 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

2.28% <= 1 milliseconds
10.90% <= 2 milliseconds
35.68% <= 3 milliseconds
63.74% <= 4 milliseconds
86.00% <= 5 milliseconds
92.65% <= 6 milliseconds
94.96% <= 7 milliseconds
97.50% <= 8 milliseconds
98.04% <= 9 milliseconds
98.75% <= 10 milliseconds
99.56% <= 11 milliseconds
99.96% <= 12 milliseconds
100.00% <= 12 milliseconds
11682.24 requests per second

====== LRANGE (first 450 elements) ======
  10000 requests completed in 1.15 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

1.13% <= 1 milliseconds
6.20% <= 2 milliseconds
10.38% <= 3 milliseconds
27.37% <= 4 milliseconds
53.45% <= 5 milliseconds
74.60% <= 6 milliseconds
89.41% <= 7 milliseconds
95.40% <= 8 milliseconds
98.04% <= 9 milliseconds
98.98% <= 10 milliseconds
99.46% <= 11 milliseconds
99.58% <= 12 milliseconds
99.73% <= 13 milliseconds
99.87% <= 14 milliseconds
100.00% <= 14 milliseconds
8695.65 requests per second

====== LRANGE (first 600 elements) ======
  10000 requests completed in 1.45 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

0.52% <= 1 milliseconds
6.23% <= 2 milliseconds
10.67% <= 3 milliseconds
16.37% <= 4 milliseconds
27.51% <= 5 milliseconds
46.06% <= 6 milliseconds
60.82% <= 7 milliseconds
79.70% <= 8 milliseconds
90.96% <= 9 milliseconds
96.01% <= 10 milliseconds
97.99% <= 11 milliseconds
99.43% <= 12 milliseconds
99.90% <= 13 milliseconds
100.00% <= 13 milliseconds
6896.55 requests per second

The incr operation is what you need and is you can see my system can handle 43103.45 requests per second .

Would I be better off looking at MongoDB or something?

I would recommend redis as proven above.

Just make sure you index the IP field, and consider representing it as something other than a string (eg an integer). See here for more details: http://daipratt.co.uk/mysql-store-ip-address/

Additionally, the cookie idea by adlawson is good. You could use both, and perhaps make the IP address just trigger an alert to you, where you could go to some admin screen and decide if those IP's look like someone that is trying to cheat the system vs. being a school.

Update regarding ipv6: I don't have a huge understanding of ipv6 w/ regards to current webhosting, so not sure if there are users out there exclusively on ipv6. If so, you could consider some of the ideas presented in these posts for how to store them:

Load balancing as you've described should be fine. A dedicated MySQL server shouldn't have any problems with that rate of queries. I don't think MongoDB would help for a problem like this. Something like memcached is much higher performing, but you'd still have to send the data to the more persistent MySQL DB at some point.

I would agree with adlawston on using cookies instead. You could still have an upper limit of votes that can be cast from a single IP

10 is a completely arbitrary value, and will not account for offices where there may be hundreds or even thousands of people behind a single public-facing IPv4 address. Not to mention you're potentially allowing individuals to vote ten times.

Clearly, this is not a robust or fit-for-purpose solution.

Find another way to uniquely identify people.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM