redis
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是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)、存储方式 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提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
一、Redis安装和基本使用
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
tar xzf redis-3.0.6.tar.gz
cd redis-3.0.6
make
sudo apt-get install redis-server
二、启动服务端
src/redis-server
/etc/redis/redis.conf
#bind-port
requirepass = *****
daemonize yes # 讲redis在后台运行
protomode yes # 安装模式

# Redis 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写) # # 1k => 1000 bytes # 1kb => 1024 bytes # 1m => 1000000 bytes # 1mb => 1024*1024 bytes # 1g => 1000000000 bytes # 1gb => 1024*1024*1024 bytes # # 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB # daemonize no 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes daemonize yes # 当redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid,你可以配置到其他地址。 # 当运行多个redis服务时,需要指定不同的pid文件和端口 pidfile /var/run/redis.pid # 指定redis运行的端口,默认是6379 port 6379 # 指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求, # 在生产环境中最好设置该项 # bind 127.0.0.1 # Specify the path for the unix socket that will be used to listen for # incoming connections. There is no default, so Redis will not listen # on a unix socket when not specified. # # unixsocket /tmp/redis.sock # unixsocketperm 755 # 设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接 # 0是关闭此设置 timeout 0 # 指定日志记录级别 # Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose # debug 记录很多信息,用于开发和测试 # varbose 有用的信息,不像debug会记录那么多 # notice 普通的verbose,常用于生产环境 # warning 只有非常重要或者严重的信息会记录到日志 loglevel debug # 配置log文件地址 # 默认值为stdout,标准输出,若后台模式会输出到/dev/null #logfile stdout logfile /var/log/redis/redis.log # To enable logging to the system logger, just set 'syslog-enabled' to yes, # and optionally update the other syslog parameters to suit your needs. # syslog-enabled no # Specify the syslog identity. # syslog-ident redis # Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. # syslog-facility local0 # 可用数据库数 # 默认值为16,默认数据库为0,数据库范围在0-(database-1)之间 databases 16 ################################ 快照 ################################# # # 保存数据到磁盘,格式如下: # # save <seconds> <changes> # # 指出在多长时间内,有多少次更新操作,就将数据同步到数据文件rdb。 # 相当于条件触发抓取快照,这个可以多个条件配合 # # 比如默认配置文件中的设置,就设置了三个条件 # # save 900 1 900秒内至少有1个key被改变 # save 300 10 300秒内至少有300个key被改变 # save 60 10000 60秒内至少有10000个key被改变 save 900 1 save 300 10 save 60 10000 # 存储至本地数据库时(持久化到rdb文件)是否压缩数据,默认为yes rdbcompression yes # 本地持久化数据库文件名,默认值为dump.rdb dbfilename dump.rdb # 工作目录 # # 数据库镜像备份的文件放置的路径。 # 这里的路径跟文件名要分开配置是因为redis在进行备份时,先会将当前数据库的状态写入到一个临时文件中,等备份完成时, # 再把该该临时文件替换为上面所指定的文件,而这里的临时文件和上面所配置的备份文件都会放在这个指定的路径当中。 # # AOF文件也会存放在这个目录下面 # # 注意这里必须制定一个目录而不是文件 dir ./ ################################# 复制 ################################# # 主从复制. 设置该数据库为其他数据库的从数据库. # 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步 # # slaveof <masterip> <masterport> # 当master服务设置了密码保护时(用requirepass制定的密码) # slav服务连接master的密码 # # masterauth <master-password> # 当从库同主机失去连接或者复制正在进行,从机库有两种运行方式: # # 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求 # # 2) 如果slave-serve-stale-data是指为no,出去INFO和SLAVOF命令之外的任何请求都会返回一个 # 错误"SYNC with master in progress" # slave-serve-stale-data yes # 从库会按照一个时间间隔向主库发送PINGs.可以通过repl-ping-slave-period设置这个时间间隔,默认是10秒 # # repl-ping-slave-period 10 # repl-timeout 设置主库批量数据传输时间或者ping回复时间间隔,默认值是60秒 # 一定要确保repl-timeout大于repl-ping-slave-period # repl-timeout 60 ################################## 安全 ################################### # 设置客户端连接后进行任何其他指定前需要使用的密码。 # 警告:因为redis速度相当快,所以在一台比较好的服务器下,一个外部的用户可以在一秒钟进行150K次的密码尝试,这意味着你需要指定非常非常强大的密码来防止暴力破解 # # requirepass foobared # 命令重命名. # # 在一个共享环境下可以重命名相对危险的命令。比如把CONFIG重名为一个不容易猜测的字符。 # # 举例: # # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 # # 如果想删除一个命令,直接把它重命名为一个空字符""即可,如下: # # rename-command CONFIG "" ################################### 约束 #################################### # 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数, # 如果设置 maxclients 0,表示不作限制。 # 当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 # # maxclients 128 # 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key # Redis同时也会移除空的list对象 # # 当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作 # # 注意:Redis新的vm机制,会把Key存放内存,Value会存放在swap区 # # maxmemory的设置比较适合于把redis当作于类似memcached的缓存来使用,而不适合当做一个真实的DB。 # 当把Redis当做一个真实的数据库使用的时候,内存使用将是一个很大的开销 # maxmemory <bytes> # 当内存达到最大值的时候Redis会选择删除哪些数据?有五种方式可供选择 # # volatile-lru -> 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) # allkeys-lru -> 利用LRU算法移除任何key # volatile-random -> 移除设置过过期时间的随机key # allkeys->random -> remove a random key, any key # volatile-ttl -> 移除即将过期的key(minor TTL) # noeviction -> 不移除任何可以,只是返回一个写错误 # # 注意:对于上面的策略,如果没有合适的key可以移除,当写的时候Redis会返回一个错误 # # 写命令包括: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # # 默认是: # # maxmemory-policy volatile-lru # LRU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存),随意你可以选择样本大小进行检测。 # Redis默认的灰选择3个样本进行检测,你可以通过maxmemory-samples进行设置 # # maxmemory-samples 3 ############################## AOF ############################### # 默认情况下,redis会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,而且备份也不能很频繁,如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失。 # 所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式。 # 开启append only模式之后,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中,当redis重新启动时,会从该文件恢复出之前的状态。 # 但是这样会造成appendonly.aof文件过大,所以redis还支持了BGREWRITEAOF指令,对appendonly.aof 进行重新整理。 # 你可以同时开启asynchronous dumps 和 AOF appendonly no # AOF文件名称 (默认: "appendonly.aof") # appendfilename appendonly.aof # Redis支持三种同步AOF文件的策略: # # no: 不进行同步,系统去操作 . Faster. # always: always表示每次有写操作都进行同步. Slow, Safest. # everysec: 表示对写操作进行累积,每秒同步一次. Compromise. # # 默认是"everysec",按照速度和安全折中这是最好的。 # 如果想让Redis能更高效的运行,你也可以设置为"no",让操作系统决定什么时候去执行 # 或者相反想让数据更安全你也可以设置为"always" # # 如果不确定就用 "everysec". # appendfsync always appendfsync everysec # appendfsync no # AOF策略设置为always或者everysec时,后台处理进程(后台保存或者AOF日志重写)会执行大量的I/O操作 # 在某些Linux配置中会阻止过长的fsync()请求。注意现在没有任何修复,即使fsync在另外一个线程进行处理 # # 为了减缓这个问题,可以设置下面这个参数no-appendfsync-on-rewrite # # This means that while another child is saving the durability of Redis is # the same as "appendfsync none", that in pratical terms means that it is # possible to lost up to 30 seconds of log in the worst scenario (with the # default Linux settings). # # If you have latency problems turn this to "yes". Otherwise leave it as # "no" that is the safest pick from the point of view of durability. no-appendfsync-on-rewrite no # Automatic rewrite of the append only file. # AOF 自动重写 # 当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写 # # 它是这样工作的:Redis会记住上次进行些日志后文件的大小(如果从开机以来还没进行过重写,那日子大小在开机的时候确定) # # 基础大小会同现在的大小进行比较。如果现在的大小比基础大小大制定的百分比,重写功能将启动 # 同时需要指定一个最小大小用于AOF重写,这个用于阻止即使文件很小但是增长幅度很大也去重写AOF文件的情况 # 设置 percentage 为0就关闭这个特性 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb ################################## SLOW LOG ################################### # Redis Slow Log 记录超过特定执行时间的命令。执行时间不包括I/O计算比如连接客户端,返回结果等,只是命令执行时间 # # 可以通过两个参数设置slow log:一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(微妙), # 另一个是slow log 的长度。当一个新命令被记录的时候最早的命令将被从队列中移除 # 下面的时间以微妙微单位,因此1000000代表一分钟。 # 注意制定一个负数将关闭慢日志,而设置为0将强制每个命令都会记录 slowlog-log-slower-than 10000 # 对日志长度没有限制,只是要注意它会消耗内存 # 可以通过 SLOWLOG RESET 回收被慢日志消耗的内存 slowlog-max-len 1024 ################################ VM ############################### ### WARNING! Virtual Memory is deprecated in Redis 2.4 ### The use of Virtual Memory is strongly discouraged. # Virtual Memory allows Redis to work with datasets bigger than the actual # amount of RAM needed to hold the whole dataset in memory. # In order to do so very used keys are taken in memory while the other keys # are swapped into a swap file, similarly to what operating systems do # with memory pages. # # To enable VM just set 'vm-enabled' to yes, and set the following three # VM parameters accordingly to your needs. vm-enabled no # vm-enabled yes # This is the path of the Redis swap file. As you can guess, swap files # can't be shared by different Redis instances, so make sure to use a swap # file for every redis process you are running. Redis will complain if the # swap file is already in use. # # The best kind of storage for the Redis swap file (that's accessed at random) # is a Solid State Disk (SSD). # # *** WARNING *** if you are using a shared hosting the default of putting # the swap file under /tmp is not secure. Create a dir with access granted # only to Redis user and configure Redis to create the swap file there. vm-swap-file /tmp/redis.swap # vm-max-memory configures the VM to use at max the specified amount of # RAM. Everything that deos not fit will be swapped on disk *if* possible, that # is, if there is still enough contiguous space in the swap file. # # With vm-max-memory 0 the system will swap everything it can. Not a good # default, just specify the max amount of RAM you can in bytes, but it's # better to leave some margin. For instance specify an amount of RAM # that's more or less between 60 and 80% of your free RAM. vm-max-memory 0 # Redis swap files is split into pages. An object can be saved using multiple # contiguous pages, but pages can't be shared between different objects. # So if your page is too big, small objects swapped out on disk will waste # a lot of space. If you page is too small, there is less space in the swap # file (assuming you configured the same number of total swap file pages). # # If you use a lot of small objects, use a page size of 64 or 32 bytes. # If you use a lot of big objects, use a bigger page size. # If unsure, use the default :) vm-page-size 32 # Number of total memory pages in the swap file. # Given that the page table (a bitmap of free/used pages) is taken in memory, # every 8 pages on disk will consume 1 byte of RAM. # # The total swap size is vm-page-size * vm-pages # # With the default of 32-bytes memory pages and 134217728 pages Redis will # use a 4 GB swap file, that will use 16 MB of RAM for the page table. # # It's better to use the smallest acceptable value for your application, # but the default is large in order to work in most conditions. vm-pages 134217728 # Max number of VM I/O threads running at the same time. # This threads are used to read/write data from/to swap file, since they # also encode and decode objects from disk to memory or the reverse, a bigger # number of threads can help with big objects even if they can't help with # I/O itself as the physical device may not be able to couple with many # reads/writes operations at the same time. # # The special value of 0 turn off threaded I/O and enables the blocking # Virtual Memory implementation. vm-max-threads 4 ############################### ADVANCED CONFIG ############################### # 当hash中包含超过指定元素个数并且最大的元素没有超过临界时, # hash将以一种特殊的编码方式(大大减少内存使用)来存储,这里可以设置这两个临界值 # Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现, # 这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap, # 当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。 hash-max-zipmap-entries 512 hash-max-zipmap-value 64 # list数据类型多少节点以下会采用去指针的紧凑存储格式。 # list数据类型节点值大小小于多少字节会采用紧凑存储格式。 list-max-ziplist-entries 512 list-max-ziplist-value 64 # set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储。 set-max-intset-entries 512 # zsort数据类型多少节点以下会采用去指针的紧凑存储格式。 # zsort数据类型节点值大小小于多少字节会采用紧凑存储格式。 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 # Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用 # # 当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no。 # # 如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存 activerehashing yes ################################## INCLUDES ################################### # 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 # include /path/to/local.conf # include /path/to/other.conf
四、特点
持久化 AOF.RDB # 一段时间写到硬盘/每来一条数据写到硬盘, 各有利弊
单进程、单线程


