交代背景
在学习redis的时候,主要是学会如何使用缓存,相应的问题也来了,无论是什么样的代码都会有崩的时候,这一次我们来模拟一下redis穿透。
redis穿透
redis穿透:突然大量访问缓存中不存在的key,直接去访问数据库,给数据库造成了极大的压力。
思路
因为访问的是缓存中不存在的key,那么就让这个缓存没有key好了,直接去数据库查!
实现的代码部分
require_once 'redis.php';
require_once 'model.php';
/*
*实现redis的穿透
*突然大量访问缓存中不存在的key,直接去访问数据库,给数据库造成了极大的压力(穿透)
*
*实现方式:缓存是空的,访问的时候查DB存缓存。
*
*/
function through(){
$rconn = redisFunction::getIntance();
$keyName = '666';
$rconn ->deleteString($keyName);
$result = $rconn ->getString($keyName);
if(!$result){
echo "缓存中没有这个key,电脑马上去数据库中取";
return "对应的key值:".selectDB($keyName);
}else{
return "对应的key值:".$result;
}
}
/*
*去数据库中寻找键对应的值
*/
function selectDB($keyName){
$rconn = redisFunction::getIntance();
$sql = "SELECT tit.ccontent,art.tcontent from t_title tit LEFT JOIN t_article art on art.ititleid = tit.id where tit.ccontent = '$keyName';";
$result = PDODB::row($sql);
//print_r($result);//Array ( [ccontent] => 第二章 [tcontent] => 地下城英雄们的传说 )
$valName = $result["tcontent"];
$rconn ->setString($keyName,$valName);
return $rconn ->getString($keyName);
}
echo through();
图片
连接mysql是单线程的,并发的时候占用资源太多是会拒绝连接的,然后就报错了。
小编使用fiddler模拟了5万条访问,其中即便是200也有好多没有返回结果,是报错信息
这个才是正确的结果
redis封装方法
<?php
/*
*这是一个关于redis的类文件夹
*/
class redisFunction{
private static $rconn = false;
private $redis;
//构造方法
function __construct(){
$this->testConn();
}
/*
*php连接redis
*
*/
private function testConn(){
$this->redis = new Redis();
$this->redis->connect('127.0.0.1',6379);
// echo "Connection to server Successful!";
// echo "Server is running".$this->redis->ping();
}
/*
*公用的静态方法
*/
public static function getIntance(){
if(self::$rconn==false){
self::$rconn=new self;
}
return self::$rconn;
}
/*
*返回当前的DB中key的数量
*/
public function getNumber(){
$count = $this->redis->dbSize();
return $count;
}
/*
*String类型(一个key对应一个value)
*存值
*取值
*/
public function setString($keyName,$valName){
$this->redis->set($keyName,$valName);
$this->redis->setTimeout($keyName, 3600); // x will disappear in 1 hour.
}
public function getString($keyName){
$valName = $this->redis->get($keyName);
return $valName;
}
/*
*如果没有就SET,有就返回False
*/
public function setnxString($keyName,$valName){
return $this->redis->setnx($keyName,$valName);
}
/*
*可以接受数组
*删除存在的key,返回剩余key的数量
*/
public function deleteString($keyName){
$count = $this->redis->delete($keyName);
return $count;
}
/*
*验证指定的一个key是否存在
*存在返回true,不存在返回false
*/
public function existsString($keyName){
$result = $this->redis->exists($keyName);
return $result;
}
/*
*默认给key加1
*也可以指定加几
*/
public function incrString($keyName,$num=1){
if($num == 1){
$this->redis->incr($keyName);
}else{
$this->redis->incrBy($keyName,$num);
}
}
/*
*默认给key减1
*也可以指定减几
*
*/
public function decrString($keyName,$num=1){
if($num == 1){
$this->redis->decr($keyName);
}else{
$this->redis->decrBy($keyName,$num);
}
}
/*
*返回字符串的长度
*
*/
public function strlenString($keyName){
$count = $this->redis->strlen($keyName);
return $count;
}
/*
*list类型函数
*(一个key对应多个value,可视为是一个栈)
*/
/*
*把值插到顶部(左侧)
*失败返回false
*
*/
public function lPushList($keyName,$member){
$result = $this->redis->lPush($keyName,$member);
return $result;
}
/*
*添加一个字符串值到LIST容器的底部(右侧)
*失败返回false
*
*/
public function rPushList($keyName,$member){
$result = $this->redis->rPush($keyName,$member);
return $result;
}
/*
*返回LIST顶部(左侧)的VALUE
*
*/
public function lPopList($keyName){
$result = $this->redis->lPop($keyName);
return $result;
}
/*
*返回LIST顶部(右侧)的VALUE
*
*/
public function rPopList($keyName){
$result = $this->redis->rPop($keyName);
return $result;
}
/*
*根据KEY返回该KEY代表的LIST的长度,
*如果这个LIST不存在或者为空,那么ISIZE返回0,如果指定的KEY的数据类型不是LIST或者不为空,那么返回FALSE。
*所以在这里多说一句,当用ISize返回判断值的时候,===就有用处了,这里FLASE和0是两个概念了。
*/
public function lSizeList($keyName){
$result = $this->redis->lSize($keyName);
return $result;
}
/*
*set类型
*一个key对用多个value,可视为一个容器
*/
/*
*添加一个VALUE到SET容器中,如果这个VALUE已经存在于SET中,那么返回FLASE
*具有唯一性
*
*/
public function sAddSet($key,$member){
$result = $this->redis->sAdd($key,$member);
return $result;
}
/*
*移除指定的VALUE从SET容器中
*
*/
public function sRemSet($key,$member){
$result = $this->redis->sRem($key,$member);
return $result;
}
/*
*检查VALUE是否是SET容器中的成员。
*存在返回true,否则返回false
*
*/
public function sIsMemberSet($key,$member){
$result = $this->redis->sIsMember($key,$member);
return $result;
}
/*
*返回SET容器的成员数
*
*/
public function sCardSet($key){
$result = $this->redis->sCard($key);
return $result;
}
/*
*返回SET集合中的所有元素。
*
*/
public function sMembersSet($key){
$result = $this->redis->sMembers($key);
return $result;
}
/*
*zSet有序集合
*具有唯一性,视为一个有序的容器
*/
/*
*存值
*
*/
public function zAddZset($key,$value1,$value2){
$this->redis->zAdd($key,$value1,$value2);
}
/*
*取得特定范围内的排序元素,0代表第一个元素,1代表第二个以此类推。-1代表最后一个,-2代表倒数第二个
*
*
*/
public function zRangeZSet($key,$num1=0,$num2=1,$value = true){
if($value == true){
$result = $this->redis->zRange($key, $num1, $num2,true); //返回$value1,$value2
}else{
$result = $this->redis->zRange($key, $num1, $num2); //返回$value2
}
return $result;
}
/*
*从有序集合中删除指定的成员。
*
*
*/
public function zDeleteZSet($key,$member){
$this->redis->zDelete($key,$member);
}
/*
*返回存储在key对应的有序集合中的元素的个数。
*
*
*/
public function zSizeZSet($key){
$result = $this->redis->zSize($key);
return $result;
}
/*
*hash类型
*
*/
public function hSetHash($key1,$key2,$value){
$this->redis->hSet($key1,$key2,$value); //hSet('h', 'key1', 'hello');
}
/*
*添加一个VALUE到HASH STORE中,如果FIELD不存在
*
*/
public function hSetNxHash($key1,$key2,$value){
$this->redis->hSetNx($key1,$key2,$value);
}
/*
*取得HASH中的VALUE,如何HASH不存在,或者KEY不存在返回FLASE
*
*/
public function hGetHash($key1,$key2){
$result = $this->redis->hGet($key1,$key2);
return $result;
}
/*
*取得HASH表的长度
*/
public function hLenHash($key1){
$result = $this->redis->hLen($key1);
return $result;
}
/*
*指定删除
*/
public function deleteHash($key1){
$this->redis->delete($key1);
}
/*
*取得HASH表中的KEYS,以数组形式返回
*
*/
public function hKeysHash($key1){
$result = $this->redis->hKeys($key1);
return $result;
}
/*
*取得HASH表中所有的VALUE,以数组形式返回
*
*/
public function hValsHash($key1){
$result = $this->redis->hVals($key1);
return $result;
}
/*
*取得整个HASH表的信息,返回一个以KEY为索引VALUE为内容的数组
*/
public function hGetAllHash($key1){
$result = $this->redis->hGetAll($key1);
return $result;
}
/*
*验证HASH表中是否存在指定的KEY-VALUE,存在返回true
*/
public function hExistsHash($key1,$key2){
$result = $this->redis->hExists($key1,$key2);
return $result;
}
/*
*批量取得HASH表中的VALUE
*
*/
public function hmGetHash($key1,$key2){
$result = $this->redis->hmGet($key1,$key2);
return $result;
}
}
?>
mysql封装方法
<?php
//这是小编在网络上寻得某位大神的作品,暂时借用一下!
class PDODB {
private static $_instance;
private static $conn = null;
private function __construct()
{
//链接数据库
self::getConn();
}
//链接数据库
private static function getConn(){
if(self::$conn === null){
self::$conn = new PDO("mysql:host=localhost:3306;dbname=dbcanteen","root","root");
// self::$conn = new PDO("mysql:host=127.0.0.1;Database=SiteMIS","sa","1211");
//设置编码
self::$conn->exec('set names utf8');
}
return self::$conn;
}
//单例模式
private static function getInstance()
{
if(! (self::$_instance instanceof self) ) {
self::$_instance = new self();
}
return self::$_instance;
}
//查询多条语句的封装函数
public static function getMore($field = '*', $table, $where = 1, $order = 'order by id asc', $offset = 0, $perpage = 1000000) {
self::getInstance();
$sql = "select $field from `$table` where $where $order limit $offset,$perpage";
$query = self::getConn()->query($sql); //执行上面的语句
$list = array(); //新建一个数组
while($row = $query->fetch(PDO::FETCH_ASSOC)) {//循环news表
$list[] = $row; //把执行出来的每一条内容赋值给list这个数组
}
return $list;
}
//获取列表
public static function select($sql) {
self::getInstance();
$query = self::getConn()->query($sql); //执行上面的语句
$list = array(); //新建一个数组
while ($row = $query) {//循环news表
$list[] = $row; //把执行出来的每一条内容赋值给list这个数组
}
return $list;
}
//获取列表
public static function query($sql) {
self::getInstance();
$query = self::getConn()->query($sql);
$row = $query->fetchAll(PDO::FETCH_ASSOC);
return $row;
}
//查询一条语句的封装函数
public static function getOne($field, $table, $where) {
self::getInstance();
$sql = "select $field from `$table` where $where";
//return $sql;
//die();
return self::getConn()->query($sql)->fetch(PDO::FETCH_ASSOC);
}
//查询一条记录
public static function row($sql) {
self::getInstance();
return self::getConn()->query($sql)->fetch(PDO::FETCH_ASSOC);
}
//增加一条语句的封装函数
//返回插入的ID
public static function set($table, $data) {
$str_key = '';
$str_val = '';
foreach ($data as $key => $val) {
$str_key.='`' . $key . '`,';
$str_val.="'" . $val . "',";
}
$str_key = substr($str_key, 0, -1);
$str_val = substr($str_val, 0, -1);
$sql = "insert into `$table` ($str_key) values($str_val)";
self::getInstance();
self::getConn()->exec($sql);
return self::getConn()->lastInsertId();
}
//修改一条语句的封装函数
//返回影响的行数
public static function update($table, $where, $date) {
$str_set = '';
foreach ($date as $key => $val) {
$str_set.="`" . $key . "`='" . $val . "',";
}
$str_set = substr($str_set, 0, -1);
$sql = "update `$table` set $str_set where $where";
self::getInstance();
return self::getConn()->exec($sql);
}
//删除一条语句的封装函数
//返回影响行数数
public static function delete($table, $where) {
$sql = "delete from `$table` where $where";
self::getInstance();
return self::getConn()->exec($sql);
}
//执行一条SQL语句
public static function setquery($sql) {
self::getInstance();
$query = self::getConn()->query($sql); //执行上面的语句
return $query;
}
}
?>
那大家知道windows下配置phpstudy的并发上线是多少吗?
大家一起来试一试!