28道PHP面試題,帶你梳理基礎知識!

2022-03-03 16:00:49
本篇文章給大家整理分享28道PHP面試題(附答案分享),帶你梳理基礎知識,有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

過完年之後打算尋找新的工作機會,發現之前自己對於很多基礎的面試理解和學習不夠深刻,為了鼓勵自己持續前進所以最近開始在論壇和搜尋引擎上開始學習和總結相關知識,其中有一些題目時論壇裡面一些前輩分享過的題目或者答案,還有一部分時自己最近面試遇到的問題,基於自己的理解和前輩們的分享歸檔了一部分,所以分享出來,希望對其他的小夥伴們也有幫助,同時也希望能收到大佬們對於理解有誤的地方的指導,最近一段時間會持續更新

一、php 陣列底層實現原理

1、底層實現是通過雜湊表(hash table) + 雙向連結串列(解決hash衝突)

  • hashtable:將不同的關鍵字(key)通過對映函數計算得到雜湊值(Bucket->h) 從而直接索引到對應的Bucket

  • hash表儲存當前迴圈的指標,所以foreach 比for更快

  • Bucket:儲存陣列元素的key和value,以及雜湊值h

2、如何保證有序性

  • 1. 雜湊函數和元素陣列(Bucket)中間新增一層大小和儲存元素陣列相同的對映表。

  • 2. 用於儲存元素在實際儲存陣列中的下標

  • 3. 元素按照對映表的先後順序插入實際儲存陣列中

  • 4. 對映表只是原理上的思路,實際上並不會有實際的對映表,而是初始化的時候分配Bucket記憶體的同時,還會分配相同數量的 uint32_t 大小的空間,然後將 arData 偏移到儲存元素陣列的位置。

3、解決hash重複(php使用的連結串列法):

  • 1. 連結串列法:不同關鍵字指向同一個單元時,使用連結串列儲存關鍵字(遍歷連結串列匹配key)

  • 2. 開放定址法:當關鍵字指向已經存在資料的單元的時候,繼續尋找其他單元,直到找到可用單元(佔用其他單元位置,更容易出現hash衝突,效能下降)

4、基礎知識

  • 連結串列:佇列、棧、雙向連結串列、

  • 連結串列 :元素 + 指向下一元素的指標

  • 雙向連結串列:指向上一元素的指標 + 元素 + 指向下一元素的指標

二、氣泡排序的時間複雜度和空間複雜度

1、程式碼實現

         $arr = [2, 4, 1, 5, 3, 6];
         for ($i = 0; $i < (count($arr)); $i++) {
             for ($j = $i + 1; $j < (count($arr)); $j++) {
                 if ($arr[$i] <= $arr[$j]) {
                     $temp = $arr[$i];
                     $arr[$i] = $arr[$j];
                     $arr[$j] = $temp;
                 }
             }
         }
     result : [6,5,4,3,2,1]

2、計算原理

  • 第一輪:將陣列的第一個元素和其他所有的元素進行比較,哪個元素更大,就換順序,從而冒泡出第一大(最大)的元素

  • 第一輪:將陣列的第二個元素和其他所有的元素進行比較(第一大已經篩選出來不用繼續比較了),哪個元素更大,就換順序,從而冒泡出第二大的元素

  • ... 依次類推,冒泡從大到小排序的陣列

平均時間複雜度:O(n^2)

最優時間複雜度:O(n) ,需要加判斷,第一次迴圈如果一次都沒有交換就直接跳出迴圈

空間複雜度:O(1),交換元素的時候的臨時變數佔用的空間

最優空間複雜度:O(1),排好序,不需要交換位置

3、時間複雜度和空間複雜度

時間複雜度:全程為漸進時間複雜度,估算對處理器的使用效率(描述演演算法的效率趨勢,並不是指演演算法具體使用的時間,因為不同機器的效能不一致,只是一種效率計算的通用方法)

表示方法:大O符號表示法

複雜度量級:

  • 常數階O(1)

  • 線性階O(n)

  • 平方階O(n²)

  • 立方階O(n³)

  • K次方階O(n^k)

  • 指數階(2^n)

  • 對數階O(logN)

  • 線性對數階O(nlogN)

時間複製型別:

  • 最好時間複雜度

  • 最壞時間複雜度

  • 平均時間複雜度

  • 均攤時間複雜度

空間複雜度:全程漸進空間複雜度,估算對計算機記憶體的使用程度(描述演算法佔用的儲存空間的趨勢,不是實際佔用空間,同上)

三、網路七層協定及 TCP 和 TCP

應用層、表示層、對談層、傳輸層、網路層、(資料)鏈路層、物理層

記憶套路:

首字:應表會傳(物鏈網)

第一個字:應用層(出現次數多,易憶)

前四個正向:應表 - 會傳

後三個反向:物聯網諧音比網鏈物更好記

四、TCP 和 UDP 的特點和區別

1、都是屬於傳輸層協定

2、TCP

  • 面向連線,所以只能一對一

  • 面向位元組流傳輸

  • 資料可靠,不丟失

  • 全雙工通訊

3、UDP(根據TCP特點反記)

  • 無連線,支援一對一,一對多,多對多

  • 面向保溫傳輸

  • 首部開銷小,資料不一定可靠但是速度更快

