首頁  >  文章  >  後端開發  >  分享2023年最新的28道PHP面試題(附答案)

分享2023年最新的28道PHP面試題(附答案)

青灯夜游
青灯夜游轉載
2022-03-03 13:20:1636081瀏覽

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

分享2023年最新的28道PHP面試題(附答案)

相關推薦:2023年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     

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_connect_timeout

 fastcgi_send_timeout

       

fastcgi_read_timeout

5)fastcgi執行時間     

max_execution_time


參考:

深入了解怎麼優化php php-fom nginx設定參數

nginx報錯502怎麼辦?解決方案分享

七、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實作分散式鎖定需要注意什麼? 【注意事項總結】

帶你深入了解Redis中的分散式鎖定

九、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的5種資料型別及應用場景

#十一、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文件,並重新命名。之後收到的新指令寫入到新的檔案中

參考:

Redis深入學習之詳解持久化原理

淺析RDB和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 885ab4617f96bfe77840175d18ed9146 根據業務選定分錶key->分錶規則(hash、取餘、range)->執行->考慮擴容問題
  • 2、水平拆分

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

#每個表的結構相同

    所有分錶的集合是全量數量
  • 3、垂直拆分

  • ##根據欄位垂直拆分

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

擴充表,熱點字段和非熱點字段的拆分(清單和詳情的拆分)

取得資料時,盡量避免使用join,而是兩次查詢結果組合
  • ##4、問題

  • 跨庫join問題

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

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

  • # 組裝法:多次查詢的結果組裝
      • 跨節點的分頁、排序​​、函數問題

      • #交易一致性

      • 全域主鍵id

      使用uuid -> 會降低叢集索引效率
使用分散式自增id

##擴充問題

#########升級從庫#############從函式庫升級為主函式庫,資料一致,只需要刪除冗餘餘資料即可############倍數擴充:需要在一倍內從庫#################雙寫遷移:# ###########新資料進行雙寫,同時寫入新舊資料庫#############舊資料複製到新資料庫########## ###以舊資料庫為準,驗證資料一致性之後刪除冗餘資料#########################18、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

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

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

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

    • 先寫binlog 再寫redo log
  •             1. binlog 寫入完成,redo log 未完成
  •             2. 重新啟動

    ##            2. 重新啟動之後沒有說明或沒有 value log
      #            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 = 100 where value = 9 ;B進程查詢value;

        1)A 進程先刪除快取還來不及修改資料或事務未提交

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

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

        4)最終 

#解決方案:

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之後再刪除快取

##    1   value 5)為空(下次查詢直接查庫)

        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/

#參考:

聊聊PHP-FPM 與Nginx 的通訊機制

#淺集PHP設定檔中的幾個逾時設定

聊聊nginx平滑重啟和FPM平滑重啟

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

1、通訊方式:fastcgi_pass

1 )tcp socket

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

  • 面向連接的協議,更好的保證通訊的正確性與完整性

2)unix socket

  • #不需要網路協定堆疊、打包拆包等

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

  • 高並發時候不穩定,連接數暴增產生大量的長時緩存,大數據套件可能直接傳回異常

參考:

#聊聊PHP-FPM 與Nginx 的通訊機制

一文淺析Nginx與php-fpm間的通訊機制

#二十九、web 漏洞及問題

1、sql注入

2、XSS攻擊

    推薦閱讀(很詳細的案例來分析XSS攻擊的不同類型及解決方案):[前端安全性系列(一):如何防止XSS攻擊? ](https://tech.meituan.com/2018/09/27/fe-security.html)

3、CSRF攻擊:

##    建議閱讀: [前端安全系列(二):如何防止CSRF攻擊? ](https://tech.meituan.com/2018/10/11/fe-security-csrf.html)

4、檔案上傳漏洞

    推薦閱讀:[淺析檔案上傳漏洞](https://xz.aliyun.com/t/7365)

###5、跨領域問題:#########    1) jsonp######    2)cors######    3)nginx代理商######推薦學習:《###PHP影片教學###》###

以上是分享2023年最新的28道PHP面試題(附答案)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除