#############################################
# redis简介: # redis是一个软件,对内存进行操作 # mysql是一个软件,对硬盘进行操作, # 要使用redis,首先需要安装, # redis是c语言编写的,数据存在内存,也可以持久化,存入文件, # 提供多种语言的api,是一种nosql数据库,
#############################################
使用Redis有哪些好处? (1) 速度快,因为数据存在内存中, (2) 支持丰富数据类型,支持string,list,set,sorted set,hash (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
#############################################
Redis安装和基本使用 1,下载:wget http://download.redis.io/releases/redis-3.0.6.tar.gz 2,解压:tar xzf redis-3.0.6.tar.gz 3,cd redis-3.0.6 4,make # 因为redis是c语言编写的所以需要进行编译 启动服务端 src/redis-server 启动客户端 src/redis-cli redis> set foo bar OK redis> get foo "bar"
查看进程:
ps aux | grep redis,这个命令,没有新的内容就没有启动,
关闭进程
sudo kill -9 进程号,这样就关闭进程了,
############################################
API使用 redis-py 的API的使用可以分类为: 1,连接方式 2,连接池 3,操作 String 操作 Hash 操作 List 操作 Set 操作 Sort Set 操作 4,管道 5,发布订阅
############################################
# Python操作Redis # 我们自己的电脑需要安装一个模块, # pip install redis import redis conn=redis.Redis(host='123.57.142.175',port=6379) conn.set('K12','123456') print(conn.get('K12'))
#############################################
# python-redis连接池 # 创建连接,更加推荐使用连接池, import redis # 创建连接池, # max_connections这是最大连接数,刚启动是一个连接都没有, pool = redis.ConnectionPool(host='123.57.142.175', port=6379,max_connections=1000) # 去连接池中获取连接, conn = redis.Redis(connection_pool=pool) # 设置值 conn.set('foo', 'bar') # 使用连接池注意: # 1,初始创建连接池,连接池内是没有连接的, # 2,创建第一个连接,在数据存储完成之后,这个连接并没有断,在内部有一个列表存储, # 3,这个时候连接池有一个连接,如果再来第二个连接,就不用再连接了,就可以拿上一个连接直接用就行了, # 4,如果第一个连接正在做操作,但是同时又来了第二个连接,这个时候就需要再次创建第二个连接,所以就是说两个连接够用了所有的操作,就不用去新建连接了, # 5,如果并发特别大的时候,还是需要新建连接的,可以设置最大1000个同时操作, # 6,连接池只需要创建一次, # 7,连接池本质就是维护一个已经和服务端连接成功的socket, # 8,这种连接池的思想,可以扩展,一般连接都是使用连接池, # 连接的好处: # 连接池的好处就是不需要每次都连接了,提高了效率, # 连接池使用 # 使用的时候要使用单例模式,# 因为这个连接池理论上只需要创建一次就够了,剩下的就是取连接池拿连接, # 但是连接池在一个py文件,这个py文件运行的时候,就建立连接池了,到其他的py文件,再引入的时候,可能会重复执行 # 最好是做一个单例模式,最简单的单例模式就是创建一个py文件,然后哪里需要去获取连接池,就把这个连接池import进来就可以了, # 以后操作的时候都需要使用连接池,并且要放入一个文件,做成一个单利模式,否则效率就降低了, # 新建一个文件:redis_pool.py, import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000)
############################################
# redis三大特点
# 1,持久化
# 2,redis是单进程,单线程的
# 3,5大数据类型,
redis = {
"k1":"123", # 字符串
"k2": [1,2,3,4], # 列表,也叫数组,
"k3": {1,2,3,4}, # 集合
"k4": {"name":"andy","age":"11"}, # 字典,也叫哈希
"k5": {("andy",79),("xiaoming",90),("xiaofang",70)}, # 有序集合
}
#############################################
# redis字符串操作 # 一个key对应一个值,这个值是string类型, # string类型可以接受任何类型的数据,可以是json,可以是图像,容纳可达512M import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # conn.delete('k2') # conn.flushall() # 清空所有的key # print(conn.keys()) # 总结:set get mset mget append ################################### # 1,设置键值:set(name, value, ex=None, px=None, nx=False, xx=False) ex,过期时间(秒) # conn.set('name','andy') # 这样一个键值就设置好了,set 键 值 # 这个键存在就是修改,不存在就是创建, # conn.set('name','andy_lee') # 可以设置过期时间, # conn.set('id',11,ex=3) # print(conn.get('id')) # 2,这是获取一个值, get(name) # print(conn.get('name')) # 3,一次性设置多个键值,这种是mset key1 value1 key2 value2 的格式, # conn.mset({"str1":"111","str2":"222"}) # print(conn.keys()) # 4,批量获取 mget(keys, *args) # conn.mget('ylr', 'wupeiqi') # 或 # conn..mget(['ylr', 'wupeiqi']) # 5,往一个键的值里面追加内容,这就是往a1的后面追加了123 conn.append('name', '123') # b'andy_lee123' print(conn.get('name'))
############################################
# redis-字典操作
# redis有多少个哈希槽:16384 import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # 字典操作: """ k1 = { "name":"liqian", "age":12 } """ # 1,创建值,或者修改值,hset(name, key, value) # conn.hset('k1','name','liqian') # conn.hset('k1','age',12) # 2,批量设置键值对,把上面的两步合并成为一步,hmset(name, mapping) # conn.hmset('hh32', {'name':'andy','age':123}) # 3,获取值,hget(name,key) # val = conn.hget('k1','name') # 获取某一个key, # 4,批量获取值,hmget(name, keys, *args) # val = conn.hmget('hh32',['name','age']) # 返回一个列表:[b'andy', b'123'] # 5,获取全部值, # val = conn.hgetall('k1') # 获取全部key,value # val = conn.hgetall('hh32') # 返回一个字典,{b'name': b'andy', b'age': b'123'} # print(val) # 6,获取name对应的hash中,键值对的个数, # val = conn.hlen('hh32') # 7,获取name对应的hash中,所有的keys # val = conn.hkeys('hh32') # 8,获取name对应的hash中,所有的value # val = conn.hvals('hh32') # print(val) # 9,判断是否存在传入的键 # conn.hexists(name, key) # 10,删除:hdel(name,*keys) # conn.hdel('hash-key','k1','k3') # 11,# 自增name对应的hash中的指定key的值,不存在则创建key=amount,hincrby(name, key, amount=1) # 比如计数器,对文章的阅读量进行统计, # 这种就可以使用redis,然后比如在每天的凌晨统一更新进入数据库, # 而且redis是单进程,单线程,所有的请求过来要排队,所以也不用担心并发的问题了, # print(conn.hget('hh32','age')) # conn.hincrby('hh32','age') # 每次加1 # conn.hincrby('hh32','age',amount=2) # 每次加2 # conn.hincrby('hh32','age',amount=-1) # 每次减1 # print(conn.hget('hh32','age')) # 面试题:如果redis的k4对应的字典中有一千万数据,请打印所有数据 # result = conn.hgetall('hh32') # print(result) # 这种操作一次可能就爆栈了,这种不可取,redis取到数据之后,服务器内存服务承受,爆栈, # conn.hscan_iter('hh32',count=100) # 利用yield封装hscan创建生成器,实现分批去redis中获取数据 # 这种就是100条,100条的取, # 所以切记,不知道有多少条,一定要使用这个方法取, # 使用字典,注意事项: # 1,基本操作, # 2,慎重使用conn.hgetall(),优先使用conn.hscan_iter() # 3,可以用做计数器, """ k1 = { "id":1 "title":"XXX", "price_list":[ # 这个列表是不行的,需要把列表转换成为字符串,然后存储, {"id":1,"title":"XXX"}, {"id":1,"title":"XXX"} ] } """ # 类似上面这种结构往redis里面放,但是redis不支持多层嵌套字典的,也就是说第一层是字典,里面的都是字符串,怎么放? # 所以需要json.dumps一下,转换成字符串,然后取回来的时候json.loads一下, # redis操作的时候,
#############################################
# redis列表操作: """ redis = { k1:[1,2,3,4] } """ import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # 1,添加元素,每个新的元素都添加到列表的最左边,lpush(name,values) conn.lpush('list1', 11) # lpush是从左边往列表追加 conn.lpush('list1', 22) # 2,添加元素,每个新的元素都添加到列表的最右边,rpush(name, values) conn.rpush('list1', 33) # rpush是从右边往列表追加 # 3,name对应的list元素的个数 llen(name) # conn.llen(name) # 4,# 在name对应的列表的某一个值前或后插入一个新值 linsert(name, where, refvalue, value)) # 参数: # name,redis的name # where,BEFORE或AFTER # refvalue,标杆值,即:在它前后插入数据 # value,要插入的数据 # 5,# 对name对应的list中的某一个索引位置重新赋值 r.lset(name, index, value) # 参数: # name,redis的name # index,list的索引位置 # value,要设置的值 # 6,# 在name对应的list中删除指定的值,r.lrem(name, value, num) # 参数: # name,redis的name # value,要删除的值 # num, num=0,删除列表中所有的指定值; # num=2,从前到后,删除2个; # num=-2,从后向前,删除2个 conn.lrem('list1', 11) # 这个是删除,和pop不一样,pop是删除还会返回给你,remove直接删除了,不返回, # 7,# 从左边拿走一个,列表里面就会减少一个,lpop(name) conn.lpop('list1') # 8,# 从右边拿走一个,列表里面就会减少一个, conn.rpop('list1') # 9,根据索引获取列表元素,lindex(name, index) # 10,# 在name对应的列表分片获取数据,lrange(name, start, end) # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置 result = conn.lrange('list1', 0, 100) print(result) # 11,# 在name对应的列表中移除没有在start-end索引之间的值,ltrim(name, start, end) # 参数: # name,redis的name # start,索引的起始位置 # end,索引结束位置 # 队列:就是先进先出,好像排队买东西, # 栈:就是后进先出,好像进电梯, # redis的列表,如果是从左边进,从右边取,就是一个队列,从左边进,从左边取,就是栈, # 12,将多个列表排列,按照从左到右去pop对应列表的元素,blpop(keys, timeout) val = conn.blpop('list1', timeout=10) # 使用这个方法会等待,一直等到有数据,使用timeout最多等待10秒, # 从右向左获取数据 val = conn.brpop('list1', timeout=10) # 从右边取, # 很多地方都会用,# 比如爬虫,比如爬100个url网站,一个一个的取, # rpoplpush(列表1,列表2,)# 这是从列表1的右边取出,推到列表2的左边, # 基本的操作就不多说了, # 如果一个列表,有一百万数据,打印出来,怎么办? # 这个列表没有scan_iter 这个方法, # 我们定义一个: def list_iter(key, count=100): index = 0 while True: data_list = conn.lrange('list1', index, index + count - 1) if not data_list: return index += count for item in data_list: yield item for item in list_iter('list', count=3): print(item) # 这就是取的时候进行遍历, # 内容详细 # 1,列表可以左插入,右插入 # 2,可以阻塞 # 3,通过yield创建一个生成器可以完成一点点的获取, # python基础里面,装饰器,迭代器,生成器都比较绕,这就是生成器的应用,
############################################
# redis集合操作 set类型: # 注意: # 1,无序集合 # 2,每一个元素都是一个string类型, # 3,元素具有唯一性,不能重复, # 4,对于集合没有修改操作 import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # 1,新增,sadd(name,values) # conn.sadd('set1',123) # 一次增加多个值 # conn.sadd('set2',12,23,34,45) # 2,获取,获取name对应的集合的所有成员 # print(conn.smembers('set2')) # 可以把a3里面所有的元素都查出来,这是无序的 # 3,删除, # 删除集合里面的某一个元素 # conn.srem('set2',12) # print(conn.smembers('set2')) # 4,从集合的右侧(尾部)移除一个成员,并将其返回,spop(name) # 5,sscan_iter(name, match=None, count=None) # 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
#############################################
# redis - 有序集合操作,# zset类型: # 1,有序集合, # 2,元素也是string类型, # 3,元素具有唯一性,不重复, # 4,每一个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序 # 5,没有修改操作, import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # 1,增加:zadd(name, *args, **kwargs) # conn.zadd("1",{'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7}) # print(conn.keys()) # print(conn.type('1')) # 2,照索引范围获取name对应的有序集合的元素, # r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) # 参数: # name,redis的name # start,有序集合索引起始位置(非分数) # end,有序集合索引结束位置(非分数) # desc,排序规则,默认按照分数从小到大排序 # withscores,是否获取元素的分数,默认只获取元素的值 # score_cast_func,对分数进行数据转换的函数 # print(conn.zrange("1",0,-1)) # 输出的结果是 ['1', '2', '3'],这个获取就是按照权值从小到大的排序的, # 3,我想要根据权值获取部分元素,如何操作? 比如我想要看权值在5 - 6的元素, # print(conn.zrangebyscore('1',5,6)) # 最后两个是min,max,而且都包含, # 4,查看某一个成员的权重值怎么操作? zscore(name, value) # print(conn.zscore('1','6')) # 5,删除指定元素, # conn.zrem('1','1') # print(conn.zrange("1",0,-1)) # 6,删除权值在指定范围的元素, conn.zremrangebyscore('1', 5, 6)
############################################
# 键命令: # 键命令,这个不只是针对字符串的,也针对hash,set,list,zset这些 import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # 1,获取所有的键, # print(conn.keys()) # 2,这是包含a的键, # print(conn.keys('s*')) # 3,这是判断a1是否存在,存在是返回1,不存在是返回0, # print(conn.exists('name')) # 4,这是判断name这个键的值,是什么类型, # print(conn.type('name')) # 5,根据删除redis中的任意数据类型,delete(*names) # conn.delete('k1','name') # 如果你没有设置过期时间,则一直存在的,直到使用del删除, # 6,这是给一个键设置过期时间,expire(name ,time) # conn.expire('id',3)
#############################################
# redis - 管道 # redis里面类似事务的操作 # 比如我们要在列表,集合,字典三个值里面操作, import redis pool = redis.ConnectionPool(host='192.168.100.128', port=6379, password='ji10201749', max_connections=1000) conn = redis.Redis(connection_pool=pool) # conn.set('set1',11) # conn.hset('hset2',"n1",666) # conn.lpush('list2','n2') # 这三个如果这样执行,就是发了三次, # 类似事务的操作: pipe = conn.pipeline(transaction=True) pipe.multi() pipe.set('set1', 11) pipe.hset('hset2', "n1", 666) pipe.lpush('list2', 'n2') pipe.execute() # 一次发送多个命令,
############################################
MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据 相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略: 1,voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 2,volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 3,volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 4,allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 5,allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 6,no-enviction(驱逐):禁止驱逐数据
############################################
# 发布者和订阅者, # 后续再说
#############################################
# 主从配置 # 集群 # 后续再说
############################################
#############################################