You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP技术问题:记录投票用户IP的最佳实现方案

嘿,这个问题问到点子上了,我来给你梳理清楚:

强烈推荐创建独立的post_votes表

你一开始想在posts表里存IP数组的思路,确实存在不少硬伤:

  • 数据库层面:数组类型(比如MySQL的JSON或TEXT存数组)的查询效率极低,要判断某个IP是否投过票,得遍历整个数组,数据量大的时候完全没法用。
  • 扩展性差:如果后续想加投票时间、投票类型的统计,数组结构根本没法支持,只能改表结构,非常麻烦。
  • 不符合数据库设计范式:把多个IP存在一个字段里,属于冗余存储,容易出现数据不一致的问题。

而单独建post_votes表(字段至少包含post_id、ip_address、vote_type、created_at),好处太多了:

  • 可以给post_id和ip_address加联合唯一索引,直接从数据库层面阻止重复投票。
  • 查询、统计效率极高,比如想查某个帖子的同意数,直接COUNT(*)加WHERE vote_type = 1就能快速得到结果。
  • 扩展性强,后续要加任何和投票相关的字段(比如用户代理、投票设备)都非常方便。
防止重复投票的最佳实现方案

结合数据库和应用层的双重保障,是最稳妥的做法:

  1. 数据库层面的终极保障
    给post_votes表创建联合唯一约束:

    ALTER TABLE post_votes ADD CONSTRAINT unique_post_ip UNIQUE (post_id, ip_address);
    

    这样不管应用层有没有检查,只要同一个IP给同一个帖子重复投票,数据库就会抛出唯一约束冲突的错误,从根源上阻止重复数据。

  2. 应用层的前置检查
    用户发起投票请求时,先执行查询:

    SELECT 1 FROM post_votes WHERE post_id = ? AND ip_address = ?;
    

    如果查询到结果,直接返回“你已经投过票啦”的提示,不让请求进入写入环节,既提升用户体验,也减少数据库的错误请求。

  3. IP存储的细节处理

    • 别用字符串存IP!如果是MySQL,推荐用VARBINARY(16)存储(IPv4转成4字节,IPv6转成16字节),用INET_ATON()和INET_NTOA()(IPv4)、INET6_ATON()和INET6_NTOA()(IPv6)来转换,既节省存储空间,又能提升查询速度。
    • 如果你的业务主要面向IPv4用户,也可以用INT UNSIGNED存储转换后的IPv4地址,空间更省。
  4. 可选的缓存优化(高流量场景)
    如果你的项目后期流量很大,可以用Redis做一层缓存:

    • 投票成功后,把post_id:ip_address作为key存入Redis,设置一个合理的过期时间(比如和帖子的生命周期一致)。
    • 用户投票前先查Redis,如果key存在,直接返回已投票提示;不存在再走数据库查询,减轻数据库压力。
  5. 投票数据的完整存储
    在post_votes表里加上vote_type字段(比如用tinyint(1),1=同意,0=反对),这样既能记录用户是否投过票,又能统计每个帖子的同意/反对票数,一举两得。

内容的提问来源于stack exchange,提问作者Paul Diamant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:15:34