五、TCP 的三次握手和四次揮手

1、三次握手:

  • 1)第一次:使用者端傳送SYN = 1,seq = client_isn

    作用:

    使用者端:無

    伺服器端:確認自己的接收功能和使用者端的傳送功能

  • 2)第二次:伺服器端傳送SYN = 1,seq = server_isn,ACK =client_isn +1

    作用:

    使用者端:確認自己傳送和接收都正常,確認伺服器端的接收和傳送正常

    伺服器端:確認自己的接收正常,確認伺服器端的傳送正常(這時候伺服器端還不能確認使用者端接收是否正常)

  • 3)第三次:使用者端傳送SYN = 0, ACK = server_isn+1,seq =client_isn+1

    作用:雙方確認互相的接收和傳送正常,建立連線

2、四次揮手

  • 1)第一次:使用者端傳送FIN

    作用:告訴伺服器端我沒有資料傳送了(但是還能接收資料)

  • 2)第二次:伺服器端傳送ACK

    作用:告訴使用者端收到請求了,可能伺服器端可能還有資料需要傳送,所以使用者端收到進入FIN_WAIT狀態,等伺服器端資料傳輸完之後傳送FIN

  • 3)第三次:伺服器端傳送FIN

    作用:伺服器端告訴使用者端我傳送完了,可以關閉連線了。

  • 4)第四次:使用者端傳送ACK

    作用:使用者端收到FIN之後,擔心伺服器端不知道要關閉,所以傳送一個ACK,進入TIME_WAIT,等待2MSL之後如果沒有收到回覆,證明伺服器端已經關閉了,這時候使用者端也關閉連線。

注意:

  • 當收到對方的FIN報文時,僅僅表示對方不再傳送資料了但是還能接收資料

  • 最後需要等待2MSL是因為網路是不可靠的,如果伺服器端沒有收到最後一次ACK,伺服器端會重新放FIN包然後等使用者端再次傳送ACK包然後關閉(所以使用者端最後傳送ACK之後不能立即關閉連線)

六、HTTP 狀態碼

1、狀態碼分類

  • - 1xx:資訊,伺服器收到請求,需要請求者繼續操作

  • - 2xx:成功

  • - 3xx:重定向

  • - 4xx:使用者端錯誤

  • - 5xx:伺服器端錯誤

2、常用狀態碼

  • 200:請求成功

  • 301:永久重定向

  • 302:臨時移動

  • 400 bad request:使用者端請求語法錯誤

  • 401 unauthorized:使用者端沒有許可權

  • 403 forbidden:伺服器拒絕使用者端請求

  • 404 not found:使用者端請求資源不存在

  • 500 Internal Server Eerro:伺服器內部錯誤

  • 502 bad gateway:作為閘道器或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應

  • 503 Service Unavailable 超載或系統維護

  • 504 Gateway timeout:閘道器超時

3、502 的原因及解決方法

原因:nginx將請求提交給閘道器(php-fpm)處理異常導致

1)fastcgi 緩衝區設定過小

fastcgi_buffers 8 16k;

fastcgi_buffer_size 32k;

2)php-cgi的程序數設定過少

檢視FastCgi程序數:netstat -anpo | grep "php-cgi"| wc -l

調整引數最大子程序數:max_children

一般按照單個程序20M計算需要需要設定的子程序數

3)max_requests(記憶體溢位或頻繁重新啟動)

引數指明每個children最多能處理的請求數量,到達最大值之後會重新啟動children。

設定過小可能導致頻繁重新啟動children:

php將請求輪詢給每個children,在大流量的場景下,每一個children 到達最大值的時間差不多,如果設定過小可能多個children 在同一時間關閉,nginx無法將請求轉發給php-fpm,cpu降低,負載變高。

設定過大可能導致記憶體洩露

4)php執行時間超過nginx等待時間

fastcgi_connect_timeout

fastcgi_send_timeout

fastcgi_read_timeout

5)fastcgi執行時間

max_execution_time

七、http 和 HTTPS 的區別

1、埠:http 80; https :443

2、http無狀態,https是有http + ssl構建的可進行加密傳輸的協定

3、http明文傳輸,https加密傳輸

4、http更快,三次握手三個包,https 需要12個包(3個tcp包+9個ssl握手包)

八、redis 分散式鎖及問題

1、實現:

加鎖:setnx

解鎖:del

鎖超時:expire

2、可能出現的問題

  • 1)setnx 和expire非原子性問題(加鎖之後還沒來得及設定超時就掛了)

    解決方案:

    Redis 2.6.12以上版本為set指令增加了可選引數,虛擬碼如下:set(key,1,30,NX),這樣就可以取代setnx指令

  • 2)超時誤刪其他程序鎖。(A程序執行超時,導致鎖釋放,這時候B程序獲取鎖開始處理請求,這時候A程序處理完成,會誤刪B程序的鎖)

    解決方案:只能刪除自己程序的鎖 (lua指令碼防止B程序獲取過期鎖之後誤刪A程序的鎖)

  • 3)並行場景,A程序執行超時導致鎖釋放,這時候B程序獲取到鎖。

    解決方案:開啟守護行程,給當前程序要過期的鎖延時。

  • 4)單點範例安全問題

    單機宕機之後導致所有使用者端無法獲取鎖

    解決:

    主從複製,因為是非同步完成的所以無法完全實現解決

