python中與數據庫相關的面試題

數據庫作爲面試中的重要部分,當然也需要整理

1.爲什麼要優化

系統的吞吐量瓶頸往往出現在數據庫的訪問速度上,隨着應用程序的運行,數據庫的中的數據會越來越多,處理時間會相應變慢,數據是存放在磁盤上的,讀寫速度無法和內存相比

2.如何優化

設計數據庫時:數據庫表、字段的設計,存儲引擎,利用好MySQL自身提供的功能,如索引等
橫向擴展:MySQL集羣、負載均衡、讀寫分離 SQL語句的優化(收效甚微)

3.字段設計

字段類型的選擇,設計規範,範式,

常見設計案例
原則:儘量使用整型表示字符串
存儲IP INET_ATON(str),address to number
INET_NTOA(number),number to address

MySQL內部的枚舉類型(單選)和集合(多選)類型
但是因爲維護成本較高因此不常使用,使用關聯表的方式來替代enum

原則:定長和非定長數據類型的選擇 decimal不會損失精度,存儲空間會隨數據的增大而增大。double佔用固定空間,較大數的存儲會損失精度。
非定長的還有varchar、text

金額 對數據的精度要求較高,小數的運算和存儲存在精度問題(不能將所有小數轉換成二進制)
定點數decimal price decimal(8,2)有2位小數的定點數,定點數支持很大的數(甚至是超過int,bigint存儲範圍的數)

小單位大數額避免出現小數 元->分

字符串存儲 定長char,非定長varchar、text(上限65535,其中varchar還會消耗1-3字節記錄長度,而text使用額外空間記錄長度)

原則:儘可能選擇小的數據類型和指定短的長度 原則:儘可能使用 not null 非null字段的處理要比null字段的處理高效些!且不需要判斷是否爲null

null在MySQL中,不好處理,存儲需要額外空間,運算也需要特殊的運算符。
如select null = null和select null <> null(<>爲不等號)有着同樣的結果,只能通過is null和is not null來判斷字段是否爲null。

如何存儲?MySQL中每條記錄都需要額外的存儲空間,表示每個字段是否爲null。
因此通常使用特殊的數據進行佔位,比如int not null default 0、string not null default ‘’

原則:字段註釋要完整,見名知意
原則:單表字段不宜過多 二三十個就極限了
原則:可以預留字段
在使用以上原則之前首先要滿足業務需求
關聯表的設計
外鍵foreign key只能實現一對一或一對多的映射
一對多 使用外鍵
多對多 單獨新建一張表將多對多拆分成兩個一對多
一對一 如商品的基本信息(item)和商品的詳細信息(item_intro),通常使用相同的主鍵或者增加一個外鍵字段(item_id)

4.範式 Normal Format

數據表的設計規範,一套越來越嚴格的規範體系(如果需要滿足N範式,首先要滿足N-1範式)。

第一範式1NF:字段原子性 字段原子性,字段不可再分割。

關係型數據庫,默認滿足第一範式 注意比較容易出錯的一點,在一對多的設計中使用逗號分隔多個外鍵,這種方法雖然存儲方便,但不利於維護和索引(比如查找帶標籤java的文章)

第二範式:消除對主鍵的部分依賴

即在表中加上一個與業務邏輯無關的字段作爲主鍵
主鍵:可以唯一標識記錄的字段或者字段集合。

course_namecourse_
classweekday(周幾)
course_teacherMySQL教育大樓
1525週一張三Java教育大樓
1521週三李四MySQL教育大樓
1521週五張三

依賴:A字段可以確定B字段,則B字段依賴A字段。 比如知道了下一節課是數學課,就能確定任課老師是誰。
於是周幾和下一節課和就能構成複合主鍵,能夠確定去哪個教室上課,任課老師是誰等。 但我們常常增加一個id作爲主鍵,而消除對主鍵的部分依賴。

對主鍵的部分依賴:某個字段依賴複合主鍵中的一部分。
解決方案:新增一個獨立字段作爲主鍵。

第三範式:消除對主鍵的傳遞依賴

