非關係型數據庫之Redis

1、Redis簡介

     REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統。html

  Redis是一個開源的使用ANSI C語言編寫、遵照BSD協議、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。它一般被稱爲數據結構服務器,由於值(value)能夠是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。常常被用做數據庫,緩存和消息代理。它支持數據結構,如字符串,散列,列表,集合,帶有範圍查詢的排序集,位圖,超級日誌,帶有半徑查詢和流的地理空間索引。Redis具備內置複製,Lua腳本,LRU驅逐,事務和不一樣級別的磁盤持久性,並經過Redis Sentinel提供高可用性並使用Redis Cluster自動分區。 python

爲何選擇Redis?

1. 使用Redis有哪些好處?

(1) 速度快,由於數據存在內存中,相似於HashMap,HashMap的優點就是查找和操做的時間複雜度都是O(1)

(2) 支持豐富數據類型,支持string,list,set,sorted set,hash

(3) 支持事務,操做都是原子性,所謂的原子性就是對數據的更改要麼所有執行,要麼所有不執行

(4) 豐富的特性:可用於緩存,消息,按key設置過時時間,過時後將會自動刪除


2. redis相比memcached有哪些優點?

(1) memcached全部的值均是簡單的字符串,redis做爲其替代者,支持更爲豐富的數據類型

(2) redis的速度比memcached快不少

(3) redis能夠持久化其數據


3. redis常見性能問題和解決方案:

(1) Master最好不要作任何持久化工做,如RDB內存快照和AOF日誌文件

(2) 若是數據比較重要,某個Slave開啓AOF備份數據,策略設置爲每秒同步一次

(3) 爲了主從複製的速度和鏈接的穩定性,Master和Slave最好在同一個局域網內

(4) 儘可能避免在壓力很大的主庫上增長從庫

(5) 主從複製不要用圖狀結構,用單向鏈表結構更爲穩定,即:Master <- Slave1 <- Slave2 <- Slave3...

這樣的結構方便解決單點故障問題,實現Slave對Master的替換。若是Master掛了,能夠馬上啓用Slave1作Master,其餘不變。



 

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

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

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(驅逐):禁止驅逐數據

 

5. Memcache與Redis的區別都有哪些?

1)、存儲方式

Memecache把數據所有存在內存之中,斷電後會掛掉,數據不能超過內存大小。

Redis有部份存在硬盤上,這樣能保證數據的持久性。

2)、數據支持類型

Memcache對數據類型支持相對簡單。

Redis有複雜的數據類型。


3),value大小

redis最大能夠達到1GB,而memcache只有1MB



6. Redis 常見的性能問題都有哪些?如何解決?

 

1).Master寫內存快照,save命令調度rdbSave函數,會阻塞主線程的工做,當快照比較大時對性能影響是很是大的,會間斷性暫停服務,因此Master最好不要寫內存快照。


2).Master AOF持久化,若是不重寫AOF文件,這個持久化方式對性能的影響是最小的,可是AOF文件會不斷增大,AOF文件過大會影響Master重啓的恢復速度。Master最好不要作任何持久化工做,包括內存快照和AOF日誌文件,特別是不要啓用內存快照作持久化,若是數據比較關鍵,某個Slave開啓AOF備份數據,策略爲每秒同步一次。

 
3).Master調用BGREWRITEAOF重寫AOF文件,AOF在重寫的時候會佔大量的CPU和內存資源,致使服務load太高,出現短暫服務暫停現象。

4). Redis主從複製的性能問題,爲了主從複製的速度和鏈接的穩定性,Slave和Master最好在同一個局域網內




7, redis 最適合的場景


Redis最適合全部數據in-momory的場景,雖然Redis也提供持久化功能,但實際更多的是一個disk-backed的功能,跟傳統意義上的持久化有比較大的差異,那麼可能你們就會有疑問,彷佛Redis更像一個增強版的Memcached,那麼什麼時候使用Memcached,什麼時候使用Redis呢?

       若是簡單地比較Redis與Memcached的區別,大多數都會獲得如下觀點:
、Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
、Redis支持數據的備份,即master-slave模式的數據備份。
、Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用。

(1)、會話緩存(Session Cache)

最經常使用的一種使用Redis的情景是會話緩存(session cache)。用Redis緩存會話比其餘存儲(如Memcached)的優點在於:Redis提供持久化。當維護一個不是嚴格要求一致性的緩存時,若是用戶的購物車信息所有丟失,大部分人都會不高興的,如今,他們還會這樣嗎?

幸運的是,隨着 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來緩存會話的文檔。甚至廣爲人知的商業平臺Magento也提供Redis的插件。

(2)、全頁緩存(FPC)

除基本的會話token以外,Redis還提供很簡便的FPC平臺。回到一致性問題,即便重啓了Redis實例,由於有磁盤的持久化,用戶也不會看到頁面加載速度的降低,這是一個極大改進,相似PHP本地FPC。

再次以Magento爲例,Magento提供一個插件來使用Redis做爲全頁緩存後端。

此外,對WordPress的用戶來講,Pantheon有一個很是好的插件  wp-redis,這個插件能幫助你以最快速度加載你曾瀏覽過的頁面。

(3)、隊列

Reids在內存存儲引擎領域的一大優勢是提供 list 和 set 操做,這使得Redis能做爲一個很好的消息隊列平臺來使用。Redis做爲隊列使用的操做,就相似於本地程序語言(如Python)對 list 的 push/pop 操做。

若是你快速的在Google中搜索「Redis queues」,你立刻就能找到大量的開源項目,這些項目的目的就是利用Redis建立很是好的後端工具,以知足各類隊列需求。例如,Celery有一個後臺就是使用Redis做爲broker,你能夠從這裏去查看。

(4),排行榜/計數器

Redis在內存中對數字進行遞增或遞減的操做實現的很是好。集合(Set)和有序集合(Sorted Set)也使得咱們在執行這些操做的時候變的很是簡單,Redis只是正好提供了這兩種數據結構。因此,咱們要從排序集合中獲取到排名最靠前的10個用戶–咱們稱之爲「user_scores」,咱們只須要像下面同樣執行便可:

固然,這是假定你是根據你用戶的分數作遞增的排序。若是你想返回用戶及用戶的分數,你須要這樣執行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來存儲數據的,你能夠在這裏看到。

(5)、發佈/訂閱

最後(但確定不是最不重要的)是Redis的發佈/訂閱功能。發佈/訂閱的使用場景確實很是多。我已看見人們在社交網絡鏈接中使用,還可做爲基於發佈/訂閱的腳本觸發器,甚至用Redis的發佈/訂閱功能來創建聊天系統!(不,這是真的,你能夠去核實)。

Redis提供的全部特性中,我感受這個是喜歡的人最少的一個,雖然它爲用戶提供若是此多功能。

2、下載和安裝

  1. windowslinux

  在redis官網 http://www.redis.net.cn/download/redis

  選擇對應版本安裝便可。數據庫

  

   隨後把下載文件夾目錄添加到環境變量。django

配置文件

bind 0.0.0.0
port 6379
requirepass 密碼

  2. linux

下載和安裝windows

yum install redis
  - redis-server /etc/redis.conf   啓動服務器
或者
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar xzf redis-3.0.6.tar.gz
cd redis-3.0.6
make
vi redis.conf 修改配置文件
  - bind 0.0.0.0
  - port 6379
  - requirepass 0000

啓動服務端後端

src/redis-server  redis.conf

啓動客戶端緩存

src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"

3、啓動客戶端:redis-cli

  

  redis默認有15個數據庫安全

 

  選擇1號數據庫

 

 

四.數據操做

  redis是key-value的數據,因此每一個數據都是一個鍵值對。鍵的類型是字符串。

  值的類型分爲五種:

  • 字符串string
  • 哈希hash
  • 列表list
  • 集合set
  • 有序集合zset

 1.string

  •   string是redis最基本的類型
  •   最大能存儲512MB數據
  •   string類型是二進制安全的,便可覺得任何數據,好比數字、圖片、序列化對象等

   命令:

  •   設置鍵值:set key value(單個值),setex key seconds value(設置時間), mset key1 value1 key2 value2 ..(爲多個值賦值).  
  •   獲取鍵值:get key(獲取單個值), mget key1 key2(獲取多個值)
  •   運算:incr,incrby,decr,decrby,append key value, strlen key   要求 value是數字

  鍵命令

  •   keys pattern:查看鍵值  keys *  查看全部鍵值 keys article*
  •   exists key:查看鍵值是否存在
  •   type key:查看key對應的類型
  •   del key:刪除key
  •   expire key seconds:設置key過時時間
  •   ttl key:查看key過時時間

 2.hash: 用於存儲對象,對象的格式爲鍵值對。

    hset key field value: 設置hash key對象指定數據類型的一個值

    hmset key field1 value1 filed2 value2 ...:設置hash key對象多個數據類型的值

    hget key field:獲取指定key的指定數據類型的值

    hmget key field1 field2 : 獲取key的field1和field中的value

    hkeys key : 返回key的field

    hlen key:返回key的鍵值的個數

    hvals key:返回key的value

    hexists key field: 判斷key的field的值是否存在

    hdel key filed: 刪除key 的field的值

              strlen key field: 判斷key中field的值的長度

 3. list  

  • 列表的元素類型爲string
  • 按照插入順序排序
  • 在列表的頭部或者尾部添加元素

   命令:lpush key value: 往列表key的左邊插入一個value

        rpush key value:往列表key的右邊插入一個value

        linsert key before|after value new_value:往列表key中value前|後插入new_value

     lset key index new_value: 將列表key的第index個value設置爲new_value

     lpop key:左彈出key列表中的值

       rpop key:右彈出key列表中的值

     lrange key start end:查看key列表中start-end中的值

 

    4.set

    • 無序集合
    • 元素爲string類型
    • 元素具備惟一性,不重複

   命令:sadd key value : 往無序集合key中插入value值,位置隨機

      spop key:在無序集合key中隨機彈出集合一個值

      smembers key:查看無序集合key中的全部元素

      scard key:查看無序集合key的值的個數

 5.zset

    • sorted set,有序集合
    • 元素爲string類型
    • 元素具備惟一性,不重複
    • 每一個元素都會關聯一個double類型的score,表示權重,經過權重將元素從小到大排序
    • 元素的score能夠相同

     命令:zadd key score1 value1 score2 value2 : 向有序集合key中添加value1,value2並制定相應權重

        zrem key value:刪除有序集合中的value

        zrange key start end:查看有序集合中start-end中的值

        zcard key:查看有序集合中元素的個數

        zsocre key value:查看有序集合key中value的score

        zcount key min max:查看有序集合key中score在min-max之間的元素

 

5、python鏈接redis

  1. 安裝

pip install redis

  redis-py提供兩個類Redis和StrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,並使用官方的語法和命令,Redis是StrictRedis的子類

  2. 建立鏈接

from redis import Redis, ConnectionPool

# 建立鏈接
result = Redis(host='127.0.0.1', port=6379)
print(result.keys())

  3. 使用鏈接池 

from redis import Redis, ConnectionPool

# 鏈接池
pool = ConnectionPool(host='127.0.0.1', port=6379)
conn = Redis(connection_pool=pool)
# print(conn.keys())
# print(conn.smembers('visited_urls'))
print(conn.smembers('dupefilter:test_scrapy_redis'))

 注意:鏈接池只建立一次

import redis
# 最簡單的單例模式:寫一個py文件導入
from redis_pool import POOL

while True:
	key = input('請輸入key:')
	value = input('請輸入value:')
	# 去鏈接池中獲取鏈接
	conn = redis.Redis(connection_pool=POOL)
	# 設置值
	conn.set(key, value)

  4. 數據操做

  • 五大數據類型
redis = {
		k1:'123', 字符串
		k2:[1,2,3,4,5], 列表
		k3:{1,2,3,4},	集合
		k4:{name:'root','age':23},	字典
		k5:{('alex',60),('eva-j',80),('rt',70),},有序集合
	}

 a.使用字典 

  - 基本操做

# HASH COMMANDS
# 建立字典
# 將字典name的key設置爲value hset(self, name, key, value):
# 若字典name的key不存在時將value設置給key,不然不設置 hsetnx(self, name, key, value): hmset(self, name, mapping): # 獲取字典的值
# 獲取單個key的值 hget(self, name, key):
# 獲取多個key的值 hmget(self, name, keys, *args):
# 獲取字典name全部的值 hgetall(self, name):
# 獲取字典name全部的key hkeys(self, name):
# 獲取字典name全部的value hvals(self, name): # 判斷某個key是否存在 hexists(self, name, key): # 獲取字典name元素的長度 hlen(self, name):
# 獲取字典name的指定key的value的長度 hstrlen(self, name, key): # 刪除字典的key hdel(self, name, *keys): # 計數器 hincrby(self, name, key, amount=1): hincrbyfloat(self, name, key, amount=1.0): # 性能相關:迭代器 hscan(self, name, cursor=0, match=None, count=None): hscan_iter(self, name, match=None, count=None): 
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/25
@Author: Zhang Yafei
"""
import redis

pool = redis.ConnectionPool(host='192.168.137.191', port=6379, password='0000', max_connections=1000)
conn = redis.Redis(connection_pool=pool)

# 字典
"""
redis = {
    k4:{
    'username': 'zhangyafei',
    'age': 23,
    }
}
"""
# 1. 建立字典
# conn.hset('k4','username','zhangyafei')
# conn.hset('k4','age',23)
# conn.hsetnx('k4','username','root')  # 若key不存在則將value賦值給key, 若是賦值成功則返回1,不然返回0
# conn.hsetnx('k4', 'hobby', 'basketball')
# conn.hmset('k4',{'username':'zhangyafei','age':23})

# 2. 獲取字典的值
# 獲取一個值
val = conn.hget('k4', 'username')   # b'zhangyafei'
# print(val)
# 獲取多個值
# vals = conn.mget('k4', ['username','age'])
# vals = conn.mget('k4', 'username','age')    # {b'username': b'zhangyafei', b'age': b'23'}
# 獲取全部值
vals = conn.hgetall('k4')   # {b'username': b'zhangyafei', b'age': b'23'}
print(vals)
# 獲取長度
lens = conn.hlen('k4')      # 2
str_lens = conn.hstrlen('k4', 'username')   # 10
keys = conn.hkeys('k4')     # [b'username', b'age']
values = conn.hvals('k4')   # [b'zhangyafei', b'23']
judge = conn.hexists('k4', 'username')  # True
# conn.hdel('k4', 'age', 'username')
# print(conn.hkeys('k4')) # []

# 計算器
# print(conn.hget('k4', 'age'))
# conn.hincrby('k4','age',amount=2)
# conn.hincrbyfloat('k4','age',amount=-1.5)
# print(conn.hget('k4', 'age'))


# 問題:若是redis的k4對應的字典中有1000w條數據,請打印全部數據
# 不可取:redis取到數據以後,服務器內存沒法承受,爆棧
# result = conn.hgetall('k4')
# print(result)

for item in conn.hscan_iter('k4'):
    print(item)
字典操做示例

    b. 使用列表

def blpop(self, keys, timeout=0):
    """
    LPOP a value off of the first non-empty list
    named in the ``keys`` list.

    If none of the lists in ``keys`` has a value to LPOP, then block
    for ``timeout`` seconds, or until a value gets pushed on to one
    of the lists.

    If timeout is 0, then block indefinitely.
    """
    if timeout is None:
        timeout = 0
    if isinstance(keys, basestring):
        keys = [keys]
    else:
        keys = list(keys)
    keys.append(timeout)
    return self.execute_command('BLPOP', *keys)

def brpop(self, keys, timeout=0):
    """
    RPOP a value off of the first non-empty list
    named in the ``keys`` list.

    If none of the lists in ``keys`` has a value to RPOP, then block
    for ``timeout`` seconds, or until a value gets pushed on to one
    of the lists.

    If timeout is 0, then block indefinitely.
    """
    if timeout is None:
        timeout = 0
    if isinstance(keys, basestring):
        keys = [keys]
    else:
        keys = list(keys)
    keys.append(timeout)
    return self.execute_command('BRPOP', *keys)

def brpoplpush(self, src, dst, timeout=0):
    """
    Pop a value off the tail of ``src``, push it on the head of ``dst``
    and then return it.

    This command blocks until a value is in ``src`` or until ``timeout``
    seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
    forever.
    """
    if timeout is None:
        timeout = 0
    return self.execute_command('BRPOPLPUSH', src, dst, timeout)

def lindex(self, name, index):
    """
    Return the item from list ``name`` at position ``index``

    Negative indexes are supported and will return an item at the
    end of the list
    """
    return self.execute_command('LINDEX', name, index)

def linsert(self, name, where, refvalue, value):
    """
    Insert ``value`` in list ``name`` either immediately before or after
    [``where``] ``refvalue``

    Returns the new length of the list on success or -1 if ``refvalue``
    is not in the list.
    """
    return self.execute_command('LINSERT', name, where, refvalue, value)

def llen(self, name):
    "Return the length of the list ``name``"
    return self.execute_command('LLEN', name)

def lpop(self, name):
    "Remove and return the first item of the list ``name``"
    return self.execute_command('LPOP', name)

def lpush(self, name, *values):
    "Push ``values`` onto the head of the list ``name``"
    return self.execute_command('LPUSH', name, *values)

def lpushx(self, name, value):
    "Push ``value`` onto the head of the list ``name`` if ``name`` exists"
    return self.execute_command('LPUSHX', name, value)

def lrange(self, name, start, end):
    """
    Return a slice of the list ``name`` between
    position ``start`` and ``end``

    ``start`` and ``end`` can be negative numbers just like
    Python slicing notation
    """
    return self.execute_command('LRANGE', name, start, end)

def lrem(self, name, count, value):
    """
    Remove the first ``count`` occurrences of elements equal to ``value``
    from the list stored at ``name``.

    The count argument influences the operation in the following ways:
        count > 0: Remove elements equal to value moving from head to tail.
        count < 0: Remove elements equal to value moving from tail to head.
        count = 0: Remove all elements equal to value.
    """
    return self.execute_command('LREM', name, count, value)

def lset(self, name, index, value):
    "Set ``position`` of list ``name`` to ``value``"
    return self.execute_command('LSET', name, index, value)

def ltrim(self, name, start, end):
    """
    Trim the list ``name``, removing all values not within the slice
    between ``start`` and ``end``

    ``start`` and ``end`` can be negative numbers just like
    Python slicing notation
    """
    return self.execute_command('LTRIM', name, start, end)

def rpop(self, name):
    "Remove and return the last item of the list ``name``"
    return self.execute_command('RPOP', name)

def rpoplpush(self, src, dst):
    """
    RPOP a value off of the ``src`` list and atomically LPUSH it
    on to the ``dst`` list.  Returns the value.
    """
    return self.execute_command('RPOPLPUSH', src, dst)

def rpush(self, name, *values):
    "Push ``values`` onto the tail of the list ``name``"
    return self.execute_command('RPUSH', name, *values)

def rpushx(self, name, value):
    "Push ``value`` onto the tail of the list ``name`` if ``name`` exists"
    return self.execute_command('RPUSHX', name, value)

def sort(self, name, start=None, num=None, by=None, get=None,
         desc=False, alpha=False, store=None, groups=False):
    """
    Sort and return the list, set or sorted set at ``name``.

    ``start`` and ``num`` allow for paging through the sorted data

    ``by`` allows using an external key to weight and sort the items.
        Use an "*" to indicate where in the key the item value is located

    ``get`` allows for returning items from external keys rather than the
        sorted data itself.  Use an "*" to indicate where int he key
        the item value is located

    ``desc`` allows for reversing the sort

    ``alpha`` allows for sorting lexicographically rather than numerically

    ``store`` allows for storing the result of the sort into
        the key ``store``

    ``groups`` if set to True and if ``get`` contains at least two
        elements, sort will return a list of tuples, each containing the
        values fetched from the arguments to ``get``.

    """
    if (start is not None and num is None) or \
            (num is not None and start is None):
        raise RedisError("``start`` and ``num`` must both be specified")

    pieces = [name]
    if by is not None:
        pieces.append(Token.get_token('BY'))
        pieces.append(by)
    if start is not None and num is not None:
        pieces.append(Token.get_token('LIMIT'))
        pieces.append(start)
        pieces.append(num)
    if get is not None:
        # If get is a string assume we want to get a single value.
        # Otherwise assume it's an interable and we want to get multiple
        # values. We can't just iterate blindly because strings are
        # iterable.
        if isinstance(get, basestring):
            pieces.append(Token.get_token('GET'))
            pieces.append(get)
        else:
            for g in get:
                pieces.append(Token.get_token('GET'))
                pieces.append(g)
    if desc:
        pieces.append(Token.get_token('DESC'))
    if alpha:
        pieces.append(Token.get_token('ALPHA'))
    if store is not None:
        pieces.append(Token.get_token('STORE'))
        pieces.append(store)

    if groups:
        if not get or isinstance(get, basestring) or len(get) < 2:
            raise DataError('when using "groups" the "get" argument '
                            'must be specified and contain at least '
                            'two keys')

    options = {'groups': len(get) if groups else None}
    return self.execute_command('SORT', *pieces, **options)
基本操做
# 左插入
conn.lpush('k1', 11)
conn.lpush('k1', 22)
# 右插入
conn.rpush('k1', 33)

# 左獲取
val = conn.lpop('k1')
val = conn.blpop('k1', timeout=10) # 夯住
# 右獲取
val = conn.rpop('k1')
val = conn.brpop('k1', timeout=10) # 夯住
左右操做
conn.blpop()
conn.brpop()
阻塞
def list_iter(key, count=3):
    index = 0
    while True:
        data_list = conn.lrange(key, index, index+count-1)
        if not data_list:
            return
        index += count

        for item in data_list:
            yield item


result = conn.lrange('k1', 0, 100)
print(result)  # [b'22', b'11', b'33']

for item in list_iter('k1', 3):
    print(item)
經過yield建立一個生成器完成一點一點獲取(經過字典操做的源碼來的靈感)

 c. 使用字符串

添加
def set(self, name, value, ex=None, px=None, nx=False, xx=False):
def append(self, key, value):
def mset(self, *args, **kwargs):
def msetnx(self, *args, **kwargs):
def setex(self, name, value, time):
def setnx(self, name, value):

刪除
def delete(self, *names):

修改
def setrange(self, name, offset, value):
def decr(self, name, amount=1):
def incr(self, name, amount=1):
def incrbyfloat(self, name, amount=1.0):
def expire(self, name, time):

查詢
def mget(self, keys, *args):
def exists(self, name):
def get(self, name):
def getrange(self, key, start, end):
def getset(self, name, value):
def keys(self, pattern='*'):
def strlen(self, name):
基本操做 
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/2/1
@Author: Zhang Yafei
"""
import redis

pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='0000', max_connections=1000)
conn = redis.Redis(connection_pool=pool)

# 添加
conn.set('str_k', 'hello')   # 爲指定key設置value
# {'str_k':'hello'}
conn.mset({'str_k':'hello','str_k1':'world'})   # 設置多個key/value
# {'str_k':'hello', 'str_k1':'world'}
conn.msetnx({'str_k':'msetnx_hello'})     # 若當前key未設定, 則基於mapping設置key/value,結果返回True或False
# {'str_k':'hello'}
conn.setex('str_k2', 'str_v2', 2)  #

conn.decr('num', amount=1)
conn.incr('num', amount=1)
conn.incrbyfloat('num', amount='1.5')

# 刪除
conn.delete('str_k1')


# 修改
conn.append('str_k', ' world')    # 爲指定key添加value
# {'str_k':'hello world'}
conn.setrange('str_k', 5, 'world')    # 在key對應的的value指定位置上設置值
# b'helloworld'


# 查詢
print(conn.get('str_k'))
print(conn.get('num'))
print(conn.getrange('str_k', 0, 100))
print(conn.keys())
print(conn.strlen('str_k'))     # 長度
print(conn.exists('str_k'))
conn.expire('str_k1', 5)
print(conn.get('str_k1'))

# 添加並查詢
print(conn.getset('str_k2', 'str_v2'))
# b'str_v2'
字符串示例

   d. 集合

添加
def sadd(self, name, *values):
刪除
def spop(self, name):
def srem(self, name, *values):
修改
def smove(self, src, dst, value):
查詢
# 判斷value是否在key的value中
def sismember(self, name, value):
# 取出key爲name的全部元素
def smembers(self, name):
# 隨機取出key爲name的指定個數元素
def srandmember(self, name, number=None):
# 元素個數
def scard(self, name):
# 差集
def sdiff(self, keys, *args):
def sdiffstore(self, dest, keys, *args):
# 交集
def sinter(self, keys, *args):
def sinterstore(self, dest, keys, *args):
# 並集
def sunion(self, keys, *args):
def sunionstore(self, dest, keys, *args):
集合基本操做
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/2/1
@Author: Zhang Yafei
"""
import redis

pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='0000', max_connections=1000)
conn = redis.Redis(connection_pool=pool)

"""
{
    'set_k':{v1,v2,v3},
}
"""
# 添加
# conn.sadd('set_k', 3, 4, 5, 6)
# conn.sadd('set_k1', 3, 4, 5, 6)

# 刪除
# print(conn.spop('set_k'))
# conn.srem('set_k', 2)

# 修改
# conn.smove('set_k', 'set_k1', 1)

# 查詢
print(conn.smembers('set_k'))
print(conn.smembers('set_k1'))
# print(conn.srandmember('set_k', 3))
# print(conn.scard('set_k'))
# print(conn.sismember('set_k', 2))

print(conn.sdiff('set_k','set_k1'))  # 集合之差
conn.sdiffstore('set_k_k1', 'set_k', 'set_k1')
print(conn.smembers('set_k_k1'))

print(conn.sinter('set_k', 'set_k1'))   # 集合交集
conn.sinterstore('set_k_k1_inter', 'set_k', 'set_k1')
print(conn.smembers('set_k_k1_inter'))

print(conn.sunion('set_k', 'set_k1'))   # 集合並集
conn.sunionstore('set_k_k1_union', 'set_k', 'set_k1')
print(conn.smembers('set_k_k1_union'))
集合操做示例

e. 有序集合

添加
def zadd(self, name, *args, **kwargs):
刪除
def zrem(self, name, *values):
def zremrangebyrank(self, name, min, max):     # 刪除等級最大者
def zremrangebyscore(self, name, min, max):    # 刪除分數最小者
查詢
# 查詢start-end個數,按分數從小到大
def zrange(self, name, start, end, desc=False, withscores=False,
           score_cast_func=float):
# 查詢分數在Min,max之間的元素
def zrangebyscore(self, name, min, max, start=None, num=None,
                  withscores=False, score_cast_func=float):
def zrank(self, name, value):  # 等級
def zcard(self, name):        # 個數
def zscore(self, name, value):   # 得分
def zrevrange(self, name, start, end, withscores=False,
              score_cast_func=float):   # 逆序:分數從大到小排序
def zrevrangebyscore(self, name, max, min, start=None, num=None,
                     withscores=False, score_cast_func=float):    # 分數處於Min,max之間的從大到小排序
基本操做
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/2/1
@Author: Zhang Yafei
"""
import redis

pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='0000', max_connections=1000)
conn = redis.Redis(connection_pool=pool)

"""
{
    'set_k':{
        {v1: score1},
        {v2: score2},
        {v3: score3},
    },
}
"""
# # 添加
# conn.zadd('zset_k', 'math', 99, 'english', 80, 'chinese', 85, 'sport', 100, 'music', 60)
#
# # 刪除
# conn.zrem('zset_k', 'music')
# conn.zremrangebyrank('zset_k', 0, 0)  # 按等級大小刪除, 刪除等級在第min-max個值
# conn.zremrangebyscore('zset_k', 0, 90)   # 按分數範圍刪除, Min < x < max之間的刪除

# 查詢
print(conn.zrange('zset_k', 0, 100))
print(conn.zrevrange('zset_k', 0, 100))
# score從小到大排序, 默認小值先出, 廣度優先
results = conn.zrangebyscore('zset_k', 0, 100)
print(results)
print(conn.zcard('zset_k'))
print(conn.zcount('zset_k', 0, 90))
print(conn.zrank('zset_k', 'chinese'))
print(conn.zscore('zset_k', 'chinese'))
print(conn.zrange('zset_k', 0, 100))
有序集合示例

6、基於redis實現隊列和棧

# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class FifoQueue(object):
    def __init__(self):
        """
        先進先出隊列:利用redis中的列表,雙端隊列改成先進先出隊列
        """
        self.server = redis.Redis(host='127.0.0.1', port=6379)

    def push(self, request):
        """Push a request"""
        self.server.lpush('USERS', request)

    def pop(self, timeout=0):
        """Pop a request"""
        data = self.server.rpop('USERS')
        return data


if __name__ == '__main__':
    q = FifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)

    print(q.pop())
    print(q.pop())
    print(q.pop())
先進先出隊列
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class LifoQueue(object):
    """Per-spider LIFO queue."""
    def __init__(self):
        self.server = redis.Redis(host='127.0.0.1', port=6379)

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

    def pop(self, timeout=0):
        """Pop a request"""
        data = self.server.lpop('USERS')
        return data


if __name__ == '__main__':
    q = LifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)

    print(q.pop())
    print(q.pop())
    print(q.pop())
後進先出隊列(棧)
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class PriorityQueue(object):
    """Per-spider priority queue abstraction using redis' sorted set"""
    def __init__(self):
        self.server = redis.Redis(host='127.0.0.1', port=6379)

    def push(self, request,score):
        """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', 'xxxxxx', score, request)

    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('xxxxxx', 0, 0).zremrangebyrank('xxxxxx', 0, 0)
        results, count = pipe.execute()
        if results:
            return results[0]


if __name__ == '__main__':
    q = PriorityQueue()

    # q.push('alex',99)    # 廣度優先:分值小的優先
    # q.push('oldboy',56)
    # q.push('eric',77)

    q.push('alex',-99)       # 深度優先:分值大的優先
    q.push('oldboy',-56)
    q.push('eric',-77)

    v1 = q.pop()
    print(v1)
    v2 = q.pop()
    print(v2)
    v3 = q.pop()
    print(v3)
優先級隊列
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
from scrapy_redis import queue
import redis

conn = redis.Redis(host='127.0.0.1', port=6379)
conn.zadd('score',alex=79, oldboy=33,eric=73)

# print(conn.keys())

v = conn.zrange('score',0,8,desc=True)
print(v)

pipe = conn.pipeline()
pipe.multi()
pipe.zrange("score", 0, 0).zremrangebyrank('score', 0, 0)
results, count = pipe.execute()
print(results,count)
redis中的pipeline

7、Django應用

1.自定義使用redis

import redis
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000)
utils.py
from django.shortcuts import render, HttpResponse
from app01.utils.redis_pool import POOL
from redis import Redis

def index(request):
    conn = Redis(connection_pool=POOL)
    conn.hset('kkk', 'age', 18)
    return HttpResponse('設置成功')

def order(request):
    conn = Redis(connection_pool=POOL)
    val = conn.hget('kkk','age')
    return HttpResponse('獲取成功{}'.format(val))
views.py

2.使用第三方組件

pip install django-redis
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": "密碼",
        }
    },
    "back": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.137.191:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100},
            "PASSWORD": "0000",
        }
    },
}
配置
from django.shortcuts import render,HttpResponse
from django_redis import get_redis_connection

def index(request):
    conn = get_redis_connection('back')
    conn.hset('kkk', 'age', 18)
    return HttpResponse('設置成功')

def order(request):
    conn = get_redis_connection('back')
    val = conn.hget('kkk','age')
    return HttpResponse('獲取成功{}'.format(val))
使用

3.高級使用  

  a. 全站緩存
   使用中間件,通過一系列的認證等操做,若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶,當返回給用戶以前,判斷緩存中是否已經存在,若是不存在則UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存

MIDDLEWARE = [
	'django.middleware.cache.UpdateCacheMiddleware',
	# 其餘中間件...
	'django.middleware.cache.FetchFromCacheMiddleware',
]

CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""

  b.單視圖

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def index(request):
	# conn = Redis(connection_pool=POOL)
	conn = get_redis_connection('back')
	conn.hset('kkk', 'age', 18)
	return HttpResponse('設置成功')

 c,局部頁面緩存

<body>
	<h1>asdfasdfasdf</h1>
	<div>
		asdf
	</div>
	{#    將指定局部頁面放到緩存中的key中#}
	{% cache 5000 key %}
		<div></div>
	{% endcache %}
</body>  
# 緩存放在redis配置
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": "密碼",
        }
    },
}
# 緩存放在文件
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
    }
}
# 緩存放在MemcachedCache
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}
補充:rest framework框架訪問頻率限制推薦放到 redis/memecached

更多詳細內容請見

  官方教程:http://www.redis.net.cn/tutorial/3501.html

相關文章
相關標籤/搜索