<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          三歪看完了這篇Redis,有點飄

          共 27739字,需瀏覽 56分鐘

           ·

          2020-08-17 16:46


          本文公眾號來源:Kerwin啊
          作者:柯小賢
          本文已收錄至我的GitHub

          Windows Redis

          安裝

          鏈接: https://pan.baidu.com/s/1MJnzX_qRuNXJI09euzkPGA 提取碼: 2c6w 復制這段內(nèi)容后打開百度網(wǎng)盤手機App,操作更方便哦

          無腦下一步即可

          使用

          出現(xiàn)錯誤:

          creating server tcp listening socket 127.0.0.1:6379: bind No error

          解決方案:

          1. redis-cli.exe
          2. shutdown
          3. exit
          4. redis-server.exe redis.windows.conf

          啟動:redis-server.exe redis.windows.conf

          客戶端啟動:redis-cli.exe ?(不修改配置的話默認即可)

          redis-cli.exe -h 127.0.0.1 -p 6379 -a password

          基本文件說明

          可執(zhí)行文件作用說明
          redis-serverredis服務
          redis-cliredis命令行工具
          redis-benchmark基準性能測試工具
          redis-check-aofAOF持久化文件檢測和修復工具
          redis-check-dumpRDB持久化文件檢測和修復工具
          redis-sentinel啟動哨兵
          redis-tribcluster集群構建工具

          基礎命令

          命令說明
          keys ?*redis允許模糊查詢key  有3個通配符 ?*、?、[]
          del ? ? ?key刪除key
          exists kxm判斷是否存在
          expire key 20設置過期時間 - 秒
          pexpire key 20000設置過期時間 - 毫秒
          move kxm 2移動key到指定位置庫中 ?2號庫
          persist key移除過期時間,key將會永久存在 ? 成功設置返回1 ?否則返回0
          pttl key以毫秒為單位返回 key 的剩余的過期時間
          ttl key以秒為單位,返回給定 key 的剩余生存時間
          randomkey從當前數(shù)據(jù)庫中隨機返回一個 key
          rename key newkxy更改key的名字,如果重復了會覆蓋
          renamenx kxm key僅當 newkey 不存在時,將 key 改名為 newkey
          type key返回 key 所儲存的值的類型
          select 0選擇第一個庫
          ping返回PONG 表示連接正常
          quit關閉當前連接

          字符串命令

          命令說明
          set key aaa設置指定 key 的值
          get key獲取指定 key 的值
          getrange key 0 1返回 key 中字符串值的子字符 ?包含 0 和 1 包含關系
          getset key aaaaaaaa將給定 key 的值設為 value ,并返回 key 的舊值(old value)
          mget key kxm獲取所有(一個或多個)給定 key 的值
          setex test 5 "this is my test"將值 value 關聯(lián)到 key ,并將 key 的過期時間設為 seconds (以秒為單位)
          setnx test test只有在 key 不存在時設置 key 的值 (用于分布式鎖)
          strlen test返回 key 所儲存的字符串值的長度
          mset key1 "1" key2 "2"同時設置一個或多個 key-value 對
          msetnx key3 "a" key2 "b"同時設置一個或多個 key-value 對,當且僅當所有給定 key 都不存在 ? 其中一個失敗則全部失敗
          incr key將 key 中儲存的數(shù)字值增一 -> ?key的值 比如為 數(shù)字類型字符串 ?返回增加后的結果
          incrby num 1000將 key 中儲存的數(shù)字值增指定的值 -> ?key的值 比如為 數(shù)字類型字符串 ?返回增加后的結果
          decr key同 -> 減一
          decrby num 500同 -> 減指定值
          append key 1123123如果 key 已經(jīng)存在并且是一個字符串, APPEND 命令將指定的 value 追加到該 key 原來值(value)的末尾 ?返回字符串長度

          哈希(Hash)命令

          命令說明
          hdel key field1 [field2]刪除一個或多個哈希表字段
          hexistskey field查看哈希表 key 中,指定的字段是否存在
          hget key field獲取存儲在哈希表中指定字段的值
          hgetall key獲取在哈希表中指定 key 的所有字段和值
          hincrby hash yeary 1為哈希表 key 中的指定字段的整數(shù)值加上增量 increment
          hkeys hash獲取所有哈希表中的字段
          hlen hash獲取哈希表中字段的數(shù)量
          hmget hash name year獲取所有給定字段的值
          hmset hash name "i am kxm" year 24同時將多個 field-value (域-值)對設置到哈希表 key 中
          hset hash name kxm將哈希表 key 中的字段 field 的值設為 value
          hsetnx key field value只有在字段 field 不存在時,設置哈希表字段的值
          hvals hash獲取哈希表中所有值
          hexists hash name是否存在

          編碼: ?field value 值由 ziplist 及 hashtable 兩種編碼格式

          字段較少的時候采用ziplist,字段較多的時候會變成hashtable編碼

          列表(List)命令

          Redis列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)

          一個列表最多可以包含 232 - 1 個元素 (4294967295, 每個列表超過40億個元素)

          容量 -> 集合,有序集合也是如此

          命令說明
          lpush list php將一個值插入到列表頭部 ?返回列表長度
          lindex list 0通過索引獲取列表中的元素
          blpop ?key1 [key2 ] timeout移出并獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發(fā)現(xiàn)可彈出元素為止
          brpop ?key1 [key2 ] timeout移出并獲取列表的最后一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發(fā)現(xiàn)可彈出元素為止
          linsert list before 3 4在值 3 前插入 4 ? 前即為頂
          linsert list after 4 5在值4 后插入5
          llen list獲取列表長度
          lpop list移出并獲取列表的第一個元素
          lpush list c++ c將一個或多個值插入到列表頭部
          lrange list 0 1獲取列表指定范圍內(nèi)的元素 ?包含0和1 ? -1 代表所有 (lrange list 0 -1)
          lrem list 1 c移除list 集合中 值為 c 的 ?一個元素, ?1 代表count 即移除幾個
          lset list 0 "this is update"通過索引設置列表元素的值
          ltrim list 1 5對一個列表進行修剪(trim),就是說,讓列表只保留指定區(qū)間內(nèi)的元素,不在指定區(qū)間之內(nèi)的元素都將被刪除
          rpop list移除列表的最后一個元素,返回值為移除的元素
          rpush list newvalue3從底部添加新值
          rpoplpush list list2轉(zhuǎn)移列表的數(shù)據(jù)

          集合(Set)命令

          Set 是 String 類型的無序集合。集合成員是唯一的,這就意味著集合中不能出現(xiàn)重復的數(shù)據(jù)

          命令說明
          sadd set java php c c++ python向集合添加一個或多個成員
          scard set獲取集合的成員數(shù)
          sdiff key1 [key2]返回給定所有集合的差集 ?數(shù)學含義差集
          sdiffstore curr set newset ?(sdiffstore destination key1 [key2])把set和 newset的差值存儲到curr中
          sinter set newset返回給定所有集合的交集
          sinterstore curr set newset ?(sinterstoredestination key1 [key2])
          sismember set c#判斷 member 元素是否是集合 key 的成員
          smembers set返回集合中的所有成員
          srandmember set 2隨機抽取兩個key (抽獎實現(xiàn)美滋滋)
          smove set newtest java (smove source destination member)將 member 元素從 source 集合移動到 destination 集合
          sunion set newset返回所有給定集合的并集
          srem set java刪除
          spop set從集合中彈出一個元素
          sdiff | sinter | sunion操作:集合間運算:差集

          有序集合(sorted set)命令

          Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重復的成員。

          不同的是每個元素都會關聯(lián)一個double類型的分數(shù)。redis正是通過分數(shù)來為集合中的成員進行從小到大的排序。

          有序集合的成員是唯一的,但分數(shù)(score)卻可以重復。

          命令說明
          zadd sort 1 java 2 python向有序集合添加一個或多個成員,或者更新已存在成員的分數(shù)
          zcard sort獲取有序集合的成員數(shù)
          zcount sort 0 1計算在有序集合中指定區(qū)間分數(shù)的成員數(shù)
          zincrby sort 500 java有序集合中對指定成員的分數(shù)加上增量 increment
          zscore sort java返回有序集中,成員的分數(shù)值
          zrange sort 0 -1獲取指定序號的值,-1代表全部
          zrangebyscore sort 0 5分數(shù)符合范圍的值
          zrangebyscore sort 0 5 limit 0 1分頁 limit ?0代表頁碼,1代表每頁顯示數(shù)量
          zrem sort java移除元素
          zremrangebyrank sort 0 1按照排名范圍刪除元素
          zremrangebyscore sort 0 1按照分數(shù)范圍刪除元素
          zrevrank sort c#返回有序集合中指定成員的排名,有序集成員按分數(shù)值遞減(從大到小)排序

          發(fā)布訂閱

          開啟兩個客戶端

          A客戶端訂閱頻道:subscribe redisChat ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(頻道名字為 redisChat)

          B客戶端發(fā)布內(nèi)容:publish redisChat "Hello, this is my wor" ? (內(nèi)容是 hello....)

          A客戶端即為自動收到內(nèi)容, 原理圖如下:

          命令說明
          pubsub channels查看當前redis ?有多少個頻道
          pubsub numsub chat1查看某個頻道的訂閱者數(shù)量
          unsubscrible chat1退訂指定頻道
          psubscribe java.*訂閱一組頻道

          Redis 事務

          Redis 事務可以一次執(zhí)行多個命令, 并且?guī)в幸韵氯齻€重要的保證:

          • 批量操作在發(fā)送 EXEC 命令前被放入隊列緩存
          • 收到 EXEC 命令后進入事務執(zhí)行,事務中任意命令執(zhí)行失敗,其余的命令依然被執(zhí)行
          • 在事務執(zhí)行過程,其他客戶端提交的命令請求不會插入到事務執(zhí)行命令序列中

          一個事務從開始到執(zhí)行會經(jīng)歷以下三個階段:

          • 開始事務
          • 命令入隊
          • 執(zhí)行事務

          注意:redis事務和數(shù)據(jù)庫事務不同,redis事務出錯后最大的特點是,一剩下的命令會繼續(xù)執(zhí)行,二出錯的數(shù)據(jù)不會回滾

          命令說明
          multi標記一個事務開始
          exec執(zhí)行事務
          discard事務開始后輸入命令入隊過程中,中止事務
          watch key監(jiān)視一個(或多個) key ,如果在事務執(zhí)行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷
          unwatch取消 WATCH 命令對所有 key 的監(jiān)視

          Redis 服務器命令

          命令說明
          flushall刪除所有數(shù)據(jù)庫的所有key
          flushdb刪除當前數(shù)據(jù)庫的所有key
          save同步保存數(shù)據(jù)到硬盤

          Redis 數(shù)據(jù)備份與恢復

          Redis SAVE 命令用于創(chuàng)建當前數(shù)據(jù)庫的備份

          如果需要恢復數(shù)據(jù),只需將備份文件 (dump.rdb) 移動到 redis 安裝目錄并啟動服務即可。獲取 redis 目錄可以使用 CONFIG 命令

          Redis 性能測試

          redis 性能測試的基本命令如下:

          redis目錄執(zhí)行:redis-benchmark [option]?[option value]

          //?會返回各種操作的性能報告(100連接,10000請求)
          redis-benchmark?-h?127.0.0.1?-p?6379?-c?100?-n?10000

          //?100個字節(jié)作為value值進行壓測
          redis-benchmark?-h?127.0.0.1?-p?6379?-q?-d?100

          Java Redis

          Jedis


          <dependency>
          ????<groupId>redis.clientsgroupId>
          ????<artifactId>jedisartifactId>
          ????<version>2.8.2version>
          dependency>

          Jedis配置

          ############# redis Config #############
          # Redis數(shù)據(jù)庫索引(默認為0)
          spring.redis.database=0
          # Redis服務器地址
          spring.redis.host=120.79.88.17
          # Redis服務器連接端口
          spring.redis.port=6379
          # Redis服務器連接密碼(默認為空)
          spring.redis.password=123456
          # 連接池中的最大空閑連接
          spring.redis.jedis.pool.max-idle=8
          # 連接池中的最小空閑連接
          spring.redis.jedis.pool.min-idle=0

          JedisConfig

          @Configuration
          public?class?JedisConfig?extends?CachingConfigurerSupport?{

          ????@Value("${spring.redis.host}")
          ????private?String?host;

          ????@Value("${spring.redis.port}")
          ????private?int?port;

          ????@Value("${spring.redis.password}")
          ????private?String?password;

          ????@Value("${spring.redis.max-idle}")
          ????private?Integer?maxIdle;

          ????@Value("${spring.redis.min-idle}")
          ????private?Integer?minIdle;

          ????@Bean
          ????public?JedisPool?redisPoolFactory(){
          ????????JedisPoolConfig?jedisPoolConfig?=?new?JedisPoolConfig();
          ????????jedisPoolConfig.setMaxIdle(maxIdle);
          ????????jedisPoolConfig.setMinIdle(minIdle);
          ????????jedisPoolConfig.setMaxWaitMillis(3000L);
          ????????int?timeOut?=?3;
          ????????return??new?JedisPool(jedisPoolConfig,?host,?port,?timeOut,?password);
          ????}
          }

          基礎使用

          @RunWith(SpringRunner.class)
          @SpringBootTest(classes?=?KerwinBootsApplication.class)
          public?class?ApplicationTests?{

          ????@Resource
          ????JedisPool?jedisPool;

          ????@Test
          ????public?void?testJedis?()?{
          ????????Jedis?jedis?=?jedisPool.getResource();
          ????????jedis.set("year",?String.valueOf(24));
          ????}
          }

          SpringBoot redis staeter RedisTemplate


          <dependency>
          ????<groupId>org.springframework.bootgroupId>
          ????<artifactId>spring-boot-starter-data-redisartifactId>
          dependency>


          <dependency>
          ????<groupId>org.apache.commonsgroupId>
          ????<artifactId>commons-pool2artifactId>
          dependency>
          ############# redis Config #############
          # Redis數(shù)據(jù)庫索引(默認為0)
          spring.redis.database=0
          # Redis服務器地址
          spring.redis.host=120.79.88.17
          # Redis服務器連接端口
          spring.redis.port=6379
          # Redis服務器連接密碼(默認為空)
          spring.redis.password=123456
          # 連接池最大連接數(shù)(使用負值表示沒有限制)
          spring.redis.jedis.pool.max-active=200
          # 連接池最大阻塞等待時間(使用負值表示沒有限制)
          spring.redis.jedis.pool.max-wait=1000ms
          # 連接池中的最大空閑連接
          spring.redis.jedis.pool.max-idle=8
          # 連接池中的最小空閑連接
          spring.redis.jedis.pool.min-idle=0
          # 連接超時時間(毫秒)
          spring.redis.timeout=1000ms
          //??Cache注解配置類
          @Configuration
          public?class?RedisCacheConfig?{

          ????@Bean
          ????public?KeyGenerator?simpleKeyGenerator()?{
          ????????return?(o,?method,?objects)?->?{
          ????????????StringBuilder?stringBuilder?=?new?StringBuilder();
          ????????????stringBuilder.append(o.getClass().getSimpleName());
          ????????????stringBuilder.append(".");
          ????????????stringBuilder.append(method.getName());
          ????????????stringBuilder.append("[");
          ????????????for?(Object?obj?:?objects)?{
          ????????????????stringBuilder.append(obj.toString());
          ????????????}
          ????????????stringBuilder.append("]");
          ????????????return?stringBuilder.toString();
          ????????};
          ????}

          ????@Bean
          ????public?CacheManager?cacheManager(RedisConnectionFactory?redisConnectionFactory)?{
          ????????return?new?RedisCacheManager(
          ????????????????RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),

          ????????????????//?默認策略,未配置的?key?會使用這個
          ????????????????this.getRedisCacheConfigurationWithTtl(15),

          ????????????????//?指定?key?策略
          ????????????????this.getRedisCacheConfigurationMap()
          ????????);
          ????}

          ????private?Map?getRedisCacheConfigurationMap()?{
          ????????Map?redisCacheConfigurationMap??=?new?HashMap<>(16);
          ????????redisCacheConfigurationMap.put("redisTest",?this.getRedisCacheConfigurationWithTtl(15));
          ????????return?redisCacheConfigurationMap;
          ????}

          ????private?RedisCacheConfiguration?getRedisCacheConfigurationWithTtl(Integer?seconds)?{
          ????????Jackson2JsonRedisSerializer?jackson2JsonRedisSerializer?=?new?Jackson2JsonRedisSerializer<>(Object.class);
          ????????ObjectMapper?om?=?new?ObjectMapper();
          ????????om.setVisibility(PropertyAccessor.ALL,?JsonAutoDetect.Visibility.ANY);
          ????????om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
          ????????jackson2JsonRedisSerializer.setObjectMapper(om);

          ????????RedisCacheConfiguration?redisCacheConfiguration?=?RedisCacheConfiguration.defaultCacheConfig();
          ????????redisCacheConfiguration?=?redisCacheConfiguration.serializeValuesWith(
          ????????????????RedisSerializationContext
          ????????????????????????.SerializationPair
          ????????????????????????.fromSerializer(jackson2JsonRedisSerializer)
          ????????).entryTtl(Duration.ofSeconds(seconds));
          ????????return?redisCacheConfiguration;
          ????}
          }
          //?RedisAutoConfiguration
          @Configuration
          @EnableCaching
          public?class?RedisConfig?{

          ????@Bean
          ????@SuppressWarnings("all")
          ????public?RedisTemplate?redisTemplate(RedisConnectionFactory?factory)?{

          ????????RedisTemplate?template?=?new?RedisTemplate();
          ????????template.setConnectionFactory(factory);

          ????????Jackson2JsonRedisSerializer?jackson2JsonRedisSerializer?=?new?Jackson2JsonRedisSerializer(Object.class);

          ????????ObjectMapper?om?=?new?ObjectMapper();
          ????????om.setVisibility(PropertyAccessor.ALL,?JsonAutoDetect.Visibility.ANY);
          ????????om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
          ????????jackson2JsonRedisSerializer.setObjectMapper(om);

          ????????StringRedisSerializer?stringRedisSerializer?=?new?StringRedisSerializer();

          ????????//?key采用String的序列化方式
          ????????template.setKeySerializer(stringRedisSerializer);

          ????????//?hash的key也采用String的序列化方式
          ????????template.setHashKeySerializer(stringRedisSerializer);

          ????????//?value序列化方式采用jackson
          ????????template.setValueSerializer(jackson2JsonRedisSerializer);

          ????????//?hash的value序列化方式采用jackson
          ????????template.setHashValueSerializer(jackson2JsonRedisSerializer);
          ????????template.afterPropertiesSet();
          ????????return?template;
          ????}
          }
          //?基礎使用
          @Resource
          RedisTemplate?redisTemplate;
          redisTemplate.opsForList().rightPush("user:1:order",?dataList.get(3).get("key").toString());

          //?注解使用
          @Cacheable(value?=?"redisTest")
          public?TestBean?testBeanAnnotation?()?{}

          Redis使用場景

          類型適用場景
          String緩存,限流,計數(shù)器,分布式鎖,分布式session
          Hash存儲用戶信息,用戶主頁訪問量,組合查詢
          List微博關注人時間軸列表,簡單隊列
          Set贊,踩,標簽,好友關系
          Zset排行榜

          或者簡單消息隊列,發(fā)布訂閱實施消息系統(tǒng)等等

          String - 緩存

          //?1.Cacheable?注解
          //?controller?調(diào)用?service?時自動判斷有沒有緩存,如果有就走redis緩存直接返回,如果沒有則數(shù)據(jù)庫然后自動放入redis中
          //?可以設置過期時間,KEY生成規(guī)則?(KEY生成規(guī)則基于?參數(shù)的toString方法)
          @Cacheable(value?=?"yearScore",?key?=?"#yearScore")
          @Override
          public?List?findBy?(YearScore?yearScore)?{}

          //?2.手動用緩存
          if?(redis.hasKey(???)?{
          ????return?....
          }?

          redis.set(find?from?DB)...

          String - 限流 | 計數(shù)器

          //?注:這只是一個最簡單的Demo 效率低,耗時舊,但核心就是這個意思
          //?計數(shù)器也是利用單線程incr...等等
          @RequestMapping("/redisLimit")
          public?String?testRedisLimit(String?uuid)?{
          ????if?(jedis.get(uuid)?!=?null)?{
          ????????Long?incr?=?jedis.incr(uuid);
          ????????if?(incr?>?MAX_LIMITTIME)?{
          ????????????return?"Failure?Request";
          ????????}?else?{
          ????????????return?"Success?Request";
          ????????}
          ????}

          ????//?設置Key?起始請求為1,10秒過期??->??實際寫法肯定封裝過,這里就是隨便一寫
          ????jedis.set(uuid,?"1");
          ????jedis.expire(uuid,?10);
          ????return?"Success?Request";
          }

          String - 分布式鎖 (重點)

          /***
          ?*?核心思路:
          ?*?????分布式服務調(diào)用時setnx,返回1證明拿到,用完了刪除,返回0就證明被鎖,等...
          ?*?????SET?KEY?value?[EX?seconds]?[PX?milliseconds]?[NX|XX]
          ?*?????EX?second:設置鍵的過期時間為second秒
          ?*?????PX?millisecond:設置鍵的過期時間為millisecond毫秒
          ?*???? NX:只在鍵不存在時,才對鍵進行設置操作
          ?*?????XX:只在鍵已經(jīng)存在時,才對鍵進行設置操作
          ?*
          ?*?1.設置鎖
          ?*?????A.?分布式業(yè)務統(tǒng)一Key
          ?*?????B.?設置Key過期時間
          ?*?????C.?設置隨機value,利用ThreadLocal?線程私有存儲隨機value
          ?*
          ?*?2.業(yè)務處理
          ?*?????...
          ?*
          ?*?3.解鎖
          ?*?????A.?無論如何必須解鎖?-?finally?(超時時間和finally?雙保證)
          ?*?????B.?要對比是否是本線程上的鎖,所以要對比線程私有value和存儲的value是否一致(避免把別人加鎖的東西刪除了)
          ?*/

          @RequestMapping("/redisLock")
          public?String?testRedisLock?()?{
          ????try?{
          ????????for(;;){
          ????????????RedisContextHolder.clear();
          ????????????String?uuid?=?UUID.randomUUID().toString();

          ????????????String?set?=?jedis.set(KEY,?uuid,?"NX",?"EX",?1000);
          ????????????RedisContextHolder.setValue(uuid);

          ????????????if?(!"OK".equals(set))?{
          ????????????????//?進入循環(huán)-可以短時間休眠
          ????????????}?else?{
          ????????????????//?獲取鎖成功?Do?Somethings....
          ????????????????break;
          ????????????}
          ????????}
          ????}?finally?{
          ????????//?解鎖?->?保證獲取數(shù)據(jù),判斷一致以及刪除數(shù)據(jù)三個操作是原子的,?因此如下寫法是不符合的
          ????????/*if?(RedisContextHolder.getValue()?!=?null?&&?jedis.get(KEY)?!=?null?&&?RedisContextHolder.getValue().equals(jedis.get(KEY)))?{
          ????????????????jedis.del(KEY);
          ????????????}*/


          ????????//?正確姿勢?->?使用Lua腳本,保證原子性
          ????????String?luaScript?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('del',KEYS[1])?else?return?0?end";
          ????????Object?eval?=?jedis.eval(luaScript,?Collections.singletonList(KEY),?Collections.singletonList(RedisContextHolder.getValue()));
          ????}
          ????return?"鎖創(chuàng)建成功-業(yè)務處理成功";
          }

          String - 分布式Session(重點)

          // 1.首先明白為什么需要分布式session -> nginx負載均衡?分發(fā)到不同的Tomcat,即使利用IP分發(fā),可以利用request獲取session,但是其中一個掛了,怎么辦???所以需要分布式session

          注意理解其中的區(qū)別??A服務-用戶校驗服務??B服務-業(yè)務層

          情況A:
          A,B 服務單機部署:
          cookie:登錄成功后,存儲信息到cookie,A服務自身通過request設置session,獲取session,B服務通過唯一key或者userid 查詢數(shù)據(jù)庫獲取用戶信息

          cookie+redis:登錄成功后,存儲信息到cookie,A服務自身通過request設置session,獲取session,B服務通過唯一key或者userid 查詢redis獲取用戶信息


          情況B:
          A服務多節(jié)點部署,B服務多節(jié)點部署
          B服務獲取用戶信息的方式其實是不重要的,必然要查,要么從數(shù)據(jù)庫,要么從cookie

          A服務:登錄成功后,存儲唯一key到cookie,?與此同時,A服務需要把session(KEY-UserInfo)同步到redis中,不能存在單純的request(否則nginx分發(fā)到另一個服務器就完犢子了)

          官方實現(xiàn):
          spring-session-data-redis
          有一個內(nèi)置攔截器,攔截request,session通過redis交互,普通使用代碼依然是request.getSession....??但是實際上這個session的值已經(jīng)被該組件攔截,通過redis進行同步了

          List 簡單隊列-棧

          //?說白了利用redis?-?list數(shù)據(jù)結構?支持從左從右push,從左從右pop
          @Component
          public?class?RedisStack?{

          ????@Resource
          ????Jedis?jedis;

          ????private?final?static?String?KEY?=?"Stack";

          ????/**?push?**/
          ????public?void?push?(String?value)?{
          ????????jedis.lpush(KEY,?value);
          ????}

          ????/**?pop?**/
          ????public?String?pop?()?{
          ????????return?jedis.lpop(KEY);
          ????}
          }
          @Component
          public?class?RedisQueue?{

          ????@Resource
          ????JedisPool?jedisPool;

          ????private?final?static?String?KEY?=?"Queue";

          ????/**?push?**/
          ????public?void?push?(String?value)?{
          ????????Jedis?jedis?=?jedisPool.getResource();
          ????????jedis.lpush(KEY,?value);
          ????}

          ????/**?pop?**/
          ????public?String?pop?()?{
          ????????Jedis?jedis?=?jedisPool.getResource();
          ????????return?jedis.rpop(KEY);
          ????}
          }

          List 社交類APP - 好友列表

          根據(jù)時間顯示好友,多個好友列表,求交集,并集??顯示共同好友等等...
          疑問:難道大廠真的用redis存這些數(shù)據(jù)嗎???多大的量啊... 我個人認為實際是數(shù)據(jù)庫存用戶id,然后用算法去處理,更省空間

          Set 抽獎 | 好友關系(合,并,交集)

          //?插入key?及用戶id
          sadd?cat:1?001?002?003?004?005?006

          //?返回抽獎參與人數(shù)
          scard?cat:1

          //?隨機抽取一個
          srandmember?cat:1

          //?隨機抽取一人,并移除
          spop?cat:1

          Zset 排行榜

          根據(jù)分數(shù)實現(xiàn)有序列表
          微博熱搜:每點擊一次?分數(shù)+1 即可

          ---?不用數(shù)據(jù)庫目的是因為避免order?by?進行全表掃描

          常見面試題

          Q1:為什么Redis能這么快

          1.Redis完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作,執(zhí)行效率高。
          2.Redis使用單進程單線程模型的(K,V)數(shù)據(jù)庫,將數(shù)據(jù)存儲在內(nèi)存中,存取均不會受到硬盤IO的限制,因此其執(zhí)行速度極快,另外單線程也能處理高并發(fā)請求,還可以避免頻繁上下文切換和鎖的競爭,同時由于單線程操作,也可以避免各種鎖的使用,進一步提高效率
          3.數(shù)據(jù)結構簡單,對數(shù)據(jù)操作也簡單,Redis不使用表,不會強制用戶對各個關系進行關聯(lián),不會有復雜的關系限制,其存儲結構就是鍵值對,類似于HashMap,HashMap最大的優(yōu)點就是存取的時間復雜度為O(1)
          5.C語言編寫,效率更高
          6.Redis使用多路I/O復用模型,為非阻塞IO
          7.有專門設計的RESP協(xié)議

          針對第四點進行說明 ->

          常見的IO模型有四種:

          • 同步阻塞IO(Blocking IO):即傳統(tǒng)的IO模型。

          • 同步非阻塞IO(Non-blocking IO):默認創(chuàng)建的socket都是阻塞的,非阻塞IO要求socket被設置為NONBLOCK。注意這里所說的NIO并非Java的NIO(New IO)庫。

          • IO多路復用(IO Multiplexing):即經(jīng)典的Reactor設計模式,有時也稱為異步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。

          • 異步IO(Asynchronous IO):即經(jīng)典的Proactor設計模式,也稱為異步非阻塞IO

          同步異步,阻塞非阻塞的概念:

          假設Redis采用同步阻塞IO:

          Redis主程序(服務端 單線程)-> 多個客戶端連接(真實情況是如開發(fā)人員連接redis,程序 redispool連接redis),這每一個都對應著一個客戶端,假設為100個客戶端,其中一個進行交互時候,如果采用同步阻塞式,那么剩下的99個都需要原地等待,這勢必是不科學的。

          IO多路復用

          Redis 采用 ?I/O 多路復用模型

          I/O 多路復用模型中,最重要的函數(shù)調(diào)用就是 select,該方法的能夠同時監(jiān)控多個文件描述符的可讀可寫情況,當其中的某些文件描述符可讀或者可寫時,select 方法就會返回可讀以及可寫的文件描述符個數(shù)

          注:redis默認使用的是更加優(yōu)化的算法:epoll


          selectpollepoll
          操作方式遍歷遍歷回調(diào)
          底層實現(xiàn)數(shù)組鏈表哈希表
          IO效率每次調(diào)用都進行線性遍歷,時間復雜度為O(n)每次調(diào)用都進行線性遍歷,時間復雜度為O(n)事件通知方式,每當fd就緒,系統(tǒng)注冊的回調(diào)函數(shù)就會被調(diào)用,將就緒fd放到readyList里面,時間復雜度O(1)
          最大連接數(shù)1024(x86)或2048(x64)無上限無上限

          所以我們可以說Redis是這樣的:服務端單線程毫無疑問,多客戶端連接時候,如果客戶端沒有發(fā)起任何動作,則服務端會把其視為不活躍的IO流,將其掛起,當有真正的動作時,會通過回調(diào)的方式執(zhí)行相應的事件

          Q2:從海量Key里查詢出某一個固定前綴的Key

          A. 笨辦法:KEYS [pattern] ?注意key很多的話,這樣做肯定會出問題,造成redis崩潰

          B. SCAN cursor [MATCH pattern] [COUNT count] 游標方式查找

          Q3:如何通過Redis實現(xiàn)分布式鎖

          見上文

          Q4:如何實現(xiàn)異步隊列

          上文說到利用?redis-list?實現(xiàn)隊列
          假設場景:A服務生產(chǎn)數(shù)據(jù)?-?B服務消費數(shù)據(jù),即可利用此種模型構造-生產(chǎn)消費者模型

          1.?使用Redis中的List作為隊列
          2.使用BLPOP?key?[key...]?timeout??->?LPOP?key?[key?...]?timeout:阻塞直到隊列有消息或者超時
          (方案二:解決方案一中,拿數(shù)據(jù)的時,生產(chǎn)者尚未生產(chǎn)的情況)

          3.pub/sub:主題訂閱者模式
          基于reds的終極方案,上文有介紹,基于發(fā)布/訂閱模式
          缺點:消息的發(fā)布是無狀態(tài)的,無法保證可達。對于發(fā)布者來說,消息是“即發(fā)即失”的,此時如果某個消費者在生產(chǎn)者發(fā)布消息時下線,重新上線之后,是無法接收該消息的,要解決該問題需要使用專業(yè)的消息隊列

          Q5:Redis支持的數(shù)據(jù)類型?

          見上文

          Q6:什么是Redis持久化?Redis有哪幾種持久化方式?優(yōu)缺點是什么?

          持久化就是把內(nèi)存的數(shù)據(jù)寫到磁盤中去,防止服務宕機了內(nèi)存數(shù)據(jù)丟失。

          Redis 提供了兩種持久化方式:RDB(默認) 和AOF

          RDB:

          rdb是Redis DataBase縮寫

          功能核心函數(shù)rdbSave(生成RDB文件)和rdbLoad(從文件加載內(nèi)存)兩個函數(shù)

          RDB: ?把當前進程數(shù)據(jù)生成快照文件保存到硬盤的過程。分為手動觸發(fā)和自動觸發(fā)

          手動觸發(fā) -> ?save (不推薦,阻塞嚴重) ?bgsave -> (save的優(yōu)化版,微秒級阻塞)

          shutdowm 關閉服務時,如果沒有配置AOF,則會使用bgsave持久化數(shù)據(jù)

          bgsave - 工作原理

          會從當前父進程fork一個子進程,然后生成rdb文件

          缺點:頻率低,無法做到實時持久化

          AOF:

          Aof是Append-only file縮寫,AOF文件存儲的也是RESP協(xié)議

          每當執(zhí)行服務器(定時)任務或者函數(shù)時flushAppendOnlyFile 函數(shù)都會被調(diào)用, 這個函數(shù)執(zhí)行以下兩個工作

          aof寫入保存:

          WRITE:根據(jù)條件,將 aof_buf 中的緩存寫入到 AOF 文件

          SAVE:根據(jù)條件,調(diào)用 fsync 或 fdatasync 函數(shù),將 AOF 文件保存到磁盤中。

          存儲結構:

          內(nèi)容是redis通訊協(xié)議(RESP )格式的命令文本存儲

          原理:

          相當于存儲了redis的執(zhí)行命令(類似mysql的sql語句日志),數(shù)據(jù)的完整性和一致性更高

          比較

          1、aof文件比rdb更新頻率高

          2、aof比rdb更安全

          3、rdb性能更好

          PS:正確停止redis服務 應該基于連接命令 加再上 shutdown -> 否則數(shù)據(jù)持久化會出現(xiàn)問題

          Q7:redis通訊協(xié)議(RESP)

          Redis 即 REmote Dictionary Server (遠程字典服務);

          而Redis的協(xié)議規(guī)范是 Redis Serialization Protocol (Redis序列化協(xié)議)

          RESP 是redis客戶端和服務端之前使用的一種通訊協(xié)議;

          RESP 的特點:實現(xiàn)簡單、快速解析、可讀性好

          協(xié)議如下:

          客戶端以規(guī)定格式的形式發(fā)送命令給服務器

          set?key value 協(xié)議翻譯如下:

          *?3????->??表示以下有幾組命令

          $?3????->??表示命令長度是3
          SET

          $6?????->??表示長度是6
          keykey

          $5?????->??表示長度是5
          value

          完整即:
          *?3
          $?3
          SET
          $6
          keykey
          $5?
          value


          服務器在執(zhí)行最后一條命令后,返回結果,返回格式如下:

          For Simple Strings the first byte of the reply is "+" 回復

          For Errors the first byte of the reply is "-" 錯誤

          For Integers the first byte of the reply is ":" 整數(shù)

          For Bulk Strings the first byte of the reply is "$" 字符串

          For Arrays the first byte of the reply is "*" 數(shù)組

          //?偽造6379?redis-服務端,監(jiān)聽??jedis發(fā)送的協(xié)議內(nèi)容
          public?class?SocketApp?{
          ????
          ????/***
          ?????*?監(jiān)聽?6379?傳輸?shù)臄?shù)據(jù)
          ?????*?JVM端口需要進行設置
          ?????*/

          ????public?static?void?main(String[]?args)??{
          ????????try?{
          ????????????ServerSocket?serverSocket?=?new?ServerSocket(6379);
          ????????????Socket?redis?=?serverSocket.accept();
          ????????????byte[]?result?=?new?byte[2048];
          ????????????redis.getInputStream().read(result);
          ????????????System.out.println(new?String(result));
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          }

          //?jedis連接-發(fā)送命令
          public?class?App?{
          ????public?static?void?main(String[]?args){
          ????????Jedis?jedis?=?new?Jedis("127.0.0.1");
          ????????jedis.set("key",?"This?is?value.");
          ????????jedis.close();
          ????}
          }

          //?監(jiān)聽命令內(nèi)容如下:
          *3
          $3
          SET
          $3
          key
          $14

          Q8:redis架構有哪些

          單節(jié)點

          主從復制

          Master-slave??主從賦值,此種結構可以考慮關閉master的持久化,只讓從數(shù)據(jù)庫進行持久化,另外可以通過讀寫分離,緩解主服務器壓力

          哨兵

          Redis sentinel 是一個分布式系統(tǒng)中監(jiān)控 redis 主從服務器,并在主服務器下線時自動進行故障轉(zhuǎn)移。其中三個特性:

          監(jiān)控(Monitoring):??? Sentinel ?會不斷地檢查你的主服務器和從服務器是否運作正常。

          提醒(Notification):?當被監(jiān)控的某個 Redis 服務器出現(xiàn)問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發(fā)送通知。

          自動故障遷移(Automatic failover):?當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

          特點:
          1、保證高可用
          2、監(jiān)控各個節(jié)點
          3、自動故障遷移

          缺點:主從模式,切換需要時間丟數(shù)據(jù)
          沒有解決?master?寫的壓力

          集群

          從redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用無中心結構,每個節(jié)點保存數(shù)據(jù)和整個集群狀態(tài),每個節(jié)點都和其他所有節(jié)點連接。

          特點:

          1、無中心架構(不存在哪個節(jié)點影響性能瓶頸),少了 proxy 層。

          2、數(shù)據(jù)按照 slot 存儲分布在多個節(jié)點,節(jié)點間數(shù)據(jù)共享,可動態(tài)調(diào)整數(shù)據(jù)分布。

          3、可擴展性,可線性擴展到 1000 個節(jié)點,節(jié)點可動態(tài)添加或刪除。

          4、高可用性,部分節(jié)點不可用時,集群仍可用。通過增加 Slave 做備份數(shù)據(jù)副本

          5、實現(xiàn)故障自動 failover,節(jié)點之間通過 gossip 協(xié)議交換狀態(tài)信息,用投票機制完成 Slave到 Master 的角色提升。

          缺點:

          1、資源隔離性較差,容易出現(xiàn)相互影響的情況。

          2、數(shù)據(jù)通過異步復制,不保證數(shù)據(jù)的強一致性

          Q9:Redis集群-如何從海量數(shù)據(jù)里快速找到所需?

          • 分片

            按照某種規(guī)則去劃分數(shù)據(jù),分散存儲在多個節(jié)點上。通過將數(shù)據(jù)分到多個Redis服務器上,來減輕單個Redis服務器的壓力。

          • 一致性Hash算法

            既然要將數(shù)據(jù)進行分片,那么通常的做法就是獲取節(jié)點的Hash值,然后根據(jù)節(jié)點數(shù)求模,但這樣的方法有明顯的弊端,當Redis節(jié)點數(shù)需要動態(tài)增加或減少的時候,會造成大量的Key無法被命中。所以Redis中引入了一致性Hash算法。該算法對2^32 取模,將Hash值空間組成虛擬的圓環(huán),整個圓環(huán)按順時針方向組織,每個節(jié)點依次為0、1、2...2^32-1,之后將每個服務器進行Hash運算,確定服務器在這個Hash環(huán)上的地址,確定了服務器地址后,對數(shù)據(jù)使用同樣的Hash算法,將數(shù)據(jù)定位到特定的Redis服務器上。如果定位到的地方?jīng)]有Redis服務器實例,則繼續(xù)順時針尋找,找到的第一臺服務器即該數(shù)據(jù)最終的服務器位置。

            一致性Hash算法

          Hash環(huán)的數(shù)據(jù)傾斜問題

          Hash環(huán)在服務器節(jié)點很少的時候,容易遇到服務器節(jié)點不均勻的問題,這會造成數(shù)據(jù)傾斜,數(shù)據(jù)傾斜指的是被緩存的對象大部分集中在Redis集群的其中一臺或幾臺服務器上。

          如上圖,一致性Hash算法運算后的數(shù)據(jù)大部分被存放在A節(jié)點上,而B節(jié)點只存放了少量的數(shù)據(jù),久而久之A節(jié)點將被撐爆。引入虛擬節(jié)點

          例如上圖:將NodeA和NodeB兩個節(jié)點分為Node A#1-A#3 NodeB#1-B#3。

          Q10:什么是緩存穿透?如何避免?什么是緩存雪崩?如何避免?什么是緩存擊穿?如何避免?

          緩存穿透

          一般的緩存系統(tǒng),都是按照key去緩存查詢,如果不存在對應的value,就應該去后端系統(tǒng)查找(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對后端系統(tǒng)造成很大的壓力。這就叫做緩存穿透。

          如何避免?

          1:對查詢結果為空的情況也進行緩存,緩存時間設置短一點,或者該key對應的數(shù)據(jù)insert了之后清理緩存。

          2:對一定不存在的key進行過濾??梢园阉械目赡艽嬖诘膋ey放到一個大的Bitmap中,查詢時通過該bitmap過濾。

          3:由于請求參數(shù)是不合法的(每次都請求不存在的參數(shù)),于是我們可以使用布隆過濾器(Bloomfilter)或壓縮filter提前進行攔截,不合法就不讓這個請求進入到數(shù)據(jù)庫層

          緩存雪崩

          當緩存服務器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給后端系統(tǒng)帶來很大壓力。導致系統(tǒng)崩潰。

          如何避免?

          1:在緩存失效后,通過加鎖或者隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。

          2:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置為短期,A2設置為長期

          3:不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻。

          4:啟用限流策略,盡量避免數(shù)據(jù)庫被干掉

          緩存擊穿

          概念 一個存在的key,在緩存過期的一刻,同時有大量的請求,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增。

          解決方案 A. 在訪問key之前,采用SETNX(set if not exists)來設置另一個短期key來鎖住當前key的訪問,訪問結束再刪除該短期key

          B. 服務層處理 - 方法加鎖 + 雙重校驗:

          //?鎖-實例
          private?Lock?lock?=?new?ReentrantLock();

          public?String?getProductImgUrlById(String?id){
          ????//?獲取緩存
          ????String?product?=?jedisClient.get(PRODUCT_KEY?+?id);
          ????if?(null?==?product)?{
          ????????//?如果沒有獲取鎖等待3秒,SECONDS代表:秒
          ????????try?{
          ????????????if?(lock.tryLock(3,?TimeUnit.SECONDS))?{
          ????????????????try?{
          ????????????????????//?獲取鎖后再查一次,查到了直接返回結果
          ????????????????????product?=?jedisClient.get(PRODUCT_KEY?+?id);
          ????????????????????if?(null?==?product)?{
          ????????????????????????//?....
          ????????????????????}
          ????????????????????return?product;
          ????????????????}?catch?(Exception?e)?{
          ????????????????????product?=?jedisClient.get(PRODUCT_KEY?+?id);
          ????????????????}?finally?{
          ????????????????????//?釋放鎖(成功、失敗都必須釋放,如果是lock.tryLock()方法會一直阻塞在這)
          ????????????????????lock.unlock();
          ????????????????}
          ????????????}?else?{
          ????????????????product?=?jedisClient.get(PRODUCT_KEY?+?id);
          ????????????}
          ????????}?catch?(InterruptedException?e)?{
          ????????????product?=?jedisClient.get(PRODUCT_KEY?+?id);
          ????????}
          ????}
          ????return?product;
          }


          解釋基礎解決方案
          緩存穿透訪問一個不存在的key,緩存不起作用,請求會穿透到DB,流量大時DB會掛掉1.采用布隆過濾器,使用一個足夠大的bitmap,用于存儲可能訪問的key,不存在的key直接被過濾;2.訪問key未在DB查詢到值,也將空值寫進緩存,但可以設置較短過期時間
          緩存雪崩大量的key設置了相同的過期時間,導致在緩存在同一時刻全部失效,造成瞬時DB請求量大、壓力驟增,引起雪崩可以給緩存設置過期時間時加上一個隨機值時間,使得每個key的過期時間分布開來,不會集中在同一時刻失效
          緩存擊穿一個存在的key,在緩存過期的一刻,同時有大量的請求,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增在訪問key之前,采用SETNX(set if not exists)來設置另一個短期key來鎖住當前key的訪問,訪問結束再刪除該短期key

          Q11:緩存與數(shù)據(jù)庫雙寫一致

          如果僅僅是讀數(shù)據(jù),沒有此類問題

          如果是新增數(shù)據(jù),也沒有此類問題

          當數(shù)據(jù)需要更新時,如何保證緩存與數(shù)據(jù)庫的雙寫一致性?

          三種更新策略:

          1. 先更新數(shù)據(jù)庫,再更新緩存 ?->
          2. 先刪除緩存,再更新數(shù)據(jù)庫
          3. 先更新數(shù)據(jù)庫,再刪除緩存

          方案一:并發(fā)的時候,執(zhí)行順序無法保證,可能A先更新數(shù)據(jù)庫,但B后更新數(shù)據(jù)庫但先更新緩存

          加鎖的話,確實可以避免,但這樣吞吐量會下降,可以根據(jù)業(yè)務場景考慮

          方案二:該方案會導致不一致的原因是。同時有一個請求A進行更新操作,另一個請求B進行查詢操作。那么會出現(xiàn)如下情形: (1)請求A進行寫操作,刪除緩存 (2)請求B查詢發(fā)現(xiàn)緩存不存在 (3)請求B去數(shù)據(jù)庫查詢得到舊值 (4)請求B將舊值寫入緩存 (5)請求A將新值寫入數(shù)據(jù)庫

          因此采用:采用延時雙刪策略 ? 即進入邏輯就刪除Key,執(zhí)行完操作,延時再刪除key

          方案三:更新數(shù)據(jù)庫 - 刪除緩存 ?可能出現(xiàn)問題的場景:

          (1)緩存剛好失效 (2)請求A查詢數(shù)據(jù)庫,得一個舊值 (3)請求B將新值寫入數(shù)據(jù)庫 (4)請求B刪除緩存 (5)請求A將查到的舊值寫入緩存

          先天條件要求:請求第二步的讀取操作耗時要大于更新操作,條件較為苛刻

          但如果真的發(fā)生怎么處理?

          A. 給鍵設置合理的過期時間

          B. 異步延時刪除key

          Q12:何保證Redis中的數(shù)據(jù)都是熱點數(shù)據(jù)

          A. 可以通過手工或者主動方式,去加載熱點數(shù)據(jù)

          B. Redis有其自己的數(shù)據(jù)淘汰策略:

          redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時候,就會施行數(shù)據(jù)淘汰策略(回收策略)。redis 提供 6種數(shù)據(jù)淘汰策略:

          1. volatile-lru:從已設置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰
          2. volatile-ttl:從已設置過期時間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過期的數(shù)據(jù)淘汰
          3. volatile-random:從已設置過期時間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰
          4. allkeys-lru:從數(shù)據(jù)集(server.db[i].dict)中挑選最近最少使用的數(shù)據(jù)淘汰
          5. allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰
          6. no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)

          Q13:Redis的并發(fā)競爭問題如何解決?

          即多線程同時操作統(tǒng)一Key的解決辦法:

          Redis為單進程單線程模式,采用隊列模式將并發(fā)訪問變?yōu)榇性L問。Redis本身沒有鎖的概念,Redis對于多個客戶端連接并不存在競爭,但是在Jedis客戶端對Redis進行并發(fā)訪問時會發(fā)生連接超時、數(shù)據(jù)轉(zhuǎn)換錯誤、阻塞、客戶端關閉連接等問題,這些問題均是由于客戶端連接混亂造成

          對此有多種解決方法:
          A:條件允許的情況下,請使用redis自帶的incr命令,decr命令
          B:樂觀鎖方式
          watch?price
          get?price?$price
          $price?=?$price?+?10
          multi
          set?price?$price
          exec

          C:針對客戶端,操作同一個key的時候,進行加鎖處理
          D:場景允許的話,使用setnx 實現(xiàn)

          Q14:Redis回收進程如何工作的? Redis回收使用的是什么算法?

          Q12 中提到過,當所需內(nèi)存超過配置的最大內(nèi)存時,redis會啟用數(shù)據(jù)淘汰規(guī)則

          默認規(guī)則是:# maxmemory-policy noeviction

          即只允許讀,無法繼續(xù)添加key

          因此常需要配置淘汰策略,比如LRU算法

          LRU算法最為精典的實現(xiàn),就是HashMap+Double LinkedList,時間復雜度為O(1)

          Q15:Redis大批量增加數(shù)據(jù)

          參考文章:https://www.cnblogs.com/PatrickLiu/p/8548580.html

          使用管道模式,運行的命令如下所示:

          cat?data.txt?|?redis-cli?--pipe

          data.txt文本:

          SET?Key0?Value0
          SET?Key1?Value1
          ...
          SET?KeyN?ValueN

          #?或者是 RESP協(xié)議內(nèi)容?-?注意文件編碼?。?!

          *8
          $5
          HMSET
          $8
          person:1
          $2
          id
          $1
          1

          這將產(chǎn)生類似于這樣的輸出:

          All?data?transferred.?Waiting?for?the?last?reply...
          Last?reply?received?from?server.
          errors:?0,?replies:?1000000

          redis-cli實用程序還將確保只將從Redis實例收到的錯誤重定向到標準輸出

          演示:

          cat?redis_commands.txt?|?redis-cli?-h?192.168.127.130?-p?6379?[-a?"password"]?-n?0?--pipe

          All?data?transferred.Waiting?for?the?last?reply...
          Last?reply?received?from?server.
          errors:0,replies:10000000

          mysql數(shù)據(jù)快速導入到redis 實戰(zhàn): ?文件詳情:可見Redis-通道實戰(zhàn)

          博文:https://www.cnblogs.com/tommy-huang/p/4703514.html

          #?1.準備一個table
          create?database??if?not?exists?`test`;
          use?`test`;
          CREATE?TABLE?`person`?(
          ??`id`?int(10)?unsigned?NOT?NULL?AUTO_INCREMENT,
          ??`name`?varchar(200)?NOT?NULL,
          ??`age`?varchar(200)?NOT?NULL,
          ??PRIMARY?KEY?(`id`)
          )?ENGINE=MyISAM?AUTO_INCREMENT=1?DEFAULT?CHARSET=utf8;

          #?2.插入七八萬條數(shù)據(jù)

          # 3.SQL查詢,將其轉(zhuǎn)化為 RESP協(xié)議命令?? Linux 版本:?->?不要在windows環(huán)境試,沒啥意義
          SELECT?CONCAT(
          ???"*8\r\n",
          ???'$',LENGTH(redis_cmd),'\r\n',redis_cmd,'\r\n',
          ???'$',LENGTH(redis_key),'\r\n',redis_key,'\r\n',
          ???'$',LENGTH(hkey1),'\r\n',hkey1,'\r\n','$',LENGTH(hval1),'\r\n',hval1,'\r\n',
          ???'$',LENGTH(hkey2),'\r\n',hkey2,'\r\n','$',LENGTH(hval2),'\r\n',hval2,'\r\n',
          ???'$',LENGTH(hkey3),'\r\n',hkey3,'\r\n','$',LENGTH(hval3),'\r\n',hval3,'\r'
          )FROM(
          ???SELECT?'HMSET'?AS?redis_cmd,
          ???concat_ws(':','person',?id)?AS?redis_key,
          ???'id'?AS?hkey1,?id?AS?hval1,
          ???'name'?AS?hkey2,?name?AS?hval2,
          ???'age'?AS?hkey3,?age?AS?hval3
          ???From?person
          )AS?t

          #?4.如果用的就是線上數(shù)據(jù)庫+線上Linux?->?把sql存到?order.sql,進行執(zhí)行
          mysql?-uroot?-p123456?test?--default-character-set=utf8?--skip-column-names?--raw?
          |
          redis-cli?-h?127.0.0.1?-p?6379?-a?123456?--pipe

          #?5.本地數(shù)據(jù)庫+線上redis
          利用Navicat導出數(shù)據(jù)?->?data.txt,清理格式(導出來的數(shù)據(jù)里面各種?"?符號),全局替換即可
          cat?data.txt?|?redis-cli?-h?127.0.0.1?-p?6379?-a?123456??--pipe

          81921條數(shù)據(jù)?一瞬間導入完成

          注意事項:RESP協(xié)議要求,不要有莫名其妙的字符,注意文件類型是Unix編碼類型

          Q16:延申:布隆過濾器

          數(shù)據(jù)結構及算法篇 / 布隆過濾器

          Redis 實現(xiàn)

          redis 4.X 以上 提供 布隆過濾器插件

          centos中安裝redis插件bloom-filter:https://blog.csdn.net/u013030276/article/details/88350641

          語法:[bf.add ?key ?options]

          語法:[bf.exists ?key ?options]

          注意: redis 布隆過濾器提供的是 最大內(nèi)存512M,2億數(shù)據(jù),萬分之一的誤差率

          Q17:Lua腳本相關

          使用Lua腳本的好處:

          • 減少網(wǎng)絡開銷??梢詫⒍鄠€請求通過腳本的形式一次發(fā)送,減少網(wǎng)絡時延
          • 原子操作,redis會將整個腳本作為一個整體執(zhí)行,中間不會被其他命令插入。因此在編寫腳本的過程中無需擔心會出現(xiàn)競態(tài)條件,無需使用事務
          • 復用,客戶端發(fā)送的腳本會永久存在redis中,這樣,其他客戶端可以復用這一腳本而不需要使用代碼完成相同的邏輯
          @RequestMapping("/testLua")
          public?String?testLua?()?{

          ????String?key???=?"mylock";
          ????String?value?=?"xxxxxxxxxxxxxxx";

          ????//????????if?redis.call('get',?KEYS[1])?==?ARGV[1]
          ????//????????????then
          ????//????????????????return?redis.call('del',?KEYS[1])
          ????//????????else
          ????//????????????return?0
          ????//????????end

          ????//?lua腳本,用來釋放分布式鎖?-?如果使用的較多,可以封裝到文件中,?再進行調(diào)用
          ????String?luaScript?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('del',KEYS[1])?else?return?0?end";
          ????Object?eval?=?jedis.eval(luaScript,?Collections.singletonList(key),?Collections.singletonList(value));
          ????return?eval.toString();
          }

          Q18:性能相關 - Redis慢查詢分析

          redis 命令會放在redis內(nèi)置隊列中,然后主線程一個個執(zhí)行,因此 其中一個 命令執(zhí)行時間過長,會造成成批量的阻塞

          命令:slowlog get 獲取慢查詢記錄 slowlog len 獲取慢查詢記錄量 (慢查詢隊列是先進先出的,因此新的值在滿載的時候,舊的會出去)

          Redis 慢查詢 -> 執(zhí)行階段耗時過長

          conf文件設置:slowlog-low-slower-than 10000 -> 10000微秒,10毫秒 (默認) 0 -> 記錄所有命令 -1 -> 不記錄命令 slow-max-len 存放的最大條數(shù)

          慢查詢導致原因: value 值過大,解決辦法:數(shù)據(jù)分段(更細顆粒度存放數(shù)據(jù))

          Q19:如何提高Redis處理效率? 基于Jedis 的批量操作 Pipelined

          Jedis?jedis?=?new?Jedis("127.0.0.1",?6379);
          Pipeline?pipelined?=?jedis.pipelined();
          for?(String?key?:?keys)?{
          ????pipelined.del(key);
          }

          pipelined.sync();
          jedis.close();

          //?pipelined?實際是封裝過一層的指令集?->??實際應用的還是單條指令,但是節(jié)省了網(wǎng)絡傳輸開銷(服務端到Redis環(huán)境的網(wǎng)絡開銷)

          各類知識點總結

          下面的文章都有對應的原創(chuàng)精美PDF,在持續(xù)更新中,可以來找我催更~

          掃碼或者微信搜Java3y?免費領取原創(chuàng)思維導圖、精美PDF。在公眾號回復「888」領取,PDF內(nèi)容純手打有任何不懂歡迎來問我。


          原創(chuàng)電子書

          原創(chuàng)思維導圖


          我是三歪,一個想要變強的男人,感謝大家的點贊收藏和轉(zhuǎn)發(fā),下期見。
          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    一级黄色片在线 | 色情片在线播放 | 天堂网A V中文字幕 | 特级AV网站在线观看 | 国产成人免费高清视频 |