Python 經典面試題彙總之數據庫篇

數據庫和緩存

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

關係型數據庫(須要有表結構)
  mysql、oracle、splserver、postgresql、db二、sybase

非關係型數據庫(是以key-value存儲的,沒有表結構)(NoSQL)
MongoDB
  MongoDB 是一個高性能,開源,無模式的文檔型數據庫,開發語言是C++。它在許多場景下可用於替代傳統的關係型數據庫或鍵/值存儲方式。
Redis
  Redis 是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。

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

InnoDB 
支持事務
支持外鍵 支持表鎖、行鎖(for update) 表鎖:select * from tb for update 行鎖:select id,name from tb where id=2 for update myisam 查詢速度快 全文索引 支持表鎖 表鎖:select * from tb for update

3.簡述數據庫三大範式

# 數據庫的三大特性:
'實體':表
'屬性':表中的數據(字段)
'關係':表與表之間的關係
----------------------------------------------------
# 數據庫設計三大範式:
1:確保每列保持原子性(即數據庫表中的全部字段值是不可分解的原子值)
2:確保表中的每列都是和主鍵相關(表中只能保存一種數據,不能夠把多種數據保存在同一張表中)--->徹底屬於當前表的數據
3:確保每列都和主鍵直接相關,而不是間接相關(在一個數據庫表中保存的數據只能與主鍵相關)----> 消除傳遞依賴(間接)
好比在設計一個訂單數據表的時候,能夠將客戶編號做爲一個外鍵和訂單表創建相應的關係。
而不能夠在訂單表中添加關於客戶其它信息(好比姓名、所屬公司等)的字段。
# 數據庫五大約束'
  1.primary KEY:設置主鍵約束;
  2.UNIQUE:設置惟一性約束,不能有重複值;
  3.CHECK:檢查約束    
  4.NOT NULL:設置非空約束,該字段不能爲空;
  5.FOREIGN key:設置外鍵約束。

四、什麼是事務?MySQL如何支持事務?

事務用於將某些操做的多個SQL做爲原子性操做,一旦有某一個出現錯誤,便可回滾到原來的狀態,從而保證數據庫數據完整性。

通常來講,事務是必須知足4個特性(ACID): Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durability(持久性)。
   一、原子性:事務包含的全部操做要麼所有成功,要麼所有失敗回滾,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。
   二、一致性:事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
   三、隔離性:當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始以前就已經結束,要麼在T1結束以後纔開始,這樣每一個事務都感受不到有其餘事務在併發地執行。
   四、持久性:持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。
最重要的是1,2兩個特性。

  關閉自動提交:SET AUTOCOMMIT=0;  # 此後須要手動提交事務,應用COMMIT或ROLLBACK提交事務或回滾事務python

  開啓自動提交:SET AUTOCOMMIT=1;mysql

  若是執行語句:START TRANSACTION(開始一個事務); 那麼不論有無設置自動提交,均須要手動提交或回滾。linux

  事務的週期由用戶在命令提示符中輸入START TRANSACTION指令開始,直至用戶輸入COMMIT結束。面試

5.簡述數據庫設計中一對多和多對多的應用場景?

FK(一對多)
下拉框裏面的數據就須要用FK關聯另外一張表

M2M(多對多)
多選的下拉框,或者checkbox

6.常見SQL

group by 分組對聚合的條件進行篩選須要經過havhing

SQL的left join 、right join、inner join之間的區別
left join (左鏈接) 返回包括左表中的全部記錄和右表中聯結字段相等的記錄
right join(右鏈接) 返回包括右表中的全部記錄1和左表中聯結字段相等的記錄
inner join(內鏈接)只返回兩個表中聯結字段相等的行

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

觸發器:
對數據庫某張表的增長、刪除,修改先後定義一些操做。

函數:(觸發函數是經過select)
聚合函數:max/sum/min/avg
時間格式化:date_format
字符串拼接:concat

存儲過程:
將SQL語句保存到數據庫中,並命名,之後在代碼調用時,直接調用名稱便可
參數類型:
  in    只將參數傳進去
  out   只拿結果
  inout 既能夠傳,能夠取

函數與存儲過程區別:
本質上沒區別。只是函數只能返回一個變量的限制,而存儲過程能夠返回多個。函數是能夠嵌入在sql中使用的,能夠在select中調用,而存儲過程不能夠,它須要執行語句call來調用。

視圖:
視圖是一個虛擬表,不是真實存在的(通常只能查,不能改)

8.MySQL索引種類

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

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

聯合索引redis

10.主鍵和外鍵的區別

主鍵是能肯定一條記錄的惟一標示。例如,身份證證號

