您好,登錄后才能下訂單哦!
這篇文章主要介紹“PHP面試題基礎知識有哪些”,在日常操作中,相信很多人在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(n2)
立方階O(n3)
K次方階O(n^k)
指數階(2^n)
對數階O(logN)
線性對數階O(nlogN)
時間復制類型:
最好時間復雜度
最壞時間復雜度
平均時間復雜度
均攤時間復雜度
空間復雜度:全程漸進空間復雜度,估算對計算機內存的使用程度(描述算法占用的存儲空間的趨勢,不是實際占用空間,同上)
應用層、表示層、會話層、傳輸層、網絡層、(數據)鏈路層、物理層
記憶套路:
首字:應表會傳(物鏈網)
第一個字:應用層(出現次數多,易憶)
前四個正向:應表 - 會傳
后三個反向:物聯網諧音比網鏈物更好記
1、都是屬于傳輸層協議
2、TCP
面向連接,所以只能一對一
面向字節流傳輸
數據可靠,不丟失
全雙工通信
3、UDP(根據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之后不能立即關閉連接)
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
1、端口:http 80; https :443
2、http無狀態,https是有http + ssl構建的可進行加密傳輸的協議
3、http明文傳輸,https加密傳輸
4、http更快,三次握手三個包,https 需要12個包(3個tcp包+9個ssl握手包)
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)單點實例安全問題
單機宕機之后導致所有客戶端無法獲取鎖
解決:
主從復制,因為是異步完成的所以無法完全實現解決
1、string :
普通的key/value存儲
2、hash:
hashmap:鍵值隊集合,存儲對象信息
3、list:
雙向鏈表:消息隊列
4、set:
value永遠為null的hashMap:無序集合且不重復:計算交集、并集、差集、去重值
5、zset:
有序集合且不重復:hashMap(去重) + skiplist跳躍表(保證有序):排行榜
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 負載均衡
3、限流機制
方式:ip限流、接口令牌限流、用戶限流、header動態token(前端加密,后端解密)
4、分布式鎖
方式:
setnx + expire (非原子性,redis2.6 之后set保證原子性)
釋放鎖超時 (開啟守護進程自動續時間)
過期鎖誤刪其他線程(requestId驗證或者lua腳本保證查 + 刪的原子性)
5、緩存數據
方式:
緩存擊穿:緩存數據預熱 + 布隆過濾器/空緩存
緩存雪崩:緩存設置隨機過期時間,防止同一時間過期
6、庫存及訂單
扣庫存
redis 自減庫存,并發場景下可能導致負數,影響庫存回倉:使用lua腳本保證原子性
redis預扣庫存之后,然后使用異步消息創建訂單并更新庫存變動
數據庫更新庫存使用樂觀鎖:where stock_num - sell_num > 0
添加消息發送記錄表及重試機制,防止異步消息丟失
創建訂單
前端建立websocket連接或者輪詢監聽訂單狀態
消費驗證記錄狀態,防止重復消費
回倉
創建訂單之后發送延時消息,驗證訂單支付狀態及庫存是否需要回倉
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)事務隔離級別
未提交讀
事務對當前讀取的數據不加鎖,都是當前讀
在更新的瞬間加行級共享鎖到事務結束釋放
提交讀
事務對當前讀取的數據不加鎖,都是快照讀
在更新的瞬間加行級排他鎖到事務結束釋放
可重復讀
主從復制的情況下 ,如果沒有間隙鎖,master庫的A、B進程
A進程 delete id < 6 ;然后還沒有commit
B進程insert id = 3,commit
A進程提交commit
該場景下,主庫會存在一條id =3 的記錄,但是binlog里面是先刪除再新增就會導致從庫沒有數據,導致主從的數據不一致
事務對當前讀取的數據不加鎖,都是快照讀
事務再更新某數據的瞬間,必須加行級排他鎖(Record 記錄鎖、GAP間隙鎖、next-key 鎖),事務結束釋放
間隙鎖解決的是幻讀問題
MVCC的快照解決的是不可重復讀問題
串行化
事務讀取數據時加表級,當前讀
事務更新數據時加表級排他鎖
索引就是幫助數據庫高效查找數據的存儲結構,存儲再磁盤中,需要消耗磁盤IO
1、存儲引擎
myisam 支持表鎖,索引和數據分開存儲適合跨服務器遷移
innodb 支持行鎖,索引和數據存儲再一個文件
2、索引類型
hash索引
適合精確查詢且效率高
無法排序、不適合范圍查詢
hash沖突的情況下需要遍歷鏈表(php數組的實現原理、redis zset 的實現原理類似)
b-tree、b+tree
b+tree 的數據全部存儲在葉子節點,內部節點只存key,一次磁盤IO能獲取到更多的節點
b-tree 的內部節點和葉子節點都存儲key和數據,查找數據不需要找到葉子節點,內部節點可以直接返回數據
b+tree 增加了葉子節點到相鄰節點的指針,方便返回查詢遍歷
b-tree 和b+tree的去區別
聚簇索引和非聚簇索引
聚簇索引 :索引和數據存儲在一個節點
非聚簇索引:索引和數據分開存儲,通過索引找到數據實際存儲的地址
概念
詳解:
innodb 使用的聚簇索引,且默認主鍵索引為聚簇索引(沒有主鍵索引的時候,選擇一個非空索引,還沒有則隱式的主鍵索引),輔助索引指向聚簇索引位置,然后在找到實際存儲地址
myisam 使用非聚簇索引,所有的索引都只需要查詢一次就能找到數據
聚簇索引的優勢和略勢
1. 索引和數據在一起,同一頁的數據會被緩存到(buffer)內存中,所以查看同一頁數據的時候只需要從內存中取出,
2. 數據更新之后之只需要維護主鍵索引即可,輔助索引不受影響
3. 輔助索引存的是主鍵索引的值,占用更多的物理空間。所以會受到影響
4. 使用隨機的UUID,數據分布不均勻,導致聚簇索引可能掃全表,降低效率,所以盡量使用自增主鍵id
1、流程
評估容量和分表數量-> 根據業務選定分表key->分表規則(hash、取余、range)->執行->考慮擴容問題
2、水平拆分
根據字段水平拆分為多個表
每個表的結構相同
所有分表的合集是全量數量
3、垂直拆分
根據字段垂直拆分
表結構不一樣,分表的同一個關聯行是一條完整的數據
擴展表,熱點字段和非熱點字段的拆分(列表和詳情的拆分)
獲取數據時,盡量避免使用join,而是兩次查詢結果組合
4、問題
跨庫join問題
全局表:需要關聯部分系統表的場景
冗余法:常用字段進行冗余
組裝法:多次查詢的結果進行組裝
跨節點的分頁、排序、函數問題
事務一致性
全局主鍵id
使用uuid -> 會降低聚簇索引效率
使用分布式自增id
擴容問題
新數據進行雙寫,同時寫進新老數據庫
舊數據復制到新數據庫
以老數據庫為準,驗證數據一致性之后刪除冗余數據
從庫升級為主庫,數據一致,只需要刪除冗余數據即可
成倍擴容:需要在加一倍從庫
升級從庫
雙寫遷移:
1、mysql 構成
server層:連接器->緩存器->分析器(預處理器)->優化器->執行器
引擎層 : 查詢和存儲數據
2、select 執行過程
客戶端發送請求,建立連接
server層查找緩存,命中直接返回,否則繼續
分析七分析sql語句以及預處理(驗證字段合法性及類型等)
優化器生成執行計劃
執行器調用引擎API查詢結果
返回查詢結果
3、update執行過程
基礎概念
redo log大小固定,循環寫入
redo log 就像一個圓圈,前面是check point (到這個point就開始覆蓋老的日志),后面是write point (當前寫到的位置)
write point 和check point 重疊的時候就證明redo log 滿了,需要開始同步redo log 到磁盤中了
記錄日志是順序IO
直接寫入磁盤(刷盤)是隨機IO,因為數據是隨機的,可能分布在不同的扇區
順序IO的效率更高,先寫入修改日志,可以延遲刷盤時機,提高吞吐量
redo log(重做日志),innodb特有的日志,物理日志,記錄修改
redo log是重復寫,空間固定且會用完,會覆蓋老日志
binlog 是server層共有的日志,邏輯日志,記錄語句的原始邏輯
binlog 是追加寫到一定大小切換到下一個,不會覆蓋以前的日志
redo log主要是用來恢復崩潰,bin log是用來記錄歸檔的二進制日志
redo log只能恢復短時間內的數據,binlog可以通過設置恢復更大的數據
buffer pool(緩存池),在內存中,下次讀取同一頁的數據的時候可以直接從buffer pool中返回(innodb的聚簇索引)
更新數據的時候先更新buffer pool,然后在更新磁盤
臟頁:內存中的緩存池更新了,但是沒有更新磁盤
刷臟:inndb 中有一個專門的進程將buffer pool的數據寫入磁盤,每隔一段時間將多個修改一次性寫入磁盤
redo log 和 binlog
WAL(write-ahead-logging)先寫日志方案
redo log 刷盤機制,check point
執行步驟(兩階段提交 - 分布式事務,保證兩個日志的一致性)
分析更新條件,查找需要更新的數據(會用到緩存)
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 的狀態
作用:
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. 逐個種植(殺一個看一下有沒有解除)
1、原因
mysql查詢分頁數據時不是直接跳過offset(100000),而是取offset + page_size = 100000 + 10 = 100010條數據,然后放棄其掉前面的100000條數據,所以效率地下
2、優化方案
延時關聯:使用覆蓋索引
主鍵閾值法:主鍵是自增的情況下,通過條件推算出符合條件的主鍵最大值&最小值(使用覆蓋索引)
記錄上一頁的結果位置,避免使用 OFFSET
方式:
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 查詢緩存并且設置最大延遲時間,還沒有完成則返回空
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創建一個連接實例
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只需要修改相鄰節點的指針
平衡樹變更會引起子樹的調整
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 :不允許淘汰,內存不足報錯
1、緩存雪崩:同一時間大量緩存失效,導致請求直接查詢數據庫,數據庫內存和CPU壓力增加甚至宕機
解決:
熱點數據永不過期或者分布到不同實例,降低單機故障問題
緩存時間添加隨機數,防止大量緩存同時失效
做二級緩存或者雙緩存,A為原始緩存 短時效,B為備份緩存 ,長期有效。更新時候雙寫緩存
2、緩存穿透:緩存和數據庫都沒有數據,大量請求下,所有請求直接擊穿到數據庫,導致宕機。
解決:
布隆過濾器:長度為m的位向量或者位列表組成(僅包含0或1位值的列表)
使用多個不用的hash函數,產生多個索引值,填充對應多個位置的值為1
布隆過濾器可以檢查值是 “可能在集合中” 還是 “絕對不在集合中”
可能誤判但是基礎過濾效率高
極端情況,當布隆過濾器沒有空閑位置的時候每次查詢返回true
空緩存(短時效)
業務層參數過濾
3、緩存擊穿:數據庫中有數據,但是緩存突然失效之后發生大量請求導致數據庫壓力增加甚至打垮宕機
解決:
熱點數據永不過期
互斥鎖:獲取鎖之后不管成功還是失敗都要釋放鎖
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生命周期:待更新
1、通信方式:fastcgi_pass
1)tcp socket
跨服務器,nginx和php不在一個機器時,只能用這個方式
面向連接的協議,更好的保證通信的正確性和完整性
2)unix socket
不需要網絡協議棧、打包拆包等
減少tcp 開銷,效率比tcp socket 更高
高并發時候不穩定,連接數暴增產生大量的長時緩存,大數據包可能直接返回異常
到此,關于“PHP面試題基礎知識有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。