redis没有实现访问控制这个功能,但是它提供了一个轻量级的认证方式,可以编辑redis.conf配置来启用认证。 1、初始化Redis密码: 在配置文件中有个参数: requirepass 这个就是配置redis访问密码的参数; 比如 requirepass test123; (Ps:需重启Redis才能生效) redis的查询速度是非常快的,外部用户一秒内可以尝试多大150K个密码;所以密码要尽量长(对于DBA 没有必要必须记住密码); 2、不重启Redis设置密码: 在配置文件中配置requirepass的密码(当redis重启时密码依然有效)。 redis 127.0.0.1:6379> config set requirepass test123 查询密码: redis 127.0.0.1:6379> config get requirepass (error) ERR operation not permitted 密码验证: redis 127.0.0.1:6379> auth test123 OK 再次查询: redis 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "test123" PS:如果配置文件中没添加密码 那么redis重启后,密码失效; 3、登陆有密码的Redis: 在登录的时候的时候输入密码: redis-cli -p 6379 -a test123 先登陆后验证: redis-cli -p 6379 redis 127.0.0.1:6379> auth test123 OK AUTH命令跟其他redis命令一样,是没有加密的;阻止不了攻击者在网络上窃取你的密码; 认证层的目标是提供多一层的保护。如果防火墙或者用来保护redis的系统防御外部攻击失败的话,外部用户如果没有通过密码认证还是无法访问redis的。
python 操作redis
pip3 install redis
redis.py 的API的使用可以分类为:
1、连接方式
-StrictRedis # 使用官方的语法和命令 -Redis(StrictRedis) #可以兼容旧版本 import redis r = redis.Redis(host="192.168.187.129", port=6379) # db=0指定数据库
# decode_response=True # 以后取出来的都是字符串,不是字节 StrictRedis
2、连接池
使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。
默认,每个Redis实例都会维护一个自己的连接池。
可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。 import redis pool = redis.ConnectionPool(host='10.211.55.4', port=6379) r = redis.Redis(connection_pool=pool)
3、操作
LPUSH + LPOP = Stack LPUSH + RPOP = Queue LPUSH + LTRIM = cAPPED cOLECTION LPUSH + BRPOP = Message QUEUE
每个value都可以对应(字符串,列表,集合,有序集合,哈希类型)这五种。
# -------------------------String------------------------ # 在内存里都是字节 r.set("foo", "bar", ex=2, px=0,nx=False,xx=False) # 秒、毫秒;nx为true,只有foo不存在,set才执行;xx为true,只有foo存在时,set执行
// 设置了时间后,实际是set和expire的一个组合,但是是原子操作,对于分布式锁,有用 r.set("foo", "bar") # 秒、毫秒 r.setnx("foo", "asd") # 只有key不存在时,才会赋值 r.setex("foo", "bar23", time=2) # 对key赋值, 秒、timedelta对象
r.psetex("foo", 2, "xxx") # 对key赋值, 秒、timedelta对象 和上边只是位置不同 r.mset(k1="v1", k2="v2") # 批量赋值或者{"k1":"v1",} 原子操作 r.get("foo") # 获取值 r.mget(["foo", ]) # 批量获取值 *args 原子操作 r.getset("foo", "xcx") # 设置新值,并取得原来的值 r.getrange("foo", 0, 3) # 字节切片 r.setrange("foo", 2, "xxxxxx") # 替换字节索引2以后所有字节 r.setbit("foo", 2, 1) # key的值所对应的二进制索引为2的值变为1 r.getbit("foo", 3) # 获取foo值二进制索引3的值 r.bitcount("foo", 0, 10) # 获取name对应的值(0,10)二进制表示中 1 的个数 r.strlen("foo") # 字节长度 # 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算求并集,将结果保存 new_name 对应的值中 r.bitop("AND", 'new_name', 'n1', 'n2', 'n3') # AND,OR,NOT,XOR r.incr("foo", amount=1) # 自增1,必须是整数 r.incrbyfloat("foo", amount=-1.0) # 自增-1.0, 不存在创建 r.decr("foo", amount=1) # 自减1,必须是整数
r.append("foo", "after") # 在对应的值后面追加内容,没有prepend
# # ------------------Hash----------------------- # 内存以字节存在 类似字典 # r.hset("box", "f", "1") # 盒子里有一组组键值对/不存在创建、否则修改
r.hget
r.hsetnx # r.hmset("box", {"k1": "v1", "k2": "v2"}) # 批量设置键值 # r.hmget("box", ["k1", "k2"], "f") # 批量获取键值返回一个列表 # r.hgetall("box") # 获取name下所有的键值对 # r.hlen("box") # 有几个键值对 # r.hkeys("box") # 获取所有的键 # r.hvals("box") # 获取所有的键值 # r.hexists("box", "k1") # 判断name下是否有某个键 # r.hdel("box", "k1", "k2") # 删除name下是否有某个键 # r.hincrby("box", "f", amount=10) # 自增/整数 # r.hincrbyfloat("box", "f", amount=1.0) # 自增/浮点数
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时,表示数据已经通过分片获取完毕
hscan_iter(name, match=None, count=None) # 利用yield封装hscan创建生成器,实现分批去redis中获取数据 # 参数: # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 # 如: # for item in r.hscan_iter('xx'): // 动态的先取100,条,将这100条通过生成器返回 # print item
# # -------------------List--------------------- # r.lpush("box", 11, 22, 33, ) # 每个元素一次往左插入列表,不存在会创建 33 22 11 # r.rpush("box", 11, 22, 33, ) # 每个元素一次往右插入列表 11 22 33 # r.lpushx("box", 11) # 和lpush一样,异:只能插一个值,且name必须存在 # r.rpushx("box", 11) # 和rpush一样,异:只能插一个值,且name必须存在 # r.llen("box") # 返回name中list的个数 O 1 # r.linsert("box", "AFTER", 3, "value") # 在box索引为3的后面插入value # r.lset("box", 3, "value") # 对box索引为3的值重新赋值 O 1 # r.lrem("box", "value", -2) # -2从右往左删除2个value;2从左往右删除2个value;0全删除 o(N) # r.lpop("box") # 弹出box左边第一个元素 O(1) # r.rpop("box") # 弹出box右边第一个元素 o(1) # r.lindex("box", 3) # 获取box索引为3的值 # r.ltrim("box", 0, 3) # 保留0-3索引间的值 o(N) 应用,分阶段保留,直到为0;比直接del好 不用阻塞 # r.rpoplpush("box", "asd") # 将box右边pop 放入asd左边
blpop(keys, timeout)将多个列表排列,按照从左到右去pop对应列表的元素 如果没有数据会夯助, 可以设置超时时间
brpop(keys, timeout),从右向左获取数据 O 1
r.lrange(name,start,end) 分片取数据(包含) O(n)
# 自定义增量迭代
# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要: # 1、获取name对应的所有列表 # 2、循环列表 # 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能: def list_iter(name): """ 自定义redis列表增量迭代 :param name: redis中的name,即:迭代name对应的列表 :return: yield 返回 列表元素 """ list_count = r.llen(name) for index in xrange(list_count): yield r.lindex(name, index) # 使用 for item in list_iter('pp'): print item
--------------------------------------------------------------------------------------
# # -----------------Set----------------- # r.sadd("box", "1", "3") # 在集合中添加元素 # r.scard("box") # 获得集合元素个数 # r.sdiff("box", "box1", "box2") # 获取第一个集合有但其他集合没有的元素 # r.sdiffstore("newbox", "box", "box1", "box2") # 获取第一个集合有但其他集合没有的元素,然后将其添加到newbox # r.sinter("box", "box1", "box2") # 获取多个集合的交集 # r.sinterstore("newbox", "box", "box1", "box2") # 获取多个集合的并集,并将其添加到新集合 # r.sismember("box", "value") # 检查value是否是box的元素True/False # r.smembers("box") # 获取box集合 # r.smove("box", "box2", 1) # 将1 从box移到box2 # r.spop("box") # 弹出box右侧一个元素 # r.srandmember("box", 3) # 随机获取box3个元素 # r.srem("box", 1) # 删除box中的1 # r.sunion("box", "box1", "box2") # 获取多个集合的并集 # r.sunionstore("newbox", "box", "box1", "box2") # 获取多个集合的并集并将其放入newbox
sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
----- v = r.sadd("box","baidu.com")
若v=1 表示之前没有
若v=0 表示已经有了
----------------------------有序集合---------------------------
# 有序集合,在value的值基础上有一个score,用来排序
# r.zadd("box", n1=11, n2=33) # 有序集合中添加元素('zz', 'n1', 1, 'n2', 2) # r.zcard("box") # 有序结合的个数 # r.zcount("box", 0, 40) # 获取name对应的有序集合中分数 在 [min,max] 之间的个数 # r.zincrby("box", "key", amount=10) # 设置一个key,分数自增10,
r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) # 按照索引范围获取name对应的有序集合的元素 # 参数: # name,redis的name # start,有序集合索引起始位置(非分数) # end,有序集合索引结束位置(非分数) # desc,排序规则,默认按照分数从小到大排序 # withscores,是否获取元素的分数,默认只获取元素的值 # score_cast_func,对分数进行数据转换的函数 # 更多: # 从大到小排序 # zrevrange(name, start, end, withscores=False, score_cast_func=float) # 按照分数范围获取name对应的有序集合的元素 # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) # 从大到小排序 # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
# r.zrank("box", "n1") # 获取key在box的索引(从0开始),排序之后
zrangebylex(name, min, max, start=None, num=None) # 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,
而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员 # 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。
如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大 # 参数: # name,redis的name # min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间 # min,右区间(值) # start,对结果进行分片处理,索引位置 # num,对结果进行分片处理,索引后面的num个元素 # 如: # ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga # r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca'] # 更多: # 从大到小排序 # zrevrangebylex(name, max, min, start=None, num=None)
r.zrem("box",["s1","s2"]) # 删除name对应的有序集合中值是values的成员
r.zremrangebyrank(name, min, max) # 根据排行范围删除
r.zremrangebyscore(name, min, max) # 根据分数范围删除
r.zremrangebylex(name, min, max) # 根据值返回删除
r.zscore(name, value) # 获取name对应有序集合中 value 对应的分数
r.zinterstore(dest, keys, aggregate=None)# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
r.zunionstore(dest, keys, aggregate=None)# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
--------------------基于name的操作-----------------------
delete(*names) # 根据删除redis中的任意数据类型 exists(name) # 检测redis的name是否存在 keys(pattern='*') # 根据模型获取redis的name //正则 “luffy_shopping_car_6_*” expire(name ,time) # 为某个redis的某个name设置超时时间
ttl key 查询key剩余的过期时间
persist key 去掉key的过期时间,永久
type key 查看key的类型 rename(src, dst) # 对redis的name重命名为 move(name, db)) # 将redis的某个值移动到指定的db下 randomkey() # 随机获取一个redis的name(不删除) type(name) # 获取name对应值的类型
scan(cursor=0, match=None, count=None) scan_iter(match=None, count=10) # 同字符串操作,用于增量迭代获取key
for item in conn.scan_iter(***,count=10):
course = conn.hgetall(item)
nil 不存在 -1 存在,没有过期时间 -2 key过期,删除
时间复杂度
mget\hmget\hmset\hgetall\hgets\hvals O(n)
keys O(n) dbsize O(1) del O(1)
set ...
get ...
exists O(1) hexists expire O(1) type O(1)
ttl O(1)
presist O(1)
incr* O(1) 原子操作
strlen O(1) hlen
长命令
keys
flushall
flushdb
slow lua script
mutil/exec
operate big value (collection)
4.管道
redis.py默认在执行每次请求都会创建(连接池申请链接)的断开(归还连接池)一次性连接操作,如果想要在一次请求中指定多个命令,则可以使用pipeline实现一次请求多个命令,并且默认下一次Pipeline是原子性操作。
# pipe = r.pipeline(transaction=False) 事务 pipe = r.pipeline(transaction=True) pipe.multi() pipe.set('name', 'alex') pipe.set('role', 'sb') pipe.execute()