傳遞依賴:B字段依賴於A,C字段又依賴於B。
比如上例中,任課老師是誰取決於是什麼課,是什麼課又取決於主鍵id。
因此需要將此表拆分爲兩張表日程表和課程表(獨立數據獨立建表):

idweekdaycourse_classcourse_id 1001 週一教育大樓
15213546 course_idcourse_namecourse_teacher 3546 Java 張三

這樣就減少了數據的冗餘(即使週一至週日每天都有Java課,也只是course_id:3546出現了7次)

5.存儲引擎選擇

早期問題:如何選擇MyISAM和Innodb?

現在不存在這個問題了,Innodb不斷完善,從各個方面趕超MyISAM,也是MySQL默認使用的。
存儲引擎Storage engine:MySQL中的數據、索引以及其他對象是如何存儲的,是一套文件系統的實現。

功能差異 show engines

EngineSupportCommentInnoDBDEFAULTSupports transactions, row-level locking, and foreign keysMyISAMYESMyISAM storage engine

存儲差異
MyISAMInnodb文件格式數據和索引是分別存儲的,數據.MYD,
索引.MYI數據和索引是集中存儲的,.ibd文件能否移動能,一張表就對應.frm、MYD、MYI3個文件否,因爲關聯的還有data下的其它文件記錄存儲順序按記錄插入順序保存按主鍵大小有序插入空間碎片(刪除記錄並flush table 表名之後,表文件大小不變)產生。
定時整理:使用命令optimize table 表名實現不產生事務不支持支持外鍵不支持支持鎖支持(鎖是避免資源爭用的一個機制,MySQL鎖對用戶幾乎是透明的)表級鎖定行級鎖定、表級鎖定,鎖定力度小併發能力高

鎖擴展 表級鎖(table-level lock):lock tables ,… read/write,unlock tables ,…。

其中read是共享鎖,一旦鎖定任何客戶端都不可讀;
write是獨佔/寫鎖,只有加鎖的客戶端可讀可寫,其他客戶端既不可讀也不可寫。鎖定的是一張表或幾張表。
行級鎖(row-level lock):鎖定的是一行或幾行記錄。
共享鎖:select from where <條件> LOCK IN SHARE MODE;,對查詢的記錄增加共享鎖;
select from where <條件> FOR UPDATE;,對查詢的記錄增加排他鎖。
這裏值得注意的是:innodb的行鎖,其實是一個子範圍鎖,依據條件鎖定部分範圍,而不是就映射到具體的行上,因此還有一個學名:間隙鎖。
比如select * from stu where id < 20 LOCK IN SHARE MODE會鎖定id在20左右以下的範圍,你可能無法插入id爲18或22的一條新紀錄。
選擇依據 如果沒有特別的需求,使用默認的Innodb即可。

MyISAM:以讀寫插入爲主的應用程序,比如博客系統、新聞門戶網站。

Innodb:更新(刪除)操作頻率也高,或者要保證數據的完整性;併發量高,支持事務和外鍵保證數據完整性。比如OA自動化辦公系統。

6.索引

關鍵字與數據的映射關係稱爲索引(包含關鍵字和對應的記錄在磁盤中的地址)。
關鍵字是從數據當中提取的用於標識、檢索數據的特定內容。

索引檢索爲什麼快?

關鍵字相對於數據本身,數據量小 關鍵字是有序的,二分查找可快速確定位置 圖書館爲每本書都加了索引號(類別-樓層-書架)、字典爲詞語解釋按字母順序編寫目錄等都用到了索引。

MySQL中索引類型 普通索引(key),唯一索引(unique key),主鍵索引(primary key),全文索引(fulltext key) 三種索引的索引方式是一樣的,只不過對索引的關鍵字有不同的限制:

普通索引:對關鍵字沒有限制
唯一索引:要求記錄提供的關鍵字不能重複
主鍵索引:要求關鍵字唯一且不爲null
索引管理語法 查看索引 show create table 表名:

7.執行計劃explain

我們可以通過explain selelct來分析SQL語句執行前的執行計劃:
執行計劃是:當執行SQL語句時,首先會分析、優化,形成執行計劃,在按照執行計劃執行。

8.索引使用場景(重點)
where