外鍵:用於與另外一張表的關聯,是能肯定另外一張表記錄的字段,用於保持數據的一致性
  主鍵 外鍵
定義 惟一標識一條記錄,不能有重複的,不容許爲空 表的外鍵是另外一張表的主鍵,外鍵能夠有重複的,能夠爲空
做用 用來保證數據完整性 用來與其餘表創建聯繫的
個數 主鍵只能有一個 一個表能夠有多個外鍵

 

 

 

 

 

11.MySQL常見的函數

聚合函數
max/min/sum/avg

時間格式化
date_format

字符串拼接
concat(當拼接了null,則返回null)

截取字符串
substring

返回字節個數
length

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

1. like '%xx'
    select * from tb1 where name like '%cn';
2. 使用函數
    select * from tb1 where reverse(name) = 'wupeiqi';
3. or
    select * from tb1 where nid = 1 or email = 'seven@live.com';
    特別的:當or條件中有未創建索引的列才失效,如下會走索引
            select * from tb1 where nid = 1 or name = 'seven';
            select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
4. 類型不一致
    若是列是字符串類型,傳入條件是必須用引號引發來,否則...
    select * from tb1 where name = 999;
5. !=
    select * from tb1 where name != 'alex'
    特別的:若是是主鍵,則仍是會走索引
        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.如何開啓慢日誌查詢?

修改配置文件
slow_query_log = OFF                    是否開啓慢日誌記錄
long_query_time = 2                     時間限制,超過此時間,則記錄
slow_query_log_file = /usr/slow.log     日誌文件
log_queries_not_using_indexes = OFF     爲使用索引的搜索是否記錄

下面是開啓
slow_query_log = ON
long_query_time = 2   
log_queries_not_using_indexes = OFF 
log_queries_not_using_indexes = ON

注:查看當前配置信息:
     show variables like '%query%'
     修改當前配置:
    set global 變量名 = 值

14.數據庫導入導出命令

備份全部數據庫:
 mysqldump –u 用戶名 –p –h 主機名 --all-databases > 備份文件名.sql算法

備份整個或多個數據庫:
 mysqldump –u 用戶名 –p –h 主機名 --databases db1 db2 db3 … > 備份文件名.sqlsql

備份某個數據庫的某些表:
 mysqldump –u 用戶名 –p –h 主機名 數據庫名 表1 表2 表3… > 備份文件名.sql數據庫

 

經過mysqldump,若是使用了"--all-databases"或"--databases"選項,則在備份文件中包含CREATE DATABASE和USE語句,故並不須要指定一個數據庫名去恢復備份文件。
 mysql –u 用戶名 –p < 備份文件.sqldjango

經過mysqldump,若是沒有使用"--databases"選項,則備份文件中不包含CREATE DATABASE和USE語句,那麼在恢復的時候必須指定數據庫。
 mysql –u 用戶名 –p 數據庫名 < 備份文件.sql緩存

15.數據庫優化方案

能夠從數據庫服務器部署、表設計、表查詢等方面考慮。

一、建立數據表時把固定長度的放在前面 二、將固定數據放入內存: 例如:choice字段 (django中有用到,數字一、二、3…… 對應相應內容) 三、char 和 varchar 的區別(char可變, varchar不可變 ) 四、聯合索引遵循最左前綴(從最左側開始檢索) 五、避免使用 select * 六、讀寫分離     - 實現:兩臺服務器同步數據     - 利用數據庫的主從分離:主,用於增長、刪除、更新;從,用於查詢; 七、分庫     - 當數據庫中的表太多,將某些表分到不一樣的數據庫,例如:1W張表時     - 代價:連表查詢 八、分表     - 水平分表:將某些列拆分到另一張表,例如:博客+博客詳情     - 垂直分表:講些歷史信息分到另一張表中,例如:支付寶帳單 九、加緩存     - 利用redis、memcache (經常使用數據放到緩存裏,提升取數據速度) 十、若是隻想獲取一條數據 - select xxx from tb where name='alex' limit 1;

16.char和varchar的區別

char可變,varchar不可變 。

17.簡述MySQL的執行計劃

查看有沒有命中索引,讓數據庫幫看看運行速度快不快
explain select * from table;

當type爲all時,是爲全表索引。

18.在對name作了惟一索引前提下,簡述如下區別:
    

    select * from tb where name = ‘Oldboy-Wupeiqi’ 
 
    select * from tb where name = ‘Oldboy-Wupeiqi’ limit 1

沒作惟一索引的話,前者查詢會全表掃描,效率低些;
limit 1,只要找到對應一條數據,就不繼續往下掃描.