九、redis 為什麼是單執行緒?為什麼快?

推薦閱讀:https://www.php.cn/redis/475918.html

十、redis 的資料型別及應用場景

1、string :

普通的key/value儲存

2、hash:

hashmap:鍵值隊集合,儲存物件資訊

3、list:

雙向連結串列:訊息佇列

4、set:

value永遠為null的hashMap:無序集合且不重複:計算交集、並集、差集、去重值

5、zset:

有序集合且不重複:hashMap(去重) + skiplist跳躍表(保證有序):排行榜

十一、redis 實現持久化的方式及原理、特點

1、RDB持久化(快照):指定時間間隔內的記憶體資料集快照寫入磁碟

1)fork一個子程序,將快照內容寫入臨時RDB檔案中(dump.rdb),當子程序寫完快照內容之後新的檔案替換老的檔案

2)整個redis資料庫只包含一個備份檔案

3)效能最大化,只需要fork子程序完成持久化工作,減少磁碟IO

4)持久化之前宕機可能會導致資料丟失

2、AOF持久化 :以紀錄檔的形式記錄伺服器的所有的寫、刪除操作

1)每接收到一個寫的命令用write函數追加到檔案appendonly.aof

2)持久化的檔案會越來越大,存在大量多餘的紀錄檔(0 自增100次到100,會產生100條紀錄檔記錄)

3)可以設定不同的fsync策略

  • appendfsync everysec :1s一次,最多丟失1s的資料(預設)

  • appendfsync always :每次變動都會執行一次

  • appendfsync no :不處理

4)AOF檔案太大之後會進行重寫:壓縮AOF檔案大小

  • fork一個子程序,將redis內地資料物件的最新狀態寫入AOF臨時檔案(類似rdb快照)

  • 主程序收到的變動會先寫入記憶體中,然後同步到老的AOF檔案中(重寫失敗之後也能保證資料完整性)

  • 子程序完成重寫之後會將記憶體中的新變動同步追加到AOF的臨時檔案中

  • 父程序將臨時AOF檔案替換成新的AOF檔案,並重新命名。之後收到的新命令寫入到新的檔案中

十二、秒殺設計流程及難點

1、靜態快取

2、nginx 負載均衡

三種方式:DNS輪詢、IP負債均衡、CDN

3、限流機制

方式:ip限流、介面令牌限流、使用者限流、header動態token(前端加密,後端解密)

4、分散式鎖

方式:

  • setnx + expire (非原子性,redis2.6 之後set保證原子性)

  • 釋放鎖超時 (開啟守護行程自動續時間)

  • 過期鎖誤刪其他執行緒(requestId驗證或者lua指令碼保證查 + 刪的原子性)

5、快取資料

方式:

  • 快取擊穿:快取資料預熱 + 布隆過濾器/空快取

  • 快取雪崩:快取設定隨機過期時間,防止同一時間過期

6、庫存及訂單

  • 扣庫存

    • redis 自減庫存,並行場景下可能導致負數,影響庫存回倉:使用lua指令碼保證原子性

    • redis預扣庫存之後,然後使用非同步訊息建立訂單並更新庫存變動

    • 資料庫更新庫存使用樂觀鎖:where stock_num - sell_num > 0

    • 新增訊息傳送記錄表及重試機制,防止非同步訊息丟失

  • 建立訂單

    • 前端建立websocket連線或者輪詢監聽訂單狀態

    • 消費驗證記錄狀態,防止重複消費

  • 回倉

    • 建立訂單之後傳送延時訊息,驗證訂單支付狀態及庫存是否需要回倉

十三、防 sql 注入

1、過濾特殊字元

2、過濾資料庫關鍵字

3、驗證資料型別及格式

4、使用預編譯模式,繫結變數

十四、事務隔離級別

1、標準的sql隔離級別實現原理

  • 未提交讀:其他事務可以直接讀到沒有提交的:髒讀

    • 事務對當前被讀取的資料不加鎖

    • 在更新的瞬間加行級共用鎖到事務結束釋放

  • 提交讀:事務開始和結束之間讀取的資料可能不一致,事務中其他事務修改了資料:不可重複度

    • 事務對當前讀取的資料(被讀到的時候)行級共用鎖,讀完釋放

    • 在更新的瞬間加行級排他鎖到事務結束釋放

  • 可重複讀:事務開始和結束之前讀取的資料保持一致,事務中其他事務不能修改資料:可重複讀

    • 事務對當前讀到的資料從事務一開始就加一個行級共用鎖

    • 在更新的瞬間加行級排他鎖到事務結束釋放

    • 其他事務再事務過程中可能會新增資料導致幻讀

  • 序列化

    • 事務讀取資料時加表級共用鎖

    • 事務更新資料時加表級排他鎖

2、innodb的事務隔離級別及實現原理(!!和上面的不一樣,區分理解一個是隔離級別 一個是!!事務!!隔離級別)

