1、问题描述
1.起源。我在作一个在线考试系统的项目中,但愿用户回答的每一道题都有相应的记录:一是记录每道题的正确、错误的次数;二是记录用户每一个错题,用来造成用户的错题集。
2.实现。因为考试的试卷中有不定数量的试题,因此我使用循环在判断用户回答是否正确,并在循环中记录数据,并写入数据库。
3.瓶颈。因为每提交一个答卷,就会产生数十次或更多的数据库写入或更新的操做,所以会耗费大量的时间。
2、问题分析php
1.因为每道题都要被记录两次:一次是更新试题对错的次数,二次是记录到错题集中,若是每一个试卷有20道试题,那么每一个答卷就要进行40次数据库操做,致使数据库压力很大。
2.因为每一个试题都是不一样的数据库记录,所以难以批量更新(有的是新增记录,有的是更新记录)。
3.若是大量用户并发提交,那么服务器就可能崩溃,速度缓慢。虽然个人小网站平时没有那么多人来光顾,但我本身开发的系统总不能最终成为不可用的废品吧,因此必须优化下!
3、解决思路和方案mysql
1.由于有大量数据库操做,因此我首先考虑到的是使用redis来提高性能。
2.因为更新数据的操做既有新增记录,又有更新记录,因此必须把更新操做和操做从逻辑上分离出来。
3.我使用的是thinkphp框架开发,模型层自带批量新增的函数addAll(),所以,新增数据的操做就用它解决了;而后经过网上搜索或本身编写一个函数,拼装批量更新sql语句,形如以下的语句结构:
UPDATEcategories SET
display_order = CASE id
WHEN 1 THEN 3
WHEN 2 THEN 4
WHEN 3 THEN 5
END,
title = CASE id
WHEN 1 THEN 'New Title 1'
WHEN 2 THEN 'New Title 2'
WHEN 3 THEN 'New Title 3'
END
WHERE id IN(1,2,3)redis
4.经过redis的hash表来记录要更新或新增的数据,在适当的时机,触发数据库操做,经过php操做redis的数据,执行成功后就删除那些redis数据,从而解决瓶颈问题。sql
附:如下是对试题对错记录的优化,对于用于错题集的优化代码相似,所以只展现前者的代码了。
5.redis记录过程:
$check ? $field = 'r' : $field = 'w';//检查对错
$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
$redis->hIncrBy(‘qid_check_log’,'qid.'.$qid.'.'.$field,1); //键值累加1thinkphp
6.把redis数据转存到mysql中:
$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
$data_cache = $redis -> hGetAll(‘qid_check_log’);
$temp = array(); //待更新的数据
$i = 0;数据库
//要增长的数据 foreach($data_cache as $key=>$num){ $arr = explode('.',$key); $qid = $arr[1]; $field = $arr[2]; $temp[$i]['qid'] = $qid; $ids[] = $qid;//须要更新的试题id $temp[$i][$field] = $num; $redis -> hDel($qid_check_log,$key); $i++; } if(empty($ids)) return true;//若是没有更新,则直接返回 //获取原来的数据 $map['qid'] = array('in',$ids); $old_data = M('questionlog') -> where($map) -> select(); $old_data2 = array(); foreach($old_data as $one){ $old_data2[$one['qid']] = $one; } unset($old_data); //合并数据 foreach($temp as &$one){ if(isset($one['r'])){ $one['r'] = $old_data2[$one['qid']]['r'] + $one['r']; }else{ $one['r'] = $old_data2[$one['qid']]['r']; } if(isset($one['w'])){ $one['w'] = $old_data2[$one['qid']]['w'] + $one['w']; }else{ $one['w'] = $old_data2[$one['qid']]['w']; } } $re = batch_update('questionlog',$temp,'qid');//执行批量更新 后记:其实当初我在编写程序的时候没有设计好,若是设计得合理的化,也许不须要redis也能完成这个优化,不过,也正好由于这个机会,让我在项目中真正用到了redis,感觉到了它的速度优点,哈哈!