2022年3月4日 星期五

《面試官別再問》PHP 面試整理

1. 列舉一些PHP 中的設計模式?

    單例模式:保證在整個應用程序的生命週期中,任何一個時刻,單例類的實例都只存在一個,同時這個類還必須提供一個訪問該類的全局訪問點。

    工廠模式:定義一個創建物件的接口,但是讓子類去實例化具體類。工廠方法模式讓類的實例化延遲到子類中。

    觀察者模式:觀察者模式有時也被稱作發布/訂閱模式,該模式用於為物件實現發布/訂閱功能:一旦主體物件狀態發生改變,與之關聯的觀察者物件會收到通知,並進行相應操作。

    適配器模式:適配器模式將一個類的接口轉換成客戶希望的另外一個接口,使得原本由於接口不兼容而不能一起工作的那些類可以在一起工作。

    依賴注入模式:依賴注入(DependencyInjection)是控制反轉(Inversion ofControl)的一種實現方式。要實現控制反轉,通常的解決方案是將創建被調用者實例的工作交由IoC 容器來完成,然後在調用者中註入被調用者(通過構造器/方法注入實現),這樣我們就實現了調用者與被調用者的解耦,該過程被稱為依賴注入。

    門面模式:門面模式(Facade)又稱外觀模式,用於為子系統中的一組接口提供一個一致的界面。


2. Session可不可以設置失效時間,比如30分鐘過期

    設置seesion.cookie_lifetime有30分鐘,並設置session.gc_maxlifetime為30分鐘

    自己為每一個Session值增加timestamp

    每次訪問之前, 判斷時間戳


3. 類的靜態調用和實例化調用各自的利弊

    靜態方法是類中的一個成員方法,屬於整個類,即使不用創建任何對像也可以直接調用!靜態方法效率上要比實例化高,靜態方法的缺點是不自動銷毀,而實例化的則可以做銷毀。


4. Redis五種資料類型及應用場景

    String: 一般做一些複雜的計數功能的緩存

    List: 做簡單的消息隊列的功能

    Hash: 單點登錄

    Set: 做全局去重的功能

    SortedSet: 做排行榜應用,取TopN操作;延時任務;做範圍查找


    Redis string 是redis 最基本的類型,你可以理解成與Memcached 一模一樣的類型,一個key 對應一個value。value其實不僅是String,也可以是數字。string 類型是二進制安全的。

    意思是redis 的string 可以包含任何資料。比如jpg圖片或者序列化的物件。string 類型是Redis 最基本的資料類型,string 類型的值最大能存儲512MB。

    常用命令:get、set、incr、decr、mget等。

    應用場景: String是最常用的一種資料類型,普通的key/ value 存儲都可以歸為此類,即可以完全實現目前Memcached 的功能,並且效率更高。還可以享受Redis的定時持久化,操作日誌及Replication等功能。

    Redis hash 是一個鍵值(key => value)對集合。Redis hash 是一個string 類型的field 和value 的映射表,hash 特別適合用於存儲物件。

    常用命令:hget,hset,hgetall 等。

    應用場景:我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶信息對像資料,包含以下信息:

    用戶ID為查找的key,存儲的value用戶物件包含姓名,年齡,生日等信息。

    使用場景:存儲部分變更資料,如用戶信息等。

    Redis list 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。

    常用命令:lpush(添加左邊元素),rpush,lpop(移除左邊第一個元素),rpop,lrange(獲取列表片段,LRANGE key start stop)等。

    應用場景:Redis list的應用場景非常多,也是Redis最重要的資料結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現。

    Redis set是string類型的無序集合。集合是通過hashtable實現的,概念和數學中個的集合基本類似,可以交集,並集,差集等等,set中的元素是沒有順序的。所以添加,刪除,查找的複雜度都是O(1)。

    sadd 命令:添加一個string 元素到key 對應的set 集合中,成功返回1,如果元素已經在集合中返回0,如果key 對應的set 不存在則返回錯誤。

    常用命令:sadd,spop,smembers,sunion 等。

    應用場景:Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的,當你需要存儲一個列表資料,又不希望出現重複資料時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。

    Set 就是一個集合,集合的概念就是一堆不重複值的組合。利用Redis提供的Set資料結構,可以存儲一些集合性的資料。

    Redis zset 和set 一樣也是string類型元素的集合,且不允許重複的成員。

    zadd 命令:添加元素到集合,元素在集合中存在則更新對應score。

    常用命令:zadd,zrange,zrem,zcard等

    使用場景:Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以通過用戶額外提供一個優先級(score)的參數來為成員排序,並且是插入有序的,即自動排序。

    當你需要一個有序的並且不重複的集合列表,那麼可以選擇sorted set資料結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣獲取時就是自動按時間排好序的。和Set相比,Sorted Set關聯了一個double類型權重參數score,使得集合中的元素能夠按score進行有序排列,redis正是通過分數來為集合中的成員進行從小到大的排序。

    zset的成員是唯一的,但分數(score)卻可以重複。比如一個存儲全班同學成績的Sorted Set,其集合value可以是同學的學號,而score就可以是其考試得分,這樣在資料插入集合的時候,就已經進行了天然的排序。另外還可以用Sorted Set來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。