order by 當我們使用order by將查詢結果按照某個字段排序時,如果該字段沒有建立索引,那麼執行計劃會將查詢出的所有數據使用外部排序(將數據從硬盤分批讀取到內存使用內部排序,最後合併排序結果),這個操作是很影響性能的,因爲需要將查詢涉及到的所有數據從磁盤中讀到內存(如果單條數據過大或者數據量過多都會降低效率),更無論讀到內存之後的排序了。

但是如果我們對該字段建立索引alter table 表名 add index(字段名),那麼由於索引本身是有序的,因此直接按照索引的順序和映射關係逐條取出數據即可。而且如果分頁的,那麼只用取出索引表某個範圍內的索引對應的數據,而不用像上述那取出所有數據進行排序再返回某個範圍內的數據。(從磁盤取數據是最影響性能的)

join

對join語句匹配關係(on)涉及的字段建立索引能夠提高效率

like查詢,不能以通配符開頭 比如搜索標題包含mysql的文章:

select * from article where title like ‘%mysql%’; 這種SQL的執行計劃用不了索引(like語句匹配表達式以通配符開頭),因此只能做全表掃描,效率極低,在實際工程中幾乎不被採用。而一般會使用第三方提供的支持中文的全文索引來做。

但是 關鍵字查詢 熱搜提醒功能還是可以做的,比如鍵入mysql之後提醒mysql 教程、mysql 下載、mysql 安裝步驟等。用到的語句是:

select * from article where title like ‘mysql%’; 這種like是可以利用索引的(當然前提是title字段建立過索引)。

9.索引的存儲結構

BTree btree(多路平衡查找樹)是一種廣泛應用於磁盤上實現索引功能的一種數據結構,也是大多數數據庫索引表的實現。

BTree的一個node可以存儲多個關鍵字,node的大小取決於計算機的文件系統,因此我們可以通過減小索引字段的長度使結點存儲更多的關鍵字。
如果node中的關鍵字已滿,那麼可以通過每個關鍵字之間的子節點指針來拓展索引表,但是不能破壞結構的有序性,比如按照first_name第一有序、last_name第二有序的規則
這與二叉搜索樹的思想是一樣的,只不過二叉搜索樹的查找效率是log(2,N)(以2爲底N的對數),而BTree的查找效率是log(x,N)(其中x爲node的關鍵字數量,可以達到1000以上)。

10.select * 要少用

即儘量選擇自己需要的字段select,但這個影響不是很大,因爲網絡傳輸多了幾十上百字節也沒多少延時,並且現在流行的ORM框架都是用的select *,只是我們在設計表的時候注意將大數據量的字段分離,比如商品詳情可以單獨抽離出一張商品詳情表,這樣在查看商品簡略頁面時的加載速度就不會有影響了。

11.order by rand()不要用

它的邏輯就是隨機排序(爲每條數據生成一個隨機數,然後根據隨機數大小進行排序)。如select * from student order by rand() limit 5的執行效率就很低,因爲它爲表中的每條數據都生成隨機數並進行排序,而我們只要前5條。

解決思路:在應用程序中,將隨機的主鍵生成好,去數據庫中利用主鍵檢索。

12.單表和多表查詢

多表查詢:join、子查詢都是涉及到多表的查詢。如果你使用explain分析執行計劃你會發現多表查詢也是一個表一個表的處理,最後合併結果。因此可以說單表查詢將計算壓力放在了應用程序上,而多表查詢將計算壓力放在了數據庫上。

現在有ORM框架幫我們解決了單表查詢帶來的對象映射問題(查詢單表時,如果發現有外鍵自動再去查詢關聯表,是一個表一個表查的)。

13.count(*)
在MyISAM存儲引擎中,會自動記錄表的行數,因此使用count(*)能夠快速返回。而Innodb內部沒有這樣一個計數器,需要我們手動統計記錄數量,解決思路就是單獨使用一張表:

idtablecount1student100

14.limit 1
如果可以確定僅僅檢索一條,建議加上limit 1,其實ORM框架幫我們做到了這一點(查詢單條的操作都會自動加上limit 1)。

15.慢查詢日誌
用於記錄執行時間超過某個臨界值的SQL日誌,用於快速定位慢查詢,爲我們的優化做參考。 開啓慢查詢日誌 配置項:slow_query_log