然而 name 字段添加惟一索引了,加不加limit 1,意義都不大。

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

  答案一:
      先查主鍵,在分頁。
      select * from tb where id in (
          select id from tb where limit 10 offset 30
      );
      
  答案二:
      記錄當前頁ID最大值和最小值
      在翻頁時,根據條件先進行篩選(子查詢);篩選完畢以後,再根據limit offset 查詢。
      
      select * from (select * from tb where id > 1000000) limit 10 offset 0;
      
      若是用戶本身修改頁碼,也可能致使慢;此時對url種的頁碼進行加密(rest framework )

20.什麼是索引合併?

簡單地說,對同一張表的一條sql可使用多個索引,對這些索引取交集、並集,從而減小從表中取數據的次數,提升查詢效率。

21.什麼是覆蓋索引?

這個概念很重要!
一個索引涵蓋了全部須要查詢和返回字段的值,稱爲"覆蓋索引"。
只需掃描索引而無須回表,也就是說只須要經過索引就能夠返回查詢數據,而沒必要先查到索引以後再去查詢數據。性能不言而喻,速度很是快,很強大!!
在 Explain 的時候,輸出的 Extra 信息中若是有 "Using Index" ,就表示這條查詢使用了覆蓋索引。

22.簡述數據庫讀寫分離

- 前提:主備兩臺服務器同步數據
- 利用數據庫的主從分離:主,用於增長、刪除、更新;從,用於查詢;
例:Django數據庫讀寫分離
步驟一:寫配置文件
class Router1:
  # 指定到某個數據庫【讀】數據
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.model_name == 'usertype':
            return 'db1'
        else:
            return 'default'
   
  # 指定到某個數據庫【寫】數據
    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        return 'default'

步驟二:配置settings
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'db1': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
DATABASE_ROUTERS = ['db_router.Router1',]

步驟三:視圖裏用 using 方式能夠指定讀寫數據庫
from django.shortcuts import render,HttpResponse
from app01 import models

def index(request):
    models.UserType.objects.using('db1').create(title='普通用戶')
  # 手動指定去某個數據庫取數據
    result = models.UserType.objects.all().using('db1')
    print(result)
    return HttpResponse('...')

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

 一、分庫
    當數據庫中的表太多,將某些表分到不一樣數據庫,例如:1W張表時
    代價:連表查詢跨數據庫,代碼變多。
 二、分表(提升查詢性能)
    水平分表:表的記錄行數龐大,將表按記錄行數分紅n份,每張表就小了不少。這些表結構一致。
    垂直分表:將表的一些內容很長的列拆出來獨立成另外一張表。

24.redis和memcached比較?

區別:
1:redis不只支持簡單的key_value類型,還支持字符串,HASH,列表,集合。
2:內存使用效率對比:使用簡單的key-value存儲的話,Memcached的內存利用率更高;
而若是Redis採用HASH結構來作key-value存儲,因爲其組合式的壓縮,Redis的內存利用率更高。 3:性能對比:因爲Redis只使用單核,而Memcached可使用多核,因此平均每個核上Redis在存儲小數據時性能更高;
而在100k以上的數據中,Memcached性能更高。 4:Redis雖然是基於內存的存儲系統,可是它自己是支持內存數據的持久化的;而memcached是不支持數據持久化操做的。 5:集羣管理不一樣,Memcached自己並不支持分佈式,所以只能在客戶端經過像一致性哈希這樣的分佈式算法來實現Memcached的分佈式存儲。

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

Redis默認支持16個數據庫,能夠經過配置databases來修改這一數字。客戶端與Redis創建鏈接後會自動選擇0號數據庫,不過能夠隨時使用SELECT命令更換數據庫。

Redis支持多個數據庫,而且每一個數據庫的數據是隔離的不能共享,而且基於單機纔有,若是是集羣就沒有數據庫的概念。

26.python操做redis的模塊

鏈接
- 直接鏈接:
    import redis 
    r = redis.Redis(host='10.211.55.4', port=6379)
    r.set('foo', 'Bar')    # 這裏的方法與Redis命令相似
    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_iter(key, count=3): start = 0 while True: result = conn.lrange(key, start, start+count-1) start += count if not result: break for item in result: yield item
    # 調用 for val in list_iter('num_list'): print(val)   
場景:投票系統

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

優點:
    - 高可用
    - 分擔主壓力
注意: 
    - slave設置只讀

從的配置文件添加如下記錄,便可:
    slaveof 1.1.1.1 3306 

