數據庫相關------一些面試題

1.列舉常見的關係型數據庫和非關係型都有那些、它們的區別?

  關係型數據庫:Oracle、DB二、Microsoft SQL Server、Microsoft Access、MySQLphp

  非關係型數據庫:NoSql、Cloudant、MongoDB、Redis、HBasepython

  二者的區別:mysql

  關係型數據庫 非關係型數據庫
特性

  一、關係型數據庫,是指採用了關係模型來組織數據的數據庫;  
  二、關係型數據庫的最大特色就是事務的一致性;
  三、簡單來講,關係模型指的就是二維表格模型,而一個關係型數據庫就是由二維表及其之間的聯繫所組              成的一個數據組織。面試

  一、使用鍵值對存儲數據;
  二、分佈式
  三、通常支持ACID(原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability))特性;
  四、非關係型數據庫嚴格上不是一種數據庫,應該是一種數據結構化存儲方法的集合。
優勢   一、容易理解:二維表結構是很是貼近邏輯世界一個概念,關係模型相對網狀、層次等其                                   他模型來講更容易理解;
  二、使用方便:通用的SQL語言使得操做關係型數據庫很是方便;
  三、易於維護:豐富的完整性(實體完整性、參照完整性和用戶定義的完整性)大大減低了                                  數據冗餘和數據不一致的機率;
  四、支持SQL,可用於複雜的查詢。
  一、無需通過sql層的解析,讀寫性能很高
  二、基於鍵值對,數據沒有耦合性,容易擴展
  三、存儲數據的格式:nosql的存儲格式是key,value形式、文檔形式、圖片形式等等,文檔形式、圖片形式等等而                   關係型數據庫則只支持基礎類型。
缺點   一、爲了維護一致性所付出的巨大代價就是其讀寫性能比較差
  二、固定的表結構;
  三、高併發讀寫需求;
  四、海量數據的高效率讀寫;
   一、不提供sql支持,學習和使用成本較高;
   二、無事務處理,附加功能和報表等支持也很差;

2.MySQL常見數據庫引擎及比較?

MySQL支持數個存儲引擎做爲對不一樣表的類型的處理器。MySQL存儲引擎包括處理事務安全表的引擎和處理非事務安全表的引擎:redis

MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜索能力。MyISAM在全部MySQL配置裏被支持,它是默認的存儲引擎,除非你配置MySQL默認使用另一個引擎。算法

MEMORY存儲引擎提供「內存中」表。MERGE存儲引擎容許集合將被處理一樣的MyISAM表做爲一個單獨的表。就像MyISAM同樣,MEMORY和MERGE存儲引擎處理非事務表,這兩個引擎也都被默認包含在MySQL中。sql

3.簡述數據三大範式?

  數據庫設計對數據的存儲性能,還有開發人員對數據的操做都有莫大的關係。因此創建科學的,規範的的數據庫是須要知足一些規範的來優化數據數據存儲方式。在關係型數據庫中這些規範就能夠稱爲範式。mongodb

  第一範式:當關系模式R的全部屬性都不能在分解爲更基本的數據單位時,稱R是知足第一範式的,簡記爲1NF。知足第一範式是關係模式規範化的最低要求,不然,將有不少基本操做在這樣的關係模式中實現不了。數據庫

  第二範式:若是關係模式R知足第一範式,而且R得全部非主屬性都徹底依賴於R的每個候選關鍵屬性,稱R知足第二範式,簡記爲2NF。編程

  第三範式設R是一個知足第一範式條件的關係模式,X是R的任意屬性集,若是X非傳遞依賴於R的任意一個候選關鍵字,稱R知足第三範式,簡記爲3NF.

簡答題:

  第一範式(確保每列保持原子性)即每列再也不須要拆分
  第二範式(確保表中的每列都和主鍵相關)
  第三範式(確保每列都和主鍵列直接相關,而不是間接相關)

4.一條 SQL 語句執行的很慢的緣由有哪些?

能夠分兩種狀況回答。

  (1).大多數狀況是正常的,只是偶爾會出現很慢的狀況:

    -- 數據庫在刷新髒頁,例如redo log中寫滿了須要同步到磁盤。

      ps:往數據庫插入或更新一條數據,數據庫會在內存中把對應字段更新,但不會立刻同步持久化到磁盤中,而是寫入redo log中,空閒時再將數據同步到磁盤

    -- 執行的時候,遇到了鎖,表鎖或者是行鎖。

  (2).在數據量不變的狀況下,這條SQL語句一直以來都執行的很慢:

    -- 沒有用上索引,例如該字段沒有索引,因爲對字段進行運算、函數操做致使沒法用索引。

    -- 數據庫選錯了索引。

5.講講MYSQL事務,說說ACID是什麼?

