Monday, November 9, 2015

实时投票系统



http://segmentfault.com/a/1190000000495903
实时投票系统:数据类型上的差异:memcache和redis
最近要写一个类投票的系统:由于访问量可能会比较大,最好不直接使用mysql数据库,完全使用缓存的话,存在缓存失效等的风险,因此在mysql上面写个缓存中间过渡:
1.memcache
按照我的习惯,肯定是使用redis,但是公司目前这个项目线上还没有redis服务,只好凑合一下使用memcache。
第一次的时候使用memcache
这么写:
key为'13453' value为13920,
key为'13454' value为12989,
...
但是这么一来的话就不好排序了,每次想排序,都要从缓存中取出很多个值。
第二次这么写:
memcache中存储了一个数组: 'pid' =>count
array(
'13453' =>13920,
'13454'=>12989,
...
)
每次有人投票需要先取出该数组,然后再对应的pid上面加1,排序,再存入memcache中,代码如下:
[php] view plaincopyprint?在CODE上查看代码片派生到我的代码片
/**
* @brief 从memcache中取出数据,并相应增加一,然后排序
*/
private function _addCount($uid) {
if(empty($uid)) {
return false;
}
$mc_key = "MEM_KEY_SORT";
$list = self::memcache()->get($mc_key);
$list[$uid]++;
if(empty($list)) {
$list = $this->_getCount();//数据不存在则查询数据库(group by)
}
asort($list);
$ret = self::memcache()->set($mc_key, $list);
}
[php] view plaincopyprint?在CODE上查看代码片派生到我的代码片
/**
* @brief 从数据库获取投票次数
* @return array('uid' => '次数')
*/
private function _getCount() {
$sql = "select pid,count(pid) as count from tmp_ssjj_zqwz group by pid";
$query = self::db()->query($sql);
while(($row = self::db()->fetch_array($query))!==FALSE) {
$list[$row['pid']] = $row['count'];
}
return $list;
}
数据结构如下:
CREATE TABLE tmp_ssjj_zqwz (
id int(11) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
uid int(11) NOT NULL COMMENT '投票id',
type int(11) NOT NULL DEFAULT '0' COMMENT '投票类型',
pid int(11) NOT NULL DEFAULT '0' COMMENT '被投票id',
PRIMARY KEY (id),
UNIQUE KEY unique_uid_type (uid,type),
KEY index_pid (pid)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=gbk;
加上索引,使用group by来统计数据:
select pid,count(pid) as count from tmp_ssjj_zqwz group by pid
2.使用redis中的有序集合,直接解决问题:
value为'13453' score为13920,
value为'13454' score为12989,
...
一段代码,直接搞定:
$list= $redis->zRange($_GET['key'], 0, -1);
最后,我是redis的fans。比较支持redis
http://segmentfault.com/a/1190000000510605
实战--积分投票系统中的细节:
好几天没有写博客了,一直忙这写这个积分投票-兑换礼包系统.有很多血泪的教训来分享下:
之前,我一直是写手机接口的,跟前端基本上没有交集,即使有也是给内部提供管理平台,这次可以说给我上了一堂课:
1.实时性:大量的操作是建立在memcache缓存的基础上的,mysql数据库是为了提供数据持久性和记录日志的.因此查询之前会先访问缓存,如果缓存不存在或者失效,去访问数据库.
2.数据量比较大:数据库的索引没有建立到最好.
3.权限的问题,不是所有人都拥有权限可以投票,于是我把拥有权限的用户id放入了一个数组中并放在缓存,同步到数据库里面,最后发现这次投票人数特别多,每次查询一个用户的权限,需要把所有人的权限都拿出来,使用in_array(),来判断,主管发现后,果断改啊,每个用户使用独立的key来保存是否拥有权限.
4.安全性:从前端传递过来的参数要假设是不可靠的,在兑换礼包的时候,我从前端传递过来了礼包的类型和要扣除的积分,放入数据库中,哎!首先悲剧的就是要扣除的积分不能是传递过来的,而是根据礼包的类型来判断.并且要把礼包的类型做出范围配置来检查是否合法.刚写完程序的时候,主管看了看然后请求了一个url: www.example.com/?type=6&score=1,本来第六个礼包要扣除积分80个,结果现在只扣除1个积分用户就领取到了第六个礼包.然后要判断礼包只有六中type应该大于1,小于7.写成配置文件来判断如下:
/**
 * @brief 将礼包对应扣除的积分写成配置,不要相信用户提交的数据,
 * @return array();
 */
public function getTypes() {
    return array(
            11 => 5,    //礼包1对应扣除的积分5,同下
            12 => 10,
            13 => 20,
            14 => 30,
            15 => 50,
            16 => 80
    );
}
5.防止刷积分的处理:如果用机器大量快速请求,有可能出现服务器挂掉,且出现一个人领取多个礼包,因此将用户id:uid和礼包类型:type做唯一索引:UNIQUE KEY unique_uid_type (uid,type),
在memcache中存入一个1s过期的key,保证一个uid每秒只能领取一次礼包.
6.一些非实时的数据,采用数据查询时从缓存中读取,不存在再从数据库读取;增删改时清除该key的缓存.下次从数据库读取.结果由于只改变了查询的时候的缓存的key,没有改 增删改 数据时缓存的key,导致数据全部从数据库中读取..因此一定要保证,写入缓存和读取缓存的key保持一致.否则...
7.由于有很多页面,需要共享用户的信息,而用户信息需要经过处理,就把用户的信息放到了__construct()里面,也就是说所有的页面都会处理用户信息,结果有几个页面是不使用用户信息的...
8.命名的规范:对缓存命名时必须遵循团队规范,否则可能会和其他开发人员发生缓存key冲突.
9.特殊字符的编码处理:用户名是utf-8编码的,而程序中是gbk编码的,结果转码后发现,用户名凌乱了.处理办法:copy到txt文档中使用记事本打开,重新复制一遍,很多的用户名中会出现特殊字符比如双引号等,使用:htmlspecialchars();
10.数据表中增加一个create_time,用来记录时间排除bug
11.缓存设置的时间要根据具体的业务逻辑来设计,比如下面的场景:用户投票和兑换积分一般时间不会超过十分钟,因此,一些非实时的缓存的过期时间设置成10分钟就够了
12.将所有权限的检测封装到一个函数中统一处理,是代码更简洁方便
13.json_encode()不能处理gbk编码:json_encode('gbk','utf-8',$array);

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts