diff --git "a/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD" "b/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD" new file mode 100644 index 0000000..a038f88 --- /dev/null +++ "b/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD" @@ -0,0 +1,22 @@ +## Java基础篇(持续更新中) +​ +公众号:捡田螺的小男孩 +​ +- [一文读懂线程池的工作原理(故事白话文)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488078&idx=1&sn=0a7cef472002f6582fd2354fba83706a&chksm=cf21cd67f85644716263c3a80cead9b7bb36d9677f6f8b06d0602077ece70fcafa9d20c1cffb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Synchronized解析——如果你愿意一层一层剥开我的心](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487942&idx=1&sn=bbc68d1b9da23bf6474378e310b1ef1b&chksm=cf21ceeff85647f9ad7a08226849fcba3f9481387d13b17a5787fb94027647de81c349f9e390&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [异步编程利器:CompletableFuture详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:JDK 5-15都有哪些经典新特性](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488058&idx=1&sn=aab4d0dc9020cb62710086474ca90baf&chksm=cf21cd13f8564405040593daa45c62aec218e13f5ff42d679c59f768dd4fcc53ddcf34e0a454&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=162724582&ang=zh_CN&scene=21#wechat_redirect) +- [给你的Java程序拍个片子吧:jstack命令解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487990&idx=1&sn=b5b3c565392f39e5ac517696603b2ed9&chksm=cf21cedff85647c960407dce77fe04d08e51f8c7332310ccacd925be5567c187aa761dd1d1c8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础结构图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487985&idx=1&sn=ead28c6c8d81b98e59603b848d250b30&chksm=cf21ced8f85647ce336f19016c7ff1936b21c81066815c8f28b830098716111548edb9767b21&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备:序列化全方位解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487980&idx=1&sn=2a9ce519f87a1ffe1511022e6724208e&chksm=cf21cec5f85647d357c79860171fc1799ef3c44a2bdd0716e8437e31708a17d9000b4224bd36&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [优化if-else代码的八种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487970&idx=1&sn=c296bb03419adf93955c6d0f27e56b29&chksm=cf21cecbf85647dd0ef5160559bc0d524a4be004a28bc5d2770a43409e3b090123c0930cf047&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:泛型解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487964&idx=1&sn=24d7228cc10afc98c52dbf35da61a7b9&chksm=cf21cef5f85647e3d2b3f1e126cdc46d9e889d2e30c09716e0aea016beee3ca6d4c321cf60ae&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:内部类解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487963&idx=1&sn=a0b49cd49a3dd51b6736c9ffa0a5997a&chksm=cf21cef2f85647e49a4bdb43f27583f03fb9ec4719767512dc084edd05675599c3bec44251fb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [老是遇到乱码问题:它是如何产生的,又如何解决呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487962&idx=1&sn=7424e843c80b228283fc08d4d24cc4bb&chksm=cf21cef3f85647e5a9c92d280624ad2564e885561a8b64cbf6722459f7c13da7421765321aa9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [谈谈Java反射:从入门到实践,再到原理](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487959&idx=1&sn=778114d611f18b0f307a3f3ab6cd9117&chksm=cf21cefef85647e84b77c0e46620e91cf5ff079785b58a7dc66e5ed7419e21e0da9180699617&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备的一些流程图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487946&idx=1&sn=57a86e1d2fe1a9ecf00594a0bb6baf5f&chksm=cf21cee3f85647f5cf2ba728cc0838923140130a18ad117e248cf9843460614fc855d556968a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备:查看日志常用的linux命令](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487954&idx=1&sn=6c04ff4edfcfea52a82795bcb9ed8efd&chksm=cf21cefbf85647ed8df72a23307315be5d1b3d4974c128f111bfdaa84da37cf7b49ff65c1112&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [ThreadLocal的八个关键知识点](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500784&idx=1&sn=6519d0e092be4ed9d6f4da8d90deef2c&chksm=cf221cd9f85595cf9123043241e92a19ca9c212aa8527cfb2aeb9a2472c6bdab9045cf40f22f&token=349136600&lang=zh_CN#rd) \ No newline at end of file diff --git "a/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD.bak" "b/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD.bak" new file mode 100644 index 0000000..4ba4024 --- /dev/null +++ "b/Java\345\237\272\347\241\200\345\255\246\344\271\240/README.MD.bak" @@ -0,0 +1,21 @@ +## Java基础篇(持续更新中) +​ +公众号:捡田螺的小男孩 +​ +- [一文读懂线程池的工作原理(故事白话文)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488078&idx=1&sn=0a7cef472002f6582fd2354fba83706a&chksm=cf21cd67f85644716263c3a80cead9b7bb36d9677f6f8b06d0602077ece70fcafa9d20c1cffb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Synchronized解析——如果你愿意一层一层剥开我的心](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487942&idx=1&sn=bbc68d1b9da23bf6474378e310b1ef1b&chksm=cf21ceeff85647f9ad7a08226849fcba3f9481387d13b17a5787fb94027647de81c349f9e390&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [异步编程利器:CompletableFuture详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:JDK 5-15都有哪些经典新特性](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488058&idx=1&sn=aab4d0dc9020cb62710086474ca90baf&chksm=cf21cd13f8564405040593daa45c62aec218e13f5ff42d679c59f768dd4fcc53ddcf34e0a454&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=162724582&ang=zh_CN&scene=21#wechat_redirect) +- [给你的Java程序拍个片子吧:jstack命令解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487990&idx=1&sn=b5b3c565392f39e5ac517696603b2ed9&chksm=cf21cedff85647c960407dce77fe04d08e51f8c7332310ccacd925be5567c187aa761dd1d1c8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础结构图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487985&idx=1&sn=ead28c6c8d81b98e59603b848d250b30&chksm=cf21ced8f85647ce336f19016c7ff1936b21c81066815c8f28b830098716111548edb9767b21&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备:序列化全方位解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487980&idx=1&sn=2a9ce519f87a1ffe1511022e6724208e&chksm=cf21cec5f85647d357c79860171fc1799ef3c44a2bdd0716e8437e31708a17d9000b4224bd36&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [优化if-else代码的八种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487970&idx=1&sn=c296bb03419adf93955c6d0f27e56b29&chksm=cf21cecbf85647dd0ef5160559bc0d524a4be004a28bc5d2770a43409e3b090123c0930cf047&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:泛型解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487964&idx=1&sn=24d7228cc10afc98c52dbf35da61a7b9&chksm=cf21cef5f85647e3d2b3f1e126cdc46d9e889d2e30c09716e0aea016beee3ca6d4c321cf60ae&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备基础:内部类解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487963&idx=1&sn=a0b49cd49a3dd51b6736c9ffa0a5997a&chksm=cf21cef2f85647e49a4bdb43f27583f03fb9ec4719767512dc084edd05675599c3bec44251fb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [老是遇到乱码问题:它是如何产生的,又如何解决呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487962&idx=1&sn=7424e843c80b228283fc08d4d24cc4bb&chksm=cf21cef3f85647e5a9c92d280624ad2564e885561a8b64cbf6722459f7c13da7421765321aa9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [谈谈Java反射:从入门到实践,再到原理](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487959&idx=1&sn=778114d611f18b0f307a3f3ab6cd9117&chksm=cf21cefef85647e84b77c0e46620e91cf5ff079785b58a7dc66e5ed7419e21e0da9180699617&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备的一些流程图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487946&idx=1&sn=57a86e1d2fe1a9ecf00594a0bb6baf5f&chksm=cf21cee3f85647f5cf2ba728cc0838923140130a18ad117e248cf9843460614fc855d556968a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java程序员必备:查看日志常用的linux命令](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487954&idx=1&sn=6c04ff4edfcfea52a82795bcb9ed8efd&chksm=cf21cefbf85647ed8df72a23307315be5d1b3d4974c128f111bfdaa84da37cf7b49ff65c1112&token=162724582&lang=zh_CN&scene=21#wechat_redirect) \ No newline at end of file diff --git "a/Java\347\250\213\345\272\217\345\221\230\351\234\200\350\246\201\347\234\213\345\223\252\344\272\233\344\271\246/README.MD" "b/Java\347\250\213\345\272\217\345\221\230\351\234\200\350\246\201\347\234\213\345\223\252\344\272\233\344\271\246/README.MD" new file mode 100644 index 0000000..5ee6e33 --- /dev/null +++ "b/Java\347\250\213\345\272\217\345\221\230\351\234\200\350\246\201\347\234\213\345\223\252\344\272\233\344\271\246/README.MD" @@ -0,0 +1,5 @@ +## Java程序员书单(持续更新中) +​ +公众号:捡田螺的小男孩 +​ +- [一份Java程序员的珍藏书单,请您注意查收](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488066&idx=1&sn=44b5a90be1b69d7938dbcf516d85f041&chksm=cf21cd6bf856447d869278386250f59a926881375df848e54f86a21682bdab50f9e09ca56fbd&token=162724582&lang=zh_CN&scene=21#wechat_redirect) diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD" new file mode 100644 index 0000000..7397d39 --- /dev/null +++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD" @@ -0,0 +1,32 @@ +## 1. 面试真题 + +关注公众号:捡田螺的小男孩 +​ +- [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) +- [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) +- [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) +- [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) +- [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) +- [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) +- [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) +- [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) +​ +## 2. 必考经典面试题 +​ +- [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=2001057130&lang=zh_CN#rd) +- [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=2001057130&lang=zh_CN#rd) +- [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=2001057130&lang=zh_CN#rd) +- [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=2001057130&lang=zh_CN#rd) +- [八种幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:零拷贝详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496788&idx=1&sn=f65ddd10d16d8376efa0037762153932&chksm=cf222b7df855a26bad76249e7b77e28da3097b226f9165d79f5031516d9c345827fca901559c&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:IO模型详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496448&idx=1&sn=cd502f850290a25949dd4a11ac55a039&chksm=cf222c29f855a53f094bde2868900fa252b07385e73564e9ee9f0510cb4e74387d9d23ab67e6&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:MVCC原理详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495277&idx=1&sn=a1812febb4246f824ce54d778f672025&chksm=cf223144f855b8528ad6cce707dc3a1b4d387817bd751dfab4f79dda90c6640f9763d25f3f33&token=2001057130&lang=zh_CN#rd) +- [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [面试必备!TCP协议经典十五连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490868&idx=1&sn=96889bfe6a97f9200fa2d682cf2f5d89&chksm=cf21c21df8564b0b0757df584560a69340b1775fe1c70b867439565969ec3aed19c442ff4eeb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [2W字!梳理50道经典计算机网络面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247492832&idx=1&sn=601fa1c340a313bc0f74bb75cdb6a95a&chksm=cf223bc9f855b2dfb8d0e74f3360e2edfe25c3a728fe17e9e80b6022340994fd9d9e1ca83ca8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [50道Java集合经典面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488007&idx=1&sn=c5c16c8ec916c791e776216f3177c7e2&chksm=cf21cd2ef85644382a985e9fed1956d6ee60c86ce69e65f31f775318435fdb86bf368e26edf2&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [100道MySQL数据库经典面试题解析(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488000&idx=1&sn=1c38db7fd110bbcc1ffb2d72a56aaf25&chksm=cf21cd29f856443f25a3fe98ae8e888faceef9bee45df045969b2cffb105363dcc2a4480bb74&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [Spring 面试63问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497672&idx=1&sn=6ff0350e23d014b29a47bdec79af9ef5&chksm=cf2228e1f855a1f70fa78d9bd85c53dfbe154c1325aa1e203e4c918132c430d51bb68e961eda&token=2001057130&lang=zh_CN#rd) +- [多线程50连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247501446&idx=1&sn=3d83f3c1035c963c1fda3f77ab750e71&chksm=cf2219aff85590b9ba054dc33956a5cafe1beaa77b231dc4dc0cf891be3e16ef367f6b2ac4ed&token=245109219&lang=zh_CN#rd) \ No newline at end of file diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD.bak" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD.bak" new file mode 100644 index 0000000..cfb8d9d --- /dev/null +++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/README.MD.bak" @@ -0,0 +1,31 @@ +## 1. 面试真题 + +关注公众号:捡田螺的小男孩 +​ +- [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) +- [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) +- [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) +- [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) +- [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) +- [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) +- [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) +- [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) +​ +## 2. 必考经典面试题 +​ +- [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=2001057130&lang=zh_CN#rd) +- [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=2001057130&lang=zh_CN#rd) +- [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=2001057130&lang=zh_CN#rd) +- [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=2001057130&lang=zh_CN#rd) +- [八种幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:零拷贝详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496788&idx=1&sn=f65ddd10d16d8376efa0037762153932&chksm=cf222b7df855a26bad76249e7b77e28da3097b226f9165d79f5031516d9c345827fca901559c&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:IO模型详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496448&idx=1&sn=cd502f850290a25949dd4a11ac55a039&chksm=cf222c29f855a53f094bde2868900fa252b07385e73564e9ee9f0510cb4e74387d9d23ab67e6&token=2001057130&lang=zh_CN#rd) +- [看一遍就理解:MVCC原理详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495277&idx=1&sn=a1812febb4246f824ce54d778f672025&chksm=cf223144f855b8528ad6cce707dc3a1b4d387817bd751dfab4f79dda90c6640f9763d25f3f33&token=2001057130&lang=zh_CN#rd) +- [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [面试必备!TCP协议经典十五连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490868&idx=1&sn=96889bfe6a97f9200fa2d682cf2f5d89&chksm=cf21c21df8564b0b0757df584560a69340b1775fe1c70b867439565969ec3aed19c442ff4eeb&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [2W字!梳理50道经典计算机网络面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247492832&idx=1&sn=601fa1c340a313bc0f74bb75cdb6a95a&chksm=cf223bc9f855b2dfb8d0e74f3360e2edfe25c3a728fe17e9e80b6022340994fd9d9e1ca83ca8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [50道Java集合经典面试题(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488007&idx=1&sn=c5c16c8ec916c791e776216f3177c7e2&chksm=cf21cd2ef85644382a985e9fed1956d6ee60c86ce69e65f31f775318435fdb86bf368e26edf2&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [100道MySQL数据库经典面试题解析(收藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488000&idx=1&sn=1c38db7fd110bbcc1ffb2d72a56aaf25&chksm=cf21cd29f856443f25a3fe98ae8e888faceef9bee45df045969b2cffb105363dcc2a4480bb74&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [Spring 面试63问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497672&idx=1&sn=6ff0350e23d014b29a47bdec79af9ef5&chksm=cf2228e1f855a1f70fa78d9bd85c53dfbe154c1325aa1e203e4c918132c430d51bb68e961eda&token=2001057130&lang=zh_CN#rd) \ No newline at end of file diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md" new file mode 100644 index 0000000..a3f6b30 --- /dev/null +++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\347\274\223\345\255\230,Redis/Redis\344\270\272\344\273\200\344\271\210\350\277\231\344\271\210\345\277\253.md" @@ -0,0 +1,175 @@ + +## 前言 + +大家好呀,我是捡田螺的小男孩。我们都知道Redis很快,它QPS可达10万(每秒请求数)。**Redis为什么这么快呢**,本文将跟大家一起学习。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3880491879ed4a228d8f4213d987f6a3~tplv-k3u1fbpfcp-zoom-1.image) + + +- 公众号:**捡田螺的小男孩** +- [github地址](https://github.com/whx123/JavaHome),感谢每一颗star + +## 基于内存实现 + +我们都知道内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的数据库,相对于数据存在磁盘的数据库,就省去磁盘磁盘I/O的消耗。MySQL等磁盘数据库,需要建立索引来加快查询效率,而Redis数据存放在内存,直接操作内存,所以就很快。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0d7be13173814a43a60960fe59a48c61~tplv-k3u1fbpfcp-zoom-1.image) + +## 高效的数据结构 + +我们知道,MySQL索引为了提高效率,选择了B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下Redis的数据结构&内部编码图: + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ddd7723cc0e4953b746b13db7a5cea3~tplv-k3u1fbpfcp-zoom-1.image) + + +### SDS简单动态字符串 + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e68a18c50f149dda136fc9b1aa73ab8~tplv-k3u1fbpfcp-zoom-1.image) + +``` +struct sdshdr { //SDS简单动态字符串 + int len; //记录buf中已使用的空间 + int free; // buf中空闲空间长度 + char buf[]; //存储的实际内容 +} +``` + + +#### 字符串长度处理 + +在C语言中,要获取```捡田螺的小男孩```这个字符串的长度,需要从头开始遍历,复杂度为O(n); +在Redis中, 已经有一个**len**字段记录当前字符串的长度啦,直接获取即可,时间复杂度为O(1)。 + +#### 减少内存重新分配的次数 + +在C语言中,修改一个字符串,需要重新分配内存,修改越频繁,内存分配就越频繁,而分配内存是会**消耗性能**的。而在Redis中,SDS提供了两种优化策略:空间预分配和惰性空间释放。 + +**空间预分配** + +当SDS简单动态字符串修改和空间扩充时,除了分配必需的内存空间,还会额外分配未使用的空间。分配规则是酱紫的: + +> - SDS修改后,len的长度小于1M,那么将额外分配与len相同长度的未使用空间。比如len=100,重新分配后,buf 的实际长度会变为100(已使用空间)+100(额外空间)+1(空字符)=201。 +> - SDS修改后, len长度大于1M,那么程序将分配1M的未使用空间。 + +**惰性空间释放** + +当SDS缩短时,不是回收多余的内存空间,而是用free记录下多余的空间。后续再有修改操作,直接使用free中的空间,减少内存分配。 + +#### 哈希 + +Redis 作为一个K-V的内存数据库,它使用用一张全局的哈希来保存所有的键值对。这张哈希表,有多个哈希桶组成,哈希桶中的entry元素保存了```*key```和```*value```指针,其中```*key```指向了实际的键,```*value```指向了实际的值。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/13924387cd9e4a2d96d873dba1cc3ca9~tplv-k3u1fbpfcp-zoom-1.image) + +哈希表查找速率很快的,有点类似于Java中的**HashMap**,它让我们在**O(1)** 的时间复杂度快速找到键值对。首先通过key计算哈希值,找到对应的哈希桶位置,然后定位到entry,在entry找到对应的数据。 + +有些小伙伴可能会有疑问:你往哈希表中写入大量数据时,不是会遇到**哈希冲突**问题嘛,那效率就会降下来啦。 +> **哈希冲突:** 通过不同的key,计算出一样的哈希值,导致落在同一个哈希桶中。 + +Redis为了解决哈希冲突,采用了**链式哈希**。链式哈希是指同一个哈希桶中,多个元素用一个链表来保存,它们之间依次用指针连接。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/131951cd80354c24b62584d71b8fe9f9~tplv-k3u1fbpfcp-zoom-1.image) + +有些小伙伴可能还会有疑问:哈希冲突链上的元素只能通过指针逐一查找再操作。当往哈希表插入数据很多,冲突也会越多,冲突链表就会越长,那查询效率就会降低了。 + +为了保持高效,Redis 会对哈希表做**rehash操作**,也就是增加哈希桶,减少冲突。为了rehash更高效,Redis还默认使用了两个全局哈希表,一个用于当前使用,称为主哈希表,一个用于扩容,称为备用哈希表。 + +#### 跳跃表 + +跳跃表是Redis特有的数据结构,它其实就是在**链表的基础上,增加多级索引**,以提高查找效率。跳跃表的简单原理图如下: + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b62d59ffbd945e18f6dfcbf650a6eed~tplv-k3u1fbpfcp-watermark.image) + +- 每一层都有一条有序的链表,最底层的链表包含了所有的元素。 +- 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。 + + +#### 压缩列表ziplist + +压缩列表ziplist是列表键和字典键的的底层实现之一。它是由一系列特殊编码的内存块构成的列表, 一个ziplist可以包含多个entry, 每个entry可以保存一个长度受限的字符数组或者整数,如下: + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ce3da7cddbe4e6e94a775151664ed93~tplv-k3u1fbpfcp-zoom-1.image) + +- zlbytes :记录整个压缩列表占用的内存字节数 +- zltail: 尾节点至起始节点的偏移量 +- zllen : 记录整个压缩列表包含的节点数量 +- entryX: 压缩列表包含的各个节点 +- zlend : 特殊值0xFF(十进制255),用于标记压缩列表末端 + +由于内存是**连续分配**的,所以遍历速度很快。。 + + +## 合理的数据编码 + +Redis支持多种数据基本类型,每种基本类型对应不同的数据结构,每种数据结构对应不一样的编码。为了提高性能,Redis设计者总结出,数据结构最适合的编码搭配。 + +Redis是使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。 +``` +//关注公众号:捡田螺的小男孩 +typedef struct redisObject{ + //类型 + unsigned type:4; + //编码 + unsigned encoding:4; + //指向底层数据结构的指针 + void *ptr; + //... + }robj; +``` + +redisObject中,**type** 对应的是对象类型,包含String对象、List对象、Hash对象、Set对象、zset对象。**encoding** 对应的是编码。 + +- String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。 +- List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码 +- Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。 +- Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。 +- Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码 + +## 合理的线程模型 + + +### 单线程模型:避免了上下文切换 + +Redis是单线程的,其实是指**Redis的网络IO和键值对读写**是由一个线程来完成的。但Redis的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。 + +Redis的单线程模型,避免了**CPU不必要的上下文切换**和**竞争锁的消耗**。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的内存数据库,所以要慎用如lrange和smembers、hgetall等命令。 + +什么是**上下文切换**?举个粟子: + +> - 比如你在看一本英文小说,你看到某一页,发现有个单词不会读,你加了个书签,然后去查字典。查完字典后,你回来从书签那里继续开始读,这个流程就很舒畅。 +> - 如果你一个人读这本书,肯定没啥问题。但是如果你去查字典的时候,别的小伙伴翻了一下你的书,然后溜了。你再回来看的时候,发现书不是你看的那一页了,你得花时间找到你的那一页。 +> - 一本书,你一个人怎么看怎么打标签都没事,但是人多了翻来翻去,这本书各种标记就很乱了。可能这个解释很粗糙,但是道理应该是一样的。 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/053215e73f844b8da8c2880205947fc1~tplv-k3u1fbpfcp-zoom-1.image) + +### I/O 多路复用 + +什么是I/O多路复用? +- I/O :网络 I/O +- 多路 :多个网络连接 +- 复用:复用同一个线程。 +- IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。 + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07838d0a48ef4b38acccb7b52e5435e1~tplv-k3u1fbpfcp-zoom-1.image) + + +> 多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。 + +## 虚拟内存机制 + +Redis直接自己构建了VM机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。 + +**Redis的虚拟内存机制是啥呢?** +> 虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。 + +### 参考与感谢 + +- [Redis之VM机制](https://www.codenong.com/cs106843764/) +- [一文揭秘单线程的Redis为什么这么快?](https://zhuanlan.zhihu.com/p/57089960) +- [洞察|Redis是单线程的,但Redis为什么这么快?](https://zhuanlan.zhihu.com/p/42272979) + + diff --git "a/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md" "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md" new file mode 100644 index 0000000..eae7eec --- /dev/null +++ "b/Java\351\235\242\350\257\225\351\242\230\351\233\206\347\273\223\345\217\267/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP\345\215\217\350\256\25615\350\277\236\351\227\256.md" @@ -0,0 +1,395 @@ +### 前言 + +TCP协议是大厂面试必问的知识点。整理了15道非常经典的TCP面试题,希望大家都找到理想的offer呀 + + +![](https://files.mdnice.com/user/3535/47429a24-7e0b-4bf6-8200-7e2ec1baad63.png) + + +- 公众号:**捡田螺的小男孩** + +### 1. 讲下TCP三次握手流程 + +![](https://files.mdnice.com/user/3535/43f4b02a-ad18-45e5-8d6e-52349cc371d6.png) + +开始客户端和服务器都处于CLOSED状态,然后服务端开始监听某个端口,进入LISTEN状态 + +- 第一次握手(SYN=1, seq=x),发送完毕后,客户端进入 SYN_SEND 状态 +- 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 发送完毕后,服务器端进入 SYN_RCVD 状态。 +- 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。 + +### 2.TCP握手为什么是三次,不能是两次?不能是四次? + +TCP握手为什么是三次呢?为了方便理解,我们以谈恋爱为例子:两个人能走到一起,最重要的事情就是相爱,就是**我爱你,并且我知道,你也爱我**,接下来我们以此来模拟三次握手的过程: + + +![](https://files.mdnice.com/user/3535/374acab7-d609-4c7b-9db6-6a40dbe42926.png) + + +**为什么握手不能是两次呢?** + +如果只有两次握手,女孩子可能就不知道,她的那句**我也爱你**,男孩子是否**收到**,恋爱关系就不能愉快展开。 + +**为什么握手不能是四次呢?** + +因为握手不能是四次呢?因为三次已经够了,三次已经能让双方都知道:你爱我,我也爱你。而四次就多余了。 + +### 3. 讲下TCP四次挥手过程 + +![](https://files.mdnice.com/user/3535/439e735c-c443-4b2f-96b1-b750004d8d05.png) + +1. 第一次挥手(FIN=1,seq=u),发送完毕后,客户端进入FIN_WAIT_1 状态 +2. 第二次挥手(ACK=1,ack=u+1,seq =v),发送完毕后,服务器端进入CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态 +3. 第三次挥手(FIN=1,ACK1,seq=w,ack=u+1),发送完毕后,服务器端进入LAST_ACK 状态,等待来自客户端的最后一个ACK。 +4. 第四次挥手(ACK=1,seq=u+1,ack=w+1),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,**等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后**,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 + +### 4. TCP挥手为什么需要四次呢? + +举个例子吧! + +> 小明和小红打电话聊天,通话差不多要结束时,小红说“我没啥要说的了”,小明回答“我知道了”。但是小明可能还会有要说的话,小红不能要求小明跟着自己的节奏结束通话,于是小明可能又叽叽歪歪说了一通,最后小明说“我说完了”,小红回答“知道了”,这样通话才算结束。 + +![](https://files.mdnice.com/user/3535/966cef0c-1477-4eca-aadf-059219da0198.png) + +### 5. TIME-WAIT 状态为什么需要等待 2MSL + +![](https://files.mdnice.com/user/3535/f56000c8-da62-4370-9559-71bf312f6214.png) + +2MSL,2 Maximum Segment Lifetime,即两个最大段生命周期 + +> - 1个 MSL 保证四次挥手中主动关闭方最后的 ACK 报文能最终到达对端 +> - 1个 MSL 保证对端没有收到 ACK 那么进行重传的 FIN 报文能够到达 + +### 6.TCP 和 UDP 的区别 + +1. TCP面向连接((如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。 +2. TCP要求安全性,提供可靠的服务,通过TCP连接传送的数据,不丢失、不重复、安全可靠。而UDP尽最大努力交付,即不保证可靠交付。 +3. TCP是点对点连接的,UDP一对一,一对多,多对多都可以 +4. TCP传输效率相对较低,而UDP传输效率高,它适用于对高速传输和实时性有较高的通信或广播通信。 +5. TCP适合用于网页,邮件等;UDP适合用于视频,语音广播等 +6. TCP面向字节流,UDP面向报文 + +### 7. TCP报文首部有哪些字段,说说其作用 + + +![](https://files.mdnice.com/user/3535/f9f25411-09d7-4441-bf64-3f0881c4fc01.png) + +- **16位端口号**:源端口号,主机该报文段是来自哪里;目标端口号,要传给哪个上层协议或应用程序 +- **32位序号**:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。 +- **32位确认号**:用作对另一方发送的tcp报文段的响应。其值是收到的TCP报文段的序号值加1。 +- **4位头部长度**:表示tcp头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。 +- **6位标志位**:URG(紧急指针是否有效),ACk(表示确认号是否有效),PSH(缓冲区尚未填满),RST(表示要求对方重新建立连接),SYN(建立连接消息标志接),FIN(表示告知对方本端要关闭连接了) +- **16位窗口大小**:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。 +- **16位校验和**:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。 +- **16位紧急指针**:一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此,确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。 + +### 8. TCP 是如何保证可靠性的 + + +![](https://files.mdnice.com/user/3535/fce5ee83-8664-4f82-a453-fe25bc35dd88.png) + + +- 首先,TCP的连接是基于**三次握手**,而断开则是**四次挥手**。确保连接和断开的可靠性。 +- 其次,TCP的可靠性,还体现在**有状态**;TCP会记录哪些数据发送了,哪些数据被接受了,哪些没有被接受,并且保证数据包按序到达,保证数据传输不出差错。 +- 再次,TCP的可靠性,还体现在**可控制**。它有报文校验、ACK应答、**超时重传(发送方)**、失序数据重传(接收方)、丢弃重复数据、流量控制(滑动窗口)和拥塞控制等机制。 + +### 9. TCP 重传机制 + +#### 超时重传 +TCP 为了实现可靠传输,实现了重传机制。最基本的重传机制,就是**超时重传**,即在发送数据报文时,设定一个定时器,每间隔一段时间,没有收到对方的ACK确认应答报文,就会重发该报文。 + +这个间隔时间,一般设置为多少呢?我们先来看下什么叫**RTT(Round-Trip Time,往返时间)**。 + +![](https://files.mdnice.com/user/3535/05b6c061-2515-4367-aa58-0526db10f6b6.png) + +RTT就是,一个数据包从发出去到回来的时间,即**数据包的一次往返时间**。超时重传时间,就是Retransmission Timeout ,简称**RTO**。 + +**RTO设置多久呢?** +- 如果RTO比较小,那很可能数据都没有丢失,就重发了,这会导致网络阻塞,会导致更多的超时出现。 +- 如果RTO比较大,等到花儿都谢了还是没有重发,那效果就不好了。 + +一般情况下,RTO略大于RTT,效果是最好的。一些小伙伴会问,超时时间有没有计算公式呢?有的!有个标准方法算RTO的公式,也叫**Jacobson / Karels 算法**。我们一起来看下计算RTO的公式 + +**1. 先计算SRTT(计算平滑的RTT)** + +``` +SRTT = (1 - α) * SRTT + α * RTT //求 SRTT 的加权平均 +``` + +**2. 再计算RTTVAR (round-trip time variation)** + + +``` +RTTVAR = (1 - β) * RTTVAR + β * (|RTT - SRTT|) //计算 SRTT 与真实值的差距 +``` + +**3. 最终的RTO** + +``` +RTO = µ * SRTT + ∂ * RTTVAR = SRTT + 4·RTTVAR +``` + +其中,```α = 0.125,β = 0.25, μ = 1,∂ = 4```,这些参数都是大量结果得出的最优参数。 + +但是,超时重传会有这些缺点: +> - 当一个报文段丢失时,会等待一定的超时周期然后才重传分组,增加了端到端的时延。 +> - 当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况:其后的报文段已经被接收端接收但却迟迟得不到确认,发送端会认为也丢失了,从而引起不必要的重传,既浪费资源也浪费时间。 + +并且,TCP有个策略,就是超时时间间隔会加倍。超时重传需要**等待很长时间**。因此,还可以使用**快速重传**机制。 + +#### 快速重传 + +**快速重传**机制,它不以时间驱动,而是以数据驱动。它基于接收端的反馈信息来引发重传。 + +一起来看下快速重传流程: + +![快速重传流程](https://files.mdnice.com/user/3535/2c172057-bb6c-40e1-8d64-b6a15818f596.png) + +发送端发送了 1,2,3,4,5,6 份数据: + +- 第一份 Seq=1 先送到了,于是就 Ack 回 2; +- 第二份 Seq=2 也送到了,假设也正常,于是ACK 回 3; +- 第三份 Seq=3 由于网络等其他原因,没送到; +- 第四份 Seq=4 也送到了,但是因为Seq3没收到。所以ACK回3; +- 后面的 Seq=4,5的也送到了,但是ACK还是回复3,因为Seq=3没收到。 +- 发送端连着收到三个重复冗余ACK=3的确认(实际上是4个,但是前面一个是正常的ACK,后面三个才是重复冗余的),便知道哪个报文段在传输过程中丢失了,于是在定时器过期之前,重传该报文段。 +- 最后,接收到收到了 Seq3,此时因为 Seq=4,5,6都收到了,于是ACK回7. + +但**快速重传**还可能会有个问题:ACK只向发送端告知最大的有序报文段,到底是哪个报文丢失了呢?**并不确定**!那到底该重传多少个包呢? +> 是重传 Seq3 呢?还是重传 Seq3、Seq4、Seq5、Seq6 呢?因为发送端并不清楚这三个连续的 ACK3 是谁传回来的。 + +#### 带选择确认的重传(SACK) + +为了解决快速重传的问题:**应该重传多少个包**? TCP提供了**SACK方法**(带选择确认的重传,Selective Acknowledgment)。 + +**SACK机制**就是,在快速重传的基础上,接收端返回最近收到的报文段的序列号范围,这样发送端就知道接收端哪些数据包没收到,酱紫就很清楚该重传哪些数据包啦。SACK标记是加在TCP头部**选项**字段里面的。 + +![SACK机制](https://files.mdnice.com/user/3535/9475e768-9d4d-46dd-97a5-ec2298c433bd.png) + +如上图中,发送端收到了三次同样的ACK=30的确认报文,于是就会触发快速重发机制,通过SACK信息发现只有```30~39```这段数据丢失,于是重发时就只选择了这个```30~39```的TCP报文段进行重发。 + +#### D-SACK + + D-SACK,即Duplicate SACK(重复SACK),在SACK的基础上做了一些扩展,,主要用来告诉发送方,有哪些数据包自己重复接受了。DSACK的目的是帮助发送方判断,是否发生了包失序、ACK丢失、包重复或伪重传。让TCP可以更好的做网络流控。来看个图吧: + +![D-SACK简要流程](https://files.mdnice.com/user/3535/d3647f5d-ce7c-4998-953a-04e8c8a9e71c.png) + +### 10. 聊聊TCP的滑动窗口 + +TCP 发送一个数据,需要收到确认应答,才会发送下一个数据。这样有个缺点,就是效率会比较低。 +> 这就好像我们面对面聊天,你说完一句,我应答后,你才会说下一句。那么,如果我在忙其他事情,没有能够及时回复你。你说完一句后,要等到我忙完回复你,你才说下句,这显然很不现实。 + +为了解决这个问题,TCP引入了**窗口**,它是操作系统开辟的一个缓存空间。窗口大小值表示无需等待确认应答,而可以继续发送数据的最大值。 + +TCP头部有个字段叫win,也即那个**16位的窗口大小**,它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度,从而达到**流量控制**的目的。 +> 通俗点讲,就是接受方每次收到数据包,在发送确认报文的时候,同时告诉发送方,自己的缓存区还有多少空余空间,缓冲区的空余空间,我们就称之为接受窗口大小。这就是win。 + +TCP 滑动窗口分为两种: 发送窗口和接收窗口。**发送端的滑动窗口**包含四大部分,如下: +- 已发送且已收到ACK确认 +- 已发送但未收到ACK确认 +- 未发送但可以发送 +- 未发送也不可以发送 + +![](https://files.mdnice.com/user/3535/8f7d9784-f6e6-47d8-82bb-deb398431025.png) + +- 虚线矩形框,就是发送窗口。 +- SND.WND: 表示发送窗口的大小,上图虚线框的格子数就是14个。 +- SND.UNA: 一个绝对指针,它指向的是已发送但未确认的第一个字节的序列号。 +- SND.NXT:下一个发送的位置,它指向未发送但可以发送的第一个字节的序列号。 + +接收方的滑动窗口包含三大部分,如下: +- 已成功接收并确认 +- 未收到数据但可以接收 +- 未收到数据并不可以接收的数据 + +![](https://files.mdnice.com/user/3535/40b906fe-aa60-42f3-b7bf-b4fcaa9a0588.png) + +- 虚线矩形框,就是接收窗口。 +- REV.WND: 表示接收窗口的大小,上图虚线框的格子就是9个。 +- REV.NXT:下一个接收的位置,它指向未收到但可以接收的第一个字节的序列号。 + +### 11. 聊聊TCP的流量控制 + +TCP三次握手,发送端和接收端进入到ESTABLISHED状态,它们即可以愉快地传输数据啦。 + +但是发送端不能疯狂地向接收端发送数据,因为接收端接收不过来的话,接收方只能把处理不过来的数据存在缓存区里。如果缓存区都满了,发送方还在疯狂发送数据的话,接收方只能把收到的数据包丢掉,这就浪费了网络资源啦。 + +> TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量,这就是**流量控制**。 + +TCP通过滑动窗口来控制流量,我们看下流量控制的**简要流程**吧: + +首先双方三次握手,初始化各自的窗口大小,均为 400 个字节。 + + +![TCP的流量控制](https://files.mdnice.com/user/3535/e233b594-72b5-4f9a-bc9c-23bb5c065bfe.png) + +1. 假如当前发送方给接收方发送了200个字节,那么,发送方的```SND.NXT```会右移200个字节,也就是说当前的可用窗口减少了200 个字节。 +2. 接受方收到后,放到缓冲队列里面,REV.WND =400-200=200字节,所以win=200字节返回给发送方。接收方会在 ACK 的报文首部带上缩小后的滑动窗口200字节 +3. 发送方又发送200字节过来,200字节到达,继续放到缓冲队列。不过这时候,由于大量负载的原因,接受方处理不了这么多字节,只能处理100字节,剩余的100字节继续放到缓冲队列。这时候,REV.WND = 400-200-100=100字节,即win=100返回发送方。 +4. 发送方继续干活,发送100字节过来,这时候,接受窗口win变为0。 +5. 发送方停止发送,开启一个定时任务,每隔一段时间,就去询问接受方,直到win大于0,才继续开始发送。 + +### 12. TCP的拥塞控制 + +拥塞控制是**作用于网络的,防止过多的数据包注入到网络中,避免出现网络负载过大的情况**。它的目标主要是最大化利用网络上瓶颈链路的带宽。它跟**流量控制**又有什么区别呢?流量控制是作用于接收者的,根据**接收端的实际接收能力控制发送速度**,防止分组丢失的。 + +我们可以把网络链路比喻成一根水管,如果我们想最大化利用网络来传输数据,那就是尽快让水管达到最佳充满状态。 + +![](https://files.mdnice.com/user/3535/f4b5b102-75db-47cc-8c1a-83fa01941dcc.png) + +发送方维护一个**拥塞窗口cwnd(congestion window)** 的变量,用来估算在一段时间内这条链路(水管)可以承载和运输的数据(水)的数量。它大小代表着网络的拥塞程度,并且是动态变化的,但是为了达到最大的传输效率,我们该如何知道这条水管的运送效率是多少呢? + +一个比较简单的方法就是不断增加传输的水量,直到水管快要爆裂为止(对应到网络上就是发生丢包),用 TCP 的描述就是: +> 只要网络中没有出现拥塞,拥塞窗口的值就可以再增大一些,以便把更多的数据包发送出去,但只要网络出现拥塞,拥塞窗口的值就应该减小一些,以减少注入到网络中的数据包数。 + +实际上,拥塞控制主要有这几种常用算法 +- 慢启动 +- 拥塞避免 +- 拥塞发生 +- 快速恢复 + +#### 慢启动算法 + +慢启动算法,表面意思就是,别急慢慢来。它表示TCP建立连接完成后,一开始不要发送大量的数据,而是先探测一下网络的拥塞程度。由小到大逐渐增加拥塞窗口的大小,如果没有出现丢包,**每收到一个ACK,就将拥塞窗口cwnd大小就加1(单位是MSS)**。**每轮次**发送窗口增加一倍,呈指数增长,如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。 + +- TCP连接完成,初始化cwnd = 1,表明可以传一个MSS单位大小的数据。 +- 每当收到一个ACK,cwnd就加一; +- 每当过了一个RTT,cwnd就增加一倍; 呈指数让升 + +![](https://files.mdnice.com/user/3535/c9edd5d1-0302-45b7-bee7-22d1e505b085.png) + +为了防止cwnd增长过大引起网络拥塞,还需设置一个**慢启动阀值ssthresh**(slow start threshold)状态变量。当```cwnd```到达该阀值后,就好像水管被关小了水龙头一样,减少拥塞状态。即当**cwnd >ssthresh**时,进入了**拥塞避免**算法。 + + +#### 拥塞避免算法 + +一般来说,慢启动阀值ssthresh是65535字节,```cwnd```到达**慢启动阀值**后 +- 每收到一个ACK时,cwnd = cwnd + 1/cwnd +- 当每过一个RTT时,cwnd = cwnd + 1 + +显然这是一个线性上升的算法,避免过快导致网络拥塞问题。 + +![](https://files.mdnice.com/user/3535/600ed914-5f98-4c01-9f1d-d7dcc12244b8.png) + +#### 拥塞发生 + +当网络拥塞发生**丢包**时,会有两种情况: + +- RTO超时重传 +- 快速重传 + +如果是发生了**RTO超时重传**,就会使用拥塞发生算法 + +- 慢启动阀值sshthresh = cwnd /2 +- cwnd 重置为 1 +- 进入新的慢启动过程 + + +![](https://files.mdnice.com/user/3535/9e54bfb4-ed83-42a9-aeb0-f98b44563067.png) + +这真的是**辛辛苦苦几十年,一朝回到解放前**。其实还有更好的处理方式,就是**快速重传**。发送方收到3个连续重复的ACK时,就会快速地重传,不必等待**RTO超时**再重传。 + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e5f5ca98465c40b0936ed83aba2ffc15~tplv-k3u1fbpfcp-watermark.image) + +慢启动阀值ssthresh 和 cwnd 变化如下: + +- 拥塞窗口大小 cwnd = cwnd/2 +- 慢启动阀值 ssthresh = cwnd +- 进入快速恢复算法 + +#### 快速恢复 + +快速重传和快速恢复算法一般同时使用。快速恢复算法认为,还有3个重复ACK收到,说明网络也没那么糟糕,所以没有必要像RTO超时那么强烈。 + +正如前面所说,进入快速恢复之前,cwnd 和 sshthresh已被更新: +``` +- cwnd = cwnd /2 +- sshthresh = cwnd +``` + +然后,真正的快速算法如下: + +- cwnd = sshthresh + 3 +- 重传重复的那几个ACK(即丢失的那几个数据包) +- 如果再收到重复的 ACK,那么 cwnd = cwnd +1 +- 如果收到新数据的 ACK 后, cwnd = sshthresh。因为收到新数据的 ACK,表明恢复过程已经结束,可以再次进入了拥塞避免的算法了。 + +![](https://files.mdnice.com/user/3535/1cb2de35-db67-4efa-8b64-1ea00d57c116.png) + +### 13. 半连接队列和 SYN Flood 攻击的关系 + +TCP进入三次握手前,服务端会从**CLOSED**状态变为**LISTEN**状态,同时在内部创建了两个队列:半连接队列(SYN队列)和全连接队列(ACCEPT队列)。 + +什么是**半连接队列(SYN队列)** 呢? 什么是**全连接队列(ACCEPT队列)** 呢?回忆下TCP三次握手的图: + +![三次握手](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/67e2444df1934f549e7509fb5ce4b561~tplv-k3u1fbpfcp-watermark.image) + +- TCP三次握手时,客户端发送SYN到服务端,服务端收到之后,便回复**ACK和SYN**,状态由**LISTEN变为SYN_RCVD**,此时这个连接就被推入了**SYN队列**,即半连接队列。 +- 当客户端回复ACK, 服务端接收后,三次握手就完成了。这时连接会等待被具体的应用取走,在被取走之前,它被推入ACCEPT队列,即全连接队列。 + +SYN Flood是一种典型的DoS (Denial of Service,拒绝服务) 攻击,它在短时间内,伪造**不存在的IP地址**,向服务器大量发起SYN报文。当服务器回复SYN+ACK报文后,不会收到ACK回应报文,导致服务器上建立大量的半连接半连接队列满了,这就无法处理正常的TCP请求啦。 + +主要有 **syn cookie**和**SYN Proxy防火墙**等方案应对。 + +- **syn cookie**:在收到SYN包后,服务器根据一定的方法,以数据包的源地址、端口等信息为参数计算出一个cookie值作为自己的SYNACK包的序列号,回复SYN+ACK后,服务器并不立即分配资源进行处理,等收到发送方的ACK包后,重新根据数据包的源地址、端口计算该包中的确认序列号是否正确,如果正确则建立连接,否则丢弃该包。 + +- **SYN Proxy防火墙**:服务器防火墙会对收到的每一个SYN报文进行代理和回应,并保持半连接。等发送方将ACK包返回后,再重新构造SYN包发到服务器,建立真正的TCP连接。 + +### 14. Nagle 算法与延迟确认 + +#### Nagle算法 + +如果发送端疯狂地向接收端发送很小的包,比如就1个字节,那么亲爱的小伙伴,你们觉得会有什么问题呢? + +> TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。**Nagle算法**就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。 + +Nagle算法的基本定义是:**任意时刻,最多只能有一个未被确认的小段**。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。 + +Nagle算法的实现规则: + +- 如果包长度达到MSS,则允许发送; +- 如果该包含有FIN,则允许发送; +- 设置了TCP_NODELAY选项,则允许发送; +- 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送; +- 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。 + +#### 延迟确认 + +如果接受方刚接收到发送方的数据包,在很短很短的时间内,又接收到第二个包。那么请问接收方是一个一个地回复好点,还是合并一起回复好呢? + +> 接收方收到数据包后,如果暂时没有数据要发给对端,它可以等一段时再确认(Linux上默认是40ms)。如果这段时间刚好有数据要传给对端,ACK就随着数据传输,而不需要单独发送一次ACK。如果超过时间还没有数据要发送,也发送ACK,避免对端以为丢包。 + +但是有些场景不能延迟确认,比如发现了**乱序包**、**接收到了大于一个 frame 的报文,且需要调整窗口大小**等。 + +一般情况下,**Nagle算法和延迟确认**不能一起使用,Nagle算法意味着延迟发,**延迟确认**意味着延迟接收,酱紫就会造成更大的延迟,会产生性能问题。 + +### 15. TCP的粘包和拆包 + +TCP是面向流,没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一**个完整的包可能会被TCP拆分成多个包进行发送**,**也有可能把多个小的包封装成一个大的数据包发送**,这就是所谓的TCP粘包和拆包问题。 + +![TCP的粘包和拆包](https://files.mdnice.com/user/3535/cf617d8f-70c3-4687-bb20-1bc3518bed11.png) + + +**为什么会产生粘包和拆包呢?** + +- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包; +- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包; +- 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包; +- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。 + +**解决方案:** + +- 发送端将每个数据包封装为固定长度 +- 在数据尾部增加特殊字符进行分割 +- 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小。 + +### 参考与感谢 +- [TCP 的那些事儿(下)](https://coolshell.cn/articles/11609.html "TCP 的那些事儿(下)") +- [面试头条你需要懂的 TCP 拥塞控制原理](https://zhuanlan.zhihu.com/p/76023663 "面试头条你需要懂的 TCP 拥塞控制原理") +- [30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制发愁](https://zhuanlan.zhihu.com/p/133307545 "30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制发愁") +- [TCP协议灵魂之问,巩固你的网路底层基础](https://juejin.cn/post/6844904070889603085 "TCP协议灵魂之问,巩固你的网路底层基础") +- [TCP粘包和拆包](https://blog.csdn.net/ailunlee/article/details/95944377 "TCP粘包和拆包") +- 百度百科 + + + diff --git "a/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/README.md" "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/README.md" new file mode 100644 index 0000000..f335927 --- /dev/null +++ "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/README.md" @@ -0,0 +1,2 @@ +- [聊聊select for update到底加了什么锁](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506728&idx=1&sn=5526ee3984e971d4c3b251c2ad76d658&chksm=c1e026a4f697afb28224d5ce0ecca7432879b357cd6433834c66d94c72a1935ba13e2e3e274e&token=337310304&lang=zh_CN#rd) +- [数据库死锁排查思路分享](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507770&idx=1&sn=b84b20aca057b34d511a501ff91941b5&chksm=c1e022b6f697aba05248128cb82f93aed341b1cc80e6d568c7150a4ffa6775692c7c9fa423a3&token=1822874069&lang=zh_CN#rd) diff --git "a/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md" "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md" new file mode 100644 index 0000000..3aeda16 --- /dev/null +++ "b/Mysql\345\237\272\347\241\200\345\255\246\344\271\240/order by\350\257\246\350\247\243.md" @@ -0,0 +1,348 @@ +## 前言 + +日常开发中,我们经常会使用到order by,亲爱的小伙伴,你是否知道order by 的工作原理呢?order by的优化思路是怎样的呢?使用order by有哪些注意的问题呢?本文将跟大家一起来学习,攻克order by~ + + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b1eda4fbc6c458c9182545d93cb6f15~tplv-k3u1fbpfcp-watermark.image) + +- 微信公众号:**捡田螺的小男孩** +- [github地址,感谢每一颗star](https://github.com/whx123/JavaHome) +- 如果觉得有收获,帮忙点赞,转发下哈,感谢感谢 + + +## 一个使用order by 的简单例子 + +假设用一张员工表,表结构如下: + +``` +CREATE TABLE `staff` ( +`id` BIGINT ( 11 ) AUTO_INCREMENT COMMENT '主键id', +`id_card` VARCHAR ( 20 ) NOT NULL COMMENT '身份证号码', +`name` VARCHAR ( 64 ) NOT NULL COMMENT '姓名', +`age` INT ( 4 ) NOT NULL COMMENT '年龄', +`city` VARCHAR ( 64 ) NOT NULL COMMENT '城市', +PRIMARY KEY ( `id`), +INDEX idx_city ( `city` ) +) ENGINE = INNODB COMMENT '员工表'; + +``` + +表数据如下: + + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aadfe321a1b74141a418f4a4c2f75e82~tplv-k3u1fbpfcp-watermark.image) + +我们现在有这么一个需求:**查询前10个,来自深圳员工的姓名、年龄、城市,并且按照年龄小到大排序**。对应的 SQL 语句就可以这么写: + +``` +select name,age,city from staff where city = '深圳' order by age limit 10; +``` + +这条语句的逻辑很清楚,但是它的**底层执行流程**是怎样的呢? + +## order by 工作原理 + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/582926f8d02e44d4b94e23a3fafc0ec8~tplv-k3u1fbpfcp-watermark.image) + +### explain 执行计划 + +我们先用**Explain**关键字查看一下执行计划 + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2da79360c7204f5d8fada7f8ec2d21ba~tplv-k3u1fbpfcp-watermark.image) + +- 执行计划的**key**这个字段,表示使用到索引idx_city +- Extra 这个字段的 **Using index condition** 表示索引条件 +- Extra 这个字段的 **Using filesort**表示用到排序 + +我们可以发现,这条SQL使用到了索引,并且也用到排序。那么它是**怎么排序**的呢? + +### 全字段排序 + +MySQL 会给每个查询线程分配一块小**内存**,用于**排序**的,称为 **sort_buffer**。什么时候把字段放进去排序呢,其实是通过```idx_city```索引找到对应的数据,才把数据放进去啦。 + +我们回顾下索引是怎么找到匹配的数据的,现在先把索引树画出来吧,**idx_city**索引树如下: + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/773e727c45254b7a858a780fdeff8a7c~tplv-k3u1fbpfcp-watermark.image) + +idx_city索引树,叶子节点存储的是**主键id**。 还有一棵id主键聚族索引树,我们再画出聚族索引树图吧: + + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f12ea50032b6428c899ee143663aa3de~tplv-k3u1fbpfcp-watermark.image) + + +**我们的查询语句是怎么找到匹配数据的呢**?先通过**idx_city**索引树,找到对应的主键id,然后再通过拿到的主键id,搜索**id主键索引树**,找到对应的行数据。 + +加上**order by**之后,整体的执行流程就是: + +1. MySQL 为对应的线程初始化**sort_buffer**,放入需要查询的name、age、city字段; +2. 从**索引树idx_city**, 找到第一个满足 city='深圳’条件的主键 id,也就是图中的id=9; +3. 到**主键 id 索引树**拿到id=9的这一行数据, 取name、age、city三个字段的值,存到sort_buffer; +4. 从**索引树idx_city** 拿到下一个记录的主键 id,即图中的id=13; +5. 重复步骤 3、4 直到**city的值不等于深圳**为止; +6. 前面5步已经查找到了所有**city为深圳**的数据,在 sort_buffer中,将所有数据根据age进行排序; +7. 按照排序结果取前10行返回给客户端。 + +执行示意图如下: + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d8760fdf99741b492ab032cd3d0d87c~tplv-k3u1fbpfcp-watermark.image) + +将查询所需的字段全部读取到sort_buffer中,就是**全字段排序**。这里面,有些小伙伴可能会有个疑问,把查询的所有字段都放到sort_buffer,而sort_buffer是一块内存来的,如果数据量太大,sort_buffer放不下怎么办呢? + +### 磁盘临时文件辅助排序 + +实际上,sort_buffer的大小是由一个参数控制的:**sort_buffer_size**。如果要排序的数据小于sort_buffer_size,排序在**sort_buffer** 内存中完成,如果要排序的数据大于sort_buffer_size,则**借助磁盘文件来进行排序** + +如何确定是否使用了磁盘文件来进行排序呢? 可以使用以下这几个命令 + +``` +## 打开optimizer_trace,开启统计 +set optimizer_trace = "enabled=on"; +## 执行SQL语句 +select name,age,city from staff where city = '深圳' order by age limit 10; +## 查询输出的统计信息 +select * from information_schema.optimizer_trace +``` + +可以从 **number_of_tmp_files** 中看出,是否使用了临时文件。 + + + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c876d88537ce4b67bcf8f991df17aa94~tplv-k3u1fbpfcp-watermark.image) + +**number_of_tmp_files** 表示使用来排序的磁盘临时文件数。如果number_of_tmp_files>0,则表示使用了磁盘文件来进行排序。 + +使用了磁盘临时文件,整个排序过程又是怎样的呢? + +1. 从**主键Id索引树**,拿到需要的数据,并放到**sort_buffer内存**块中。当sort_buffer快要满时,就对sort_buffer中的数据排序,排完后,把数据临时放到磁盘一个小文件中。 +2. 继续回到主键 id 索引树取数据,继续放到sort_buffer内存中,排序后,也把这些数据写入到磁盘临时小文件中。 +3. 继续循环,直到取出所有满足条件的数据。最后把磁盘的临时排好序的小文件,合并成一个有序的大文件。 + + +**TPS:** 借助磁盘临时小文件排序,实际上使用的是**归并排序**算法。 + +小伙伴们可能会有个疑问,既然**sort_buffer**放不下,就需要用到临时磁盘文件,这会影响排序效率。那为什么还要把排序不相关的字段(name,city)放到sort_buffer中呢?只放排序相关的age字段,它**不香**吗? 可以了解下**rowid 排序**。 + + +### rowid 排序 + +rowid 排序就是,只把查询SQL**需要用于排序的字段和主键id**,放到sort_buffer中。那怎么确定走的是全字段排序还是rowid 排序排序呢? + +实际上有个参数控制的。这个参数就是**max_length_for_sort_data**,它表示MySQL用于排序行数据的长度的一个参数,如果单行的长度超过这个值,MySQL 就认为单行太大,就换rowid 排序。我们可以通过命令看下这个参数取值。 + + +``` +show variables like 'max_length_for_sort_data'; +``` + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ce8fc30884e74dd5b09c0028640ce7ea~tplv-k3u1fbpfcp-watermark.image) + +**max_length_for_sort_data** 默认值是1024。因为本文示例中name,age,city长度=64+4+64 =132 < 1024, 所以走的是全字段排序。我们来改下这个参数,改小一点, + +``` +## 修改排序数据最大单行长度为32 +set max_length_for_sort_data = 32; +## 执行查询SQL +select name,age,city from staff where city = '深圳' order by age limit 10; +``` + +使用rowid 排序的话,整个SQL执行流程又是怎样的呢? + +1. MySQL 为对应的线程初始化**sort_buffer**,放入需要排序的age字段,以及主键id; +2. 从**索引树idx_city**, 找到第一个满足 city='深圳’条件的主键 id,也就是图中的id=9; +3. 到**主键 id 索引树**拿到id=9的这一行数据, 取age和主键id的值,存到sort_buffer; +4. 从**索引树idx_city** 拿到下一个记录的主键 id,即图中的id=13; +5. 重复步骤 3、4 直到**city的值不等于深圳**为止; +6. 前面5步已经查找到了所有city为深圳的数据,在 **sort_buffer**中,将所有数据根据age进行排序; +7. 遍历排序结果,取前10行,并按照 id 的值**回到原表**中,取出city、name 和 age 三个字段返回给客户端。 + + +执行示意图如下: + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2addcf67082741e19637dae2e69ee515~tplv-k3u1fbpfcp-watermark.image) + + +对比一下**全字段排序**的流程,rowid 排序多了一次**回表**。 + +> 什么是回表?拿到主键再回到主键索引查询的过程,就叫做回表 + + +我们通过**optimizer_trace**,可以看到是否使用了rowid排序的: + + +``` +## 打开optimizer_trace,开启统计 +set optimizer_trace = "enabled=on"; +## 执行SQL语句 +select name,age,city from staff where city = '深圳' order by age limit 10; +## 查询输出的统计信息 +select * from information_schema.optimizer_trace + +``` + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/446258ee4bfa40e6ad80596202c8d5a0~tplv-k3u1fbpfcp-watermark.image) + + +### 全字段排序与rowid排序对比 + + +- 全字段排序: sort_buffer内存不够的话,就需要用到磁盘临时文件,造成**磁盘访问**。 +- rowid排序: sort_buffer可以放更多数据,但是需要再回到原表去取数据,比全字段排序多一次**回表**。 + +一般情况下,对于InnoDB存储引擎,会优先使**用全字段**排序。可以发现 **max_length_for_sort_data** 参数设置为1024,这个数比较大的。一般情况下,排序字段不会超过这个值,也就是都会走**全字段**排序。 + + +## order by的一些优化思路 + +我们如何优化order by语句呢? + + +- 因为数据是无序的,所以就需要排序。如果数据本身是有序的,那就不用排了。而索引数据本身是有序的,我们通过建立**联合索引**,优化order by 语句。 +- 我们还可以通过调整**max_length_for_sort_data**等参数优化; + + +### 联合索引优化 + +再回顾下示例SQL的查询计划 + +``` +explain select name,age,city from staff where city = '深圳' order by age limit 10; +``` + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7fce894de9024dd5913e98116dca832f~tplv-k3u1fbpfcp-watermark.image) + +我们给查询条件```city```和排序字段```age```,加个联合索引**idx_city_age**。再去查看执行计划 + +``` +alter table staff add index idx_city_age(city,age); +explain select name,age,city from staff where city = '深圳' order by age limit 10; +``` + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33621e91730b44e5bb88c27b74614b89~tplv-k3u1fbpfcp-watermark.image) + +可以发现,加上**idx_city_age**联合索引,就不需要**Using filesort**排序了。为什么呢?因为**索引本身是有序的**,我们可以看下**idx_city_age**联合索引示意图,如下: + + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/19b368a039a34678aeea8e297eee05e8~tplv-k3u1fbpfcp-watermark.image) + +整个SQL执行流程变成酱紫: +1. 从索引idx_city_age找到满足**city='深圳’** 的主键 id +2. 到**主键 id索引**取出整行,拿到 name、city、age 三个字段的值,作为结果集的一部分直接返回 +3. 从索引**idx_city_age**取下一个记录主键id +4. 重复步骤 2、3,直到查到**第10条**记录,或者是**不满足city='深圳’** 条件时循环结束。 + +流程示意图如下: + +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/95f6a85904934d9688598d436fbcc1ee~tplv-k3u1fbpfcp-watermark.image) + + +从示意图看来,还是有一次回表操作。针对本次示例,有没有更高效的方案呢?有的,可以使用**覆盖索引**: + +> 覆盖索引:在查询的数据列里面,不需要回表去查,直接从索引列就能取到想要的结果。换句话说,你SQL用到的索引列数据,覆盖了查询结果的列,就算上覆盖索引了。 + +我们给city,name,age 组成一个联合索引,即可用到了覆盖索引,这时候SQL执行时,连回表操作都可以省去啦。 + +### 调整参数优化 + +我们还可以通过调整参数,去优化order by的执行。比如可以调整sort_buffer_size的值。因为sort_buffer值太小,数据量大的话,会借助磁盘临时文件排序。如果MySQL服务器配置高的话,可以使用稍微调整大点。 + +我们还可以调整max_length_for_sort_data的值,这个值太小的话,order by会走rowid排序,会回表,降低查询性能。所以max_length_for_sort_data可以适当大一点。 + +当然,很多时候,这些MySQL参数值,我们直接采用默认值就可以了。 + +## 使用order by 的一些注意点 + +### 没有where条件,order by字段需要加索引吗 + +日常开发过程中,我们可能会遇到没有where条件的order by,那么,这时候order by后面的字段是否需要加索引呢。如有这么一个SQL,create_time是否需要加索引: + +``` +select * from A order by create_time; +``` + +无条件查询的话,即使create_time上有索引,也不会使用到。因为MySQL优化器认为走普通二级索引,再去回表成本比全表扫描排序更高。所以选择走全表扫描,然后根据全字段排序或者rowid排序来进行。 + +如果查询SQL修改一下: + +``` +select * from A order by create_time limit m; +``` +- 无条件查询,如果m值较小,是可以走索引的.因为MySQL优化器认为,根据索引有序性去回表查数据,然后得到m条数据,就可以终止循环,那么成本比全表扫描小,则选择走二级索引。 + + +### 分页limit过大时,会导致大量排序怎么办? + +假设SQL如下: +``` +select * from A order by a limit 100000,10 +``` + +- 可以记录上一页最后的id,下一页查询时,查询条件带上id,如: where id > 上一页最后id limit 10。 +- 也可以在业务允许的情况下,限制页数。 + + +### 索引存储顺序与order by不一致,如何优化? + +假设有联合索引 idx_age_name, 我们需求修改为这样:**查询前10个员工的姓名、年龄,并且按照年龄小到大排序,如果年龄相同,则按姓名降序排**。对应的 SQL 语句就可以这么写: + +``` +select name,age from staff order by age ,name desc limit 10; +``` +我们看下执行计划,发现使用到**Using filesort**。 + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bdc45d41d0744567bc753892661789b7~tplv-k3u1fbpfcp-watermark.image) + +这是因为,idx_age_name索引树中,age从小到大排序,如果**age相同,再按name从小到大排序**。而order by 中,是按age从小到大排序,如果**age相同,再按name从大到小排序**。也就是说,索引存储顺序与order by不一致。 + +我们怎么优化呢?如果MySQL是8.0版本,支持**Descending Indexes**,可以这样修改索引: + +``` +CREATE TABLE `staff` ( + `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主键id', + `id_card` varchar(20) NOT NULL COMMENT '身份证号码', + `name` varchar(64) NOT NULL COMMENT '姓名', + `age` int(4) NOT NULL COMMENT '年龄', + `city` varchar(64) NOT NULL COMMENT '城市', + PRIMARY KEY (`id`), + KEY `idx_age_name` (`age`,`name` desc) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='员工表'; +``` + + +### 使用了in条件多个属性时,SQL执行是否有排序过程 + +如果我们有**联合索引idx_city_name**,执行这个SQL的话,是不会走排序过程的,如下: + +``` +select * from staff where city in ('深圳') order by age limit 10; +``` + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0d5c0099035f48529075b6bc9d396cca~tplv-k3u1fbpfcp-watermark.image) + + + +但是,如果使用in条件,并且有多个条件时,就会有排序过程。 + +``` + explain select * from staff where city in ('深圳','上海') order by age limit 10; +``` + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1813404917da4dc9898ab7a14afb811f~tplv-k3u1fbpfcp-watermark.image) + +这是因为:in有两个条件,在满足深圳时,age是排好序的,但是把满足上海的age也加进来,就不能保证满足所有的age都是排好序的。因此需要Using filesort。 + +## 最后 + +- 如果觉得有收获,帮忙点赞,转发下哈,感谢感谢 +- 微信搜索公众号:**捡田螺的小男孩**,加个好友,进技术交流群 + + +### 参考与感谢 + +- MySQL实战45讲 + + + diff --git a/README.md b/README.md index 6018cc3..7018310 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,46 @@ -整理一份超级详细的Java面试题+后端基础+日常工作总结,做最暖心的男孩子,后面会慢慢把答案完善,希望大家找到理想offer +## 前言 -## 个人公众号 +整理一份超级详细的Java面试题+后端基础+日常工作总结,做最暖心的男孩子,后面会慢慢完善,希望大家找到理想offer + +⭐ 点右上角给一个 Star,鼓励技术人输出更多干货,感谢感谢,爱了! ! + +作者捡田螺的小男孩,浪迹过几家大厂,**掘金优秀创作者**,CSDN博主,知乎博主。以下内容全部出自公众号:**捡田螺的小男孩**,欢迎关注。 + +- [田螺原创精品100篇](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497536&idx=1&sn=3ac9934f607d79e51457fd01f4c8a4ef&chksm=cf222869f855a17fc30c744e5b7ccdeca407f3b7ddcca46bae1c93b1436ffc6fe417ccb8aef4&token=1990771297&lang=zh_CN#rd) + +## 工作总结 -![image](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943) +- [盘点数据库主从延迟的9个原因以及解决方案](https://mp.weixin.qq.com/s/aT7YjsTrM_dhDbddr8TSjg?token=528541177&lang=zh_CN) +- [实战项目,是如何保证缓存跟数据库数据一致性的?](https://mp.weixin.qq.com/s/UVHMeFDO4NYTnSwHZc9f1A?token=528541177&lang=zh_CN) +- [工作总结!日志打印的15个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [25种代码坏味道总结+优化示例](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [聊聊日常开发中,如何减少bug呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490662&idx=1&sn=d38a090611af7f64ee3c6a31331d5228&chksm=cf21c34ff8564a59e505e6edf3065a0fc506c6d2c96f492c8d8873cd46dedbe0704e43cb9c2e&token=1990771297&lang=zh_CN#rd) +- [工作四年,分享50个让你代码更好的小建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488708&idx=1&sn=6e2e0a740f5d42a59641487a0bf1e3bf&chksm=cf21cbedf85642fbb485fa1c7bf9af21923d8503f2542b6f8283ce79ddc683f7d9e45da83100&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [写代码有这16个好习惯,可以减少80%非业务的bug](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488097&idx=1&sn=eaca1f92ca3ccd9de00dbc4ef3e4029a&chksm=cf21cd48f856445e4cc24c1f8bcf18d1479bad0a37a87a2fb70717d8a4e65dcf7b4d5f83d24f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [CAS乐观锁解决并发问题的一次实践](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487937&idx=1&sn=206a37bf6d6a7aa1d05674c479ed7a72&chksm=cf21cee8f85647fe7a082049a41c0f640f54976d2cdf4302b24c5517ca42b854eb84b13ece10&token=1990771297&lang=zh_CN#rd) +- [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487961&idx=1&sn=e646231067968d9f58e6665914293f9a&chksm=cf21cef0f85647e6f3ff2feece004ac3bd979e37fe45103c88d0f299dfe632a5cf6dd547c1d9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备:Java日期处理的十个坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487973&idx=1&sn=0f713413098fb579e5f200b829f71e89&chksm=cf21ceccf85647da450765d79bf5943da551c3be950447063b9f8c77c21bf2a39b99387a949b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:加签验签](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [记一次接口性能优化实践总结:优化接口性能的八个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488004&idx=1&sn=00840efd9c0bd0a7f172b59eb2ca130f&chksm=cf21cd2df856443bf21d8e09cfe5c8452ecaf82e3c2210fca3b28829ded04defddcf63c0a59b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488117&idx=1&sn=5d3d0eda0ed45f3f576e211de31ca3a9&chksm=cf21cd5cf856444af1407a94a2abf445265ca7c5f5855cfa1c223cb209e99040c7889621f231&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [一次代码优化实践,用了模板方法+策略+工厂方法模式](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488061&idx=1&sn=1d9ab7954b03521ab81ecf033c0e5e50&chksm=cf21cd14f8564402b213f0ef908bbdb0e12fed4b281c5803b8e539cacb1551654194becfb7d6&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [保证接口数据安全的10种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500285&idx=1&sn=7d0723f25d46e858859cfd79acb6fb9d&chksm=cf221ed4f85597c2093f81baa5fdedc65817bf2d23a7951236836b0f54c2335695cbed61cd13&token=1990771297&lang=zh_CN#rd) +- [实战总结!18种接口优化方案的总结](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506674&idx=1&sn=8b2914d9aafa334029495b029b69d0b6&chksm=c1e0277ef697ae68e8c2bffe4bd7d9849be3165ef1a20286538f6a7569a6ba0879d517d55b87&token=337310304&lang=zh_CN#rd) +- [聊聊工作中常用的Lambda表达式](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506654&idx=1&sn=4835e9f486e643765d4ad3b3fc93e079&chksm=c1e02752f697ae442f62fc122d7604f4b01979f6d1665df414bb499fd8ba211335ebc503c368&token=337310304&lang=zh_CN#rd) +- [21个MySQL表设计的经验准则](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506621&idx=1&sn=afca898cb461827054d706a92f9b9250&chksm=c1e02731f697ae27a83e5637ee2184d1e26e5090caeaa58121d3cf5afab7d4d5832cac6d171a&token=337310304&lang=zh_CN#rd) +- [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506023&idx=1&sn=b96dde436c1c9fe4bda745ca5ca1b170&source=41#wechat_redirect) + +## 福利 500+页原创面试题 + +- [田螺原创500+页面试题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499943&idx=1&sn=fe869c0a97a306e42830336fe74e17a6&chksm=cf221f8ef8559698781709bfbccbb85087286e48434905fb18bec3a3ec0af7329c2a1632c230&token=1990771297&lang=zh_CN#rd) + + +## 个人公众号 -- 如果你是个爱学习的好孩子,可以关注我公众号,一起学习讨论哈~~ +微信搜公众号:**捡田螺的小男孩** -## 一分也是爱,谢谢大家的支持哈~ -![](https://user-gold-cdn.xitu.io/2020/7/15/1735311bf66cecd8?w=430&h=580&f=jpeg&s=35456) +- 小伙伴可以关注我的公众号(扫描下面二维码,还有**很多很多干货文章**),一起学习讨论哈~~ +![扫一扫](https://user-images.githubusercontent.com/20244922/179399354-8a9fd2a8-42ba-4303-9ce5-04891e899e6d.png) diff --git "a/letecode\350\247\243\351\242\230\347\256\227\346\263\225\344\273\213\347\273\215/README.MD" "b/letecode\350\247\243\351\242\230\347\256\227\346\263\225\344\273\213\347\273\215/README.MD" new file mode 100644 index 0000000..921f58b --- /dev/null +++ "b/letecode\350\247\243\351\242\230\347\256\227\346\263\225\344\273\213\347\273\215/README.MD" @@ -0,0 +1,13 @@ +## leetcode(持续更新中) + +关注公众号:捡田螺的小男孩 + +- [看一遍就理解:动态规划详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247489016&idx=1&sn=bba3fb1a7a864b6ccefeb9f633751811&chksm=cf21cad1f85643c716c8c9396d3a6711f7722f8f81c8f40f5a91c525c98f73f5c476b7d49dd4&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备的基本算法:递归详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488073&idx=1&sn=ec81b4a1f8b11ea59264b55e571fed91&chksm=cf21cd60f8564476952c5abb8ffa93fc38fde354a61ca5596e1875d35760383f3a92b2879e30&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [看一遍就理解,图解单链表反转](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487967&idx=1&sn=e75373dcb0507081c242ba018b42ca82&chksm=cf21cef6f85647e0cbf0b2072eb1264a44abcaa9f4a0621ef8954a1b1d6719560f7f4cbbce60&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [面试必备:回溯算法详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497665&idx=1&sn=39011296fa99eda839ab2bbe83a42cdf&chksm=cf2228e8f855a1fe8f059130dc0b3d9ad34431a27bbe7e16f508b7e9340c24e2e4dfd8b414c2&token=1990771297&lang=zh_CN#rd) +- [leetcode必备算法:聊聊滑动窗口](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496003&idx=1&sn=8c40eb3e611514f3bafb8d6873c03fda&chksm=cf222e6af855a77ce2fc36d4e4fc02945286300206f43975e30bc23b65c9ca67b6a1ac9806d1&token=1990771297&lang=zh_CN#rd) +- [五分钟搞定贪心算法,从此不惧大厂面试](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490681&idx=1&sn=0388da1492fe0fdfa3ed6b1a43511328&chksm=cf21c350f8564a466d89578f73886eb462c6dd485f42e7953f126be5f9af49b3fb0be3457d52&token=1990771297&lang=zh_CN#rd) +- [双指针+归并排序!图解排序链表!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247496038&idx=1&sn=96a1a665e43ee9e3337e3d941db49f1e&chksm=cf222e4ff855a75919f0be68e78472199c44d0e9d94de6d5bf621a892ba211738d6f4dbd53ac&token=1990771297&lang=zh_CN#rd) +- [双指针技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488116&idx=1&sn=aeec0553e2317bef76d158d2b0e0b5a5&chksm=cf21cd5df856444b8963efc2745bce6801df4bc547b679ae8366fa8c3cd293f1f7c60c18e4f6&token=1990771297&lang=zh_CN#rd) +- [字符串匹配算法详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494506&idx=1&sn=1f13b0cc1f03af464e1063be8ef1cb57&chksm=cf223443f855bd5597898126d12c6039f64da47b8a95714018203ee5e453950c802ebecfabe1&token=1990771297&lang=zh_CN#rd) diff --git "a/\344\270\255\351\227\264\344\273\266/README.MD" "b/\344\270\255\351\227\264\344\273\266/README.MD" new file mode 100644 index 0000000..74d80b2 --- /dev/null +++ "b/\344\270\255\351\227\264\344\273\266/README.MD" @@ -0,0 +1,8 @@ +## 中间件 + +- [一文快速入门分库分表中间件 Sharding-JDBC (必修课)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499781&idx=1&sn=74bbb25c9347408f1edf7f8c9c82d7cf&chksm=cf221f2cf855963a6549069deeabe93bb6d6e889bcd086668bf6f0e23327fa1ddb31adc6d10c&token=1990771297&lang=zh_CN#rd) +- [全方位对比Zookeeper、Eureka、Nacos、Consul和Etcd](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498268&idx=1&sn=7b24b8625fb4ff88d50c9bd55335f478&chksm=cf222535f855ac230dfca629127f93efec606641d7338c29a8d41e7d2016a7f0b6ec28a432a0&token=1990771297&lang=zh_CN#rd) +- [消息队列经典十连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497847&idx=1&sn=29a32672b712e7dfadfa36c9902b2ec7&chksm=cf22275ef855ae484fb3f51a5726e9a4bc45222e8fbbd33631d177dc4b5619c36889ea178463&token=1990771297&lang=zh_CN#rd) +- [Kafka性能篇:为何Kafka这么"快"?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488717&idx=1&sn=006c65f9a9a5796961c42f3cafc37cb4&chksm=cf21cbe4f85642f2e8ff948f8de8a69508783cee6dafd22512d6a06cd03f7065001bd1d8d87b&token=1990771297&lang=zh_CN#rd) +- [后端程序员必备:RocketMQ相关流程图/原理图](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487949&idx=1&sn=888e0917884b2918a94053e5cd560e00&chksm=cf21cee4f85647f24877791d574f5ef3f979fc9c4c84ca3fd1ea1aa08ab30c1041ad3aaa5650&token=1990771297&lang=zh_CN#rd) +- [ZooKeeper的十二连问,你顶得了嘛?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488047&idx=1&sn=4913c7e1c3b8835f7512d8dc6b845727&chksm=cf21cd06f8564410cce6121230256facb1ab3b5a9ed35579896f428d84bdea7b86836109d575&token=162724582&lang=zh_CN&scene=21#wechat_redirect) \ No newline at end of file diff --git "a/\345\210\206\345\270\203\345\274\217/README.MD" "b/\345\210\206\345\270\203\345\274\217/README.MD" new file mode 100644 index 0000000..1b5f507 --- /dev/null +++ "b/\345\210\206\345\270\203\345\274\217/README.MD" @@ -0,0 +1,12 @@ +## 分布式 + +关注公众号:捡田螺的小男孩 + +- [面试必备:聊聊分布式锁的多种实现!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498595&idx=1&sn=4e5308930e151a609baa2df820e48a89&chksm=cf22244af855ad5c71822cb33e828ce652c6f34202096a9344922b86dcbc08076d7922acde5f&token=1990771297&lang=zh_CN#rd) +- [我们为什么要分库分表?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498625&idx=1&sn=0d7bd9d1b46eeff4c715a6761355e9b0&chksm=cf2224a8f855adbea8931c8e011711f6c70cffeef8ddf8b87729c710eacef11b46eef80fda36&token=1990771297&lang=zh_CN#rd) +- [聊聊高可用的 11 个关键技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498784&idx=1&sn=aad1c00d6eafb0c1f08959612c69959a&chksm=cf222309f855aa1f71ef9cf470bfa72ac73365c401ec7c7d0c3b241a9116c3112f83760793e8&token=1990771297&lang=zh_CN#rd) +- [看一遍就理解:分布式事务详解](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498358&idx=1&sn=aa6c7ceb61b73267d68d1b4fb7ccc2ed&chksm=cf22255ff855ac495861d57df276517e89779006267fa8413fe925cc15b0c3e0b0f1b1a5675e&token=1990771297&lang=zh_CN#rd) +- [几种主流的分布式定时任务,你知道哪些?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498121&idx=1&sn=e3d7e4f5297c7b2390b412a9bafc3385&chksm=cf2226a0f855afb669cde8d7f400fb334bd4c75a8c672d1208667387d03d2dfd24884e60b825&token=1990771297&lang=zh_CN#rd) +- [redis分布式锁的8大坑,记得拿小本本记下来啦](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495390&idx=1&sn=87cc1567c709cfa67b43dd8d273bb426&chksm=cf2231f7f855b8e17919f7763469d87c47d9b4c4ad25aba7e6ff60fa33b048bc47a4afd287fc&token=1990771297&lang=zh_CN#rd) +- [框架篇:分布式一致性解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490468&idx=2&sn=91b8e5dd2ce3db218708b5c736fce700&chksm=cf21c48df8564d9b30164e1dbf9b5ebcc1847a9450d08ee146c98eb53107af475149ad12a748&token=1990771297&lang=zh_CN#rd) +- [这三年被分布式坑惨了,曝光十大坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488553&idx=2&sn=fa13e9698e59f5a5485d3d3d4b8ef2b1&chksm=cf21cb00f8564216277806780c64e13c48fe32009f588349b3365afa8de97bd8ef192507bd50&token=1990771297&lang=zh_CN#rd) \ No newline at end of file diff --git "a/\345\210\267leetcode\345\277\205\345\244\207\347\256\227\346\263\225/\351\200\222\345\275\222\350\257\246\350\247\243.md" "b/\345\210\267leetcode\345\277\205\345\244\207\347\256\227\346\263\225/\351\200\222\345\275\222\350\257\246\350\247\243.md" deleted file mode 100644 index fb5ecdb..0000000 --- "a/\345\210\267leetcode\345\277\205\345\244\207\347\256\227\346\263\225/\351\200\222\345\275\222\350\257\246\350\247\243.md" +++ /dev/null @@ -1,380 +0,0 @@ -### 前言 -递归是一种非常重要的算法思想,无论你是前端开发,还是后端开发,都需要掌握它。在日常工作中,统计文件夹大小,解析xml文件等等,都需要用到递归算法。它太基础太重要了,这也是为什么面试的时候,面试官经常让我们手写递归算法。本文呢,将跟大家一起学习递归算法~ - -- 什么是递归? -- 递归的特点 -- 递归与栈的关系 -- 递归应用场景 -- 递归解题思路 -- leetcode案例分析 -- 递归可能存在的问题以及解决方案 - - -**公众号:捡田螺的小男孩** - -### 什么是递归? - -递归,在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。简单来说,递归表现为函数调用函数本身。在知乎看到一个比喻递归的例子,个人觉得非常形象,大家看一下: - -> 递归最恰当的比喻,就是查词典。我们使用的词典,本身就是递归,为了解释一个词,需要使用更多的词。当你查一个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第二个词,可惜,第二个词里仍然有不懂的词,于是查第三个词,这样查下去,直到有一个词的解释是你完全能看懂的,那么递归走到了尽头,然后你开始后退,逐个明白之前查过的每一个词,最终,你明白了最开始那个词的意思。 - -来试试水,看一个递归的代码例子吧,如下: -``` -public int sum(int n) { - if (n <= 1) { - return 1; - } - return sum(n - 1) + n; -} -``` - -### 递归的特点 - -实际上,递归有两个显著的特征,终止条件和自身调用: -- 自身调用:原问题可以分解为子问题,子问题和原问题的求解方法是一致的,即都是调用自身的同一个函数。 -- 终止条件:递归必须有一个终止的条件,即不能无限循环地调用本身。 - -结合以上demo代码例子,看下递归的特点: - -![](https://imgkr2.cn-bj.ufileos.com/94f250ad-41ea-4760-ae79-060f91605aeb.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=gQGMVnPTDwM%252F5AX2OrH%252F0ugG9MA%253D&Expires=1602691176) - - -### 递归与栈的关系 -其实,递归的过程,可以理解为出入栈的过程的,这个比喻呢,只是为了方便读者朋友更好理解递归哈。以上代码例子计算sum(n=3)的出入栈图如下: -![](https://imgkr2.cn-bj.ufileos.com/d4f09883-727b-44b6-90e0-dfad9d768b8c.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=V%252BDCQxqTa24HsCxQcooMJ%252FzuOHc%253D&Expires=1602692111) - - -为了更容易理解一些,我们来看一下 函数sum(n=5)的递归执行过程,如下: -![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f587c37fea484be192fec7710634ec47~tplv-k3u1fbpfcp-zoom-1.image) - -- 计算sum(5)时,先sum(5)入栈,然后原问题sum(5)拆分为子问题sum(4),再入栈,直到终止条件sum(n=1)=1,就开始出栈。 -- sum(1)出栈后,sum(2)开始出栈,接着sum(3)。 -- 最后呢,sum(1)就是后进先出,sum(5)是先进后出,因此递归过程可以理解为栈出入过程啦~ - - -### 递归的经典应用场景 -哪些问题我们可以考虑使用递归来解决呢?即递归的应用场景一般有哪些呢? -- 阶乘问题 -- 二叉树深度 -- 汉诺塔问题 -- 斐波那契数列 -- 快速排序、归并排序(分治算法也使用递归实现) -- 遍历文件,解析xml文件 - -### 递归解题思路 -解决递归问题一般就三步曲,分别是: -- 第一步,定义函数功能 -- 第二步,寻找递归终止条件 -- 第二步,递推函数的等价关系式 - -这个递归解题三板斧理解起来有点抽象,我们拿阶乘递归例子来喵喵吧~ - -#### 1.定义函数功能 -定义函数功能,就是说,你这个函数是干嘛的,做什么事情,换句话说,你要知道递归原问题是什么呀?比如你需要解决阶乘问题,定义的函数功能就是n的阶乘,如下: -``` -//n的阶乘(n为大于0的自然数) -int factorial (int n){ - -} -``` - -#### 2.寻找递归终止条件 -递归的一个典型特征就是必须有一个终止的条件,即不能无限循环地调用本身。所以,用递归思路去解决问题的时候,就需要寻找递归终止条件是什么。比如阶乘问题,当n=1的时候,不用再往下递归了,可以跳出循环啦,n=1就可以作为递归的终止条件,如下: -``` -//n的阶乘(n为大于0的自然数) -int factorial (int n){ - if(n==1){ - return 1; - } -} -``` - -#### 3.递推函数的等价关系式 -递归的**本义**,就是原问题可以拆为同类且更容易解决的子问题,即**原问题和子问题都可以用同一个函数关系表示。递推函数的等价关系式,这个步骤就等价于寻找原问题与子问题的关系,如何用一个公式把这个函数表达清楚**。阶乘的公式就可以表示为 f(n) = n * f(n-1), 因此,阶乘的递归程序代码就可以写成这样,如下: -``` -int factorial (int n){ - if(n==1){ - return 1; - } - return n * factorial(n-1); -} -``` -**注意啦**,不是所有递推函数的等价关系都像阶乘这么简单,一下子就能推导出来。需要我们多接触,多积累,多思考,多练习递归题目滴~ - -### leetcode案例分析 - -来分析一道leetcode递归的经典题目吧~ -> 原题链接在这里哈:https://leetcode-cn.com/problems/invert-binary-tree/ - -**题目:** 翻转一棵二叉树。 - -输入: -``` - 4 - / \ - 2 7 - / \ / \ -1 3 6 9 -``` -输出: -``` - 4 - / \ - 7 2 - / \ / \ -9 6 3 1 -``` - -我们按照以上递归解题的三板斧来: - -**1. 定义函数功能** - -函数功能(即这个递归原问题是),给出一颗树,然后翻转它,所以,函数可以定义为: -``` -//翻转一颗二叉树 -public TreeNode invertTree(TreeNode root) { -} - -/** - * Definition for a binary tree node. - * public class TreeNode { - * int val; - * TreeNode left; - * TreeNode right; - * TreeNode(int x) { val = x; } - * } - */ -``` - -**2.寻找递归终止条件** - -这棵树什么时候不用翻转呢?当然是当前节点为null或者当前节点为叶子节点的时候啦。因此,加上终止条件就是: -``` -//翻转一颗二叉树 -public TreeNode invertTree(TreeNode root) { - if(root==null || (root.left ==null && root.right ==null)){ - return root; - } -} -``` - -**3. 递推函数的等价关系式** - -原问题之你要翻转一颗树,是不是可以拆分为子问题,分别翻转它的左子树和右子树?子问题之翻转它的左子树,是不是又可以拆分为,翻转它左子树的左子树以及它左子树的右子树?然后一直翻转到叶子节点为止。嗯,看图理解一下咯~ -![](https://imgkr2.cn-bj.ufileos.com/938b0fcf-7ab2-4c8f-833f-382be7f8b46d.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=9BiTdqNsNnz%252FqynFWb52CMQkMnU%253D&Expires=1602692880) - - -首先,你要翻转根节点为4的树,就需要**翻转它的左子树(根节点为2)和右子树(根节点为7)**。这就是递归的**递**的过程啦 -![](https://imgkr2.cn-bj.ufileos.com/381538fe-fe34-4cae-9d1e-b5894ea542b2.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=HK51fa4jYvucnu6r3829BqFWcys%253D&Expires=1602693025) - - -然后呢,根节点为2的树,不是叶子节点,你需要继续**翻转它的左子树(根节点为1)和右子树(根节点为3)**。因为节点1和3都是**叶子节点**了,所以就返回啦。这也是递归的**递**的过程~ - -![](https://imgkr2.cn-bj.ufileos.com/c9195723-d803-4b76-9cb3-bec8192a696f.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=n5Ct31iSBnMmK2HJ7kB0I6ZhEYs%253D&Expires=1602693145) - -同理,根节点为7的树,也不是叶子节点,你需要翻转**它的左子树(根节点为6)和右子树(根节点为9)**。因为节点6和9都是叶子节点了,所以也返回啦。 - -![](https://imgkr2.cn-bj.ufileos.com/63333c60-747b-45d3-a8eb-dacf0cf36231.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=8NTb2Fj1PBlwVIxwe1InO5YqoWg%253D&Expires=1602693197) - - - -左子树(根节点为2)和右子树(根节点为7)都被翻转完后,这几个步骤就**归来**,即递归的归过程,翻转树的任务就完成了~ - -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/afada1b801734cdc899e896b0816b63f~tplv-k3u1fbpfcp-watermark.webp) - -显然,**递推关系式**就是: -``` -invertTree(root)= invertTree(root.left) + invertTree(root.right); -``` - -于是,很容易可以得出以下代码: -``` -//翻转一颗二叉树 -public TreeNode invertTree(TreeNode root) { - if(root==null || (root.left ==null && root.right ==null){ - return root; - } - //翻转左子树 - TreeNode left = invertTree(root.left); - //翻转右子树 - TreeNode right= invertTree(root.right); -} -``` -这里代码有个地方需要注意,就是翻转完一棵树的左右子树后,需要交换它左右子树的引用位置。 -``` - root.left = right; - root.right = left; -``` - -因此,leetcode这个递归经典题目的**终极解决代码**如下: -``` -class Solution { - public TreeNode invertTree(TreeNode root) { - if(root==null || (root.left ==null && root.right ==null)){ - return root; - } - //翻转左子树 - TreeNode left = invertTree(root.left); - //翻转右子树 - TreeNode right= invertTree(root.right); - //左右子树交换位置~ - root.left = right; - root.right = left; - return root; - } -} -``` -拿终极解决代码去leetcode提交一下,通过啦~ - - -![](https://imgkr2.cn-bj.ufileos.com/3c9bc7ba-1677-4b8b-a389-e7177fd2b747.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=oNJFxSVWmku%252FMg%252BKlGHop%252BQ%252BxIw%253D&Expires=1602693611) - - -### 递归存在的问题 -- 递归调用层级太多,导致栈溢出问题 -- 递归重复计算,导致效率低下 - -#### 栈溢出问题 -- 每一次函数调用在内存栈中分配空间,而每个进程的栈容量是有限的。 -- 当递归调用的层级太多时,就会超出栈的容量,从而导致调用栈溢出。 -- 其实,我们在前面小节也讨论了,递归过程类似于出栈入栈,如果递归次数过多,栈的深度就需要越深,最后栈容量真的不够咯 - -**代码例子如下:** -``` -/** - * 递归栈溢出测试 - */ -public class RecursionTest { - - public static void main(String[] args) { - sum(50000); - } - private static int sum(int n) { - if (n <= 1) { - return 1; - } - return sum(n - 1) + n; - } -} -``` -**运行结果:** -``` -Exception in thread "main" java.lang.StackOverflowError - at recursion.RecursionTest.sum(RecursionTest.java:13) -``` - -怎么解决这个栈溢出问题?首先需要**优化一下你的递归**,真的需要递归调用这么多次嘛?如果真的需要,先稍微**调大JVM的栈空间内存**,如果还是不行,那就需要弃用递归,**优化为其他方案**咯~ - -#### 重复计算,导致程序效率低下 -我们再来看一道经典的青蛙跳阶问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - -绝大多数读者朋友,很容易就想到以下递归代码去解决: -``` -class Solution { - public int numWays(int n) { - if (n == 0){ - return 1; - } - if(n <= 2){ - return n; - } - return numWays(n-1) + numWays(n-2); - } -} -``` - -但是呢,去leetcode提交一下,就有问题啦,超出时间限制了 - - -![](https://imgkr2.cn-bj.ufileos.com/47049001-7fae-4c98-8f4c-2e55aca367f3.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=xELiHeOso7vAsXwthfHEIDarVGs%253D&Expires=1602693967) - - -为什么超时了呢?递归耗时在哪里呢?先画出**递归树**看看: - -![](https://imgkr2.cn-bj.ufileos.com/9539296a-f5b1-433e-94ba-2e23eddfc409.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=Rzlo2ChPE7UEkCSJhbMNTBbtfaA%253D&Expires=1602694031) - - -- 要计算原问题 f(10),就需要先计算出子问题 f(9) 和 f(8) -- 然后要计算 f(9),又要先算出子问题 f(8) 和 f(7),以此类推。 -- 一直到 f(2) 和 f(1),递归树才终止。 - -**递归时间复杂度 = 解决一个问题时间*问题个数** -- 一个问题时间 = f(n-1)+f(n-2),也就是一个加法的操作,所以复杂度是 **O(1)**; -- 问题个数 = 递归树节点的总数,递归树的总结点 = 2^n-1,所以是复杂度**O(2^n)**。 - -因此,青蛙跳阶,递归解法的时间复杂度 = O(1) * O(2^n) = O(2^n),就是指数级别的,**如果n比较大的话,超时很正常的了**。 - -你仔细观察这颗递归树,你会发现存在**大量重复计算**,比如f(8)被计算了两次,f(7)被重复计算了3次...所以这个递归算法低效的原因,就是存在大量的重复计算! - -**那么,怎么解决这个问题呢?** - -既然存在大量重复计算,那么我们可以先把计算好的答案存下来,即造一个备忘录,等到下次需要的话,先去**备忘录**查一下,如果有,就直接取就好了,备忘录没有才再去计算,那就可以省去重新重复计算的耗时啦!这就是**带备忘录的解法** - -我们来看一下**带备忘录的递归解法**吧~ - -一般使用一个数组或者一个哈希map充当这个**备忘录**。 - -假设f(10)求解加上**备忘录**,我们再来画一下递归树: - -**第一步**,f(10)= f(9) + f(8),f(9) 和f(8)都需要计算出来,然后再加到备忘录中,如下: - -![](https://imgkr2.cn-bj.ufileos.com/22fe0dc0-136e-4e8d-9b54-7f1ff2a9d066.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=gJl54y4g86XMhK2K1ZbaqmVl94Y%253D&Expires=1602694255) - - -**第二步,** f(9) = f(8)+ f(7),f(8)= f(7)+ f(6), 因为 f(8) 已经在备忘录中啦,所以可以省掉,f(7),f(6)都需要计算出来,加到备忘录中~ - -![](https://imgkr2.cn-bj.ufileos.com/f9b26b22-c745-4bad-b14d-d8b2b51075f4.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=RkOd5Zj5Wwqonn63eXfoWqStvx4%253D&Expires=1602694275) - - -**第三步,** f(8) = f(7)+ f(6),发现f(8),f(7),f(6)全部都在备忘录上了,所以都可以剪掉。 -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec535ab8a0d3401eae5cb041dabac221~tplv-k3u1fbpfcp-watermark.image) - -所以呢,用了备忘录递归算法,递归树变成光秃秃的咯,如下: -![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b78faafe49b4ee0ab939194459cf386~tplv-k3u1fbpfcp-watermark.image) - -带「备忘录」的递归算法,子问题个数就为n了,解决一个子问题还是O(1),所以**带「备忘录」的递归算法的时间复杂度是O(n)**。接下来呢,我们用带「备忘录」的递归算法去撸代码,解决这个青蛙跳阶问题咯~,代码如下: - -``` -public class Solution { - //哈希map充当备忘录的作用 - Map tempMap = new HashMap(); - public int numWays(int n) { - // n = 0 也算1种 - if (n == 0) { - return 1; - } - if (n <= 2) { - return n; - } - //先判断有没计算过,即看看备忘录有没有 - if (tempMap.containsKey(n)) { - //备忘录有,即计算过,直接返回 - return tempMap.get(n); - } else { - // 备忘录没有,即没有计算过,执行递归计算,并且把结果保存到备忘录map中,对1000000007取余(这个是leetcode题目规定的) - tempMap.put(n, (numWays(n - 1) + numWays(n - 2)) % 1000000007); - return tempMap.get(n); - } - } -} -``` - -去leetcode提交一下,如图,稳了: - -![](https://imgkr2.cn-bj.ufileos.com/8208e494-07e6-467b-96a2-8acf77121737.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=zNVyzVvQaWVQaQmABckQvIVh1XE%253D&Expires=1602694530) - -啥叫「自顶向下」?注意我们刚才画的递归树(或者说图),是从上向下延伸,都是从一个规模较大的原问题比如说 f(20),向下逐渐分解规模,直到 f(1) 和 f(2) 触底,然后逐层返回答案,这就叫「自顶向下」。 - - -啥叫「自底向上」?反过来,我们直接从最底下,最简单,问题规模最小的 f(1) 和 f(2) 开始往上推,直到推到我们想要的答案 f(20),这就是动态规划的思路,这也是为什么动态规划一般都脱离了递归,而是由循环迭代完成计算。 - -动态规划问题,下期分解 - -### 参考与感谢 -- [一文学会递归解题](https://mp.weixin.qq.com/s/Hew44D8rdXb3pf8mZGk67w) -- [动态规划详解](https://mp.weixin.qq.com/s/1V3aHVonWBEXlNUvK3S28w) - -### 个人公众号 -![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e6b23c704fc94ca09207b779f953cce6~tplv-k3u1fbpfcp-zoom-1.image) -- 更多干货,关注公众号 - diff --git "a/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/README.md" "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/README.md" new file mode 100644 index 0000000..7f6092a --- /dev/null +++ "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/README.md" @@ -0,0 +1,10 @@ +## 后端思维篇(持续更新中) + +公众号:捡田螺的小男孩 + +- [后端思想篇:设计好接口的36个锦囊!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499388&idx=1&sn=49a22120a3238e13ad7c3d3b73d9e453&chksm=cf222155f855a8434026b2c460d963c406186578c2527ca8f2bb829bbe849d87a2392a525a9b&scene=178&cur_album_id=2396778860463161350#rd) +- [后端思维篇:手把手教你写一个并行调用模板](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499504&idx=1&sn=bb62226e6cffeb1859efb0100c796050&chksm=cf2221d9f855a8cf23f75cb51c1a407578fb0f279e96ddae74b5b8c84f2f5dc71762425b17cb&scene=178&cur_album_id=2396778860463161350#rd) +- [后端思维篇:如何应用设计模式优化代码](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499524&idx=1&sn=cb4cc48a3e8d9a54b0ebc4c7ad517f14&chksm=cf22202df855a93b37327856ee88b0bf5f6ed7da67964438fc2cf747666260d5026dd62d4a17&scene=178&cur_album_id=2396778860463161350#rd) +- [后端思维篇:统一参数校验、异常处理、结果返回](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499708&idx=1&sn=808979c495acd9344732d147c0ad40d3&chksm=cf222095f855a983f31d5f6abf401fa3b5967f8839c6775d35cefc5cc6244fb4135563ff1090&scene=178&cur_album_id=2396778860463161350#rd) +- [后端思维篇:如何抽一个观察者模板](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500159&idx=1&sn=a5328372e580b22c939a5b3084aef164&chksm=cf221e56f85597401e8c99b8dd1bc1af97fcf69207ceaa04c5c26e028ac47d1658b79ae32291&scene=178&cur_album_id=2396778860463161350#rd) +- [后端思维篇:后端思维专栏:通过层层代码去重,我又搞了一个通用模板](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506942&idx=1&sn=ae14ed5cc179f73ea0b2f37c73ad8da4&chksm=c1e02672f697af645943ea8ee53b7cef6257ebbc21d2b77058994e98bdb1e107ad313e29e8c3&token=134957671&lang=zh_CN#rd) diff --git "a/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md" "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md" new file mode 100644 index 0000000..f999d6b --- /dev/null +++ "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\344\270\200\357\274\232\350\256\276\350\256\241\346\216\245\345\217\243\347\232\20436\344\270\252\351\224\246\345\233\212.md" @@ -0,0 +1,540 @@ +## ǰ + +ҺãǼݵСкΪ˿ʲôԣ```Java``````Go``````C++```䱳ĺ˼붼Ƶġһ˼ļרҪ˵һЩơߺ˹淶صģϣԴճа + +˿ʦҪǣ**ΰһӿƺ**ԣ͸ҽܣƺýӿڵ36ҡľǺ˼רĵһƪ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1bfc8123cee34de2ad82b736121165d2~tplv-k3u1fbpfcp-zoom-1.image) + +- ںţݵСк + + +## 1. ӿڲУ + +γУÿԱرĻƵĽӿڣУǷΪգγǷԤڳȡҪϰ߹ճУܶͼbugDzУµġ + +> ݿֶΪ```varchar(16)```,Էһ32λַ㲻У**ݿֱ쳣** + +Ҳǣ㶨ĽӿڱģDzΪյģĽӿڷزûУ飬ΪijЩԭֱرһ```null```ֵ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bfd3392f3ce6408daa1940cc185f0d5f~tplv-k3u1fbpfcp-zoom-1.image) + +## 2. ޸Ͻӿʱעӿڵļ + +ܶbugΪ޸˶ɽӿڣȴ****µġؼDZȽصģֱӵϵͳʧܵġֳԱ׷Ŷ~ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/701ac23b5dd04149b277c4001721fb87~tplv-k3u1fbpfcp-zoom-1.image) + +ԣԭӿ޸ģӿǶṩĻһҪǽӿڼݡٸӰɣdubboӿڣԭֻABһCͿԿ + +``` +//Ͻӿ +void oldService(A,B){ + //½ӿڣnullC + newService(A,B,null); +} + +//½ӿڣʱɾϽӿڣҪݡ +void newService(A,B,C){ + ... +} +``` + +## 3. ƽӿʱֿǽӿڵĿչ + +Ҫʵҵ񳡾ƽӿڣֿǽӿڵĿչԡ + +ӵһûӻ޸ԱʱҪˢǷṩһԱύˢϢӿڣ˼ύˢDzͨأת˻һҪˢĻǷҪʵһӿأǵǰҵͻģ飬ӿھͺãӿڵĿչԡ + +ģ黮ֵĻδһֽˢĻٸһµĽӿڣֻҪö٣Ȼˢͨ̽ӿڣʵһˢIJ컯ɡ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cd3ee52ecaa34de384bb529cbb358889~tplv-k3u1fbpfcp-zoom-1.image) + + +## 4.ӿڿǷҪش + +ǰظ߼δDzǿǽӿȥش + +ȻDzѯʵ÷ءǸ޸ĻתģҪظˡ򵥵㣬ʹRedisظͬ󷽣һʱڵͬ󣬿ǷˡȻתӿڣߵĻ**Ƽʹݿر****ΨһˮΪΨһ** + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/059928f565ba4d27a17c54f451b0235d~tplv-k3u1fbpfcp-zoom-1.image) + + +## 5. صӿڣ̳߳ظ롣 + +һЩ½ת˽סµҪӿڣ̳߳ظҵ񶼹һ̳߳أЩҵbug̳߳ĻǾͱˣ**ҵӰ**˽̳߳ظ룬Ҫҵһḷ́߳͸ñҪҵ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d30804afc044026b4eb7bad23689c42~tplv-k3u1fbpfcp-zoom-1.image) + + +## 6. õӿҪ쳣ͳʱ + +õӿڣ߷ֲʽԶ̷ĵĻҪǣ + +- 쳣 + +> 磬˵Ľӿڣ쳣ˣôԻǵʧܻǸ澯 + +- ӿڳʱ + +> ûԤԷӿһ÷أһøʱϿʱ䣬ԱĽӿڡ**֮ǰһ**httpòóʱʱ䣬Ӧ̼һֱռ̲߳ͷţϿ̳߳ء + +- Դ +> Ľӿڵʧܣ費ҪԣԼΣҪվҵϽǶ˼ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25ec61c10c324ada9252745fa4017ad6~tplv-k3u1fbpfcp-zoom-1.image) + + +## 7. ӿʵֿ۶Ϻͽ + +ǰϵͳһ㶼Ƿֲʽġֲʽϵͳоij񲻿ãյϵͳõ, 󱻳Ϊ**ѩЧӦ** + +ֲʽ·```A->B->C....```ͼʾ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/65240791c94c44b6aab143178eeb790c~tplv-k3u1fbpfcp-zoom-1.image) + +> C⣬**ΪSQLµû**ǽBҲӳ٣ӶAҲӳ١סAռϵͳ̡߳IOԴ AķԽԽ࣬ռüԴҲԽԽ࣬ջᵼϵͳƿ֣ͬãҵϵͳ + +ΪӦԷѩ, **۶Ϻͽ**ǼӿؿƣϵͳʱؽٵϵͳѡÿԴ```Hystrix``` + +## 8. ־ӡãӿڵĹؼ룬Ҫ־ݻ + +ؼҵεأӦ㹻־ݻ +磺ʵתҵתȻתʧˣſͻͶߣȻ㻹ûдӡ־ˮȵ£ȴް취 + +ôתҵҪЩ־Ϣأ٣ǰҪӡҪɣӿڵúҪһ쳣ɣͬʱӡ쳣־ɣ£ +``` +public void transfer(TransferDTO transferDTO){ + log.info("invoke tranfer begin"); + //ӡ + log.info("invoke tranfer,paramters:{}",transferDTO); + try { + res= transferService.transfer(transferDTO); + }catch(Exception e){ + log.error("transfer fail,account{}", + transferDTO.getAccount) + log.error("transfer fail,exception:{}",e); + } + log.info("invoke tranfer end"); + } +``` + +֮ǰдһƪӡ־15飬ҿԿ[ܽᣡ־ӡ15](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) + +## 9. ӿڵĹܶҪ߱һ + +һָӿȽϵһרһһ½ӿڣֻУ˻룬Ȼ󷵻ص½ɹԼ```userId```ɡ**Ϊ˼ٽӿڽһЩעᡢһЩòѯȫŵ½ӿڣͲ̫ס** + +ʵҲ΢һЩ˼룬ӿڵĹܵһȷ綩񡢻֡ƷϢصĽӿڶǻֿġ΢ĻDzǾͱȽϼ + + +## 10.ӿЩʹ첽 + +ٸ򵥵ӣʵһûעĽӿڡûעɹʱʼ߶ȥ֪ͨûʼ߷ţ͸ʺ첽Ϊܲһ֪ͨʧܣעʧܰɡ + +첽ķʽ򵥵ľ**̳߳**ʹϢУûעɹ߲һעɹϢעɹϢͷ֪ͨ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/585b098a67b349d495e6e8579ea85e4c~tplv-k3u1fbpfcp-zoom-1.image) + + +еĽӿڶʺΪͬӿڡҪһת˵Ĺܣǵʵתˣǿ԰ѽӿͬûתʱͻھȴת˽ͺáתˣһһǧʣһʵģ԰ѽӿΪ첽ûתʱ־ûɹȷɹȻûʮӻʮӵת˽ͺáֻߣת˳ɹٻصϵͳ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ae74868492344c4bcbab9b480904c47~tplv-k3u1fbpfcp-zoom-1.image) + + +## 11. ŻӿںʱԶ̴пǸIJе + +һAPPҳĽӿڣҪûϢҪbannerϢҪ鵯ϢȵȡһһӿڴеDzеأ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d118e2b09e1f4fc6a1003fd44a43e4c7~tplv-k3u1fbpfcp-zoom-1.image) + +Ǵһһ飬ûϢ200msbannerϢ100ms鵯Ϣ50msһͺʱ```350ms```ˣϢǺʱ͸ˡֳǿԸΪеõġҲ˵ûϢbannerϢ鵯Ϣͬʱ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/83561366219b48a2a85a6bb0419f82a3~tplv-k3u1fbpfcp-zoom-1.image) + +Javaи첽```CompletableFuture```ͿԺܺʵܡȤСԿ֮ǰ¹[CompletableFuture](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490456&idx=1&sn=95836324db57673a4d7aea4fb233c0d2&chksm=cf21c4b1f8564da72dc7b39279362bcf965b1374540f3b339413d138599f7de59a5f977e3b0e&token=1260947715&lang=zh_CN#rd) + +## 12. ӿںϲ˵˼ + +ݿԶ̵ʱͲҪforѭá +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/855cd5cf57d047be909dbc41ddacc021~tplv-k3u1fbpfcp-zoom-1.image) + +һӣƽʱһбϸݲݿʱҪforѭһһ룬һμ롣ͬԶ̵Ҳ뷨ѯӪǩǷУһǩһǩȥ飬Ҳǩȥ飬УЧʾ͸ + +``` +// +for(int i=0;i һЩƽʱ䶯С˵ƷϢԷŵ棬ʱȲѯ棬ûٲݿ⣬Ұݿݸµ档ǣʹûҪЩ㣺ݿһα֤Ⱥѩ洩͸⡣ + +- ֤ݿͻһԣ**ʱ˫ɾɾԻơȡbiglog첽ɾ** +- +- ѩRedisȺ߿áùʱ +- 洩͸ӿڲУ顢ѯΪøĬϿֵǡ¡ + +һ```Redis```ֲʽ棬ȻЩʱҲԿʹñػ棬```Guava CacheCaffeine```ȡʹñػЩȱ㣬޷дݴ洢Ӧý̵ʧЧ + +## 14. ӿڿȵݸ + +˲ʱĸ߲ܻϵͳһЩȵݵĸ롣**ҵ롢ϵͳ롢û롢ݸ**ȡ + +- ҵԣ12306ķʱƱȵݷɢϵͳѹ +- ϵͳ룺ϵͳֳûƷ顣ֱʹòͬݿ⣬ӽ㵽Ӧòٵݲȫ롣 +- û룺صûøõĻ +- ݸ룺ʹõĻ漯Ⱥݿȵݡ + +## 15. ɱûƤл + +Ʒ˸ʥڵʱ򣬺ƤΪʥصģڵʱΪںƤȡ + +ڴдƣ´룺 +``` +if(duringChristmas){ + img = redPacketChristmasSkin; +}else if(duringSpringFestival){ + img = redSpringFestivalSkin; +} +``` +ԪڵʱӪСͻȻ뷨ƤɵصģʱDzҪȥ޸Ĵˣ·ˣ + +һʼӿʱʵ**һźƤñ**ƤûأƤֻ޸һ±ݾͺˡ + +ȻһЩʺһЩûIJһҳơijʱЩԸ㵽û档**Ҳչ˼һ֡** + +## 16.ӿڿݵ + +ӿҪݵԵģתЩҪӿڡֱ۵ҵ񳡾**ûŵ**Ľӿû**holdס**Ϣгظѵҵ߼ôƣ + +£**ʲôݵȣ** + +> ѧУݵȱʾһκͶijһԴӦþͬĸã˵ӰһִеӰЧͬ + +ұ**غݵʵ**ҪΪ˱ظݣظɡݵƳѾ󣬻Ҫÿͬ󶼷һЧأܶʱǵĴ̡ƵĹ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f8c3d0d5a653455198ba3259ef221387~tplv-k3u1fbpfcp-zoom-1.image) + + +ӿݵʵַҪ8֣ + +- select+insert+/Ψһͻ +- ֱinsert + /Ψһͻ +- ״̬ݵ +- ȡر +- token +- +- ֹ +- ֲʽ + +ҿԿƪ¹[ݵ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=1260947715&lang=zh_CN#rd) + +## 17. д룬ȿǶӿ⣬עӳ + +ǵݿⶼǼȺģҲдӿ⣬ǰһ㶼Ƕдġдݣ϶д⣬ǶڶȡʵʱҪ󲻸ߵݣȿǶӿ⣬ΪԷֵѹ + +ȡӿĻҪӳٵ⡣ + +## 18.ӿעⷵصҪҳ + +һӿڷرģӦðӣѹҲdzʵDZȽϴ󣬿ԷҳأǹܲصıģӦÿǽӿڲ֡ + +## 19. õĽӿʵ֣벻SQLŻ + +˵ģдһӿڣ벻SQLŻ + +SQLŻ⼸ά˼ + +- explain SQLѯƻصעtypeextrafilteredֶΣ +- show profile˽SQLִе̵߳״̬Լĵʱ +- Ż ǰ׺ԭʽתorder byԼgroup byŻjoinŻ +- ҳŻӳٹ¼һҳID +- ̫**ֱֿ**ͬesesѯ + +## 20.ȿƺ + +ʲôǼأ + +> ʵǾҪסķΧǶ󡣱ڼ䣬ֻҪסͿ˰ɣҪҶü˽Űɣļȡ + +дʱ漰ԴûбҪסġͺ䣬ðҶססžͿˡ + +磬ҵУһArrayListΪ漰̲߳ҪպһαȽϺʱIJе```slowNotShare```漰̰߳ȫ⣬μأ + + +``` +//漰Դ +private void slowNotShare() { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + } +} + +//ļ +public int wrong() { + long beginTime = System.currentTimeMillis(); + IntStream.rangeClosed(1, 10000).parallel().forEach(i -> { + //̫ˣslowNotShareʵ漰Դ + synchronized (this) { + slowNotShare(); + data.add(i); + } + }); + log.info("cosume time:{}", System.currentTimeMillis() - beginTime); + return data.size(); +} +``` + + +``` +public int right() { + long beginTime = System.currentTimeMillis(); + IntStream.rangeClosed(1, 10000).parallel().forEach(i -> { + slowNotShare();//Բ + //ֻListⲿּ + synchronized (data) { + data.add(i); + } + }); + log.info("cosume time:{}", System.currentTimeMillis() - beginTime); + return data.size(); +} +``` + +## 21.ӿ״̬ʹҪͳһȷ + +ṩҪĽӿڵ״̬Ϣһת˽ӿڵdzɹʧܡлɹȣҪȷ߿ͻˡӿʧܣôʧܵԭʲôЩҪϢҪ߸ͻˣҪȷĴͶӦͬʱԱϢװһ£ҪѺ˵쳣Ϣȫ׳ͻˡ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20a1080126274c04aa31802178c01bb0~tplv-k3u1fbpfcp-zoom-1.image) + + +## 22.ӿҪ쳣 + +ʵһõĽӿڣ벻ŵ쳣쳣ʮСɣ + +- Ҫʹ```e.printStackTrace()```,ʹ```log```ӡΪ```e.printStackTrace()```ܻᵼڴռ +- ```catch```ס쳣ʱӡ```exception```ڸöλ +- Ҫһ```Exception```׽пܵ쳣 +- ǵʹ```finally```رԴֱʹ```try-with-resource``` +- 쳣׳쳣ȫƥ䣬߲쳣쳣ĸ +- 񵽵쳣ܺٴ־ +- ע쳣ĴνṹȾ +- Զװ쳣Ҫԭʼ쳣Ϣ```Throwable cause``` +- ʱ쳣```RuntimeException``` Ӧͨ```catch```ķʽԤ飬磺```NullPointerException``` +- ע쳣ƥ˳Ȳ쳣 + +СȤԿ֮ǰдƪ¹[Java 쳣ʮ](https://mp.weixin.qq.com/s/3mqY77c8iXWvJFzkVQi9Og) + +## 23. Ż߼ + +Ż߼黹ͦҪģҲ˵ʵֵҵ룬**DZȽϸӵĻעд**У߼뾡Ч + +> 磬ҪʹûϢԣsessionѾȡ```userId```ˣȻͰûϢݿѯʹ󣬺ҪõûϢԣЩСû̫࣬־Ͱ```userId```ٴȥٲһݿ⡣ĿУִ롣ֱӰû + +α룺 + +``` +public Response test(Session session){ + UserInfo user = UserDao.queryByUserId(session.getUserId()); + + if(user==null){ + reutrn new Response(); + } + + return do(session.getUserId()); +} + +public Response do(String UserId){ + //һݿ + UserInfo user = UserDao.queryByUserId(session.getUserId()); + ...... + return new Response(); +} + +``` + + + +``` +public Response test(Session session){ + UserInfo user = UserDao.queryByUserId(session.getUserId()); + + if(user==null){ + reutrn new Response(); + } + + return do(session.getUserId()); +} + +//ֱӴUserInfoɣٶһݿ +public Response do(UserInfo user){ + ...... + return new Response(); +} +``` + +ȻֻһЩСһӣкܶƵӣҪҿУ˼Ĺ + + +## 24. ӿʵֹ̻Уעļ񡢴 + +- ȡļʱҪ```Files.readAllBytes```ֱӶȡڴ棬OOMģʹ```BufferedReader```һһ +- ܵعʱ䳤ӳٵ⣬о +- עһЩʹãΪֱӽģᴥfullGC + +## 25. ĽӿڣҪ + +ϵͳÿ뿸ס1000һʮأǶȾ˵߲ʱˣϵͳijôأ + +ȡʩеϵͳCPUڴ桢Load쮵ĺܸߣе޷Ӧ + +ֳǿԲΪ˱ϵͳֱӶ + +壺 +> ڼУǿӿڷͻʣɷֹDoSWeb档Ҳơָϵͳٸ߲ߴ£µϵͳķʣӶ֤ϵͳȶԡ + +ʹGuava```RateLimiter```Ҳʹ```Redis```ֲʽʹð↑Դ```sentinel``` + +ҿԿ֮ǰƪ¹[4־㷨](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490393&idx=1&sn=98189caa486406f8fa94d84ba0667604&chksm=cf21c470f8564d665ce04ccb9dc7502633246da87a0541b07ba4ac99423b28ce544cdd6c036b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) + + +## 26.ʵʱעʱ쳣ָ롢±Խȣ + +ճУҪȡʩ**ָ߽**ʱƴȽϳ +``` +String name = list.get(1).getName(); //listԽ磬Ϊһ2Ԫع +``` + +ӦòȡʩԤһ߽£ +``` +if(CollectionsUtil.isNotEmpty(list)&& list.size()>1){ + String name = list.get(1).getName(); +} +``` +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10199365140845ea8f7b29a07fbaf3cc~tplv-k3u1fbpfcp-zoom-1.image) + + +## 27.֤ӿڰȫ + +APIӿǶṩģҪ֤ӿڵİȫԡ֤ӿڵİȫ**tokenƺͽӿǩ** + +**token֤**Ƚϼ򵥵ģ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9b468f89cdaf4040b84e432182903fd9~tplv-k3u1fbpfcp-zoom-1.image) + +1. ͻ˷ȡtoken +2. ȫΨһtoken浽redisУһһʱ䣩Ȼ󷵻ظͻˡ +3. ͻ˴token +4. ȥredisȷtokenǷڣһ redis.del(token)ķʽڻɾɹҵ߼ɾʧܲҵ߼ֱӷؽ + +**ӿǩ**ķʽǰѽӿϢģʱ汾šappidȣͻ˽ԿǩȻùԿǩ֤ͨΪǺϷġûб۸Ĺ + +йڼǩǩģҿԿƪ¹[Աرǩǩ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) + +**ǩǩtokenƣӿڱһҪܵ**ȻhttpsЭǻԱļܵġǷĻμӽأ +> ԲοHTTPSԭǷ˰ѹԿͻˣȻͻɶԳԿſͻ÷˵ĹԿܶԳԿٷˣԼ˽Կܣõͻ˵ĶԳԿʱͿ촫䱨ͻ**ԳԿ****öӦĶԳԿܱ** + +ʱ򣬽ӿڵİȫԣ**ֻš֤Ϣ**˵**û˽ݣ㱩¶** + +## 28.ֲʽα֤ + +> ֲʽ񣺾ָIJߡ֧ķԴԼֱλڲͬķֲʽϵͳIJͬڵ֮ϡ˵ֲʽָľǷֲʽϵͳеĴھΪ˱֤ͬݿڵһԡ + +ֲʽļֽ +- 2PC(׶ύ)3PC +- TCCTryConfirmCancel +- Ϣ +- Ŭ֪ͨ +- seata + +ҿԿƪ¹[һ⣺ֲʽ](https://mp.weixin.qq.com/s/3r9MfIz2RAtdFhYzwwZxjA) + +## 29. ʧЧһЩ䳡 + +ǵĽӿڿУҪʹõҪܿʧЧһЩ䳡 + +- ķȨޱpublicprivateȨޣʧЧ +- finalģᵼʧЧ +- ͬһеķֱڲãᵼʧЧ +- һûspringͲspring +- ̵߳ãͬһ߳УȡݿӲһġ +- Ĵ洢治֧ +- Լtry...catch쳣ʧЧ +- Ĵ + +Ƽҿƪ£[springʧЧ12ֳ̫](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494570&idx=2&sn=17357bcd328b2d1d83f4a72c47daac1b&chksm=cf223483f855bd95351a778d5f48ddd37917ce2790ebbbcd1d6ee4f27f7f4b147f0d41101dcc&token=2044040586&lang=zh_CN&scene=21#wechat_redirect) + + +## 30. ճõģʽ + +ѴдãҪõģʽģʽģʽģ巽ģʽ۲ģʽȵȡģʽǴƾܽᡣʹģʽԿô롢ôױ⡢֤ɿԡ + +֮ǰдһƪܽṤгģʽ£дͦģҿԿ£[ʵսгõЩģʽ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495616&idx=1&sn=e74c733d26351eab22646e44ea74d233&chksm=cf2230e9f855b9ffe1ddb9fe15f72a273d5de02ed91cc97f3066d4162af027299718e2bf748e&token=1260947715&lang=zh_CN#rd) + +## 31. дʱ԰ȫ + +**߲**£```HashMap```ܻѭΪǷ԰ȫģԿʹ```ConcurrentHashMap```ҲϰߣҪ־һ```new HashMap()```; + +> - HashmapArraylistLinkedListTreeMapȶԲȫģ +> - VectorHashtableConcurrentHashMapȶ԰ȫ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ba0cab945874264a8d8e87b7d7c4a1b~tplv-k3u1fbpfcp-zoom-1.image) + + +## 32.ӿڶ׶淶 + +д룬ΪʵֵǰĹܣҲҪںά˵ά벻дԼģҲǸ˿ġԽӿڶҪ׶淶 + +## 33. ӿڵİ汾 + +ӿҪð汾ơ˵ģӦð```version```ӿڰ汾ֶΣδӿڼݡʵҲӿչԵһֵɡ + +ͻAPPijŻˣϰ汾Ṳ棬ʱǵ```version```汾žóˣ```version```ð汾ơ + +## 34. ע淶 + +עһЩĴ뻵ζ +- ظ루鹫÷ģʽ +- ࣨɷװһDTO +- С +- ж̫ࣨŻif...else +- ûõĴ +- עشʽ +- + +ĻζҶд[25ִ뻵ζܽ+Żʾ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect) + +## 35.֤ӿȷԣʵDZ֤ٵbug + +֤ӿڵȷԣǶȽDZ֤ٵbugûbugԽӿڿһҪ**Բһ**ȻĻӿڵȷڣ̲߳ʱ**֤ݵȷ**,ȵȡһת˽ףۼʱ򣬿ͨCASֹķʽ֤ۼȷɡ + +ʵɱӿڣ÷ֹɡʹRedisֲʽֹ⡣ʹRedisֲʽмעҪ㣬ҿԿ֮ǰƪ¹[ַ̽Redisֲʽȷʹ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) + +## 36.ѧṵͨǰ˹ͨƷͨ + +ҰһŵѧṵͨǷdzdzҪġ㿪ӿʱ**һԼͷѽӿڶ****Ҫͻȶӿ**һЩѵʱleader뷽ʵĹУʲô⣬ʱƷͨ + +֮ǣӿڹУһҪͨ~ + + +## (ע) + +ƪ¶ĻӭעҵĹںţݵСк + + diff --git "a/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md" "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md" new file mode 100644 index 0000000..762cb1b --- /dev/null +++ "b/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207/\345\220\216\347\253\257\346\200\235\347\273\264\347\257\207\344\272\214\357\274\232\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\236\347\216\260\344\270\200\344\270\252\345\271\266\350\241\214\350\260\203\347\224\250\346\250\241\346\235\277.md" @@ -0,0 +1,599 @@ +## ǰ + +ҺãǼݵСк + +Ǻ˼άרĵڶƪһƪ[36ƽӿڵĽ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499388&idx=1&sn=49a22120a3238e13ad7c3d3b73d9e453&chksm=cf222155f855a8434026b2c460d963c406186578c2527ca8f2bb829bbe849d87a2392a525a9b&token=1380536362&lang=zh_CN#rd)õdzСϿɡ +36ƽӿڵĽҲᵽһ㣺**ʹòеŻӿ**ԽͿӱޣдڶƪְֽдһеģ塣 + +- һеõӣAppҳϢѯ +- CompletionServiceʵֲе +- ȡͨõIJе÷ +- ˼ԼģʽӦ +- ˼ܽ +- ںţ**ݵСк** + + +## 1. һеõ + +һAPPҳѯĽӿڣҪûϢҪ```banner```ϢҪǩϢȵȡһСʵ£ + +``` +public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) { + //ûϢ + UserInfoParam userInfoParam = buildUserParam(req); + UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); + //bannerϢ + BannerParam bannerParam = buildBannerParam(req); + BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); + //ǩϢ + LabelParam labelParam = buildLabelParam(req); + LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); + //װ + return buildResponse(userInfoDTO,bannerDTO,labelDTO); +} +``` + +δʲô ʵһͦĴ룬ʵУѯûbannerǩϢ**Ǵе**ѯûϢ```200ms```ѯbannerϢ```100ms```ѯǩϢ```200ms```Ļʱ```500ms``` + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/35fa8e071a7048d5ae7d8e3e7f339532~tplv-k3u1fbpfcp-zoom-1.image) + +ʵΪŻܣǿ޸Ϊ**е**ķʽʱԽΪ```200ms```ͼʾ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e8559cfd7bb2449dbab91c0b38a3d78e~tplv-k3u1fbpfcp-zoom-1.image) + + +## 2. CompletionServiceʵֲе + +ӣ**ʵֲеأ** + +С˵ʹ```Future+Callable```ʵֶIJеáִ̳߳ʱֵ```Futureget()```ȡģǰһִбȽϺʱĻ```get```γŶӵȴ + +```CompletionService```ǶԶ```ExecutorService```˰װһ,һ߻ȡķֵ·ִֿ,֮䲻ụԻȡɵ + + +> ```CompletionService```ʵԭȽϼ򵥣ײͨFutureTask+УʵɵĻȻȡҲ˵ִнɵȺ˳ɿŻȡڲһȽȳУڱѾִɵFuture```CompletionService```polltakeɻȡһѾִɵFutureͨFutureӿʵ```get```ȡյĽ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10e375f58f5d490193888b0c5375e0f0~tplv-k3u1fbpfcp-zoom-1.image) + + +£```CompletionService```ʵֲвѯAPPҳϢ˼£ + +1. ȰѲѯûϢ񣬷ŵ̳߳أ£ +``` +ExecutorService executor = Executors.newFixedThreadPool(10); +//ѯûϢ +CompletionService userDTOCompletionService = new ExecutorCompletionService(executor); +Callable userInfoDTOCallableTask = () -> { + UserInfoParam userInfoParam = buildUserParam(req); + return userService.queryUserInfo(userInfoParam); + }; +userDTOCompletionService.submit(userInfoDTOCallableTask); +``` + +2. Ѳѯ```banner```ϢҲŵ̳߳صĻֲ÷ˣΪͲһһ```UserInfoDTO```һ```BannerDTO```ʱDzǰѷΪObjectɣΪжǼ̳Objectģ£ + +``` +ExecutorService executor = Executors.newFixedThreadPool(10); +//ѯûϢ +CompletionService baseDTOCompletionService = new ExecutorCompletionService(executor); +Callable userInfoDTOCallableTask = () -> { + UserInfoParam userInfoParam = buildUserParam(req); + return userService.queryUserInfo(userInfoParam); +}; +//bannerϢ +Callable bannerDTOCallableTask = () -> { + BannerParam bannerParam = buildBannerParam(req); + return bannerService.queryBannerInfo(bannerParam); +}; + +//ύûϢ +baseDTOCompletionService.submit(userInfoDTOCallableTask); +//ύbannerϢ +baseDTOCompletionService.submit(bannerDTOCallableTask); +``` +3. и⣬ǻȡ**ֵʱ**Dz֪ĸ```Object```ûϢDTOĸ```BannerDTO```**ôأ**ʱǿڲչΪһBaseRspDTOٸͷObjectݵģȻBaseRspDTOиUserDTOBannerDTO**Ψһkey**£ + +``` +public class BaseRspDTO { + + //DTOصΨһǣUserInfoDTOBannerDTO + private String key; + //صdata + private T data; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} + +//вѯAppҳϢ +public AppHeadInfoResponse parallelQueryAppHeadPageInfo(AppInfoReq req) { + + long beginTime = System.currentTimeMillis(); + System.out.println("ʼвѯappҳϢʼʱ䣺" + beginTime); + + ExecutorService executor = Executors.newFixedThreadPool(10); + CompletionService> baseDTOCompletionService = new ExecutorCompletionService>(executor); + + //ѯûϢ + Callable> userInfoDTOCallableTask = () -> { + UserInfoParam userInfoParam = buildUserParam(req); + UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); + BaseRspDTO userBaseRspDTO = new BaseRspDTO(); + userBaseRspDTO.setKey("userInfoDTO"); + userBaseRspDTO.setData(userInfoDTO); + return userBaseRspDTO; + }; + + //bannerϢѯ + Callable> bannerDTOCallableTask = () -> { + BannerParam bannerParam = buildBannerParam(req); + BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); + BaseRspDTO bannerBaseRspDTO = new BaseRspDTO(); + bannerBaseRspDTO.setKey("bannerDTO"); + bannerBaseRspDTO.setData(bannerDTO); + return bannerBaseRspDTO; + }; + + //labelϢѯ + Callable> labelDTODTOCallableTask = () -> { + LabelParam labelParam = buildLabelParam(req); + LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); + BaseRspDTO labelBaseRspDTO = new BaseRspDTO(); + labelBaseRspDTO.setKey("labelDTO"); + labelBaseRspDTO.setData(labelDTO); + return labelBaseRspDTO; + }; + + //ύûϢ + baseDTOCompletionService.submit(userInfoDTOCallableTask); + //ύbannerϢ + baseDTOCompletionService.submit(bannerDTOCallableTask); + //ύlabelϢ + baseDTOCompletionService.submit(labelDTODTOCallableTask); + + UserInfoDTO userInfoDTO = null; + BannerDTO bannerDTO = null; + LabelDTO labelDTO = null; + + try { + //Ϊύ3Իȡ3 + for (int i = 0; i < 3; i++) { + Future> baseRspDTOFuture = baseDTOCompletionService.poll(1, TimeUnit.SECONDS); + BaseRspDTO baseRspDTO = baseRspDTOFuture.get(); + if ("userInfoDTO".equals(baseRspDTO.getKey())) { + userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); + } else if ("bannerDTO".equals(baseRspDTO.getKey())) { + bannerDTO = (BannerDTO) baseRspDTO.getData(); + } else if ("labelDTO".equals(baseRspDTO.getKey())) { + labelDTO = (LabelDTO) baseRspDTO.getData(); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + System.out.println("вѯappҳϢ,ܺʱ" + (System.currentTimeMillis() - beginTime)); + return buildResponse(userInfoDTO, bannerDTO, labelDTO); +} +``` + +Ϊֹһ```CompletionService```ʵֲеõѾʵDzǺܿģ + +## 3. ȡͨõIJе÷ + +ǻع۲µ2СڣѯappҳϢdemo```CompletionService```ʵ˲еáûʲô뷨أ,ҵ񳡾ҲͨеŻDzҲøһƵ2СڵĴ롣ԣ**DzǿԳȡһͨõIJзñijҲã԰ɣǺ˼ά** + +ڵ2СڵĴ룬γȡͨòе÷ء + +ȣͨõIJе÷**ܸҵصԹҹ**԰ɣԷӦЩأ + +> Σ```Callable```԰ɡΪУ϶ǶCallableġԣӦһ```Callable```顣Ȼ󣬻APPҳѯӣ```Callable```ô```BaseRspDTO```ͣ԰ɣξ```List>> list``` + +Dzеõijأ ж```Callable```DzǵжӦķأˣijο```List>```dzȡͨòеģ壬Ϳдɽϣ + +``` + public List> executeTask(List>> taskList) { + + List> resultList = new ArrayList<>(); + //У + if (taskList == null || taskList.size() == 0) { + return resultList; + } + + ExecutorService executor = Executors.newFixedThreadPool(10); + CompletionService> baseDTOCompletionService = new ExecutorCompletionService>(executor); + //ύ + for (Callable> task : taskList) { + baseDTOCompletionService.submit(task); + } + + try { + //ȡ + for (int i = 0; i < taskList.size(); i++) { + Future> baseRspDTOFuture = baseDTOCompletionService.poll(2, TimeUnit.SECONDS); + resultList.add(baseRspDTOFuture.get()); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + return resultList; + } +``` +ȻdzȡͨõIJе÷ϵķǷ**ЩطҪĽ**أ + +- һŻĵط```executor̳߳```Щҵ񳡾```A̳߳```Щҵ```B̳߳```ôͲͨ԰ɡǿ԰̳߳Բʵṩ÷Լơ +- ڶŻĵط```CompletionService``````poll```ȡʱʱʱдġΪͬҵ񳡾ʱʱܲһԣʱʱҲǿԲʽų÷Լơ + +ٴŻһͨõIJеģ壬£ +``` +public List> executeTask(List>> taskList, long timeOut, ExecutorService executor) { + + List> resultList = new ArrayList<>(); + //У + if (taskList == null || taskList.size() == 0) { + return resultList; + } + if (executor == null) { + return resultList; + } + if (timeOut <= 0) { + return resultList; + } + + //ύ + CompletionService> baseDTOCompletionService = new ExecutorCompletionService>(executor); + for (Callable> task : taskList) { + baseDTOCompletionService.submit(task); + } + + try { + //ȡ + for (int i = 0; i < taskList.size(); i++) { + Future> baseRspDTOFuture = baseDTOCompletionService.poll(timeOut, TimeUnit.SECONDS); + resultList.add(baseRspDTOFuture.get()); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + + return resultList; +} +``` + +ԺijҲҪõеõĻֱӵɣDzеССijɾ͸ + +## 4. ˼ԼģʽӦ + +ǰѳȡǸõIJе÷Ӧõ```AppҳϢѯ```ӣ£ + +``` +public AppHeadInfoResponse parallelQueryAppHeadPageInfo1(AppInfoReq req) { + + long beginTime = System.currentTimeMillis(); + System.out.println("ʼвѯappҳϢʼʱ䣺" + beginTime); + //ûϢѯ + Callable> userInfoDTOCallableTask = () -> { + UserInfoParam userInfoParam = buildUserParam(req); + UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); + BaseRspDTO userBaseRspDTO = new BaseRspDTO(); + userBaseRspDTO.setKey("userInfoDTO"); + userBaseRspDTO.setData(userInfoDTO); + return userBaseRspDTO; + }; + + //bannerϢѯ + Callable> bannerDTOCallableTask = () -> { + BannerParam bannerParam = buildBannerParam(req); + BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); + BaseRspDTO bannerBaseRspDTO = new BaseRspDTO(); + bannerBaseRspDTO.setKey("bannerDTO"); + bannerBaseRspDTO.setData(bannerDTO); + return bannerBaseRspDTO; + }; + + //labelϢѯ + Callable> labelDTODTOCallableTask = () -> { + LabelParam labelParam = buildLabelParam(req); + LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); + BaseRspDTO labelBaseRspDTO = new BaseRspDTO(); + labelBaseRspDTO.setKey("labelDTO"); + labelBaseRspDTO.setData(labelDTO); + return labelBaseRspDTO; + }; + + List>> taskList = new ArrayList<>(); + taskList.add(userInfoDTOCallableTask); + taskList.add(bannerDTOCallableTask); + taskList.add(labelDTODTOCallableTask); + ExecutorService executor = Executors.newFixedThreadPool(10); + List> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor); + if (resultList == null || resultList.size() == 0) { + return new AppHeadInfoResponse(); + } + + UserInfoDTO userInfoDTO = null; + BannerDTO bannerDTO = null; + LabelDTO labelDTO = null; + + // + for (int i = 0; i < resultList.size(); i++) { + BaseRspDTO baseRspDTO = resultList.get(i); + if ("userInfoDTO".equals(baseRspDTO.getKey())) { + userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); + } else if ("bannerDTO".equals(baseRspDTO.getKey())) { + bannerDTO = (BannerDTO) baseRspDTO.getData(); + } else if ("labelDTO".equals(baseRspDTO.getKey())) { + labelDTO = (LabelDTO) baseRspDTO.getData(); + } + } + + System.out.println("вѯappҳϢ,ܺʱ" + (System.currentTimeMillis() - beginTime)); + return buildResponse(userInfoDTO, bannerDTO, labelDTO); + } + +``` + +ϴ룬СǣǷŻ뷨أ ⼸```Callable```ѯDzҲԳȡһ£ôӼࡣ + +> ˵ֱӽһ```BaseTaskCommand```࣬ʵ```Callable```ӿڣѲѯûϢѯbannerϢlabelǩϢIJѯŽȥ + +£ + +``` +public class BaseTaskCommand implements Callable> { + + private String key; + private AppInfoReq req; + private IUserService userService; + private IBannerService bannerService; + private ILabelService labelService; + + public BaseTaskCommand(String key, AppInfoReq req, IUserService userService, IBannerService bannerService, ILabelService labelService) { + this.key = key; + this.req = req; + this.userService = userService; + this.bannerService = bannerService; + this.labelService = labelService; + } + + @Override + public BaseRspDTO call() throws Exception { + + if ("userInfoDTO".equals(key)) { + UserInfoParam userInfoParam = buildUserParam(req); + UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); + BaseRspDTO userBaseRspDTO = new BaseRspDTO(); + userBaseRspDTO.setKey("userInfoDTO"); + userBaseRspDTO.setData(userInfoDTO); + return userBaseRspDTO; + } else if ("bannerDTO".equals(key)) { + BannerParam bannerParam = buildBannerParam(req); + BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); + BaseRspDTO bannerBaseRspDTO = new BaseRspDTO(); + bannerBaseRspDTO.setKey("bannerDTO"); + bannerBaseRspDTO.setData(bannerDTO); + return bannerBaseRspDTO; + } else if ("labelDTO".equals(key)) { + LabelParam labelParam = buildLabelParam(req); + LabelDTO labelDTO = labelService.queryLabelInfo(labelParam); + BaseRspDTO labelBaseRspDTO = new BaseRspDTO(); + labelBaseRspDTO.setKey("labelDTO"); + labelBaseRspDTO.setData(labelDTO); + return labelBaseRspDTO; + } + + return null; + } + + + private UserInfoParam buildUserParam(AppInfoReq req) { + return new UserInfoParam(); + } + + private BannerParam buildBannerParam(AppInfoReq req) { + return new BannerParam(); + } + + private LabelParam buildLabelParam(AppInfoReq req) { + return new LabelParam(); + } +} +``` +룬캯**Ƚ϶IJ**```call()```Уж```if...else...```,һ֧**ѯϢ**ֵ```call```޸ˣ**BaseTaskCommandĹҲҪ޸** + +> Ƿӡ󣬶гֶif...else...ʱǾͿԿʹ**ģʽ+ģʽ**Ż + +ʵ࣬£ + +``` + +public interface IBaseTask { + + //ÿkey + String getTaskType(); + + BaseRspDTO execute(AppInfoReq req); + +} + +//ûϢ +@Service +public class UserInfoStrategyTask implements IBaseTask { + + @Autowired + private IUserService userService; + + @Override + public String getTaskType() { + return "userInfoDTO"; + } + + @Override + public BaseRspDTO execute(AppInfoReq req) { + UserInfoParam userInfoParam = userService.buildUserParam(req); + UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam); + BaseRspDTO userBaseRspDTO = new BaseRspDTO(); + userBaseRspDTO.setKey(getTaskType()); + userBaseRspDTO.setData(userBaseRspDTO); + return userBaseRspDTO; + } +} + +/** + * bannerϢʵ + **/ +@Service +public class BannerStrategyTask implements IBaseTask { + + @Autowired + private IBannerService bannerService; + + @Override + public String getTaskType() { + return "bannerDTO"; + } + + @Override + public BaseRspDTO execute(AppInfoReq req) { + BannerParam bannerParam = bannerService.buildBannerParam(req); + BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam); + BaseRspDTO bannerBaseRspDTO = new BaseRspDTO(); + bannerBaseRspDTO.setKey(getTaskType()); + bannerBaseRspDTO.setData(bannerDTO); + return bannerBaseRspDTO; + } +} + +... +``` +Ȼ⼸ʵ࣬ô```spring```أ ǿʵ```ApplicationContextAware```ӿڣѲԵʵע뵽һmapȻ󷽲ͬIJ(DTOͣȥʵֲͬIJáʵڹģʽ˼롣£ + +``` +/** + * Թ + **/ +@Component +public class TaskStrategyFactory implements ApplicationContextAware { + + private Map map = new ConcurrentHashMap<>(); + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + Map tempMap = applicationContext.getBeansOfType(IBaseTask.class); + tempMap.values().forEach(iBaseTask -> { + map.put(iBaseTask.getTaskType(), iBaseTask); + }); + } + + public BaseRspDTO executeTask(String key, AppInfoReq req) { + IBaseTask baseTask = map.get(key); + if (baseTask != null) { + System.out.println("ʵִ"); + return baseTask.execute(req); + } + return null; + } +} +``` + +˲Թ```TaskStrategyFactory```ٻŻ```BaseTaskCommand```Ĵ롣ĹѾҪ```IUserService userService, IBannerService bannerService, ILabelService labelService```ֻҪԹ```TaskStrategyFactory```ɡͬʱҲҪ```if...else...```жˣòԹ```TaskStrategyFactory```漴ɡŻĴ£ + +``` +public class BaseTaskCommand implements Callable> { + + private String key; + private AppInfoReq req; + private TaskStrategyFactory taskStrategyFactory; + + public BaseTaskCommand(String key, AppInfoReq req, TaskStrategyFactory taskStrategyFactory) { + this.key = key; + this.req = req; + this.taskStrategyFactory = taskStrategyFactory; + } + + @Override + public BaseRspDTO call() throws Exception { + return taskStrategyFactory.executeTask(key, req); + } +} +``` + +```appҳϢ```ѯͿŻ£ + +``` +public AppHeadInfoResponse parallelQueryAppHeadPageInfo2(AppInfoReq req) { + long beginTime = System.currentTimeMillis(); + System.out.println("ʼвѯappҳϢհ汾ʼʱ䣺" + beginTime); + List>> taskList = new ArrayList<>(); + //ûϢѯ + taskList.add(new BaseTaskCommand("userInfoDTO", req, taskStrategyFactory)); + //bannerѯ + taskList.add(new BaseTaskCommand("bannerDTO", req, taskStrategyFactory)); + //ǩѯ + taskList.add(new BaseTaskCommand("labelDTO", req, taskStrategyFactory)); + + ExecutorService executor = Executors.newFixedThreadPool(10); + List> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor); + + if (resultList == null || resultList.size() == 0) { + return new AppHeadInfoResponse(); + } + + UserInfoDTO userInfoDTO = null; + BannerDTO bannerDTO = null; + LabelDTO labelDTO = null; + + for (BaseRspDTO baseRspDTO : resultList) { + if ("userInfoDTO".equals(baseRspDTO.getKey())) { + userInfoDTO = (UserInfoDTO) baseRspDTO.getData(); + } else if ("bannerDTO".equals(baseRspDTO.getKey())) { + bannerDTO = (BannerDTO) baseRspDTO.getData(); + } else if ("labelDTO".equals(baseRspDTO.getKey())) { + labelDTO = (LabelDTO) baseRspDTO.getData(); + } + } + + System.out.println("вѯappҳϢհ汾,ܺʱ" + (System.currentTimeMillis() - beginTime)); + return buildResponse(userInfoDTO, bannerDTO, labelDTO); + } +``` + + +## 5. ˼ܽ + +ϴŻѾܼǻûбŻ˼·ء +> ʵеģ磬Ψһǵ```key```Ϊö٣дַ```"userInfoDTO""bannerDTO""labelDTO"```У```CompletionService```ЩСϲ```CompletableFuture```ʵвеá + +ĴѧЩ֪ʶأ +1. ŻӿܣijЩ£ʹòеô洮С +2. ʵֲеأ ʹ```CompletionService``` +3. ѧĺ˼άǣ ճУҪѧȡͨõķ߹ߡ +4. ģʽ͹ģʽӦ + +ĵĻģʽ黹ǺϸȻһƪҽдϣȡģʽĹȻҪĵĻԹעҵĹںţ**ݵСк**ҵϵʽ + + + + + diff --git "a/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/README.MD" "b/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/README.MD" new file mode 100644 index 0000000..04f4506 --- /dev/null +++ "b/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/README.MD" @@ -0,0 +1,13 @@ +## 1. 面试真题 + +关注公众号:捡田螺的小男孩 +​ +- [oppo后端16连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498750&idx=1&sn=19fe8b4fff28fe81db14e733053bbc74&chksm=cf2224d7f855adc1d0984980a4e3de31fe33329164a472ca8d8255a8a80b69b2e23850811323&token=2001057130&lang=zh_CN#rd) +- [小厂后端十连问(附答案)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498316&idx=1&sn=7749b78293b7b2af51eda99844e08a56&chksm=cf222565f855ac7324232e2af459f8b6e6eb5fd5b272c2b29bda08cc579421b6704a0de94b2e&token=2001057130&lang=zh_CN#rd) +- [腾讯云后端15连问!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498171&idx=1&sn=f5a7ec25a569822be0f73fbcd413e8ba&chksm=cf222692f855af84fba419166fcd4235c0e78af3a2e1ec4c723a4efb1bd1ad6f8a5b9404c599&token=2001057130&lang=zh_CN#rd) +- [社招后端21连问(三年工作经验一面)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498084&idx=1&sn=96c8148cfeeeb16668ed9e03fa9131cc&chksm=cf22264df855af5b6e81b93738cca28989226a53ec702fcfaa0cc5004dded4208c5ee5ea844a&token=2001057130&lang=zh_CN#rd) +- [一份热乎乎的字节面试真题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497742&idx=1&sn=18765e7356f446a7f2521f45b467d5d3&chksm=cf222727f855ae31dd2029e3219814211336c41d9228d271a583d3691ddadca586529aca9302&token=2001057130&lang=zh_CN#rd) +- [面试必备:虾皮服务端15连问](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497256&idx=1&sn=3b799c2d514aa25e85a6faa60d639a0b&chksm=cf222901f855a017b73356b99b830b8800a7a9172fab891c5759d8dd69a270872ea9480c0b7c&token=2001057130&lang=zh_CN#rd) +- [宇宙条一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495342&idx=1&sn=54e1c0c16a6467001524c34818025331&chksm=cf223187f855b89140db5ca429e6efc19d0111abf7f36b78a0ecd73b00fded1ff1e7ba32a6f1&token=2001057130&lang=zh_CN#rd) +- [蚂蚁金服一面:十道经典面试题解析](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493270&idx=1&sn=1c78a81d6e1bd0f0fd947fe8c3a33e32&chksm=cf2239bff855b0a9627855f20a17799e0506eb7548a409bfa0ee0450328d7519ec70f7b962cc&token=2001057130&lang=zh_CN#rd) +- [田螺精品面试PDF发布](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499943&idx=1&sn=fe869c0a97a306e42830336fe74e17a6&chksm=cf221f8ef8559698781709bfbccbb85087286e48434905fb18bec3a3ec0af7329c2a1632c230&token=1990771297&lang=zh_CN#rd) \ No newline at end of file diff --git "a/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/\351\230\277\351\207\214\344\270\200\351\235\242\357\274\214\347\273\231\344\272\206\345\207\240\346\235\241SQL\357\274\214\351\227\256\351\234\200\350\246\201\346\211\247\350\241\214\345\207\240\346\254\241\346\240\221\346\220\234\347\264\242\346\223\215\344\275\234\357\274\237.md" "b/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/\351\230\277\351\207\214\344\270\200\351\235\242\357\274\214\347\273\231\344\272\206\345\207\240\346\235\241SQL\357\274\214\351\227\256\351\234\200\350\246\201\346\211\247\350\241\214\345\207\240\346\254\241\346\240\221\346\220\234\347\264\242\346\223\215\344\275\234\357\274\237.md" new file mode 100644 index 0000000..a695048 --- /dev/null +++ "b/\345\244\247\345\216\202\351\235\242\350\257\225\347\234\237\351\242\230/\351\230\277\351\207\214\344\270\200\351\235\242\357\274\214\347\273\231\344\272\206\345\207\240\346\235\241SQL\357\274\214\351\227\256\351\234\200\350\246\201\346\211\247\350\241\214\345\207\240\346\254\241\346\240\221\346\220\234\347\264\242\346\223\215\344\275\234\357\274\237.md" @@ -0,0 +1,246 @@ +### ǰ + +λȥԣ˵Թٸ˼ѯSQL:Ҫִмѵʱеµģ侲˼ŷ־ǿļ֪ʶ~~ Ƿ־Ÿ֪ʶ㣬һ̽һ¡вȷĻӭָһѧϰ~ + +- ںţ**ݵСк** +- githubַлÿstar +> https://github.com/whx123/JavaHome + +- Թٿ֮ʲô +- Թٿ֮ +- Թٿ֮ΪʲôѡB+Ϊṹ +- Թٿ֮һ +- Թٿ֮ +- Թٿ֮ʧЧ +- Թٿ֮ǰ׺ +- Թٿ֮ +- Թٿ֮ + +### һԹٿ֮ʲô + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/21eed26e34534b51aadf28d5defaed7e~tplv-k3u1fbpfcp-watermark.image) + +- һݿѯЧʵݽṹԱһֵĿ¼԰ҵӦļ¼ +- һ洢ڴ̵ļУռռġ +- νˮۣҲܸۡʵ߲ѯЧʣӰݿIJ͸¹ܡ + +### Щ + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf6725e1a8cb44b496cca8fa45e15c2f~tplv-k3u1fbpfcp-watermark.image) + +#### ݽṹά + +- B+ݴ洢Ҷӽڵ㣬ӶΪO(logn)ʺϷΧѯ +- ϣ: ʺϵֵѯЧʸߣһελ +- ȫMyISAMInnoDBж֧ʹȫһıchar,text,varcharϴ +- R-Tree: GISʹSPATIAL + +#### 洢ά + +- ۼۼҶӽڵ洢DZеݡ +- ǾۼǾۼԷҶӽڵ洢С + +#### ߼ά + +- һΨһпֵ +- ͨMySQLлֵͣظֵ +- ֶδʹʱѭǰ׺ԭ +- ΨһеֵΨһģΪֵ +- ռMySQL5.7ֿ֧֮ռڿռⷽѭOpenGISģ͹ + +### Թٿ֮ΪʲôѡB+Ϊṹ + +ԴӼάȥ⣬ѯǷ񹻿죬ЧǷȶ洢ݶ٣ԼҴ̴ȵȡΪʲôǹϣṹΪʲôǶΪʲôƽΪʲôBƫƫB+أ + +дҵSQLѯʱ£ǷΧѯģһSQL +``` +select * from employee where age between 18 and 28; +``` +#### Ϊʲôʹùϣṹ +֪ϣṹk-vṹҲǣkeyvalueһһϵ**ֵѯ**ԣǷΧѯΪŶ + +#### Ϊʲôʹöأ + +Ȼ¶֪ʶ~ ν**ص£** +- ÿֱΪ +- ӽڵֵСڵǰڵֵǰڵֵСӽڵֵ +- ˵ĽڵΪڵ㣬ûӽڵĽڵֵΪҶӽڵ㡣 + +ԺУ׾͸ֳֶṹͼ +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d4c349a7bda4b3db1791f9cff9c093e~tplv-k3u1fbpfcp-watermark.image) + +أЩŶ +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/97b71a48d43545a0bc20a55adf2be207~tplv-k3u1fbpfcp-watermark.image) + +⻯Ϊһ൱ȫɨ衣ôҪѽˣһʺΪṹ + +#### Ϊʲôʹƽأ + +ƽص㣺ҲһŶκνڵ߶Ϊ1ԾͲ⻯һ + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74afa9b54e7f4c0996ff83e66016c09a~tplv-k3u1fbpfcp-watermark.image) + +أ +- ƽ߸ǣҪάƽ⣬ά۴ +- Ļĸ߶ȻܸߡΪǴڴ̵ģΪṹÿδӴ̶ȡһڵ㣬IOĴͶ + + +#### ΪʲôʹBأ + + +Ļƽĸ߶ȻܸߣIOΪʲôѡͬ**߶ȸB**أ + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/acc8f2e2cfb54092b4fb131e500334a3~tplv-k3u1fbpfcp-watermark.image) + +BƽͿԴ洢ݣ߶ȸ͡ΪѡB+أΪB+B棺 +- B+ҶӽڵDz洢ݵģ洢ֵBڵв洢ֵҲ洢ݡinnodbҳĬϴС16KB洢ݣôͻ洢ļֵӦĽڵӽڵͻͻ֣һDzݽд̵IOлٴμ٣ݲѯЧҲ졣 +- B+ݾ洢Ҷӽڵ㣬ǰ˳еģŵġôB+ʹ÷ΧңңԼȥزұ쳣򵥡 + +### ġԹٿ֮һB+ + +**Թ٣** ±ṹ⼸ +``` +CREATE TABLE `employee` ( + `id` int(11) NOT NULL, + `name` varchar(255) DEFAULT NULL, + `age` int(11) DEFAULT NULL, + `date` datetime DEFAULT NULL, + `sex` int(1) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_age` (`age`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +insert into employee values(100,'С',43,'2021-01-20','0'); +insert into employee values(200,'',48,'2021-01-21','0'); +insert into employee values(300,'',36,'2020-01-21','1'); +insert into employee values(400,'',32,'2020-01-21','0'); +insert into employee values(500,'Ѹ',37,'2020-01-21','1'); +insert into employee values(600,'С',49,'2021-01-21','0'); +insert into employee values(700,'С',28,'2021-01-21','1'); +``` + +**Թ٣** ִµIJѯSQLҪִмεԻ¶Ӧṹͼ~ +``` +select * from Temployee where age=32; +``` + +**** ʵԹپǿѡǷϤB+ṹͼϻش~ + +- Ȼ`idx_age`ṹͼ£ + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f88457b43e354ca18795fa0033ad075f~tplv-k3u1fbpfcp-watermark.image) + +- ٻidȻṹͼ£ + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/963a8dacb39345008c93b1d0ea079eec~tplv-k3u1fbpfcp-watermark.image) + + +ˣ SQL ѯִд̾ǽϣ +- 1. `idx_age`̿1صڴ棬32<37,·֧Ѱַ̿2 +- 2. ̿2صڴУڴҵage=32ļ¼ȡid = 400. +- 3. õid=400󣬻صid +- 4. `id`̿1ڴ棬ڴҵ400B+ҶӽڵDzݵġ400ҷ֧Ѱַ̿3. +- 5. ̿3ڴ棬ڴҵid=400ļ¼õR4һеݣõģ󹦸ɡ + +ˣSQLѯִ˼DzһȻѽ**ر**`idx_age`ҵ`id`󣬻صidĹ,ͳΪر +> ʲôǻرõٻصѯḶ́ͽ**ر** + +### 塢Թٿ֮ + +**Թ٣** `select *`, ʹ`select id,age`ϵĿִ˼أ + +**** ⣬Ҫѡ˵ĸ֪ʶ㡣ص`idx_age`ԷֲѯѡidageҶӽڵˡˣֱṩѯͲҪٻر~ + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/629b6cfd08614adcbb20154707c0c8e0~tplv-k3u1fbpfcp-watermark.image) + +> ڲѯ棬Ҫرȥ飬ֱӴоȡҪĽ仰˵SQLõݣ˲ѯУϸˡ + +ԣϸ⣬ʡȥ˻ر + +### Թٿ֮ʧЧ + +**Թ٣** ڸ`name`ֶμͨȻølikeģǻִжٴβѯأSQL£ +``` +select * from employee where name like '%%'; +``` +**** ֪ʶǣlikeǷᵼ²ȸSQLexplainִмƻɡʵlike ģᵼ²ģ£ + +![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b01bb44ff5744729e55939cc88bd424~tplv-k3u1fbpfcp-watermark.image) + +ˣSQLȫɨ~ճУ⼸ɧܻᵼʧЧ£ +- ѯorܵʧЧ +- ֶַwhereʱһʧЧ +- likeͨܵʧЧ +- ѯʱвеĵһУʧЧ +- ʹmysqlúʧЧ +- 㣨磬+-*/ʧЧ +- ֶʹã= < >not inʱܻᵼʧЧ +- ֶʹis null is not nullܵʧЧ +- ӲѯӲѯѯֶαʽһܵʧЧ +- mysqlʹȫɨҪʹ,ʹ + +### ߡԹٿ֮ǰ׺ԭ + +**Թ٣** ڸname,ageֶμSQLִжٴأȻ +``` +select * from employee where name like 'С%' order by age desc; +``` +**** ǰ׺ԭԼlikeǷ֪ʶ㡣ʾͼ£ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/29df075df94549b693a6a0ee06730666~tplv-k3u1fbpfcp-watermark.image) + +ȰnameСnameͬageСԹҪֵһǡСˣSQLlike 'С%'ǿ```idx_name_age```ġ + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/58b49f88f75f48be873deb395431c2fa~tplv-k3u1fbpfcp-watermark.image) + +òѯidx_name_ageҵһСֵҵ```ССסСࡢ```ֱõId=```600100700```ȻαȥҶӦļ¼ ǰ׺```С```ַMַʵϣ +- ǰ׺NֶΡa,b,c൱ڽˣaa,b,(a,b,c) +- ǰ׺ҲַMַ + +### ˡԹٿ֮ + +**Թ٣** ǻǾ idx_name_ageSQLִмأ + +``` +select * from employee where name like 'С%' and age=28 and sex='0'; +``` + +**** Ƶ֪ʶ㣬**Mysql5.6֮ǰ**idx_name_ageҳֵһǡСˣõǵidȻرҳУȥԱԱֶΡͼ + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5729b1a84fd4ee9b872717903ca6f75~tplv-k3u1fbpfcp-watermark.image) + +Щѿܾ֣name,age)ΪʲôѡСֺ󣬲˳㿴ageٻرأǸЧѽMySQL 5.6 **Ż**Уаֶжϣֱӹ˵ļ¼ٻر + + +ˣMySQL5.6汾֮ѡСֺ˳age=28,Ծֻһλر + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/168aecb4709d4c30bc93c489ad76d712~tplv-k3u1fbpfcp-watermark.image) + +### š Թٿ֮ + +**Թ٣** һűǧ򼶱ϵģôűҪôأ + +**** Ҫ֪һ㣬ʱǻԱġпܳ¹ʵġԲο· + +- 1.ȴһŸԭAݽṹͬ±B +- 2.±BҪϵ +- 3.ԭAݵ±B +- 4.rename±BΪԭıAԭAı + +### ܽϰ + +Ҫ9ؼ֪ʶ㣬ϣԴаأҳһйҵ񿪷ļSQL´ôشģȤϵҹ~Ŀ£ +``` + +select * from A where type ='1' and status ='s' order by create_time desc; +``` +type9ֶͣԻԣstatusֶȲߣ3ͣôμأ +- Ǹtypeӵ +- ǣtypestatuscreate_time +- ǣtypecreate_timeأ + +### οл +- [ MySQLЩ ?](https://segmentfault.com/q/1010000003832312) +- [](https://zhuanlan.zhihu.com/p/151460679) +- [MySQLʵս45](https://time.geekbang.org/column/article/69636) + diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/READEME.MD" "b/\345\267\245\344\275\234\346\200\273\347\273\223/READEME.MD" new file mode 100644 index 0000000..6c258b9 --- /dev/null +++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/READEME.MD" @@ -0,0 +1,17 @@ +## 工作总结 + +- [工作总结!日志打印的15个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494838&idx=1&sn=cdb15fd346bddf3f8c1c99f0efbd67d8&chksm=cf22339ff855ba891616c79d4f4855e228e34a9fb45088d7acbe421ad511b8d090a90f5b019f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [25种代码坏味道总结+优化示例](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490148&idx=1&sn=00a181bf74313f751b3ea15ebc303545&chksm=cf21c54df8564c5bc5b4600fce46619f175f7ae557956f449629c470a08e20580feef4ea8d53&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [聊聊日常开发中,如何减少bug呢?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490662&idx=1&sn=d38a090611af7f64ee3c6a31331d5228&chksm=cf21c34ff8564a59e505e6edf3065a0fc506c6d2c96f492c8d8873cd46dedbe0704e43cb9c2e&token=1990771297&lang=zh_CN#rd) +- [工作四年,分享50个让你代码更好的小建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488708&idx=1&sn=6e2e0a740f5d42a59641487a0bf1e3bf&chksm=cf21cbedf85642fbb485fa1c7bf9af21923d8503f2542b6f8283ce79ddc683f7d9e45da83100&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [写代码有这16个好习惯,可以减少80%非业务的bug](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488097&idx=1&sn=eaca1f92ca3ccd9de00dbc4ef3e4029a&chksm=cf21cd48f856445e4cc24c1f8bcf18d1479bad0a37a87a2fb70717d8a4e65dcf7b4d5f83d24f&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [Java日常开发的21个坑,你踩过几个?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488115&idx=1&sn=bdd4a4ca36bc7ea902106d058e8537fb&chksm=cf21cd5af856444cb36af600705615454b0aaa2b289b97ddb52d594556ac07a1915b73ecce19&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [CAS乐观锁解决并发问题的一次实践](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487937&idx=1&sn=206a37bf6d6a7aa1d05674c479ed7a72&chksm=cf21cee8f85647fe7a082049a41c0f640f54976d2cdf4302b24c5517ca42b854eb84b13ece10&token=1990771297&lang=zh_CN#rd) +- [写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487961&idx=1&sn=e646231067968d9f58e6665914293f9a&chksm=cf21cef0f85647e6f3ff2feece004ac3bd979e37fe45103c88d0f299dfe632a5cf6dd547c1d9&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备:Java日期处理的十个坑](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487973&idx=1&sn=0f713413098fb579e5f200b829f71e89&chksm=cf21ceccf85647da450765d79bf5943da551c3be950447063b9f8c77c21bf2a39b99387a949b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:加签验签](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488022&idx=1&sn=70484a48173d36006c8db1dfb74ab64d&chksm=cf21cd3ff8564429a1205f6c1d78757faae543111c8461d16c71aaee092fe3e0fed870cc5e0e&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [记一次接口性能优化实践总结:优化接口性能的八个建议](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488004&idx=1&sn=00840efd9c0bd0a7f172b59eb2ca130f&chksm=cf21cd2df856443bf21d8e09cfe5c8452ecaf82e3c2210fca3b28829ded04defddcf63c0a59b&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [程序员必备基础:如何安全传输存储用户密码?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488117&idx=1&sn=5d3d0eda0ed45f3f576e211de31ca3a9&chksm=cf21cd5cf856444af1407a94a2abf445265ca7c5f5855cfa1c223cb209e99040c7889621f231&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [一次代码优化实践,用了模板方法+策略+工厂方法模式](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488061&idx=1&sn=1d9ab7954b03521ab81ecf033c0e5e50&chksm=cf21cd14f8564402b213f0ef908bbdb0e12fed4b281c5803b8e539cacb1551654194becfb7d6&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [保证接口数据安全的10种方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500285&idx=1&sn=7d0723f25d46e858859cfd79acb6fb9d&chksm=cf221ed4f85597c2093f81baa5fdedc65817bf2d23a7951236836b0f54c2335695cbed61cd13&token=1990771297&lang=zh_CN#rd) \ No newline at end of file diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\345\267\245\344\275\234\345\233\233\345\271\264\357\274\21450\344\270\252\350\256\251\344\275\240\344\273\243\347\240\201\346\233\264\345\245\275\347\232\204\345\260\217\345\273\272\350\256\256.md" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\345\267\245\344\275\234\345\233\233\345\271\264\357\274\21450\344\270\252\350\256\251\344\275\240\344\273\243\347\240\201\346\233\264\345\245\275\347\232\204\345\260\217\345\273\272\350\256\256.md" new file mode 100644 index 0000000..e47a0ba --- /dev/null +++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/\345\267\245\344\275\234\345\233\233\345\271\264\357\274\21450\344\270\252\350\256\251\344\275\240\344\273\243\347\240\201\346\233\264\345\245\275\347\232\204\345\260\217\345\273\272\350\256\256.md" @@ -0,0 +1,1478 @@ +### ǰ +꣬ܶ˼Ĵ룬дһܽɣ50õĽ顣еһЩ㣬ǰҲдҪһ¡ϣճд˼ܽᣬͣͬʱвԵģҲָл~ + +- ںţ**ݵСк** +- githubַhttps://github.com/whx123/JavaHome + +### 1. жǷʱselect count select Уá + +ǾƵҵ񳡾磬жijû```userId```ǷǻԱ + +**** һЩСʵ֣ȲûϢû¼ȻȥжǷǻԱ: +``` + + +boolean isVip (String userId){ + UserInfo userInfo = userInfoDAp.selectUserByUserId(userId); + return UserInfo!=null && "Y".equals(userInfo.getVipFlag()) +} +``` + +**** ҵ񳡾ʵõʵֱ֣```select count```һ££ + +``` + + + boolean isVip (String userId){ + int vipNum = userInfoDAp.countVipUserByUserId(userId); + return vipNum>0 +} +``` + + +### 2. ӵif߼Ե˳óЧ + + +ҵûǻԱҵһε½ʱҪһ֪ͨĶšûо˼ֱܿдˡ + +``` +if(isUserVip && isFirstLogin){ + sendMsgNotify(); +} +``` + +ܹ5isUserVipͨ3isFirstLoginͨ1 ôϴ룬isUserVipִеĴΪ5ΣisFirstLoginִеĴҲ3Σ£ + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b30c2899d10f4258bf1fc94a9ebf324c~tplv-k3u1fbpfcp-watermark.image) + + +һisUserVipisFirstLogin˳أ +``` +if(isFirstLogin && isUserVip ){ + sendMsg(); +} +``` +isFirstLoginִеĴ5ΣisUserVipִеĴ1Σ£ + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/103a20c642e04ea899d74c4cb8187b28~tplv-k3u1fbpfcp-watermark.image) + +isFirstLoginж߼ֻselect count һݿisUserVipҲselect count һݿĻȻisFirstLoginǰЧ + + +### 3. дѯSqlʱֻҪõֶΣͨõֶΣֵܾselect * + +**** + +``` +select * from user_info where user_id =#{userId}; +``` + +**** + + +``` + selct user_id , vip_flag from user_info where user_id =#{userId}; +``` + +**ɣ** + +- ʡԴ翪 +- õٻر߲ѯЧʡ + +### 4. Żij򣬾ܾҪĶ + + +ı߼жϣһᱻֵ˵ֻһֱַӳʼַͿˣûбҪҪnew String(). + + + +``` +String s = new String ("ӭעںţݵСк"); +``` + + + +``` +String s= "ӭעںţݵСк ; +``` + + +### 5. ʼʱָ + + +ĿֲᣬҲȷᵽ㣺 +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2ac43ab291794c608537f1c4da6a9caa~tplv-k3u1fbpfcp-watermark.image) + + +mapҪ洢Ԫظ15ңд +``` + //initialCapacity = 15/0.75+1=21 + Map map = new HashMap(21); + ΪhashMap2йأԿȡ32 + Map map = new HashMap(32); +``` + +### 6.catch쳣Ҫӡexceptionöλ + +**** +``` +try{ + // do something +}catch(Exception e){ + log.info("ݵСкij쳣"); +} +``` + +**** + +``` +try{ + // do something +}catch(Exception e){ + log.info("ݵСкij쳣",e); //exceptionӡ +} +``` + +**ɣ** +- УûаexceptionʱŲͲòSQlд쳣IO쳣أӦðexceptionӡ־Ŷ~ + +### 7. ӡ־ʱ򣬶ûиObjecttoStringķֱӰӡˡ + + +ڴӡ־ʱ򣬾뿴һrequestʲôǺЩ룺 + + +``` +publick Response dealWithRequest(Request request){ + log.info("ǣ".request.toString) +} +``` +ӡ£ + +``` +ǣlocal.Request@49476842 +``` + +ΪtoStringĬϵʵǡ@ɢ޷ʮơ㿴ɣӴӡ־ûɶ˼㶼֪ӡʲôݡ + +һ(ΪεĶ󣩣**дtoString()** + +``` +class Request { + + private String age; + + private String name; + + @Override + public String toString() { + return "Request{" + + "age='" + age + '\'' + + ", name='" + name + '\'' + + '}'; + } +} + +publick Response dealWithRequest(Request request){ + log.info("ǣ".request.toString) +} + + +``` +ӡ£ + +``` +ǣRequest{age='26', name='ںţݵСк'} +``` + +### 8. һܾIJб + +ôһзβĸ + +``` +public void getUserInfoString name,String age,String sex,String mobile){ + // do something ... +} +``` + +ҪഫһversionĹзdubboֶṩĽӿڵĻôĽӿDzҪϰ汾 + +``` +public void getUserInfoString name,String age,String sex,String mobile){ + // do something ... +} + +/** + * ½ӿڵ + */ +public void getNewUserInfoString name,String age,String sex,String mobileString version){ + // do something ... +} +``` + +أһһIJһ㲻˹IJбţҽӿʱܻҪϰ汾ݡʵǶôأøDTOװһЩ~£ + +``` +public void getUserInfoUserInfoParamDTO userInfoParamDTO){ + // do something ... +} + +class UserInfoParamDTO{ + private String name; + private String age; + private String sex; + private String mobile; +} +``` +øDTOװһ£ʹв䶯ҲԲöӿˣôܸܵġ + +### 9. ʹûIO + +**** + +``` +/** + * ںţݵСк + * @desc: һͼƬļ + */ +public class MainTest { + public static void main(String[] args) throws FileNotFoundException { + long begin = System.currentTimeMillis(); + try (FileInputStream input = new FileInputStream("C:/456.png"); + FileOutputStream output = new FileOutputStream("C:/789.png")) { + byte[] bytes = new byte[1024]; + int i; + while ((i = input.read(bytes)) != -1) { + output.write(bytes,0,i); + } + } catch (IOException e) { + log.error("ļ쳣",e); + } + log.info("дܹʱms"+(System.currentTimeMillis() - begin)); + } +} +``` + +н + +``` +дܹʱms:52 +``` + + + +ʹ```FileInputStream``````FileOutputStream```ʵļдܣûʲôġأʹû```BufferedReader``````BufferedWriter``````BufferedInputStream``````BufferedOutputStream```ȣIO߶дЧʡ +> DzȡһֽڻַģͻֱˡȡһֽڻַʱȲǵȴﵽһ + +**** + + +``` +/** + * ںţݵСк + * @desc: һͼƬļ + */ +public class MainTest { + public static void main(String[] args) throws FileNotFoundException { + long begin = System.currentTimeMillis(); + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C:/456.png")); + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("C:/789.png"))) { + byte[] bytes = new byte[1024]; + int i; + while ((i = input.read(bytes)) != -1) { + output.write(bytes,0,i); + } + } catch (IOException e) { + log.error("ļ쳣",e); + } + log.info("ܹʱms"+(System.currentTimeMillis() - begin)); + } +} +``` +н + +``` +дܹʱms:12 +``` + +### 10. Żij߼ǰѾ鵽ݣںķҲõĻǿ԰´εģٷ/ + +**** +``` +public Response dealRequest(Request request){ + + UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); + if(Objects.isNull(request)){ + return ; + } + + insertUserVip(request.getUserId); + +} + +private int insertUserVipString userId{ + //ֲһ + UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); + //ûvipˮ + insertUserVipFlow(userInfo); + .... +} + +``` + +Ȼϳ룬Ѿ鵽 userInfoȻְuserIdȥֲһΡʵϣ԰userInfoȥģʡȥһβЧ + +**** + +``` +public Response dealRequest(Request request){ + + UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); + if(Objects.isNull(request)){ + return ; + } + + insertUserVip(userInfo); +} + +private int insertUserVipUserInfo userInfo{ + //ûvipˮ + insertUserVipFlow(userInfo); + .... +} +``` + + +### 11. ҪΪ˷㣬ֱڴʹ0,1ħֵӦҪenumöٴ档 + + +**** + +``` +if("0".equals(userInfo.getVipFlag)){ + //ǻԱʾȥͨԱ + tipOpenVip(userInfo); +}else if("1".equals(userInfo.getVipFlag)){ + //Աѫ· + addMedaluserInfo; +} +``` + +**** + + +``` +if(UserVipEnum.NOT_VIP.getCode.equals(userInfo.getVipFlag)){ + //ǻԱʾȥͨԱ + tipOpenVip(userInfo); +}else if(UserVipEnum.VIP.getCode.equals(userInfo.getVipFlag)){ + //Աѫ· + addMedaluserInfo; +} + +public enum UserVipEnum { + + VIP("1","Ա"), + NOT_VIP("0","ǻԱ"),:; + + private String code; + private String desc; + + UserVipEnum(String code, String desc) { + this.code = code; + this.desc = desc; + } +} +``` + +дʱ򣬲Ҫһʱ𣬾ֱʹħֵʹħֵάܵġ + +### 12. ԱֵıʱȶΪ̬ + +**** + + +``` +public class Task { + private final long timeout = 10L; + ... +} +``` + +**** + +``` +public class Task { + private static final long TIMEOUT = 10L; + ... +} +``` + +> ΪΪstaticྲ̬ÿʵУֻһݸdzԱÿʵУһݸȻĻΪ̬һЩ + +### 13. עָ룬Ҫҵ˵߼ijΪա + +NullPointerException ճзdzǴ뿪УһҪԿָ뱣 + +Ҫ⼸ָ⣺ + +- װ͵Ŀָ +- õĿָ +- EqualsߵĿָ +- ConcurrentHashMap ֧ k-vΪ null +- ϣֱӻȡԪ +- ֱӻȡ + +**** +``` +public class NullPointTest { + public static void main(String[] args) { + String s = null; + if (s.equals("666")) { //sΪգᵼ¿ָ + System.out.println("ںţݵСкɻ"); + } + } +} + +``` + +### 14񵽵쳣ܺٴ־ + +**** + +``` +public static void testIgnoreException() throws Exception { + try { + // + } catch (Exception e) { + //쳣ɶ鲻־Ҳ򣿣 + } +} + +``` + +**** + +``` +public static void testIgnoreException() { + try { + // + } catch (Exception e) { + log.error("쳣ˣϵС翴",e); + } +} +``` + +### 15. Lambdaʽ滻ڲ࣬ʹ + +JDK8-LambdaʽLambdaʽڲţڴУDzinvokeDynamicָʵ֣ڲ࣬ЧҲ + + +**** +``` + public void sortUserInfoList(List userInfoList){ + userInfoList.sort(new Comparator() { + @Override + public int compare(UserInfo user1, UserInfo user2) { + Long userId1 = user1.getUserId(); + Long userId2 = user2.getUserId(); + return userId1.compareTo(userId2); + }}); + } +``` + +**** + +``` + public void sortUserInfoList(List userInfoList){ + userInfoList.sort((user1, user2) -> { + Long userId1 = user1.getUserId(); + Long userId2 = user2.getUserId(); + return userId1.compareTo(userId2); + }); + } +``` + +### 16. ֪ͨࣨ緢ʼжţĴ룬첽 + +ҵҪû½ʱӸ֪ͨķ˿ 뵽ʵ£ + + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed6daf4932b34452b1aacdb98aa2dcc8~tplv-k3u1fbpfcp-watermark.image) + +ṩsendMsgNotifyϵͳˣߵsendMsgNotifyʧˣôû½ʧˡ +һ֪ͨܵ˵½̲ãԵļ֥鶪ϡôûƼõķأеģŽӿڲ쳣߳첽£ + + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/58a956dc04c6456395b39d02afe3187e~tplv-k3u1fbpfcp-watermark.image) + +ˣ֪ͨȲǷҪɽĽӿʱӦþǷӰҪ̣˼ôá + +### 17. JavaʱYYYYʽõ⡣ + +ճУǾҪڡҪʱڸʽʱǴд```YYYY```Ŀӡ + + +``` +Calendar calendar = Calendar.getInstance(); +calendar.set(2019, Calendar.DECEMBER, 31); + +Date testDate = calendar.getTime(); + +SimpleDateFormat dtf = new SimpleDateFormat("YYYY-MM-dd"); +System.out.println("2019-12-31 ת YYYY-MM-dd ʽ " + dtf.format(testDate)); +``` +н + +``` +2019-12-31 ת YYYY-MM-dd ʽ 2020-12-31 +``` + +> Ϊʲô20191231ţתһ¸ʽͱ20201231ˣΪYYYYǻģָڵݣһܴտʼֻҪܿ꣬ôһܾһˡȷʹyyyyʽ + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2e3654c06cd4727b203ea5710cc8410~tplv-k3u1fbpfcp-watermark.image) + +### 18. һȷᱻ̳УAOPɧָfinalηfinalһࡣ + +**** +``` +public final class Tools { + public static void testFinal(){ + System.out.println("෽"); + } +} +``` + +һָfinalηᱻ̳ˣзfinalˡJavaһеfinalJavaЧʡ + + +### 19. static̬Ҫspringʵܻᵼ³ʼ + +֮ǰĿƵĴ롣̬springbean +``` + private static SmsService smsService = SpringContextUtils.getBean(SmsService.class); +``` + +̬smsServiceпܻȡģΪ˳ȷģϵĴ룬̬smsServiceʼǿspringʵˡȷд£ + + +``` + private static SmsService smsService =null; + + //ʹõʱȡȡ + public static SmsService getSmsService(){ + if(smsService==null){ + smsService = SpringContextUtils.getBean(SmsService.class); + } + return smsService; + } + +``` + +### 20. Ա޹صķӦɾ̬ + + + +ЩʵԱ޹أͿΪ̬һ㣬õúܶࡣ**** + +``` +/** + * BigDecimalĹ + */ +public class BigDecimalUtils { + + public BigDecimal ifNullSetZERO(BigDecimal in) { + return in != null in : BigDecimal.ZERO; + } + + public BigDecimal sum(BigDecimal ...in){ + BigDecimal result = BigDecimal.ZERO; + for (int i = 0; i < in.length; i++){ + result = result.add(ifNullSetZERO(in[i])); + } + return result; + } +``` + +ΪBigDecimalUtilsķûstaticΣԣҪʹõʱÿζҪnewһ,DzͺԴȥ**** + +``` +BigDecimalUtils bigDecimalUtils = new BigDecimalUtils; +bigDecimalUtils.sum(a,b); +``` +Կɾ̬ʹõʱֱ```.```üɣ£ + + +``` +/** + * BigDecimalĹ + */ +public class BigDecimalUtils { + + public static BigDecimal ifNullSetZERO(BigDecimal in) { + return in != null in : BigDecimal.ZERO; + } + + public static BigDecimal sum(BigDecimal ...in){ + BigDecimal result = BigDecimal.ZERO; + for (int i = 0; i < in.length; i++){ + result = result.add(ifNullSetZERO(in[i])); + } + return result; + } +``` + +### 21. ҪһException׽пܵ쳣 + +**:** + +``` + +public void test(){ + try{ + //׳ IOException Ĵ + //׳ SQLException Ĵ + }catch(Exception e){ + //û Exception ׽пܵ쳣ζ׽ᶪʧԭʼ쳣ЧϢŶ + log.info(Exception in test,exception:{}, e); + } +} + +``` + +**** + + +``` +public void test(){ + try{ + //׳ IOException Ĵ + //׳ SQLException Ĵ + }catch(IOException e){ + //׽ IOException + log.info(IOException in test,exception:{}, e); + }catch(SQLException e){ + //׽ SQLException + log.info(SQLException in test,exception:{}, e); + } +} +``` + +### 22. ҪȷװԼ༴ɡ + +**** + +``` +// װ +public static boolean isUserVip(Boolean isVip) { + return Boolean.TRUE.equals(isVip); +} + +// ʹô +boolean isVip = isVip(user.getUserVip()); +``` + +**** + + +``` +boolean isVip = Boolean.TRUE.equals(user.getUserVip()); +``` + +Ҫȷװ˼ɡңûջͳջĸCPUڴ棬ȷװܵģ + + +### 23. ijֵһᱻǣûбҪֵ + +**:** + +``` +List userList = new ArrayList<>(); +if (isAll) { + userList = userInfoDAO.queryAll(); +} else { + userList = userInfoDAO.queryActive(); +} + +``` +**** + + +``` +List userList ; +if (isAll) { + userList = userInfoDAO.queryAll(); +} else { + userList = userInfoDAO.queryActive(); +} + +``` + + +### 24.ֵҪʹBigDecimal + +Ӱɣ +``` +public class DoubleTest { + public static void main(String[] args) { + System.out.println(0.1+0.2); + System.out.println(1.0-0.8); + System.out.println(4.015*100); + System.out.println(123.3/100); + + double amount1 = 3.15; + double amount2 = 2.10; + if (amount1 - amount2 == 1.05){ + System.out.println("OK"); + } + } +} +``` +н + +``` +0.30000000000000004 +0.19999999999999996 +401.49999999999994 +1.2329999999999999 +``` +> ΪԶƴ洢ֵģڸҲǡڼԣ0.1޷ȷΪʲôᵼ¾ȷȱʧġˣ㣬һ㶼BigDecimal + + +``` +System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2))); +//output: +0.3000000000000000166533453693773481063544750213623046875 +``` +ʵʹ BigDecimal ʾͼ㸡ʹַĹ췽ʼ BigDecimalңҪעBigDecimalļλС㣬аģʽ + +### 25. עArrays.asListļ + +- **ͲΪ Arrays.asListIJᱻһ** + +``` +public class ArrayAsListTest { + public static void main(String[] args) { + int[] array = {1, 2, 3}; + List list = Arrays.asList(array); + System.out.println(list.size()); + } +} +//н +1 +``` +- **Arrays.asList ص List ֧ɾ** + + +``` +public class ArrayAsListTest { + public static void main(String[] args) { + String[] array = {"1", "2", "3"}; + List list = Arrays.asList(array); + list.add("5"); + System.out.println(list.size()); + } +} + +// н +Exception in thread "main" java.lang.UnsupportedOperationException + at java.util.AbstractList.add(AbstractList.java:148) + at java.util.AbstractList.add(AbstractList.java:108) + at object.ArrayAsListTest.main(ArrayAsListTest.java:11) + +``` + Arrays.asList ص List java.util.ArrayList Arrays ڲArrayListڲArrayListûʵaddǸaddʵ֣ǻ׳쳣ء + +- **ʹArrays.asLisʱ򣬶ԭʼ޸ĻӰ쵽ǻõǸList** + +``` +public class ArrayAsListTest { + public static void main(String[] args) { + String[] arr = {"1", "2", "3"}; + List list = Arrays.asList(arr); + arr[1] = "4"; + System.out.println("ԭʼ"+Arrays.toString(arr)); + System.out.println("list" + list); + } +} + +//н +ԭʼ[1, 4, 3] +list[1, 4, 3] +``` + +### 26ʱرIOԴ + +ӦôҶйľwindowsϵͳ̫ļϵͳͻõԺܿȻlinuxҲһƽʱļݿӣIOԴûرգôIOԴͻᱻռţ˾ûа취ˣԴ˷ѡ + + +ʹIOǵùرչʹtry-with-resourceرյģ +``` +/* + * עںţݵСк + */ +try (FileInputStream inputStream = new FileInputStream(new File("jay.txt")) { + // use resources +} catch (FileNotFoundException e) { + log.error(e); +} catch (IOException e) { + log.error(e); +} +``` + +### 27. ʹúڵĻʱ + +> - ڷڣͲԼʱDZջеģٶȱȽϿ졣 +> - ͵IJʱöջУݶڶУٶȽ +> - Уκ͵ijԱڶѣHeapУٶȽ + +``` +public class AccumulatorUtil { + + private double result = 0.0D; + // + public void addAllOne( double[] values) { + for(double value : values) { + result += value; + } + } + //ڷһֲʱۼٸֵijԱ + public void addAll1Two(double[] values) { + double sum = 0.0D; + for(double value : values) { + sum += value; + } + result += sum; + } +} + +``` + +### 28. ݿһβѯ࣬ҳ + +SqlһԲȽ϶࣬ҳ + + +**** +``` + +select user_id,name,age from user_info ; + +``` + +**** + +``` +select user_id,name,age from user_info limit #{offset},#{pageSize}; +``` + +ƫرʱ򣬲ѯЧʾͱõ¡ַʽŻ + +``` +//һ ϴβѯ¼(ƫ) +select idname from user_info where id>10000 limit #{pageSize}. + +//order by + +select idname from user_info order by id limit #{offset},#{pageSize} + +//ҵҳ + +``` + +### 29. ٶԱظ + +һдʱ򣬻µķʽʵֱ +``` +for (int i = 0; i < list.size; i++){ + +} +``` +listȽСǻálistȽϴʱŻ +``` +for (int i = 0, int length = list.size; i < length; i++){ + +} +``` + +ɣ +- Էĵãʹֻһ䣬Ҳĵģ紴ջ֡listȽϴʱεlist.sizeҲǻԴĵġ + + +### 30. ޸ĶϽӿڵʱ˼ӿڵļԡ + +ܶbugΪ޸˶ϽӿڣȴݵµġؼDZȽصģֱӵϵͳʧܵġֳԱ׾ͷŶ~ + +ԣԭӿ޸ģӿǶṩĻһҪǽӿڼݡٸӰɣdubboӿڣԭֻABһCͿԿ + +``` +//Ͻӿ +void oldService(A,B);{ + //½ӿڣnullC + newService(A,B,null); +} + +//½ӿڣʱɾϽӿڣҪݡ +void newService(A,B,C); +``` + + +### 31 ȡʩʱ߽ȣ + +ճУҪȡʩָ߽ʱ + + +ƴȽϳ: + +``` +String name = list.get(1).getName(); //listԽ磬Ϊһ2Ԫع +``` + +ԣӦòȡʩԤһ߽**** + +``` +if(CollectionsUtil.isNotEmpty(list)&& list.size()>1){ + String name = list.get(1).getName(); +} +``` + + +### 32. ע ArrayList.toArray() ǿתĿ + +``` +public class ArrayListTest { + public static void main(String[] args) { + List list = new ArrayList(1); + list.add("ںţݵСк"); + String[] array21 = (String[])list.toArray();//ת쳣 + } +} + +``` + +ΪصObjectͣObjectǿתString飬ᷢClassCastExceptionǣʹtoArray()طtoArray(T[] a) + +``` +String[] array1 = list.toArray(new String[0]);// +``` + + +### 33. ѭԶ̵áݿȿС + +̲ݿDZȽϺ硢IOԴģԾѭԶ̵áѭݿ⣬һԲҪѭȥ顣أҲҪһԲ̫ݹҪ500һνϣ + + +**** +``` +remoteBatchQuery(param); +``` + +**** + +``` +for(int i=0;i userInfoList) { + for (int i = 0; i < userInfoList.size(); i++) { + //ظuserList.size() + } + } +``` + +**** + + +``` + public static void listDetail(List userInfoList) { + int length = userInfoList.size(); + for (int i = 0; i < length; i++) { + //ٵuserList.size()ֻlengthһΡ + } + } +``` + +### 37ֱӴļһԴݿȡ̫ݵڴ棬ܵOOM + +һ԰Ѵļݿ̫ݴﵽڴ棬ǻᵼOOMġԣΪʲôѯDBݿ⣬һ㶼 + +ȡļĻһļ̫󣬲ʹFiles.readAllLines()ΪʲôأΪֱӰļڴģԤ²OOMʹɣԿԴ룺 + + +``` +public static List readAllLines(Path path, Charset cs) throws IOException { + try (BufferedReader reader = newBufferedReader(path, cs)) { + List result = new ArrayList<>(); + for (;;) { + String line = reader.readLine(); + if (line == null) + break; + result.add(line); + } + return result; + } +} +``` +̫ļʹFiles.line()ȡʱȡļЩһʹҪرԴĹ + +### 38. õӿڣҪ쳣ȫԣʱ⼸㡣 + +ճУҪõ񣬻߷ֲʽԶ̷ĵĻҪǣ + + +- 쳣磬˵Ľӿڣ쳣ˣôԻǵʧܣ +- ʱûԤԷӿһ÷أһøʱϿʱ䣬ԱĽӿڣ +- ԴĽӿڵʧܣ費ҪԣҪվҵϽǶ˼⣩ + +> һӣһhttp˵ķҪconnect-timeretry + + +### 39 ҪʹѭϣʹJDKṩķ + +> - JDKṩԭAPIֱָϵܡ +> - ЩĵײSystem.arraycopyʵ֣ݵЧʸߡ + +**** + +``` +public List copyMergeList(List user1List, List user2List) { + List userList = new ArrayList<>(user1List.size() + user2List.size()); + for (UserInfo user : user1List) { + userList.add(user); + } + for (UserInfo user : user2List) { + userList.add(user); + } + + return user1List; + } +``` + +**** + +``` +public List copyMergeList(List user1List, List user2List) { + List userList = new ArrayList<>(user1List.size() + user2List.size()); + userList.addAll(user1List); + userList.addAll(user2List); + return user1List; + } +``` + +### 40. ڸӵĴ߼ע + +дʱûбҪд̫ע͵ģõķõע͡ǣҵ߼ܸӵĴ룬ķdzбҪдע͡עͣںά + + +### 41. ߳£԰ȫ + +ڸ߲£HashMapܻѭΪǷ԰ȫģԿʹConcurrentHashMap ҲϰߣҪ־һnew HashMap(); + +- HashmapArraylistLinkedListTreeMapȶԲȫģ +- VectorHashtableConcurrentHashMapȶ԰ȫ + +### 42. ʹspringʱע⼸δЧĿ + +ճҵ񿪷УǾ򽻵ʧЧҪ¼ + +- ײݿ治֧ +- ڷpublicεķʹ +- rollbackForô +- ෽ֱӵ +- 쳣try...catchˣʧЧ + +**** + + +``` +public class TransactionTest{ + public void A(){ + //һ + //÷B (صãʧЧ) + B(); + } + + @Transactional + public void B(){ + // + } +} +``` + +**ע񷽷෽ֱӵãʧЧ** + + +### 43. ʹExecutors̳߳أnewFixedThreadPoolOOM + + +``` + ExecutorService executor = Executors.newFixedThreadPool(10); + for (int i = 0; i < Integer.MAX_VALUE; i++) { + executor.execute(() -> { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + //do nothing + } + }); + } + +``` +IDEָJVM-Xmx8m -Xms8m : + +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b7154077d1074b3c8e1f5670e754adfd~tplv-k3u1fbpfcp-watermark.image) + +н + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa3de03ca1924787aa1775b0a94890b4~tplv-k3u1fbpfcp-watermark.image) + +ǿԴ룬ʵnewFixedThreadPoolʹõ޽У + + +``` +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); +} + +public class LinkedBlockingQueue extends AbstractQueue + implements BlockingQueue, java.io.Serializable { + ... + /** + * Creates a {@code LinkedBlockingQueue} with a capacity of + * {@link Integer#MAX_VALUE}. + */ + public LinkedBlockingQueue() { + this(Integer.MAX_VALUE); + } +... +} + +``` + +> newFixedThreadPool̳߳صĺ߳ǹ̶ģʹ˽޽LinkedBlockingQueueС߳ӵУִеʱȽϳûͷţᵼԽԽѻУ»ڴʹòͣJVM OOM + + +### 44. catchס쳣󣬾Ҫʹe.printStackTrace(),ʹlogӡ + +**** + +``` +try{ + // do what you want +}catch(Exception e){ + e.printStackTrace(); +} +``` + +**** + +``` +try{ + // do what you want +}catch(Exception e){ + log.info("ij쳣",e); +} +``` + +### 45. ӿҪݵ + +ӿҪݵԵģתЩҪӿڡֱ۵ҵ񳡾ûŵΣĽӿûholdס + +һݵȼ⼸: + +- ѯ +- Ψһ +- tokenƣֹظύ +- ݿdelete/update +- ֹ +- +- Rediszookeeper ֲʽǰRedisֲʽ +- ״̬ݵ + + +### 46. Ƚ϶ĺ黮Сǿɶԡ + +**** +``` +public class Test { + private String name; + private Vector orders = new Vector(); + + public void printOwing() { + //print banner + System.out.println("****************"); + System.out.println("*****customer Owes *****"); + System.out.println("****************"); + + //calculate totalAmount + Enumeration env = orders.elements(); + double totalAmount = 0.0; + while (env.hasMoreElements()) { + Order order = (Order) env.nextElement(); + totalAmount += order.getAmout(); + } + + //print details + System.out.println("name:" + name); + System.out.println("amount:" + totalAmount); + } +} + +``` + +**** + + +``` +public class Test { + private String name; + private Vector orders = new Vector(); + + public void printOwing() { + + //print banner + printBanner(); + //calculate totalAmount + double totalAmount = getTotalAmount(); + //print details + printDetail(totalAmount); + } + + void printBanner(){ + System.out.println("****************"); + System.out.println("*****customer Owes *****"); + System.out.println("****************"); + } + + double getTotalAmount(){ + Enumeration env = orders.elements(); + double totalAmount = 0.0; + while (env.hasMoreElements()) { + Order order = (Order) env.nextElement(); + totalAmount += order.getAmout(); + } + return totalAmount; + } + + void printDetail(double totalAmount){ + System.out.println("name:" + name); + System.out.println("amount:" + totalAmount); + } + +} + +``` + +һ߳ĺһҪעͲ;Ĵ룬ԿǰзֳһȷĺԪ̵ĺôøš + + +### 47. Ĺؼҵ룬һ㽨־ݻ + +ؼҵεأӦ㹻־ݻ + +> 磺ʵתҵתȻתʧˣſͻͶߣȻ㻹ûдӡ־ˮȵ£ȴް취 + +ôתҵҪЩ־Ϣأ٣ǰҪӡҪɣӿڵúҪһ쳣ɣͬʱӡ쳣־ɣ£ + +``` +public void transfer(TransferDTO transferDTO){ + log.info("invoke tranfer begin"); + //ӡ + log.info("invoke tranfer,paramters:{}",transferDTO); + try { + res= transferService.transfer(transferDTO); + }catch(Exception e){ + log.error("transfer fail,cifno:{}account{}",transferDTO.getCifno + transferDTO.getaccount) + log.error("transfer fail,exception:{}",e); + } + log.info("invoke tranfer end"); + } +``` + +˴ӡ㹻־ǻҪעһǣ־ʹã𱾸ôӡinfo־ȴӡerror𣬸澯ҹŲͲˡ + +### 48. ijЩɱأƤȵȣûǷء + +Ʒ˸ʥڵʱ򣬺ƤΪʥصģڵʱ򣬺Ƥȡ + +: + +``` +if(duringChristmas){ + img = redPacketChristmasSkin; +}else if(duringSpringFestival){ + img = redSpringFestivalSkin; +} +``` + +ԪڵʱӪСͻȻ뷨ƤɵصģʱDzҪȥ޸Ĵˣ·ˣһʼʵһźƤñƤûأƤֻ޸һ±ݾͺˡ + +### 49.ֱӵҪʹõļ,ڶ + +ֱӵҪʹõļϣͨȡݣȽϵ;Mapĵ + + +**** +``` +Map userMap = ...; +for (Long userId : userMap.keySet()) { + UserDO user = userMap.get(userId); + ... +} +``` + +**** + + +``` +Map userMap = ...; +for (Map.Entry userEntry : userMap.entrySet()) { + Long userId = userEntry.getKey(); + UserDO user = userEntry.getValue(); + ... +} +``` + +### 50. ģʽ+Żif else + + + + +``` + String medalType = "guest"; + if ("guest".equals(medalType)) { + System.out.println("αѫ"); + } else if ("vip".equals(medalType)) { + System.out.println("Աѫ"); + } else if ("guard".equals(medalType)) { + System.out.println("չʾػѫ"); + } + ... + +``` + +ȣǰÿ߼飬һĽӿڣǸÿ߼ӦIJʵ࣬ɵ´룺 + + +``` +//ѫ½ӿ +public interface IMedalService { + void showMedal(); +} + +//ػѫ²ʵ +public class GuardMedalServiceImpl implements IMedalService { + @Override + public void showMedal() { + System.out.println("չʾػѫ"); + } +} +//αѫ²ʵ +public class GuestMedalServiceImpl implements IMedalService { + @Override + public void showMedal() { + System.out.println("αѫ"); + } +} +//VIPѫ²ʵ +public class VipMedalServiceImpl implements IMedalService { + @Override + public void showMedal() { + System.out.println("Աѫ"); + } +} + +``` + +ٶԹ࣬Щѫʵֲ࣬£ + + +``` +//ѫ·񹤲 +public class MedalServicesFactory { + + private static final Map map = new HashMap<>(); + static { + map.put("guard", new GuardMedalServiceImpl()); + map.put("vip", new VipMedalServiceImpl()); + map.put("guest", new GuestMedalServiceImpl()); + } + public static IMedalService getMedalService(String medalType) { + return map.get(medalType); + } +} + +``` + +Ż£ + +``` +ublic class Test { + public static void main(String[] args) { + String medalType = "guest"; + IMedalService medalService = MedalServicesFactory.getMedalService(medalType); + medalService.showMedal(); + } +} +``` + diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md" new file mode 100644 index 0000000..4577332 --- /dev/null +++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/\346\227\245\345\277\227\346\211\223\345\215\260\350\247\204\350\214\203.md" @@ -0,0 +1,269 @@ +## ǰ + +Һã**ݵСк**־ǿٶλĺð֣**˺ƺ˦**ӡ־dzҪ**־ӡ**15ý~ + +- ںţ**ݵСк** + + +## 1. ѡǡ־ + +־5ֱ֣errorwarninfodebugtraceճУҪѡǡ־𣬲Ҫ־Ǵӡinfo~ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4502943568747e79ac4091b4d2868ea~tplv-k3u1fbpfcp-zoom-1.image) + +- error־ָȽصĴ󣬶ҵӰ죬Ҫ**άüص** +- warn־һĴ󣬶ҵӰ첻󣬵Ҫ**ע** +- infoϢ־¼ŲĹؼϢʱ䡢εȵȣ +- debugڿDEBUGģؼ߼ʱݣ +- traceϸϢһЩϢֻ¼־ļС + + +## 2. ־ҪӡΡ + +DzҪӡܶܶ־ֻҪӡ**ٶλЧ־**Ч־˦ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c59f66bfc09d42ffa8528c16145952b8~tplv-k3u1fbpfcp-zoom-1.image) + +Щõ**Чؼ**־أ˵ʱ򣬴ӡ****Ȼأڷصʱ򣬾**ӡΣֵ**εĻһ**userIdbizSeqЩؼ**Ϣ£ + +``` +public String testLogMethod(Document doc, Mode mode){ + log.debug(method enter param{},userId); + String id = "666"; + log.debug(method exit param{},id); + return id; +} +``` + + +## 3. ѡʵ־ʽ + +־ʽӦЩϢ統**ǰʱ**һ뾫ȷȣ**־****߳**ȵȡlogback־ôã + +``` + + + %d{HH:mm:ss.SSS} %-5level [%thread][%logger{0}] %m%n + + +``` + +ǵ־ʽǰʱ䶼]м¼**ʱ㶼֪** + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6ecfebbe1a0b411e8186d46edaddbd38~tplv-k3u1fbpfcp-zoom-1.image) + + +## 4. if...else...ʱÿ֧жӡ־ + +**if...else...switch**ʱڷ֧оʹӡ־ŲʱͿͨ־ȷĸ֧߼ҲŲˡ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/60bc12dfb6324c089b86a9dc05acc2f3~tplv-k3u1fbpfcp-zoom-1.image) + + + +``` +if(user.isVip()){ + log.info("ûǻԱ,Id:{},ʼԱ߼",user,getUserId()); + //Ա߼ +}else{ + log.info("ûǷǻԱ,Id:{},ʼǻԱ߼",user,getUserId()) + //ǻԱ߼ +} +``` + +## 5.־Ƚϵʱ־ж + +trace/debugЩȽϵ͵־𣬱־Ŀжϡ + + +``` +User user = new User(666L, "ں", "ݵСк"); +if (log.isDebugEnabled()) { + log.debug("userId is: {}", user.getId()); +} +``` + +Ϊǰµ־룺 +``` +logger.debug("Processing trade with id: " + id + " and symbol: " + symbol); +``` + +**õ־warn**Ļ־ӡǻִַƴӲ```symbol```Ƕ +ִ```toString()```˷ϵͳԴִ־ȴûдӡ˽**־жϡ** + +## 6. ֱʹ־ϵͳLog4jLogbackе APIʹ־SLF4JеAPI + +SLF4J ģʽ־ܣά͸־ʽͳһҿڱ֤޸Ĵ£ܷʵֵײ־ܵĸ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9846b8d9ddd2485483e41b7134954f91~tplv-k3u1fbpfcp-zoom-1.image) + + +``` +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +private static final Logger logger = LoggerFactory.getLogger(TianLuoBoy.class); +``` + +## 7. ʹòռλ{}+ƴӡ + + +``` +logger.info("Processing trade with id: " + id + " and symbol: " + symbol); +``` + +Уʹ```+```ַƴӣһ**** + +£ +``` +logger.info("Processing trade with id: {} and symbol : {} ", id, symbol); +``` +ʹ˴```{}```Ϊ־еռλʹ```+```żࡣң**ڷ**ʹռλ滻Чܡ + +## 8. ʹ첽ķʽ־ + +- ־ջļеģIOܻҪġ첽ͿIOܡ +- ҪҪȻʹ첽ķʽ־logbackΪɣҪ첽ܼ򵥣ʹAsyncAppender +``` + + + +``` + +## 9. Ҫʹe.printStackTrace() + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e2aeaca5e0f44c08ad25fb92e75222dc~tplv-k3u1fbpfcp-zoom-1.image) + + + +``` +try{ + // ҵ봦 +}catch(Exception e){ + e.printStackTrace(); +} +``` + +``` +try{ + // ҵ봦 +}catch(Exception e){ + log.error("ij쳣",e); +} +``` + +**ɣ** + +- e.printStackTrace()ӡĶջ־ҵ־ǽһģͨŲ쳣־̫㡣 +- e.printStackTrace()ַ¼ǶջϢϢַ̫̫࣬ڵڴûпռ,ڴˣôûͿס~ + +## 10. 쳣־Ҫֻһ룬ҪȫϢ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/420f0da65bac44b5b340a96facfd5c11~tplv-k3u1fbpfcp-zoom-1.image) + +1 + +``` +try { + //ҵ봦 +} catch (Exception e) { + // + LOG.error('ij쳣'); +} + +``` +- 쳣eûдӡѹ֪ʲô͵쳣 + +2 +``` +try { + //ҵ봦 +} catch (Exception e) { + // + LOG.error('ij쳣', e.getMessage()); +} +``` + +- ```e.getMessage()```¼ϸĶջ쳣Ϣֻ¼ϢŲ⡣ + + + +``` +try { + //ҵ봦 +} catch (Exception e) { + // + LOG.error('ij쳣', e); +} +``` + +## 11. ֹϻ debug + +ֹϻdebugһdzҪ + + +Ϊһϵͳdebug־ܶ࣬ҸֿҲʹ debug־Ͽdebugÿܻ̣ӰҵϵͳС + +## 12.Ҫ¼쳣׳쳣 + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/671289ecaa4b4eb39dca1139b657f8cd~tplv-k3u1fbpfcp-zoom-1.image) + + +£ +``` +log.error("IO exception", e); +throw new MyException(e); +``` + +- ʵֵĻͨջϢӡΡΪMyException쳣ĵطٴӡһΡ +- ־¼߰װ׳ȥҪͬʱʹã־˺Ի + + +## 13.ظӡ־ + +ظӡ־ϻ˷Ѵ̿ռ䡣Ѿһ־˼**ӡ**£ + +``` +if(user.isVip()){ + log.info("ûǻԱ,Id:{}",user,getUserId()); + //࣬Ըǰ־ϲһ + log.info("ʼԱ߼,id:{}",user,getUserId()); + //Ա߼ +}else{ + //ǻԱ߼ +} +``` + +ʹlog4j־ܣ```log4j.xml``` additivity=falseΪԱظӡ־ + + +``` + +``` + +## 14.־ļ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/12a2cbe6cf7f4ba981ba7103f9d81858~tplv-k3u1fbpfcp-zoom-1.image) + + +- ǿ԰Ѳͬ͵־ȥaccess.logerrorerror.logԵӡһļ档 +- ȻҲԸݲͬҵģ飬ӡͬ־ļŲͳƵʱ򣬶ȽϷ + + +## 15. Ĺģ飬ӡ־ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6f26259cda042edb98400145d208d12~tplv-k3u1fbpfcp-zoom-1.image) + + +- ճУĻ߼ӵĴ룬ϸעͣԼϸ־ +- ־ҪϸأԶһ£ĺijһˣͨ־ԶλǾͿ + + + + + + diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\237\272\347\241\200\357\274\232\345\246\202\344\275\225\345\256\211\345\205\250\344\274\240\350\276\223\345\255\230\345\202\250\347\224\250\346\210\267\345\257\206\347\240\201.md" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\237\272\347\241\200\357\274\232\345\246\202\344\275\225\345\256\211\345\205\250\344\274\240\350\276\223\345\255\230\345\202\250\347\224\250\346\210\267\345\257\206\347\240\201.md" new file mode 100644 index 0000000..978ae68 --- /dev/null +++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\237\272\347\241\200\357\274\232\345\246\202\344\275\225\345\256\211\345\205\250\344\274\240\350\276\223\345\255\230\345\202\250\347\224\250\346\210\267\345\257\206\347\240\201.md" @@ -0,0 +1,123 @@ +

前言

+

我们开发网站或者APP的时候,首先要解决的问题,就是如何安全传输和存储用户的密码。一些大公司的用户数据库泄露事件也时有发生,带来非常大的负面影响。因此,如何安全传输存储用户密码,是每位程序员必备的基础。本文将跟大家一起学习,如何安全传输存储用户的密码。

+
+

公众号:捡田螺的小男孩(一起讨论密码传输存储问题)

+

1. 如何安全地传输用户的密码

+

要拒绝用户密码在网络上裸奔,我们很容易就想到使用https协议,那先来回顾下https相关知识吧~

+

1.1 https 协议

+
+
    +
  • http的三大风险
+

为什么要使用https协议呢?http它不香吗? 因为http是明文信息传输的。如果在茫茫的网络海洋,使用http协议,有以下三大风险:

+
+
    +
  • 窃听/嗅探风险:第三方可以截获通信数据。
  • 数据篡改风险:第三方获取到通信数据后,会进行恶意修改。
  • 身份伪造风险:第三方可以冒充他人身份参与通信。
+
+

如果传输不重要的信息还好,但是传输用户密码这些敏感信息,那可不得了。所以一般都要使用https协议传输用户密码信息。

+
    +
  • https 原理
+

https原理是什么呢?为什么它能解决http的三大风险呢?

+
+

https = http + SSL/TLS, SSL/TLS 是传输层加密协议,它提供内容加密、身份认证、数据完整性校验,以解决数据传输的安全性问题。

+
+

为了加深https原理的理解,我们一起复习一下一次完整https的请求流程吧~

+
+
+
    +
    1. +
    2. 客户端发起https请求
    +
    1. +
    2. 服务器必须要有一套数字证书,可以自己制作,也可以向权威机构申请。这套证书其实就是一对公私钥。
    +
    1. +
    2. 服务器将自己的数字证书(含有公钥、证书的颁发机构等)发送给客户端。
    +
    1. +
    2. 客户端收到服务器端的数字证书之后,会对其进行验证,主要验证公钥是否有效,比如颁发机构,过期时间等等。如果不通过,则弹出警告框。如果证书没问题,则生成一个密钥(对称加密算法的密钥,其实是一个随机值),并且用证书的公钥对这个随机值加密。
    +
    1. +
    2. 客户端会发起https中的第二个请求,将加密之后的客户端密钥(随机值)发送给服务器。
    +
    1. +
    2. 服务器接收到客户端发来的密钥之后,会用自己的私钥对其进行非对称解密,解密之后得到客户端密钥,然后用客户端密钥对返回数据进行对称加密,这样数据就变成了密文。
    +
    1. +
    2. 服务器将加密后的密文返回给客户端。
    +
    1. +
    2. 客户端收到服务器发返回的密文,用自己的密钥(客户端密钥)对其进行对称解密,得到服务器返回的数据。
    +
+
+
    +
  • https一定安全吗?
+

https的数据传输过程,数据都是密文的,那么,使用了https协议传输密码信息,一定是安全的吗?其实不然~

+
+
    +
  • 比如,https 完全就是建立在证书可信的基础上的呢。但是如果遇到中间人伪造证书,一旦客户端通过验证,安全性顿时就没了哦!平时各种钓鱼不可描述的网站,很可能就是黑客在诱导用户安装它们的伪造证书!
  • 通过伪造证书,https也是可能被抓包的哦。
+
+

1.2 对称加密算法

+

既然使用了https协议传输用户密码,还是不一定安全,那么,我们就给用户密码加密再传输呗~

+

加密算法有对称加密非对称加密两大类。用哪种类型的加密算法靠谱呢?

+
+

对称加密:加密和解密使用相同密钥的加密算法。 +

+
+

常用的对称加密算法主要有以下几种哈: +

+

如果使用对称加密算法,需要考虑密钥如何给到对方,如果密钥还是网络传输给对方,传输过程,被中间人拿到的话,也是有风险的哦。

+

1.3 非对称加密算法

+

再考虑一下非对称加密算法呢?

+
+

非对称加密: 非对称加密算法需要两个密钥(公开密钥和私有密钥)。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有对应的私钥才能解密。

+
+
+

常用的非对称加密算法主要有以下几种哈: +

+
+

如果使用非对称加密算法,也需要考虑密钥公钥如何给到对方,如果公钥还是网络传输给对方,传输过程,被中间人拿到的话,会有什么问题呢?他们是不是可以伪造公钥,把伪造的公钥给客户端,然后,用自己的私钥等公钥加密的数据过来? 大家可以思考下这个问题哈~

+
+

我们直接登录一下百度,抓下接口请求,验证一发大厂是怎么加密的。可以发现有获取公钥接口,如下:

+
+

再看下登录接口,发现就是RSA算法,RSA就是非对称加密算法。其实百度前端是用了JavaScript库jsencrypt,在github的star还挺多的。

+
+

因此,我们可以用https + 非对称加密算法(如RSA) 传输用户密码~

+

2. 如何安全地存储你的密码?

+

假设密码已经安全到达服务端啦,那么,如何存储用户的密码呢?一定不能明文存储密码到数据库哦!可以用哈希摘要算法加密密码,再保存到数据库。

+
+

哈希摘要算法: 只能从明文生成一个对应的哈希值,不能反过来根据哈希值得到对应的明文。

+
+

2.1 MD5摘要算法保护你的密码

+

MD5 是一种非常经典的哈希摘要算法,被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。但是仅仅使用 MD5 对密码进行摘要,并不安全。我们看个例子,如下:

+
public class MD5Test {
    public static void main(String[] args) {
        String password = "abc123456";
        System.out.println(DigestUtils.md5Hex(password));
    }
}
+

运行结果:

+
0659c7992e268962384eb17fafe88364
+

在MD5免费破解网站一输入,马上就可以看到原密码了。。。

+
+

试想一下,如果黑客构建一个超大的数据库,把所有20位数字以内的数字和字母组合的密码全部计算MD5哈希值出来,并且把密码和它们对应的哈希值存到里面去(这就是彩虹表)。在破解密码的时候,只需要查一下这个彩虹表就完事了。所以单单MD5对密码取哈希值存储,已经不安全啦~

+

2.2 MD5+盐摘要算法保护用户的密码

+

那么,为什么不试一下MD5+盐呢?什么是加盐

+
+

在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。

+
+

用户密码+盐之后,进行哈希散列,再保存到数据库。这样可以有效应对彩虹表破解法。但是呢,使用加盐,需要注意一下几点:

+
+
    +
  • 不能在代码中写死盐,且盐需要有一定的长度(盐写死太简单的话,黑客可能注册几个账号反推出来)
  • 每一个密码都有独立的盐,并且盐要长一点,比如超过 20 位。(盐太短,加上原始密码太短,容易破解)
  • 最好是随机的值,并且是全球唯一的,意味着全球不可能有现成的彩虹表给你用。
