一 Redis简介
redis可以支持五大(字符串string、链表list、字典hash、集合set、有序集合zset)数据类型,比起Memcached来说,它只能支持一种(字符串)数据类型。redis虽然是单线程,但是效率极高,并且可以将数据持久化;相反,Memcached只能将数据暂存到内存中,断电数据就会丢失!
二 Redis使用
2.1 Redis安装
下载Redis-x64-3.2.100.msi,在pycharm中安装redis模块
- 使用命令行输入命令:pip3 install redis
- 也可用pycharm
- settings,选择project下的Project Interpreter进行安装
- Terminal界面安装,输入命令:pip3 install redis
2.2 Redis连接
import redis conn = redis.Redis('127.0.0.1', 6379) conn.set('f1', 'hello') print(conn.get('f1')) # b'hello'
注意点:redis默认的是bytes的数据类型,可以加参数设置为字符串类型,decode_responses=True
import redis conn = redis.Redis('127.0.0.1', 6379, decode_responses=True) conn.set('f1', 'hello') print(conn.get('f1')) # hello
2.3 使用密码认证登陆
1. 修改配置文件
安装目录下的 C:Program Files (x86)Redisredis.windows-service.conf 文件,找到文件中的这一行:# requirepass foobared,去掉注释,将后面的foobared改为自己需要的密码即可!如:
2. 重启Redis
方法有很多,自己都可以尝试
3. 使用密码登陆
如果不使用密码登陆则无法操作Redis,会报一个NOAUTH的错,如:
所以登陆时要制定密码,-a 制定自己设置好的密码,如:redis-cli -h 127.0.0.1 -p 6379 -a test
2.4 Redis连接池
redis使用ConnectionPool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认每个Redis实例都会维护一个自己的连接池。为避免重复开启连接池,可以用单例的思想实现多个Redis实例共享一个连接池
import redis # 实例化 POOL = redis.ConnectionPool() conn = redis.Redis(host='127.0.0.1', port=6379, connection_pool=POOL) conn.set('m', 'name') print(conn.get('m'))
方法一:利用python的便利之处,导入模块来实现连接池的单例化
import redis POOL = redis.ConnectionPool(host='127.0.0.1', port=6379)
import redis import redis_test conn = redis.Redis(connection_pool=redis_test.POOL) conn.set('m', 'name') print(conn.get('m'))
方法二:安装django-redis模块,然后配置、导入,实现连接池的单例化
- 在settings.py 文件中进行如下的配置:
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", } } }
- 视图函数
from django_redis import get_redis_connection def setname(request): connect = get_redis_connection() connect.set('name', 'bob') return HttpResponse('success') def getname(request): connect = get_redis_connection() name = connect.get('name') return HttpResponse(name)
三 Redis数据类型之string
redis中的String在内存中按照一个name对应一个value的形式来存储,如下
import redis import redis_test conn = redis.Redis(connection_pool=redis_test.POOL) # set 设置值 conn.set('name', 'value', ex=None, px=None, nx=False, xx=False) ''' ex:过期时间(s) px:过期时间(ms) nx:设置为True,只有name不存在时,set操作才执行,值存在,不修改原值 xx:设置为True,只有name存在时,set操作才执行,值不存在,不设置新值 ''' # get 取值 conn.get('name') # mset 批量设置值,键值对的关系 conn.mset({'key1': 'value1', 'key2': 'value2'}) # mget 批量取值 conn.mget('key1', 'key2') conn.mget(['key1', 'key2']) conn.mget(('key1', 'key2')) # strlen 返回name对应值的字节长度(一个汉字3个字节) conn.strlen(name='key2') # append 在key对应的值后面追加内容 conn.append(key='key1', value='value') # incr 自增name对应的值,当name不存在时,则创建name=amount,否则,则自增 conn.incr(name='key1', amount=1) ''' name: Redis的name amount: 自增数(必须是整数) ''' # decr 自减name对应的值,当name不存在时,则创建name=amount,否则,则自减 conn.decr(name='key1', amount=1) ''' name: Redis的name amount: 自减数(必须是整数) ''' # setnx 设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改 conn.setnx('name', 'jack') # setex 设置值,time,过期时间(数字秒 或 timedelta对象) conn.setex(name='name', time=5, value='value') # psetex 设置值,time_ms,过期时间(数字毫秒 或 timedelta对象) conn.psetex(name='name', time_ms=3, value='value') # getset 设置新值并获取原来的值 conn.getset('name', 'value') # getrange 获取子序列(根据字节获取,非字符) conn.getrange('key1', start=0, end=4) ''' start: 起始位置(字节) end: 结束位置(字节) 如: "jack" ,0-4表示 "jack" ''' # setrange 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加) conn.setrange(name='key', offset=1, value='value') ''' offset: 字符串的索引,字节(一个汉字三个字节) value: 要设置的值 '''
四 Redis数据类型之list
redis中的List在内存中按照name对应一个List的形式来存储,如下
import redis import redis_test # from django_redis import get_redis_connection # conn = get_redis_connection(alias='default') conn = redis.Redis(connection_pool=redis_test.POOL) # lpush 设置值,存入redis的顺序是倒序,取值时按照倒序存入的顺序,如下: conn.lpush('ll', 'bob', 'jack', 'lee') # ll = ['lee', 'jack', 'bob'] print(conn.lindex('ll', 2)) # b'bob' # lpushx 添加值,name存在时,会将新值添加到链表的左边 conn.lpushx(name='ll', value='zero') # ll = ['zero', 'lee', 'jack', 'bob'] # name不存在时,不会新增数据,取值时会取到None conn.lpushx(name='lll', value='zero') print(conn.lindex('lll', 2)) # None # rpush 设置值,正常顺序存取 conn.rpush('age', 12, 15, 23, 34) # ll = [12, 15, 23, 34] print(conn.lindex('age', 3)) # b'34' # lpushx 添加值,name存在时,会新增数据 conn.rpushx(name='age', value=55) # ll = [12, 15, 23, 34, 55] # name不存在时,不会新增数据,取值时会取到None conn.rpushx(name='ages', value=66) print(conn.lindex('ages', 2)) # None # llen 统计链表的长度 print(conn.llen('age')) # linsert 插入值 conn.linsert(name='age', where='after', refvalue=23, value=111) # ll = [12, 15, 23, 111, 34, 55] ''' name: redis的name where: BEFORE或AFTER(小写也可以) refvalue: 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准) value: 要插入的新值 ''' # lset 修改值, name、index存在时,修改原值;name或index不存在时,报错 conn.lset(name='age', index=2, value=333) # lpop 删除数据,name存在,从左边删除一个值并返回已删除的值;name不存在,返回None print(conn.lpop(name='age')) # b'12' # lrem 删除值,返回删除的数据个数, name不存在时,返回0 print(conn.lrem(name='ll', count=2, value='jack')) # 1 ''' name: redis的name count=0,删除列表中所有的指定值; count=n,从前到后删除n个,值不足n个时,有多少删除多少,并返回删除的个数 count=-n,从后向前删除n个,值不足n个时,有多少删除多少,并返回删除的个数 value: 要删除的值 ''' # lrange 分片取值,前闭后闭区间 print(conn.lrange(name='age', start=1, end=3)) # [b'111', b'333', b'55'] print(conn.lrange('age', 0, conn.llen('age'))) # [b'23', b'111', b'333', b'55'] ''' name: redis的name start: 索引的起始位置 end: 索引结束位置 ''' # ltrim 移除值,在name对应的列表中移除没有在start-end索引之间的值 conn.ltrim(name='ll', start=1, end=3) ''' name: redis的name start: 索引的起始位置 end: 索引结束位置(大于列表长度,则代表不移除任何) ''' # rpoplpush 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边 print(conn.rpoplpush(src='ll', dst='age')) ''' src: 要取数据的列表的name dst: 要添加数据的列表的name ''' # blpop 将多个列表排列,按照从左到右的顺序去pop掉对应列表的元素 # brpop 从右向左获取数据 # 如果列表中没有值,会一直hang住,可实现简单的分布式 print(conn.blpop(keys='age', timeout=3)) ''' keys: redis的name的集合 timeout: 超时时间,当所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞 ''' # 爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
- 自定义增量迭代
# 自定义增量迭代 conn.lpush('test', *[1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 43, 45, 56, 68, 89, 99, 65, 94, 23, 54, 57, 88, 68]) def list_iter(name, count=2): index = 0 while True: list_data = conn.lrange(name, index, index + count - 1) if not list_data: return index += count for iter in list_data: yield iter print(conn.lrange('test', 0, 100)) # [b'68', b'88', b'57', b'54', b'23', b'94', b'65', b'99', b'89', b'68', b'56', b'45', b'43', b'9', b'8', b'7', b'6', b'5', b'5', b'4', b'3', b'2', b'1'] for i in list_iter('test'): print('---------------') print(i)
五 Redis数据类型之hash
redis中的Hash在内存中的存储格式,如下
import redis import redis_test conn = redis.Redis(connection_pool=redis_test.POOL) # hset 设置值 # name对应的hash中设置一个键值对(不存在,则创建;否则,修改) conn.hset('mm', 'name', 'bob') # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加) # hget 取值 conn.hget('mm', 'key') # hmset 批量设置值,键值对的关系 conn.hmset('mm', {'name': 'bob', 'age': 18}) # hmget 批量取值 conn.hmget('mm', 'key1', 'key2') conn.hmget('mm', ['key1', 'key2']) conn.hmget('mm', ('key1', 'key2')) # hlen 返回name对应的键值对的个数 conn.hlen(name='mm') # hkeys 获取name对应的hash中所有的key的值 conn.hkeys(name='mm') # hvals 获取name对应的hash中所有的value的值 conn.hvals(name='mm') # hexists 检查name对应的hash是否存在当前传入的key conn.hexists(name='mm', key='name') # hdel 将name对应的hash中指定key的键值对删除 conn.hdel('mm', 'name') # hgetall 获取name对应hash的所有键值,拿到的是字典类型 conn.hgetall(name='mm') # hincrby 自增name对应的hash中的指定key的值,不存在则创建key=amount conn.hincrby(name='mm', key='age', amount=1) ''' name,redis中的name key, hash对应的key ''' # hscan 增量式迭代取值,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,而使内存爆满 conn.hscan(name='mm', cursor=0, match=None,count=None) ''' cursor: 游标(基于游标分批取获取数据) match: 匹配指定key,默认None 表示所有的key count: 每次分片后最少取值的个数,默认None ''' # hscan_iter 利用yield封装hscan创建生成器,实现分批去redis中获取数据 conn.hscan_iter(name='mm', match=None, count=None) ''' match: 匹配指定key,默认None 表示所有的key count: 每次分片最少获取个数,默认None表示采用Redis的默认分片个数 '''