ZHANGYONGHAO0 2020-06-17
Memcached(一般大公司会做二次开发再使用) Redis Tair(淘宝所使用的缓存产品,基于Memcache进行二次开发诞生的产品)
数据类型丰富(也就是存储结构不一样) (笔试、面试) 支持持久化 (笔试、面试) 多种内存分配及回收策略 支持弱事务 (面试) 消息队列、消息订阅 支持高可用 支持分布式分片集群 (面试) 缓存穿透,缓存雪崩 (笔试、面试) Redis API
(1)Memcached 优点:高性能读写、单一数据类型、支持客户端分布式集群、一致性hash、多核结构、多线程读写,性能高。 缺点:无持久化、节点故障可能出现缓存穿透、分布式需要客户端实现、跨机房数据同步困难、架构扩容复杂度高。 (2)Redis 优点:高性能读写、多数据类型支持、数据持久化、高可用架构、支持自定义虚拟内存、支持分布式分片集群、单线程读写性能极高。 缺点:多线程读写较Memcached慢。 (3)Memcache与Redis在读写性能的对比 Memcached:适合多用户访问,每个用户少量的读写。 Redis:适合少用户访问,每个用户大量的读写。 (4)Tair 优点:高性能读写、支持三种存储引擎(ddb、rdb、ldb)、支持高可用、支持分布式分片集群、支撑了几乎所有淘宝业务的缓存。 缺点:单机情况下,读写性能较其他两种产品较慢。
(1)Memcached:多核的缓存服务,更加适合于多用户并发访问次数较少的应用场景。 (2)Redis:单核的缓存服务,单节点情况下,更加适合于少量用户,多次访问的应用场景,Redis一般是单机多实例架构,配置redis集群出现。
(1)下载源码包 wget http://download.redis.io/releases/redis-3.2.12.tar.gz (2)解压 tar zxf redis-3.2.12.tar.gz ln -s redis-3.2.12 redis (3)安装 yum -y install gcc automake autoconf libtool make cd redis make (4)设置环境变量 cat >> /etc/profile << EOF export PATH=/data/redis/src:$PATH EOF source /etc/profile (5)启动 redis-server & (6)连接测试(redis默认只能本地登录) redis-cli 127.0.0.1:6379> set num 10 OK 127.0.0.1:6379> get num "10"
(1)编写配置文件 mkdir /data/6379 cat > /data/6379/redis.conf <<EOF daemonize yes port 6379 logfile /data/6379/redis.log dir /data/6379 dbfilename dump.rdb EOF (2)应用配置文件 redis-cli shutdown redis-server /data/6379/redis.conf redis-cli 127.0.0.1:6379> set name zhangsan OK 127.0.0.1:6379> get name "zhangsan"
daemonize yes 是否后台运行 port 6379 默认端口号 logfile /var/log/redis.log 日志文件位置 dir /data/6379 持久化文件存储位置 dbfilename dump.rdb RDB持久化数据文件
redis默认开启了保护模式,只允许本地回环地址登录并访问数据库。
[ ~]# redis-cli -h 192.168.1.51 -p 6379 192.168.1.51:6379> keys * (error) DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command ‘CONFIG SET protected-mode no‘ from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to ‘no‘, and then restarting the server. 3) If you started the server manually just for testing, restart it with the ‘--protected-mode no‘ option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside. 这里我们着重关注第四种 (1)配置方法 [ ~]# cat >> /data/6379/redis.conf << EOF bind 192.168.1.51 127.0.0.1 requirepass 123456 EOF [ ~]# tail /data/6379/redis.conf daemonize yes port 6379 logfile /data/6379/redis.log dir /data/6379 dbfilename dump.rdb bind 192.168.1.51 127.0.0.1 requirepass 123456 (2)重启redis让配置文件生效 [ ~]# redis-cli shutdown [ ~]# redis-server /data/6379/redis.conf (3)再次测试,远程登录后能否操作 [ ~]# redis-cli -h 192.168.1.51 -p 6379 192.168.1.51:6379> set alex sb (error) NOAUTH Authentication required.(这里又报错了,解决办法见下方) (4)解决方法 [ ~]# redis-cli -h 192.168.1.51 -p 6379 192.168.1.51:6379> set alex sb (error) NOAUTH Authentication required. 192.168.1.51:6379> AUTH 123456 OK 192.168.1.51:6379> set alex sb OK 192.168.1.51:6379> get alex "sb" [ ~]# redis-cli -h 192.168.1.51 -p 6379 -a 123456 192.168.1.51:6379> get alex "sb" 192.168.1.51:6379>
(1)查询所有配置信息 127.0.0.1:6379> CONFIG GET * (2)查询单个配置信息 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "123456" (3)模糊查询某个配置 127.0.0.1:6379> config get r* 1) "requirepass" 2) "123456" 3) "repl-ping-slave-period" 4) "10" 5) "repl-timeout" 6) "60" 7) "repl-backlog-size" 8) "1048576" 9) "repl-backlog-ttl" 10) "3600" 11) "repl-diskless-sync-delay" 12) "5" 13) "rdbcompression" 14) "yes" 15) "rdbchecksum" 16) "yes" 17) "repl-disable-tcp-nodelay" 18) "no" 19) "repl-diskless-sync" 20) "no" (4)在线修改配置 127.0.0.1:6379> config set requirepass 123 OK 127.0.0.1:6379> exit [ ~]# redis-cli -a 123 127.0.0.1:6379> keys * 1) "alex"
redis持久化分为RDB、AOF。 (1)RDB 持久化 可以在指定的时间间隔内生成数据集的 时间点快照(point-in-time snapshot)。 优点:速度快,适合于用做备份,主从复制也是基于RDB持久化功能实现的。 缺点:会有数据丢失 rdb持久化核心配置参数: vim /data/6379/redis.conf dir /data/6379 dbfilename dump.rdb save 900 1 save 300 10 save 60 10000 配置分别表示: 900秒(15分钟)内有1个更改 300秒(5分钟)内有10个更改 60秒内有10000个更改 (2)AOF 持久化(append-only log file) 记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾,类似于mysql的binlog日志文件。 优点:可以最大程度保证数据不丢 缺点:日志记录量级比较大 AOF持久化配置 appendonly yes *是否打开aof日志功能 appendfsync always *每1个命令,都立即同步到aof appendfsync everysec *每秒写1次 appendfsync no *写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof. 配置方式: vim /data/6379/redis.conf appendonly yes appendfsync everysec 配置生效: redis-cli -a 123 shutdown redis-server /data/6379/redis.conf 面试: redis 持久化方式有哪些?有什么区别? rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能 aof:以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,类似于mysql的binlog
String:字符类型 Hash:字典类型(缓存专用) List:列表 Set:集合 Sorted set:有序集合
KEYS *、KEYS a、KEYS a*。 查看已存在所有键的名字。 TYPE 返回键所存储值的类型。 EXPIRE、PEXPIRE 以秒、毫秒设定生存时间。 TTL、PTTL 以秒、毫秒为单位返回生存时间。 PERSIST 取消生存时间设置。 DEL 删除一个key。 EXISTS 检查是否存在。 RENAME 变更KEY名。 实操举例: (1)查询redis中有多少个KEY [ ~]# redis-cli -a 123456 127.0.0.1:6379> keys * #生产中禁止使用 1) "aa" 2) "a" (2)查询以a开头的KEY 127.0.0.1:6379> keys a* 1) "aa" 2) "a" (3)单独查询一个KEY 127.0.0.1:6379> keys a 1) "a" (4)查询某一个键的数据类型 127.0.0.1:6379> type a string (5)以秒\毫秒为单位设定键值的生存时间 127.0.0.1:6379> EXPIRE a 60 (设置该键的生存时间为60秒) (integer) 1 (6)以秒\毫秒为单位返回生存时间(查询某个键值的生存时间) 127.0.0.1:6379> ttl a (integer) -1(-1表示永不过期) 127.0.0.1:6379> ttl a (integer) -2(-2表示该key不存在) (7)取消生存时间设置 127.0.0.1:6379> EXPIRE alex 60 (integer) 1 127.0.0.1:6379> ttl alex (integer) 50 127.0.0.1:6379> PERSIST alex(取消键的生存时间) (integer) 1 127.0.0.1:6379> ttl alex (integer) -1 (8)删除一个key 127.0.0.1:6379> del alex (integer) 1 (9)检查键key是否存在 127.0.0.1:6379> exists alex (integer) 0 (10)变更key名 127.0.0.1:6379> keys * 1) "a" 127.0.0.1:6379> rename a b OK 127.0.0.1:6379> keys * 1) "b"
存储方式:key:value 应用场景如下: session 共享 常规计数(如:微博数、粉丝数、订阅数、礼物数) ---------- (1) set name zhangsan (2) MSET id 101 name zhangsan age 20 gender m 等价于以下操作: SET id 101 set name zhangsan set age 20 set gender m (3)计数器 每点一次关注,都执行以下命令一次 127.0.0.1:6379> incr num(incr命令执行一次,key的数量+1) 显示粉丝数量: 127.0.0.1:6379> get num 暗箱操作: 127.0.0.1:6379> INCRBY num 10000(给某一个key加指定的数量) (integer) 10006 127.0.0.1:6379> get num "10006" 127.0.0.1:6379> DECRBY num 10000(给某一个key减去相应的数量) (integer) 6 127.0.0.1:6379> get num "6" 详细的例子:------------------------------------ 增 set mykey "test" 为键设置新值,并覆盖原有值 getset mycounter 0 设置值,取值同时进行 setex mykey 10 "hello" 设置指定 Key 的过期时间为10秒,在存活时间可以获取value setnx mykey "hello" 若该键不存在,则为键设置新值 mset key3 "zyx" key4 "xyz" 批量设置键 删 del mykey 删除已有键 改 append mykey "hello" 若该键并不存在,返回当前 Value 的长度 该键已经存在,返回追加后 Value的长度 incr mykey 值增加1,若该key不存在,创建key,初始值设为0,增加后结果为1 decrby mykey 5 值减少5 setrange mykey 20 dd 把第21和22个字节,替换为dd, 超过value长度,自动补0 查 exists mykey 判断该键是否存在,存在返回 1,否则返回0 get mykey 获取Key对应的value strlen mykey 获取指定 Key 的字符长度 ttl mykey 查看一下指定 Key 的剩余存活时间(秒数) getrange mykey 1 20 获取第2到第20个字节,若20超过value长度,则截取第2个和后面所有的 mget key3 key4 批量获取键
存数据: hmset stu id 101 name zhangsan age 20 gender m hmset stu1 id 102 name zhangsan1 age 21 gender f 取数据: hmget stu id name age gender hmget stu1 id name age gender 应用场景: 存储部分变更的数据,如用户信息等。 最接近mysql表结构的一种数据类型,主要是可以做数据库缓存。
MySQL数据转换到Redis演示
(1)先在mysql中用拼接语句查找出需要的数据 select concat("hmset t100w_",id," id ",id," num ",num," k1 ",k1," k2 ",k2," dt "," ‘ ",dt," ‘ ") from t100w where id<100 into outfile ‘/tmp/aa.txt‘; (2)把aa.txt里面的数据导入到redis中 cat /tmp/aa.txt | redis-cli
应用场景: 消息队列系统 比如新浪微博 在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。 但是做了限制不能超过5000个ID,因此获取ID的函数会一直询问Redis。 只有在start/count参数超出了这个范围的时候,才需要去访问数据库。 系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。 SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发。 而主页或第一个评论页是不会麻烦到硬盘上的数据库了。
模拟朋友圈(列表)
(1)插入数据 127.0.0.1:6379> lpush pyq "jin tian shi zhou 1" (integer) 1 127.0.0.1:6379> lpush pyq "jin tian shi zhou 2" (integer) 2 127.0.0.1:6379> lpush pyq "jin tian shi zhou 3" (integer) 3 127.0.0.1:6379> lpush pyq "jin tian shi zhou 4" (integer) 4 127.0.0.1:6379> lpush pyq "jin tian shi zhou 5" (integer) 5 (2)查询数据 显示所有 127.0.0.1:6379> lrange pyq 0 -1 1) "jin tian shi zhou 5" 2) "jin tian shi zhou 4" 3) "jin tian shi zhou 3" 4) "jin tian shi zhou 2" 5) "jin tian shi zhou 1" 显示前最近两条 127.0.0.1:6379> lrange pyq 0 1 1) "jin tian shi zhou 5" 2) "jin tian shi zhou 4" 显示前最近三条 127.0.0.1:6379> lrange pyq 0 2 1) "jin tian shi zhou 5" 2) "jin tian shi zhou 4" 3) "jin tian shi zhou 3"
应用场景: 案例:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有的粉丝存在一个集合中。 Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能。 对上面的所有集合,还可以使用不同的命令选择结果返回给客户端还是存集到一个新的集合中。 实操演示: 127.0.0.1:6379> sadd lxl pg1 jnl baoqiang gsy alexsb (integer) 5 127.0.0.1:6379> sadd inl baoqiang ms bbh yf wxg (integer) 5 127.0.0.1:6379> sunion lxl inl #查两个key的值(并集) 1) "ms" 2) "bbh" 3) "gsy" 4) "baoqiang" 5) "yf" 6) "jnl" 7) "pg1" 8) "wxg" 9) "alexsb" 127.0.0.1:6379> sinter lxl inl #查询两个key都有的值(交集) 1) "baoqiang" 127.0.0.1:6379> sdiff inl lxl #查询inl有的值,lxl没有的值(差集) 1) "yf" 2) "bbh" 3) "ms" 4) "wxg" 127.0.0.1:6379> sdiff lxl inl #查询lxl有的值,inl没有的值(差集) 1) "alexsb" 2) "gsy" 3) "jnl" 4) "pg1"
应用场景: 适合排行榜应用,取TOP N操作 这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序。 这个时候就需要我们的sorted set出马了,将需要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。 操作演示: 127.0.0.1:6379> zadd topN 0 fskl 0 lg 0 ll 0 xs 0 smlt 0 oldboy 0 oldguo #模拟歌曲排行,刚开始上架没有点击量,所以都为0. (integer) 7 127.0.0.1:6379> ZINCRBY topN 100 fskl #为fskl增加100点击量,下面的以此类推 "100" 127.0.0.1:6379> zincrby topN 1000 lg "1000" 127.0.0.1:6379> zincrby topN 7898 ll "7898" 127.0.0.1:6379> zincrby topN 1222 xs "1222" 127.0.0.1:6379> zincrby topN 99999 smlt "99999" 127.0.0.1:6379> zincrby topN 100000 oldboy "100000" 127.0.0.1:6379> zincrby topN 1000000000 oldguo "1000000000" 127.0.0.1:6379> ZREVRANGE topN 0 2 WITHSCORES #查询排名前三的歌曲,并显示点击量 1) "oldguo" 2) "1000000000" 3) "oldboy" 4) "100000" 5) "smlt" 6) "99999"
PUBLISH channel msg 将信息 message 发送到指定的频道 channel SUBSCRIBE channel [channel ...] 订阅频道,可以同时订阅多个频道 UNSUBSCRIBE [channel ...] 取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道 PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所 有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有 以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类 PUNSUBSCRIBE [pattern [pattern ...]] 退订指定的规则, 如果没有参数则会退订所有规则 PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态 注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,之前发送的不会缓存,必须Provider和Consumer同时在线。 发布订阅例子: 窗口1: 127.0.0.1:6379> SUBSCRIBE baodi 窗口2: 127.0.0.1:6379> PUBLISH baodi "jin tian zhen kaixin!" 订阅多频道: 窗口1: 127.0.0.1:6379> PSUBSCRIBE wang* 窗口2: 127.0.0.1:6379> PUBLISH wangbaoqiang "jintian zhennanshou "
redis的事务是基于队列实现的。 mysql的事务是基于事务日志和锁机制实现的。 redis是乐观锁机制。 mysql是悲观锁机制。 开启事务功能(multi) multi command1 command2 command3 command4 exec discard 4条语句作为一个组,并没有真正执行,而是被放入同一队列中。 如果,这是执行discard,会直接丢弃队列中所有的命令,而不是做回滚。 exec 当执行exec时,对列中所有操作,要么全成功要么全失败 127.0.0.1:6379> set a b OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set a b QUEUED 127.0.0.1:6379> set c d QUEUED 127.0.0.1:6379> exec 1) OK 2) OK redis乐观锁实现(模拟买票) 发布一张票 set ticket 1 窗口1: watch ticket multi set ticket 0 1---->0 窗口2: multi set ticket 0 exec 窗口1: exec
(1)info #显示当前redis的工作情况 主要关注redis内存使用量(redis默认使用全部内存) # Memory used_memory:843736 used_memory_human:823.96K used_memory_rss:8122368 used_memory_rss_human:7.75M #实际使用的内存 used_memory_peak:843736 used_memory_peak_human:823.96K total_system_memory:2076790784 total_system_memory_human:1.93G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction mem_fragmentation_ratio:9.63 mem_allocator:jemalloc-4.0.3 Client list Client kill ip:port config get * CONFIG RESETSTAT 重置统计 CONFIG GET/SET 动态修改 Dbsize #统计键的总数 FLUSHALL 清空所有数据 (生产禁用) select 1 #选择库(0-15) FLUSHDB 清空当前库(生产禁用) MONITOR 监控实时指令 SHUTDOWN 关闭服务器 关闭数据库: redis-cli -a root shutdown