+
+

2.3 提升密码存储安全的利器登场,Bcrypt

+

即使是加了盐,密码仍有可能被暴力破解。因此,我们可以采取更慢一点的算法,让黑客破解密码付出更大的代价,甚至迫使他们放弃。提升密码存储安全的利器~Bcrypt,可以闪亮登场啦。

+
+

实际上,Spring Security 已经废弃了 MessageDigestPasswordEncoder,推荐使用BCryptPasswordEncoder,也就是BCrypt来进行密码哈希。BCrypt 生而为保存密码设计的算法,相比 MD5 要慢很多。

+
+

看个例子对比一下吧:

+
public class BCryptTest {

    public static void main(String[] args) {
        String password = "123456";
        long md5Begin = System.currentTimeMillis();
        DigestUtils.md5Hex(password);
        long md5End = System.currentTimeMillis();
        System.out.println("md5 time:"+(md5End - md5Begin));
        long bcrytBegin = System.currentTimeMillis();
        BCrypt.hashpw(password, BCrypt.gensalt(10));
        long bcrytEnd = System.currentTimeMillis();
        System.out.println("bcrypt Time:" + (bcrytEnd- bcrytBegin));
    }
}
+

运行结果:

+
md5 time:47
bcrypt Time:1597
+

粗略对比发现,BCrypt比MD5慢几十倍,黑客想暴力破解的话,就需要花费几十倍的代价。因此一般情况,建议使用Bcrypt来存储用户的密码

+

3. 总结

+
    +
  • 因此,一般使用https 协议 + 非对称加密算法(如RSA)来传输用户密码,为了更加安全,可以在前端构造一下随机因子哦。
  • 使用BCrypt + 盐存储用户密码。
  • 在感知到暴力破解危害的时候,开启短信验证、图形验证码、账号暂时锁定等防御机制来抵御暴力破解。
+

参考与感谢

+
    +
  • 如何正确保存和传输敏感数据? https://time.geekbang.org/column/article/239150[1]
  • 如何加密传输和存储用户密码 https://juejin.cn/post/6844903604944371726#heading-8[2]
+

公众号

+
    +
  • 公众号:捡田螺的小男孩
  • github地址:https://github.com/whx123/JavaHome
