当前位置: 首页 > news >正文

定制网站模板网站不备案可以建设吗

定制网站模板,网站不备案可以建设吗,优化关键词的方法,明星百度指数排名redis相关内容 默认端口6379 默认16个数据库#xff0c;初始默认使用0号库 使用select 切换数据库 统一密码管理#xff0c;所有库密码相同 dbsize#xff1a;查看当前库key的数量 flushdb#xff1a;清空当前库 flushall#xff1a;清空全部库 redis是单线程 多路…redis相关内容 默认端口6379 默认16个数据库初始默认使用0号库 使用select 切换数据库 统一密码管理所有库密码相同 dbsize查看当前库key的数量 flushdb清空当前库 flushall清空全部库 redis是单线程 多路IO复用技术 **多路复用**使用一个线程来检测多个文件描述符socket的就绪状态比如调用select和poll函数传入多个文件描述符如果有一个文件描述符就绪则返回否则阻塞到超时得到就绪状态后进行真正的操作可以在同一个线程里执行也可以启动线程执行比如使用线程池 串行 vs 多线程锁memcached vs 单线程多路复用redis 与memcache三不同支持多数据类型支持持久化单线程多路复用redis6.0中提供了多线程命令解析和io数据读写这部分采用了多线程而命令的执行还是采用的是单线 程多个客户端发送来的命令会在同一个线程去执行相当于排队执行效率极高 数据类型 这里的数据类型指value数据类型key类型都是字符串 redis键key keys *查看当前库所有的keyexists key判断某个key是否存在type key查看你的key是什么类型del key删除指定的key数据unlink key根据value删除非阻塞删除仅仅将keys从keyspace元数据中删除真正的删除会在后续异步中操作。expire key 10为指定的key设置有效期10秒ttl key查看指定的key还有多少秒过期-1表示永不过期-2表示已过期select dbindex切换数据库【0-15】默认为0dbsize查看当前数据库key的数量flushdb清空当前库flushall通杀全部库 redis字符串String String是Redis最基本的类型 一个key对应一个value。是二进制安全的。意味着Redis的string可以包含任何数据egjpg图片或者序列化的对象一个Redis中字符串value最多可以是512M 。 常用命令 set添加键值对 NX当数据库中key不存在时可以将key-value添加到数据库XX当数据库中key存在时可以将key-value添加数据库与NX参数互斥EXkey的超时秒数PXkey的超时毫秒数与EX互斥value中若包含空格、特殊字符需用双引号包裹 get获取值apend追加值将给定的值追加到原值的末尾strlen获取值的长度setnxkey不存在时设置key的值incr原子递增将key中存储的值增1只能对数字值操作如果key不存在则会新建一个值为1decr原子递减1将key中存储的值减1只能对数字值操作如果为空新增值为-1incrby/decrby递增或递减指定的数字mset同时设置多个key-valuemget获取多个key对应的值msetnx当多个key都不存在时则设置成功 。原子性的要么都成功或者都失败getrange获取值的范围类似java中的substringsetrange覆盖指定位置的值setex设置键值过期时间秒getset以新换旧设置新值同时返回旧值 数据结构 简单动态字符串是可以修改的字符串采用分配冗余空间的方式来减少内存的频繁分配 。内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时扩容都是加倍现有的空间如果超过1M扩容时一次会多扩容1M的空间。 redis列表List 单键多值双向链表实现。 命令 lpush/rpush从左边或者右边插入一个或多个值lrange从列表左边获取指定范围内的值lpop/rpop从左边或者右边弹出多个元素rpoplpush从一个列表右边弹出一个元素放到另外一个列表中lindex获取指定索引位置的元素从左到右llen获得列表长度linsert在某个值的前或者后面插入一个值lrem删除指定数量的某个元素lset替换指定位置的值 数据结构 快速链表quickList 多个zipList使用双向指针串起来使用 redis集合Set set是可以自动排重的底层实际是一个value为null的hash表收益添加删除查找复杂度都是O(1) 命令 sadd添加一个或多个元素smembers取出所有元素sismember判断集合中是否有某个值scard返回集合中元素的个数srem删除多个元素spop随机弹出多个值srandmember随机获取多个元素不会从集合中删除smove将某个原创从一个集合移动到另一个集合sinter取多个集合的交集sinterstore将多个集合的交集放到一个新的集合中sunion取多个集合的并集自动去重sunionstore将多个集合的并集放到一个新的集合中sdiff取多个集合的差集sdiffstore将多个集合的差集放到一个新的集合中 数据结构 set数据结构是字典字典是用hash表实现的。内部使用hash结构所有的value都指向同一个内部值。 redis哈希hash Redis hash是一个键值对集合是一个string类型的field和value的映射表hash特别适合用于存储对象 常用命令 hset设置多个field的值hget获取指定filed的值hgetall返回hash表所有的域和值hexists判断给定的field是否存在1存在0不存在hkeys列出所有的filedhvals列出所有的valuehlen返回filed的数量hincrbyfiled的值加上指定的增量hsetnx当filed不存在的时候设置filed的值 数据结构 Hash类型对应的数据结构是2种ziplist压缩列表hashtable哈希表。 当field-value长度较短个数较少时使用ziplist否则使用hashtable。 redis有序集合zsetsorted set redis有序集合zset是一个没有重复元素的字符串集合。有序集合的每个成员都关联了一个评分score这个评分score被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的但是评分是可以重复的。 常用命令 zadd添加元素zrangescore升序获取指定索引范围的元素zrevrangescore降序获取指定索引范围的元素zrangebyscore按照score升序返回指定score范围内的数据zrevrangebyscore按照score降序返回指定score范围内的数据zincrby为指定元素的score加上指定的增量zrem删除集合中多个元素zremrangebyrank根据索引范围删除元素zremrangebyscore根据score的范围删除元素zcount统计指定score范围内元素的个数zrank按照score升序返回某个元素在集合中的排名zrevrank按照score降序返回某个元素在集合中的排名zscore返回集合中指定元素的score 数据结构 hash表 类似于java中的MapString,scorekey为集合中的元素value为元素对应的score可以用来快速定位元素定义的score时间复杂度为O(1) 跳表 跳表在原来的有序链表上加上了多级索引实现简单插入、删除、查找的复杂度均为O(logN)有序链表基础上增加“跳跃”功能空间换时间层就是我们假设的抽象出来的新链表。 Bitmaps:位操作字符串 可以实现对位的操作 Bitmaps本身不是一种数据类型 实际上它就是字符串key-value 但是它可以对字符串的位进行操作字符串中每个字符对应1个字节也就是8位一个字符可以存储8个bit位信息。Bitmaps单独提供了一套命令 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组 数组的每个单元只能存储0和1 数组的下标在Bitmaps中叫做偏移量。 常用命令 setbit设置某个偏移量的值0或1getbit获取某个偏移位的值bitcount统计bit位都为1的数量bittop对一个多个bitmaps执行位操作 bitmaps与set比较 set 和 Bitmaps 存储一天活跃用户对比 使用 Bitmaps 能节省很多的内存空间 尤其是随着时间推移节省的内存还是非常可观的。 set 和 Bitmaps 存储独立用户空间对比 假如该网站每天的独立访问用户很少 例如只有 10 万大量的僵尸用户 那么两者的对比如下表所示 很显然 这时候使用 Bitmaps 就不太合适了 因为基本上大部分位都是 0。 HyperLoglog 是用来做基数统计的算法HyperLogLog 的优点是在输入元素的数量或者体积非常非常大时计算基数所需的空间总是固定的、并且是很小的。 命令 pfadd添加多个元素pfcount获取多个HLL合并后元素的个数pfmerge将多个HLL合并后元素放入另外一个HLL Geographic 该类型就是元素的 2 维坐标在地图上就是经纬度redis基于该类型提供了经纬度设置、查询、范围查询、距离查询经纬度Hash等常见操作。 命令 geoadd添加多个位置的经纬度geopos获取多个位置的坐标值geodist获取两个位置的直线距离georadius以给定的经纬度为中心找出某一半径内的元素 发布和订阅 发布和订阅 消息通信模式 发布者发布消息订阅者接收消息 redis发布和订阅 客户端订阅频道客户端A,B,C—订阅 —channel1频道当给这个频道发布消息后消息就会发送给订阅的客户端 发布和订阅的命令行实现 订阅命令subscribe channel1给channel1发布消息hellopublish channel1 helloworld切换到订阅窗口即可看到 发布和订阅常用命令 subscribe订阅一个或者多个频道 订阅msg和chat_room两个频道subscribe msg chat_root publish发布消息到指定的频道 publish msg “hello” psubscribe订阅一个或多个符合给定模式的频道 每个模式以 * 作为匹配符eg:it* 匹配以it开头的频道psubscribe news.* tweet.* Jedis maven dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion2.9.3/version /dependencyapi package jedis;import org.junit.After; import org.junit.Before; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub;import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit;/*** author 308158* description: TODO* date: Created in 2023-8-17 13:58*/ public class JedisDemo {Jedis jedis;Beforepublic void before(){this.jedis new Jedis(1.1.1.1, 6379);}Afterpublic void after(){//关闭jedisthis.jedis.close();}/** 测试redis是否连通 */Testpublic void test1(){String ping jedis.ping();System.out.println(ping);}/** string类型测试 */Testpublic void stringTest(){jedis.set(site, http://);System.out.println(jedis.get(site));System.out.println(jedis.ttl(site));}/** list类型测试 */Testpublic void listTest(){jedis.rpush(courses, java, spring, springmvc);ListString courses jedis.lrange(courses, 0, -1);for (String course : courses) {System.out.println(course);}}/** set类型 */Testpublic void setTest(){jedis.sadd(users,tom,jack,ready);SetString users jedis.smembers(users);for (String user : users) {System.out.println(user);}}/** hash类型测试 */Testpublic void hashTest() {jedis.hset(user:1001, id, 1001);jedis.hset(user:1001, name, 张三);jedis.hset(user:1001, age, 30);MapString, String userMap jedis.hgetAll(user:1001);System.out.println(userMap);}/** zset类型测试 */Testpublic void zsetTest() {jedis.zadd(languages, 100d, java);jedis.zadd(languages, 95d, c);jedis.zadd(languages, 70d, php);SetString languages jedis.zrange(languages, 0, -1);System.out.println(languages);}/** 订阅消息 */Testpublic void subscribeTest() throws InterruptedException {//subscribe(消息监听器,频道列表)jedis.subscribe(new JedisPubSub() {Overridepublic void onMessage(String channel, String message) {System.out.println(channel : message);}}, sitemsg);TimeUnit.HOURS.sleep(1);}/** 发布消息*/Testpublic void publishTest() {jedis.publish(sitemsg, hello redis);}}springboot整合redis maven配置 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdversion1.5.22.RELEASE/version /dependency配置信息 #redis服务器ip spring.redis.host192.168.1.1 #redis服务器端口 spring.redis.port6379 #redis密码 spring.redis.passwordroot #连接超时时间 spring.redis.timeout6000 #redis默认情况下有16个分片默认0 spring.redis.database0RedisTemplate工具类操作redis package jedis;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set;/*** author 308158* description: TODO* date: Created in 2023-8-17 14:17*/ RestController RequestMapping(/redis) public class RedisController {Autowiredprivate RedisTemplateString, String redisTemplate;RequestMapping(/stringTest)public String stringTest() {this.redisTemplate.delete(name);this.redisTemplate.opsForValue().set(name, 路人);return this.redisTemplate.opsForValue().get(name);}RequestMapping(/listTest)public ListString listTest() {this.redisTemplate.delete(names);this.redisTemplate.opsForList().rightPushAll(names, 刘德华, 张学友, 郭富城, 黎明);return this.redisTemplate.opsForList().range(names, 0, -1);}RequestMapping(setTest)public SetString setTest() {this.redisTemplate.delete(courses);this.redisTemplate.opsForSet().add(courses, java, spring, springboot);return this.redisTemplate.opsForSet().members(courses);}RequestMapping(hashTest)public MapObject, Object hashTest() {this.redisTemplate.delete(userMap);MapString, String map new HashMap();map.put(name, 路人);map.put(age, 30);this.redisTemplate.opsForHash().putAll(userMap, map);return this.redisTemplate.opsForHash().entries(userMap);}RequestMapping(zsetTest)public SetString zsetTest() {this.redisTemplate.delete(languages);this.redisTemplate.opsForZSet().add(languages, java, 100d);this.redisTemplate.opsForZSet().add(languages, c, 95d);this.redisTemplate.opsForZSet().add(languages, php, 70);return this.redisTemplate.opsForZSet().range(languages, 0, -1);} } }redis 事务 定义 redis事务是一个单独的隔离操作事务中的所有命令都会序列化、按顺序地执行事务在执行的过程中不会被其他客户端发送来的命令请求所打断。主要作用串联多个命令防止别的命令插队。 MultiExecdiscard 从输入Multi命令开始输入的命令都会依次进入命令队列中但不会执行直到输入Exec后redis会将之前的命令依次执行。组队的过程中可以通过discard来放弃组队。 redis事务分2个阶段组队阶段、执行阶段 组队阶段只是将所有命令加入命令队列执行阶段依次执行队列中的命令在执行这些命令的过程中不会被其他客户端发送的请求命令插队或者打断。 相关的命令 multi标记一个事务块的开始 事务块内的多条命令会按照先后顺序被放进一个队列当中最后由 exec 命令原子性(atomic)地执行 exec执行所有事务块内的命令 假如某个(或某些) key 正处于 watch 命令的监视之下且事务块中有和这个(或这些) key 相关的命令那么 exec 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效否则该事务被打断(abort)。 返回值 事务块内所有命令的返回值按命令执行的先后顺序排列。 当操作被打断时返回空值 nil 。 discard取消事务 取消事务放弃执行事务块内的所有命令。 返回值总是返回ok 事务的错误处理 case1组队中命令有误导致所有命令取消执行 组队中某个命令出现了错误报告执行时整个队列中所有的命令都会被取消 case2组队中没有问题执行中部分成功部分失败 事务冲突的问题 悲观锁 每次去拿数据的时候都认为会修改所以每次在拿数据的时候都会上锁这样别人拿到这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制比如行锁、表锁、读锁、写锁等都是在做操作之前先上锁 乐观锁 每次去拿数据的时候都认为别人不会修改所以不会上锁但是在修改的时候会判断一下在此期间别人有没有去更新这个数据可以使用版本号等机制。乐观锁适用于多读的应用类型这样可以提高吞吐量。redis就是使用这种check-and-set机制实现事务的。 watch key [key…] 在执行multi之前先执行watch key1 [key2 …]可以监视一个或者多个key若在事务的exec命令之前这些key对应的值被其他命令所改动了那么事务中所有命令都将被打断即事务所有操作将被取消执行 。 unwatch:取消监视 如果在执行 WATCH 命令之后 EXEC 命令或 DISCARD 命令先被执行了的话那么就不需要再执行UNWATCH 了。 因为 EXEC 命令会执行事务因此 WATCH 命令的效果已经产生了而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视因此这两个命令执行之后就没有必要执行 UNWATCH 了。 redis事务三特性 单独的隔离操作 事务中的所有命令都会序列化、按顺序地执行事务在执行过程中不会被其他客户端发送来的命令请求所打断 没有隔离级别的概念 队列中的命令没有提交exec之前都不会实际被执行因为事务提交前任何指令都不会被实际执行。 不能保证原子性 事务中如果有一条命令执行失败后续的命令仍然会被执行没有回滚。 如果在组队阶段有1个失败了后面都不会成功如果在组队阶段成功了在执行阶段有那个命令失败就这条失败其他的命令则正常执行不保证都成功或都失败。 redis持久化–RDB 定义 在指定的时间间隔内将内存中的数据集快照写入磁盘也就是行话讲的Snapshot快照它恢复时是键快照文件直接读到内存里。 备份如何执行 Redis会单独创建fork一个子进程进行持久化将数据写入到一个临时文件中待持久化过程都结束后再用这个临时文件替换上次持久化好的文件。整个过程中主进程是不进行任何IO操作的如果需要进行大规模的恢复且对数据恢复的完整性不是非常敏感那RDB方式要比AOF方式更加的高效。缺点是最后一次持久化后的数据可能丢失。 Fork 作用复制一个与当前进程一样的进程新进程的所有数据变量、环境变量、程序计数器等数值都和原进程一致它是一个全新的进程并作为原进程的子进程。在Linux程序中fork()会产生一个和父进程完全相同的子进程但子进程在此后多会exec系统调用处于效率考虑linux中引入了“写时复制技术”一般情况父进程和子进程会共用一段物理内存只有进程空间的各段的内容要发生变化时才会将父进程的内容复制一份给子进程。 RDB持久化流程 bgsave命令给到父进程 如果有其他子进程正在执行直接返回否则fork一个子进程生成rdb文件并通知父进程 配置 指定备份文件的名称redis.conf中默认为dump.rdb指定备份文件存放的目录redis.conf中默认dir ./触发rdb备份 自动备份需配置备份规则redis.conf save用来配置备份的规则格式save 秒钟 写操作次数 手动执行命令备份 save只管保存其他不管全部阻塞手动保存不建议使用bgsave后台异步进行快照操作快照同时还可以相应客户端情况可以通过lastsave命令获取最后一次成功生成快照时间 flushall命令 执行flushall命令也会产生dump.rdb文件但里面是空的无意义 stop-writes-on-bgsave-error当磁盘满时是否关闭redis的写操作rdbcompressionrdb备份是否开启压缩rdbchecksum是否检查rdb备份文件的完整性 rdb备份和恢复 rdb备份 通过config get dir 查询rdb文件的目录然后将rdb的备份文件*.rdb文件拷贝到别的地方 rdb的恢复 关闭redis先把备份的文件拷贝到工作目录启动redis备份数据直接加载数据被恢复 优劣势 优势 适合大规模数据恢复对数据完整性和一致性要求不高更适合使用节省磁盘空间恢复速度快 劣势 Fork的时候内存中的数据会被克隆一份大致2倍的膨胀需要考虑虽然Redis在fork的时候使用了写时拷贝技术但是如果数据庞大时还是比较消耗性能在备份周期在一定间隔时间做一次备份所以如果Redis意外down的话就会丢失最后一次快照后所有修改 停止rdb 动态停止RDBredis-cli config set save “” #save后给空值表示禁用保存策略 redis事务三特性 单独的隔离操作 事务中的所有命令都会序列化、按顺序地执行事务在执行过程中不会被其他客户端发送来的命令请求所打断 没有隔离级别的概念 队列中的命令没有提交exec之前都不会实际被执行因为事务提交前任何指令都不会被实际执行。 不能保证原子性 事务中如果有一条命令执行失败后续的命令仍然会被执行没有回滚。 如果在组队阶段有1个失败了后面都不会成功如果在组队阶段成功了在执行阶段有那个命令失败就这条失败其他的命令则正常执行不保证都成功或都失败。 redis持久化–RDB 定义 在指定的时间间隔内将内存中的数据集快照写入磁盘也就是行话讲的Snapshot快照它恢复时是键快照文件直接读到内存里。 备份如何执行 Redis会单独创建fork一个子进程进行持久化将数据写入到一个临时文件中待持久化过程都结束后再用这个临时文件替换上次持久化好的文件。整个过程中主进程是不进行任何IO操作的如果需要进行大规模的恢复且对数据恢复的完整性不是非常敏感那RDB方式要比AOF方式更加的高效。缺点是最后一次持久化后的数据可能丢失。 Fork 作用复制一个与当前进程一样的进程新进程的所有数据变量、环境变量、程序计数器等数值都和原进程一致它是一个全新的进程并作为原进程的子进程。在Linux程序中fork()会产生一个和父进程完全相同的子进程但子进程在此后多会exec系统调用处于效率考虑linux中引入了“写时复制技术”一般情况父进程和子进程会共用一段物理内存只有进程空间的各段的内容要发生变化时才会将父进程的内容复制一份给子进程。 RDB持久化流程 bgsave命令给到父进程 如果有其他子进程正在执行直接返回否则fork一个子进程生成rdb文件并通知父进程 配置 指定备份文件的名称redis.conf中默认为dump.rdb指定备份文件存放的目录redis.conf中默认dir ./触发rdb备份 自动备份需配置备份规则redis.conf save用来配置备份的规则格式save 秒钟 写操作次数 手动执行命令备份 save只管保存其他不管全部阻塞手动保存不建议使用bgsave后台异步进行快照操作快照同时还可以相应客户端情况可以通过lastsave命令获取最后一次成功生成快照时间 flushall命令 执行flushall命令也会产生dump.rdb文件但里面是空的无意义 stop-writes-on-bgsave-error当磁盘满时是否关闭redis的写操作rdbcompressionrdb备份是否开启压缩rdbchecksum是否检查rdb备份文件的完整性 rdb备份和恢复 rdb备份 通过config get dir 查询rdb文件的目录然后将rdb的备份文件*.rdb文件拷贝到别的地方 rdb的恢复 关闭redis先把备份的文件拷贝到工作目录启动redis备份数据直接加载数据被恢复 优劣势 优势 适合大规模数据恢复对数据完整性和一致性要求不高更适合使用节省磁盘空间恢复速度快 劣势 Fork的时候内存中的数据会被克隆一份大致2倍的膨胀需要考虑虽然Redis在fork的时候使用了写时拷贝技术但是如果数据庞大时还是比较消耗性能在备份周期在一定间隔时间做一次备份所以如果Redis意外down的话就会丢失最后一次快照后所有修改 停止rdb 动态停止RDBredis-cli config set save “” #save后给空值表示禁用保存策略 redis持久化 – AOF 定义 以日志的形式来记录每个写操作增量保存将redis执行过的所有写指令记录下来读操作不记录只允追加文件但不可改写文件redis启动之初会读取该文件重新构造数据换言之redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。 持久化流程 客户端的请求写命令会被append追加到AOF缓冲区内AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中AOF文件大小超过重写策略或手动重写时会对AOF文件进行重写rewrite压缩AOF文件容量redis服务器重启时会重新load加载AOF文件中的写操作达到数据恢复的目的 默认不开启 redis.conf文件中对AOF进行配置 appendonly no # 是否开启AOFyes开启no不开启默认为noappendfilename “appendonly.aof” # aof文件名称默认为appendonly.aofdir ./ # aof文件所在目录默认./表示执行启动命令时所在的目录比如在/opt目录中去执行redis-server /etc/redis.conf 来启动redis那么dir此时就是/opt目录 aof和rdf同时开启系统默认取AOF数据数据不会存在丢失 AOF 启动修复恢复 AOF的备份机制和性能虽然和RDB不同但是备份和恢复的操作同RDB一样都是拷贝备份文件需要恢复时再拷贝到Redis工作目录下启动系统即加载。正常恢复 修改默认的appendonly no改为yes将有数据的aof文件复制一份保存到对应的目录查看目录config get dir恢复重启redis然后重新加载 异常恢复 修改默认的appendonly no改为yes如遇到aof文件损坏通过 /usr/local/bin/redis-check-aof --fix appendonly.aof 进行 恢复 AOF 同步频率设置 redis.config配置AOF同步的频率 appendfsync always每次写入立即同步 始终同步每次redis的写入都会立刻记入日志性能较差但数据完整性比较好。 appendfsync everysec每秒同步 每秒同步每秒记录日志一次如果宕机本秒数据可能丢失更新的命令会放在内存中AOF缓冲区每秒将缓冲区的命令追加到AOF文件 appendfsync no不主动同步 redis不主动进行同步把同步交给操作系统。 rewrite压缩 rewrite压缩 AOF采用文件追加方式文件会越来越大为了避免出现此情况新增了重写机制。 重写机制当AOF文件的大小超过锁审定的阈值时Redis就会启动AOF文件的内容压缩只保留可以恢复数据的最小指令集可以使用命令bgrewriteaof触发重写 重写原理实现 AOF文件持续增长而过大时会fork出一条新进程来将文件重写也是先写临时文件最后在rename替换旧文件redis4.0版本后的重写是指就把rdb的快照以二进制的形式附在新的aof头部作为已有的历史数据替换掉原来的流水账操作。 触发机制 何时重写 bgrewriteaof手动触发重写 从 Redis 2.4 开始 AOF 重写由 Redis 自行触发 bgrewriteaof 仅仅用于手动触发重写操作。 redis会记录上次重写的aof大小默认配置是当aof文件大小是上次rewrite后大小的2倍且文件大于64M时触发。 重写虽然可以节约大量磁盘空间减少恢复时间但是每次重写还是有一定负担的因此设置redis满足一定条件才会进行重新。 auto-aof-rewrite-percentage设置重写基准值 设置重写的基准值默认100当文件达到100%时开始重写文件是原来重写后文件的2倍时重写。 auto-aof-rewrite-min-size设置重写基准值 设置重写的基准值默认64MBAOF文件大小超过这个值开始重写。 重写流程 手动执行 bgrewriteaof 命令触发重写判断是否当前有bgfsave或bgrewriteaof在运行如果有则等待该命令结束后再继续执行主进程fork出子进程执行重写操作保证主进程不会阻塞子进程遍历redis内存中的数据到临时文件客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整性以及新AOF文件生成期间的新的数据修改动作不会丢失子进程写完新的AOF文件后向主进程发送信号父进程更新统计信息主进程把aof_rewrite_buf中的数据写入到新的AOF文件使用新的AOF文件覆盖旧的AOF文件完成AOF重写 no-appendfsync-on-rewrite重写时不会执行appendfsync操作 该参数表示在正在进行AOF重写时不会将AOF缓冲区中的数据同步到旧的AOF文件磁盘也就是说在进行AOF重写的时候如果此时有写操作进来此时写操作的命令会放在aof_buf缓存中内存中而不会将其追加到旧的AOF文件中这么做是为了避免同时写旧的AOF文件和新的AOF文件对磁盘产生的压力。默认是ON表示关闭即在AOF重写时会对AOF缓冲区中的数据做同步磁盘操作这在很大程度上保证了数据的安全性。但在数据量很大的场景因为两者都会消耗磁盘IO对磁盘的影响较大可以将其设置为“yes”减轻磁盘压力但在极端情况下可能丢失整个AOF重写期间的数据。如果no-appendfsync-on-rewrite为yes不写入aof文件只写入缓存用户请求不会阻塞但是在这段时间如果宕机会丢失这段时间的缓存数据。降低数据安全性提高性能如果no-appendfsync-on-rewrite为no还是会把数据库往磁盘里刷但是遇到重写操作可能会发生阻塞。数据安全但是性能降低 优劣势 优势 备份机制更稳健丢失数据概率更低可读的日志文本通过操作AOF文件可以处理误操作 劣势 比RDB占用更多的磁盘空间恢复备份速度要慢每次读写都同步的话有一定的性能压力存在个别bug造成不能恢复 总结 AOF文件是一个只进行追加的日志文件Redis可以在AOF文件体积变得过大时自动地在后台对AOF文件进行重写AOF文件有序地保存了对数据库执行的所有写入操作这些写入操作以redis协议的格式保存因此AOF文件的内容非常容易被人读懂对文件进行分析也很轻松。对于相同的数据集来说AOF文件的体积通常要大于RDB文件的体积根据所使用的fsync策略AOF的速度可能会慢于RDB redis主从复制 定义 主机更新后根据配置和策略自动同步到备机的master/slave机制Master以写为主Slave以读为主。 作用 读写分离性能扩展降低主服务器的压力容灾快速恢复主机挂掉时从机变为主机 用法 配置1主2从 master主6379slave1从6380slave2从6381 配置主从 创建案例工作目录master-slave 执行下面命令创建 /opt/master-slave 目录本次所有操作均在 master-slave 目录进行。 将redis.conf复制到master-slave目录创建master的配置文件redis-6379.conf创建slave1的配置文件redis-6380.conf创建slave2的配置文件redis-6381.conf启动master redis-server 命令启动slave1启动slave2查看主机的信息 连接主机redis-cli -h xx.xx.查看信息info Repliacation 查看slave1的信息同样查看slave2的信息验证主从同步效果 flushdbset name ready查看从机是否把数据同步过来了 主从复制原理 slave启动成功连接到master后会给master发送数据同步消息发送sync命令master接收到slave发来的数据同步消息后把主服务器的数据进行持久化到rdb文件同时会收集接收到的用于修改数据的命令master将传rdb文件发送给你slave完成一次完全同步全量复制而slave服务在接收到master发来的rdb文件后将其存盘并加载到内存增量复制master继续将收集到的修改命令依次传给slave完成同步但是只要重新连接master一次完全同步全量复制将会被自动执行 小结 主redis挂掉以后情况会如何 主机挂掉后从机会待命 从挂掉后又恢复了会继续从主同步数据么 会的当从重启之后会继续将中间缺失的数据同步过来。 常用的主从结构 一主二从 从机可以采用命令的方式配置 创建案例工作目录master-slave将redis.conf复制到master-slave创建master的配置文件redis-6379.conf创建slave1的配置文件redis-6380.conf创建slave2的配置文件redis-6381.conf启动master slave1 slave2 :redis-server 命令分别登陆3台机器查看各自主从信息 info replication配置slave1为master的从库 连接到slave1redis-cli设置master密码config set指定slave1作为master从机slaveof 配置slave2为master的从库再来看看master的主从信息 注意通过 slaveof 命令指定主从的方式slave重启之后主从配置会失效所以重启后需要在slave上重新通过 slaveof 命令进行设置。中途通过 slaveof 变更转向本地的数据会被清除会从新的master重新同步数据。 薪火相传 若master下面挂很多slavemaster会有压力实际上slave下面也可以挂slave配置和上面的类似。 反客为主 当master挂掉之后可以从slave中选择一个作为主机。 执行命令slaveof no oneslave1就变成主机了然后再去其他slave上面执行 slaveof 命令将其挂在slave1上。 缺点需要手动去执行命令去操作不是太方便。 其他方案哨兵模式主挂掉之后自动从slave中选举一个作为主机自动实现故障转移。 哨兵模式 定义 能够自动监控master是否发生故障如果故障了会根据投票数从slave中挑选一个作为master其他的slave会自动转向同步新的master实现故障自动转义。 原理 sentinel会按照指定的频率给master发送ping请求看看master是否还活着若master在指定时间内未正常响应sentinel发送的ping请求sentinel则认为master挂掉了存在误判可能master并没有挂只是sentinel和master之间的网络不通导致导致ping失败。为了避免误判通常会启动多个sentinel一般是奇数个比如3个那么可以指定当有多个sentinel都觉得master挂掉了此时才断定master真的挂掉了通常这个值设置为sentinel的一半比如sentinel的数量是3个那么这个量就可以设置为2个。当多个sentinel经过判定断定master确实挂掉了sentinel会进行故障转移会从slave中投票选出一个服务器将其升级为新的主服务器 并让失效主服务器的其他从服务器slaveof指向新的主服务器当客户端试图连接失效的主服务器时 集群也会向客户端返回新主服务器的地址 使得集群可以使用新主服务器代替失效服务器。 用法 需求配置1主2从3个哨兵 最少有2个哨兵认为主的挂掉了才进行故障转移。 master6379slave1:6380slave2:6381sentinel1:26379sentinel2:26380sentinel3:26381 步骤 创建案例工作目录 sentinel将redis.conf复制到sentinel目录创建master的配置文件redis-6379.conf创建slave1的配置文件redis-6380.conf创建slave2的配置文件redis-6381.conf启动master、slave1、slave2配置slave1为master的从库配置slave2为master的从库验证主从复制是否正常创建sentinel1的配置文件sentinel-26379.conf创建sentinel2的配置文件sentinel-26380.conf创建sentinel3的配置文件sentinel-26381.conf启动3个sentinel分别查看3个sentinel的信息 info sentinel验证故障自动转移是否成功 step1在master中执行下面命令停止master shutdownstep2等待2分钟等待完成故障转移 sentinel中的配置down-after-milliseconds60 表示判断主机下线时间是60秒所以等2分钟让系统先自动完成故障转移 step3查看slave1的主从信息 info replicationstep4查看slave2的主从信息step5验证下slave1和slave2是否同步 恢复旧的master当旧的master恢复之后会自动挂在新的master下面 step1执行命令启动旧的master redis-serverstep2执行命令连接旧的masterstep3执行命令查看其主从信息 springboot整合sentinel模式 配置 # redis sentinel主服务名称这个可不是随便写的哦来源于sentinel配置文件中sentinel monitor后面跟的那个名称 spring.redis.sentinel.mastermymaster # sentinel节点列表(host:port)多个之间用逗号隔开 spring.redis.sentinel.nodes192.168.200.129:26379,192.168.200.129:26380,192.168.200.129:26381 # sentinel密码 #spring.redis.sentinel.password # 连接超时时间毫秒 spring.redis.timeout60000 # Redis默认情况下有16个分片这里配置具体使用的分片默认是0 spring.redis.database0RequestMapping(value /info, produces MediaType.TEXT_PLAIN_VALUE) public String info() {Object obj this.redisTemplate.execute(new RedisCallbackObject() {Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {return connection.execute(info);}});return obj.toString(); }redis集群 存在问题 单台redis容量限制如何进行扩容继续加内存、加硬件么单台redis并发写量太大有性能瓶颈如何解决redis3.0中提供了集群可以解决这些问题。 集群定义 redis集群是对redis的水平扩容即启动N个redis节点将整个数据分布存储在这个N个节点中每个节点存储总数据的1/N。 例如由3台master和3台slave组成的redis集群每台master承接客户端三分之一请求和写入的数据当master挂掉后slave会自动替代master做到高可用。 配置 需求配置3主3从集群 master1:6379 master2:6380 master3:6381 slave1:6389 slave2:6390 slave3:6391 创建案例工作目录cluster将redis.conf复制到cluster目录创建master1的配置文件redis-6379.conf创建master2的配置文件redis-6380.conf创建master3的配置文件redis-6381.conf创建slave1的配置文件redis-6389.conf创建slave2的配置文件redis-6390.conf创建slave3的配置文件redis-6391.conf启动master、slave1、slave2查看6个redis的启动情况 ps -ef | grep redis确保node-xxxx.conf文件已正常生成将6个节点合成一个集群 /opt/redis-6.2.1/src/redis-cli --cluster create --cluster-replicas 1 192.168.200.129:6379 192.168.200.129:6380 192.168.200.129:6381 192.168.200.129:6389 192.168.200.129:6390 192.168.200.129:6391合体的命令后面会跟上所有节点的ip:port列表多个之间用空格隔开注意ip不要写127.0.0.1要写真实ip–cluster-replicas 1表示采用最简单的方式配置集群即每个master配1个slave6个节点就形成了3主3从执行期间会让确定是否同样这样的分配方式输入yes然后等几秒集群合体成功 连接集群节点查看集群信息cluster nodes验证集群数据的读写操作 连接 6379 这个节点然后执行一个set操作[roothspEdu01 cluster]# redis-cli -c -h 192.168.200.129 -p 6379 192.168.200.129:6379 set name ready - Redirected to slot [5798] located at 192.168.200.129:6380 OK 192.168.200.129:6380明明在 6379 上操作的但是请求被转发到了6380这个节点去处理了涉及slot了 redis集群如何分配这6个节点 一个集群至少有3个主节点因为新master的选举需要大于半数的集群master节点同意才能选举成功如果只有两个master节点当其中一个挂了是达不到选举新master的条件的。选项–cluster-replicas 1表示希望为集群中的每个主节点创建一个从节点。 分配原则尽量保证每个主库运行在不同的ip每个主库和从库不在一个ip上这样才能做到高可用。 slots槽 Redis集群内部划分了16384个slots插槽合并的时候会将每个slots映射到一个master上面比如上面3个master和slots的关系如下 master10-5460插槽位置从0开始0表示第1个插槽master25460-10922master310923-16383slave1slave2slave3从节点没有槽位slave是用来对master做替补的 数据库中的每个key都属于16384个slots中的其中1个当通过key读写数据的时候redis需要先根据key计算出key对应的slots然后根据slots和master的映射关系找到对应的redis节点key对应的数据就在这个节点上面。 集群中使用公式 CRC16(key)%16384 计算key属于哪个槽 在集群中录入值 在 redis-cli 每次录入、查询键值redis都会计算key对应的插槽如果不是当前redis节点的插槽redis会报错并告知应前往的redis实例地址和端口。 连接6379这个实例来操作k1这个节点发现k1的槽位在6381上面返回了错误信息。 errormoved slot ip: port 使用redis-cli客户端提供了-c参数可以解决这个问题表示以集群方式执行执行命令的时候当前节点处理不了的时候会自动将请求重定向到目标节点效果如下被重定向到6381了 redis-cli -c -h xxxxxx 6379 redirected to slot [12706] located at ip:port(6381) 同样执行get也会被重定向 slot相关的一些命令 cluster keyslot 计算key对应的slotcluster coutkeysinslot 获取slot槽位中key的个数cluster getkeysinslot 返回count个slot槽中的键 故障恢复 如果主节点下线从节点是否能够提升为主节点注意要等15秒 如果某一段插槽的主从都宕机了redis服务是否还能继续 要看 cluster-require-full-coverage 参数的值了 yes(默认值)整个集群都都无法提供服务了no宕机的这部分槽位数据全部不能使用其他槽位正常 springboot 整合redis集群 配置 # 集群节点(host:port)多个之间用逗号隔开 spring.redis.cluster.nodes192.168.200.129:6379,192.168.200.129:6380,192.168.200 .129:6381,192.168.200.129:6389,192.168.200.129:6390,192.168.200.129:6391 # 连接超时时间毫秒 spring.redis.timeout60000应用问题解决 缓存穿透 问题描述 当系统中引入redis缓存后一个请求进来后会先从redis缓存中查询缓存有就直接返回缓存中没有就去db中查询db中如果有就会将其丢到缓存中但是有些key对应更多数据在db中并不存在每次针对此次key的请求从缓存中取不到请求都会压到db从而可能压垮db。 比如用一个不存在的用户id获取用户信息不论缓存还是数据库都没有若黑客利用大量此类攻击可能压垮数据库 解决方案 对空值缓存 如果一个查询返回的数据为空不管数据库是否存在我们仍然把这个结果null进行缓存给其设置一个很短的过期时间最长不超过五分钟 设置可访问的名单白名单 使用redis中的bitmaps类型定义一个可以访问的名单名单id作为bitmaps的偏移量每次和bitmap里面的id进行比较如果访问的id不在bitmaps里面则进行拦截不允许访问 采用布隆过滤器 一个很长的二进制向量位图和一系列随机映射函数哈希函数可以用于检测一个元素是否在一个集合中优点空间效率和查询的时间都远超一般算法缺点有一定的误识别率和删除困难将所有可能存在的数据哈希到一个足够大的bitmaps中一个一定不存在的数据会被这个bitmaps拦截掉从而避免了对底层存储系统的查询压力 进行实时监控 实时监控可以设置黑名单限制对其提供服务 缓存击穿 问题描述 redis中某个热点key访问量很高的key过期此时大量请求同时过来发现缓存中没有命中这些请求都打到db上了导致db压力瞬时大增可能会打垮db这种情况成为缓存击穿。 现象 数据库访问压力瞬间增大redis里面没有出现大量的key过期redis正常运行 解决方案 预先设置热门数据适时调整过期时间 在redis高峰之前把热门数据提前存入到redis里面对缓存中这些热门数据进行监控实时调整过期时间。 使用锁 缓存中拿不到数据的时候此时不是立即去db中查询而是去获取分布式锁例如redis中的setnx拿到锁再去db中load数据没有拿到锁的线程休眠一段时间再重试整个获取数据的方法。 缓存雪崩 问题描述 key对应的数据存在但是极短时间内有大量的key集中过期此时若有大量的并发请求过来发现缓存没有数据大量的请求就会落到db上去加载数据会将db击垮导致服务奔溃。 缓存雪崩与缓存击穿的区别在于前者是大量的key集中过期而后者是某个热点key过期。 解决方案 构建多级缓存 nginx缓存redis缓存其他缓存 使用锁或队列 用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写从而避免失效时大量的并发请求落到底层存储系统上不适用高并发情况 监控缓存过期提前更新 监控缓存发下缓存快过期了提前对缓存进行更新。 将缓存失效时间分散开 在原有的失效时间基础上增加一个随机值比如1-5分钟随机这样缓存的过期时间重复率就会降低就很难引发集体失效的事件。 分布式锁 问题描述 随着业务发展的需要原单体单机部署的系统被演化成分布式集群系统后由于分布式系统多线程、多进程且分布在不同机器上这将使原单机部署情况下的并发控制锁策略失效单纯的Java API并不能提供分布式锁的能力为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问这就是分布式锁要解决的问题 分布式锁主流的实现方案 基于数据库实现分布式锁基于缓存redis等基于zookeeper 每一种分布式锁解决方案都有各自的优缺点性能redis最高可靠性zookeeper最高 解决方案使用redis实现分布式锁 命令 set key value NX PX 有效期毫秒 表示 当key不存在的时候设置其值为value且同时设置其有效期 eg set sku:1:info ok NX PX 10000**表示**当 sku:1:info 不存在的时候设置值为ok且有效期为1万毫秒 上锁过程 执行 set key value NX PX 有效期(毫秒) 命令返回ok表示执行成功则获取锁成功多个客户端并发执行此命令的时候redis可确保只有一个可以执行成功 客户端发起命令没有返回ok上锁失败会休眠重试返回ok表示上锁成功执行业务del释放锁结束 为什么设置过期时间 客户端获取锁后由于系统问题如系统宕机了会导致锁无法释放其他客户端就无法或锁了所以需要给锁指定一个使用期限。 设置的有效期太短了怎么办 客户端需要实现续命的功能 解决锁误删的问题 误删自己把别人持有的锁给删掉了 例如 线程A获取锁的时候设置的有效期是10秒但是执行业务的时候A程序突然卡主了超过了10秒此时这个锁就可能被其他线程拿到比如被线程B拿到了然后A从卡顿中恢复了继续执行业务业务执行完毕之后去执行了释放锁的操作此时A会执行del命令此时就出现了锁的误删导致的结果就是把B持有的锁给释放了然后其他线程又会获取这个锁挺严重的。 解决 获取锁的之前生成一个全局唯一id将这个id也丢到key对应的value中释放锁之前从redis中将这个id拿出来和本地的比较一下看看是不是自己的id如果是的再执行del释放锁的操作。 还是存在误删的可能原子操作问题 del之前会先从redis中读取id然后和本地id对比一下如果一致则执行删除伪代码如下 1.判断redis.get(key).id本地id是否相当如果是则执行2 2.del key此时如果执行step2的时候系统卡主了比如卡主了10秒然后redis才收到这个期间锁可能又被其他线程获取了此时又发生了误删的操作。 根本原因是判断和删除这2个步骤对redis来说不是原子操作导致的。 需要使用Lua脚本来解决 终极方案Lua脚本来释放锁 将复杂的或者多步的redis操作写为一个脚本一次提交给redis执行减少反复连接redis的次数提升性能。 Lua脚本类似于redis事务有一定的原子性不会被其他命令插队可以完成一些redis事务的操作。 redis的LUA脚本功能只能在redis2.6以上版本才能使用 Autowired private RedisTemplateString, String redisTemplate;public String lock(){String lockKey k1;String uuid UUID.randomUUID().toString();//获取锁有效期10秒if (redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 10, TimeUnit.SECONDS)){//业务处理//使用lua脚本释放锁String script if redis.call(get,KEY[1])ARGV[1] then return redis.call(del,KEYS[1]) else return 0 end;DefaultRedisScriptLong redisScript new DefaultRedisScript();redisScript.setScriptText(script);redisScript.setResultType(Long.class);Long result redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);System.out.println(result);return 获取锁成功;}else{return 加锁失败;} }分布式锁总结 为了确保分布式锁可用至少需要确保分布式锁的实现同时满足以下四个条件 互斥性在任意时刻只能有一个客户端能够持有锁不发生死锁即使有一个客户端在持有锁期间崩溃而没有释放锁也能够保证后续其他客户端能够加锁加锁和解锁必须是同一个客户端客户端不能把别人的锁给解了加锁和解锁必须有原子性
http://www.hkea.cn/news/14463144/