1)基本概念

  • mvcc:多版本並行控制:依賴於undo log 和read view

    • 讓資料都讀不會對資料加鎖,提高資料庫並行處理能力

    • 寫操作才會加鎖

    • 一條資料有多個版本,每次事務更新資料的時候會生成一個新的資料版本,舊的資料保留在undo log

    • 一個事務啟動的時候只能看到所有已經提交的事務結果

  • 當前讀:讀取的是最新版本

  • 快照讀:讀取的是歷史版本

  • 間隙鎖:間隙鎖會鎖住一個範圍內的索引

    • update id between 10 and 20

    • 無論是否範圍內是否存在資料,都會鎖住整個範圍:insert id = 15,將被防止

    • 只有可重複讀隔離級別才有間隙鎖

  • next-key lock:

    • 索引記錄上的記錄鎖+ 間隙鎖(索引值到前一個索引值之間的間隙鎖)

    • 前開後閉

    • 阻止幻讀

2)事務隔離級別

  • 未提交讀

    • 事務對當前讀取的資料不加鎖,都是當前讀

    • 在更新的瞬間加行級共用鎖到事務結束釋放

  • 提交讀

    • 事務對當前讀取的資料不加鎖,都是快照讀

    • 在更新的瞬間加行級排他鎖到事務結束釋放

  • 可重複讀

    • 事務對當前讀取的資料不加鎖,都是快照讀

    • 事務再更新某資料的瞬間,必須加行級排他鎖(Record 記錄鎖、GAP間隙鎖、next-key 鎖),事務結束釋放

    • 間隙鎖解決的是幻讀問題

      • 主從複製的情況下 ,如果沒有間隙鎖,master庫的A、B程序

      • A程序 delete id < 6 ;然後還沒有commit

      • B程序insert id = 3,commit

      • A程序提交commit

      • 該場景下,主庫會存在一條id =3 的記錄,但是binlog裡面是先刪除再新增就會導致從庫沒有資料,導致主從的資料不一致

    • MVCC的快照解決的是不可重複讀問題

  • 序列化

    • 事務讀取資料時加表級,當前讀

    • 事務更新資料時加表級排他鎖

十五、索引原理

索引就是幫助資料庫高效查詢資料的儲存結構,儲存再磁碟中,需要消耗磁碟IO

1、儲存引擎

  • myisam 支援表鎖,索引和資料分開儲存適合跨伺服器遷移

  • innodb 支援行鎖,索引和資料儲存再一個檔案

2、索引型別

  • hash索引

    • 適合精確查詢且效率高

    • 無法排序、不適合範圍查詢

    • hash衝突的情況下需要遍歷連結串列(php陣列的實現原理、redis zset 的實現原理類似)

  • b-tree、b+tree

    • b-tree 和b+tree的去區別

      • b+tree 的資料全部儲存在葉子節點,內部節點只存key,一次磁碟IO能獲取到更多的節點

      • b-tree 的內部節點和葉子節點都儲存key和資料,查詢資料不需要找到葉子節點,內部節點可以直接返回資料

      • b+tree 增加了葉子節點到相鄰節點的指標,方便返回查詢遍歷

  • 聚簇索引和非聚簇索引

    • 概念

      • 聚簇索引 :索引和資料儲存在一個節點

      • 非聚簇索引:索引和資料分開儲存,通過索引找到資料實際儲存的地址

    • 詳解:

      • innodb 使用的聚簇索引,且預設主鍵索引為聚簇索引(沒有主鍵索引的時候,選擇一個非空索引,還沒有則隱式的主鍵索引),輔助索引指向聚簇索引位置,然後在找到實際儲存地址

      • myisam 使用非聚簇索引,所有的索引都只需要查詢一次就能找到資料

      • 聚簇索引的優勢和略勢

        1. 索引和資料在一起,同一頁的資料會被快取到(buffer)記憶體中,所以檢視同一頁資料的時候只需要從記憶體中取出,

        2. 資料更新之後之只需要維護主鍵索引即可,輔助索引不受影響

        3. 輔助索引存的是主鍵索引的值,佔用更多的物理空間。所以會受到影響

        4. 使用隨機的UUID,資料分佈不均勻,導致聚簇索引可能掃全表,降低效率,所以儘量使用自增主鍵id

十六、分表 (分庫) 的策略

1、流程

評估容量和分表數量-> 根據業務選定分表key->分表規則(hash、取餘、range)->執行->考慮擴容問題

2、水平拆分

  • 根據欄位水平拆分為多個表

  • 每個表的結構相同

  • 所有分表的合集是全量數量

3、垂直拆分

  • 根據欄位垂直拆分

  • 表結構不一樣,分表的同一個關聯行是一條完整的資料

  • 擴充套件表,熱點欄位和非熱點欄位的拆分(列表和詳情的拆分)

  • 獲取資料時,儘量避免使用join,而是兩次查詢結果組合

