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


數據庫和緩存

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

關系型數據庫(需要有表結構)
  mysql、oracle、splserver、postgresql、db2、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:設置外鍵約束。

4、什么是事務?MySQL如何支持事務?

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

一般來說,事務是必須滿足4個特性(ACID): Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durability(持久性)。
   1、原子性:事務包含的所有操作要么全部成功,要么全部失敗回滾,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
   2、一致性:事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處於一致性狀態。拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
   3、隔離性:當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
   4、持久性:持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
最重要的是1,2兩個特性。

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

  開啟自動提交:SET AUTOCOMMIT=1;

  如果執行語句:START TRANSACTION(開始一個事務); 那么不論有無設置自動提交,均需要手動提交或回滾。

  事務的周期由用戶在命令提示符中輸入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.索引在什么情況下遵循最左前綴的規則?

聯合索引。

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 … > 備份文件名.sql

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

 

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

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

15.數據庫優化方案

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

1、創建數據表時把固定長度的放在前面 2、將固定數據放入內存: 例如:choice字段 (django中有用到,數字1、2、3…… 對應相應內容) 3、char 和 varchar 的區別(char可變, varchar不可變 ) 4、聯合索引遵循最左前綴(從最左側開始檢索) 5、避免使用 select * 6、讀寫分離     - 實現:兩台服務器同步數據     - 利用數據庫的主從分離:主,用於增加、刪除、更新;從,用於查詢; 7、分庫     - 當數據庫中的表太多,將某些表分到不同的數據庫,例如:1W張表時     - 代價:連表查詢 8、分表     - 水平分表:將某些列拆分到另外一張表,例如:博客+博客詳情     - 垂直分表:講些歷史信息分到另外一張表中,例如:支付寶賬單 9、加緩存     - 利用redis、memcache (常用數據放到緩存里,提高取數據速度) 10、如果只想獲取一條數據 - 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.簡述數據庫分庫,分表(水平、垂直)?

 1、分庫
    當數據庫中的表太多,將某些表分到不同數據庫,例如:1W張表時
    代價:連表查詢跨數據庫,代碼變多。
 2、分表(提高查詢性能)
    水平分表:表的記錄行數龐大,將表按記錄行數分成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 10.169.130.11 6379 

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
# redis實例 dbs = Redlock( [ {"host": "localhost", "port": 6379, "db": 0}, {"host": "localhost", "port": 6379, "db": 0}, {"host": "localhost", "port": 6379, "db": 0}, ] ) # 加鎖 my_lock = dbs.lock("unique_key", 10000)    # 唯一資源名、有效時間(ms) if my_lock: # 進行操作 ... dbs.unlock(my_lock)  # 釋放鎖 else: print('獲取鎖失敗')
 redis分布式鎖?
# 不是單機操作,又多了一/多台機器
# redis內部是單進程、單線程,是數據安全的(只有自己的線程在操作數據)
----------------------------------------------------------------
A、B、C,三個實例(主)
1、來了一個'隔壁老王'要操作,且不想讓別人操作,加鎖;
   加鎖:'隔壁老王'自己生成一個隨機字符串,設置到A、B、C里(xxx=666)
2、來了一個'鄰居老李'要操作A、B、C,一讀發現里面有字符串,擦,被加鎖了,不能操作了,等着吧~
3、'隔壁老王'解決完問題,不用鎖了,把A、B、C里的key:'xxx'刪掉;完成解鎖
4、'鄰居老李'現在可以訪問,可以加鎖了
# 問題:
1、如果'隔壁老王'加鎖后突然掛了,就沒人解鎖,就死鎖了,其他人干看着沒法用咋辦?
2、如果'隔壁老王'去給A、B、C加鎖的過程中,剛加到A,'鄰居老李'就去操作C了,加鎖成功or失敗?
3、如果'隔壁老王'去給A、B、C加鎖時,C突然掛了,這次加鎖是成功還是失敗?
4、如果'隔壁老王'去給A、B、C加鎖時,超時時間為5秒,加一個鎖耗時3秒,此次加鎖能成功嗎?
# 解決
1、安全起見,讓'隔壁老王'加鎖時設置超時時間,超時的話就會自動解鎖(刪除key:'xxx')
2、加鎖程度達到(n/2)+1個(即過半)就表示加鎖成功,即使沒有給全部實例加鎖;
3、加鎖程度達到(n/2)+1個(即過半)就表示加鎖成功,即使沒有給全部實例加鎖;
4、不能成功,鎖還沒加完就過期,沒有意義了,應該合理設置過期時間

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

一致性hash算法(DHT)可以通過減少影響范圍的方式,解決增減服務器導致的數據散列問題,從而解決了分布式環境下負載均衡問題;
如果存在熱點數據,可以通過增添節點的方式,對熱點區間進行划分,將壓力分配至其他服務器,重新達到負載均衡的狀態。
Python模塊--hash_ring,即Python中的一致性hash

45.如何高效的找到redis中所有以"w3c"開頭的key?

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

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

# 注意
KEYS 的速度非常快,但如果數據太大,內存可能會崩掉,
如果需要從一個數據集中查找特定的key,最好還是用Redis的集合結構(set)來代替。
 
來自轉載,有較大改動。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM