Redis-技術專題-Redis知識體系

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1.基本文件說明"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c8/c8df417d1216165831d65a00031e2cd7.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2.基礎命令"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/44e02da4e1f6074ee9e842548728d63b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3.字符串命令"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e8/e862e54c858061df98afe66101a90f4b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"4.哈希(Hash)命令"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a86c8f4b7abe2c84ddb5e7d7746992f0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"編碼: field value 值由 ziplist 及 hashtable 兩種編碼格式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字段較少的時候採用"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ziplist"},{"type":"text","text":",字段較多的時候會變成"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"hashtable"},{"type":"text","text":"編碼"}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"5.列表(List)命令"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Redis列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 一個列表最多可以包含 "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"232 - 1 "},{"type":"text","text":"個元素 ("},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"4294967295"},{"type":"text","text":", 每個列表超過"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"40"},{"type":"text","text":"億個元素)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容量 -> 集合,有序集合也是如此。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6e/6e7776adb9af2e046cff64e4cd5d4052.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"6.集合(Set)命令"}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" Set 是 String 類型的無序集合。集合成員是唯一的,這就意味着集合中不能出現重複的數據"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/23/23cea99d44b943c05fa77fe4df5411cd.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"7.有序集合(sorted set)命令"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Redis 有序集合和集合一樣也是string類型元素的集合,且不允許重複的成員。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 不同的是每個元素都會關聯一個"},{"type":"text","marks":[{"type":"strong"}],"text":"double"},{"type":"text","text":"類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。"}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"有序集合的成員是唯一的,但分數("},{"type":"text","text":"score"},{"type":"text","marks":[{"type":"strong"}],"text":")卻可以重複。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/944d58a6a9019a80b271a6c60327367d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"8.發佈訂閱"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開啓兩個客戶端"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A客戶端訂閱頻道:subscribe redisChat (頻道名字爲 redisChat)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"B客戶端發佈內容:publish redisChat \"Hello, this is my wor\" (內容是 hello....)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A客戶端即爲自動收到內容, 原理圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/dd/dddfbfb67c61d912da8ad918a837771b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/00/0062974a89fc02b192bd3ac5fb2247f5.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/57/572ad5a6ea461175bb3c807a73e07902.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"9.Redis 事務"}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Redis 事務可以一次執行多個命令, 並且帶有以下三個重要的保證:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"批量操作在發送 EXEC 命令前被放入隊列緩存"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中"}]}]}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 一個事務從開始到執行會經歷以下三個階段:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"開始事務"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"命令入隊"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"執行事務"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"注意:redis事務和數據庫事務不同,redis事務出錯後最大的特點是,一剩下的命令會繼續執行,二出錯的數據不會回滾"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af63ec8d056d9cdcadb3562016a9ab22.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"10.Redis 服務器命令"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/168c17614429783926a971f902e79b12.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"11.Redis 數據備份與恢復"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" Redis "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"SAVE"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" 命令用於創建當前數據庫的備份"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 如果需要恢復數據,只需將備份文件 ("},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"dump.rdb"},{"type":"text","text":") 移動到 redis 安裝目錄並啓動服務即可。獲取 redis 目錄可以使用 "},{"type":"text","marks":[{"type":"strong"}],"text":"CONFIG"},{"type":"text","text":" 命令"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"12.Redis 性能測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis 性能測試的基本命令如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"redis目錄執行:redis-benchmark [option] [option value]\n// 會返回各種操作的性能報告(100連接,10000請求)\nredis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 10000\n// 100個字節作爲value值進行壓測\nredis-benchmark -h 127.0.0.1 -p 6379 -q -d 100"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Jedis"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n\n redis.clients\n jedis\n 2.8.2\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Jedis配置"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"############# redis Config #############\n# Redis數據庫索引(默認爲0)\nspring.redis.database=0\n# Redis服務器地址\nspring.redis.host=120.79.88.17\n# Redis服務器連接端口\nspring.redis.port=6379\n# Redis服務器連接密碼(默認爲空)\nspring.redis.password=123456\n# 連接池中的最大空閒連接\nspring.redis.jedis.pool.max-idle=8\n# 連接池中的最小空閒連接\nspring.redis.jedis.pool.min-idle=0"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"JedisConfig"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@Configuration\npublic class JedisConfig extends CachingConfigurerSupport {\n\n @Value(\"${spring.redis.host}\")\n private String host;\n\n @Value(\"${spring.redis.port}\")\n private int port;\n\n @Value(\"${spring.redis.password}\")\n private String password;\n\n @Value(\"${spring.redis.max-idle}\")\n private Integer maxIdle;\n\n @Value(\"${spring.redis.min-idle}\")\n private Integer minIdle;\n\n @Bean\n public JedisPool redisPoolFactory(){\n JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();\n jedisPoolConfig.setMaxIdle(maxIdle);\n jedisPoolConfig.setMinIdle(minIdle);\n jedisPoolConfig.setMaxWaitMillis(3000L);\n int timeOut = 3;\n return new JedisPool(jedisPoolConfig, host, port, timeOut, password);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"基礎使用"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@RunWith(SpringRunner.class)\[email protected](classes = KerwinBootsApplication.class)\npublic class ApplicationTests {\n @Resource\n JedisPool jedisPool;\n @Test\n public void testJedis () {\n Jedis jedis = jedisPool.getResource();\n jedis.set(\"year\", String.valueOf(24));\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"SpringBoot RedisTemplate"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n\n org.springframework.boot\n spring-boot-starter-data-redis\n\n\n\n org.apache.commons\n commons-pool2\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"############# redis Config #############\n# Redis數據庫索引(默認爲0)\nspring.redis.database=0\n# Redis服務器地址\nspring.redis.host=120.79.88.17\n# Redis服務器連接端口\nspring.redis.port=6379\n# Redis服務器連接密碼(默認爲空)\nspring.redis.password=123456\n# 連接池最大連接數(使用負值表示沒有限制)\nspring.redis.jedis.pool.max-active=200\n# 連接池最大阻塞等待時間(使用負值表示沒有限制)\nspring.redis.jedis.pool.max-wait=1000ms\n# 連接池中的最大空閒連接\nspring.redis.jedis.pool.max-idle=8\n# 連接池中的最小空閒連接\nspring.redis.jedis.pool.min-idle=0\n# 連接超時時間(毫秒)\nspring.redis.timeout=1000ms"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// Cache註解配置類\[email protected]\npublic class RedisCacheConfig {\n\n @Bean\n public KeyGenerator simpleKeyGenerator() {\n return (o, method, objects) -> {\n StringBuilder stringBuilder = new StringBuilder();\n stringBuilder.append(o.getClass().getSimpleName());\n stringBuilder.append(\".\");\n stringBuilder.append(method.getName());\n stringBuilder.append(\"[\");\n for (Object obj : objects) {\n stringBuilder.append(obj.toString());\n }\n stringBuilder.append(\"]\");\n return stringBuilder.toString();\n };\n }\n \n @Bean\n public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {\n return new RedisCacheManager(\n RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),\n // 默認策略,未配置的 key 會使用這個\n this.getRedisCacheConfigurationWithTtl(15),\n // 指定 key 策略\n this.getRedisCacheConfigurationMap()\n );\n }\n \n private Map getRedisCacheConfigurationMap() {\n Map redisCacheConfigurationMap = new HashMap<>(16);\n redisCacheConfigurationMap.put(\"redisTest\", this.getRedisCacheConfigurationWithTtl(15));\n return redisCacheConfigurationMap;\n }\n \n private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {\n Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);\n ObjectMapper om = new ObjectMapper();\n om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n jackson2JsonRedisSerializer.setObjectMapper(om);\n\n RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();\n redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(\n RedisSerializationContext\n .SerializationPair\n .fromSerializer(jackson2JsonRedisSerializer)\n ).entryTtl(Duration.ofSeconds(seconds));\n return redisCacheConfiguration;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// RedisAutoConfiguration\[email protected]\[email protected]\npublic class RedisConfig {\n\n @Bean\n @SuppressWarnings(\"all\")\n public RedisTemplate redisTemplate(RedisConnectionFactory factory) {\n\n RedisTemplate template = new RedisTemplate();\n template.setConnectionFactory(factory);\n\n Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n\n ObjectMapper om = new ObjectMapper();\n om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n jackson2JsonRedisSerializer.setObjectMapper(om);\n\n StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();\n\n // key採用String的序列化方式\n template.setKeySerializer(stringRedisSerializer);\n\n // hash的key也採用String的序列化方式\n template.setHashKeySerializer(stringRedisSerializer);\n\n // value序列化方式採用jackson\n template.setValueSerializer(jackson2JsonRedisSerializer);\n\n // hash的value序列化方式採用jackson\n template.setHashValueSerializer(jackson2JsonRedisSerializer);\n template.afterPropertiesSet();\n return template;\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 基礎使用\[email protected]\nRedisTemplate redisTemplate;\n\nredisTemplate.opsForList().rightPush(\"user:1:order\", dataList.get(3).get(\"key\").toString());\n\n// 註解使用\[email protected](value = \"redisTest\")\npublic TestBean testBeanAnnotation () {}\n"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"13.Redis使用場景"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1d/1db1fe511fbb8e26fc545fd0d2877933.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"或者簡單消息隊列,發佈訂閱實施消息系統等等"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"String - 緩存"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 1.Cacheable 註解\n// controller 調用 service 時自動判斷有沒有緩存,如果有就走redis緩存直接返回,如果沒有則數據庫然後自動放入redis中\n// 可以設置過期時間,KEY生成規則 (KEY生成規則基於 參數的toString方法)\[email protected](value = \"yearScore\", key = \"#yearScore\")\[email protected]\npublic List findBy (YearScore yearScore) {}\n// 2.手動用緩存\nif (redis.hasKey(???) {\n return ....\n} \nredis.set(find from DB)..."}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"String - 限流 | 計數器"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 注:這只是一個最簡單的Demo 效率低,耗時舊,但核心就是這個意思\n// 計數器也是利用單線程incr...等等\[email protected](\"/redisLimit\")\npublic String testRedisLimit(String uuid) {\n if (jedis.get(uuid) != null) {\n Long incr = jedis.incr(uuid);\n if (incr > MAX_LIMITTIME) {\n return \"Failure Request\";\n } else {\n return \"Success Request\";\n }\n }\n // 設置Key 起始請求爲1,10秒過期 -> 實際寫法肯定封裝過,這裏就是隨便一寫\n jedis.set(uuid, \"1\");\n jedis.expire(uuid, 10);\n return \"Success Request\";\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"String - 分佈式鎖 (重點)"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/***\n * 核心思路:\n * 分佈式服務調用時setnx,返回1證明拿到,用完了刪除,返回0就證明被鎖,等...\n * SET KEY value [EX seconds] [PX milliseconds] [NX|XX]\n * EX second:設置鍵的過期時間爲second秒\n * PX millisecond:設置鍵的過期時間爲millisecond毫秒\n * NX:只在鍵不存在時,纔對鍵進行設置操作\n * XX:只在鍵已經存在時,纔對鍵進行設置操作\n *\n * 1.設置鎖\n * A. 分佈式業務統一Key\n * B. 設置Key過期時間\n * C. 設置隨機value,利用ThreadLocal 線程私有存儲隨機value\n *\n * 2.業務處理\n * ...\n *\n * 3.解鎖\n * A. 無論如何必須解鎖 - finally (超時時間和finally 雙保證)\n * B. 要對比是否是本線程上的鎖,所以要對比線程私有value和存儲的value是否一致(避免把別人加鎖的東西刪除了)\n */\[email protected](\"/redisLock\")\npublic String testRedisLock () {\n try {\n for(;;){\n RedisContextHolder.clear();\n String uuid = UUID.randomUUID().toString();\n String set = jedis.set(KEY, uuid, \"NX\", \"EX\", 1000);\n RedisContextHolder.setValue(uuid);\n\n if (!\"OK\".equals(set)) {\n // 進入循環-可以短時間休眠\n } else {\n // 獲取鎖成功 Do Somethings....\n break;\n }\n }\n } finally {\n // 解鎖 -> 保證獲取數據,判斷一致以及刪除數據三個操作是原子的, 因此如下寫法是不符合的\n /*if (RedisContextHolder.getValue() != null && jedis.get(KEY) != null && RedisContextHolder.getValue().equals(jedis.get(KEY))) {\n jedis.del(KEY);\n }*/\n\n // 正確姿勢 -> 使用Lua腳本,保證原子性\n String luaScript = \"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end\";\n Object eval = jedis.eval(luaScript, Collections.singletonList(KEY), Collections.singletonList(RedisContextHolder.getValue()));\n }\n return \"鎖創建成功-業務處理成功\";\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"String - 分佈式Session(重點)"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 1.首先明白爲什麼需要分佈式session -> nginx負載均衡 分發到不同的Tomcat,即使利用IP分發,可以利用request獲取session,\n\n但是其中一個掛了,怎麼辦?? 所以需要分佈式session\n注意理解其中的區別 A服務-用戶校驗服務 B服務-業務層\n\n情況A:\n\nA,B 服務單機部署:\n\ncookie:登錄成功後,存儲信息到cookie,A服務自身通過request設置session,獲取session,B服務通過唯一key或者userid 查詢數據庫獲取用戶信息\n\ncookie+redis:登錄成功後,存儲信息到cookie,A服務自身通過request設置session,獲取session,B服務通過唯一key或者userid 查詢redis獲取用戶信息\n\n情況B:\n\nA服務多節點部署,B服務多節點部署\nB服務獲取用戶信息的方式其實是不重要的,必然要查,要麼從數據庫,要麼從cookie\nA服務:登錄成功後,存儲唯一key到cookie, 與此同時,A服務需要把session(KEY-UserInfo)同步到redis中,不能存在單純的request\n(否則nginx分發到另一個服務器就完犢子了)\n\n官方實現:\nspring-session-data-redis\n有一個內置攔截器,攔截request,session通過redis交互,普通使用代碼依然是request.getSession.... \n但是實際上這個session的值已經被該組件攔截,通過redis進行同步了\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"List 簡單隊列-棧"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 說白了利用redis - list數據結構 支持從左從右push,從左從右pop\[email protected]\npublic class RedisStack {\n\n @Resource\n Jedis jedis;\n\n private final static String KEY = \"Stack\";\n\n /** push **/\n public void push (String value) {\n jedis.lpush(KEY, value);\n }\n\n /** pop **/\n public String pop () {\n return jedis.lpop(KEY);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@Component\npublic class RedisQueue {\n\n @Resource\n JedisPool jedisPool;\n\n private final static String KEY = \"Queue\";\n\n /** push **/\n public void push (String value) {\n Jedis jedis = jedisPool.getResource();\n jedis.lpush(KEY, value);\n }\n\n /** pop **/\n public String pop () {\n Jedis jedis = jedisPool.getResource();\n return jedis.rpop(KEY);\n }\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"List 社交類APP - 好友列表"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"根據時間顯示好友,多個好友列表,求交集,並集 顯示共同好友等等...\n疑問:難道大廠真的用redis存這些數據嗎???多大的量啊... 我個人認爲實際是數據庫存用戶id,然後用算法去處理,更省空間\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Set 好友關係(合,並,交集)"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 插入key 及用戶id\nsadd cat:1 001 002 003 004 005 006\n\n// 返回抽獎參與人數\nscard cat:1\n\n// 隨機抽取一個\nsrandmember cat:1\n\n// 隨機抽取一人,並移除\nspop cat:1\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Zset 排行榜"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"根據分數實現有序列表\n微博熱搜:每點擊一次 分數+1 即可\n--- 不用數據庫目的是因爲避免order by 進行全表掃描"}]},{"type":"heading","attrs":{"align":null,"level":2}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章