5. Redis 緩存雪崩、擊穿、穿透

    什麼是緩存雪崩?

    當某一個時刻出現大規模的緩存失效的情況,那麼就會導致大量的請求直接打在資料庫上面,導致資料庫壓力巨大,如果在高並發的情況下,可能瞬間就會導致資料庫宕機。這時候如果運維馬上又重啟資料庫,馬上又會有新的流量把資料庫打死。這就是緩存雪崩。

    解決方案:

    1、在原有的失效時間上加上一個隨機值,比如1-5分鐘隨機。這樣就避免了因為採用相同的過期時間導致的緩存雪崩。

    2、使用熔斷機制。當流量到達一定的閾值時,就直接返回“系統擁擠”之類的提示,防止過多的請求打在數據庫上。至少能保證一部分用戶是可以正常使用,其他用戶多刷新幾次也能得到結果。

    3、提高數據庫的容災能力,可以使用分庫分錶,讀寫分離的策略。

    什麼是緩存擊穿?

    其實跟緩存雪崩有點類似,緩存雪崩是大規模的key失效,而緩存擊穿是一個熱點的Key,有大並發集中對其進行訪問,突然間這個Key失效了,導致大並發全部打在數據庫上,導致數據庫壓力劇增。這種現象就叫做緩存擊穿。

    解決方案:

    1、上面說過了,如果業務允許的話,對於熱點的key可以設置永不過期的key。

    2、使用互斥鎖。如果緩存失效的情況,只有拿到鎖才可以查詢數據庫,降低了在同一時刻打在數據庫上的請求,防止數據庫打死。當然這樣會導致系統的性能變差。

    什麼是緩存穿透?

    使用Redis大部分情況都是通過Key查詢對應的值,假如發送的請求傳進來的key是不存在Redis中的,那麼就查不到緩存,查不到緩存就會去數據庫查詢。假如有大量這樣的請求,這些請求像“穿透”了緩存一樣直接打在數據庫上,這種現象就叫做緩存穿透。

    解決方案:

    1、把無效的Key存進Redis中。如果Redis查不到數據,數據庫也查不到,我們把這個Key值保存進Redis,設置value="null",當下次再通過這個Key查詢時就不需要再查詢數據庫。這種處理方式肯定是有問題的,假如傳進來的這個不存在的Key值每次都是隨機的,那存進Redis也沒有意義。

    2、使用布隆過濾器。布隆過濾器的作用是某個key 不存在,那麼就一定不存在,它說某個key 存在,那麼很大可能是存在(存在一定的誤判率)。於是我們可以在緩存之前再加一層布隆過濾器,在查詢的時候先去布隆過濾器查詢key 是否存在,如果不存在就直接返回。