可以使用show variables like ‘slov_query_log’查看是否開啓,如果狀態值爲OFF,可以使用set GLOBAL slow_query_log = on來開啓,它會在datadir下產生一個xxx-slow.log的文件。

16.什麼是 MongoDB ?

MongoDB 是一個介於關係數據庫和非關係數據庫之間的開源產品,是最接近於關係型數據庫的 NoSQL 數據庫。

它在輕量級JSON 交換基礎之上進行了擴展,即稱爲 BSON 的方式來描述其無結構化的數據類型。

儘管如此它同樣可以存儲較爲複雜的數據類型。它和Redis有異曲同工之妙。

雖然兩者均爲 NoSQL ,但是 MongoDB 相對於 Redis 而言,MongoDB 更像是傳統的數據庫

早些年我們是先有了 Relation Database (關係型數據庫),然後出現了很多很複雜的query ,裏面用到了很多嵌套,很多 join 操作。所以在設計數據庫的時候,我們也考慮到了如何應用他們的關係,使得寫 query 可以使 database效率達到最高。
後來人們發現,不是每個系統,都需要如此複雜的關係型數據庫。
有些簡單的網站,比如博客,比如社交網站,完全可以斬斷數據庫之間的一切關係。這樣做帶來的好處是,設計數據庫變得更加簡單,寫 query 也變得更加簡單。然後,query 消耗的時間可能也會變少。因爲 query 簡單了,少了許多消耗資源的 join操作,速度自然會上去。
正如所說的, query 簡單了,很有以前 MySQL 可以找到的東西,現在關係沒了,通過 Mongo找不到了。我們只能將幾組數據都抓到本地,然後在本地做 join,所以在這點上可能會消耗很多資源。這裏我們可以發現。如何選擇數據庫,完全取決於你所需要處理的數據的模型,即 Data Model。
如果它們之間,關係錯綜複雜,千絲萬縷,這個時候 MySQL 一定是首選。如果他們的關係並不是那麼密切,那麼, NoSQL 將會是利器。

MongoDB 和 Redis 一樣均爲 key-value 存儲系統,它具有以下特點:

面向集合存儲,易存儲對象類型的數據。
模式自由。
支持動態查詢。
支持完全索引,包含內部對象。
支持查詢。
支持複製和故障恢復。
使用高效的二進制數據存儲,包括大型對象(如視頻等)。
自動處理碎片,以支持雲計算層次的擴展性 支持 Python , PHP , Ruby , Java , C , C# , Javascript ,Perl 及 C++ 語言的驅動程序,社區中也提供了對Erlang 及 .NET 等平臺的驅動程序。
文件存儲格式爲 BSON (一種 JSON 的擴展)。 可通過網絡訪問。

17.MongoDB 與 MySQL 性能比較

像 MySQL 一樣, MongoDB 提供了豐富的遠遠超出了簡單的鍵值存儲中提供的功能和功能。

MongoDB 具有查詢語言,功能強大的輔助索引(包括文本搜索和地理空間),數據分析功能強大的聚合框架等。
相比使用關係數據庫而言,使用MongoDB ,您還可以使用如下表所示的這些功能,跨越更多樣化的數據類型和數據規模。

MySQL 中的許多概念在 MongoDB 中具有相近的類比。

18.MongoDB應用範圍和限制

MongoDB 的主要目標是在 key-value (鍵/值)存儲方式(提供了高性能和高度伸縮性)以及傳統的 RDBMS 系統(豐富的功能)架起一座橋樑,集兩者的優勢於一身。

MongoDB 適用範圍如下:

網站數據: Mongo 非常適合實時的插入,更新與查詢,並具備網站實時數據存儲所需的複製及高度伸縮性。
緩存:由於性能很高, Mongo 也適合作爲信息基礎設施的緩存層。在系統重啓之後,由 Mongo 搭建的持久化緩存層可以避免下層的數據源過載
低價值的數據:使用傳統的關係型數據庫存儲一些數據時可能會比較昂貴,在此之前,很多時候程序員往往會選擇傳統的文件進行存儲。
高伸縮性的場景: Mongo 非常適合由數十或數百臺服務器組成的數據庫。
Mongo 的路線圖中已經包含對 MapReduce 引擎的內置支持。
用於對象及JSON 數據的存儲: Mongo 的 BSON 數據格式非常適合文檔化格式的存儲及查詢。

