redis是一个kei-value存储系统,它支持存储的value类型包括string(字符串),list(链表),set(集合),zset(sorted set:有序集合)和hash(哈希类型),这些数据类型都支持push/pop,add/remove及取交集并集和差集以及更丰富的操作,而且这些操作都是原子性的,在此基础上,redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步.
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)存储方式:Memcache把数据全部存在内存之中,断点后悔挂掉,数据不能超过内存大小,Redis有部分存在硬盘上,这样能保证数据的持久性(2)数据支持类型:Memcace对数据类型支持相对简单,Redis有复杂的数据类型(3)value大小:redis最大可以到达1GB,而memcache只有1MB6.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的功能,跟传统意义上的持久化有比较大的差别,那么何时使用Memcached,何时使用Redis呢?如果简单地比较热你打算与Memcached的区别,大多数都会得到以下观点:1.Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储2.Redis支持数据的备份,即master-slave模式的数据备份3.Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用(1)会话缓存(Session Cache)最常用的一种使用Redis的情景是绘画缓存(session cache),用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化,随着Redis这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档,甚至Magento也提供Redis插件(2)全页缓存(FPC)除了基本的会话token之外,Redis还提供很简便的FPC平台,回到一致性的问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似于PHP本地FPC(3)队列Redis在内存存储引擎领域的一大优点是提供lisg和set操作,这使得Redis能作为一个很好的消息队列平台来使用.Redis作为队列使用的操作,就类似于本地程序语言(如Python)对list的push/pop操作(4)排行榜、计数器Redis在内存中对数字进行递增或递减的操作实现的非常好,集合(set)和有序集合(sorted set)也使得我们在执行这些操纵的时候变得非常简单,Redis只是正好提供了这两种数据结构,所以,我们要从排序集合中获取到排名最靠前的10个用户-我们称之为"user-scores",我们只需要像下面一样执行即可:ZRANGE user_scores 0 10 WITHSCORESAgora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的
Python操作Redis
1.操作模式
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令。Redis是StrictRedis的子类,用于向后兼容旧版本额redis-py.
import redisr = redis.Redis(host='10.211.55.4', port=6379)r.set('foo', 'Bar')print(r.get('foo'))
2.连接池
redis-py使用connection pool 来管理对一个redis server的所有连接,避免每次建立,释放连接的开销。默认每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池.
import redispool = redis.ConnectionPool(host='10.211.55.4', port=6379)r = redis.Redis(connection_pool=pool)r.set('foo', 'Bar')print(r.get('foo'))
3.操作
import redisimport json# r = redis.Redis()# r.set('foo', 'bar')# print(r.get('foo'))# 连接池pool = redis.ConnectionPool(decode_responses=True)r = redis.Redis(connection_pool=pool)# 字符串操作# 1.set 只能添加一个键值# r.set("a","1",5) # 会覆盖# print(r.get("a")) # 5s后为None# 2.setnx# r.setnx("b","2")# 如果没有设置,如果有,不覆盖# print(r.get("b"))# 3.mset 批量创建# r.mset(k1='v1', k2='v2')# r.mset({'k1':'v1', 'k2':'v2'})# print(r.get("k1")) #v1# print(r.get("k2")) #v2# 4.mget 批量获取# print(r.mget("k1","k2")) #['v1', 'v2']# 5. getset 设置新值并获取原来的值# print(r.get("k1")) #v1# print(r.getset("k1","a1")) #v1# print(r.get("k1")) #a1# 6. getrange 切片, 顾头顾尾# r.set("xxx","12345")# print(r.getrange("xxx",1,2)) #23# 7. setrange# r.set("name","abc def ghi")# r.setrange("name", 2, "123") #从name的索引为2的位置开始替换# print(r.get("name")) #ab123ef ghi# 8. strlen #长度# print(r.strlen("name")) #ab123ef ghi 11# 9. incr(增加),decr(减少)# r.set("age",100)# print(r.get("age")) #100# r.incr("age",10)# print(r.get("age")) #110# 10.append# r.append("name","?")# print(r.get("name")) # ab123ef ghi?# 删除所有keys:r.delete(*r.keys())# list操作# 1.# r.lpush("name","aaa","bbb")# print(r.lrange("name",0,-1)) #['bbb', 'aaa']# print(r.llen("name")) #2# r.linsert("name", "AFTER", "aaa", "ccc")# print(r.lrange("name", 0, -1)) #['bbb', 'aaa', 'ccc']# r.lset("name",0,"ddd")# print(r.lrange("name",0,-1)) #['ddd', 'aaa', 'ccc']# r.lrem("name", 1, "ddd")# print(r.lrange("name",0,-1)) #['aaa', 'ccc']# r.lpop("name")# print(r.lrange("name",0,-1)) #['ccc']# print(r.lindex("name",0)) #ccc# Hash操作# 1. hset, 只能设一个键值# r.hset("info","name","abc")# print(r.hget("info","name")) #abc# 2. hmset, 可以设多个键值# r.hmset("qwe",{"k1":"v1","k2":"v2"})# print(r.hmget("qwe",["k1","k2"])) #['v1', 'v2']# print(r.hgetall("qwe")) #{'k1': 'v1', 'k2': 'v2'}# print(r.hkeys("qwe")) #['k1', 'k2'] 拿到某个键下对应的所有键# print(r.keys()) #['qwe', 'info', 'asd', 'name'] 拿到所有的外边的键# print(r.hexists("qwe","k3")) #False# print(r.exists("qwe")) #1# r.hdel("qwe","k1")# print(r.hgetall("qwe")) #{'k2': 'v2'}# print(r.keys()) #['qwe', 'info', 'asd', 'name']# r.delete("asd")# print(r.exists("asd")) #0# r.delete(*r.keys())# print(r.keys()) #[]# r.hmset("qwer",{"k1":"v1","k2":"v2"})# print(r.keys("qwe*")) #['qwe', 'qwer']# print(r.keys("*")) #*匹配所有的# r.expire("qwe",5) #将键设置过期时间# print(r.keys())