+ + +
+ \ No newline at end of file diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\237\272\347\241\200\357\274\232\345\246\202\344\275\225\345\256\211\345\205\250\344\274\240\350\276\223\345\255\230\345\202\250\347\224\250\346\210\267\345\257\206\347\240\201.md.bak" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\237\272\347\241\200\357\274\232\345\246\202\344\275\225\345\256\211\345\205\250\344\274\240\350\276\223\345\255\230\345\202\250\347\224\250\346\210\267\345\257\206\347\240\201.md.bak" new file mode 100644 index 0000000..e69de29 diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\350\201\212\350\201\212\346\227\245\345\270\270\345\274\200\345\217\221\344\270\255\357\274\214\345\246\202\344\275\225\345\207\217\345\260\221bug\345\221\242\357\274\237.md" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\350\201\212\350\201\212\346\227\245\345\270\270\345\274\200\345\217\221\344\270\255\357\274\214\345\246\202\344\275\225\345\207\217\345\260\221bug\345\221\242\357\274\237.md" new file mode 100644 index 0000000..0c47dce --- /dev/null +++ "b/\345\267\245\344\275\234\346\200\273\347\273\223/\350\201\212\350\201\212\346\227\245\345\270\270\345\274\200\345\217\221\344\270\255\357\274\214\345\246\202\344\275\225\345\207\217\345\260\221bug\345\221\242\357\274\237.md" @@ -0,0 +1,663 @@ +## ǰ + +Һѽ~ ǼݵСкճУμbugĽ**ݿ⡢桢ʹƪ**3ܽһ60ע㣬ҳΪ֮ǡ +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/41bd0d208d054f87a99f6c7df514f35d~tplv-k3u1fbpfcp-zoom-1.image) + +- ӭעںţ**ݵСк** +- [githubַ](https://github.com/whx123/JavaHome)лÿһstar + +## 1. ݿƪ + +![ѯ](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a05dccf50dc3403c8c665d3395cdcc91~tplv-k3u1fbpfcp-zoom-1.image) + +ݿƪĻЩط׵bugأܽ7棺**ѯݿֶע㡢ʧЧijӳ١ݼݡһЩSQLע** + +### 1.1 ѯ + +![ѯ.gif](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d38c462aa484b47b9fcfded5f4f2140~tplv-k3u1fbpfcp-watermark.image) + +#### 1.1.1 Ƿ +ѯϾͻ뵽һSQLûûĻͻѯ + +**ЩʧЧ** + +- ѯorܵʧЧ +- ֶַwhereʱһʧЧ +- likeͨܵʧЧ +- ѯʱвеĵһУʧЧ +- ʹmysqlúʧЧ +- 㣨磬+-*/ʧЧ +- ֶʹã= < >not inʱܻᵼʧЧ +- ֶʹis null is not nullܵʧЧ +- ӲѯӲѯѯֶαʽһܵʧЧ +- mysqlʹȫɨҪʹ,ʹ + + +#### 1.1.2 󣬿Ƿֱֿ + +̫󣬾ͻӰSQLִܡ֪ݽṹһB+һø߶Ϊ3B+ſԴ洢ǧݡĻB+Ҫߣѯܻ½ + +ˣʱ򣬽ֱֱֿֿм**mycatsharding-jdbc** + + +#### 1.1.3 SQL + +ճУ߼ܶ಻SQLһSQLȻ**6**,̫ӰѯܣٱһȻ**10**ȵȡǻή˲͸SQLܣһ㲻̫࣬һ㲻ܳ + + +### 1.2 ݿֶע + +ݿֶݣ׳bug磬Ի޸˱ṹijֶΣǰѽűǷ϶ˡ + +#### 1.2.1 ֶǷᳬ + +ݿֶǣ + +``` +`name` varchar(255) DEFAULT NOT NULL +``` + +˱nameֶγ300Dzʱ****ˡҪУֶֹγ + +#### 1.2.2 ֶΪգǷᵼ¿ָ + +ݿֶεʱ,ֶΪ**not null** + +- Σһʹ0-1ΪĬֵ +- ַĬϿַ + +ݿֶΪ```NULL```ֵ׵³ָ룻ݿֶΪ```NULL```ֵҪע**count()** ʹãпӡ + +#### 1.2.3 ֶȱʧ + +ǵճڲԻԱ޸ģһֶΣҪSQLűֶȱʧ + + +#### 1.2.4 ֶǷֱ֧ + +һֶҪֱ֧洢ʹ**utf8mb4** + +#### 1.2.5 ʹtextblobֶ + +Ҫһֶδ洢ļ**洢ļ·**ļȥʹtextʱ漰ѯʱעⴴ**ǰ׺** + +### 1.3 ʧЧij + +#### 1.3.1 @Transactional ڷpublicεķʧЧ + + +@Transactionalע⣬ڷpublicεķϣDzЧġspringǽAOP˼룬Ҳ̬ͨʵֵġspringԼڵö̬֮ǰѾԷpublicˣԷpublicЧ + +#### 1.3.2 طֱӵ + @TransactionalҲЧ +``` +public class TransactionTest{ + public void A(){ + //һ + //÷B (صãʧЧ) + B(); + } + + @Transactional + public void B(){ + // + } +} +``` + +#### 1.3.3 쳣try...catchˣʧЧ + + +``` +@Transactional +public void method(){ + try{ + //һ + insertA(); + //һ + updateB(); + }catch(Exception e){ + logger.error("쳣ˣʧЧ",e); + } +} + +``` + +#### 1.3.4 rollbackForô + +SpringĬ׳δ```unchecked```쳣̳RuntimeException 쳣ErrorŻع쳣ᴥع׳͵쳣Ҫָ```rollbackFor```ԡ + +#### 1.3.5 ײݿ治֧ + +MyISAM洢治֧InnoDb֧ + +#### 1.3.6 springҵ߼һ߳ + +ҵҪspringԴͬһ߳УŻspringĿơ룬motheḍ߳ڲִеmothedspringĿƣһҪע⡣ΪspringʵʹThreadLocalʵͬһ߳ݹ + +``` +@Transactional +public void mothed() { + new Thread() { + + }.start(); +} +``` + + +### 1.4 + +ָͬһԴ໥ռãԷԴӶ¶ѭ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3513a3fd3ef64e318f157dd51ec280ef~tplv-k3u1fbpfcp-zoom-1.image) + + +MySQLڲһƣһعһһִȥ**Դʽ̵͡òȷ**Σ + +#### 1.4.1 9SQL + +ҪҪѧһSQLļνе?һSQLԷ9֣̽ + +- һidRC뼶 +- ϶idǶΨһRC뼶 +- idǶΨһRC뼶 +- ģidûRC뼶 +- 壺idRR뼶 +- idǶΨһRR뼶 +- ߣidǶΨһRR뼶 +- ϰˣidûRR뼶 +- ϾţSerializable뼶 + + +#### 1.4.2 η + +IJ£ + +- ģ +- show engine innodb status;鿴־ +- ҳSQL +- SQLȥ +- ־ʲôȴʲô +- ϤģʽݾInnoDB洢ļԾ + +ȤС飬Կ֮ǰдƪ£[ְֽMysql](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487979&idx=1&sn=588c83d77a8851f3b3c18cd68ed9c454&chksm=cf21cec2f85647d4a77cc239ae9a4cfd31bb8832be3d98540a08ea8b4a1f46b38cf736210a02&token=1327808550&lang=zh_CN#rd) + + +### 1.5 ӳ⿼ + +Ȳ룬žȥѯ,߼Ƚϳܻġһݿⶼ⣬ӿġдĻд⣬һǶӿ⡣ӳ٣ܿܳɹˣѯ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/560555c31cfe42a0b07dd9bcad24c0e1~tplv-k3u1fbpfcp-zoom-1.image) + + +#### 1.5.1 ҪǿһԣǶ + +ҪҵҪǿһԣֱӶ + +#### 1.5.2 Ҫǿһԣӿ + +һҵ񣬿ԽܶݵݲһµĻȿǶӿ⡣ΪӿԷֵĶдѹϵͳܡ + +### 1.6 ݼ + +#### 1.6.1 ¼ӵֶΣǴݵĬֵ + +ճУҵҪijݿӸֶΡijAPPñҪӸֶΣ```scene_type```,öֵ ```010203```ǾҪҵ룬ӵֶΣʲôĬֵΪջĬ01Ϊ```NULL```ĻҪÿָ봦 + +#### 1.6.2 ҵϵֶΣݵֵǷп + +ǿУҪݿֶΣдݣǾҪϴݿֵǷпӡDZиuser_role_code ֶΣϵУöֵ ``` 01Ա 02Ա 03һû```ҵ**һû**Ϊ**03ѯû04û**ڿУҪݵ + +### 1.7 һЩSQLľע + +#### 1.7.1 limitҳ + +limitҳһdzSQL⣬һ3ֶӦĽ + +**һ** idģϴβѯ¼(ƫ)limit + + +``` +select id,name from employee where id>1000000 limit 10. +``` + +**:** ҵҳ + +ҵۣûбҪôķҳΪû̫ҳȸҳҲҳ˲limitҳ⡣ + +**** ӳٹӲѯŻҳȿٶλҪȡidΣȻٹ + +``` +SELECT a.* FROM employee a, (select id from employee where LIMIT 1000000,10 ) b where a.id=b.id +``` + +#### 1.7.2 ޸ġѯʱǷС + +Ǹ»߲ѯݿʱѭȥݿ⣬ԿǷСҪ10ݵĻһβ500 + +**** +``` +remoteBatchQuery(param); +``` +**** + +``` + +for(int i=0;i<100000;i++){ + remoteSingleQuery(param) +} +``` + + +## 2. ƪ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/819c4665e0064db5bb3441c5bee4cb7a~tplv-k3u1fbpfcp-zoom-1.image) + +### 2.1 ϸ + +![ϸ.gif](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aaa1fee8efa745d39a2ef869caa55b4f~tplv-k3u1fbpfcp-watermark.image) + +#### 2.1.1 Ϳָ + +DZʱҪע͵Ŀָ + +- װ͵Ŀָ +- õĿָ +- EqualsߵĿָ +- ConcurrentHashMap ֧ k-vΪ null +- ϣֱӻȡԪ +- ֱӻȡ + + +``` +if(object!=null){ + String name = object.getName(); +} +``` + +#### 2.1.2 ̳߳ʹע + +- ʹ Executors.newFixedThreadPoolܻOOM⣬Ϊʹõ޽ +- ʹԶ̳߳أø̳߳һŲ +- ͬҵ̳߳ظ룬еҵһ̳߳ء +- ̳߳쳣ҪǺ + +#### 2.1.3 ԰ȫļϡ + +ڸ߲£```HashMap```ܻѭΪǷ԰ȫģԿʹ```ConcurrentHashMap```ʹЩϵʱҪעDz԰ȫġ + +- HashmapArraylistLinkedListTreeMapȶԲȫģ +- VectorHashtableConcurrentHashMapȶ԰ȫ + +#### 2.1.4 ڸʽȵ + +ճҪڸʽأΪYYYYдʱпӵŶ + + +``` +Calendar calendar = Calendar.getInstance(); +calendar.set(2019, Calendar.DECEMBER, 31); + +Date testDate = calendar.getTime(); + +SimpleDateFormat dtf = new SimpleDateFormat("YYYY-MM-dd"); +System.out.println("2019-12-31 ת YYYY-MM-dd ʽ " + dtf.format(testDate)); +``` +н + +``` +2019-12-31 ת YYYY-MM-dd ʽ 2020-12-31 +``` + +нҲȽϳҪע⾫⣺ + + +``` +public class DoubleTest { + public static void main(String[] args) { + System.out.println(0.1+0.2); + System.out.println(1.0-0.8); + System.out.println(4.015*100); + System.out.println(123.3/100); + + double amount1 = 3.15; + double amount2 = 2.10; + if (amount1 - amount2 == 1.05){ + System.out.println("OK"); + } + } +} + +``` +н + + +``` +0.30000000000000004 +0.19999999999999996 +401.49999999999994 +1.2329999999999999 +``` + + + +#### 2.1.5 ļ + + +ȡļʱ򣬲Ҫ```Files.readAllBytes```ֱӶڴ棬OOMģʹ```BufferedReader ```һһʹ```NIO``` + + +#### 2.1.6 ʹIOԴҪر + + +ʹtry-with-resourceдļҪر + +``` +/* + * עںţݵСк + */ +try (FileInputStream inputStream = new FileInputStream(new File("jay.txt")) { + // use resources +} catch (FileNotFoundException e) { + log.error(e); +} catch (IOException e) { + log.error(e); +} +``` + + +#### 2.1.7 try...catch쳣ʹõһЩ + +- Ҫʹe.printStackTrace()ӡַܵڴռռ +- catch쳣ʹlogӡ +- ҪһException׽пܵ쳣 +- ҪѲ쳣ҵ߼ + + +#### 2.1.8 Ȳѯٸ/ɾIJһ + + +ճУִʵ־ɼȲѯǷʣõƱȥƱ + + +``` +if(selectIsAvailable(ticketId){ + 1deleteTicketById(ticketId) + 2ֽӲ +}else{ + return ûпֽȯ +} +``` + +DzִУܿģӦݿ/ɾԭԣ£ + +``` +if(deleteAvailableTicketById(ticketId) == 1){ + 1ֽӲ +}else{ + return ûпֽȯ +} +``` + + +### 2.2 ṩӿ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/818d79dfadab43e5a2bddf26a3ea2870~tplv-k3u1fbpfcp-zoom-1.image) + + +#### 2.2.1 УϷ + +ṩĽӿڣṩͻˡǰˣֻDZϵͳãҪУһεĺϷԡ + +> ݿֶΪvarchar(16),Էһ32λַ㲻Уȣݿֱ쳣ˡ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4f4649f1bb3b43418ef808b8c5599c76~tplv-k3u1fbpfcp-watermark.image) + +#### 2.2.2 Ͻӿڼ + +ܶbugΪ޸˶ϽӿڣȴݵµġؼDZȽصģֱӵϵͳʧܵġֳԱ׷Ŷ~ + +иdubboķֲʽӿڣ޸ΣҪϽӿڼݡԭֻABһCͿԿ + + +``` +//Ͻӿ +void oldService(A,B){ + //½ӿڣnullC + newService(A,B,null); +} + +//½ӿڣʱɾϽӿڣҪݡ +void newService(A,B,C); +``` + + + +#### 2.2.3 ֹѹϵͳ + +˲ĴѹϵͳΪ˱ǵϵͳһҪʹ**guava ratelimiter** Ҳð↑Դ**Sentinel** + +#### 2.2.4 ӿڰȫԣǩǩȨ + + +ת˵͵ĽӿڣһҪעⰲȫԡһҪȨ**ǩǩ**Ϊûױݻ + + +#### 2.2.5 ǽӿݵ + +ӿҪݵԵģתЩҪӿڡֱ۵ҵ񳡾**ûŵ**Ľӿûholdס + +> 1. ݵȣidempotentidempotenceһѧѧڳС +> 2. ڱ.һݵȲصִӰһִеӰͬݵȺݵȷָʹͬظִУܻͬĺ + + +һ㡸ݵȼ⼸: + +1. ѯ +2. Ψһ +3. tokenƣֹظύ +4. ݿdeleteɾ +5. ֹ +6. +7. Rediszookeeper ֲʽǰRedisֲʽ +8. ״̬ݵ + +![ӿݵ.gif](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55e09e293249431a85d68f8b217d9a8d~tplv-k3u1fbpfcp-watermark.image) + +### 2.3 õӿ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/41e0a0d34083431ca0d44f3354dca2f5~tplv-k3u1fbpfcp-zoom-1.image) + +#### 2.3.1 ʱ + +ǵñ˵Ľӿڣʱôأ + +> ٸӣǵһԶת˽ӿڣAͻBͻת100򣬳ɹʱͰѱתˮΪɹʧܵʱͰѱˮΪʧܡתϵͳʱأôأΪɹʧأ**ʱҪǺ**ҪȻʽʧˡֳ£ӿڳʱǾͿ**±תˮ**״̬·ѯԶת󣬲ѯת˳ɹļ¼ٸ±״̬״̬ + + + +#### 2.3.2 Ի + +ǵһԶhttpdubboӿڣʧˣǿԿԻơʱ·һ£ӿھ͵ʧˣԻƿû顣ԻҪЩӿڲ֧ݵȣͲʺԵġ + +#### 2.3.3 Ƿ񽵼 + + +ϵͳһṩעķûעɹ֮󣬵ԶAӿڷţԶBӿڷʼע״̬Ϊɹ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2a2180e624d4f8fadcf8fc8f63651bf~tplv-k3u1fbpfcp-zoom-1.image) + + +ýӿBʼʧܣûעʧܣҵܾͲͬˡʱǿԿǸBӿ****ṩ****Ҳ˵BӿʧܣȲʼûעɹʱʼͺ + +#### 2.3.4 Ƿ첽 + +һʹϸСڵ**ûע**ӡǿԿ첽߳ȥAӿڷţ첽BӿڷʼǼʹABӿڵʧܣǻǿԱ֤ûעɹ + +ѷЩ֪ͨӿڣŵ첽̴߳ԽͽӿںʱûŶ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07eef178cfe04048ade9fbed2f465d12~tplv-k3u1fbpfcp-zoom-1.image) + + + +#### 2.3.5 ӿ쳣 + +ǵһԶ̽ӿڣһҪ˼£˽ӿ쳣ҪôôףԻǵʧܣô֤ݵһԵȵȡ + + +## 3. ƪ + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f84d96671e8e451d936e110a53bf9cf6~tplv-k3u1fbpfcp-zoom-1.image) + +### 3.1 ݿ뻺һ + +ʹû棬Խͺʱṩϵͳܡǣʹû棬һԵ⡣ + +#### 3.1.1 ֻʹģʽ + +- Cache-Aside Pattern·ģʽ +- Read-Through/Write-Throughд͸ +- Write- behind 첽д룩 + +һʹû棬**·ģʽ**: + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38f7efc4d066409dbf65a56eb10eb90e~tplv-k3u1fbpfcp-zoom-1.image) + + +- ʱȶ棬еĻֱӷ +- ûеĻȥݿ⣬ݿȡݣ뻺ͬʱӦ + +·ģʽд̣ +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c80f0df22739439088d6a47ced7b6ae0~tplv-k3u1fbpfcp-zoom-1.image) + + +#### 3.1.2 ɾأǸ»棿 + +ڲʱ򣬵Ӧɾ滹Ǹ»أӣ + + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b0a52a30ec994ca7b66cf83fba5398a2~tplv-k3u1fbpfcp-zoom-1.image) + + +1. ߳Aȷһдһȸݿ +2. ߳Bٷһдڶݿ +3. ԭ߳Bȸ˻ +4. ߳A»档 + +ʱ򣬻汣AݣݣݿⱣBݣݣݲһˣݳɾȡ»򲻻⡣ + + +#### 3.1.3 Ȳݿ⻹Ȳ + +˫д£Ȳݿ⻹Ȳ棿һӣABA²Bѯȡ + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/95b0b2cd05d34ff1b66a8aa0a768cc04~tplv-k3u1fbpfcp-watermark.image) + +1. ߳Aһдһdel cache +2. ʱ߳Bһcache miss +3. ߳BDBһ +4. Ȼ߳Bcache +5. ߳AдDBµ + +Ͼݿݲһˡ汣ݣݿⱣݡˣCache-AsideģʽѡȲݿȲ档 + + +#### 3.1.4 α֤һ + +- ʱ˫ɾ +- ɾԻ +- ȡbiglog첽ɾ + + + +### 3.2 洩͸ + +> 洩͸ָѯһһڵݣڻ治ʱҪݿѯ鲻д뻺棬⽫ڵÿҪݿȥѯݿѹ + +洩͸һ㶼⼸ģ**ҵ񲻺ơҵ/ά/ʧIJڿͷǷ󹥻** +α⻺洩͸أ һַ + +- ǷǷAPIڣ**ԲУ**˷Ƿֵ +- ѯݿΪգǿ**øֵĬֵ**дĻҪ»Ա֤һԣͬʱʵĹʱ䡣ҵϱȽϳãЧ +- ʹ**¡**жǷڡһѯʱͨ¡жֵǷڣڲż²顣 + +### 3.3 ѩ + +> ѩָݴʱ䣬ѯ޴ݿѹdown + +- ѩһڴͬʱɵģԭ򣬿ͨ**ùʱùʱɢһ**һϴ̶ֵ+һСֵ5Сʱ+01800뽴ϡ +- **Redis 崻Ҳ𻺴ѩ**ҪRedis߿üȺ + + +### 3.4 + +> ָȵkeyijʱڵʱ򣬶ǡʱKeyдIJӶdb + +е񻺴ѩʵǣѩָݿѹdownֻǴDBݿ档ΪǻѩһӼɡЩΪڻijһȵkey棬ѩǺܶkey + + +֣ + +1. **ʹû**ʧЧʱȥdbݣʹijЩɹصԭӲ(Redissetnxȥɹʱȥdbݿݺû档ȥԻȡ档 +2. **ڡ**ָûùʱ䣬ȵݿҪʱ첽߳ȥºùʱ䡣 + + +### 3.5 Key + +RedisУǰѷƵʸߵkeyΪȵkeyijһȵkey󵽷ʱر󣬿ܻᵼԴ㣬崻ӶӰķ + +νkey⣿ + +- **RedisȺ**ӷƬ +- **keyhashɢ**罫һkeyΪkey1,key2keyNͬNݣNݷֲͬƬʱNеһһֵ +- **ʹö**JVMػ,RedisĶ + +### 3.6 ڴ濼 + +#### 3.6.1 + +ʹõRedisRedisڴDZȽϰģDzҪʲôݶRedisһRedisֻѯȽƵݡͬʱҪRedisҲƵsetǣ˹ʱkeyʧЧ + +ʹõDZػ棬guavaıػ棬ҲҪ + + +#### 3.6.2 Redisİڴ̭ + +Ϊ˱Redisڴ治ãRedis8ڴ̭ԱԼ~ + +> - volatile-lruڴ治дʱ˹ʱkeyʹLRUʹã㷨̭ +> - allkeys-lruڴ治дʱkeyʹLRUʹã㷨̭ +> - volatile-lfu4.0汾ڴ治дʱڹڵkeyУʹLFU㷨ɾkey +> - allkeys-lfu4.0汾ڴ治дʱkeyʹLFU㷨̭ +> - volatile-randomڴ治дʱ˹ʱkeyУ̭ݣ +> - allkeys-randomڴ治дʱkey̭ݡ +> - volatile-ttlڴ治дʱ˹ʱkeyУݹʱ̭Խڵȱ̭ +> - noevictionĬϲԣڴ治дʱдᱨ + +#### 3.6.3 ͬҵ񳡾Redisѡʺϵݽṹ + +- аʺzset +- ûϢһhash +- ϢУбlist +- ûǩ罻һset +- ֲʽһString + +### 3.7 RedisһЩпӵ + +1. ʹ keysָ +2. O(n)Ӷhgetall +3. Redismonitor +4. ֹʹflushallflushdb +5. עʹdel + +## + +ܽ60bugıע㣬ճķϣԴа⣬ +עںţ**ݵСк**ظ**˼άͼ****ȡĵĸ˼άͼ** + + diff --git "a/\345\267\245\344\275\234\346\200\273\347\273\223/\350\201\212\350\201\212\346\227\245\345\270\270\345\274\200\345\217\221\344\270\255\357\274\214\345\246\202\344\275\225\345\207\217\345\260\221bug\345\221\242\357\274\237.md.bak" "b/\345\267\245\344\275\234\346\200\273\347\273\223/\350\201\212\350\201\212\346\227\245\345\270\270\345\274\200\345\217\221\344\270\255\357\274\214\345\246\202\344\275\225\345\207\217\345\260\221bug\345\221\242\357\274\237.md.bak" new file mode 100644 index 0000000..e69de29 diff --git "a/\346\226\271\346\241\210\350\256\276\350\256\241/README.MD" "b/\346\226\271\346\241\210\350\256\276\350\256\241/README.MD" new file mode 100644 index 0000000..e986b15 --- /dev/null +++ "b/\346\226\271\346\241\210\350\256\276\350\256\241/README.MD" @@ -0,0 +1,9 @@ +- [实现一个刷数任务,需要思考哪些维度?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508890&idx=1&sn=919b8a794eb4902d958ae13d1f424737&chksm=c1e05e16f697d700ee9f79e087279de6312222b8e45887d976a572b01599f1177b358ade265b&token=337310304&lang=zh_CN#rd) +- [手把手教你写设计方案](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507937&idx=1&sn=33fd37f28675ce756e5d048b99254fcb&chksm=c1e0226df697ab7b4907fb2815c8dd2d195ea04c03a2f8fd0697c9a15a81fc639e5c5f7dab1b&token=337310304&lang=zh_CN#rd) +- [简易版,基于JWT 实现登录认证](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508057&idx=1&sn=06b6fee69c63afbe7ebd2f81a3627341&chksm=c1e05dd5f697d4c32e38bcb58c2ecba8115ea7f94a17bc197bcb7b042a18ef07fef0f0e03878&token=337310304&lang=zh_CN#rd) +- [高并发系统设计的15个建议](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508062&idx=1&sn=71e9647479ea71e8660d6ba48616c122&chksm=c1e05dd2f697d4c45ffd09e07fd40770e3d11591fa2b53161cd38da908cd55e41a38d5192605&token=337310304&lang=zh_CN#rd) +- [面试必备:聊聊分布式锁的多种实现!](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247506434&idx=1&sn=c6ae1ec19558626897295bbe41304b62&chksm=c1e0278ef697ae989b14f4746d1049be976d1d5744e4f9c7ec6e17d006f206edcc1c47a3e862&token=337310304&lang=zh_CN#rd) +- [并发环境下,先操作数据库还是先操作缓存?](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247508208&idx=1&sn=ac92523e33b478ad83560471338742f4&chksm=c1e05d7cf697d46aba95dc6661a8acbea0c894e44a793d054648b552a73b404aa3344d8a4826&token=337310304&lang=zh_CN#rd) + + + diff --git "a/\347\224\237\344\272\247\351\227\256\351\242\230\345\210\206\346\236\220/README.MD" "b/\347\224\237\344\272\247\351\227\256\351\242\230\345\210\206\346\236\220/README.MD" new file mode 100644 index 0000000..9d21f55 --- /dev/null +++ "b/\347\224\237\344\272\247\351\227\256\351\242\230\345\210\206\346\236\220/README.MD" @@ -0,0 +1,9 @@ +## 生产问题分析 + +- [内存泄漏问题的分析和解决方案](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487986&idx=1&sn=d681a585ac489703788e3baa48eb9aa3&chksm=cf21cedbf85647cd23bbab9dfec63e6877f83c34efb19bd16075d5d90fea91d3f4a20fc77921&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [生产问题分析!delete in子查询不走索引?!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495170&idx=1&sn=ce914de3abdb0d887e286b680b25111f&chksm=cf22312bf855b83d31a00da110626747df8e69fca1bc310642c56e39d663b006a8105f9fb1e1&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [手把手教你分析Mysql死锁问题](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487979&idx=1&sn=588c83d77a8851f3b3c18cd68ed9c454&chksm=cf21cec2f85647d4a77cc239ae9a4cfd31bb8832be3d98540a08ea8b4a1f46b38cf736210a02&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [线程池运用不当的一次线上事故](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487992&idx=1&sn=733335f2f69d743712915abc99f83b1d&chksm=cf21ced1f85647c7ab8c5d8bc4e8206b04acb5fd4feb94b8d088a782ed458b82aab69dba82aa&token=1990771297&lang=zh_CN#rd) +- [盘点MySQL慢查询的12个原因](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247499624&idx=1&sn=561b9cb7fe831ca7cb2d9fd65691e85e&chksm=cf222041f855a957ac50c0a53baaec6d26be32427259b2974450620f33a8c834419fe535e83d&token=1990771297&lang=zh_CN#rd) +- [线程池如何监控,才能帮助开发者快速定位线上错误?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497444&idx=1&sn=1b2cc8b4685413149e46c814e468c6e6&chksm=cf2229cdf855a0db5f2da881d27c69f11c69480552985baa2a08cbe4d5a48bad7fb31a78dd5a&token=1990771297&lang=zh_CN#rd) +- [数据库死锁排查思路分享](https://mp.weixin.qq.com/s?__biz=MzkyMzU5Mzk1NQ==&mid=2247507770&idx=1&sn=b84b20aca057b34d511a501ff91941b5&chksm=c1e022b6f697aba05248128cb82f93aed341b1cc80e6d568c7150a4ffa6775692c7c9fa423a3&token=337310304&lang=zh_CN#rd) diff --git "a/\347\250\213\345\272\217\344\272\272\347\224\237&\351\235\242\350\257\225\345\273\272\350\256\256/README.MD" "b/\347\250\213\345\272\217\344\272\272\347\224\237&\351\235\242\350\257\225\345\273\272\350\256\256/README.MD" new file mode 100644 index 0000000..ae9424d --- /dev/null +++ "b/\347\250\213\345\272\217\344\272\272\347\224\237&\351\235\242\350\257\225\345\273\272\350\256\256/README.MD" @@ -0,0 +1,6 @@ +## 程序人生 + +- [跟大家聊聊天,我周末都在干啥](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493222&idx=1&sn=29eb95b01b54bed2abbcf5a72285b38a&chksm=cf22394ff855b059b29ffb562e22d8ecc048caa743eb5c6257ad474676940ba8d36840f075ed&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [跟大家聊聊如何学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247495313&idx=1&sn=7f521db08e84b07177d847c60071d709&chksm=cf2231b8f855b8ae765f2dd584994836c0b74ce0ef761653233c3af04f38b4a1aa1833f7a55a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [写了两年文章,终于破万!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247489788&idx=1&sn=66efbc1b718915bfd8996b521d317a55&chksm=cf21c7d5f8564ec3928957d3c23959f5cb99d9f9bd2c1bab0dcf1750a6a017c3869189a3651a&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [夏天的风,我永远记得~](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247487989&idx=2&sn=9eb923d4c8c22bee1a408e4f86983f65&chksm=cf21cedcf85647cac6fe4bfa6d732856fd0335f4fcadad4d1e0dd10702e95905e06c9e38e8e8&token=162724582&lang=zh_CN&scene=21#wechat_redirect) \ No newline at end of file diff --git "a/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\267\245\345\205\267\347\257\207/README.MD" "b/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\267\245\345\205\267\347\257\207/README.MD" new file mode 100644 index 0000000..73e3dce --- /dev/null +++ "b/\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\345\267\245\345\205\267\347\257\207/README.MD" @@ -0,0 +1,8 @@ + +## 程序员工具篇 + +- [用代码画时序图!YYDS](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247500478&idx=1&sn=ec674e3eadba9bb87849292f46f84989&chksm=cf221d97f8559481fae8f0e1871ae19499568b3e49980e92018c4a5acdcf743a37da79c2436d&token=1990771297&lang=zh_CN#rd) +- [程序员必备基础:Git 命令全方位学习](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488013&idx=1&sn=7011a51a347e3da2cf8f8540b4d9a5d6&chksm=cf21cd24f8564432d74bc13551ebdeae71a71ea31e339c7a8f1f42f181078b5192475d598626&token=1569911403&lang=zh_CN&scene=21#wechat_redirect) +- [MyBatis 插件原理与实战](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498815&idx=1&sn=737e8f92ff526dac408af7a409f3a3d4&chksm=cf222316f855aa007fe16f7bca0636c552f238deb766bb54c34db7b633c13451fc91a4fe8a3e&token=1990771297&lang=zh_CN#rd) +- [更快的Maven来了,速度提升了8倍!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497470&idx=1&sn=7a3a5bb48f7d3b1a627460b698e7e9a0&chksm=cf2229d7f855a0c1e892c23f7690e6ab1a745040142672b982a3934c8307901d0be03dff3cff&token=1990771297&lang=zh_CN#rd) +- [因为知道了30+款在线工具,我的工作效率提升500%!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488041&idx=1&sn=26d55c23ecd439860c4d9865bec61976&chksm=cf21cd00f8564416fe991974d24a51798d925b2e79d62935accf02aa6895c7b02adf48e9e207&token=1990771297&lang=zh_CN#rd) \ No newline at end of file diff --git "a/\347\274\223\345\255\230Redis\346\200\273\347\273\223/README.MD" "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/README.MD" new file mode 100644 index 0000000..996dc81 --- /dev/null +++ "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/README.MD" @@ -0,0 +1,7 @@ +## 缓存 + +- [大厂经典面试题:Redis为什么这么快?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490736&idx=1&sn=95377e729b27f0afefbaa5f20239fc9d&chksm=cf21c399f8564a8ff5239fbaa86d616a48086b47b3bb03c8ccc1d3cc066e41c75e16638c3fc8&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [美团二面:Redis与MySQL双写一致性如何保证?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490243&idx=1&sn=ff11c3aab9ada3b16d7f2b57c846d567&chksm=cf21c5eaf8564cfc59e3d0d56fd02b0f5513015005f498381be4d12db462442a49aabe4159ef&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [使用Redis,你必须知道的21个注意要点](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488325&idx=1&sn=6d9bbe5bf2f2f2904755de5c786fb21b&chksm=cf21cc6cf856457a9d23b3e25ec48107a582e709f05964dfdb5ba77e9a239d8307334c485fdf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [七种方案!探讨Redis分布式锁的正确使用姿势](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=162724582&lang=zh_CN&scene=21#wechat_redirect) +- [2W字!详解20道Redis经典面试题!(珍藏版)](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect) \ No newline at end of file diff --git "a/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\270\203\347\247\215\346\226\271\346\241\210\345\257\271\346\257\224\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\270\203\347\247\215\346\226\271\346\241\210\345\257\271\346\257\224\345\210\206\345\270\203\345\274\217\351\224\201.md" new file mode 100644 index 0000000..d861cdb --- /dev/null +++ "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\270\203\347\247\215\346\226\271\346\241\210\345\257\271\346\257\224\345\210\206\345\270\203\345\274\217\351\224\201.md" @@ -0,0 +1,225 @@ +### 前言 +日常开发中,秒杀下单、抢红包等等业务场景,都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方,欢迎大家指出哈,一起学习一起进步。 + + +公众号:**捡田螺的小男孩** + +- 什么是分布式锁 +- 方案一:SETNX + EXPIRE +- 方案二:SETNX + value值是(系统时间+过期时间) +- 方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令) +- 方案四:SET的扩展命令(SET EX PX NX) +- 方案五:SET EX PX NX + 校验唯一随机值,再释放锁 +- 方案六: 开源框架:Redisson +- 方案七:多机实现的分布式锁Redlock + +- github地址,感谢每颗star +> https://github.com/whx123/JavaHome + + +### 什么是分布式锁 + +> 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 + +我们先来看下,一把靠谱的分布式锁应该有哪些特征: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42884a1613344c11be5fef3b9e8ed7c5~tplv-k3u1fbpfcp-zoom-1.image) + +- **互斥性**: 任意时刻,只有一个客户端能持有锁。 +- **锁超时释放**:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。 +- **可重入性**:一个线程如果获取了锁之后,可以再次对其请求加锁。 +- **高性能和高可用**:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。 +- **安全性**:锁只能被持有的客户端删除,不能被其他客户端删除 + +### Redis分布式锁方案一:SETNX + EXPIRE + +提到Redis的分布式锁,很多小伙伴马上就会想到```setnx```+ ```expire```命令。即先用```setnx```来抢锁,如果抢到之后,再用```expire```给锁设置一个过期时间,防止锁忘记了释放。 + +> SETNX 是SET IF NOT EXISTS的简写.日常命令格式是SETNX key value,如果 key不存在,则SETNX成功返回1,如果这个key已经存在了,则返回0。 + +假设某电商网站的某商品做秒杀活动,key可以设置为key_resource_id,value设置任意值,伪代码如下: + +``` +if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁 + expire(key_resource_id,100); //设置过期时间 + try { + do something //业务请求 + }catch(){ +  } +  finally { + jedis.del(key_resource_id); //释放锁 + } +} +``` +但是这个方案中,```setnx```和```expire```两个命令分开了,**不是原子操作**。如果执行完```setnx```加锁,正要执行```expire```设置过期时间时,进程crash或者要重启维护了,那么这个锁就“长生不老”了,**别的线程永远获取不到锁啦**。 + + +### Redis分布式锁方案二:SETNX + value值是(系统时间+过期时间) + +为了解决方案一,**发生异常锁得不到释放的场景**,有小伙伴认为,可以把过期时间放到```setnx```的value值里面。如果加锁失败,再拿出value值校验一下即可。加锁代码如下: +``` +long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间 +String expiresStr = String.valueOf(expires); + +// 如果当前锁不存在,返回加锁成功 +if (jedis.setnx(key_resource_id, expiresStr) == 1) { + return true; +} +// 如果锁已经存在,获取锁的过期时间 +String currentValueStr = jedis.get(key_resource_id); + +// 如果获取到的过期时间,小于系统当前时间,表示已经过期 +if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { + + // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈) + String oldValueStr = jedis.getSet(key_resource_id, expiresStr); + + if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { + // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁 + return true; + } +} + +//其他情况,均返回加锁失败 +return false; +} +``` + +这个方案的优点是,巧妙移除```expire```单独设置过期时间的操作,把**过期时间放到setnx的value值**里面来。解决了方案一发生异常,锁得不到释放的问题。但是这个方案还有别的缺点: + +> - 过期时间是客户端自己生成的(System.currentTimeMillis()是当前系统的时间),必须要求分布式环境下,每个客户端的时间必须同步。 +> - 如果锁过期的时候,并发多个客户端同时请求过来,都执行jedis.getSet(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖 +> - 该锁没有保存持有者的唯一标识,可能被别的客户端释放/解锁。 + + +### Redis分布式锁方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令) + +实际上,我们还可以使用Lua脚本来保证原子性(包含setnx和expire两条指令),lua脚本如下: +``` +if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then + redis.call('expire',KEYS[1],ARGV[2]) +else + return 0 +end; +``` +加锁代码如下: + +``` + String lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + + " redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end"; +Object result = jedis.eval(lua_scripts, Collections.singletonList(key_resource_id), Collections.singletonList(values)); +//判断是否成功 +return result.equals(1L); +``` +这个方案还是有缺点的哦,至于哪些缺点,你先思考一下。也可以想下。跟方案二对比,哪个更好? + +### Redis分布式锁方案方案四:SET的扩展命令(SET EX PX NX) + +除了使用,使用Lua脚本,保证```SETNX + EXPIRE```两条指令的原子性,我们还可以巧用Redis的SET指令扩展参数!(```SET key value[EX seconds][PX milliseconds][NX|XX]```),它也是原子性的! + +> SET key value[EX seconds][PX milliseconds][NX|XX] +> - NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。 +> - EX seconds :设定key的过期时间,时间单位是秒。 +> - PX milliseconds: 设定key的过期时间,单位为毫秒 +> - XX: 仅当key存在时设置值 + +伪代码demo如下: +``` +if(jedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1){ //加锁 + try { + do something //业务处理 + }catch(){ +  } +  finally { + jedis.del(key_resource_id); //释放锁 + } +} +``` + +但是呢,这个方案还是可能存在问题: + +- 问题一:**锁过期释放了,业务还没执行完**。假设线程a获取锁成功,一直在执行临界区的代码。但是100s过去后,它还没执行完。但是,这时候锁已经过期了,此时线程b又请求过来。显然线程b就可以获得锁成功,也开始执行临界区的代码。那么问题就来了,临界区的业务代码都不是严格串行执行的啦。 +- 问题二:**锁被别的线程误删**。假设线程a执行完后,去释放锁。但是它不知道当前的锁可能是线程b持有的(线程a去释放锁时,有可能过期时间已经到了,此时线程b进来占有了锁)。那线程a就把线程b的锁释放掉了,但是线程b临界区业务代码可能都还没执行完呢。 + +### 方案五:SET EX PX NX + 校验唯一随机值,再删除 + +既然锁可能被别的线程误删,那我们给value值设置一个标记当前线程唯一的随机数,在删除的时候,校验一下,不就OK了嘛。伪代码如下: +``` +if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 + try { + do something //业务处理 + }catch(){ +  } +  finally { + //判断是不是当前线程加的锁,是才释放 + if (uni_request_id.equals(jedis.get(key_resource_id))) { + jedis.del(lockKey); //释放锁 + } + } +} +``` + +在这里,**判断是不是当前线程加的锁**和**释放锁**不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。 + +![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9237655e6b1a47038d2774231e507e11~tplv-k3u1fbpfcp-watermark.image) + +为了更严谨,一般也是用lua脚本代替。lua脚本如下: +``` +if redis.call('get',KEYS[1]) == ARGV[1] then + return redis.call('del',KEYS[1]) +else + return 0 +end; +``` + +### Redis分布式锁方案六:Redisson框架 + +方案五还是可能存在**锁过期释放,业务没执行完**的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。 + +当前开源框架Redisson解决了这个问题。我们一起来看下Redisson底层原理图吧: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/367cd1a7a3fb4d398988e4166416d71d~tplv-k3u1fbpfcp-zoom-1.image) + + +只要线程一加锁成功,就会启动一个```watch dog```看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了**锁过期释放,业务没执行完**问题。 + +### Redis分布式锁方案七:多机实现的分布式锁Redlock+Redisson + +前面六种方案都只是基于单机版的讨论,还不是很完美。其实Redis一般都是集群部署的: + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7349794feeee458aa71c27f27a0b2428~tplv-k3u1fbpfcp-watermark.image) + +如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。 + +为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的: +> 搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。 + +我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。 + +![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0df0a36c7ccd439291a8a869ff4ddad3~tplv-k3u1fbpfcp-watermark.image) + +RedLock的实现步骤:如下 +> - 1.获取当前时间,以毫秒为单位。 +> - 2.按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。 +> - 3.客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms) +> - 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。 +> - 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。 + +简化下步骤就是: +- 按顺序向5个master节点请求加锁 +- 根据设置的超时时间来判断,是不是要跳过该master节点。 +- 如果大于等于三个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。 +- 如果获取锁失败,解锁! + +Redisson实现了redLock版本的锁,有兴趣的小伙伴,可以去了解一下哈~ + +### 公众号 +- 欢迎关注公众号:捡田螺的小男孩 + +### 参考与感谢 + +- [redis系列:分布式锁](https://juejin.cn/post/6844903656911798285 "redis系列:分布式锁") +- [浅析 Redis 分布式锁解决方案](https://www.infoq.cn/article/dvaaj71f4fbqsxmgvdce "浅析 Redis 分布式锁解决方案") +- [细说Redis分布式锁🔒](https://juejin.cn/post/6844904082860146695#heading-3 "细说Redis分布式锁🔒") +- [Redlock:Redis分布式锁最牛逼的实现](https://mp.weixin.qq.com/s?__biz=MzU5ODUwNzY1Nw==&mid=2247484155&idx=1&sn=0c73f45f2f641ba0bf4399f57170ac9b&scene=21#wechat_redirect) + diff --git "a/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\275\277\347\224\250Redis\347\232\20421\344\270\252\346\263\250\346\204\217\347\202\271.md" "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\275\277\347\224\250Redis\347\232\20421\344\270\252\346\263\250\346\204\217\347\202\271.md" new file mode 100644 index 0000000..6a4364e --- /dev/null +++ "b/\347\274\223\345\255\230Redis\346\200\273\347\273\223/\344\275\277\347\224\250Redis\347\232\20421\344\270\252\346\263\250\346\204\217\347\202\271.md" @@ -0,0 +1,386 @@ +### ǰ + +ѧϰRedis֪ʶ˰redis淶ԼRedisάⱾ顣ʹù淶пӵĿʵսάĸʹRedis21ע㣬ϣԴаһѧϰ + +ںţ**ݵСк** +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c59661f74a014a63a4527939096f11aa~tplv-k3u1fbpfcp-watermark.image) + +## 1Redisʹù淶 + +### 1.1 keyĹ淶Ҫ + +RediskeyʱҪע⼸㣺 + +> - ҵΪkeyǰ׺ðŸԷֹkeyͻǡ磬live:rank:1 +> - ȷkey£keyijȾС30ַ +> - keyַֹո񡢻С˫Լתַ +> - RediskeyttlԱ֤ʹõKeyܱʱ̭ + +### 1.2valueĹ淶Ҫ + +RedisvalueֵõŶ + +**һ**洢bigKeyǻģᵼѯڴȵȡ +> - StringͣvalueС10kڡ +> - hashlistsetzsetͣԪظһ㲻5000 + + +**ڶ**Ҫѡʺϵ͡СֻRedisStringͣsetgetʵϣRedis ṩ**ḻݽṹ**Щҵ񳡾ʺ```hashzset```ݽ + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1874e69c709940fd80d66cc8ab683135~tplv-k3u1fbpfcp-watermark.image) + +**** + +``` +set user:666:name jay +set user:666:age 18 +``` + +**** + +``` +hmset user:666 name jay age 18 +``` + +### 1.3. Keyùʱ䣬ͬʱעⲻͬҵkeyʱɢһ + +- ΪRedisǴڴеģڴԴǺܱġ +- һǰRedisã**ݿ**keyھͲ̫ +- ˣkeyһ㽨**expireùʱ** + + +keyijʱ㼯йڣڵǸʱ㣬Redisܻڿ٣**ѩ**һ㲻ͬҵkeyʱӦ÷ɢһЩʱͬҵģҲʱϼһֵùʱɢһЩ + + +### 1.4.ʹЧ + +ճдSQLʱ򣬶֪Чʻߣһθ50ѭ50ΣÿθһЧʸߡʵRedisҲ + +RedisͻִһɷΪ4̣1.-> 2.Ŷ-> 3.ִ-> 4. ؽ14 ΪRRTִʱ䣩 Redisṩ**mgetmset**ȣЧԼRRTأ󲿷ֵDz֧ģhgetallûmhgetallڡ**Pipeline** Խ⡣ + +> Pipelineʲô?ܽһRedisװͨһRTTRedisٽRedisִн˳򷵻ظͻ. + +ûʹPipelineִnģͣ + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b71283614a3344c4afac8ac82438fa44~tplv-k3u1fbpfcp-watermark.image) + +ʹPipelineִnҪ1RTTģ£ + +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b865d241c02d45e7a88540ab7f70280a~tplv-k3u1fbpfcp-watermark.image) + +## 2Redis пӵЩ + + +### 2.1. ```O(n)```Ӷ```hgetall``````smember``````lrange``` + +ΪRedisǵִ߳ġhgetallsmemberʱ临ӶΪO(n)nʱᵼ Redis CPU 쭸ߣִС + +> hgetallsmemberlrangeЩһʹãҪۺȷnֵȥ +> hgetallϣԪnȽ϶Ļȿʹ**hscan** + + +### 2.2 Redismonitor + +Redis Monitor ʵʱӡRedisյ֪ͻ˶redisЩͿMonitor 鿴һ****öѣҪãΪ**monitorܵredisڴ** + +> monitorģǽϵģὫRedisִеһRedisQPSǺܸߵģҲִmonitorRedisMonitorͻ˵ֻдҲռ˴Redisڴ档 + + +![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3610a4a95fe4995862d9ddd878269fe~tplv-k3u1fbpfcp-watermark.image) + + +### 2.3ʹ keysָ + +Redis Keys ڲзϸģʽpatternkey鿴Redis ij͵keyжٸС뵽keys£ + +``` +keys keyǰ׺* +``` + +ǣredis```keys```DZƥģӶ```On```ݿԽԽ֪redisǵ̵߳ģݱȽ϶Ļkeysָͻᵼredis߳ϷҲͣˣֱִָ꣬Żָˣ**һҪʹkeysָ**ٷĵҲ + +> Warning: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases. This command is intended for debugging and special operations, such as changing your keyspace layout. Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using sets. + +ʵʹscanָͬkeysһṩģʽƥ书ܡĸӶҲ O(n)ͨαֲУ**redis߳**;ǻһ**ظ**Ҫ**ͻһȥ** + +> scan֧ʽʽҲȱģٸӣ ʹ SMEMBERS ԷؼϼǰԪأ Ƕ SCAN ʽ˵ ΪڶԼʽĹУ ܻᱻ޸ģ ʽֻܶԱصԪṩ޵ı֤ + + +### 2.4 ֹʹflushallflushdb + +> - Flushall Redis (ɾݿ key ) +> - Flushdb յǰݿе key + +ԭԵģִֹСһʼִУִʧܵġ + +### 2.5 עʹdel + +ɾkeyһʹʲôֱdelɾһkeyֱʹdelȻû⡣ǣdelʱ临ӶǶǷ̽һ£ +- ɾһString͵keyʱ临ӶȾ```O1```**ֱdel** +- ɾһList/Hash/Set/ZSetʱĸӶ```O(n)```, nʾԪظ + +ˣɾһList/Hash/Set/ZSet͵keyʱԪԽ࣬Խ**nܴʱҪע**̵߳ġôdelӦôɾأ + +> - Listִͣ```lpoprpop```ֱԪɾɡ +> - Hash/Set/ZSetִͣ```hscan/sscan/scan```ѯִ```hdel/srem/zrem```ɾÿԪء + +### 2.6 ʹSORTSINTERȸӶȹߵ + +ִиӶȽϸߵĸ CPU Դ̡߳Ҫִ```SORTSINTERSINTERSTOREZUNIONSTOREZINTERSTORE```Ⱦۺһ㽨ŵͻִС + +## 3ĿʵսܿӲ + +### 3.1 ֲʽʹõע + +ֲʽʵǣƷֲʽϵͳ̹ͬͬʹԴһʵ֡ɱµȵҵ񳡾ҪõֲʽǾʹRedisΪֲʽҪЩע㣺 + +#### 3.1.1 SETNX + EXPIREֿдʹʵַ +``` +ifjedis.setnx(key_resource_id,lock_value) == 1{ // + expirekey_resource_id100; //ùʱ + try { + do something //ҵ + }catch(){ +} +finally { + jedis.del(key_resource_id); //ͷ + } +} +``` +ִ```setnx```ҪִexpireùʱʱcrashҪάˣô͡ϡˣ**߳Զȡ**һֲʽôʵ֡ + +#### 3.1.2 SETNX + valueֵǹʱ (ЩСôʵ֣п) + + +``` +long expires = System.currentTimeMillis() + expireTime; //ϵͳʱ+õĹʱ +String expiresStr = String.valueOf(expires); + +// ǰڣؼɹ +if (jedis.setnx(key_resource_id, expiresStr) == 1) { + return true; +} +// ѾڣȡĹʱ +String currentValueStr = jedis.get(key_resource_id); + +// ȡĹʱ䣬Сϵͳǰʱ䣬ʾѾ +if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { + + // ѹڣȡһĹʱ䣬Ĺʱ䣨˽redisgetSetС飬ȥ¹ + String oldValueStr = jedis.getSet(key_resource_id, expiresStr); + + if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { + // Ƕֻ̲߳һ̵ֵ߳͵ǰֵͬſԼ + return true; + } +} + +//ؼʧ +return false; +} +``` +ַ**ȱ** +> - ʱǿͻԼɵģֲʽ£ÿͻ˵ʱͬ +> - ûбߵΨһʶܱĿͻͷ/ +> - ڵʱ򣬲ͻͬʱִ```jedis.getSet()```ֻһͻ˼ɹǸÿͻĹʱ䣬ܱĿͻ˸ǡ + +#### 3.1.3 SETչSET EX PX NXעܴڵ⣩ + +``` +ifjedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1{ // + try { + do something //ҵ + }catch(){ +} +finally { + jedis.del(key_resource_id); //ͷ + } +} +``` + +ǿܴ⣺ +- ͷˣҵûִꡣ +- ߳ɾ + + +#### 3.1.4 SET EX PX NX + УΨһֵ,ɾɾ⣬Ǵڣҵûִ⣩ + +``` +ifjedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1{ // + try { + do something //ҵ + }catch(){ +} +finally { + //жDzǵǰ̼߳ӵ,Dzͷ + if (uni_request_id.equals(jedis.get(key_resource_id))) { + jedis.del(lockKey); //ͷ + } + } +} +``` + +жDzǵǰ̼߳ӵͷһԭӲjedis.del()ͷʱ򣬿Ѿڵǰͻˣ˼ӵ + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88518412ca20489cbd38498d883639db~tplv-k3u1fbpfcp-watermark.image) + + +һҲluaű档luaű£ + +``` +if redis.call('get',KEYS[1]) == ARGV[1] then + return redis.call('del',KEYS[1]) +else + return 0 +end; +``` + + +#### 3.1.5 Redisson + Redlock㷨 ͷţҵûִ+ + +Redisson ʹһ```Watch dog```ͷţҵûִ⣬Redissonԭͼ: +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a80964e2ed1d4e739dc2a62ac73110da~tplv-k3u1fbpfcp-watermark.image) + +ϵķֲʽڵ⣺ +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b9e6b6050874be98f6a256291552459~tplv-k3u1fbpfcp-watermark.image) +> ߳һRedismasterڵõǼkeyûͬslaveڵ㡣ǡʱmasterڵ㷢ϣһslaveڵͻΪmasterڵ㡣̶߳ͿԻȡͬkey߳һҲѾõˣİȫԾûˡ + +Ե⣬ʹRedlock㷨ȤѿԿƪ¹[ַ̽Redisֲʽȷʹ](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488142&idx=1&sn=79a304efae7a814b6f71bbbc53810c0c&chksm=cf21cda7f85644b11ff80323defb90193bc1780b45c1c6081f00da85d665fd9eb32cc934b5cf&token=1120875912&lang=zh_CN#rd) + +### 3.2 һע + +- Ƕȶ棬ݿ +- дȸݿ⣬д +- ÿθݺҪ +- һ㶼ҪһĹʧЧ +- һҪߵĻʹbiglog+MQ֤ + +ȤѣԿƪ¹[£Ȳݿ⻹Ȳ棿](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247488079&idx=1&sn=49255f6c0c540deeb3333bcf86d6c77c&chksm=cf21cd66f856447061b5eca47f51199e120a9eaa83fa7546b4bd2667218403ccc97e726ab456&token=1120875912&lang=zh_CN#rd) + +### 3.3 RedisƵsetǣ֮ǰõĹʱЧ + +֪RedisݽṹͣǿùʱġһַѾ˹ʱ䣬ȥͻᵼ֮ǰĹʱЧ + +![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/29a81b1775e044e2b1198dff2e3b1ca1~tplv-k3u1fbpfcp-watermark.image) + +Redis ```setKey```Դ£ +``` +void setKey(redisDb *db,robj *key,robj *val) { + if(lookupKeyWrite(db,key)==NULL) { + dbAdd(db,key,val); + }else{ + dbOverwrite(db,key,val); + } + incrRefCount(val); + removeExpire(db,key); //ȥʱ + signalModifiedKey(db,key); +} +``` + +ʵҵ񿪷УͬʱҪRedisƵsetǣ˹ʱkeyʧЧС׷ + +### 3.4 洩͸ + +һĻʹ÷ʽˣȲ»棬ֵУֱӷأûУȥݿ⣬Ȼݿֵµ棬ٷء + + +![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/59c69359b2c249ad8954b7da7c0e6fb8~tplv-k3u1fbpfcp-watermark.image) + +**洩͸**ָѯһһڵݣڻDzʱҪݿѯ鲻д뻺棬⽫ڵÿҪݿȥѯݿѹ + +> ͨ׵˵ʱݿⶼûijֵͻᵼÿζֵIJѯ󶼻ᴩ͸ݿ⣬ǻ洩͸ + +洩͸һ㶼⼸ģ + +- **ҵ񲻺**ûûػÿȥ棬ѯijuseridѯûػ +- **ҵ/ά/ʧIJ**绺ݿݶɾˡ +- **ڿͷǷ󹥻**ڿ͹ǷԶȡڵҵݡ + +**α⻺洩͸أ** һַ + +- 1. ǷǷAPIڣԲУ飬˷Ƿֵ +- 2. ѯݿΪգǿԸøֵĬֵдĻҪ»Ա֤һԣͬʱʵĹʱ䡣ҵϱȽϳãЧ +- 3. ʹò¡жǷڡһѯʱͨ¡жֵǷڣڲż²顣 +> ¡ԭɳʼֵΪ0λͼNϣɡһһkeyNhash㷨ȡNֵڱнNֵɢк趨Ϊ1Ȼʱض⼸λöΪ1ô¡жϸkeyڡ + + +### 3.5 ѩ + +**ѩ** ָݴʱ䣬ѯ޴ֱӷݿ⣬ݿѹdown + +- ѩһڴͬʱɵģԭ򣬿ͨùʱùʱɢһ㡣һϴ̶ֵ+һСֵ5Сʱ+01800뽴ϡ +- Redis 崻Ҳ𻺴ѩҪRedis߿üȺ + + +### 3.6 + +**** ָȵkeyijʱڵʱ򣬶ǡʱKeyдIJӶdb + +еʵǣѩָݿѹdownֻǴDBݿ档ΪǻѩһӼɡЩΪڻijһȵkey棬ѩǺܶkey + +֣ + +- **1.ʹû**ʧЧʱȥdbݣʹijЩɹصԭӲ(Redissetnxȥɹʱȥdbݿݺû档ȥԻȡ档 +- **2. ڡ**ָûùʱ䣬ȵݿҪʱ첽߳ȥºùʱ䡣 + +### 3.7key + +RedisУǰѷƵʸߵkeyΪȵkeyijһȵkey󵽷ʱر󣬿ܻᵼԴ㣬崻ӶӰķ + +ȵKeyôأҪԭ +> - ûѵԶݣɱȵŵȶдٵij +> - ƬУRediܣ̶keyHashͬһ̨˲󣬳ƿȵKey⡣ + +ôճУʶȵkeyأ +> - ƾжЩKey +> - ͻͳϱ +> - ϱ + +νkey⣿ + +> - RedisȺݣӷƬ +> - keyhashɢУ罫һkeyΪkey1,key2keyNͬNݣNݷֲͬƬʱNеһһֵ +> - ʹö棬JVMػ,RedisĶ + +## 4. Redisά + +### 4.1 ʹóӶǶӣҺÿͻ˵ӳ + +- ʹöӣÿζҪ TCP ֡Ĵλ֣ӺʱȻӵĻһӣredisһֱʹãϿԼٽredisʱ䡣 +- ӳؿʵڿͻ˽ӲҲͷţҪʹӵʱ򣬲ÿζӣʡ˺ʱҪòʱ䲻 RedisʱҲ輰ʱͷԴ + +### 4.2 ֻʹ db0 + +Redis-standaloneֹܹʹ÷db0.ԭ + +- һӣRedisִselect 0select 1лܡ +- Redis Cluster ֻ֧ db0ҪǨƵĻɱ + +### 4.3 maxmemory + ǡ̭ԡ + +Ϊ˷ֹڴѹ͡ЩʱҵˣrediskeyʹãڴֱӲˣάСҲǼӴڴˡѵredisֱҵҪʵҵѡmaxmemory-policy(ڴ̭)úùʱ䡣һ8ڴ̭ԣ + + - volatile-lruڴ治дʱ˹ʱkeyʹLRUʹã㷨̭ +- allkeys-lruڴ治дʱkeyʹLRUʹã㷨̭ +- volatile-lfu4.0汾ڴ治дʱڹڵkeyУʹLFU㷨ɾkey +- allkeys-lfu4.0汾ڴ治дʱkeyʹLFU㷨̭ +- volatile-randomڴ治дʱ˹ʱkeyУ̭ݣ +- allkeys-randomڴ治дʱkey̭ݡ +- volatile-ttlڴ治дʱ˹ʱkeyУݹʱ̭Խڵȱ̭ +- noevictionĬϲԣڴ治дʱдᱨ + +### 4.4 lazy-free + +Redis4.0+汾֧lazy-freeƣRedisbigKeyڣlazy-freeRedis ɾһ bigkey ʱͷڴĺʱŵ̨߳ȥִУٶ̵߳Ӱ졣 + +![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6f6c5e7c58fa488ea0540f671c08081b~tplv-k3u1fbpfcp-watermark.image) + +### οл + +- [Redis ǧҪKEYSȻᰤ](https://www.cnblogs.com/tonyY/p/12175032.html) +- [Redis淶](https://developer.aliyun.com/article/531067) +- [Redis ʵָϣ7ά+43ʹù淶](https://mp.weixin.qq.com/s/2sUWnpJCvkJ8-7XSGLdesA) +- [RedisĻ洩͸¡BloomFilter](https://blog.csdn.net/wx1528159409/article/details/88357728) +- [ Redis ʵܽ](https://www.shangmayuan.com/a/d2f178b548a64c25854a9750.html) + diff --git "a/\351\253\230\345\271\266\345\217\221/README.MD" "b/\351\253\230\345\271\266\345\217\221/README.MD" new file mode 100644 index 0000000..0ce39a5 --- /dev/null +++ "b/\351\253\230\345\271\266\345\217\221/README.MD" @@ -0,0 +1,7 @@ +## 高并发 +- [美团二面:Redis与MySQL双写一致性如何保证?](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247490243&idx=1&sn=ff11c3aab9ada3b16d7f2b57c846d567&chksm=cf21c5eaf8564cfc59e3d0d56fd02b0f5513015005f498381be4d12db462442a49aabe4159ef&token=1495321435&lang=zh_CN&scene=21#wechat_redirect) +- [Redis主从、哨兵、 Cluster集群一锅端!](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247498254&idx=1&sn=6489360c3b226df9811e66cb40ec7656&chksm=cf222527f855ac3112628bcec7730064fee3fdbe869fbd0a7410c22766a0c036a7e5c1a69fa0&token=1990771297&lang=zh_CN#rd) +- [面试必备:聊聊MySQL的主从](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497982&idx=1&sn=bb589329cceb5462fc41f66ec63dbf56&chksm=cf2227d7f855aec16dd4d3b3425c0401850eeaf2c9cdc82e82722d38a00c24ee9ccfa3353774&token=1990771297&lang=zh_CN#rd) +- [聊聊幂等设计](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497427&idx=1&sn=2ed160c9917ad989eee1ac60d6122855&chksm=cf2229faf855a0ecf5eb34c7335acdf6420426490ee99fc2b602d54ff4ffcecfdab24eeab0a3&token=1990771297&lang=zh_CN#rd) +- [聊聊接口性能优化的11个小技巧](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247497361&idx=1&sn=a0a2b0f92804921ba3d31b6236f275c2&chksm=cf2229b8f855a0aec650f4e0c3f105aa08e52fabbc54807dd37fefc4873749698b2b1445b59f&token=1990771297&lang=zh_CN#rd) +- [面试必备:秒杀场景九个细节](https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247493227&idx=1&sn=10e5064d7d224c69dce400e90cd44de6&chksm=cf223942f855b0541ada22a312e0d4ffbc99df463678247a0dede3ef16eb81e3344a4a54ceaf&token=1990771297&lang=zh_CN#rd) \ No newline at end of file