redis介紹:
windows:
https://www.cnblogs.com/liuqingzheng/articles/9833534.html
linux:
https://lupython.gitee.io/2017/05/05/Redis學習目錄/
中文官網:www.redis.cn
訪問量多的時候咱們須要多個服務器作負載均衡,一種方式叫輪循, 另外一種叫權重,
當請求數據庫壓力過大時,咱們須要作主從(通常是一主多從),
當主從仍然沒法知足咱們需求時.咱們須要加緩存(之前用的memcache,redis出現之後用redis),
加緩存之後請求數據庫先從緩存中查找,找不到才從數據庫中查找,在數據庫中拿到之後要在緩存中放一份,以防止再次訪問
Redis-x64-3.2.100(1).msi: redis安裝包 redis-desktop-manager-0.9.3.817.exe:redis管理工具 #安裝介紹網站 https://www.cnblogs.com/liuqingzheng/p/9831331.html
redis官方沒有提供windows安裝包,建議安裝到linux中
安裝到linux中咱們須要關注下面幾個文件
redis-benchmark :壓力測試
(apache有一個ab工具能夠作壓力測試,模擬流量大時網站的承受能力
運行:在windows系統下,打開cmd命令切換到apache安裝目錄下
輸入命令:ab -n 800 -c 800 http://192.168.0.10/
-n發出800個請求,-c模擬800併發,至關於800人同時訪問,後面是測試url
ab -t 60 -c100 http://192.168.0.10/
在60秒內發送請求,一次100個請求)
redis-check-aof :檢查aof日誌
redis-check-rdb :檢查rdb日誌 redis中數據持久化的方式:aof和rdb
redis-sentinel :哨兵 作redis集羣時會用到,有sentinel當主redis出現故障的時候會自動切換到從redis
redis-cli :客戶端鏈接
redis-server :啓動redis服務 默認端口6379 修改:redis.conf
''' -mysql,oracle:關係型數據庫 -redis,mongodb:非關係型數據庫/nosql -redis儲存在內存中,redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步 -mongodb儲存在硬盤上 -redis通常用來
-數據庫 -作緩存(最重要用途) -session數據 -遊戲排行 -對速度要求比較高的數據儲存 -作消息隊列 -redis是key-value的存儲,支持持久化,像py中的字典,有五大數據類型: -字符串(string) -列表(list) -字典(hash) -集合(set) -有序集合(order_set) redis={ k1:'123', 字符串 k2:[1,2,3,4], 列表/數組 k3:{1,2,3,4} 集合 k4:{name:lqz,age:12} 字典/哈希表 k5:{('lqz',18),('egon',33)} 有序集合 } #redis支持上述五大數據類型,可是它只支持一層 -比較redis和Memcached -redis 支持5大數據類型 -redis支持持久化 -單線程,單進程,速度是很是快 -Memcached不能持久化,只支持字符串 -存redis的token 三種方案: -request.user 是字典或者用戶id -request.user 就是user對象,在redis中存的value值是pickle以後的字符串 -request.user 就是user對象,在redis中存的value值是json格式數據
1.安裝redis模塊 2.簡單操做 ''' import redis #拿到redis的鏈接 conn=redis.Redis(host='127.0.0.1',port=6379,password=12345) #插入字符串數據 conn.set('age','18') #獲取數據 name=conn.get('name') #redis中存的都是byte格式 print(name) '''
redis-py使用connection pool來管理對一個redis server的全部鏈接,避免每次創建、釋放鏈接的開銷。默認,每一個Redis實例都會維護一個本身的鏈接池。能夠直接創建一個鏈接池,而後做爲參數Redis,這樣就能夠實現多個Redis實例共享一個鏈接池 #必須是單例 -實現單例的方式 ①當其以模塊導入實現單例,這是python中一種自然的單例方式 ②__new__實現單例 ③裝飾器來控制實現單例 ''' #模塊導入實現單例 -新建一個模塊 import redis
POOL=redis.ConnectionPool(host='127.0.0.1',port=6379,password=12345,max_connections=1000) -使用鏈接池 #導入池子 from conn_pool import POOL # 每次執行這句話,從池子中取一個連接 conn=redis.Redis(connection_pool=POOL) '''
#重點掌握 get set mget mset incr decr append #String操做,redis中的String在在內存中按照一個name對應一個value來存儲 1.set(name, value, ex=None, px=None, nx=False, xx=False) 在Redis中設置值,默認,不存在則建立,存在則修改 參數: ex,過時時間(秒) px,過時時間(毫秒) nx,若是設置爲True,則只有name不存在時,當前set操做才執行,值存在,就修改不了,執行沒效果 xx,若是設置爲True,則只有name存在時,當前set操做才執行,值存在才能修改,值不存在,不會設置新值 2.setnx(name, value) #設置值,只有name不存在時,執行設置操做(添加),若是存在,不會修改 3.setex(name, time, value) # 設置值 # 參數: # time,過時時間(數字秒 或 timedelta對象) 4.psetex(name, time_ms, value) # 設置值 # 參數: # time_ms,過時時間(數字毫秒 或 timedelta對象 5.mset(*args, **kwargs) #批量設置值 如: mset(k1='v1', k2='v2') 或 mset({'k1': 'v1', 'k2': 'v2'}) 6.get(name) #獲取值 7.mget(names, *args) #批量獲取 如: mget('k1', 'k2') 或 mget(['k3', 'k4']) 8.getset(name, value) #設置新值並獲取原來的值 9.getrange(name, start, end) # 獲取子序列(根據字節獲取,非字符) # 參數: # name,Redis 的 name # start,起始位置(字節) # end,結束位置(字節) # 如: "henry" ,0-2表示 "hen" 前閉後閉,全閉區間 10.setrange(name, offset, value) # 修改字符串內容,從指定字符串索引開始向後替換(新值太長時,則向後添加) # 參數: # offset,字符串的索引,字節(一個漢字三個字節) # value,要設置的值 11.setbit(name, offset, value) # 對name對應值的二進制表示的位進行操做 # 參數: # name,redis的name # offset,位的索引(將值變換成二進制後再進行索引) # value,值只能是 1 或 0 # 注:若是在Redis中有一個對應: n1 = "foo", 那麼字符串foo的二進制表示爲:01100110 01101111 01101111 因此,若是執行 setbit('n1', 7, 1),則就會將第7位設置爲1, 那麼最終二進制則變成 01100111 01101111 01101111,即:"goo" 12.getbit(name, offset) # 獲取name對應的值的二進制表示中的某位的值 (0或1) 13.bitcount(name, start=None, end=None) # 獲取name對應的值的二進制表示中 1 的個數 # 參數: # name,Redis的name # start,位起始位置 # end,位結束位置 14.bitop(operation, dest, *keys) # 獲取多個值,並將值作位運算,將最後的結果保存至新的name對應的值 # 參數: # operation,AND(並) 、 OR(或) 、 NOT(非) 、 XOR(異或) # dest, 新的Redis的name # *keys,要查找的Redis的name # 如: bitop("AND", 'new_name', 'n1', 'n2', 'n3') # 獲取Redis中n1,n2,n3對應的值,而後講全部的值作位運算(求並集),而後將結果保存 new_name 對應的值中 15.strlen(name) # 返回name對應值的字節長度(一個漢字3個字節) 16.incr(self, name, amount=1) #amount默認爲1 用於瀏覽量等 # 自增 name對應的值,當name不存在時,則建立name=amount,不然,則自增。 # 參數: # name,Redis的name # amount,自增數(必須是整數) # 注:同incrby 17.incrbyfloat(self, name, amount=1.0) # 自增 name對應的值,當name不存在時,則建立name=amount,不然,則自增。 # 參數: # name,Redis的name # amount,自增數(浮點型) 18.decr(self, name, amount=1) #amount默認爲1 # 自減 name對應的值,當name不存在時,則建立name=amount,不然,則自減。 # 參數: # name,Redis的name # amount,自減數(整數) 19.append(name, value) # 在redis name對應的值後面追加內容 # 參數: name, redis的name value, 要追加的字符串 20.自定義增量迭代 # 因爲redis類庫中沒有提供對列表元素的增量迭代,若是想要循環name對應的列表的全部元素,那麼就須要: # 一、獲取name對應的全部列表 # 二、循環列表 # 可是,若是列表很是大,那麼就有可能在第一步時就將程序的內容撐爆,全部有必要自定義一個增量迭代的功能: def scan_list(name,count=1000) #count爲默認一次取出數據的個數 cursor=0 while True: if cursor >= re.llen(name): break ret = re.lrange(name,cursor,count+cursor-1) cursor+=count for i in ret: yield i #使用 for i in scan_list('l1',100):#一次取100條數據 print(i)
應用場景:在登陸時能夠作頻率控制
每次訪問判斷
if conn.get('cnt') > 5:
set cnt 0 EX 900 #設置cnt超時時間900s
else:
conn.incr('cnt')
#List操做,redis中的List在在內存中按照一個name對應一個List來存儲。 1.r.lpush(name,values) # 在name對應的list中添加元素,每一個新的元素都添加到列表的最左邊 # 如: # r.lpush('l1', 1,2,3) # 保存順序爲: 3,2,1 # r.lpush('l2',*[1,2,3]) #保持順序爲: 3,2,1 # 擴展: # rpush(name, values) 表示從右向左操做 2.r.lpushx(name,value) # 在name對應的list中添加元素,只有name已經存在時,值添加到列表的最左邊,若是name不存在不進行任何操做 # 更多: # rpushx(name, value) 表示從右向左操做 3.r.llen(name) # name對應的list元素的個數 4.r.linsert(name, where, refvalue, value)) # 在name對應的列表的某一個值前或後插入一個新值 # 參數: # name,redis的name # where,BEFORE或AFTER(小寫也能夠) # refvalue,標杆值,即:在它先後插入數據(若是存在多個標杆值,以找到的第一個爲準) # value,要插入的數據 # r.linsert('l1','BEFORE','3','p') 在l1列表中3的前面加一個p 5.r.lset(name, index, value) # 對name對應的list中的某一個索引位置從新賦值 # 參數: # name,redis的name # index,list的索引位置 # value,要設置的值 6.r.lrem(name, num, value) # 在name對應的list中刪除指定的值 # 參數: # name,redis的name # num, num=0,刪除列表中全部的指定值; # num=2,從前到後,刪除2個; # num=-2,從後向前,刪除2個 # value,要刪除的值 7.r.lpop(name) # 在name對應的列表的左側獲取第一個元素並在列表中移除,返回值則是第一個元素 # 更多: # rpop(name) 表示從右向左操做 8.r.lindex(name, index) #在name對應的列表中根據索引獲取列表元素 9.r.lrange(name, start, end) # 在name對應的列表分片獲取數據 # 參數: # name,redis的name # start,索引的起始位置 # end,索引結束位置 print(r.lrange('aa',0,r.llen('aa'))) #前閉後閉區間 10.r.ltrim(name, start, end) # 在name對應的列表中移除沒有在start-end索引之間的值 # 參數: # name,redis的name # start,索引的起始位置 # end,索引結束位置(大於列表長度,則表明不移除任何) #前閉後閉區間 11.r.rpoplpush(src, dst) # 從一個列表取出最右邊的元素,同時將其添加至另外一個列表的最左邊 # 參數: # src,要取數據的列表的name # dst,要添加數據的列表的name 12.blpop(name, timeout) # 從左到右去pop對應列表的元素 # 參數: # name,redis的name # timeout,超時時間,當全部列表的元素獲取完以後,將會阻塞,而後等待列表內有數據的時間(秒), 0 表示永遠阻塞,默認是0 # 更多: # r.brpop(keys, timeout),從右向左獲取數據 爬蟲實現簡單分佈式:多個url放到列表裏,往裏不停放URL,程序循環取值,可是隻能一臺機器運行取值,能夠把url放到redis中,多臺機器從redis中取值,爬取數據,實現簡單分佈式,一個負責爬鏈接,一個負責取數據存數據庫 13.brpoplpush(src, dst, timeout=0) # 從一個列表的右側移除一個元素並將其添加到另外一個列表的左側 # 參數: # src,取出並要移除元素的列表對應的name # dst,要插入元素的列表對應的name # timeout,當src對應的列表中沒有數據時,阻塞等待其有數據的超時時間(秒),0 表示永遠阻塞
#Hash操做,redis中Hash在內存中的存儲格式 name -> k1->v1 k2->v2 k3->v3 1.hset(name, key, value) # name對應的hash中設置一個鍵值對(不存在,則建立;不然,修改) # 參數: # name,redis的name # key,name對應的hash中的key # value,name對應的hash中的value #r.hset('n1','name','henry') # 注: # hsetnx(name, key, value),當name對應的hash中不存在當前key時則建立(至關於添加),存在不進行任何操做 2.hmset(name, mapping) # 在name對應的hash中批量設置鍵值對 # 參數: # name,redis的name # mapping,字典,如:{'k1':'v1', 'k2': 'v2'} # 如: # r.hmset('xx', {'k1':'v1', 'k2': 'v2'}) 3.hget(name,key) # 在name對應的hash中根據key獲取value 4.hmget(name, keys) # 在name對應的hash中獲取多個key的值 # 參數: # name,reids對應的name # keys,要獲取key集合,如:['k1', 'k2', 'k3'] # 第一個參數爲name,後面也能夠直接寫'k1','k2','k3' # 如: # r.mget('xx', ['k1', 'k2']) # 或 # print(r.hmget('xx', 'k1', 'k2')) 5.hgetall(name) # 把字典中的全部鍵-值對取出來,若是存儲的數據量過大,儘可能慎用 # 獲取name對應hash的全部鍵值 print(re.hgetall('xxx').get(b'name')) 6.hlen(name) # 獲取name對應的hash中鍵值對的個數 7.hkeys(name) # 獲取name對應的hash中全部的key的值 8.hvals(name) # 獲取name對應的hash中全部的value的值 9.hexists(name, key) # 檢查name對應的hash是否存在當前傳入的key 10.hdel(name,*keys) # 將name對應的hash中指定key的鍵值對刪除,返回值爲你刪除了幾個鍵值對 print(re.hdel('xxx','sex','name')) 11.hincrby(name, key, amount=1) # 自增name對應的hash中的指定key的值,不存在則建立key=amount # 參數: # name,redis中的name # key, hash對應的key # amount,自增數(整數) re.hincrby('n1','age',amount=1) #n1中的age字段的值自增1,不存在則建立age字段值爲1 12.hincrbyfloat(name, key, amount=1.0) # 自增name對應的hash中的指定key的值,不存在則建立key=amount # 參數: # name,redis中的name # key, hash對應的key # amount,自增數(浮點數) # 自增name對應的hash中的指定key的值,不存在則建立key=amount 13.hscan(name, cursor=0, match=None, count=None) # 增量式迭代獲取,對於數據大的數據很是有用,hscan能夠實現分片的獲取數據,並不是一次性將數據所有獲取完,從而放置內存被撐爆 # 參數: # name,redis的name # cursor,遊標(基於遊標分批取獲取數據) # match,匹配指定key,默認None 表示全部的key # count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數 #返回有兩個值,一個是遊標的位置,一個是取到的值 # 如: # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None) # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None) # ... # 直到返回值cursor的值爲0時,表示數據已經經過分片獲取完畢 14.hscan_iter(name, match=None, count=None) # 利用yield封裝hscan建立生成器,實現分批去redis中獲取數據 # 參數: # match,匹配指定key,默認None 表示全部的key # count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數 # 如: # for item in r.hscan_iter('xx'): # print item
linux使用下 網址: https://lupython.gitee.io/2017/05/05/Redis%E5%AD%A6%E4%B9%A0%E7%9B%AE%E5%BD%95/ sinter s1 s2 s3 :求s1,s2,s3三個集合的交集 sunion s1 s2 s3 :求s1,s2,s3三個集合的並集 sdiff s1 s2 s3 :求s1與s2,s3的差集 s1-s2-s3 應用場景:微博共同關注 sinter
linux 1.zadd s1 score1 value1 score2 value2 .. zadd stu 18 lily 19 hmm 20 lilei 21 2.ZRANGE key start stop [WITHSCORES] 把集合排序後,返回名次[start,stop]的元素 默認是升續排列 Withscores 是把score也打印出來 ZRANGE stu 0 -1 (從頭開始到結束全拿出來) ZRANGE stu 0 -1 WITHSCORES 3.zrank key member 查詢member的排名(升序 0名開始) ZRANK stu lily #0 ZRANK stu hmm #1 ZRANK stu lilei #2 4.zrevrank key memeber 查詢member的排名(降序 0名開始) 5.zcard key 返回元素個數
1.delete(*names) # 根據刪除redis中的任意數據類型 2.exists(name) # 檢測redis的name是否存在,返回值爲存在的個數 3.keys(pattern='*') # 根據模型獲取redis的name # 更多: # KEYS * 匹配數據庫中全部 key 。 # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 # KEYS h*llo 匹配 hllo 和 heeeeello 等。 # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 4.expire(name ,time) # 爲某個redis的某個name設置超時時間 5.rename(src, dst) # 對redis的name重命名爲 6.move(name, db)) # 將redis的某個值移動到指定的db下 7.randomkey() # 隨機獲取一個redis的name(不刪除) 8.type(name) # 獲取name對應值的類型 9.scan(cursor=0, match=None, count=None) scan_iter(match=None, count=None) # 用於增量迭代獲取key
redis-py默認在執行每次請求都會建立(鏈接池申請鏈接)和斷開(歸還鏈接池)一次鏈接操做,若是想要在一次請求中指定多個命令,則可使用pipline實現一次請求指定多個命令,而且默認狀況下一次pipline 是原子性操做。 #注意:關係型數據庫存在事務,非關係型數據庫不存在事務,可是咱們能夠經過管道模擬出事務 import redis pool = redis.ConnectionPool(host='10.211.55.4', port=6379) r = redis.Redis(connection_pool=pool) pipe = r.pipeline(transaction=True) pipe.multi() pipe.set('name', 'alex') pipe.set('role', 'sb') pipe.execute()
創建redis_pool.py import redis POOL = redis.ConnectionPool(host='127.0.0.1', port=6379,password='1234',max_connections=1000) 視圖函數中使用: import redis from django.shortcuts import render,HttpResponse from utils.redis_pool import POOL def index(request): conn = redis.Redis(connection_pool=POOL) conn.hset('kkk','age',18) return HttpResponse('設置成功') def order(request): conn = redis.Redis(connection_pool=POOL) conn.hget('kkk','age') return HttpResponse('獲取成功')
安裝django-redis模塊 pip3 install django-redis setting裏配置: CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } }, "aa": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } }, } 視圖函數: from django_redis import get_redis_connection conn = get_redis_connection('default') #能夠選擇本身要操做的服務器,默認default print(conn.hgetall('xxx'))
網址: https://lupython.gitee.io/2017/05/05/Python操做redis及其應用/ 1.統計頁面點擊數 論壇每一個帖子都要記錄點擊次數,而點擊次數比回帖的次數的多得多。若是使用關係數據庫來存儲點擊,可能存在大量的行級鎖爭用。因此,點擊數的增長使用redis的INCR命令最好不過了 2.如何保存一個對象數據 3.社交圈子數據 微博共同關注 4.反垃圾系統 獲取某段時間全部數據排重值,這個使用Redis的set數據結構最合適了,只須要不斷地將數據往set中扔就好了,set意爲集合,因此會自動去重 5.Pub/Sub構建實時消息系統 6.Session共享存入redis 咱們能夠把Session存入redis,來進行共享 Session共享:剛開始只有一臺服務器,用戶訪問服務器,登陸成功,會產生一個session,下次登陸cookie中攜帶session過來咱們就能夠肯定該用戶爲合法用戶.隨着網站的發展,會變成分佈式的有多臺服務器,假設有三臺機器,A訪問經過第一臺服務器,登陸成功之後,A用戶的session值只存在在第一臺服務器上,第二臺和第三臺服務器沒有,若是A下次訪問,不是訪問的第一臺服務器,就找不到session,會返回請登陸頁面,就形成剛剛訪問過又要登陸的情景,因此須要進行session共享. session共享解決方案:1.文件複製的方式(通常不採用) 2.存到專門存放session的服務器上(存放在session服務器mysql中 後來存放到session服務器的redis中)
介紹下載網址: memcached.org https://lupython.gitee.io/2017/05/05/MemCache介紹/
redis和memcache區別:
redis支持數據持久化,落地到硬盤
memcache 不支持數據持久化,斷電數據丟失
新浪博客用的memcache 可是他們將新浪博客改成持久化的了 memcacheq
memcache內存管理機制:
內存碎片化?
若是用c語言直接 malloc,free 來向操做系統申請和釋放內存時,
在不斷的申請和釋放過程當中,造成了一些很小的內存片段,沒法再利用.
這種空閒,但沒法利用內存的現象,—稱爲內存的碎片化.
memcache是將數據存入到內存中進行管理的,故在MemCache中也存在內存碎片化的現象,所以爲了解決內存碎片化帶來的浪費,
在MemCache中採用的是slab allocator來進行緩解內存的碎片化.
slab allocator的原理:
在啓動MemCache時,把內存劃分紅數個slabclass倉庫(每一個slabclass大小1M),各倉庫,切分紅不一樣尺寸的小塊(chunk).
須要存內容時,判斷內容的大小,爲其選取合理的倉庫.
如何將數據存入到MemCache中呢?
memcache根據收到的數據的大小,選擇最適合數據大小的chunk組(slabclass)
memcache中保存着slabclass內空閒chunk的列表,根據該列表選擇空的chunk, 而後將數據緩存於其中.
注意:
有100byte的內容要存,但122大小的倉庫中的chunks滿了
並不會尋找更大的,如144的倉庫來存儲,而是把122倉庫的舊數據踢掉!
固定大小chunks帶來的內存浪費:
好比當咱們將100字節的數據緩存到122字節的chunk中的時候,剩餘的22字節就浪費了
這個問題沒有根治的方法,只能緩解
1.過時數據惰性刪除機制 ①當某個值過時後,並無從內存刪除 ②當get值時, 首先判斷是否過時,若是過時, 則返回空, 而且清空該值, 此時curr_item就減小了. 即–這個過時,只是讓用戶看不到這個數據而已,並無在過時的瞬間當即從內存刪除.這個稱爲lazyexpiration,惰性失效.好處— 節省了cpu時間和檢測的成本 2.LRU刪除機制 以122byte大小的chunk舉例,122的chunk都滿了, 又有新的值(長度爲120)要加入, 要擠掉誰? 原理 : 當某個單元被請求時,維護一個計數器,經過計數器來判斷最近誰最少被使用.就把誰踢出去,即便某個key是設置的永久有效期,也同樣會被踢出來!即–永久數據被踢現象