29.redis中的sentinel (哨兵)的做用?

  自動主從之間進行切換,實現熱切。
    檢測主是否掛掉,且超過一半的sentinel檢測到掛了以後才進行進行切換。
    若是主修復好了,再次啓動時候,會變成從。

    啓動主redis:
    redis-server /etc/redis-6379.conf  啓動主redis
    redis-server /etc/redis-6380.conf  啓動從redis
        
    在linux中:
        找到 /etc/redis-sentinel-8001.conf  配置文件,在內部:
            - 哨兵的端口 port = 8001
            - 主redis的IP,哨兵個數的一半/1
        
        找到 /etc/redis-sentinel-8002.conf  配置文件,在內部:
            - 哨兵的端口 port = 8002
            - 主redis的IP, 1 
    
        啓動兩個哨兵   

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中默認有多少個哈希槽?

16384

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

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

# 經過"發佈訂閱"模式(P-S)實現消息隊列。只要有任務就給全部訂閱者每人一份。
# 發佈者發佈消息到頻道了,頻道就是一個消息隊列。

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

37.什麼是codis及做用?

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

38.什麼是twemproxy及做用?

  是 Twtter 開源的一個 Redis 和 Memcache 代理服務器,主要用於管理 Redis 和 Memcached 集羣,減小與Cache 服務器直接鏈接的數量。

39.寫代碼實現redis事務操做

  import redis

  pool = redis.ConnectionPool(host='192.168.1.99', port=6379)
  conn = redis.Redis(connection_pool=pool)

# 開始事務 pipe = conn.pipeline(transaction=True) pipe.multi() pipe.set('name', 'bendere') pipe.set('role', 'sb')
# 提交 pipe.execute()

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

在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。
假設咱們經過WATCH命令在事務執行以前【監視】了多個Keys,假若在WATCH以後有任何Key的值發生了變化,
EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知調用者事務執行失敗。 面試題:你如何控制剩餘的數量不會出問題? 方式一:- 經過redis的watch實現 import redis conn = redis.Redis(host='192.168.1.99', 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()
方式二 - 數據庫的鎖

41.談談數據庫鎖

以MySQL爲例。InnoDB 存儲引擎實現了行鎖與表鎖。行鎖能夠以行爲單位對數據集進行鎖定。表鎖能夠以表爲單位對數據集進行鎖定。

行鎖、表鎖又可分爲兩種鎖:共享鎖與排他鎖。

  • 共享鎖:容許一個事務讀取一行,阻止其餘事務得到相同數據集的排他鎖。但容許其餘事務獲取共享鎖。
  • 排他鎖:容許得到排他鎖的事務更新數據,阻止其餘事務取得相同數據集的共享與排他鎖。可是能夠對獲取了排他鎖的數據集進行單純的查詢訪問。

對於 Update、Delete、insert 語句,InnoDB 會自動給涉及的數據集隱式的加上排他鎖。對於 select 語句 InnoDB 不會加任何鎖。能夠經過顯式的方式獲取共享鎖或者排他鎖。

  • 共享鎖:select * from table where ... lock in share mode
  • 排他鎖:select * from table where ... for update

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

import redis

conn = redis.Redis(host='192.168.1.99', port=6379)
conn.set('count',1000)
with conn.pipeline(transaction=True) 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實現分佈式鎖管理器的算法,咱們把這個算法稱爲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}, ] ) # 加鎖 my_lock = dlm.lock("my_resource_name", 10000) if my_lock: # 進行操做 # 解鎖 dlm.unlock(my_lock) else: print('獲取鎖失敗')
 redis分佈式鎖?
# 不是單機操做,又多了一/多臺機器
# redis內部是單進程、單線程,是數據安全的(只有本身的線程在操做數據)
----------------------------------------------------------------
A、B、C,三個實例(主)
一、來了一個'隔壁老王'要操做,且不想讓別人操做,加鎖;
   加鎖:'隔壁老王'本身生成一個隨機字符串,設置到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中全部以"w3c"開頭的key?

redis 有一個keys命令。
# 語法:KEYS pattern
# 說明:返回與指定模式相匹配的所用的keys。
該命令所支持的匹配模式以下:
一、"?":用於匹配單個字符。例如,h?llo能夠匹配hello、hallo和hxllo等;
二、"*":用於匹配零個或者多個字符。例如,h*llo能夠匹配hllo和heeeello等;
二、"[]":能夠用來指定模式的選擇區間。例如h[ae]llo能夠匹配hello和hallo,可是不能匹配hillo。同時,可使用「/」符號來轉義特殊的字符
# 例子:

  redis 127.0.0.1:6379> KEYS w3c*
  1) "w3c1"
  2) "w3c123"
  3) "w3c12"

# 注意
KEYS 的速度很是快,但若是數據太大,內存可能會崩掉,
若是須要從一個數據集中查找特定的key,最好仍是用Redis的集合結構(set)來代替。
 
來自轉載,有較大改動。
相關文章
相關標籤/搜索