關係型數據庫(須要有表結構) mysql、oracle、splserver、postgresql、db二、sybase 非關係型數據庫(是以key-value存儲的,沒有表結構)(NoSQL) MongoDB MongoDB 是一個高性能,開源,無模式的文檔型數據庫,開發語言是C++。它在許多場景下可用於替代傳統的關係型數據庫或鍵/值存儲方式。 Redis Redis 是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。
InnoDB 支持事務
支持外鍵 支持表鎖、行鎖(for update) 表鎖:select * from tb for update 行鎖:select id,name from tb where id=2 for update myisam 查詢速度快 全文索引 支持表鎖 表鎖:select * from tb for update
# 數據庫的三大特性: '實體':表 '屬性':表中的數據(字段) '關係':表與表之間的關係 ---------------------------------------------------- # 數據庫設計三大範式: 1:確保每列保持原子性(即數據庫表中的全部字段值是不可分解的原子值) 2:確保表中的每列都是和主鍵相關(表中只能保存一種數據,不能夠把多種數據保存在同一張表中)--->徹底屬於當前表的數據 3:確保每列都和主鍵直接相關,而不是間接相關(在一個數據庫表中保存的數據只能與主鍵相關)----> 消除傳遞依賴(間接) 好比在設計一個訂單數據表的時候,能夠將客戶編號做爲一個外鍵和訂單表創建相應的關係。
而不能夠在訂單表中添加關於客戶其它信息(好比姓名、所屬公司等)的字段。
# 數據庫五大約束' 1.primary KEY:設置主鍵約束; 2.UNIQUE:設置惟一性約束,不能有重複值; 3.CHECK:檢查約束 4.NOT NULL:設置非空約束,該字段不能爲空; 5.FOREIGN key:設置外鍵約束。
事務用於將某些操做的多個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結束。面試
FK(一對多) 下拉框裏面的數據就須要用FK關聯另外一張表 M2M(多對多) 多選的下拉框,或者checkbox
group by 分組對聚合的條件進行篩選須要經過havhing SQL的left join 、right join、inner join之間的區別 left join (左鏈接) 返回包括左表中的全部記錄和右表中聯結字段相等的記錄 right join(右鏈接) 返回包括右表中的全部記錄1和左表中聯結字段相等的記錄 inner join(內鏈接)只返回兩個表中聯結字段相等的行
觸發器: 對數據庫某張表的增長、刪除,修改先後定義一些操做。 函數:(觸發函數是經過select) 聚合函數:max/sum/min/avg 時間格式化:date_format 字符串拼接:concat 存儲過程: 將SQL語句保存到數據庫中,並命名,之後在代碼調用時,直接調用名稱便可 參數類型: in 只將參數傳進去 out 只拿結果 inout 既能夠傳,能夠取 函數與存儲過程區別: 本質上沒區別。只是函數只能返回一個變量的限制,而存儲過程能夠返回多個。函數是能夠嵌入在sql中使用的,能夠在select中調用,而存儲過程不能夠,它須要執行語句call來調用。 視圖: 視圖是一個虛擬表,不是真實存在的(通常只能查,不能改)
單列 普通索引:加速查找 惟一索引:加速查找 + 約束:不能重複(只能有一個空,否則就重複了) 主鍵(primay key):加速查找 + 約束:不能重複 + 不能爲空 多列 聯合索引(多個列建立索引)-----> 至關於單列的普通索引 聯合惟一索引 -----> 至關於單列的惟一索引 ps:聯合索引的特色:遵循最左前綴的規則 其餘詞語: ·· - 索引合併,利用多個單例索引查詢;(例如在數據庫查用戶名和密碼,分別給用戶名和密碼創建索引) - 覆蓋索引,在索引表中就能將想要的數據查詢到;
聯合索引redis
主鍵是能肯定一條記錄的惟一標示。例如,身份證證號 外鍵:用於與另外一張表的關聯,是能肯定另外一張表記錄的字段,用於保持數據的一致性
主鍵 | 外鍵 | |
定義 | 惟一標識一條記錄,不能有重複的,不容許爲空 | 表的外鍵是另外一張表的主鍵,外鍵能夠有重複的,能夠爲空 |
做用 | 用來保證數據完整性 | 用來與其餘表創建聯繫的 |
個數 | 主鍵只能有一個 | 一個表能夠有多個外鍵 |
聚合函數 max/min/sum/avg 時間格式化 date_format 字符串拼接 concat(當拼接了null,則返回null) 截取字符串 substring 返回字節個數 length
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 -- 不使用索引
修改配置文件 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 變量名 = 值
備份全部數據庫:
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緩存
能夠從數據庫服務器部署、表設計、表查詢等方面考慮。
一、建立數據表時把固定長度的放在前面 二、將固定數據放入內存: 例如:choice字段 (django中有用到,數字一、二、3…… 對應相應內容) 三、char 和 varchar 的區別(char可變, varchar不可變 ) 四、聯合索引遵循最左前綴(從最左側開始檢索) 五、避免使用 select * 六、讀寫分離 - 實現:兩臺服務器同步數據 - 利用數據庫的主從分離:主,用於增長、刪除、更新;從,用於查詢; 七、分庫 - 當數據庫中的表太多,將某些表分到不一樣的數據庫,例如:1W張表時 - 代價:連表查詢 八、分表 - 水平分表:將某些列拆分到另一張表,例如:博客+博客詳情 - 垂直分表:講些歷史信息分到另一張表中,例如:支付寶帳單 九、加緩存 - 利用redis、memcache (經常使用數據放到緩存裏,提升取數據速度) 十、若是隻想獲取一條數據 - select xxx from tb where name='alex' limit 1;
char可變,varchar不可變 。
查看有沒有命中索引,讓數據庫幫看看運行速度快不快 explain select * from table;
當type爲all時,是爲全表索引。
select * from tb where name = ‘Oldboy-Wupeiqi’
select * from tb where name = ‘Oldboy-Wupeiqi’ limit 1
沒作惟一索引的話,前者查詢會全表掃描,效率低些;
limit 1,只要找到對應一條數據,就不繼續往下掃描.
然而 name 字段添加惟一索引了,加不加limit 1,意義都不大。
答案一: 先查主鍵,在分頁。 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 )
簡單地說,對同一張表的一條sql可使用多個索引,對這些索引取交集、並集,從而減小從表中取數據的次數,提升查詢效率。
這個概念很重要!
一個索引涵蓋了全部須要查詢和返回字段的值,稱爲"覆蓋索引"。
只需掃描索引而無須回表,也就是說只須要經過索引就能夠返回查詢數據,而沒必要先查到索引以後再去查詢數據。性能不言而喻,速度很是快,很強大!!
在 Explain 的時候,輸出的 Extra 信息中若是有 "Using Index" ,就表示這條查詢使用了覆蓋索引。
- 前提:主備兩臺服務器同步數據 - 利用數據庫的主從分離:主,用於增長、刪除、更新;從,用於查詢;
例: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('...')
一、分庫 當數據庫中的表太多,將某些表分到不一樣數據庫,例如:1W張表時 代價:連表查詢跨數據庫,代碼變多。 二、分表(提升查詢性能) 水平分表:表的記錄行數龐大,將表按記錄行數分紅n份,每張表就小了不少。這些表結構一致。 垂直分表:將表的一些內容很長的列拆出來獨立成另外一張表。
區別: 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的分佈式存儲。
Redis默認支持16個數據庫,能夠經過配置databases來修改這一數字。客戶端與Redis創建鏈接後會自動選擇0號數據庫,不過能夠隨時使用SELECT命令更換數據庫。 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')
- 若是一個列表在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)
場景:投票系統
優點: - 高可用 - 分擔主壓力 注意: - slave設置只讀 從的配置文件添加如下記錄,便可: slaveof 1.1.1.1 3306
自動主從之間進行切換,實現熱切。 檢測主是否掛掉,且超過一半的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 啓動兩個哨兵
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 實例中。
16384
RDB:每隔一段時間對redis進行一次持久化。 - 缺點:數據不完整 - 優勢:速度快 AOF:把全部命令保存起來,若是想到從新生成到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(驅逐):禁止驅逐數據
相關知識: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(驅逐):禁止驅逐數據
參看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
# 經過"發佈訂閱"模式(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,用它作緩存、作計算,還作任務隊列,壓力太大,很差。
Codis是一個分佈式Redis解決方案, 對於上層的應用來講, 鏈接到Codis Proxy和鏈接原生的Redis Server沒有明顯的區別,
上層應用能夠像使用單機的Redis同樣使用, Codis底層會處理請求的轉發, 不停機的數據遷移等工做,
全部後邊的一切事情, 對於前面的客戶端來講是透明的, 能夠簡單的認爲後邊鏈接的是一個內存無限大的Redis服務。
是 Twtter 開源的一個 Redis 和 Memcache 代理服務器,主要用於管理 Redis 和 Memcached 集羣,減小與Cache 服務器直接鏈接的數量。
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()
在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()
方式二 - 數據庫的鎖
以MySQL爲例。InnoDB 存儲引擎實現了行鎖與表鎖。行鎖能夠以行爲單位對數據集進行鎖定。表鎖能夠以表爲單位對數據集進行鎖定。
行鎖、表鎖又可分爲兩種鎖:共享鎖與排他鎖。
對於 Update、Delete、insert 語句,InnoDB 會自動給涉及的數據集隱式的加上排他鎖。對於 select 語句 InnoDB 不會加任何鎖。能夠經過顯式的方式獲取共享鎖或者排他鎖。
select * from table where ... lock in share mode
select * from table where ... for update
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()
在不一樣進程須要互斥地訪問共享資源時,分佈式鎖是一種很是有用的技術手段。
用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個就表示加鎖成功,即便沒有給所有實例加鎖; 四、不能成功,鎖還沒加完就過時,沒有意義了,應該合理設置過時時間
一致性hash算法(DHT)能夠經過減小影響範圍的方式,解決增減服務器致使的數據散列問題,從而解決了分佈式環境下負載均衡問題; 若是存在熱點數據,能夠經過增添節點的方式,對熱點區間進行劃分,將壓力分配至其餘服務器,從新達到負載均衡的狀態。
Python模塊--hash_ring,即Python中的一致性hash
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)來代替。