4、問題

  • 跨庫join問題

    • 全域性表:需要關聯部分系統表的場景

    • 冗餘法:常用欄位進行冗餘

    • 組裝法:多次查詢的結果進行組裝

  • 跨節點的分頁、排序、函數問題

  • 事務一致性

  • 全域性主鍵id

    • 使用uuid -> 會降低聚簇索引效率

    • 使用分散式自增id

  • 擴容問題

    • 升級從庫

      • 從庫升級為主庫,資料一致,只需要刪除冗餘資料即可

      • 成倍擴容:需要在加一倍從庫

    • 雙寫遷移:

      • 新資料進行雙寫,同時寫進新老資料庫

      • 舊資料複製到新資料庫

      • 以老資料庫為準,驗證資料一致性之後刪除冗餘資料

十七、select 和 update 的執行流程

1、mysql 構成

  • server層:聯結器->快取器->分析器(前處理器)->優化器->執行器

  • 引擎層 : 查詢和儲存資料

2、select 執行過程

  • 使用者端傳送請求,建立連線

  • server層查詢快取,命中直接返回,否則繼續

  • 分析七分析sql語句以及預處理(驗證欄位合法性及型別等)

  • 優化器生成執行計劃

  • 執行器呼叫引擎API查詢結果

  • 返回查詢結果

3、update執行過程

  • 基礎概念

    • buffer pool(快取池),在記憶體中,下次讀取同一頁的資料的時候可以直接從buffer pool中返回(innodb的聚簇索引)

    • 更新資料的時候先更新buffer pool,然後在更新磁碟

    • 髒頁:記憶體中的快取池更新了,但是沒有更新磁碟

    • 刷髒:inndb 中有一個專門的程序將buffer pool的資料寫入磁碟,每隔一段時間將多個修改一次性寫入磁碟

    • redo log 和 binlog

      • redo log(重做紀錄檔),innodb特有的紀錄檔,物理紀錄檔,記錄修改

      • redo log是重複寫,空間固定且會用完,會覆蓋老紀錄檔

      • binlog 是server層共有的紀錄檔,邏輯紀錄檔,記錄語句的原始邏輯

      • binlog 是追加寫到一定大小切換到下一個,不會覆蓋以前的紀錄檔

      • redo log主要是用來恢復崩潰,bin log是用來記錄歸檔的二進位制紀錄檔

      • redo log只能恢復短時間內的資料,binlog可以通過設定恢復更大的資料

    • WAL(write-ahead-logging)先寫紀錄檔方案

      • 記錄紀錄檔是順序IO

      • 直接寫入磁碟(刷盤)是隨機IO,因為資料是隨機的,可能分佈在不同的磁區

      • 順序IO的效率更高,先寫入修改紀錄檔,可以延遲刷盤時機,提高吞吐量

    • redo log 刷盤機制,check point

      • redo log大小固定,迴圈寫入

      • redo log 就像一個圓圈,前面是check point (到這個point就開始覆蓋老的紀錄檔),後面是write point (當前寫到的位置)

      • write point 和check point 重疊的時候就證明redo log 滿了,需要開始同步redo log 到磁碟中了

  • 執行步驟(兩階段提交 - 分散式事務,保證兩個紀錄檔的一致性)

    • 分析更新條件,查詢需要更新的資料(會用到快取)

    • server 呼叫引擎層的API,Innodb 更新資料到記憶體中,然後寫入redo log,然後進入prepare

    • 引擎通知server層開始提交資料

    • server層寫入binlog 紀錄檔,並且呼叫innodb 的介面發出commit請求

    • 引擎層收到請求之後提交commit

  • 宕機後資料崩潰恢復規則

    • 如果redo log 狀態為commit ,則直接提交

    • 如果redo log 狀態為prepare,則判斷binlog 中的事務是否commit,是則提交,否則回滾

  • 如果不使用兩次提交的錯誤案例(update table_x set value = 10 where value = 9)

    • 先redo log 再寫入binlog

      1. redo log 寫完之後,binlog沒寫完,這時候宕機。

      2. 重新啟動之後redo log 完整,所以恢復資料 value = 10

      3. bin log紀錄檔中沒有記錄,如果需要恢復資料的時候 value = 9

    • 先寫binlog 再寫redo log

      1. binlog 寫入完成,redo log 未完成

      2. 重新啟動之後沒有redo log ,所以value 還是9

      3. 當需要恢復資料的時候binlog 紀錄檔完整,value 更新成10

  • undo log

    • 在更新寫入buffer pool之前記錄

    • 如果更新過程中出錯,直接回滾到undo log 的狀態

十八、binlog 的作用和三種格式

作用:

1. 資料恢復

2. 主從複製

格式(二進位制檔案):

1)statement

  • 1. 記錄每次sql語句的原文

  • 2. 刪除一個表只需要記錄一條sql語句,不需要記錄每一行的變化,節約IO,提高效能,減少紀錄檔量

  • 3. 可能出現主從不一致(儲存過程、函數等)

  • 4. RC隔離級別(讀提交),因為binlog 記錄順序是按照事務commit 順序記錄的,所以可能導致主從複製不一致。通過可重複讀級別的間隙鎖的引入,可以解決。

2)row

  • 1. 記錄每條記錄的修改情況,不需要記錄sql語句的上下文記錄

  • 2. 導致binlog紀錄檔量很大

  • 3. 刪除一個表:記錄每條記錄都被刪除的狀況

3)mixed

  • 1. 前兩個格式的混合版

  • 2. 根據語句自動選擇使用哪一種:

    • 一般的sql語句修改使用statement

    • 修改表結構、函數、儲存過程等操作選擇row

    • update 和delete 還是會記錄全部記錄的變化

十九、主從同步(主從複製)的原理和問題及讀寫分離

1、解決的問題

  • 資料分佈

  • 負載均衡

  • 資料備份,高可用,避免單點失敗

  • 實現讀寫分離,緩解資料庫壓力

  • 升級測試(使用高版本mysql當從庫)

2、支援的複製型別(binlog 的三種格式)

  • 基於sql語句的複製

  • 基於行的複製

  • 混合型複製

3、原理

1)基礎概念

  • 從庫生成兩個執行緒

    • I/O執行緒

    • SQL執行緒

  • 主庫生成執行緒

    • log dumo 執行緒

2)流程(主節點必須開啟bin log功能,)

  • 1. 從節點開啟start slave 命令之後,建立一個IO程序連線到主節點

  • 2. 連線成功之後,主節點建立一個 log dump執行緒(主節點會為每一個從節點創一個log dump執行緒)

  • 3. 當binlog發生變化時,主節點的dump log執行緒會讀取bin-log內容並行送給從節點

  • 4. 主節點dump log 執行緒讀取bin-log 的內容時會對主節點的bin-log加鎖,讀取完成在傳送給從節點之前釋放鎖

  • 5. 從節點的IO執行緒接收主節點傳送的binlog內容,並將其寫入本地relay log 檔案中

  • 6. 主從節點通過binlog檔案+position偏移量定位主從同步的位置,從節點會儲存接收到的position偏移量,如果從節點發生宕機重新啟動,自動從postion位置發起同步

  • 7. 從節點的SQL執行緒複製讀取本地relay log的內容,解析成具體的操作並執行,保證主從資料一致性

4、主從複製的模式

1)非同步模式(預設方式)

  • 1. 可能導致主從不一致(主從延時)

  • 2. 主節點接收到使用者端提交的事務之後直接提交事務並返回給使用者端

  • 3. 如果主節點事務提交之後,log dump還沒來得及寫入就宕機就會導致主從資料不一致

  • 4. 不用關心主從的同步操作,效能最好

2)全同步模式

  • 1. 可靠更高,但是會影響主庫相應時間

  • 2. 主節點接收到使用者端提交的事務之後,必須等待binlog 傳送給從庫,並且所有從庫全部執行完事務之後才返回給使用者端

3)半同步模式

  • 1. 增加一部分可靠性,增加主庫一部分相應時間

  • 2. 主節點接收到使用者端提交的事務之後,等待binlog傳送給至少一個從庫並且成功儲存到本地relay log中,此時主庫提交事務並返回給使用者端

4)server-id的設定和server-uuid

  • 1. server-id用於標識資料庫範例,防止在鏈式主從、多主多從拓撲中導致SQL語句的無限迴圈

  • 2. server-id預設值為0,對於主機來說依然會記錄二進位制紀錄檔,但是會拒絕所有的從機連線。

  • 2. server-id = 0 對於從機來說會拒絕連線其他範例

  • 3. server-id是一個全域性變數,修改之hi偶必須重新啟動服務

  • 4. 主庫和從庫的server-id重複時

    • 預設replicate-same-server-id = 0,從庫會跳過所有主從同步的資料,導致主從資料不一致

    • replicate-same-server-id = 1,可能導致無線迴圈執行sql

  • 兩個從庫(B、C)server-id重複會導致主從連線異常,時斷時連

    • 主庫(A)發現相同的server-id會斷開之前的連線,重新註冊新的連線

    • B、C從庫的連線會周而復始的重連

  • MySQL服務會自動建立並生成server-uuid設定

    • 當主從同步時如果主從範例的server-uuid相同會報錯退出,不過我們可以通過設定replicate-same-server-id=1來避免報錯(不推薦)

5、讀寫分離

1)基於程式碼實現,減少硬體開支

2)基於中間代理實現

3)主從延時

  • 從庫效能比主庫差

  • 大量查詢導致從庫壓力大,消耗大量CPU資源,影響同步速度:一主多從

  • 大事務執行:事務執行完之後才會寫入binlog,從庫讀取延時

  • 主庫ddl(alter、drop、create)

二十、死鎖

1、產生的四個必要條件

  • 1. 互斥條件

  • 2. 請求與保持條件:一次性分配全部資源,否則一個都不分配

  • 3. 非剝奪條件:當程序獲得一部分資源等待其他資源的時候釋放佔有的資源

  • 4. 迴圈等待條件:

    理解:一個資源只能被一個程序佔用,程序獲取資源資源還能申請新的資源,並且已經獲得的資源不能被剝奪,同時多個程序相互等待其他程序被佔用的資源

2、解除死鎖

  • 1. 終止程序(全部幹掉)

  • 2. 逐個種植(殺一個看一下有沒有解除)

二十一、Mysql 優化大分頁查詢 limit 100000 (offset),10 (page_sie)

1、原因

mysql查詢分頁資料時不是直接跳過offset(100000),而是取offset + page_size = 100000 + 10 = 100010條資料,然後放棄其掉前面的100000條資料,所以效率地下