MongoDB 當然也會有以下場景的限制:

高度事物性的系統:例如銀行或會計系統。傳統的關係型數據庫目前還是更適用於需要大量原子性複雜事務的應用程序。
傳統的商業智能應用:針對特定問題的 BI 數據庫會對產生高度優化的查詢方式。對於此類應用,數據倉庫可能是更合適的選擇。 需要 SQL的問題。

19.Reids的特點

Redis本質上是一個Key-Value類型的內存數據庫,很像memcached,整個數據庫統統加載在內存當中進行操作,定期通過異步操作把數據庫數據flush到硬盤上進行保存。

因爲是純內存操作,Redis的性能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知性能最快的Key-Value DB。

Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數據結構,此外單個value的最大限制是1GB,不像 memcached只能保存1MB的數據,因此Redis可以用來實現很多有用的功能。

比方說用他的List來做FIFO雙向鏈表,實現一個輕量級的高性 能消息隊列服務,用他的Set可以做高性能的tag系統等等。

另外Redis也可以對存入的Key-Value設置expire時間,因此也可以被當作一 個功能加強版的memcached來用。

Redis的主要缺點是數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此Redis適合的場景主要侷限在較小數據量的高性能操作和運算上。

20.使用redis有哪些好處?

1.速度快,因爲數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1)
2.支持豐富數據類型,支持string,list,set,sorted set,hash
3.支持事務,操作都是原子性,所謂的原子性就是對數據的更改要麼全部執行,要麼全部不執行
4.豐富的特性:可用於緩存,消息,按key設置過期時間,過期後將會自動刪除

21.爲什麼redis需要把所有數據放到內存中?

Redis爲了達到最快的讀寫速度將數據都讀到內存中,並通過異步的方式將數據寫入磁盤。
所以redis具有快速和數據持久化的特徵。
如果不將數據放在內存中,磁盤I/O速度爲嚴重影響redis的性能。
在內存越來越便宜的今天,redis將會越來越受歡迎。

如果設置了最大使用的內存,則數據已有記錄數達到內存限值後不能繼續插入新值。

22.Redis是單進程單線程的

redis利用隊列技術將併發訪問變爲串行訪問,消除了傳統數據庫串行控制的開銷

redis持久化的幾種方式

  • 1、快照(snapshots)

缺省情況情況下,Redis把數據快照存放在磁盤上的二進制文件中,文件名爲dump.rdb。
你可以配置Redis的持久化策略,例如數據集中每N秒鐘有超過M次更新,就將數據寫入磁盤;
或者你可以手工調用命令SAVE或BGSAVE。

工作原理

Redis forks.

子進程開始將數據寫到臨時RDB文件中。

當子進程完成寫RDB文件,用新文件替換老文件。

這種方式可以使Redis使用copy-on-write技術。

  • 2、AOF

快照模式並不十分健壯,當系統停止,或者無意中Redis被kill掉,最後寫入Redis的數據就會丟失。

這對某些應用也許不是大問題,但對於要求高可靠性的應用來說,Redis就不是一個合適的選擇。Append-only文件模式是另一種選擇。你可以在配置文件中打開AOF模式

  • 3、虛擬內存方式

當你的key很小而value很大時,使用VM的效果會比較好.因爲這樣節約的內存比較大.

當你的key不小時,可以考慮使用一些非常方法將很大的key變成很大的value,比如你可以考慮將key,value組合成一個新的value.

vm-max-threads這個參數,可以設置訪問swap文件的線程數,設置最好不要超過機器的核數,如果設置爲0,那麼所有對swap文件的操作都是串行的.可能會造成比較長時間的延遲,但是對數據完整性有很好的保證.

自己測試的時候發現用虛擬內存性能也不錯。
如果數據量很大,可以考慮分佈式或者其他數據庫。

再來一篇MySQL常見面試題
歡迎採閱!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章