什麼是事務?

  事務是由一步或幾步數據庫操做序列組成邏輯執行單元,這系列操做要麼所有執行要麼所有放棄執行。程序和事務是兩個不一樣的概念。

  通常而言:一段程序中可能包含多個事務。

  事務具備四大特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability)。簡稱ACID。

           (1)原子性:事務是應用中最小的執行單位,就如原子是天然界最小顆粒,具備不可再分的特徵同樣。事務是應用中不可再分的最小邏輯執行體。

           (2)一致性:事務執行的結果,必須使數據庫從一個一致性狀態,變到另外一個一致性狀態。當數據庫中只包含事務成功提交的結果時,數據庫處於一致性狀態。一致性是經過原子性來保證的。

           (3)隔離性:各個事務的執行互不干擾,任意一個事務的內部操做對其餘併發的事務,都是隔離的。也就是說:併發執行的事務之間不能看到對方的中間狀態,併發執行的事務之間不能相互影響。

           (4)持續性:持續性也稱爲持久性,指事務一旦提交,對數據所作的任何改變,都要記錄到永久存儲器中,一般是保存進物理數據庫。

MYSQL的事務處理主要有兩種方法:

  1.用begin,rollback,commit來實現
    begin開始一個事務
    rollback事務回滾
       commit 事務確認
  2.直接用set來改變mysql的自動提交模式
          mysql默認是自動提交的,也就是你提交一個query,就直接執行!能夠經過
          set autocommit = 0 禁止自動提交
          set autocommit = 1 開啓自動提交

 

6.簡述簡述觸發器、函數、視圖、存儲過程?

一、視圖

  視圖只是一種邏輯對象,是一種虛擬表,它並非物理對象,由於視圖不佔物理存儲空間,在視圖中被查詢的表稱爲視圖的基表,大多數的select語句均可以用在建立視圖中(說白了,視圖就是一種虛擬表,就像是一張電子照片)

  優勢:集中用戶使用的數據,掩碼數據的複雜性,簡化權限管理以及爲向其餘應用程序輸出而從新組織數據等

二、觸發器

  (1)觸發器是一個特殊的存儲過程,它是MySQL在insert、update、delete的時候自動執行的代碼塊。

  (2)觸發器必須定義在特定的表上。

  (3)自動執行,不能直接調用,

三、函數

  它跟php或js中的函數幾乎同樣:須要先定義,而後調用。

  只是規定,這個函數,必需要返回數據——要有返回值

四、存儲過程

  存儲過程(procedure),概念相似於函數,就是把一段代碼封裝起來,當要執行這一段代碼的時候,能夠經過調用該存儲過程來實現。

  在封裝的語句體裏面,能夠同if/else ,case,while等控制結構,能夠進行sql編程,查看現有的存儲過程。

7.如何基於數據庫實現商城商品計數器?

  見下題解;

8.MySQL索引種類。

單列:

  普通索引:加速查找
  惟一索引:加速查找 + 約束:不能重複(只能有一個空,否則就重複了)
       主鍵(primay key):加速查找 + 約束:不能重複 + 不能爲空
多列:
  聯合索引(多個列建立索引)-----> 至關於單列的普通索引
  聯合惟一索引 -----> 至關於單列的惟一索引
  ps:聯合索引的特色:遵循最左前綴的規則
其餘:
  合併索引,利用多個單例索引查詢;(例如在數據庫查用戶名和密碼,分別給用戶名和密碼創建索引)
  覆蓋索引,在索引表中就能將想要的數據查詢到;

9.索引在什麼狀況下遵循最左前綴的規則?

答:聯合索引

索引的最左前綴原理:
  一般咱們在創建聯合索引的時候,也就是對多個字段創建索引,相信創建過索引的同窗們會發現,不管是oralce仍是mysql都會讓咱們選擇索引的順序,好比咱們想在a,b,c三個字段上創建一個聯合索引,咱們能夠選擇本身想要的優先級,a、b、c,或者是b、a、c 或者是c、a、b等順序。爲何數據庫會讓咱們選擇字段的順序呢?不都是三個字段的聯合索引麼?這裏就引出了數據庫索引的最左前綴原理。
  好比:索引index1:(a,b,c)有三個字段,咱們在使用sql語句來查詢的時候,會發現不少狀況下不按照咱們想象的來走索引。

10.主鍵和外鍵的區別?

主鍵:

  定義:惟一標識一條記錄,不能有重複的,不容許爲空。

  做用:用來保證數據完整性。

  個數:主鍵只能有一個。

ALTER TABLE 「表名」 ADD PRIMARY KEY (字段名)

外鍵:  

  定義:表的外鍵是另外一表的主鍵, 外鍵能夠有重複的, 能夠是空值。

  做用:用來和其餘表創建聯繫用的。

  個數:一個表能夠有多個外鍵。

ALTER TABLE 「表名」 ADD FOREIGN KEY (字段名) REFERENCES 「另外一張表名」( 字段名)

11.MySQL常見的函數?

數學函數
字符串函數
日期和時間函數
條件判斷函數
系統信息函數
加密函數
格式化函數

12.列舉建立索引可是沒法命中索引的8種狀況。