import redis conn = redis.Redis(host='192.168.1.41',port=6379) conn.set('count',1000) with conn.pipeline() as pipe: # 先监视,自己的值没有被修改过 conn.watch('count') # 事务开始 pipe.multi() old_count = conn.get('count') count = int(old_count) if count > 0: # 有库存 pipe.set('count', count - 1) # 执行,把所有命令一次性推送过去 pipe.execute()
5 发布、订阅

#!/usr/bin/env python # -*- coding:utf-8 -*- import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host='10.211.55.4') self.chan_sub = 'fm104.5' self.chan_pub = 'fm104.5' def public(self, msg): self.__conn.publish(self.chan_pub, msg) return True def subscribe(self): pub = self.__conn.pubsub() pub.subscribe(self.chan_sub) pub.parse_response() return pub

from monitor.RedisHelper import RedisHelper obj = RedisHelper() redis_sub = obj.subscribe() while True: msg= redis_sub.parse_response() print msg

from monitor.RedisHelper import RedisHelper obj = RedisHelper() obj.public('hello')
watch
为了保证事务的幂等性
Hyperloglog
类似于Set,但是只需要很小的内存(只能计数,有错误率,)
pfadd key element... 向gyperloglog添加元素
pfcount key ... 计算hyperloglog的独立总数
pfmerge destkey sourcekey... 合并多个hyperloglog
Geo
geo key longitude latitude member 增加地理信息位置 geo key x 116.28 39.55 beijing
geopos x beijing
获取地理位置信息
geodist key member1 member2 [unit]
获取两个地理位置的距离
m米 km千米 mi英里 ft尺