相关文章:

  • 自已建设网站流程wordpress自定义文章类型如何调用
  • 自己做免费手机网站软件网站建设基本流程
  • wordpress主题 动漫从百万到千万 网站怎么优化
  • 常见cms网站源码下载网站弹出广告gif出处
  • 营销网站的建立网站建设力度
  • 河源哪里做网站网站开发和运行 法律
  • 数字货币网站开发需求wordpress搭建cms网站
  • 怎么搭建网站视频教程网站销售怎么样
  • 购物网站运作建筑平面设计图
  • wordpress删除用户下所有文章上海网站营销seo电话
  • 没有网站怎么做排名优化网页怎么制作二维码
  • wordpress网站建设教程海外seo网站推广
  • 营销型网站跟云网站网站建设与规划专业
  • 免费做网站表白电商网站设计欣赏
  • 怎么创建网站要钱吗做复印机的模板网站
  • 教育网站图片中国学校网站前台模板
  • 找公司开发网站美容院做免费推广哪个网站
  • 像淘宝购物网站建设需要哪些专业人员?电子商务网站详细设计
  • 网站集约化建设优点wordpress 聚合6
  • 微信营销微网站建设石家庄网站建设网站
  • 深圳市住建局网站网上营销手段
  • 搭建网站的价格怎么修改自己公司网站
  • 广东省工程建设注册中心网站哪里有网站设计学
  • 网站开发路径一个公司的官网怎么做
  • 一站式建站价格襄阳建设21网站
  • 教育培训网站模板下载h5制作软件 知乎
  • 检测设备技术支持东莞网站建设能够免费换友链的平台
  • 建设网站前准备资料wordpress 模板 橱窗
  • 湖南中核建设工程公司官方网站上海网站制作设计公司
  • 工信部网站域名备案手机网页版