PHP 面試常見問題

過去在準備面試,收集簡單整理分享出來,是否過時或有誤,在自己斟酌反查確認

資料庫優化的方法

  • 如果資料欄位確定永遠不會為空,可以設置 NOT NULL,但如果欄位有可能是空值,強制使用 NOT NULL 可能會帶來不必要的約束
  • 選取最適用的欄位屬性,儘可能減少定義欄位寬度
  • 使用 MYSQL 8 ENUM 確實能節省空間,但擴展性有限,當需要頻繁修改選項時,維護成本較高,如果選項較少且固定,這是個好選擇
  • 從結構層: 負載均衡、主從複製、讀寫分離,負載均衡與讀寫分離可以減少伺服器壓力,但需要根據應用的實際讀寫負載來設計
  • 從儲存層: 選擇合適的儲存引擎與正規化,InnoDB 通常是推薦的儲存引擎,因為它支援 ACID 和行級鎖定。正規化應根據具體需求採取適當的級別,過度正規化會降低效能,適當使用逆正規化可提升查詢速度
  • 從設計層: 分割槽、分表、索引、逆正規化、快取,適合在資料量極大的情況下使用。分表、分區需要謹慎設計,並考慮到系統成長性與複雜度。MySQL 快取在高讀取頻率的場景下非常有效
  • SQL 語句層: 高效 SQL 語句,確保 SQL 語句的簡潔和高效性,避免過度複雜的查詢會提升效能

