tip:以下配置文件为windows上的,Linux版本可以参考配置文件的单位大小写不敏感INCLUDES包含可以把多个conf文件,组合成一个##################################INCLUDES###################################...#include.\path\to\local.conf#includec:\path\to\other.confNETWORK网络##################################NETWORK#####################################...#绑定IP,可以绑定多个#如需远程访问,可以填本机IPbind127.0.0.1#默认protected-modeyes#保护模式(默认)port6379#端口(默认)GENERAL通用#windows不支持daemonizeno#以守护进程(后台)运行,默认no,需要改为yessupervisedno#守护进程管理默认no,不用管#windows不支持pidfile/var/run/redis.pid#以守护进程运行,需要指定pid文件loglevelnotice#日志级别,默认notice,支持debug、verbose、notice、warninglogfile""#日志输入文件,默认控制台输出databases16#默认16个数据库always-show-logoyes#启动是的logo显示SNAPSHOTTING快照持久化的配置主要在此部分################################SNAPSHOTTING#################################执行持久化保存的时间save9001#900秒改动1次,执行1次持久化操作save30010#300改动10次,执行1次持久化操作save6010000stop-writes-on-bgsave-erroryes#持久化出错,是否继续工作,默认yesrdbcompressionyes#是否压缩rdb文件,默认yes,需要消耗CPU的性能rdbchecksumyes#保存rdb文件时校验,默认yesdbfilenamedump.rdb#rdb文件的路径,默认当前路径REPLICATION主从复制Redis启动默认为主机#在配置主从复制中只需修改从机的配置文件#replicaof<masterip><masterport>#主机ipportSECURITY安全密码配置文件##################################SECURITY####################################requirepassfoobared#密码,默认没有命令行127.0.0.1:6379>configgetrequirepass#获取密码1)"requirepass"2)""127.0.0.1:6379>configsetrequirepass123456#设置密码OK127.0.0.1:6379>configsetrequirepass(error)NOAUTHAuthenticationrequired.#设置后需要验证密码127.0.0.1:6379>auth123456#验证密码OK127.0.0.1:6379>configgetrequirepass1)"requirepass"2)"123456"CLIENTS客户端客户端的限制###################################CLIENTS#####################################maxclients10000#客户端最大连接数量MEMORYMANAGEMENT内存设置##############################MEMORYMANAGEMENT#################################maxmemory<bytes>#最大的内存容量#maxmemory-policynoeviction#内存达到上限的处理策略#volatile-lru->使用带有过期时间的Key使用lru算法进行删除。#allkeys-lru->使用lru算法进行删除Key#volatile-random->随机删除一些有过期时间的Key#allkeys-random->随机删除一些Key#volatile-ttl->删除最近要过期的Key#noeviction->不要删除任何东西,只在写操作时返回一个错误。【默认】APPENDONLYMODE(AOF持久化)主要功能是记录改变Key的操作##############################APPENDONLYMODE###############################appendonlyno#默认no不开启,大部分情况rdb完全够用appendfilename"appendonly.aof"#aof持久化文件路径appendfsynceverysec#执行aof持久化同步操作的模式,有三种#always->每次修改值,都会执行fsync(),消耗资源#everysec->每秒执行一次fsync(),可能丢失这一秒的数据(默认)#no->不执行fsync(),让操作系统在需要的时候刷新数据,速度快
什么是持久化Redis是内存数据库,数据保存在内存中,当关机时,内存中的数据也就丢失了。持久化就是把内存中的数据写入到磁盘上,保存内存在断电情况下,数据也不会丢失。RDB(RedisDataBase)RDB文件是在硬盘上的二进制文件;RDB文件是Redis在内存存储的数据在某一时刻的快照;RDB文件可在Redis启动的时候自动载入;RDB文件是Redis节点复制时的媒介;相关配置#RDB自动持久化规则#当900秒内有至少有1个键被改动时,自动进行数据集保存操作save9001#当300秒内有至少有10个键被改动时,自动进行数据集保存操作save30010#当60秒内有至少有10000个键被改动时,自动进行数据集保存操作save6010000#RDB持久化文件名dbfilenamedump.rdb#数据持久化文件存储目录dir./#bgsave发生错误时是否停止写入,通常为yesstop-writes-on-bgsave-erroryes#rdb文件是否使用压缩格式rdbcompressionyes#是否对rdb文件进行校验和检验,通常为yesrdbchecksumyes持久化过程Redis会单独创建一个子线程来进行持久化操作,会先将内存中的数据写入到一个文件中,待持久化结束后,再替换掉上次持久化的文件,整个过程中,主进程不进行IO操作,保证了性能。缺点:最后一次持久化的数据可能丢失(子进程还没有写入完成就被某种原因关闭)触发RDB持久化机制的方式手动触发save、bgsavesave命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求。127.0.0.1:6379>saveOK127.0.0.1:6379>bgsaveBackgroundsavingstarted自动触发规则,在配置文件配置save9001save30010save6010000AOF(AppendOnlyFile)快照功能(RDB)并不是非常耐久(durable):如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从1.1版本开始,Redis增加了一种完全耐久的持久化方式:AOF持久化。需要在配置文件开启,重启Redis服务生效。appendonlyyes相关配置#开启AOF持久化方式appendonlyyes#AOF持久化文件名appendfilenameappendonly-<port>.aof#appendfsyncalways#每次修改值,都会同步数据到磁盘,消耗资源appendfsynceverysec#每秒把缓冲区的数据同步到磁盘#appendfsyncno#不执行数据同步,让操作系统在需要的时候刷新数据#数据持久化文件存储目录dir./#是否在执行重写时不同步数据到AOF文件#这里的yes,就是执行重写时不同步数据到AOF文件no-appendfsync-on-rewriteyes#触发AOF文件执行重写的最小尺寸auto-aof-rewrite-min-size64mb#触发AOF文件执行重写的增长率auto-aof-rewrite-percentage100执行流程windows下需要使用redis-server.exeredis.windows.conf启动,配置文件才生效127.0.0.1:6379>setk1v1OK127.0.0.1:6379>setk2v2OK127.0.0.1:6379>setk3v3OK127.0.0.1:6379>setk4v4OK127.0.0.1:6379>getk2"v2"127.0.0.1:6379>setk5v5OK可以看到修改值的操作都被记录下来了AOF文件修复Redis自带有redis-check-aof程序,用来修复AOF文件,修复后的文件能打开,但会出现数据丢失的情况,至少比不能打开好。删除RDB文件(里面有完整数据),破坏AOF文件(删除某些文件),模拟数据被破坏的场景。启动Redis服务,启动失败使用aof修复工具,修复成功再次启动Redis服务,启动成功查看所有的Key,发现只有两个,其他的已经丢失,至少能打开了扩展对比-RDBAOF启动优先级低高体积小大恢复速度快慢数据安全性丢数据根据策略决定RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写(缩减某些格式),使得AOF文件的体积不至于过大。只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.同时开启两种持久化方式在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。性能建议:因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save9001这条规则。如果启用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOFrewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOFrewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。如果不启用AOF,仅靠Master-SlaveReplication(主从复制)实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。
乐观锁乐观锁并不是Redis的功能,而是Redis的watch关键字能实现乐观锁。开启事务前,设置对数据的监听(watch),exec时,如果发生数据发生过修改,作用于改数据的事务会自动取消(discard),事务exec后,无论成败,监听会被移除添加监听:watchkey[keys]取消监听(会取消所有的):unwatch乐观锁实例开启监听新开启一个客户端,修改k值127.0.0.1:6379>setkbbOK继续exec执行事务(exec之前k值已经被改变),事务执行不会成功应用场景多个客户端可能同时操作同一组数据,并且该数据一旦被操作修改后,便不适合在原基础上继续操作,如四个业务员对一个物品进行入库操作,一名业务员操作完成之后,其他的业务员将不可以再原先基础的数量上进行入库操作。
事务什么是事务?一次性执行多条命令的过程重要概念Redis单条命令是原子性的【一起成功,一起失败】事务不是原子性,执行成功的成功,失败的报错。事务的流程一次性、顺序性、排他性一次性:一次执行完顺序性:按照队列顺序执行排他性:不允许被干扰开始事务('multi')-->命令入队列('此时命令没有执行')-->执行事务('exec')【按照顺序执行命令】127.0.0.1:6379>multiOK127.0.0.1:6379>setk1v1QUEUED127.0.0.1:6379>setk2v2QUEUED127.0.0.1:6379>getk2QUEUED127.0.0.1:6379>exec1)OK2)OK3)"v2"127.0.0.1:6379>取消事务127.0.0.1:6379>multiOK127.0.0.1:6379>getk2QUEUED127.0.0.1:6379>discardOK事务的错误处理编译型错误事务添加了错误命令,在事务执行时会报错,整个事务都不会被执行127.0.0.1:6379>multiOK127.0.0.1:6379>setkkvvQUEUED127.0.0.1:6379>setgetk5(error)ERRunknowncommand`setget`,withargsbeginningwith:`k5`,127.0.0.1:6379>setk4v4QUEUED127.0.0.1:6379>exec(error)EXECABORTTransactiondiscardedbecauseofpreviouserrors.运行时异常逻辑错误,语法正确127.0.0.1:6379>multiOK127.0.0.1:6379>setkkvvQUEUED127.0.0.1:6379>incrk1QUEUED127.0.0.1:6379>setk1v1QUEUED127.0.0.1:6379>getk1QUEUED127.0.0.1:6379>exec1)OK2)(error)ERRvalueisnotanintegeroroutofrange3)OK4)"v1"
说明bitmaps(位图)主要用来记录只有两种的状态的事件。比如:活跃|不活跃、登录|未登录、成功|失败优势存储信息时通常可以极大地节省空间,仅使用512MB内存就可以记住40亿用户的单个位信息。设置时候时间复杂度O(1)、读取时候时间复杂度O(n),操作是非常快的。二进制数据的存储,进行相关计算的时候非常快。方便扩容使用常用三个命令,就很简单,就直接举例子(打卡|签到)setbitkeyoffsetvalueoffset:value的偏移值,初始值为0(看下面的图)value:只能是0or1(这是二进制的位)127.0.0.1:6379>SETBITsign:Bob:20220111#设置这里的1|0是二进制(integer)0127.0.0.1:6379>SETBITsign:Bob:20220121(integer)0127.0.0.1:6379>SETBITsign:Bob:20220130(integer)0127.0.0.1:6379>SETBITsign:Bob:20220141(integer)0127.0.0.1:6379>SETBITsign:Bob:20220151(integer)0127.0.0.1:6379>SETBITsign:Bob:20220161(integer)0127.0.0.1:6379>SETBITsign:Bob:20220171(integer)0127.0.0.1:6379>GETBITsign:Bob:2022012#获取某天状态(integer)1127.0.0.1:6379>BITCOUNTsign:Bob:202201#统计位1的数量(integer)6
说明说明是基数数据集:{1,1,2,6,7,8,9}那么这个数据的基数集为:{1,2,6,7,8,9}hyperloglogs优势在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。每个HyperLogLog键只需要花费12KB内存,就可以计算接近2^64个不同元素的基数。使用添加元素pfaddkelement[element...]127.0.0.1:6379>PFADDkabcdd(integer)1统计基数pfcountkkey[key...]127.0.0.1:6379>pfcountk(integer)4合并多个HyperLogLogpfmergedestkeysourcekey[sourcekey...]destkey:合并结果保存的HyperLogLog集127.0.0.1:6379>pfaddkkasdfgg(integer)1127.0.0.1:6379>pfmergekeykkkOK127.0.0.1:6379>pfcountkey(integer)7使用场景网页的UV统计传统的方式,set存储用户的id,然后取统计元素的数量,如果这种方式要保存大量的用户id,就很麻烦。统计UV是为了计数,而不是保存用户id
说明常应用于附近的人、两地距离计算、朋友圈定位…中文文档:http://redis.cn/commands/geoadd.html将有效的地理空间位置(经度,纬度,名称)添加到指定的Key中,这些Key会存到Hset(有序集合)中。有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度。该类型的底层实现是Hset(有序聚合),所以也能使用有序集合的命令操作常用六大命令地理位置的坐标可以在网络上下载完整的信息进行添加geoaddkeylongitudelatitudemember[longitudelatitudemember...]127.0.0.1:6379>geoaddchina:city104.06573530.659462chengdu(integer)1127.0.0.1:6379>geoaddchina:city116.40528539.904989beijing114.08594722.547shenzhen(integer)2获取地理位置的坐标geoposkeymember[member]127.0.0.1:6379>geoposchina:citybeijing1)1)"116.40528291463851929"2)"39.9049884229125027"127.0.0.1:6379>geoposchina:citybeijingshenzhen1)1)"116.40528291463851929"2)"39.9049884229125027"2)1)"114.08594459295272827"2)"22.54699993773966327"获取两地距离keymember1member2[unit][unit]:m(米)、km(千米)、mi(英里)、ft(尺)127.0.0.1:6379>geodistchina:citybeijingshenzhenkm"1943.0240"获取附近的位置集合需要给==经纬度==通过半径来返回位置集合georadiuskeylongitudelatituderaidusm|km|ft|mi[withcoord][withdist][withhash][COUNTcount][asc|desc][storekey][storedistkey]下列可选参数是在返回结果上嵌套一层显示[withcoord]:返回结果显示位置经纬度[withdist]:返回结果显示距离[COUNTcount]:指定返回结果的数量[withhash]:返回结果哈希[asc|desc]:根据中心,由近到远|由远到近返回结果127.0.0.1:6379>georadiuschina:city110301000km1)"chengdu"2)"shenzhen"127.0.0.1:6379>georadiuschina:city110301000kmwithcoordwithdistwithhashasccount21)1)"chengdu"2)"574.3396"3)(integer)40261377941277544)1)"104.06573742628097534"2)"30.65946118872339099"2)1)"shenzhen"2)"923.4929"3)(integer)40464337336821184)1)"114.08594459295272827"2)"22.54699993773966327"获取附近的位置集合需要给==位置==通过半径来返回位置集合georadiusbymemberkeymemberraidusm|km|ft|mi[withcoord][withdist][withhash][COUNTcount][asc|desc][storekey][storedistkey]下列可选参数是在返回结果上嵌套一层显示[withcoord]:返回结果显示位置经纬度[withdist]:返回结果显示距离[COUNTcount]:指定返回结果的数量[withhash]:返回结果哈希[asc|desc]:根据中心,由近到远|由远到近返回结果127.0.0.1:6379>georadiusbymemberchina:citybeijing2000km1)"chengdu"2)"shenzhen"3)"beijing"127.0.0.1:6379>georadiusbymemberchina:citybeijing2000kmwithcoordwithdistwithhashasccount21)1)"beijing"2)"0.0000"3)(integer)40698853706710104)1)"116.40528291463851929"2)"39.9049884229125027"2)1)"chengdu"2)"1517.9907"3)(integer)40261377941277544)1)"104.06573742628097534"2)"30.65946118872339099"获取位置HASH将二维经纬度信息,转换成一维字符串,返回的11位哈希值字符串越相似,说明距离越近geohashkeymember[member]127.0.0.1:6379>GEOHASHchina:citybeijingchengdushenzhen1)"wx4g0b7xrt0"2)"wm6n2j6k730"3)"ws10k0dcg10"
说明中文文档:http://redis.cn/documentation.htmlString文档:http://redis.cn/commands.html#stringList文档:http://redis.cn/commands.html#listSet文档:http://redis.cn/commands.html#setHash文档:http://redis.cn/commands.html#hashHset文档:http://redis.cn/commands.html#sorted_setKey实际使用中,建议key使用:(冒号)分割不同层次127.0.0.1:6379>setuser:2:nameAoaOK127.0.0.1:6379>setuser:1:nameBobOKString类型最简单Redis类型##设置K-V#################################################setkv[nx|xx][过期时间/秒]127.0.0.1:6379>setkvnx#Key不存在设置(分布式锁用到)OK127.0.0.1:6379>setkvnx(nil)127.0.0.1:6379>setkkvxx#Key存在设置(nil)##V自增|减#################################################原子性几个客户端同时操作会增加|减少127.0.0.1:6379>setk10OK127.0.0.1:6379>incrk#key自增1(integer)11127.0.0.1:6379>incrbyk10#key增加10(integer)21127.0.0.1:6379>decrk#key自减1(integer)20127.0.0.1:6379>decrbyk5#key减少5(integer)15##批量set|get#################################################原子操作一起成功|一起失败127.0.0.1:6379>msetk1v1k2v2k3v3OK127.0.0.1:6379>mgetk1k2k31)"v1"2)"v2"3)"v3"##修改|检查|删除################################################127.0.0.1:6379>setkvv#再次设置为修改OK127.0.0.1:6379>existsk#检查是否存在(integer)1127.0.0.1:6379>delk#删除并返回(integer)1127.0.0.1:6379>existsk(integer)0##获取指定长度#################################################getrange键起始位置长度127.0.0.1:6379>setkabcdeOK127.0.0.1:6379>getrangek12#getrange"bc"127.0.0.1:6379>getrangek0-1"abcde"##设置指定'位置'的值#################################################setrange键位置值127.0.0.1:6379>setkabcdeOK127.0.0.1:6379>setrangek1mm#setrange(integer)5127.0.0.1:6379>getk"ammde"List列表list列表常用于队列和栈的操作。【消息队列】实际上是一个链表如果key不存在,就创建新的链表如果key存在,就添加值移除了所有值,也就不存在了两边插入或者改动值,效率最高高!对中间元素进行操作,效率就相对于低一些##添加值(头部)#################################################lpush键值(一个值或者多个值)127.0.0.1:6379>lpushkv1(integer)1127.0.0.1:6379>lpushkv2(integer)2127.0.0.1:6379>lpushkv3(integer)3127.0.0.1:6379>LPUSHkv4v5(integer)5127.0.0.1:6379>##添加值(尾部)#################################################rpush键值(一个值或者多个值)127.0.0.1:6379>rpushkv6(integer)6##查看值(批量)#################################################lrange键startend127.0.0.1:6379>lrangek0-11)"v5"2)"v4"3)"v3"4)"v2"5)"v1"6)"v6"127.0.0.1:6379>lrangek011)"v5"2)"v4"##查看值(单个索引)#################################################lindex键索引127.0.0.1:6379>lindexk1"v4"##删除值(头部)#################################################lpop键[timeout]删除值并返回127.0.0.1:6379>lpopk"v5"##删除值(尾部)#################################################rpop键[timeout]删除值并返回127.0.0.1:6379>rpopk"v6"##获取长度################################################127.0.0.1:6379>llenk(integer)4##移除指定值#################################################lrem键数量值(移除多个数量时,是移除相同值)127.0.0.1:6379>lremk1v1(integer)1##修剪#################################################ltrim键01(修剪后,原来数据列表数据被改变)127.0.0.1:6379>ltrimk01OK(integer)1##修改值(替换值)#################################################指定下标,替换值127.0.0.1:6379>lrangek0-11)"v4"2)"v3"127.0.0.1:6379>lsetk3v2#修改值(error)ERRindexoutofrange#超出索引127.0.0.1:6379>lsetk1v2#修改值OK127.0.0.1:6379>lrangek0-11)"v4"2)"v2"##插入值#################################################linsert键before|after值插入值(某个值之前|之后添加)127.0.0.1:6379>linsertkbeforev2v3(integer)3127.0.0.1:6379>lrangek0-11)"v4"2)"v3"3)"v2"127.0.0.1:6379>linsertkafterv2v5(integer)4127.0.0.1:6379>lrangek0-11)"v4"2)"v3"3)"v2"4)"v5"127.0.0.1:6379>Set集合Set里面的值,是无序不重复的##添加值################################################127.0.0.1:6379>saddkv1(integer)1127.0.0.1:6379>saddkv2(integer)1##查看所有值################################################127.0.0.1:6379>smembersk1)"v2"2)"v1"##值是否存在################################################127.0.0.1:6379>sismemberkv5(integer)0127.0.0.1:6379>sismemberkv2(integer)1##获取Set集合元素个数################################################127.0.0.1:6379>scardk(integer)2##移除元素################################################127.0.0.1:6379>sremkv2(integer)1127.0.0.1:6379>smembersk1)"v1"##获取值(随机)#################################################srandmemberk[count]Set无序不重复,所以是随机获取127.0.0.1:6379>srandmemberk"v4"127.0.0.1:6379>SRANDMEMBERk"v3"127.0.0.1:6379>SRANDMEMBERk"v3"##删除值(随机)#################################################spopk[count]127.0.0.1:6379>spopk"v4"##移动值#################################################SMOVE源集合目标集合值(移动Set1元素到Set2)127.0.0.1:6379>SMOVEkk1v1(integer)1使用技巧交集,并集,差集127.0.0.1:6379>sdiffk1k2#差集1)"v1"127.0.0.1:6379>127.0.0.1:6379>sinterk1k2#交集(emptylistorset)127.0.0.1:6379>sunionk1k2#并集1)"v1"Hash哈希存储形式(Key-Map集合)Hash更适用于经常变更的数据,尤其用户数据之类的.Hash适用于对象的存储(用户、姓名、性别)String更适用于字符的存储##单个设置值################################################127.0.0.1:6379>hsetkhash1value1(integer)1##批量设置值################################################127.0.0.1:6379>HMSETkhash1value1hash2value2OK##查看所有Hash################################################127.0.0.1:6379>hgetallk1)"hash1"2)"value1"3)"hash2"4)"value2"##删除单个|多个Hash#################################################hdel键Map键(可以多个)127.0.0.1:6379>hdelkhash1(integer)1##Hash字段数量################################################127.0.0.1:6379>hlenk(integer)1##某个Hash是否存在#################################################127.0.0.1:6379>hexistskhash1(integer)0#不存在127.0.0.1:6379>hexistskhash2(integer)1#存在127.0.0.1:6379>##获取Hash所有key&value#################################################127.0.0.1:6379>hkeysk1)"hash2"127.0.0.1:6379>hvalsk1)"value2"127.0.0.1:6379>##Hash自增|自减################################################127.0.0.1:6379>hsetkhash1(integer)1127.0.0.1:6379>hincrbykhash1#增加(integer)2127.0.0.1:6379>hincrbykhash-1#减少(integer)1Zset有序集合在Set基础上,增加一个顺序段##添加值#################################################zadd键score值127.0.0.1:6379>zaddk1value1(integer)1127.0.0.1:6379>zaddk2value23value3(integer)2127.0.0.1:6379>##排序################################################127.0.0.1:6379>zaddk55value244value311value1(integer)3127.0.0.1:6379>zrevrangek0-1#大~小1)"value2"2)"value3"3)"value1"127.0.0.1:6379>zrangek0-1#小~大1)"value1"2)"value3"3)"value2"127.0.0.1:6379>##通过score段排序#################################################zrangebyscore键小值大值[withscores](小~大排序)127.0.0.1:6379>zaddk55value244value311value1(integer)3127.0.0.1:6379>zrangebyscorek-inf+inf#无穷小无穷大1)"value1"2)"value3"3)"value2"127.0.0.1:6379>ZRANGEBYSCOREk-inf+infwithscores1)"value1"2)"11"3)"value3"4)"44"5)"value2"6)"55"127.0.0.1:6379>##移除元素#################################################zremkmember[可以多个]通过member段移除127.0.0.1:6379>zremkvalue1(integer)1127.0.0.1:6379>zrangek0-11)"value3"2)"value2"##获取元素个数################################################127.0.0.1:6379>zcardk(integer)2##元素区间统计#################################################zcountkminmax127.0.0.1:6379>zcountk4466(integer)2使用技巧带权重元素的使用:1重要消息,2普通消息成绩表、工资表排行榜的实现
介绍Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。官网:https://redis.io/中文官网:http://www.redis.cn/安装Windows安装Redis不推荐在Windows上使用,但可用以学习msi是安装程序,为了方便Linux上手下载压缩包下载地址:https://github.com/tporadowski/redis/releases解压点击redis-server.exe启动服务器点击redis-cli.exe启动客户端Linux安装==待补==性能测试快速测试直接点击redis-benchmark.exe带参测试redis-benchmark.exe-n10000-c50-q相关参数WindowsRidis可视化国内下载地址:https://gitee.com/qishibo/AnotherRedisDesktopManager/releases官方中文地址:https://github.com/qishibo/AnotherRedisDesktopManager/blob/master/README.zh-CN.md小知识Redis小知识&问题【遇到补】默认端口6379默认没有密码,密码在配置文件requirepass[密码]设置,需要重启生效默认有16个数据(0~15)基础命令命令官网:http://www.redis.cn/commands.html127.0.0.1:6379>ping#测试连通性PONG127.0.0.1:6379>SELECT2#切换数据库OK127.0.0.1:6379[2]>setnameI#设置K-VOK127.0.0.1:6379[2]>getna#K获取V(nil)127.0.0.1:6379[2]>getname"I"127.0.0.1:6379[2]>keys*#获取所有K1)"name"127.0.0.1:6379[2]>dbsize#当前数据库大小(integer)1127.0.0.1:6379[2]>flushdb#清空当前数据库OK127.0.0.1:6379[2]>dbsize(integer)0127.0.0.1:6379[2]>flushall#清空所有数据库OK127.0.0.1:6379>setnamennnOK127.0.0.1:6379>EXISTSname#检查K存在(integer)1127.0.0.1:6379>EXISTSna(integer)0127.0.0.1:6379>EXPIREname5#设置K-V过期时间(秒)(integer)1127.0.0.1:6379>ttlname#查看剩余过期时间(integer)-2#不存在(过期自动删除)127.0.0.1:6379>setnamennnOK127.0.0.1:6379>movename5#移动K-V到5号数据库(integer)1127.0.0.1:6379>setnamennnOK127.0.0.1:6379>typename#检查类型string
介绍用于Python的下一代HTTP客户端。支持同步&异步支持HTTP/1.1和HTTP/2支持HTTP&HTTPS代理还不支持SOCKS代理【aiohttp支持】需要Python3.6+安装pip安装$pipinstallhttpxHTTP/2支持pipinstallhttpx[http2]同步跟requests基本一样r=httpx.get('https://httpbin.org/get')r>>><Response[200OK]>同步客户端(会话)与使用顶级API相比,这可以带来显着的性能提升,包括:减少请求之间的延迟(无握手)。减少CPU使用率和往返次数。减少网络拥塞。还支持顶级API中不可用的功能,例如:跨请求的Cookie持久性。跨所有传出请求应用配置。通过HTTP代理发送请求。使用HTTP/2。importhttpx#上下文自动关闭会话withhttpx.Client()asclient:res=client.get(url='https://bigdataboy.cn')print(res)>>><Response[200OK]>#手动关闭会话client=httpx.Client()try:...finally:client.close()异步异步需要httpx.AsyncClient()importhttpximportasyncio#上下文方式asyncdefmain():asyncwithhttpx.AsyncClient()asclient:r=awaitclient.get('https://bigdataboy.cn/')print(r)#普通方式asyncdefmain():client=httpx.AsyncClient()r=awaitclient.get('https://bigdataboy.cn/')print(r)awaitclient.aclose()asyncio.run(main())>>><Response[200OK]>高级使用同步普通写法,同步上下文写法,异步普通写法,异步上下文写法都是一样的用法,不一样的地方会标注请求httpx.get('https://bigdataboy.cn/',params=params)#data:str&表单数据json:json数据content:字节数据httpx.post('https://bigdataboy.cn/',data='',json={},content=b'')httpx.put('https://bigdataboy.cn/',data={'key':'value'})httpx.delete('https://bigdataboy.cn/')httpx.head('https://bigdataboy.cn/')httpx.options('https://bigdataboy.cn/')响应#请求urlr.url#文本显示r.text#Json响应r.json()#解码方式r.encoding#设置解码方式r.encoding='utf-8'#字节响应r.content#cookiesr.cookiesr.cookies[xxx]#响应状态码r.status_code#响应headersr.headers#检查http版本r.http_version流式响应#二进制流式响应【同步模式】withhttpx.stream("GET","https://www.example.com")asr:fordatainr.iter_bytes():print(data)#文本流式响应【同步模式】withhttpx.stream("GET","https://www.example.com")asr:forlineinr.iter_lines():print(line)#二进制流式响应【异步模式】client=httpx.AsyncClient()asyncwithclient.stream('GET','https://www.example.com/')asresponse:asyncforchunkinresponse.aiter_bytes():#response.aiter_text()文本流式响应#response.aiter_raw()用于流式传输原始响应字节,而不应用内容解码。Cookiecookies={"k":"v"}httpx.get('https://httpbin.org/cookies',cookies=cookies)#或者cookies=httpx.Cookies()cookies.set('cookie_on_domain','hello,there!',domain='httpbin.org')cookies.set('cookie_off_domain','nope.',domain='example.org')httpx.get('http://httpbin.org/cookies',cookies=cookies)代理还不支持SOCKS代理#str型withhttpx.Client(proxies="http://localhost:8030")asclient:...#字典型proxies={"http://":"http://localhost:8030","https://":"http://localhost:8031",}withhttpx.Client(proxies=proxies)asclient:...重定向httpx默认不会进行重定向跳转#不会重定向跳转r=httpx.get('https://www.bigdataboy.cn/')r.history#[]r.next_request#<Request('GET','https://bigdataboy.cn/')>#开启重定向跳转r=httpx.get('https://www.bigdataboy.cn/',follow_redirects=True)r.history#[<Response[301MovedPermanently]>]r.history[0]#<Response[301MovedPermanently]>r.history[0].url#https://www.bigdataboy.cn/r.next_request#None启用HTTP/2需要服务端支持HTTP/2才有用client=httpx.AsyncClient(http2=True)事件挂钩httpx.AsyncClient()需要异步钩子函数目前支持两种事件:request:在请求完全准备好之后,但在它被发送到网络之前调用。response:在从网络获取响应之后但在返回给调用者之前调用。deflog_request(request):print(f"Requesteventhook:{request.method}{request.url}-Waitingforresponse")deflog_response(response):request=response.requestprint(f"Responseeventhook:{request.method}{request.url}-Status{response.status_code}")client=httpx.Client(event_hooks={'request':[log_request],'response':[log_response]})事件允许对request&response进修改defadd_timestamp(request):request.headers['x-request-timestamp']=datetime.now(tz=datetime.utc).isoformat()client=httpx.Client(event_hooks={'request':[add_timestamp]})
主要特点支持异步客户端和异步服务端支持开箱即用的WebSocket服务端和WebSocket客服端服务端还支持中间件(Middlewares)和信号(Signals)初始安装pipinstallaiohttp客户端importasyncioimportaiohttpasyncdefmain():asyncwithaiohttp.ClientSession()assession:res=awaitsession.get(url='https://bigdataboy.cn')print(res)if__name__=='__main__':loop=asyncio.get_event_loop()loop.run_until_complete(main())服务端本文不会介绍fromaiohttpimportwebasyncdefhandle(request):name=request.match_info.get('name',"bigdataboy")text="Hello,"+namereturnweb.Response(text=text)app=web.Application()app.add_routes([web.get('/',handle),web.get('/{name}',handle)])if__name__=='__main__':web.run_app(app=app,host='127.0.0.1',port=8080)快速上手发出请求importasyncio,aiohttpasyncdefmain():asyncwithaiohttp.ClientSession()assession:#会话上下文asyncwithsession.get('http://httpbin.org/get')asresp:#请求上下文print(resp.status)print(awaitresp.text())asyncio.run(main())其他请求方式session.post('http://httpbin.org/post',data=b'data')session.put('http://httpbin.org/put',data='data')session.delete('http://httpbin.org/delete')session.head('http://httpbin.org/get')session.options('http://httpbin.org/get')session.patch('http://httpbin.org/patch',data='data')为了请求同一个网站更方便asyncwithaiohttp.ClientSession(base_url='http://httpbin.org')assession:asyncwithsession.get(url='/get')asresp:print(resp.status)#或调用其他函数asyncwithsession.post(url='/post',data='data')asresp:print(resp.status)asyncwithsession.put(url='/put',data='data')asresp:print(resp.status)提示&技巧一个站点的使用一个会话,==不要==为一个请求创建一个会话会话内部包含一个连接池。连接重用和保持活动(默认情况下都打开)可以提高整体性能。不使用上下文形式需要收到关闭会话session=aiohttp.ClientSession()asyncwithsession.get('...'):#...awaitsession.close()参数传递#GETparams={'k':'v'}params=[('k','v'),('k1','v1')]asyncwithsession.get(url='/get',params=params)asresp:pass#POSTdata='str'asyncwithsession.post(url='/post',data=data)asresp:passjson={'k':'v'}asyncwithsession.post(url='/post',json=json)asresp:pass响应内容asyncwithsession.get('https://bigdataboy.cn')asresp:#状态码resp.status#文本解码awaitresp.text(encoding='utf-8')#指定解码方式#json解码awaitresp.json()#二进制解码awaitresp.read()流式响应内容当响应文件过于庞大,使用read()、json()、text()会把内容全部加载到内存,这是愚蠢的做法,应该使用流式响应。asyncwithsession.get('https://api.github.com/events')asresp:withopen('./xx','wb')asfd:chunk_size=10asyncforchunkinresp.content.iter_chunked(chunk_size):fd.write(chunk)网络套接字(WebSocket)#使用小蝌蚪聊天室测试asyncwithsession.ws_connect('ws://kedou.workerman.net:8280/')asws:asyncformsginws:print(msg)awaitws.send_str('1122')#发送数据#awaitws.send_json()#awaitws.send_json()超时#单位秒默认超时300秒timeout=aiohttp.ClientTimeout(total=60)asyncwithaiohttp.ClientSession(timeout=timeout)assession:...#会覆盖session设置的超时asyncwithsession.get(url,timeout=timeout)asresp:...自定义请求头#会话请求头headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/96.0.4664.110Safari/537.36'}asyncwithaiohttp.ClientSession(headers=headers)assession:...#单独设置会合并会话请求头asyncwithsession.get(url='http://httpbin.org/headers',headers=headers)asresp:...自定义Cookieurl='http://httpbin.org/cookies'cookies={'cookies_are':'working'}asyncwithaiohttp.ClientSession(cookies=cookies)assession:#添加Cookiessession.cookie_jar.update_cookies(cookies={'k1':'v1'})#单独设置会合并到会话Cookiesasyncwithsession.get(url,cookies={'k':'v'})asresp:res=awaitresp.json()print(res)重定向禁止重定向allow_redirects=Falseasyncwithaiohttp.ClientSession()assession:asyncwithsession.get(url)asresp:print(resp.history)#重定向历史元祖print(resp.history[0])print(resp.history[0].url)代理aiohttp还不能很好的支持https代理#HTTP代理asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(url=url,proxy='http://127.0.0.1:7890')asresp:print(resp.status)#授权代理asyncwithaiohttp.ClientSession()assession:proxy_auth=aiohttp.BasicAuth('user','pass')asyncwithsession.get("http://python.org",proxy="http://127.0.0.1:7890",proxy_auth=proxy_auth)asresp:print(resp.status)#socks代理pipinstallaiohttp_socksfromaiohttp_socksimportProxyConnectorconn=ProxyConnector.from_url('socks5://127.0.0.1:7890')asyncwithaiohttp.ClientSession(connector=conn,headers=headers)assession:...小技巧推荐写法importaiohttpimportasyncioasyncdeffetch(session,url):asyncwithsession.get(url)asresponse:returnawaitresponse.text()asyncdefmain():asyncwithaiohttp.ClientSession()assession:#此步html=awaitfetch(session,'https://bigdataboy.cn')print(html)loop=asyncio.get_event_loop()loop.run_until_complete(main())
什么是协程协程不是计算机提供的,是人为创造的(多线程、多进程就是计算机提供)协程(Coroutine),可以认为是微线程,是一种用户态内的上下文切换技术,简单理解就是,遇到IO耗时操作时,切换到其他代码块继续执行的技术。Py协程实现的方式greenlet,早期的第三方模块yield关键字asyncio装饰器(@asyncio.coroutine)【py3.4】async、await关键字【py3.5】【推荐】异步编程事件循环可以理解成一个死循环,去检查并执行某些代码。#伪代码任务列表=[任务1,任务2,任务3]whileTrue:#将'可执行'和'已完成'的任务返回可执行的任务列表,已完成的任务列表=去任务列表中检查所有的任务for就绪任务in可执行的任务列表:'执行'已就绪的任务for已完成的任务in已完成的任务列表:在任务列表中'移除'已完成的任务如果任务列表中的任务'都已完成',则'终止'循环importasyncio#生成或获取一个事件循环loop=asyncio.get_event_loop()#把任务放入事件循环loop.run_until_complete(task_list)快速上手协程函数:定义函数时asyncdef函数名(不是普通的函数了)importasyncioasyncdeffun():passresult=fun()#事件对象(Py3.7以前)【两种写法都有应用场景】#loop=asyncio.get_event_loop()#loop.run_until_complete(result)#事件对象asyncio.run(result)#Py3.7以后支持await关键字await后面只可以跟(协程对象、Task对象、Future对象)也可以理解为等待耗时操作,在这个等待的时间可以去执行其他任务。importasynciofromloguruimportloggerasyncdeffun():logger.info(f'开始')awaitasyncio.sleep(2)logger.info(f'结束')#两个协程对象任务tasks=[fun(),fun(),]asyncio.run(asyncio.wait(tasks))常规执行两次fun()需要四秒,使用协程只需要两秒Task对象理解:可以向事件循环里添加任务的对象。Task用于并发调度协程,使用asyncio.create_task(协程对象,...)的方式创建Task对象,这样就可以加入到事件循环等待调度。还能使用更低一级的loop.create_task()或者ensure_future()创建,不建议手动实例化Task函数。importasynciofromloguruimportloggerasyncdeffun():logger.info(f'开始')awaitasyncio.sleep(2)logger.info(f'结束')#执行两次`fun()`用时两秒asyncdefmain():task1=asyncio.create_task(fun())task2=asyncio.create_task(fun())awaittask1awaittask2asyncio.run(main())Task的返回值与nameimportasynciofromloguruimportloggerasyncdefio():logger.debug('io执行中...')awaitasyncio.sleep(2)logger.debug('io操作完成...')asyncdeffun():awaitio()returnTrueasyncdefmain():tasks=[asyncio.create_task(fun(),name='0'),asyncio.create_task(fun(),name='1'),]done,padding=awaitasyncio.wait(tasks,timeout=None)logger.info(f'done={done}')logger.info(f'padding={padding}')if__name__=='__main__':asyncio.run(main())Future可等待对象Task继承Future,Task对象内部await结果的处理基于Future对象而来。使用loop.create_future()来创建Future对象。Future的特性:awaitfuture等待future结果,future没有结果则一直等待importasynciofromloguruimportloggerasyncdeffun(fut):#设置fut值fut.set_result('xxx')asyncdefmain():#获取当前时间循环下面的runloop=asyncio.get_event_loop()#创建future对象fut=loop.create_future()#创建Task对象,通过'fun()'给fut赋值awaitasyncio.create_task(fun(fut))#注释掉fut一直等待#等待fut结果,fut没有结果则一直等待data=awaitfutlogger.info(data)asyncio.run(main())异步迭代器异步迭代器:实现了__aiter__()和__anext__()方法的对象,必须返回一个awaitable对象。asyncfor支持处理异步迭代器的__anext__()方法返回的可等待对象,直到引发一个stopAsyncIteration异常异步可迭代对象:可在asyncfor语句中被使用的对象,必须通过它的__aiter__()方法返回一个asynchronous_iterator(异步迭代器)importasyncioclassReader(object):def__init__(self):self.count=0#返回自己def__aiter__(self):returnself#迭代asyncdef__anext__(self):self.count+=1ifself.count==5:raiseStopAsyncIteration#迭代完成returnself.countasyncdefmain():reader=Reader()asyncforiteminreader:print(item)if__name__=='__main__':asyncio.run(main())异步上下文管理器上下文管理器:withopen操作,实现了\_\_enter__(),\_\_exit__()。异步上下文管理器:通过定义__aenter__()和__aexit__()方法来对asyncwith语句中的环境进行控制的对象。importasynciofromloguruimportloggerclassAsyncContextManager(object):asyncdefdo(self):logger.debug(f'操作数据库')asyncdef__aenter__(self):logger.debug(f'连接数据库')returnselfasyncdef__aexit__(self,exc_type,exc_val,exc_tb):logger.debug(f'关闭数据库')asyncdefmain():asyncwithAsyncContextManager()asacm:awaitacm.do()if__name__=='__main__':asyncio.run(main())