1.- like '%xx';

select * from tb1 where name like '%cn';

2.- 使用函數;

select * from tb1 where reverse(name) = 'Clint';

3.- or;

select * from tb1 where nid = 1 or email = 'Clint@qq.com';

特別的:當or條件中有未創建索引的列才失效,如下會走索引;

select * from tb1 where nid = 1 or name = 'jack';
select * from tb1 where nid = 1 or email = 'rose@163.com' and name = 'rose'

4.- 類型不一致;
  若是列是字符串類型,傳入條件是必須用引號引發來;

select * from tb1 where name = 999;

5.- !=

select * from tb1 where name != 'rose'

特別的:若是是主鍵,則仍是會走索引

select * from tb1 where nid != 123

6.- >

select * from tb1 where name > 'alex'

特別的:若是是主鍵或索引是整數類型,則仍是會走索引

select * from tb1 where nid > 123
select * from tb1 where num > 123

7.- order by

select email from tb1 order by name desc;

當根據索引排序時候,選擇的映射若是不是索引,則不走索引
特別的:若是對主鍵排序,則仍是走索引:

select * from tb1 order by nid desc;

8.- 組合索引最左前綴
若是組合索引爲:(name,email)
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引

13.如何開啓慢日誌查詢?

爲何要開啓慢查詢日誌:

  開啓慢查詢日誌,可讓MySQL記錄下查詢超過指定時間的語句,經過定位分析性能的瓶頸,才能更好的優化數據庫系統的性能。

怎麼開啓:

  參數說明:

      slow_query_log 慢查詢開啓狀態
      slow_query_log_file 慢查詢日誌存放的位置(這個目錄須要MySQL的運行賬號的可寫權限,通常設置爲MySQL的數據存放目錄)
      long_query_time 查詢超過多少秒才記錄

設置步驟:

1.查看慢查詢相關參數

mysql> show variables like 'slow_query%';
+---------------------------+----------------------------------+
| Variable_name             | Value                            |
+---------------------------+----------------------------------+
| slow_query_log            | OFF                              |
| slow_query_log_file       | /mysql/data/localhost-slow.log   |
+---------------------------+----------------------------------+

mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

2.設置方法
方法一:全局變量設置
  將 slow_query_log 全局變量設置爲「ON」狀態

mysql> set global slow_query_log='ON'; 

  設置慢查詢日誌存放的位置

mysql> set global slow_query_log_file='/usr/local/mysql/data/slow.log';

  查詢超過1秒就記錄

mysql> set global long_query_time=1;

方法二:配置文件設置
  修改配置文件my.cnf,在[mysqld]下的下方加入