6、sentinel
redis重的sentinel主要用于在redis主从复制中,如果master顾上,则自动将slave替换成master #!/usr/bin/env python # -*- coding:utf-8 -*- from redis.sentinel import Sentinel # 连接哨兵服务器(主机名也可以用域名) sentinel = Sentinel([('10.211.55.20', 26379), ('10.211.55.20', 26380), ], socket_timeout=0.5) # # 获取主服务器地址 # master = sentinel.discover_master('mymaster') # print(master) # # # # 获取从服务器地址 # slave = sentinel.discover_slaves('mymaster') # print(slave) # # # # # 获取主服务器进行写入 # master = sentinel.master_for('mymaster') # master.set('foo', 'bar') # # # # 获取从服务器进行读取(默认是round-roubin) # slave = sentinel.slave_for('mymaster', password='redis_auth_pass') # r_ret = slave.get('foo') # print(r_ret)
twemproxy
1、twemproxy 是 twitter 开发的一个代理服务器,它兼容Redis 和 Memcached ,允许用户将多个 Redis 服务器添加到一个服务器池(pool)里面,并通过用户选择的散列函数和分布函数,将来自客 户端的命令请求分发给服务器池中的各个服务器。 2、通过使用 twemproxy ,我们可以将数据库分片到多台Redis 服务器上面,并使用这些服务器来分担系统负责以及数据库容量:在服务器硬件条件相同的情况下, 对于一个包含 N 台 Redis 服务器的池来说,池中每台服务器平均包含整个数据库数据的 1/N ,并处理平均 1/N 的客户端命令请求。 3、向池里面添加更多服务器可以线性地扩展系统处理命令请求的能力,以及系统能够保存的数据量。
1. 安装 twemproxy : 下载并解压压缩包: http://code.google.com/p/twemproxy/downloads/list $ ./configure $ make $ sudo make install 2. 使用 yml 格式编写配置文件: my_redis_server_group: # 服务器池的名字,支持创建多个服务器池 listen: 127.0.0.1:22121 # 这个服务器池的监听地址和端口号 hash: fnv1a_64 # 键散列算法,用于将键映射为一个散列值 distribution: ketama # 键分布算法,决定键被分布到哪个服务器 redis: true # 代理 Redis 命令请求,不给定时默认代理 Memcached 请求 servers: # 池中各个服务器的地址和端口号,以及 权重 - 127.0.0.1:6379:1 - 127.0.0.1:6380:1 - 127.0.0.1:6381:1 3. $ nutcracker -c nutcracker.yml
django - redis
from django.core.cache import cache # 提供的一个模块
pip3 install django-redis
# 不需要再手动创建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": "密码", } },# 支持集群 }
# views.py
from django_redis import get_redis_connection conn = get_redis_connection("default") # 从default的配置里获取一个连接,可以指定别的