雙引號 “” 和單引號 ” 的區別

  • 單引號內部的變數不會執行, 雙引號會執行
    • 單引號解析速度比雙引號快,因為單引號不需要解析變數插值,但在現代語言和執行環境中,這種性能差異極其微小,幾乎可以忽略不計
    • 單引號只能解析部分特殊字元,雙引號可以解析所有特殊字元
    • 雙引號解釋轉義字符,單引號不解釋轉義字符,但是解釋’\和\\
    • 能使單引號字符儘量使用單引號,單引號的效率比雙引號要高,因為雙引號要先遍歷一遍,判斷裡面有沒有變量,然後再進行操作,而單引號則不需要判斷

    怎麼保證促銷商品不會超賣?

    超賣的原因主要是下的訂單的數目和要促銷的商品的數目不一致導致的,每次總是訂單的數比我們的促銷商品的數目要多

    • 第一種方案: 在每次下訂單前我們判斷促銷商品的數量夠不夠,不夠不允許下訂單,更改庫存量時加上一個條件,只更改商品庫存大於0的商品的庫存,當時我們使用AB進行壓力測試,當併發超過500,訪問量超過2000時,還是會出現超賣現象,因此單純依賴檢查庫存並不足夠,需結合其他機制來確保併發場景下的數據一致性
    • 第二種方案: 使用MYSQL的事務加排他鎖(Exclusive Locks)來解決,若資料庫的儲存引擎為innoDB,使用ROW他鎖實現,測試了下共享鎖(Shared locks),發現還是會出現超賣的現象。有個問題是,當我們進行高併發測試時,對資料庫的效能影響很大,導致資料庫的壓力很大,最終也被我們否定了
    • 第三種方案: 使用檔案鎖實現。當用戶搶到一件促銷商品後先觸發檔案鎖,防止其他使用者進入,該使用者搶到促銷品後再解開檔案鎖,放其他使用者進行操作。這樣可以解決超賣的問題,但是會導致檔案得I/O開銷很大
    • 最後我們使用了Redis的佇列來實現。將要促銷的商品數量以佇列的方式存入redis中,每當使用者搶到一件促銷商品則從佇列中刪除一個數據,確保商品不會超賣。這個操作起來很方便,而且效率極高,最終我們採取這種方式來實現

    include() 和 require()方法的差別

    • require 在找不到檔案時會觸發 Fatal Error 進而使程式執行停止
    • include 在找不到檔案時只會觸發 Warning 所以不對程式有任何影響
    • require是無條件包含,也就是如果一個流程里加入require,無論條件成立與否都會先執行require,當文件不存在或者無法打開的時候,會提示錯誤,並且會終止程序執行
    • include()require() 都有返回值。兩者都會返回包含文件的結果,如果被包含的文件內有 return 語句,它會返回該值。如果文件不包含任何 return,則返回 1
    • require()include() 在性能上基本沒有明顯的差異,兩者執行時的開銷非常相似。任何可能的差異通常是微不足道的,並且在現代 PHP 版本中幾乎可以忽略不計

    了解XSS攻擊嗎?如何防止?

    • XSS是跨站腳本攻擊,首先是利用跨站腳本漏洞以一個特權模式去執行攻擊者構造的腳本,然後利用不安全的Activex控制項執行惡意的行為
    • 使用 htmlspecialchars() 對提交的內容進行過濾是一個基礎的防禦方法,它將 HTML 中的特殊字符(如 <, >, &, ")轉換成實體,避免它們被解釋為 HTML 或腳本,除了輸入過濾外,還應該根據輸入位置進行有針對性的編碼
    • 跨網站指令碼XSS (Cross-site scripting)攻擊,是一種針對網站應用程式設計不夠嚴謹所導致的安全弱點攻擊行為;最原始的弱點是允許攻擊者將惡意程序植入網頁中,導致使用者瀏覽網頁時,受到不同程度的影響,如在背景中下載惡意程序,後門開啟或是密碼與個人資料之竊取
    • 加入編碼:不管是HtmlEncode或者UrlEncode,在系統中只要是有可能造成錯誤或者資安漏洞的,應該都要先進行編碼
    • 設定TextBox的MaxLength屬性:非常常見的方法,只要你無法突破最大長度的限制,你想登打什麼樣的資料都無法達成
    • 對cookie資料加密:如果你的系統會將cookie中的資料直接拿來使用,而沒有經過特別的處理,那是非常可能被當成一個入侵的點,例如下面這個例子,直接讀取cookie的值,並直接將form送出,如果cookie的值被別人修改過,那安全性就岌岌可危了

    SQL注入漏洞產生的原因?如何防止?

    • SQL 注入是因為應用程式直接將未經過濾或未經安全處理的用戶輸入嵌入到 SQL 查詢語句中。這導致攻擊者可以通過惡意輸入來操縱查詢語句,執行任意的 SQL 指令,可能包括查詢、修改、刪除數據,甚至控制整個數據庫
    • 防止 SQL 注入的最佳方法是使用預備語句(Prepared Statements)和參數化查詢,並結合輸入過濾、驗證、最小權限控制等多種手段來增強應用程式的安全性

    防止SQL注入的方式

    • 不要依賴 magic_quotes,而是使用參數化查詢或預備語句來避免 SQL 注入,使用magic_quotes_gpcmagic_quotes_runtime 是 PHP 早期用來自動為用戶輸入的特殊字符添加反斜杠的配置。這些配置已被棄用並在 PHP 5.4.0 後完全移除,因為這種方式並不能可靠地防止 SQL 注入,且會導致開發人員混淆如何處理數據
    • 不要使用 addslashes(),應使用專門針對 SQL 查詢的轉義函數,如 mysqli_real_escape_string() 或 PDO 的預備語句,最好的做法是使用參數化查詢來自動處理所有輸入,而不需要手動轉義
    • 使用參數化查詢來完全隔離 SQL 語句和用戶輸入,這樣即使輸入中含有關鍵詞,也無法改變查詢的結構
    • register_globals 會自動將用戶提交的數據注入全局變量,這會導致嚴重的安全風險,包括 SQL 注入。因此,應始終保持 register_globalsoff
    • 控制錯誤信息,不要在瀏覽器上輸出錯誤信息,將錯誤信息寫到日誌文件中
    • 使用現代的 mysqli_real_escape_string() 或 PDO 的參數化查詢來完全防止 SQL 注入。參數化查詢是將 SQL 語句和數據分開處理,從而杜絕 SQL 注入的風險
    • 舊的防範方式如 magic_quotes_gpcaddslashes() 已經過時並且存在缺陷。如今防止 SQL 注入的最佳實踐是使用參數化查詢和預備語句,並結合適當的輸入過濾、驗證以及嚴格控制資料庫權限等方法。這樣可以大幅降低 SQL 注入的風險並增強應用程式的安全性

    magic_quotes_gpc=off跟 magic_quotes_gpc=on的差別

    • OFF寫入資料庫的字串未經過任何過濾處理。從資料庫讀出的字串也未作任何處理
    • ON寫入資料庫的字串經過函數addslashes()處理。從資料庫讀出的字串未作任何處理

    InnoDB與MyISAM的區別

    InnoDB存儲引擎

    • 主要面向OLTP(Online Transaction Processing,在線事務處理)方面的應用,是第一個完整支持ACID事務的存儲引擎(BDB第一個支持事務的存儲引擎,已經停止開發)
    • 特點
      • ROW行鎖設計、支持外鍵
      • 支持類似於Oracle風格的一致性非鎖定讀(即:默認情況下讀取操作不會產生鎖)
      • InnoDB將數據放在一個邏輯的表空間中,由InnoDB自身進行管理。從MySQL4.1版本開始,可以將每個InnoDB存儲引擎的表單獨存放到一個獨立的ibd文件中
      • InnoDB通過使用MVCC(多版本並發控制:讀不會阻塞寫,寫也不會阻塞讀)來獲得高並發性,並且實現了SQL標準的4種隔離級別(默認為REPEATABLE級別)
      • InnoDB還提供了插入緩衝(insert buffer)、二次寫(double write)、自適應哈希索引(adaptive hash index)、預讀(read ahead)等高性能和高可用的功能
      • InnoDB採用了聚集(clustered)的方式來存儲表中的數據,每張標的存儲都按主鍵的順序存放(如果沒有顯式的在建表時指定主鍵,InnoDB會為每一行生成一個6字節的ROWID,並以此作為主鍵)
      • InnoDB表會有三個隱藏欄位:除了上面提到了6字節的DB_ROW_ID外,還有6字節的DB_TX_ID(事務ID)和7字節的DB_ROLL_PTR(指向對應回滾段的地址)。這個可以通過innodb monitor看到

    MyISAM存儲引擎

    • 是MySQL官方提供的存儲引擎,主要面向OLAP(Online Analytical Processing,在線分析處理)方面的應用
    • 特點
      • 不支持事務,支持表所和全文索引
      • 操作速度
      • MyISAM存儲引擎表由MYD和MYI組成,MYD用來存放數據文件,MYI用來存放索引文件。MySQL資料庫只緩存其索引文件,數據文件的緩存交給作業系統本身來完成
      • MySQL5.0版本開始,MyISAM默認支持256T的單表數據

    解釋MySQL外連接、內連接與自連接的區別

    1. 交叉連接(CROSS JOIN):無條件連接,生成兩張表的笛卡爾積,通常會產生大量數據
    2. 內連接(INNER JOIN):只返回兩張表中符合條件的行,即只返回匹配的行
    3. 外連接(OUTER JOIN):包括左外連接(LEFT JOIN)、右外連接(RIGHT JOIN)和全外連接(FULL OUTER JOIN)。左外連接和右外連接在結果中保留未匹配表中的行,並用 NULL 填充另一表的對應欄位。MySQL 不支持全外連接,但可以用 UNION 模擬
    4. 自連接(SELF JOIN):表與自身進行連接,通常用於查詢表中的關聯數據

    這些不同的連接方式為數據庫操作提供了靈活的選擇,可以根據不同的業務需求進行篩選和匹配數據

    Session 和 Cookie 的區別

    • Session 是伺服器端的存儲機制,用來保存用戶的會話數據,安全性更高但會佔用伺服器資源,適合存儲敏感數據或需要更高安全性的應用場景。
    • Cookie 是客戶端的存儲機制,存儲在用戶的瀏覽器中,適合存儲一些輕量且不敏感的數據,使用時需要注意安全性和隱私問題。
    • 兩者的互補性:Session 和 Cookie 可以結合使用,Session 通常依賴 Cookie 來傳遞 Session ID,而 Cookie 可以用來存儲一些不重要的、需要持久化的數據,如偏好設置等。

    根據不同的應用需求,選擇合適的技術來實現狀態保持和數據存儲。

    購物車實現方式:Cookie、Session 與資料庫結合

    • Cookie:適合不敏感的購物車信息,具有輕量、持久性好、不依賴伺服器資源的優點,但安全性較低,並且用戶可能禁用 Cookie
    • Session:更安全,適合存儲敏感的購物車數據,但會佔用伺服器資源,並且需要考慮過期和伺服器資源的使用問題
    • 結合資料庫:可以在 Cookie 和 Session 的基礎上提升購物車數據的持久性和安全性,但會增加伺服器負擔

    根據不同的應用場景,可以選擇單獨使用 Cookie 或 Session,或者與資料庫結合使用來實現更加靈活和高效的購物車功能

    Nginx 與 Apache 比較

    Nginx

    • 在處理高併發、高流量的靜態頁面和代理請求時,性能優勢明顯
    • 更輕量、資源佔用少
    • 配置和擴展模組需要重新編譯,不如 Apache 靈活,但性能更高

    Apache

    • 擁有豐富的模組庫和靈活的模組加載機制
    • 在 URL 重寫、動態內容處理、細粒度的配置(例如 .htaccess 文件)上具有優勢
    • 與 Nginx 相比,對於高併發和高效能要求的應用可能稍遜色