А файло отдается как?
Исходя из предположения, что с одного ip нет большого смысла скачивать файл более одного раза, то ограничивающий фактор будет ip. Далее делаем так:
На фронт nginx, и делаем локейшн, в котором лежат файлы.
location ^~ /user_files { # Это где реально лежат файлы
internal;
root /path/to/folder;
}
location ^~ /userfiles { # Это то, куда указывают ссылки на сайте
proxy_pass http://127.0.0.1:80;
}
Обрабатываете локейшн /userfiles в php и отдаете заголовки X-Accel-Redirect, чтобы статику отдавал энджи.
Дальше, как считать.
Если стоит memcache - делаем add($fileid.$ip,1) - если ключ уже установлен, add вернет false, и счетчик увеличивать не надо, если не установлен - надо увеличить счетчик. Ключ при этом установится. Счетчик увеличиваем соответственно так: inc("counter/".$fileid,1)
Если стоит redis - все очень похоже. Делаем setnx($fileid.$ip,1) - если ключ установлен, вернет false. Если нет - ключ установится. Счетчик увеличиваем incr("counter/".$fileid,1). Но redis так же может помочь сильно сэкономить память. Для этого надо использовать не строки, а хеши. То есть стейт храним как hsetnx($fileid,$ip,1). А счетчик в этом случае храним как hincrby("counters",$fileid,1). Это даст перед мемкешем следующие преимущества:
1. Драматическая экономия памяти. Хеши c короткими ключами и значениями весьма эффективно расходуют память.
2. Сайд-эффект - очень просто посмотреть, с каких ip загружали файл - hgetall($fileid). Если еще немного модифицировать алгоритм так: hincrby($fileid,$ip,1) - будем получать на выходе целое значение - количество закачек с этого ip - так можно попалить накрутчиков и просто банить их по ip.
3. Сайд-эффект - очень просто получить таблицу всех счетчиков файлов - hgetall("counters").
Ну и все это, разумеется, очень быстро. Делать то же самое в sql базе, думаю, сложновато. Но если сильно хочется - может заморочиться кто-то еще, и расписать алгоритм для мускуля. А мы поржом.
Если ограничивать по пользователям (кукам), что на мой взгляд не очень, то все делается ровно так же, только вместо ip используем id, который записан в сессии. Тогда использовать хеши для хранения состояния не удачная мысль - надо хранить в строках, и ставить им expire - время жизни сессии, или, там, сутки - как угодно.