2、優化方案

  • 延時關聯:使用覆蓋索引

  • 主鍵閾值法:主鍵是自增的情況下,通過條件推算出符合條件的主鍵最大值&最小值(使用覆蓋索引)

  • 記錄上一頁的結果位置,避免使用 OFFSET

二十二、redis 快取和 mysql 資料一致性

方式:

1、先更新redis 再更新資料庫

 場景:update set value = 10 where value = 9

 1) redis更新成功:redis value = 10

 2)資料庫更新失敗:mysql value = 9

 3)資料不一致

2、先更新資料庫,再更新redis

 場景: A程序update set value = 10 where value = 9 ;B程序 update set value = 11 where value = 9;

 1)A 程序先更新資料庫,還未寫入快取:mysql value = 10 ;redis value = 9

 2)B 程序更新資料庫並且提交事務,寫入快取:mysql value = 11;redis value = 11;

 3)A 程序處理完請求提交事務,寫入快取:redis value = 10;

 4)最終 mysql value = 11; redis value = 10

3、先刪除快取再更新資料庫

場景:A程序update set value = 10 where value = 9 ;B程序查詢value;

1)A 程序先刪除快取 還沒來得及修改資料或者事務未提交

2)B 程序開始查詢,沒有命中快取,所以查庫並寫入快取 redis value = 9

3)A 程序更新資料庫完成 mysql value = 10

4)最終 mysql value = 10;redis value = 9

解決方案:

1、延時雙刪除

場景:A程序update set value = 10 where value = 9 ;B程序查詢value;

1)A 程序先刪除快取 還沒來得及修改資料或者事務未提交

2)B 程序開始查詢,沒有命中快取,所以查庫並寫入快取 redis value = 9

3)A 程序更新資料庫完成 mysql value = 10

4)A 程序估算延時時間,sleep之後再次刪除快取

5)最終mysql value = 10;redis value 為空(下次查詢直接查庫)

6)延時的原因時防止B程序在A程序更新完之後B程序還沒來得及寫入快取

2、請求序列化

1)建立兩個佇列 :更新佇列和查詢佇列

2)當快取不存在需要查庫的時候將key存入更新佇列

3)如果查詢未完成之前有新的請求進來,並且發現更新佇列中還存在key則將key放入查詢佇列,則等待;不存在則重複第二步

4)如果查詢的資料發現查詢佇列已經存在則不需要再次寫入佇列

5)資料更新完成之後rpop更新佇列,同時rpop查詢佇列,釋放查詢請求

6)查詢請求可以使用while + sleep 查詢快取並且設定最大延遲時間,還沒有完成則返回空

二十三、redis 中的 connect 和 pconnect

1、connect :指令碼結束之後釋放連線

1. close :釋放連線

2、pconnect(長連線) :指令碼結束連線不釋放,連線保持在php-fpm程序中,生命週期隨著php-fpm程序的生命週期

  • 1. close不釋放連線

    • 只是當前php-cgi程序中不能再次請求redis

    • 當前php-cgi中的後續連線仍然可以複用,直到php-fpm結束生命週期

  • 2. 減少建立redis連線的消耗

  • 3. 減少一個php-fpm多次建立連線

  • 4. 消耗更多的記憶體,並且連線數持續增加

  • 5. 同一個php-fpm的woker子程序(php-cgi)的上一個請求可能會影響到下一個請求

3、pconnect 的連線複用問題

  • 變數A select db 1 ;變數B select db 2;會影響到變數A的db

  • 解決:每一個db建立一個連線範例

二十四、redis zset 有序集合使用 skiplist 的原理

1、基本概念

1. skiplist是一個隨機的資料,以有序的方式在層次化的連結串列中儲存元素(只能用於元素有序的情況)

2. skiplist實在有序連結串列和多層連結串列的基礎上演變的

3. 允許重複值,所以對比檢查除了要對比key 還要對比value

4. 每個節點都帶有一個高度為1的後退指標,用於表頭方向到表尾方向的迭代

5. 時間複雜度O(logn)、空間複雜度O(n)

2、跳躍表和平衡樹的對比

1)範圍查詢效率

  • 跳躍表範圍查詢效率更高,因為找到最小值之後只需要對第一層的連結串列進行遍歷直到小於最大值即可

  • 平衡樹範圍查詢找到最小值之後還要進行中序遍歷找到其他不超過最大值的節點

2)記憶體佔用

  • skiplist 每個節點的指標數量為1/(1-p)

  • 平衡樹的每個節點指標數都為2

3)插入和刪除操作

  • skiplist只需要修改相鄰節點的指標

  • 平衡樹變更會引起子樹的調整

二十五、redis 的過期刪除和淘汰機制

1、常規過期刪除策略

1)定時刪除

  • 通過定時器在過期的時候立即刪除

  • 記憶體釋放及時但是消耗更多的CPU,大並行的時候需要消耗CPU資源影響處理請求的速度

  • 記憶體友好,CPU不友好

2)惰性刪除

  • 放任鍵過期不管,到下次需要去取出的時候檢查是否過期並刪除

  • 可能存在大量過期鍵,且不會使用,導致記憶體溢位

  • 記憶體不友好,CPU友好