[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 1

3.重啓MySQL服務

service mysqld restart

4.查看設置後的參數

複製代碼
mysql> show variables like 'slow_query%';
+---------------------+--------------------------------+
| Variable_name       | Value                          |
+---------------------+--------------------------------+
| slow_query_log      | ON                             |
| slow_query_log_file | /usr/local/mysql/data/slow.log |
+---------------------+--------------------------------+

mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
複製代碼

5.測試

  1.執行一條慢查詢SQL語句

mysql> select sleep(2);

  2.查看是否生成慢查詢日誌

ls /usr/local/mysql/data/slow.log

若是日誌存在,MySQL開啓慢查詢設置成功!

14.數據庫導入導出命令(結構+數據)?

導出數據庫:

mysqldump -u 用戶名 -p 數據庫名 > 導出的文件名,如我輸入的命令行:mysqldump -u root -p news > news.sql (輸入後會讓你輸入進                          入MySQL的密碼),(若是導出單張表的話在數據庫名後面輸入表名便可)

導入數據庫:

1,將要導入的.sql文件移至bin文件下,這樣的路徑比較方便
2,同上面導出的第1步
3,進入MySQL:mysql -u 用戶名 -p ,如我輸入的命令行:mysql -u root -p (輸入一樣後會讓你輸入MySQL的密碼)
4,在MySQL-Front中新建你要建的數據庫,這時是空數據庫,如新建一個名爲news的目標數據庫
5,輸入:mysql>use 目標數據庫名,如我輸入的命令行:mysql>use news;
6,導入文件:mysql>source 導入的文件名;如我輸入的命令行:mysql>source news.sql;

15.數據庫優化方案?

1.對查詢進行優化,避免全表掃描

2.避免在where子句中對字段進行null值判斷

16.char和varchar的區別?

1.定長和變長: char長度固定,varchar長度可變

2.存儲容量不一樣:char最多隻能存放字符個數255,和編碼無關;而varchar 最對能夠存65532個字符

17.簡述MySQL的執行計劃?

* * *

18.在對name作了惟一索引前提下,簡述如下區別:
 
        select * from tb where name = ‘CRM-Clint’ 
 
        select * from tb where name = ‘CRM-Clint’ limit 1

19.1000w條數據,使用limit offset 分頁時,爲何越日後翻越慢?如何解決?

1:先查主鍵,在分頁;

select * from tb where id in (
select id from tb where limit 10 offset 30
)

2:按照也無需求是否能夠設置只讓用戶看200頁;

3:記錄當前頁 數據ID最大值和最小值,在翻頁時,根據條件先進行篩選;篩選完畢以後,再根據limit offset 查詢;

select * from (select * from tb where id > 22222222) as B limit 10 offset 0

若是用戶本身修改頁碼,也可能致使慢;此時對url種的頁碼進行加密(rest framework );

20.什麼是索引合併?

說明: 

一、索引合併是把幾個索引的範圍掃描合併成一個索引。
二、索引合併的時候,會對索引進行並集,交集或者先交集再並集操做,以便合併成一個索引。
三、這些須要合併的索引只能是一個表的。不能對多表進行索引合併。

怎麼肯定?

在使用explain對sql語句進行操做時,若是使用了索引合併,那麼在輸出內容的type列會顯示 index_merge,key列會顯示出全部使用的索引。

21.什麼是覆蓋索引?

定義:索引是高效找到行的一個方法,當能經過檢索索引就能夠讀取想要的數據,那就不須要再到數據表中讀取行了。若是一個索引包含了(或覆蓋了)知足查詢語句中字段與條件的數據就叫作覆蓋索引

查看覆蓋索引:只須要在select關鍵字以前添加explain這個命令查看。當發起一個被索引覆蓋的查詢時,在explain的Extra列能夠看到 Using index的標識。

22.簡述數據庫讀寫分離?

對於數據存儲層高併發問題,最早想到的可能就是讀寫分離,在網站訪問量大而且讀寫不平均的狀況下,將存儲分爲master,slave兩臺,全部的寫都路由到master上,全部的讀都路由到slave上,而後master和slave同步。若是一臺salve不夠,能夠加多臺,好比一臺master,3臺slave。對於什麼是讀寫分離,以及讀寫分離有什麼好處,這裏再也不敘述,有興趣的能夠參考 這裏 。 
在設計讀寫分離的時候,有幾種解決方案:
    1. 將讀寫分離放在dao層,在dao層, 全部的insert/update/delete都訪問master庫,全部的select 都訪問salve庫,這樣對於業務層是透明的。 
    2. 將讀寫分離放在ORM層,好比mybatis能夠經過mybatis plus攔截sql語句,全部的insert/update/delete都訪問master庫,全部的select 都訪問salve庫,這樣對於dao層都是透明。 
    3. 放在代理層,好比MySQL-Proxy,這樣針對整個應用程序都是透明的。 
對於絕大多數情景,讀寫分離都適用,可是讀寫分離有一個問題是master slave同步,這個同步是會有必定延遲。

23.簡述數據庫分庫分表?(水平、垂直)

數據庫瓶頸:

  IO瓶頸:

      磁盤讀IO瓶頸:熱點數據太多,數據庫緩存放不下,每次查詢時會產生大量的IO,下降查詢速度    ---->  分表

      網絡IO瓶頸:請求的數據太多,網絡帶寬不夠                                                                                   ----->  分庫

  CPU瓶頸:

      單表數據量太大,查詢時掃描的行太多,SQL效率低,CPU率先出現瓶頸          --->  水平分表

 

  水平分庫:以字段爲依據,按照必定的策略(hash、range等),將一個中的數據拆分到多個

  水平分表:同理,...,將一個中的數據拆分到多個

 

  垂直分庫:爲依據,按照業務歸屬不一樣,將不一樣的拆分到不一樣的

  垂直分表:字段爲依據,按照字段的活躍性,將表中的字段拆到不一樣的表(主表和擴展表)中

分庫分表工具:

  1. sharding-sphere:jar,前身是sharding-jdbc;
  2. TDDL:jar,Taobao Distribute Data Layer;

  3. Mycat:中間件。

24.redis和memcached還有MongoDB比較?

1.數據庫類型方面  

  memcache數據結構單一,Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲;
  Redis和Memcache都是將數據存放在內存中,都是內存數據庫。不過memcache還可用於緩存其餘,例如圖片、視頻等;

 

二、操做的便利性
  redis豐富一些,數據操做方面,redis更好一些,較少的網絡IO次數;
  mongodb支持豐富的數據表達,索引,最相似關係型數據庫,支持的查詢語言很是豐富;

三、內存空間的大小和數據量的大小
  redis在2.0版本後增長了本身的VM特性,突破物理內存的限制;能夠對key value設置過時時間(相似memcache);
  memcache能夠修改最大可用內存,採用LRU算法
  mongoDB適合大數據量的存儲,依賴操做系統VM作內存管理,吃內存也比較厲害,服務不要和別的服務在一塊兒;

四、可用性(單點問題)
  redis,依賴客戶端來實現分佈式讀寫;主從複製時,每次從節點從新鏈接主節點都要依賴整個快照,無增量複製,因性能和效率問題,因此單點問題比較複雜;不支持自動sharding,須要依賴程序設定一致hash 機制;
  Memcache自己沒有數據冗餘機制,也不必;對於故障預防,採用依賴成熟的hash或者環狀的算法,解決單點故障引發的抖動問題;
  mongoDB支持master-slave,replicaset(內部採用paxos選舉算法,自動故障恢復),auto sharding機制,對客戶端屏蔽了故障轉移和切分機制;

六、數據一致性(事務支持
  Memcache 在併發場景下,用cas保證一致性;
  redis事務支持比較弱,只能保證事務中的每一個操做連續執行;
  mongoDB不支持事務;

七、數據分析
  mongoDB內置了數據分析的功能(mapreduce),其餘不支持;

八、應用場景
  redis:數據量較小的、更小性能操做和運算上;
  memcache:用於在動態系統中減小數據庫負載,提高性能;作緩存,提升性能(適合讀多寫少,對於數據量比較大,能夠採用sharding);
  MongoDB:主要解決海量數據的訪問效率問題;   

25.redis中數據庫默認是多少個db 及做用?

redis下,數據庫是由一個整數索引標識,而不是由一個數據庫名稱。默認狀況下,一個客戶端鏈接到數據庫0。redis配置文件中下面的參數來控制數據庫總數:
/etc/redis/redis.conf;該文件中,有個配置項 databases = 16 //默認有16個數據庫

26.python操做redis的模塊?

- 鏈接
- 直接鏈接:
    import redis 
    r = redis.Redis(host='10.211.55.4', port=6379)
    r.set('foo', 'Bar')
    print r.get('foo')
- 鏈接池:
    import redis
    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
     
    r = redis.Redis(connection_pool=pool)
    r.set('foo', 'Bar')
    print r.get('foo')

27.若是redis中的某個列表中的數據量很是大,若是實現循環顯示每個值?

- 若是一個列表在redis中保存了10w個值,我須要將全部值所有循環並顯示,請問如何實現?
一個一個取值,列表沒有iter方法,但能自定義

def list_scan_iter(name,count=3):
    start = 0
    while True:
        result = conn.lrange(name, start, start+count-1)
        start += count
        if not result:
            break
        for item in result:
            yield item

for val in list_scan_iter('num_list'):
    print(val)
場景:投票系統,script-redis                            

28.redis如何實現主從複製?以及數據同步機制?

和Mysql主從複製的緣由同樣,Redis雖然讀取寫入的速度都特別快,可是也會產生讀壓力特別大的狀況。爲了分擔讀壓力,Redis支持主從複製,Redis的主從結構能夠採用一主多從或者級聯結構,Redis主從複製能夠根據是不是全量分爲全量同步增量同步

29.redis中的sentinel的做用?

Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,當用Redis作Master-slave的高可用方案時,假如master宕機了,Redis自己(包括它的不少客戶端)都沒有實現自動進行主備切換,而Redis-sentinel自己也是一個獨立運行的進程,它能監控多個master-slave集羣,發現master宕機後能進行自動切換。

主要做用: 

  不時地監控redis是否按照預期良好地運行;

  若是發現某個redis節點運行出現情況,可以通知另一個進程(例如它的客戶端);

  可以進行自動切換。當一個master節點不可用時,可以選舉出master的多個slave(若是有超過一個slave的話)中的一個來做爲新的master,其它的slave節點會將它所追隨的master的地址改成被提高爲master的slave的新地址;

30.如何實現redis集羣?

redis集羣、分片、分佈式redis
redis-py-cluster
集羣方案:
- redis cluster 官方提供的集羣方案。
- codis,豌豆莢技術團隊。
- tweproxy,Twiter技術團隊。
redis cluster的原理?
- 基於分片來完成。
- redis將全部能放置數據的地方建立了 16384 個哈希槽。
- 若是設置集羣的話,就能夠爲每一個實例分配哈希槽:
- 192.168.1.20【0-5000】
- 192.168.1.21【5001-10000】
- 192.168.1.22【10001-16384】
- 之後想要在redis中寫值時,
set k1 123
將k1經過crc16的算法,將k1轉換成一個數字。而後再將該數字和16384求餘,若是獲得的餘數 3000,那麼就將該值寫入到 192.168.1.20 實例中。

31.redis中默認有多少個哈希槽?

Redis 集羣中內置了 16384 個哈希槽,當須要在 Redis 集羣中放置一個 key-value時,redis 先對 key 使用 crc16 算法算出一個結果,而後把結果對 16384 求餘數,這樣每一個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大體均等的將哈希槽映射到不一樣的節點.

Redis 集羣沒有使用一致性hash, 而是引入了哈希槽的概念。

Redis 集羣有16384個哈希槽,每一個key經過CRC16校驗後對16384取模來決定放置哪一個槽.集羣的每一個節點負責一部分hash槽。這種結構很容易添加或者刪除節點,而且不管是添加刪除或者修改某一個節點,都不會形成集羣不可用的狀態。

使用哈希槽的好處就在於能夠方便的添加或移除節點。

當須要增長節點時,只須要把其餘節點的某些哈希槽挪到新節點就能夠了;

當須要移除節點時,只須要把移除節點上的哈希槽挪到其餘節點就好了;

在這一點上,咱們之後新增或移除節點的時候不用先停掉全部的 redis 服務。

**"用了哈希槽的概念,而沒有用一致性哈希算法,不都是哈希麼?這樣作的緣由是爲何呢?"
Redis Cluster是本身作的crc16的簡單hash算法,沒有用一致性hash。Redis的做者認爲它的crc16(key) mod 16384的效果已經不錯了,雖然沒有一致性hash靈活,但實現很簡單,節點增刪時處理起來也很方便。

**"爲了動態增刪節點的時候,不至於丟失數據麼?"
節點增刪時不丟失數據和hash算法沒什麼關係,不丟失數據要求的是一份數據有多個副本。

**「還有集羣總共有2的14次方,16384個哈希槽,那麼每個哈希槽中存的key 和 value是什麼?」
當你往Redis Cluster中加入一個Key時,會根據crc16(key) mod 16384計算這個key應該分佈到哪一個hash slot中,一個hash slot中會有不少key和value。你能夠理解成表的分區,使用單節點時的redis時只有一個表,全部的key都放在這個表裏;改用Redis Cluster之後會自動爲你生成16384個分區表,你insert數據時會根據上面的簡單算法來決定你的key應該存在哪一個分區,每一個分區裏有不少key。

32.簡述redis的有哪幾種持久化策略及比較?

RDB:每隔一段時間對redis進行一次持久化。
- 缺點:數據不完整
- 優勢:速度快
AOF:把全部命令保存起來,若是想到從新生成到redis,那麼就要把命令從新執行一次。
- 缺點:速度慢,文件比較大
- 優勢:數據完整

33.列舉redis支持的過時策略。

voltile-lru:    從已設置過時時間的數據集(server.db[i].expires)中挑選最近頻率最少數據淘汰
  volatile-ttl:   從已設置過時時間的數據集(server.db[i].expires)中挑選將要過時的數據淘汰
  volatile-random:從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據淘汰

  
  allkeys-lru:       從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
  allkeys-random:    從數據集(server.db[i].dict)中任意選擇數據淘汰
  no-enviction(驅逐):禁止驅逐數據

34.MySQL 裏有 2000w 數據,redis 中只存 20w 的數據,如何保證 redis 中都是熱點數據? 

相關知識:redis 內存數據集大小上升到必定大小的時候,就會施行數據淘汰策略(回收策略)。redis 提供 6種數據淘汰策略:

  volatile-lru:從已設置過時時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰
  volatile-ttl:從已設置過時時間的數據集(server.db[i].expires)中挑選將要過時的數據淘汰
  volatile-random:從已設置過時時間的數據集(server.db[i].expires)中任意選擇數據淘汰
  allkeys-lru:從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
  allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰
  no-enviction(驅逐):禁止驅逐數據

35.寫代碼,基於redis的列表實現 先進先出、後進先出隊列、優先級隊列。

參看script—redis源碼
from scrapy.utils.reqser import request_to_dict, request_from_dict

  from . import picklecompat


  class Base(object):
      """Per-spider base queue class"""

      def __init__(self, server, spider, key, serializer=None):
          """Initialize per-spider redis queue.

          Parameters
          ----------
          server : StrictRedis
              Redis client instance.
          spider : Spider
              Scrapy spider instance.
          key: str
              Redis key where to put and get messages.
          serializer : object
              Serializer object with ``loads`` and ``dumps`` methods.

          """
          if serializer is None:
              # Backward compatibility.
              # TODO: deprecate pickle.
              serializer = picklecompat
          if not hasattr(serializer, 'loads'):
              raise TypeError("serializer does not implement 'loads' function: %r"
                              % serializer)
          if not hasattr(serializer, 'dumps'):
              raise TypeError("serializer '%s' does not implement 'dumps' function: %r"
                              % serializer)

          self.server = server
          self.spider = spider
          self.key = key % {'spider': spider.name}
          self.serializer = serializer

      def _encode_request(self, request):
          """Encode a request object"""
          obj = request_to_dict(request, self.spider)
          return self.serializer.dumps(obj)

      def _decode_request(self, encoded_request):
          """Decode an request previously encoded"""
          obj = self.serializer.loads(encoded_request)
          return request_from_dict(obj, self.spider)

      def __len__(self):
          """Return the length of the queue"""
          raise NotImplementedError

      def push(self, request):
          """Push a request"""
          raise NotImplementedError

      def pop(self, timeout=0):
          """Pop a request"""
          raise NotImplementedError

      def clear(self):
          """Clear queue/stack"""
          self.server.delete(self.key)


  class FifoQueue(Base):
      """Per-spider FIFO queue"""

      def __len__(self):
          """Return the length of the queue"""
          return self.server.llen(self.key)

      def push(self, request):
          """Push a request"""
          self.server.lpush(self.key, self._encode_request(request))

      def pop(self, timeout=0):
          """Pop a request"""
          if timeout > 0:
              data = self.server.brpop(self.key, timeout)
              if isinstance(data, tuple):
                  data = data[1]
          else:
              data = self.server.rpop(self.key)
          if data:
              return self._decode_request(data)


  class PriorityQueue(Base):
      """Per-spider priority queue abstraction using redis' sorted set"""

      def __len__(self):
          """Return the length of the queue"""
          return self.server.zcard(self.key)

      def push(self, request):
          """Push a request"""
          data = self._encode_request(request)
          score = -request.priority
          # We don't use zadd method as the order of arguments change depending on
          # whether the class is Redis or StrictRedis, and the option of using
          # kwargs only accepts strings, not bytes.
          self.server.execute_command('ZADD', self.key, score, data)

      def pop(self, timeout=0):
          """
          Pop a request
          timeout not support in this queue class
          """
          # use atomic range/remove using multi/exec
          pipe = self.server.pipeline()
          pipe.multi()
          pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)
          results, count = pipe.execute()
          if results:
              return self._decode_request(results[0])


  class LifoQueue(Base):
      """Per-spider LIFO queue."""

      def __len__(self):
          """Return the length of the stack"""
          return self.server.llen(self.key)

      def push(self, request):
          """Push a request"""
          self.server.lpush(self.key, self._encode_request(request))

      def pop(self, timeout=0):
          """Pop a request"""
          if timeout > 0:
              data = self.server.blpop(self.key, timeout)
              if isinstance(data, tuple):
                  data = data[1]
          else:
              data = self.server.lpop(self.key)

          if data:
              return self._decode_request(data)


  # TODO: Deprecate the use of these names.
  SpiderQueue = FifoQueue
  SpiderStack = LifoQueue
  SpiderPriorityQueue = PriorityQueue
View Code

36.如何基於redis實現消息隊列?

# 經過發佈訂閱模式的PUB、SUB實現消息隊列
# 發佈者發佈消息到頻道了,頻道就是一個消息隊列。
# 發佈者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
conn.publish('104.9MH', "hahahahahaha")
# 訂閱者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
pub = conn.pubsub()
pub.subscribe('104.9MH')
while True:
    msg= pub.parse_response()
    print(msg)
對了,redis 作消息隊列不合適
業務上避免過分複用一個redis,用它作緩存、作計算,還作任務隊列,壓力太大,很差。

37.如何基於redis實現發佈和訂閱?以及發佈訂閱和消息隊列的區別?

發佈和訂閱,只要有任務就給全部訂閱者沒人一份
  發佈者:
      import redis

      conn = redis.Redis(host='127.0.0.1',port=6379)
      conn.publish('104.9MH', "hahaha")
  訂閱者:
      import redis

      conn = redis.Redis(host='127.0.0.1',port=6379)
      pub = conn.pubsub()
      pub.subscribe('104.9MH')

      while True:
          msg= pub.parse_response()
          print(msg)

38.什麼是codis及做用?

Codis 是一個分佈式 Redis 解決方案, 對於上層的應用來講, 鏈接到 Codis Proxy 和鏈接原生的 Redis Server 沒有明顯的區別
(不支持的命令列表), 上層應用能夠像使用單機的 Redis 同樣使用, Codis 底層會處理請求的轉發, 不停機的數據遷移等工做,
全部後邊的一切事情, 對於前面的客戶端來講是透明的, 能夠簡單的認爲後邊鏈接的是一個內存無限大的 Redis 服務.

39.什麼是twemproxy及做用?

概念:

  Twemproxy是由Twitter開源的Redis代理,其基本原理是:Redis客戶端把請求發送到Twemproxy,Twemproxy根據路由規則發送到正確的Redis實例,最後Twemproxy把結果聚集返回給客戶端;
  Twemproxy經過引入一個代理層,將多個Redis實例進行統一管理,使Redis客戶端只須要在Twemproxy上進行操做,而不須要關心後面有多少個Redis實例;

做用:

  實現Redis集羣;

40.寫代碼實現redis事務操做。

41.redis中的watch的命令的做用?

watch 用於在進行事務操做的最後一步也就是在執行exec 以前對某個key進行監視;
若是這個被監視的key被改動,那麼事務就被取消,不然事務正常執行;
通常在MULTI 命令前就用watch命令對某個key進行監控.若是想讓key取消被監控,能夠用unwatch命令; 

在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。
假設咱們經過WATCH命令在事務執行以前監控了多個Keys,假若在WATCH以後有任何Key的值發生了變化,
EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知調用者事務執行失敗。

面試題:你如何控制剩餘的數量不會出問題?
方式一:- 經過redis的watch實現

import redis
conn = redis.Redis(host='127.0.0.1',port=6379)

# conn.set('count',1000)
val = conn.get('count')
print(val)

with conn.pipeline(transaction=True) as pipe:

# 先監視,本身的值沒有被修改過
conn.watch('count')

# 事務開始
pipe.multi()
old_count = conn.get('count')
count = int(old_count)
print('如今剩餘的商品有:%s',count)
input("問媳婦讓不讓買?")
pipe.set('count', count - 1)

# 執行,把全部命令一次性推送過去
pipe.execute()

方式二 - 數據庫的鎖

42.基於redis如何實現商城商品數量計數器?

import redis

conn = redis.Redis(host='192.168.1.41',port=6379)

conn.set('count',1000)

with conn.pipeline() as pipe:

    # 先監視,本身的值沒有被修改過
    conn.watch('count')

    # 事務開始
    pipe.multi()
    old_count = conn.get('count')
    count = int(old_count)
    if count > 0:  # 有庫存
        pipe.set('count', count - 1)

    # 執行,把全部命令一次性推送過去
    pipe.execute()

43.簡述redis分佈式鎖和redlock的實現機制。

在不一樣進程須要互斥地訪問共享資源時,分佈式鎖是一種很是有用的技術手段。
有不少三方庫和文章描述如何用Redis實現一個分佈式鎖管理器,可是這些庫實現的方式差異很大
,並且不少簡單的實現其實只需採用稍微增長一點複雜的設計就能夠得到更好的可靠性。
用Redis實現分佈式鎖管理器的算法,咱們把這個算法稱爲RedLock。

實現
- 寫值並設置超時時間
- 超過一半的redis實例設置成功,就表示加鎖完成。
- 使用:安裝redlock-py

from redlock import Redlock
dlm = Redlock(
[
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
]
)
# 加鎖,acquire
my_lock = dlm.lock("my_resource_name",10000)
if my_lock:
# J進行操做
# 解鎖,release
dlm.unlock(my_lock)
else:
print('獲取鎖失敗')

redis分佈式鎖?

# 不是單機操做,又多了一/多臺機器
# redis內部是單進程、單線程,是數據安全的(只有本身的線程在操做數據)
----------------------------------------------------------------
\A、B、C,三個實例(主)
一、來了一個'隔壁老王'要操做,且不想讓別人操做,so,加鎖;
加鎖:'隔壁老王'本身生成一個隨機字符串,設置到A、B、C裏(xxx=666)
二、來了一個'鄰居老李'要操做A、B、C,一讀發現裏面有字符串,擦,被加鎖了,不能操做了,等着吧~
三、'隔壁老王'解決完問題,不用鎖了,把A、B、C裏的key:'xxx'刪掉;完成解鎖
四、'鄰居老李'如今能夠訪問,能夠加鎖了
# 問題:
一、若是'隔壁老王'加鎖後忽然掛了,就沒人解鎖,就死鎖了,其餘人幹看着無法用咋辦?
二、若是'隔壁老王'去給A、B、C加鎖的過程當中,剛加到A,'鄰居老李'就去操做C了,加鎖成功or失敗?
三、若是'隔壁老王'去給A、B、C加鎖時,C忽然掛了,此次加鎖是成功仍是失敗?
四、若是'隔壁老王'去給A、B、C加鎖時,超時時間爲5秒,加一個鎖耗時3秒,這次加鎖能成功嗎?
# 解決
一、安全起見,讓'隔壁老王'加鎖時設置超時時間,超時的話就會自動解鎖(刪除key:'xxx')
二、加鎖程度達到(1/2)+1個就表示加鎖成功,即便沒有給所有實例加鎖;
三、加鎖程度達到(1/2)+1個就表示加鎖成功,即便沒有給所有實例加鎖;
四、不能成功,鎖還沒加完就過時,沒有意義了,應該合理設置過時時間

44.什麼是一致性哈希?Python中是否有相應模塊?

一致性哈希 一致性hash算法(DHT)能夠經過減小影響範圍的方式,解決增減服務器致使的數據散列問題,從而解決了分佈式環境下負載均衡問題; 若是存在熱點數據,能夠經過增添節點的方式,對熱點區間進行劃分,將壓力分配至其餘服務器,從新達到負載均衡的狀態。

Python模塊--hash_ring,即Python中的一致性hash

45.如何高效的找到redis中全部以clint開頭的key?

redis 有一個keys命令。# 語法:KEYS pattern# 說明:返回與指定模式相匹配的所用的keys。該命令所支持的匹配模式以下:一、?:用於匹配單個字符。例如,h?llo能夠匹配hello、hallo和hxllo等;二、*:用於匹配零個或者多個字符。例如,h*llo能夠匹配hllo和heeeello等;二、[]:能夠用來指定模式的選擇區間。例如h[ae]llo能夠匹配hello和hallo,可是不能匹配hillo。同時,可使用「/」符號來轉義特殊的字符# 注意KEYS 的速度很是快,但若是數據太大,內存可能會崩掉,若是須要從一個數據集中查找特定的key,最好仍是用Redis的集合結構(set)來代替。

相關文章
相關標籤/搜索