6. php7新特性

    1.可以使用字符串(string), 整數(int), 浮點數(float), 以及布爾值(bool),來聲明函數的參數類型與函數返回值

    2.新增操作符“<=>” 語法:$c = $a <=> $b 

    如果$a > $b, $c 的值為1

    如果$a == $b, $c 的值為0

    如果$a < $b, $c 的值為-1

    3.新增操作符“??” 如果變數存在且值不為NULL, 它就會返回自身的值,否則返回它的第二個操作數。

    //原寫法

    $username = isset($_GET['user]) ? $_GET['user] : 'nobody';

    //新寫法

    $username = $_GET['user'] ?? 'nobody';

    4.define() 定義常量陣列

    define('ARR',['a','b']);

    echo ARR[1];// a

    5.命名空間引用優化

    // PHP7以前语法的写法

    use FooLibrary\Bar\Baz\ClassA;

    use FooLibrary\Bar\Baz\ClassB;

    // PHP7新语法写法

    use FooLibrary\Bar\Baz\{ ClassA, ClassB};

    6.list修改

    不再按照相反的順序賦值

    //$arr将会是[1,2,3]而不是之前的[3,2,1]

    list($arr[], $arr[], $arr[]) = [1,2,3];

    不再支持字符串拆分功能

    // $x = null 并且 $y = null

    $str = 'xy';

    list($x, $y) = $str;

    空的list()賦值不再允許

    list() = [123];

    list()現在也適用於陣列物件

    list($a, $b) = (object)new ArrayObject([0, 1]);


7. 如何設計/優化一個訪問量比較大的部落格/論壇

    減少http請求(比如使用雪碧圖)

    優化數據庫(範式、SQL語句、索引、配置、讀寫分離)

    緩存使用(Memcache、Redis)

    負載均衡

    動態內容靜態化+CDN

    禁止外部盜鏈(refer、圖片添加水印)

    控制大文件下載

    使用集群

    

8. PHP使用Nginx實現反向代理

    1、什麼是代理伺服器

    代理伺服器,客戶機在發送請求時,不會直接發送給目的主機,而是先發送給代理伺服器,代理服務接受客戶機請求之後,再向主機發出,並接收目的主機返回的數據,存放在代理伺服器的硬盤中,再發送給客戶機。

    2、為什麼要使用代理伺服器

    1)提高訪問速度

    由於目標主機返回的數據會存放在代理伺服器的硬盤中,因此下一次客戶再訪問相同的站點數據時,會直接從代理伺服器的硬盤中讀取,起到了緩存的作用,尤其對於熱門站點能明顯提高請求速度。

    2)防火牆作用

    由於所有的客戶機請求都必須通過代理伺服器訪問遠程站點,因此可在代理伺服器上設限,過濾某些不安全信息。

    3)通過代理伺服器訪問不能訪問的目標站點

    互聯網上有許多開發的代理伺服器,客戶機在訪問受限時,可通過不受限的代理伺服器訪問目標站點,通俗說,我們使用的翻牆瀏覽器就是利用了代理伺服器,雖然不能出國,但也可直接訪問外網。

    二、反向代理VS 正向代理

    1、什麼是正向代理?什麼是反向代理?

    正向代理,架設在客戶機與目標主機之間,只用於代理內部網絡對Internet的連接請求,客戶機必須指定代理伺服器,並將本來要直接發送到Web伺服器上的http請求發送到代理伺服器中。

    反向代理伺服器架設在伺服器端,通過緩衝經常被請求的頁面來緩解伺服器的工作量,將客戶機請求轉發給內部網絡上的目標伺服器;並將從伺服器上得到的結果返回給Internet上請求連接的客戶端,此時代理伺服器與目標主機一起對外表現為一個伺服器。

    2、反向代理有哪些主要應用?

    現在許多大型web網站都用到反向代理。除了可以防止外網對內網伺服器的惡性攻擊、緩存以減少伺服器的壓力和訪問安全控制之外,還可以進行負載均衡,將用戶請求分配給多個伺服器。


9. php構建api上的CORS問題

    跨域資源共享 CORS (Cross-origin resource sharing) 是一個 W3C 標準

    這裡是常見的設定方式:

    <?php

      header("Access-Control-Allow-Origin: *");

      header("Access-Control-Allow-Methods: *");

      header("Access-Control-Allow-Headers: Origin, Methods, Content-Type");

沒有留言:

張貼留言

熱門文章