# -*- coding:utf-8 -*- import redis # 创建连接池 # 单例模式 不然性能就降低了 POOL = redis.ConnectionPool(host="10.0.0.200", port=6379, password="luffy1234", max_connections=1000) import redis from redis_pool import POOL while True: key = input("请输入key:") value = input("请输入value") # 去连接池中获取连接 conn = redis.Redis(connection_pool=POOL) # 设置池 conn.set(key,value)
全站缓存
MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 只有一个process_response ...... 'django.middleware.cache.FetchFromCacheMiddleware', # 只有一个process_request ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
单视图缓存
方式一: from django.views.decorators.cache import cache_page @cache_page(60 * 15) # 15分钟 def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
局部视图缓存
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %}
django 缓存 地点
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)

# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func

# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } # 注:其他配置同开发调试版本

# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } # 注:其他配置同开发调试版本

# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } } # 注:执行创建表命令 python manage.py createcachetable

# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }

# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }

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": "密码", } } } from django_redis import get_redis_connection conn = get_redis_connection("default")
更多:猛击这里
雪崩: 1、redis缓存大量过期,导致流量引向数据库 2、redis崩 可能导致数据库崩 击穿: 大量请求redis中的某条记录,但是当其丢失时,会导致redis击穿。
解决方法:
1、取数据库中查询数据,放入到缓存层, 请求量不大
2、布隆过滤器
3、分布式锁 请求量大