redis
# 概述 (redis.memcache(内存数据库,高速缓存),mongodb(文档数据库)) ###### 入门概述 - Nosql(not noly sql) 数据之间无关系,容易扩展 速度快:读11w,写8w - RDBMS VS NOSQL - Rdbms:acid - Nosql:cap定理,键值对存储, - 组成:Kv,Cache,Persistence ###### 3v+3高 - 海量volume - 多样variety - 实时velocity - 高并发, - 高扩展, - 高性能 # Nosql的经典应用 阿里sql 与nosql商品信息的存放 - 第一代1999:Perl,CGI,Oracle - 2000java时代:JAVA,servlet - 2001-2004:EJB(SLSB,CMP,MDB) pattern(servletLocator,Delegate,Façade,DAO,DTO) - 2005-2007:EJB重构:spring+ibatis+webx,antx(struts前身) - 底层架构:iSearch,MQ+ESB,数据挖掘,CMS - 2008-2009:Memcached集群,mysql+数据切分=cobar - 2010:安全,镜像,应用服务器升级,秒杀,nosql,SSD - 第四代:解决性能和海量数据问题 - 大规模memcached集群,高性能服务器,kv,cnd - 安全问题:镜像站 - 第五代2011:敏捷,开放,体验 去IOE(去除IBM小型机,oracle数据库,EMC存储设备)-云计算 ## Nosql数据模型简介 - Kv键值 - Bson - 列簇 - 图形 ## Nosql数据库的四大分类 - Kv键值 - 新浪:Berkeley+redis - 美团:redis+tair - 阿里,百度:memcache+redis - 文档数据库 - bson:mongodb - 列存储数据库 - Cassandra,Hbase,分布式文件系统 - 图关系数据库 - 社交网络,关系图,neo4j,infoGrid !\[image-20200912203507645\](https://i.loli.net/2020/09/12/W81COBKUJMvulGE.png) !\[image-20200912203529570\](https://i.loli.net/2020/09/12/Gguasz4yDvTPWcC.png) # 在分布式数据库中CAP原理 - ACID - A(Atomicity)原子性 - C(Consistency)一致性 - I(Isolation)独立性 - D(Durability)持久性 - CAP - C(Consistency)强一致性 - A(Availability)可用性 - P(Partition tolerance)分区容错性 - CAP的3选2(注意)不能同时满足三个 - CA:单点集群,一致性,可用性 - CP:一致性,分区容错,性能不是特别高 - AP:可用性,分区容错,对一致性要求低:用的较多 - 经典CAP图 !\[image-20200912203710184\](https://i.loli.net/2020/09/12/SLUhQvctN6yO1Ds.png) - Base - 基本可用(Basically Available) - 软状态(Soft state) - 最终一致(Eventually consistent) - 放松某时刻一致性的要求,由base解决 - 分布式+集群简介 - 分布式:不同服务器:不同服务模块 - 集群:不同服务器:相同服务模块 # Redis入门介绍 ###### 是什么 - Remote dictionary server(远程字典服务) - c语言编写,遵守BSD协议 - Kv分布式存储数据库,基于内存运行,数据结构服务器 - 特点 - 支持持久化 - 不仅支持kv,还提供list,set,zset,hash等 - 支持数据备份,即master-slave模式数据备份 ###### 做什么 - 支持异步将内存数据写入硬盘, - 发布,订阅系统消息mq+nq:消息中间件 - 定时器,计数器 - 最新的消息放在内存中list. ### 怎么做 - 数据类型,基本操作,配置 - 持久化,复制,RDB/AOF - 事务控制 - 灵活性高,有时候事务回滚 - 例如3条语句,回滚2条错的,另一个不会滚,与RDMS不同 - 复制 # Redis安装 下载地址:https://github.com/dmajkic/redis/downloads # Redis启动后杂项基础知识讲解 - 单进程以epoll包装.linux下多路复用io - 默认16个数据库,类似数组下标从0开始.初始默认0号库 - Select命令切换数据库 select 7选择7号库 - Dbsize查看当前数据库key数量 - Flushdb:清空当前库 - Flushall通杀全部库 - 统一密码管理,16个库都是同样密码,要么ok,要么都连不上 - Redis索引都是从0开始 - 默认端口6379-\\\>键盘6379-\\\>merz - Keys d\\\* 显示d开头的key # Redis数据类型 ###### Redis五大数据类型string,hash,list,set,zset - String:一个key一个value,二进制安全,可包含任何数据,比如图片,序列化对象,value最多512M - Hash:类似java中Map, 是一个string类型的filed和value的映射表,适合存储对象 - List:字符串链表,按照插入顺序,可以添加元素到头部,尾部 - Set:实际上是map,string类型无序集合,hashtable实现 - Zset:string类型集合,不重复,每个元素关联一个double分数,通过分数排序,分数可重复 # 获得常见类型操作命令 http://redisdoc.com # Redis key - Key \\\* 查看所有 - Exist key 判断是否存在key(返回0表示无) - Move key db 当前库就没了,剪贴 - Expire key 秒 设置过期时间 - Ttl key 查看还有多少时间过期 -1 不过期 -2 已过期(移除) - Type key 查看key类型 - Del key删除类型(一般是设置过期时间) - Set key 多次会覆盖 # Redis String - 单值单value - Set/get/del/append/strlen:拼接字符,返回长度 - Incr/decr/incrby/decrby,一定要是数字才能进行加减 - Incdecrr k2:把k2的值加1,减1 - Incrby/decrby k2 步长x:每次加减x - Getrange/setrange:获取指定区间的值,类似between and - Getrange key 0 3:获取key的0-3个字符 - Setrange key 2 x:设置第下标2开始到最后为x - Setex(set with expire)键秒值/setnx(set if not exist) - Setx key time value;带时间设置 - Setnx key value:如果存在不再添加 - Mset/mget/msetnx - Mset k1 v1 k2 v2 k3 v3:一次设置多个 - Mget k1 k2 k3:获取,注意不能超出k4 - Msetnx k1 v1 k2 v2 k3 v3 k4 v4:存在的不再插入,不存在的也不再插入 - Getset(先get再set) # Redis list - 单值多value(l在上,r在下) - Lpush/rpush/lrange(已存在是添加是append) \> Lpush list 0 1 2 3 4 5:0-5左边添加list, 正进反出 \> \> lrange list 0 -1:输出 \> \> rpush list 0 1 2 3 4 5:5-0右边添加list,正进正出 - Lpop/rpop \> Lpop list:返回输出第一个,切弹出 \> \> Rpop lsit: 返回输出第一个,切弹出 - Lindex:按照索引下标获得元素 \> Lindex list 2:获取下标2元素 - Llen list:长度 - Lrem key n v1:删除n个v1元素 - Ltrim key start end:截取指定范围给再给key(包括end):截取后赋给自己 - Rpoplpush 源列表 目的列表:把源列表头 给目的列表 - Lset key index value:设置下标index的值 - Linsert key before/after v1:v1前后插入v1 - 若值全部移除,对应键也就消失,头尾操作极高,中间较慢 # Redis set - 单值多value - Sadd/smembers/sismember - Sad set01 1 1 2 2 3 3:只插入3 个,自己会去重 - Smember set:查看 - Sismember set01 2:判断是否存在 - Scard:获得集合里面元素个数 - Srem key value:删除元素value - Srandmember key 3: - Spop key:随机弹出一个 - Smove key1 key2 numINkey1:将key1中的某个值移入key2 - 数学集合类 - 差集:sdiff sdiff set1 set2:返回差集(在第一个里面,不再第二个里面) - 交集:sinter ...... - 并集:sunion..... # Redis hash - Kv模式不变,但v是一个键值对 - Hset/hget/hmset/hmget/hgetall/hdel - Hset user id 11: key --\\\>user value -\\\>id 11 - Hget user id:获取; - Hmset:设置多个 - Hmget:得到多个 - Hgetall:得到所有 - Hdel key value:删除 - Hlen:返回长度 - Exists user id:判断是否存在 - Hkeys/hvals - Hkeys user:得到所有的 key (id name) - Hvals user:得到对应值 - Hincrby/hincrbyfloat - Hincrby user id 2:使id 加2 - Hincrbyfloat ....:改小数的值属性 - Hsetnx:添加,若不存在 # Redis zset(sorted set)有序集合 Set k1 v1 k2 v2 Zset k1 score1(分数) v1 k2 score2 v2(比set多一个分数,用来排序) - Zadd/zrange:两个值是一个整体 - Zrange key 0 -1:取出(v1 v2) - Zrange key 0 -1 withscores:取出(v1 socre1 v2 score2) - Zrangebyscore key score1 score2 小到大:返回范围内的(v1..不带score) - score1 score2:都包含 - (score1 (score2:都不包含 - Limit 2 5:在返回的结果进行截取(从结果下标2开始截取5个) - Zrem key v1:删除v1 - Zcard key:返回数量 v的数量 不带分数 - Zcount key score1 score2:统计分数内个数 - Zrank key v1:返回v1下标 - Zscore key v1:返回v1的score - Zrevrank key v1:逆序获得下标值 - Zrevrange key 0 -1:逆序输出 v1 v2 - Zrevrangebyscore key 90 60(大到小):返回v2 v1 # Units单位 - 1k=1000bytes;1kb=1024bytes; - 不区分大小写;不支持bit; # 配置文件 - Window不用更改,linux要改为yes:守护进程 !\[image-20200912205055983\](https://i.loli.net/2020/09/12/s6l1V4pBDwiWHqJ.png) - 进程管道id文件 windows没有 !\[image-20200912205110285\](https://i.loli.net/2020/09/12/3tQosA6rYVEWLXf.png) - Port 6379 - Tcp-backlog 511 !\[image-20200912205143972\](https://i.loli.net/2020/09/12/MHFRSOA3KhYGQzq.png) windwos中在network部分设置tcp的backlog ,是一个连接队列,队列总和=未完成3次握手+已完成三次握手在高并发环境下需要一个高backlog值,避免客户端连接缓慢,注意:linux内核会将这个值减小到/proc.sys/net/core/somacconn的值所以需要确认增大somaxconn和tcp_syn_backlog两个值来达到效果 - 绑定的网卡端口 !\[image-20200912205223789\](https://i.loli.net/2020/09/12/XtnPV5ADCkNi869.png) - Timeout 0; 空闲多少秒后关闭空闲连接 0表示不断 - Tcp-keepalive:单位为s 设置为0不进行keepalive检测,建议60s,每隔60秒检查一下状态是否良好 - Loglevel:日志级别默认四个 - debug (最详细:a lot of information, useful for development/testing) - verbose (many rarely useful info, but not a mess like the debug level) - notice (moderately verbose, what you want in production probably) - warning (最少:only very important / critical messages are logged) - Logfile "":日志文件 - Syslog-enabled no:系统日志开关 - Syslog-ident redis:系统日志文件名 - Syslog-facility local0(值可以是user或local0到local7) window没有 - Database 16 :16个数据库 - 存内存数据到硬盘 !\[image-20200912205432562\](https://i.loli.net/2020/09/12/Bav129PrDMbEFGe.png) - 备份条件:Save time 修改次数(save "")不备份 - 15min内至少一个key变化 - 5min内10个key变化 - 1min内10000key变化 - 执行shutdown时会执行备份 - 手动备份save命令 - Fushall也会执行save - Stop-writes-on-bgsave-error 备份出错停止写入, - Rdbchecksum:压缩校验crc64算法,10%性能损耗 - rdbcompression:是否启动压缩 # Replication复制 - Config get requirepass 获得当前用户密码串,此时应为""空串 - Config get dir :获取服务启动路径(dir 配置文件中) - 当前路径 - 当配置完path或用完整路径时,即返回的是那个目录路径 - Config set requirepass "123456",(立即加验证)此时再次ping报错 - Auth 123456 即可 - Maxmemory-policy:对于那些不常用的数据,占内存:过期策略 - volatile-lru(最近最少使用:这对设置了过期时间的建) -\\\> remove the key with an expire set using an LRU algorithm - allkeys-lru -\\\> remove any key according to the LRU algorithm - volatile-random(随机过期的键删除:对有过期时间的有效) -\\\> remove a random key with an expire set - allkeys-random -\\\> remove a random key, any key - volatile-ttl(有限时间内:移除ttl只最小的,即即将过期的) -\\\> remove the key with the nearest expire time (minor TTL) - noeviction (默认永不过期,针对写操作,会返回错误信息)-\\\> don\\'t expire at all, just return an error on write operations - Maxmemory-samples - 设置样本数量:lru算法最小ttl算法都是非精确的,设置样本大小,redis会默认检查这么多个key并选择其中lru的那个.. # Append only mode追加比rdb较好 - 开关,改变后启动时会有appendonly.aof:记录所有写操作 !\[image-20200912205834706\](https://i.loli.net/2020/09/12/eL7SsvUY8i2Oj4P.png) - 最后一秒:故障.可能日志文件损坏, - 两者可同时存在,先加载aof,若失败 - 就会加载dump.rdb - 修复aof: redis-check-aof \\--fix appendonly.aof - Appendfilename:日志文件名字 - Appendfsync - Always:同步化持久,每次改变立即记录(性能差) - Everysec:出厂设置,异步操作每秒记录,会损失1秒 - No: - No-appendfsync-on-rewrite:重写时是否运用appendfsync 用no即可保证数据安全 - Auto-aof-rewrite-min-size:设置重写的基准值 - Auto-aof-rewrite-percentage:设置重写的基准值 # 常见配置redis.conf介绍 - Redis 守护进程启动 daemonize no-\\\>yes - 一守护进程方式启动时 pid会写入/var/run/redis.pid prefile / var/run/redis.pid指定 - Port 6379 - Bind 127.0.0.1 - Timeout 300 - Loglevel 日志级别 debug,verbose,notice,warning 默认 verbose - Logfile stdout:标准输出路径 - Database 16 - Dir ./:变值 - Requirepass ... - Maxclient xxx:最大连接数 - Maxmemory xx:启动时,加载数据,达到最大,尝试清除将到期,或已到期key处理后, - 又达到最大,无法写,仍可读.对于新的vm机制,会把key放入内存value放在swap区 # Redis持久化 ###### RDB(Redis DataBase) - 是什么 - 在指定时间内将数据写入磁盘,即snapshot快照,恢复时写入内存 - Redis会单独创建(fork)子进程进行持久化,会先将数据写入到临时文件,等待持久化过程结束,再用临时文件替换上次的文件,整个过程主进程不需要io - 大规模数据恢复,数据完整性不是很敏感,rdb比aof更高效, - 缺点最后一次持久化数据可能丢失(每5min备份一次,可能最后一次备份时故障) - Fork - 复制一个与当前进程一样的进程,所有基本数据与原进程一致,但作为它的子进程运行 - Rdb保存的是dump.rdb文件 - 配置位置 redis.conf - 如何触发rdb快照 - 配置文件中默认的快照配置 - 命令save或是bgsave - Save:只管保存其他不管,全部阻塞 - Bgsave:异步快照,备份同时可以响应请求,可以lastsave获取最后一次成功快照时间 - 执行flushall 也会产生备份,但是空文件,无意义 - 如何恢复 - 将备份文件移入 redis安装目录 - 优势 - 适合大规模数据恢复,对数据一致性完整性要求不高 - 劣势 - 最后一次可能丢失,fork的时候内存数据克隆,性能问题 - 如何停止 - Redis-cli config set save "" - 小总结 !\[image-20200912210104993\](https://i.loli.net/2020/09/12/a3wgAtJORQnBckT.png) ###### AOF(Append Only File:修复最后一次读写) - 是什么 - 以日志形式记录每个写操作, - 日志只追加文件,redis启动会读取文件重新构建数据, - 重启根据日志内容再次执行以完成恢复工作 - Aof保存的是appendonly.aof - 配置位置 - Aof启动/恢复/修复 - 正常恢复: - 启动设置为appendonly no-\\\>yes - 将所有数据aof文件复制到对应目录(config get dir) - 重启 - 异常恢复: - 启动:设置yes - 找到被写坏的aof文件 - 修复:redis-check-aof \\--fix name - 恢复:重启 - Rewrite - 是什么? - Aof采用追加方式,文件越来越大,增加重写 - 当文件大小超过所设阈值时,aof内容压缩 - 只保留可以恢复数据的最小指令集,使用命令 - Bgrewriteaof - 重写原理 - Aof增加,会fork新进程将文件重写:注意不是读取aof, - 读取内存中数据重写了一个aof,与快照类似 - 触发机制 - 记录上次重写时aof大小,切大于64M - !\[image-20200912210304234\](https://i.loli.net/2020/09/12/69QYhNkPdyFZcug.png) - 优势 - 每秒同步,每修改同步,不同步 - 劣势 - Aof文件远大于rdb,回复速度慢 - 运行效率慢,尽量使用美妙同步策略,若不同步,效率与rdb相同 - 小总结 !\[image-20200912210333888\](https://i.loli.net/2020/09/12/WNDAEUbiOlswMLe.png) # 总结which one - 官网建议:完整性低,快速-\\\>rdb - 只做缓存:可以不使用任何备份,不用持久化 - 同时开启两种:先aof,aof比rdb完整.建议保留rdb !\[image-20200912210403808\](https://i.loli.net/2020/09/12/F7gnzNxIXYQSiUK.png) # Redis的事务 - 是什么:一次多个命令,所有命令都会序列化,按顺序串行 - 能干嘛:一个队列中,一次性,顺序性,排他性, - 怎么做 !\[image-20200912210540693\](https://i.loli.net/2020/09/12/Xd9UHzb4rapRWuQ.png) - 常用命令 - Case 1:正常执行 - Case 2:放弃事务 - Case 3:全体连坐 - Case 4:冤头债主 - Case 5:watch监控 - 悲观锁/乐观锁/cas(check and set) - 悲观锁:修改时上锁 - 乐观锁:不会上锁,更新时判断有没有他人更新过数据,用版本号机制 - 提交版本必须大于记录当前版本才能执行更新 - 小结 - Watch类似乐观锁 - 监控的数据被改,exec命令会被抛弃,返回nullmulti-bulk通知失败 ###### 3阶段 - Multi开启事务 - 命令入队 - 执行exec ###### 3特性 - 单独的隔离操作:不会被其他客户端命令打断 - 没有隔离级别 - 不保证原子性:冤头债主:全体连坐 # Redis的发布订阅(了解) - 是什么:进程间的一种消息通信模式,发送者pub发送,订阅者sub接受 - 命令 !\[image-20200912210903972\](https://i.loli.net/2020/09/12/WcpM5oH7FzqhrZg.png) 源-\\\>channel-\\\>client ###### 案例 !\[image-20200912210929351\](https://i.loli.net/2020/09/12/dE5a3CzbXfWlcPp.png) # Redis的复制 - 是什么 - Replication:主从复制,主机更新后,根据配置策略,自动同步到备机的matser/salver机制 - Master:写;slave:读 - 能干嘛 - 读写分离 - 容灾恢复 - 怎么做 - 配从库不配主库 - 从库配置:slaveof主库ip主库端口 - 每次master断开之后都要重新连接,除非配置进redis.conf - 修改配置文件(不同的几个库) - 拷贝多个redis.conf - 开启daemonize yes - Pid文件名字 - 指定端口 - Log文件名字 - Dump.rdb名字 - 常用三招(以不同配置文件啊启动数据库) - 一主二仆 - 分别连接数据库 - :Info replication//显示状态 都是master - :主机添加数据v1 - :丛机:slaveof 主机ip port (全量复制,从机变为slave:只能读) - :主机:又加数据v2 - :丛机 get v2可以获得,注意也能get v1 - 当主机故障:从机连接状态down,从机原地待命,主机随时连接 - 从机故障:再次恢复时:身份变为master,再次slaveof - 薪火相传 - 上一个slave可以使下一个slaver的master,slaver同样可以接受其他slaves的链接和同步请求,和减少主库的压力 - 中途变更转向:会清除之前数据. - 中间的slave的身份:还是slave ,担有连接的slave - 反客为主 - 从机:Slaveof no one:状态变为master - 其他主机要同步时:要再次slaveof...... ###### 复制原理 - Slave启动后连接到master 会发送sync命令同步 - Master接到后,若此时有修改数据命令,完成后再处理同步 - 全量复制:slave接收到数据库文件后 - 增量复制:master继续将新数据传给slave时,但只要是重新连接都会一次同步所有 ###### 哨兵模式sentinel 自动的反客为主: - 调整结构:一主二仆 - 自定义/myredis目录下新建sentinel.conf文件,名字不能错 - 主机配置哨兵sentinel.conf(可监控多个master) - Sentinel monitor 数据库名字(自己起) 127.0.0.1 6379 1(选择主机方案,票数高的作为主机) - 启动redis-sentinel sentinel.conf - 此时 主机故障, 一段时间后 会在从机中选出一个作为主机,(不用手动更改) - 主机恢复时:slave:变为新主机的slave(有延迟) ###### 复制的缺点 复制延时,slave多问题更严重 # Redis的java客户端(jedis) ## Jedis常用操作 - 测试 \`\`\`java Jedis jedis = new Jedis("127.0.0.1",6379); System.out.println(jedis.ping()); Set jedis.set("k1","v1"); Get jedis.get("k1") Set listSet = jedis.keys("\*"); \`\`\` - 事物提交 !\[image-20200912211405853\](https://i.loli.net/2020/09/12/uOkm5NEAW3j6PGS.png) !\[image-20200912211415906\](https://i.loli.net/2020/09/12/2BKAzJ8wgLxqT1v.png) 若事物执行时数据被更改,在执行exec时不会报错,只是事物内容不再执行 - 主从复制 - 两个库,先各自独立 - \`\`\`Java Jedis jedis_M = new Jedis("127.0.0.1",6379); Jedis jedis_S = new Jedis("127.0.0.1",6380); jedis_S.slaveof("127.0.0.1", 6379); jedis_M.set("k", "v"); String result=jedis_S.get("k"); System.out.println(result); //很可能获取不到(主从关系没建立)太快了,再次执行即可 \`\`\` ## Jedispool !\[image-20200912211505137\](https://i.loli.net/2020/09/12/S5Ogo4N2CyEZFMi.png)