以往至今的PHP筆試題和麵試題
echo、print、print_r、var_dump的區別
print、echo:是語言結構,只能輸出簡單型別的值(int,string),它們在輸出陣列時提示Notice錯誤,輸出物件時提示Catchable fatal error。而這二者的區別是echo可以可多參,print只單參
print_r、var_dump:是函式,可用於列印陣列和物件,就像echo和print_r的升級版,而其二者的區別,print_r只單參,簡單列印,var_dump可多參,連資料型別也列印
strlen和mb_strlen的區別
echo mb_strlen('W.X 你好啊'); // 7 = 4 + 1*3 echo mb_strlen('W.X 你好啊', 'gbk'); // 9 約等於 4 + 1.5*3 echo strlen('W.X 你好啊'); // 13 = 4 + 3*3 /** * 可以看出: * mb_strlen預設utf-8計算,中文字元=1位元組,若為gbk計算,中文字元=1.5位元組 * strlen 把中文字元當作3位元組 * mb_strlen是屬於MBString擴充套件的一個函式,而strlen是php核心函式 */
PHP單引號和雙引號的區別
$one = 1; $two = 2; class A{ protected $name; public function __construct() { $this->name = 'X.W.X'; } public function __toString()// 不使用這魔術方法時,print_r會報錯 { return strval($this->name); } } $a = new A(); print_r('$one"$two"'.PHP_EOL); // 單引號不會把$one和$two解析成變數,注意:為啥單引號中的雙引號不解析 print_r("自動解析'$one''{$two}'{$two }{ $two}{ $two }{$a}"); // 雙引號自動解析變數$one和$two /* result: $one"$two" 自動解析'1''2'2{ 2}{ 2 }X.W.X */ /* 可以推出: 1.雙引號解釋變數,單引號不解釋變數 2.單引號比雙引號效率高,所以儘量使用單引號 3.雙引號可放單引號,單引號也可放雙引號,但巢狀之後的引號不再具備之前的功能 */
GET和POST提交方式的區別
- GET請求會被瀏覽器主動cache,所以可回退;而POST不會,除非手動設定,所以回退時POST會再次提交請求
- GET請求只能進行url編碼,而POST支援多種編碼方式
- GET比POST更不安全,因為引數直接暴露在URL上,所以不能用來傳遞敏感資訊
include和require的區別
都是匯入檔案,都有返回值。
有無_once的區別?
- 當重複執行require_once一個檔案時,但需要注意的是返回值再第一次require_once之後,都為true,require的返回值會是相同的。所以require執行一次
- require比require_once效率高,因此開發之初,儘量不要使用_once的情況
<>php
class Test1
{
public function __construct()
{
echo __CLASS__.PHP_EOL;
}
}
return [1,2,3];
<?php
$a = include_once './test1/Test1.php';
$b = include_once './test1/Test1.php';
var_dump($a);
var_dump($b);
/*
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
bool(true)
*/
但require(需要)當檔案不存在或者無法開啟的時候,會報E_ERROR,停止指令碼
include(包含)當檔案不存在或者無法開啟的時候,也會E_WARNING,但不會停止指令碼
AJAX的優勢是什麼?
頁面區域性重新整理,減輕伺服器壓力
SESSION與COOKIE的區別?
- 儲存位置:session儲存在伺服器,cookie儲存在瀏覽器
- 安全性:session比cooike安全
PHP錯誤和異常的區別?
其它語言一般只剩下異常,無錯誤和異常之分,php有錯誤和異常之分
錯誤:不會被catch捕捉到,不過php提供產生錯誤之後的鉤子:set_error_handler($err_type),需要注意的是這個函式還是捕捉不到 E_ERROR
、 E_PARSE
、 E_CORE_ERROR
、 E_CORE_WARNING
、 E_COMPILE_ERROR
、 E_COMPILE_WARNING
等級別的
錯誤,但可使用三種方法
方法一:@mysql_connect(...) or die("Database Connect Error")。
方法二:編輯php.ini ,查詢"display_errors =" ,將“=”後面的值改為"off。
方法三:在php指令碼前加error_reporting(0),遮蔽所有錯誤提示。
去遮蔽E_ERROR等級別的錯誤,但需要注意的是若使用了set_error_handler函式代理錯誤會使@魔法糖失效
異常:可手動丟擲異常,若丟擲不主動catch,則會fatal錯誤,停止指令碼,若丟擲主動catch,則是異常,可通過set_exception_handler代理所有異常
二者語法:
mixed set_error_handler ( callable $error_handler
[, int $error_types
= E_ALL | E_STRICT ] )
bool error_handler ( int $errno , string $errstr [, string $errfile [, int $errline ]] )
第一個引數 errno,包含了錯誤的級別,是一個 integer。
第二個引數 errstr,包含了錯誤的資訊,是一個 string。
第三個引數是可選的,errfile, 包含了發生錯誤的檔名,是一個 string。
第四個引數是一個可選項, errline, 包含了錯誤發生的行號,是一個 integer。
callable set_exception_handler (callable $exception_handler
)
<?php
//自定義IO異常
class IoException extends Exception {
public function __construct($message, $code=0, Exception $previous=null)
{
parent::__construct($message, $code, $previous);
}
}
function exception_handler($e){
echo "異常資訊如下<br/>";
echo $e->getMessage();
}
set_exception_handler("exception_handler");
if(!function_exists("write")){
//丟擲自定義IO異常 若無exception_handler代理異常,會報fatal錯,停止指令碼
throw new IoException("方法write()不存在");
}
PHP中的魔術常量、預定義常量和預定義變數
一、魔術常量
特點:它們的值會隨著它們在程式碼中的位置的改變而改變。這些特殊的常量不區分大小寫
__LINE__ :返回檔案中的當前行號。也可寫成__line__。
__FILE__:返回當前檔案的絕對路徑(包含檔名)。
__DIR__:返回當前檔案的絕對路徑(不包含檔名),等價於 dirname(__FILE__)。
__FUNCTION__:返回當前函式(或方法)的名稱。
__CLASS__:返回當前的類名(包括該類的作用區域或名稱空間)。
__TRAIT__:返回當前的trait名稱(包括該trait的作用區域或名稱空間)。
__METHOD__:返回當前的方法名(包括類名)。
__NAMESPACE__:返回當前檔案的名稱空間的名稱。
二、魔術方法
點選此連結:魔術方法
三、預定義常量
特點:已經在PHP的核心中就定義好了的常量。區分大小寫。
PHP_VERSION:返回PHP的版本。
PHP_OS:返回執行PHP直譯器的作業系統名稱。
PHP_EOL:系統換行符,Windows是(\r\n),Linux是(\n),MAC是(\r)
四、預定義變數
特點:都是陣列型別,是對於全部指令碼而言的(全部指令碼都能使用的環境變數)
$GLOBALS:global全域性變數,是一個包含了所有全域性變數的組合陣列,全域性變數的名稱就是該組合陣列的鍵。
$_GET:HTTP GET 變數,通過 URL 引數傳遞給當前指令碼的變數的陣列。
$_POST:HTTP POST 變數,通過 HTTP POST 方式傳遞給當前指令碼的變數的陣列。
$_COOKIE:HTTP Cookies 變數,通過 HTTP Cookies 方式傳遞給當前指令碼的變數的陣列。
$_SESSION:session 變數,當前指令碼可用的 SESSION 變數的陣列。
$_REQUEST:HTTP Request 變數,預設情況下包含了 $_GET,$_POST 和 $_COOKIE 的陣列。
$_FILES:HTTP 檔案上傳變數,通過 HTTP POST 方式上傳到當前指令碼的專案的陣列。
$_SERVER:伺服器資訊變數,包含了諸如頭資訊(header)、路徑(path)、以及指令碼位置(script locations)等資訊的陣列。這個陣列中的專案由 Web 伺服器建立。
$_ENV:環境變數,通過環境方式傳遞給當前指令碼的變數的陣列。
$php_errormsg:前一個錯誤資訊,$php_errormsg 變數包含由 PHP 生成的最新錯誤資訊。這個變數只在錯誤發生的作用域內可用,並且要求 track_errors 配置項是開啟的(預設是關閉的)。
$HTTP_RAW_POST_DATA:包含 POST 提交的原始資料。
$http_response_header:HTTP 響應頭,$http_response_header 陣列與 get_headers() 函式類似。當使用HTTP包裝器時,$http_response_header 將會被 HTTP 響應頭資訊填充。
$argc:傳遞給指令碼的引數數目,包含當運行於命令列下時傳遞給當前指令碼的引數的數目。指令碼的檔名總是作為引數傳遞給當前指令碼,因此 $argc 的最小值為 1,這個變數僅在 register_argc_argv 開啟時可用。
$argv:傳遞給指令碼的引數陣列,包含當運行於命令列下時傳遞給當前指令碼的引數的陣列。第一個引數總是當前指令碼的檔名,因此 $argv[0] 就是指令碼檔名,這個變數僅在 register_argc_argv 開啟時可用。
特別注意:
php 4.2.0 以及後續版本中,php 指令 register_globals 的預設值為 off。這是 php 的一個主要變化。
讓 register_globals 的值為 off 將影響到預定義變數集在全域性範圍內的有效性。例如,為了得到 DOCUMENT_ROOT 的值,
將必須使用 $_SERVER['DOCUMENT_ROOT'] 代替 $DOCUMENT_ROOT,又如,
使用 $_GET['id'] 來代替 $id 從中獲取 id 值,亦或使用 $_ENV['HOME'] 來代替 $HOME 獲取環境變數 HOME 的值。
1,1,2,3,5,8,13,21...,求出第n個數
// 1,1,2,3,5,8,13,21...,求出第n個數
// 採用 for控制迴圈
/**
* @param int $n 輸入迴圈次數
*
* @return int 返回值
*
*/
function _add($n){
$pre = 0;
$next = 1;
$tmp = null;
for($i=0; $i<$n; $i++){
$tmp = $pre + $next;
$pre = $next;
$next = $tmp;
}
return $pre;
}
// 採用 遞迴控制迴圈
/**
* @param int $pre 輸入前一個數
* @param int $next 輸入後一個數
* @param int $n 輸入迴圈次數
*
* @return int 返回值
*
*/
function rec($pre, $next, $n){
// 使用遞迴,必須有退出條件,不然就死迴圈
if($n<=0){
return $pre;
}
$n--;
return rec($next, $pre+$next, $n);
}
echo _add(7); // 13
echo rec(0,1,7); // 13
// 可以看出
// for迴圈和遞迴迴圈的區別
// for迴圈: 使用臨時值$tmp,來記錄每一次和的值
// 遞迴迴圈: 使用兩個輸入引數,來記錄每一次和的值
介面類和抽象類的區別
二者差異:
// 介面類使用interface class_name定義,而抽象類使用abstract class class_name定義
// 介面類使用implements繼承,可以多繼承interface類,而抽象類是使用extends繼承,只能單繼承抽象類
// 介面類預設都是abstract,純粹是介面,且不能自己實現方法,而抽象類都不一定
遍歷出某資料夾下的資料夾和檔案
// 使用了四個函式
bool is_dir(string $dir_name)
resource||false opendir(string $dirname) // resource代表檔案控制代碼
string||false readdir(resource $dir_handler)
bool is_file(string $file_name)
// 實現方法
$dir = 'D:\Temp';
function getAll($dir){
$allFile = [];
if (is_dir($dir)) {
$dir_h = opendir($dir);
if ($dir_h) {
// 萬一返回 目錄名為 '0',所以採用 !==
while (false !== ($row = readdir($dir_h))){
if ($row == '.' || $row == '..'){
continue;
}
if (is_file($dir . '/' . $row)){
$allFile[] = $row;
}
elseif (is_dir($dir . '/' . $row)){
$allFile[$row] = getAll($dir . '/' . $row);
}
}
closedir($dir_h);
}
}
return $allFile;
}
var_dump(getAll($dir));
防止SQL注入的函式
// 注意:本擴充套件自 PHP 5.5.0 起已廢棄,並在自 PHP 7.0.0 開始被移除。應使用 MySQLi 或 PDO_MySQL 擴充套件來替換之
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] )
// mysqli擴充套件的函式
string mysqli_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] )
解決多程序讀寫一個檔案的方法
// 使用了fopen、flock、fwrite、fclose四個函式
function putFile($file,$mode="w"){
$file = fopen($file,$mode);
if(flock($file,LOCK_EX)){
fwrite($file,'write a word');
flock($file,LOCK_UN);
}else{
echo "無法訪問";
}
fclose($file);
}
while,外迴圈是釋放不了內迴圈
$s_ = 0;
$e_ = 2;
while ($s_ < $e_){
while (true){
$s_++;
echo '死迴圈';
}
}
測試程式碼塊執行時間
string|float microtime ([ bool $get_as_float ] ); //返回當前 Unix 時間戳的微秒數
string number_format ( float $number , int $decimals = 0 , string $dec_point = "." , string $thousands_sep = "," ); //通過千位分組來格式化數字
mixed call_user_func_array ( callback $funcname , array $param_arr ); //動態呼叫函式
Class Debug
{
// 記錄時間點
protected static $info;
/**
* @param string $name 標記時間名
* @param int $overide 是否重寫標記時間名
*/
public static function remark($name='start', $overide=0){
if ($overide){
static::$info[$name] = microtime(true);
}
else{
if (!isset(static::$info[$name])){
static::$info[$name] = microtime(true);
}
else {
echo '您輸入的標記時間名已經存在';
}
}
}
/**
* @param string $start_name 起始標記時間名
* @param string $end_name 結束標記時間名
* @param int $dec 數字格式化,保留兩個小數點
*
* @return int|string 返回時間差
*/
public static function getRangeTime($start_name, $end_name, $dec = 2){
if (!isset(static::$info[$start_name])){
echo '輸入的起始時間名不存在';
return -1;
}
elseif (!isset(static::$info[$end_name]))
{
echo '自動幫您以當前時間戳作為您的結束時間名';
static::$info[$end_name]=microtime(true);
}
return number_format(static::$info[$end_name]-static::$info[$start_name], $dec);
}
/**
* 計算某函式使用時間
* @param callback(string|array) $function [空間名+類名+]函式名
* @param array $arr 傳引數組
*/
public static function countClassTime($function, $arr){
Debug::remark('start',1);
call_user_func_array($function, $arr);
Debug::remark('end',1);
echo Debug::getRangeTime('start', 'end').PHP_EOL;
}
}
PHP獲取真實IP
/**
* 獲取客戶端IP地址
* @param integer $type 返回型別 0 返回IP地址 1 返回IPV4地址數字
* @param boolean $adv 是否進行高階模式獲取(有可能被偽裝)
* @return mixed
*/
public function ip($type = 0, $adv = true)
{
$type = $type ? 1 : 0;
static $ip = null; // 為什麼ip變數要使用static?防止一個請求多次執行$httpAgentIp以下程式碼
if (null !== $ip) {
return $ip[$type];
}
// 是否使用自己配置的代理ip
$httpAgentIp = Config::get('http_agent_ip');
if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
$ip = $_SERVER[$httpAgentIp];
} elseif ($adv) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) {
unset($arr[$pos]);
}
$ip = trim(current($arr));
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
// 為什麼使用$_SERVER['REMOTE_ADDR'],而不使用getenv('REMOTE_ADDR')
// getenv在一些web伺服器下是不支援,例如iis伺服器
$ip = $_SERVER['REMOTE_ADDR'];
}
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// IP地址合法驗證
$long = sprintf("%u", ip2long($ip));
$ip = $long ? [$ip, $long] : ['0.0.0.0', 0];
return $ip[$type];
}
談談資料庫的事務?
事務,為了高併發導致資料不一致而生,
SELECT @@TRANSACTION_ISOLATION; // 檢視事務隔離級別
SELECT @@AUTOCOMMIT; // 檢視語句是否被自動提交
1、事務四大特性:ACID
原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、永續性(Durability)
2、隔離性的四個等級(重點)
高併發的情況下會主要產生以下情況:
①、髒讀(未提交讀):又稱無效資料的讀出,一個事務讀取到了另一個事務未提交的資料操作結果。若依據髒資料update、insert、delete,這是相當危險的,因為很可能所有的操作都被回滾。
假設在秒殺活動中,某一商品的庫存數量剩100?
事務A 事務B
select查詢 庫存=100
update更新 庫存=99
select查詢 庫存=100
事務回滾庫存=100
事務結束庫存=100
②、更新丟失:兩個事務都同時更新一行資料,一個事務對資料的更新把另一個事務對資料的更新覆蓋了。
第一類丟失更新 (通過設定事務隔離級別為 Repeatable Read可以防止)
時間 |
取款事務A |
轉賬事務B |
T1 |
開始事務 |
|
T2 |
|
開始事務 |
T3 |
查詢賬戶餘額為1000元 |
|
T4 |
|
查詢賬戶餘額為1000元 |
T5 |
|
匯入100元把餘額改為1100元 |
T6 |
|
提交事務 |
T7 |
取出100元把餘額改為900元 |
|
T8 |
撤銷事務 |
|
T9 |
餘額恢復為1000 元(丟失更新) |
|
A事務在撤銷時,把1100的資料替換成1000,導致銀行把匯入的100抹去,進而導致使用者虧了100
第二類丟失更新 (需要應用程式控制,樂觀鎖)
時間 |
轉賬事務A |
取款事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
|
查詢賬戶餘額為1000元 |
T4 |
查詢賬戶餘額為1000元 |
|
T5 |
|
取出100元把餘額改為900元 |
T6 |
|
提交事務 |
T7 |
匯入100元 |
|
T8 |
提交事務 |
|
T9 |
把餘額改為1100 元(丟失更新) |
|
A事務提交在提交時,把900替換成1100,導致銀行把取出的100抹去,進而導致銀行虧了100
二者的區別:第一類丟失更新是回滾覆蓋丟失,第二類丟失更新是提交覆蓋丟失
③、不可重複讀:一個事務對同一行資料重複讀取兩次,但是卻得到了不同的結果。包括以下情況:
- 虛讀:事務T1讀取某一資料後,事務T2對其做了修改,當事務T1再次讀該資料時得到與前一次不同的值。
- 幻讀(Phantom Reads):事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的資料或者缺少了第一次查詢中出現的資料(這裡並不要求兩次查詢的sql語句相同)。這是因為在兩次查詢過程中有另外一個事務插入資料造成的。
- 虛讀和幻讀的差異:一個update、一個insert、delete
為了避免上面出現的幾種情況,在標準SQL規範中,定義了4個事務隔離級別,不同的隔離級別對事務的處理不同。
未授權讀取
也稱為讀未提交(Read Uncommitted):允許髒讀取,但不允許更新丟失。如果一個事務已經開始寫資料,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行資料。該隔離級別可以通過“排他寫鎖”實現。
授權讀取
也稱為讀提交(Read Committed):允許不可重複讀取,但不允許髒讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。
可重複讀取(Repeatable Read)<==>mysql的預設隔離級別
禁止不可重複讀和髒讀取,但是有時可能出現幻讀資料。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
序列化(Serializable)
提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,不能併發執行。僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。
隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。對於多數應用程式,可以優先考慮把資料庫系統的隔離級別設為Read Committed。它能夠避免髒讀取,而且具有較好的併發效能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。
三、樂觀鎖、悲觀鎖、行鎖、表鎖
1、樂觀鎖
樂觀鎖:顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新提交的時候才會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。
實現方式:多增加個欄位version,代表當前資料版本,當寫(增、刪、改)表的時候,必須在原來資料版本上自增1;
2、悲觀鎖
悲觀鎖:顧名思義,就是很悲觀,總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖。
innodb有行鎖、表鎖,myisam只有表鎖
①、行鎖
- 行鎖又分排它鎖(事務A對某一行資料上鎖,事務A可進行讀、寫,但其它事務不可對那一行資料進行讀、寫)和共享鎖(事務A對某一行資料上鎖,事務A可進行讀、寫,其它事務仍可對那一行可讀、但不可寫)
- 加排他鎖可以使用select ...for update語句,加共享鎖可以使用select ... lock in share mode語句
②、表鎖
- 有索引的行鎖就是行鎖,沒有索引的行鎖就是表鎖
主鍵、外來鍵和索引的區別?
一、三者區別
- 作用
主鍵--用來保證資料完整性,能夠唯一標識表中某一行資料
外來鍵--用來和其他表建立聯絡用的(一般不使用用、廢棄狀態)
索引--是提高查詢排序的速度 - 個數
主鍵--主鍵只能有一個,且是一種特殊的唯一索引,不允許有空值
外來鍵--一個表可以有多個外來鍵
索引--一個表可以有多個唯一索引
二、索引的分類
1、從資料結構角度
1、B+樹索引(O(log(n))):關於B+樹索引,可以參考 MySQL索引背後的資料結構及演算法原理
2、hash索引:
- 僅僅能滿足"=","IN"和"<=>"查詢,不能使用範圍查詢
- 其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引
- 只有Memory儲存引擎顯示支援hash索引
3、FULLTEXT索引(現在MyISAM和InnoDB引擎都支援了)
4、R-Tree索引(用於對GIS資料型別建立SPATIAL索引)
2、從物理儲存角度
- 聚集索引(clustered index)
- 非聚集索引(non-clustered index)
3、從邏輯角度
- 主鍵索引:主鍵索引是一種特殊的唯一索引,不允許有空值
- 普通索引或者單列索引
- 多列索引(複合索引):複合索引指多個欄位上建立的索引,只有在查詢條件中使用了建立索引時的第一個欄位,索引才會被使用。使用複合索引時遵循最左字首集合
- 唯一索引或者非唯一索引
- 空間索引(patical):空間索引是對空間資料型別的欄位建立的索引,MYSQL中的空間資料型別有4種,分別是GEOMETRY、POINT、LINESTRING、POLYGON。
- 全文索引:僅可用於 MyISAM 表,針對較大的資料,生成全文索引很耗時好空間
MySQL儲存引擎的選擇?
點選連結:Mysql資料庫儲存引擎
堆記憶體和棧記憶體的區別?
棧是編譯期間就分配好的記憶體空間,因此你的程式碼中必須就棧的大小有明確的定義;
堆是程式執行期間動態分配的記憶體空間,你可以根據程式的執行情況確定要分配的堆記憶體的大小,也就是new的時候。
redis是單執行緒的麼,為什麼?
是啊!Redis屬於記憶體資料庫,程序廢記憶體,多執行緒不也廢記憶體,這樣cpu才不會成為負擔
Redis和Memcached的區別?
- Redis和Memcache都是將資料存放在記憶體中,都是記憶體資料庫。但是Memcache還可以快取其他東西,比如圖片、視訊
- Redis不只支援簡單的k/v型別的資料,同時還提供list、set、hash等資料結構的儲存
- 虛擬記憶體,當實體記憶體用完時Redis可以將一些很久沒有用到的value交換到磁碟
- 過期策略,memcache在set時就指定,例如
set key1 0 0 8
即永不過期,redis可以通過expire設定,例如:expire name 10
- 分散式,設定memcache叢集,利用magent做一主多從;redis也可以做一主多從。
- 儲存安全,memcache掛掉後,資料沒了;redis可以定期儲存在磁碟(持久化)
- 災難恢復,memcache掛掉後資料不可恢復;redis資料丟失後可以通過aof恢復
- redis支援資料的備份,即master-slave模式的資料備份
- 應用場景不同:redis除了可以做nosql資料庫之外,還能做訊息佇列、資料堆疊和資料快取等。memcache適合於快取sql語句、資料集、使用者臨時性資料、延遲查詢資料和session等
redis有哪些資料結構?
string、hash、list、set等
-
String
字串型別是redis最基礎的資料結構,首先鍵是字串型別,而且其他幾種結構都是在字串型別基礎上構建的
字串型別實際上可以是字串、數字、二進位制(圖片、音訊),單最大不能超過512M
使用場景:- 快取
字串最經典的使用場景,redis作為快取層,mysql作為儲存層,絕大部分請求資料都是redis中獲取,由於redis具有支撐高併發特性,所以快取通常能起到加速讀寫和降低後端壓力的作用 - 計數器
許多應用都會使用redis作為技術的基礎工具,它可以實現快速技術、查詢快取的功能。 - 共享session
處於負載均衡的考慮,分散式服務會將使用者資訊的訪問均衡到不同伺服器,使用者重新整理一次訪問可訥訥個會需要重新登入,為了避免這個問題可以使用redis將使用者session集中管理,在這種模式下只要保證redis的高可用和擴充套件性,每次獲取使用者更新或查詢登入資訊都直接從redis中集中獲取 - 限速
出於安全考慮,每次進行登入時讓使用者輸入手機驗證碼,為了簡訊介面不被頻繁訪問,會限制使用者每分鐘獲取驗證碼的頻率
- 快取
-
Hash
在redis中雜湊型別是指鍵本身又是一種鍵值對結構,如value = {{field1,value1}...{fieldn,valuen}}
使用場景:- 雜湊結構相對於字串序列化快取資訊更加直觀,並且在更新操作上更加便捷。
-
list
列表型別是用來儲存多個有序的字串,列表的每個字串成為一個元素,一個列表最多可以儲存2的32次方減1個元素。在redis中,可以對列表插入(push)和彈出(pop),還可以獲取指定範圍的元素列表。列表是一種比較靈活的資料結構,它可以充當棧和佇列的角色。
使用場景:- 訊息佇列
redis的lpush+brpop
命令組合就可以實現阻塞佇列,生產者客戶端是用lpush
從列表左側插入元素,多個消費者客戶端使用brpop
命令阻塞式的搶列表尾部的元素,多個客戶端保證了消費的負載均衡的高可用性。 -
使用技巧列表
rpush+rpop = lpush+lpop = Stack(棧) 先進後出 rpush+lpop = lpush+rpop = Queue(佇列) 先進先出 lpush+ltrim=Capped Collection(有限集合) rpush+blpop = lpush+brpop=Message Queue(訊息佇列)
- 訊息佇列
- set
- sortedset