3)定期刪除

  • 每隔一段時間檢查,刪除過期的鍵

  • 刪除多少和檢查多少有演演算法決定

2、redis採用的 惰性刪除 + 定期刪除

  • 週期性隨機測試一些設定了過期時間的鍵進行檢查,到期則刪除

  • 每次清理的時間不超過CPU的25%,達到時間則退出檢查

  • 定期沒有刪除到的鍵,且以後不會使用的鍵還是會存在記憶體中,所以需要配合淘汰策略

3、淘汰策略(記憶體不足以寫入新資料的時候執行)

  • volatile-lru :設定了過期時間且最近使用越少越優先淘汰

  • volatile-ttl :設定了過期時間且過期時間越早越優先淘汰

  • volatile-random :設定了過期時間中隨機刪除

  • allkeys-lru :所有鍵中過期時間越早越優先淘汰

  • allkeys-random :所有鍵中過期隨機淘汰

  • no-enviction :不允許淘汰,記憶體不足報錯

二十六、redis 常見問題及解決方案

1、快取雪崩:同一時間大量快取失效,導致請求直接查詢資料庫,資料庫記憶體和CPU壓力增加甚至宕機

解決:

  • 熱點資料永不過期或者分佈到不同範例,降低單機故障問題

  • 快取時間新增亂數,防止大量快取同時失效

  • 做二級快取或者雙快取,A為原始快取 短時效,B為備份快取 ,長期有效。更新時候雙寫快取

2、快取穿透:快取和資料庫都沒有資料,大量請求下,所有請求直接擊穿到資料庫,導致宕機。

解決:

  • 布隆過濾器:長度為m的位向量或者位列表組成(僅包含0或1位值的列表)

    • 使用多個不用的hash函數,產生多個索引值,填充對應多個位置的值為1

    • 布隆過濾器可以檢查值是 「可能在集合中」 還是 「絕對不在集合中」

    • 可能誤判但是基礎過濾效率高

    • 極端情況,當布隆過濾器沒有空閒位置的時候每次查詢返回true

  • 空快取(短時效)

  • 業務層引數過濾

3、快取擊穿:資料庫中有資料,但是快取突然失效之後發生大量請求導致資料庫壓力增加甚至打垮宕機

解決:

  • 熱點資料永不過期

  • 互斥鎖:獲取鎖之後不管成功還是失敗都要釋放鎖

二十七、php-fpm 詳解及生命週期

1、基礎知識

1)CGI協定

  • 動態語言的程式碼檔案需要通過對應的解析器才能被伺服器識別

  • CGI協定就是用來使伺服器和直譯器相互通訊的

  • 伺服器解析PHP檔案需要PHP直譯器加上對應的CGI協定

2)CGI程式 = php-cgi

  • php-cgi就是一個遵守CGI協定的CGI程式

  • 同時也就是PHP直譯器

  • 標準的CGI每個請求都會解析php.ini,初始化執行環境等,降低效能

  • 每次修改設定之後需要重新php-cgi才能讓php.ini生效

  • 不能動態worker排程,只能一開始指定數量的worker

3)FastCGI協定

  • 和CGI一樣也是一個協定/規範,不過是再CGI的基礎上優化,效率更高

  • 用來提高CGI程式效能的

  • 實現了CGI程序的管理

4)FastCGI程式 = php-fpm

  • php-fpm就是一個遵守FastCGI協定的FastCGI程式

  • FastCGI程式對CGI程式的管理模式

    • 啟動一個master程序,解析組態檔,初始化環境

    • 啟動多個worker子程序

    • 接受到請求之後,傳遞給woker程序去執行

  • 解決修改php.ini之後平滑重新啟動問題

    • process_control_timeout:子程序接受主程序複用訊號的超時時間(在規定時間內處理完請求,完成不了就不管了)

    • 設定php-fpm留給fastcgi程序響應重新啟動訊號的時間

    • process_control_timeout = 0,也就是不生效,無法保證平滑重新啟動

    • process_control_timeout設定過大可能導致系統請求堵塞

    • process_control_timeout =10的情況下,如果程式碼邏輯需要11s,重新啟動舊可能導致程式碼執行部分退出

    • 建議值:request_terminate_timeout

  • 重新啟動型別

    • 優雅重新啟動

    • 強制重新啟動

2、php-fpm生命週期:待更新

PHP-FPM 生命週期:https://www.abelzhou.com/php/php-fpm-lifespan/#

二十八、Nginx 和 php 之間的通訊

1、通訊方式:fastcgi_pass

1)tcp socket

  • 跨伺服器,nginx和php不在一個機器時,只能用這個方式

  • 面向連線的協定,更好的保證通訊的正確性和完整性

2)unix socket

  • 不需要網路協定棧、打包拆包等

  • 減少tcp 開銷,效率比tcp socket 更高

  • 高並行時候不穩定,連線數暴增產生大量的長時快取,巨量資料包可能直接返回異常

推薦學習:《》

以上就是28道PHP面試題,帶你梳理基礎知識!的詳細內容,更多請關注TW511.COM其它相關文章!