1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-10 00:41:37 +08:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Guide
3b39b53e38 [docs add]从校招入职腾讯的四年工作总结 - 程序人生 2023-03-24 19:23:46 +08:00
Guide
b2351c877d [docs update]完善Redis事务的介绍 2023-03-24 17:34:58 +08:00
12 changed files with 240 additions and 50 deletions

View File

@ -4,7 +4,7 @@ export const navbarConfig = navbar([
{ text: "面试指南", icon: "java", link: "/home.md" },
{ text: "开源项目", icon: "github", link: "/open-source-project/" },
{ text: "技术书籍", icon: "book", link: "/books/" },
{ text: "技术文章", icon: "article", link: "/high-quality-technical-articles/" },
{ text: "程序人生", icon: "article", link: "/high-quality-technical-articles/" },
{
text: "网站相关",
icon: "about",

View File

@ -16,6 +16,7 @@ export const highQualityTechnicalArticles = [
prefix: "personal-experience/",
collapsable: false,
children: [
"four-year-work-in-tencent-summary",
"two-years-of-back-end-develop--experience-in-didi&toutiao",
"8-years-programmer-work-summary",
"huawei-od-275-days",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1 +1 @@
<mxfile host="Electron" modified="2022-08-22T01:01:48.794Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="nH91fIJ1FQXltYQjMT6B" version="13.4.5" type="device"><diagram id="9BE8MdjfxC0TaVaDFPY0" name="Page-1">7Vrbcuo2FP0aPSbjq2w/2uCk92aah7ZPHcUW4MRY1IgA/fpKtoQvEgTOMQkZMmfmxNq62XvtJS1tBOzRfHNfosXsV5LiHFhGugH2GFiWaRqQ/eGWbW0JAq82TMssFY0aw2P2HxZGQ1hXWYqXnYaUkJxmi64xIUWBE9qxobIk626zCcm7sy7QFCuGxwTlqvXPLKUz+V0waCp+wNl0JqeGrlPXzJFsLT5lOUMpWbdMdgzsUUkIrZ/mmxHOufekY+p+d3tqd29W4oIe0wEvn4t/bh7+QOOU0PtFdFNuf74Ro7yifCW+WLws3UoXlGRVpJgPYgI7Ws8yih8XKOG1awY6s83oPBfVKVrOdm3VN5TT4ZLiTcsk3vgekzmm5ZY1kbW+8J6IH9MR5XULDenhWQsIxxBGJCJguhu78RF7EG46wWWW6rLYBaELWGjHEPgxf+aWMZDB3vIm+27addmSluQFj0hOSmYpSMFaRpMsz3smlGfTghUT5k3M7BH3YsZCNRQV8yxN+TRajBoUjT5ME1JQwTzLGAY2y+jBBlXYfA1q1tlAMxQkcMqYLoqkpDMyJQXK48ba81nT5hdCFsJ3z5jSrXAeWlHShZY5sNz+JfpXhb954daVxfGmXTneilILERMeQmRJVmWCD3y3LdZMVE4xPdBOBCr3yUF8S5wjmr12V8fB0bIVii1J8oKpqYKY52wr2Bf0LSj6Lm0TDFh2irA/SRQ2shqY+PhpMtBqFrgdWuxo0qaFq9LCOxstNB69Blo4n5IWzh5aqHv256aF/eG00Hj0/LQYLrzdocNWdH0gGXuVHW5Ob5e37R4gNb9ErwaTsCzRttVswRss989jO915HM9tD/dme/leTUjUb9AEyM4n3x4z7h5y/vbJydmXco5Ggb8rOaHiaNXDRRryw18jnjuCt3deeUCUCeqisliGvXOoPPBZp2jkNznXcpurUcDS9p3UtOzgtruoOn1y1quIQs4jhrL943g+FLUs+zpVinekSvEvSqV4Cj9/ZOP8zkzVqdgBQQhiH0QeCO+ExR+D2AOBA0KfP7D//YhXRSHw7xTsT0pI9InbW0MnEwwT7RqaesGTMdBxWNkoNQLH1J2H4bkWUV9dRGMIAgh8D8QOiCIQweohrKDYWVzgM3AgbxwxY8wtQcAROy9KE2sPSvAJugdZdjxKMLg0lIK3t7oPzc4FsJfmsTQegxqPOWdTBzJx3IlsXRzzZceplh2jCuh9cXwdCbvA727xuoSdaWqQPFvGzlRPu186TxFnpqGoM7evzo4VerqxjjzRDaX0TPUUVTHTBNFYsJfxlqkHJh2iKt0emcC3TuHzt+5LuhMYNlMXe7p9KYCejQbalwKvnzV0VW7a77kvmZozmAYmZolAELdE3lXBFGiW0PeFSZXilfK+q0Bp/VTFdsegAoX/ZuVcF0y29+Ew6cQ4I5HPeVSziSGlBe56YHLcj4bJclT9cQ1ZCBFNb2YhzMGzzt8Hl/sF10G44GXBBb/gOgjXZf0Uaak3W77g0uzplwKXRmJYMKfCNx0g4b8rIitulpXXQtbAhItNU8meptVfnhFkOmRUCZVRdQfK5Ulbfjq7qyzhT+gV3fN7fHLKp7Lp7vEjAzs4cPEZcfUiez2zXtXtv9ukkB3Zt9evK3pfZq4mR084j1DyMq3s/clbseiLcktDRdW/akzKAoMUYtDlC6bJTBbkVcKBsj+7Y748uuhu2ekyn+bpYosVmzuPdd6guTpqx/8D</diagram></mxfile>
<mxfile host="Electron" modified="2023-03-24T02:15:32.077Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.3.0 Chrome/104.0.5112.114 Electron/20.1.3 Safari/537.36" etag="a9N6Y6CqlEFiDDgChlI2" version="20.3.0" type="device"><diagram id="9BE8MdjfxC0TaVaDFPY0" name="Page-1">7Vtbc6s2EP41ekyG++XR2DjnnPa0meah7VNHARmTYOSCnNj99ZWEMGDJSZz4QjJMZhxpdQPtp0+7KwTM8WJ9U8Dl/CeOUQYMLV4DcwIMQ9c1h/5jkk0lcT2/EiRFGotKjeAu/Q8JoSakqzRGZaciwTgj6bIrjHCeo4h0ZLAo8HO32gxn3VGXMEGS4C6CmSz9M43JvH4vx28KvqE0mddDO7ZVldzD6DEp8CoXAwLDnDrT6dSrihew7ky8aTmHMX5uicwQmOMCY1KlFusxytjk1vNWtZvuKd0+eIFy8pYGqHzI/7m6/QNOYkxulsFVsfnlSvTyBLMVql+DPyzZ1DPEXxGxTnRgBs/zlKC7JYxY6TPFBJXNySITxTEs59u6M5yTKVykGcPGD0SCAqZ5SXv/iXMsyu/wquB9zQmhKjdsc0R/6EuwH1ahvE4wTjIEl2l5HeEFL4hKXnU6q3qnyXb/thGIEUpS4MetYumUB/Ks1VOACoLWLZGYxRuEF4gUdBCtLvWERgXkdUvkn1sAqrU+b2HH0oQQCtAm274bvdGEUN0BajRkNYY2GNnAd0HoAC9kaSaZMMmuhul7k64aq3kb4wwXVJLjHDF1pVm2I4JZmuQ0G9HZRFQesFlM6eoaiYJFGsdsGCVuGmRpKugIsjC0XkDpCMAxtB3gODJwPAVujJPBRpOwgGJKjyKLCzLHCc5hFjbSHa01dX7FeCm094AI2Qj1wRXBXXDRCSw2f4n2PPM3y1zbdXaybhdONiLXwoTu9AITB9BLWT/IPl2YYvODRYLIC/XE8mV6ehFzBcogSZ+629zREWRKxFPi6BERXQZWltE9fR8VtOAhqblFO3SPjSHyZtFejro4Jo6xwfh2hye2vNHmCVvmCfdkPKFQ58ATl+EJ61I8wZuOigJuWhWWOKVz0+r5lgkaKJt6d8szzR0j9bD6NFE9QYPl7au8H97WHhKTbeGBxN5NYubFSUyhztOT2NcmI/skJCOxgqWpWWHbRcWGolWDk4PZyuqOY7n2y2xlXYCt7D1s9dvAVsdzzSyFT39WtnIkLcvqzeMRi4A1qui40DtRmVtIqIuec4mhmUoi+ERe96uU01KcrfCpa9kHmckw3evuPmfteusViUrcpOqqSyb+20juWLximIOR35d91X2jke8de//9EGe5Emd9p/38TkU89mgBfwRCDwQuGE2FxJuA0AW+BUYeS9BfL2BFwQh4UwmPB4WiJTLrbmqzGXKir72pSWaTwgTXVQFH51S7mifvaqEDfAd4LggtEAQgcHhixHGwldjAo8hwWOWACkMm8X0Gl9NCZGZ8cYg4ft8g4r9u+AwnUjta9J2uFnVToUVHoUXrZPZrHcfpLHXVwmabgMU3AY2v8H0LezikOg8j+F7XntVdBZZ0BZZOdkqly/G5wRfqoS+kazvhENt7pyuka6a/41bZ9nm9IV0Os3Bu0kEwEfxFmYtas9SUDfghe6ADzziE0d5rqqhCNEiPbeR+aVPFd3dOxWoVtYnJPKepoiuCNAqMUEkA/LDl8QwYORNGlObseTEiO8XcB55yRLQ+zaGWkc8Rwb7RsQaMnA8j3sUxonKLKX14jEEqHqEwUaJmwMhZMGIpvtQ6L0YMS7Z5h+jsZaKzYnm9Gp3Vj348+jEI2QOEPh2EnH5ByBkg9Okg1K8vRg35+/MBQn2HUL/OGQ2FvWw4GRHz3wGX8+8K1wVXJdck1YamO8t1U0hTCf/PDpqoUT3mVveY356w2UEki/BMuWT0Az7BG3ZpqR7yvmiau8zzp/4/c+MCZorXrR5oK37V6TrK64agQsO2dT8j3hm8R1mwveuksPbr9eGJfMtVCPgf75NQYOBcdFo+IhLN60x9MarnYda3uw+22Q2EGqobQqpTNf1w94FmmztkVfSzualnhv8D</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -374,7 +374,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺
**需要随机获取数据源中的元素的场景**
- 举例 :抽奖系统、随机。
- 举例 :抽奖系统、随机点名等场景
- 相关命令:`SPOP`(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、`SRANDMEMBER`(随机获取集合中的元素,适合允许重复中奖的场景)。
## Sorted Set有序集合

View File

@ -59,7 +59,7 @@ Redis 提供了两个命令来生成 RDB 快照文件:
appendonly yes
```
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令Redis 就会将该命令写入到 AOF 缓冲区 `server.aof_buf` 中,然后再根据 `appendfsync` 配置来决定何时将其同步到硬盘中的 AOF 文件。
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令Redis 就会将该命令写入到 AOF 缓冲区 `server.aof_buf` 中,然后再根据持久化方式( `fsync`策略)的配置来决定何时将其同步到硬盘中的 AOF 文件。
AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 `appendonly.aof`
@ -120,6 +120,12 @@ AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的
Redis 7.0 版本之前如果在重写期间有写入命令AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
Redis 7.0 版本之后AOF 重写机制得到了优化改进。下面这段内容摘自阿里开发者的[从Redis7.0发布看Redis的过去与未来](https://mp.weixin.qq.com/s/RnoPPL7jiFSKkx3G4p57Pg) 这篇文章。
> AOF 重写期间的增量数据如何处理一直是个问题在过去写期间的增量数据需要在内存中保留写结束后再把这部分增量数据写入新的AOF文件中以保证数据完整性。可以看出来AOF 写会额外消耗内存和磁盘IO这也是Redis AOF 写的痛点,虽然之前也进行过多次改进但是资源消耗的本质问题一直没有解决。
>
> 阿里云的Redis企业版在最初也遇到了这个问题在内部经过多次迭代开发实现了Multi-part AOF机制来解决同时也贡献给了社区并随此次7.0发布。具体方法是采用base全量数据+inc增量数据独立文件存储的方式彻底解决内存和IO资源的浪费同时也支持对历史AOF文件的保存管理结合AOF文件中的时间信息还可以实现PITR按时间点恢复阿里云企业版Tair已支持这进一步增强了Redis的数据可靠性满足用户数据回档等需求。
**相关 issue** [Redis AOF 重写描述不准确 #1439](https://github.com/Snailclimb/JavaGuide/issues/1439)
## Redis 4.0 对于持久化机制做了什么优化?
@ -151,4 +157,10 @@ Redis 7.0 版本之前如果在重写期间有写入命令AOF 可能会使
- Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。
- 不建议单独使用 AOF因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
- 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。
- 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。
## 参考
- Redis persistence - Redis 官方文档https://redis.io/docs/management/persistence/
- The difference between AOF and RDB persistencehttps://www.sobyte.net/post/2022-04/redis-rdb-and-aof/
- Redis AOF 持久化详解 - 程序员历小冰http://remcarpediem.net/article/376c55d8/

View File

@ -18,11 +18,11 @@ head:
[Redis](https://redis.io/) 是一个基于 C 语言开发的开源数据库BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的内存数据库读写速度非常快被广泛应用于缓存方向。并且Redis 存储的是 KV 键值对数据。
为了满足不同的业务场景Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap。并且Redis 还支持事务 、持久化、Lua 脚本、多种开箱即用的集群方案Redis Sentinel、Redis Cluster
为了满足不同的业务场景Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap、HyperLogLog、GEO。并且Redis 还支持事务 、持久化、Lua 脚本、多种开箱即用的集群方案Redis Sentinel、Redis Cluster
Redis 没有外部依赖Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。
个人学习的话,你可以自己本机安装 Redis 或者通过 Redis 官网提供的[在线 Redis 环境](https://try.redis.io/)来实际体验 Redis。
个人学习的话,你可以自己本机安装 Redis 或者通过 Redis 官网提供的[在线 Redis 环境](https://try.redis.io/)(少部分命令无法使用)来实际体验 Redis。
![try-redis](https://oss.javaguide.cn/github/javaguide/database/redis/try.redis.io.png)
@ -30,11 +30,11 @@ Redis 没有外部依赖Linux 和 OS X 是 Redis 开发和测试最多的两
### Redis 为什么这么快?
Redis 内部做了非常多的性能优化,比较重要的主要有下面 3 点:
Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:
- Redis 基于内存,内存的访问速度是磁盘的上千倍;
- Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用Redis 线程模式后面会详细介绍到);
- Redis 内置了多种优化过后的数据结构实现,性能非常高。
1. Redis 基于内存,内存的访问速度是磁盘的上千倍;
2. Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用Redis 线程模式后面会详细介绍到);
3. Redis 内置了多种优化过后的数据结构实现,性能非常高。
下面这张图片总结的挺不错的,分享一下,出自 [Why is Redis so fast?](https://twitter.com/alexxubyte/status/1498703822528544770) 。
@ -69,7 +69,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
3. **Redis 有灾难恢复机制。** 因为可以把缓存中的数据持久化到磁盘上。
4. **Redis 在服务器内存使用完之后可以将不用的数据放到磁盘上。但是Memcached 在服务器内存使用完之后,就会直接报异常。**
5. **Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的。**
6. **Memcached 是多线程,非阻塞 IO 复用的网络模型Redis 使用单线程的多路 IO 复用模型。** Redis 6.0 引入了多线程 IO
6. **Memcached 是多线程,非阻塞 IO 复用的网络模型Redis 使用单线程的多路 IO 复用模型。** Redis 6.0 针对网络数据的读写引入了多线程)
7. **Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且Redis 支持更多的编程语言。**
8. **Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。**
@ -79,13 +79,13 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
下面我们主要从“高性能”和“高并发”这两点来回答这个问题。
**高性能**
**1、高性能**
假如用户第一次访问数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。但是,如果说,用户访问的数据属于高频数据并且不会经常改变的话,那么我们就可以很放心地将该用户访问的数据存在缓存中。
**这样有什么好处呢?** 那就是保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。
**高并发**
**2、高并发**
一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右4 核 8g ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 Redis 的情况Redis 集群的话会更高)。
@ -115,6 +115,8 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
> 实际项目中也没见谁使用 Redis 来做消息队列,对于这部分知识点大家了解就好了。
先说结论:可以是可以,但不建议使用 Redis 来做消息队列。和专业的消息队列相比,还是有很多欠缺的地方。
**Redis 2.0 之前,如果想要使用 Redis 来做消息队列的话,只能通过 List 来实现。**
通过 `RPUSH/LPOP` 或者 `LPUSH/RPOP`即可实现简易版消息队列
@ -145,7 +147,7 @@ null
**Redis 2.0 引入了 发布订阅 (pub/sub) 解决了 List 实现消息队列没有广播机制的问题。**
pub/sub 中引入了一个概念叫 channel频道发布订阅机制的实现就是基于这个 channel 来做的。
pub/sub 中引入了一个概念叫 **channel频道**,发布订阅机制的实现就是基于这个 channel 来做的。
pub/sub 涉及发布者和订阅者(也叫消费者)两个角色:
@ -184,7 +186,11 @@ pub/sub 既能单播又能广播,还支持 channel 的简单正则匹配。不
### String 的应用场景有哪些?
- 常规数据(比如 session、token、、序列化后的对象的缓存
String 是 Redis 中最简单同时也是最常用的一个数据结构。String 是一种二进制安全的数据结构,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。
String 的常见应用场景如下:
- 常规数据(比如 session、token、序列化后的对象、图片的路径的缓存
- 计数比如用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数;
- 分布式锁(利用 `SETNX key value` 命令可以实现一个最简易的分布式锁)
- ......
@ -282,7 +288,7 @@ struct sdshdr {
- 用户 id 为 key
- 商品 id 为 field商品数量为 value
![Hash维护简单的购物车信息](./images/hash-shopping-cart.png)
![Hash维护简单的购物车信息](https://oss.javaguide.cn/github/javaguide/database/redis/hash-shopping-cart.png)
那用户购物车信息的维护具体应该怎么操作呢?
@ -302,18 +308,37 @@ Redis 中有一个叫做 `sorted set` 的数据结构经常被用在各种排行
![](https://oss.javaguide.cn/github/javaguide/database/redis/2021060714195385.png)
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) 的「技术面试题篇」就有一篇文章详细介绍如何使用 Sorted Set 来设计制作一个排行榜。
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) 的「技术面试题篇」就有一篇文章详细介绍如何使用 Sorted Set 来设计制作一个排行榜,感兴趣的小伙伴可以看看
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719071115140.png)
### 使用 Set 实现抽奖系统需要用到什么命令
### Set 的应用场景是什么
Redis 中 `Set` 是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 `HashSet`
Set 的常见应用场景如下:
- 存放的数据不能重复的场景:网站 UV 统计(数据量巨大的场景还是 `HyperLogLog`更适合一些)、文章点赞、动态点赞等等。
- 需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集) 、订阅号推荐(差集+交集) 等等。
- 需要随机获取数据源中的元素的场景:抽奖系统、随机点名等等。
### 使用 Set 实现抽奖系统怎么做?
如果想要使用 Set 实现一个简单的抽奖系统的话,直接使用下面这几个命令就可以了:
- `SADD key member1 member2 ...`:向指定集合添加一个或多个元素。
- `SPOP key count` 随机移除并获取指定集合中一个或多个元素,适合不允许重复中奖的场景。
- `SRANDMEMBER key count` : 随机获取指定集合中指定数量的元素,适合允许重复中奖的场景。
### 使用 Bitmap 统计活跃用户怎么做?
使用日期(精确到天)作为 key然后用户 ID 为 offset如果当日活跃过就设置为 1。
Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。
你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量
![img](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220720194154133.png)
如果想要使用 Bitmap 统计活跃用户的话,可以使用日期(精确到天)作为 key然后用户 ID 为 offset如果当日活跃过就设置为 1。
初始化数据:
@ -346,6 +371,11 @@ Redis 中有一个叫做 `sorted set` 的数据结构经常被用在各种排行
### 使用 HyperLogLog 统计页面 UV 怎么做?
使用 HyperLogLog 统计页面 UV主要需要用到下面这两个命令
- `PFADD key element1 element2 ...`:添加一个或多个元素到 HyperLogLog 中。
- `PFCOUNT key1 key2`:获取一个或者多个 HyperLogLog 的唯一计数。
1、将访问指定页面的每个用户 ID 添加到 `HyperLogLog` 中。
```bash
@ -358,7 +388,11 @@ PFADD PAGE_1:UV USER1 USER2 ...... USERn
PFCOUNT PAGE_1:UV
```
## Redis 线程模型
## Redis 持久化机制(重要)
Redis 持久化机制RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持久化) 相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 Redis 持久化机制相关的知识点和问题: [Redis 持久化机制详解](./redis-persistence.md) 。
## Redis 线程模型(重要)
对于读写命令来说Redis 一直是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作, Redis 6.0 版本之后引入了多线程来处理网络请求(提高网络 IO 读写性能)。
@ -388,7 +422,7 @@ Redis 通过 **IO 多路复用程序** 来监听来自客户端的大量连接
- 文件事件分派器(将 socket 关联到相应的事件处理器)
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
![文件事件处理器](https://oss.javaguide.cn/github/javaguide/database/redis/redis-event-handler.png)
![文件事件处理器file event handler](https://oss.javaguide.cn/github/javaguide/database/redis/redis-event-handler.png)
相关阅读:[Redis 事件机制详解](http://remcarpediem.net/article/1aa2da89/) 。
@ -520,15 +554,10 @@ Redis 提供 6 种数据淘汰策略:
7. **volatile-lfuleast frequently used**:从已设置过期时间的数据集(`server.db[i].expires`)中挑选最不经常使用的数据淘汰。
8. **allkeys-lfuleast frequently used**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。
## Redis 持久化机制
Redis 持久化机制RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持久化) 相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 Redis 持久化机制相关的知识点和问题: [Redis 持久化机制详解](./redis-persistence.md) 。
## 参考
- 《Redis 开发与运维》
- 《Redis 设计与实现》
- Redis 命令手册https://www.redis.com.cn/commands.html
- WHY Redis choose single thread (vs multi threads): [https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153](https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153)
- The difference between AOF and RDB persistencehttps://www.sobyte.net/post/2022-04/redis-rdb-and-aof/
- Redis AOF 持久化详解 - 程序员历小冰http://remcarpediem.net/article/376c55d8/

View File

@ -14,9 +14,19 @@ head:
## Redis 事务
### 什么是 Redis 事务?
你可以将 Redis 中的事务理解为 **Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。**
Redis 事务实际开发中使用的非常少,功能比较鸡肋,不要将其和我们平时理解的关系型数据库的事务混淆了。
除了不满足原子性和持久性之外,事务中的每条命令都会与 Redis 服务器进行网络交互,这是比较浪费资源的行为。明明一次批量执行多个命令就可以了,这种操作实在是看不懂。
因此Redis 事务是不建议在日常开发中使用的。
### 如何使用 Redis 事务?
Redis 可以通过 **`MULTI``EXEC``DISCARD``WATCH`** 等命令来实现事务(transaction)功能。
Redis 可以通过 **`MULTI``EXEC``DISCARD``WATCH`** 等命令来实现事务(Transaction)功能。
```bash
> MULTI
@ -131,22 +141,36 @@ Redis 的事务和我们平时理解的关系型数据库的事务不同。我
3. **持久性Durability** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
4. **一致性Consistency** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
Redis 事务在运行错误的情况下除了执行过程中出现错误的命令外其他命令都能正常执行。并且Redis 事务是不支持回滚roll back操作的。因此Redis 事务其实是不满足原子性的(而且不满足持久性)
Redis 事务在运行错误的情况下除了执行过程中出现错误的命令外其他命令都能正常执行。并且Redis 事务是不支持回滚roll back操作的。因此Redis 事务其实是不满足原子性的。
Redis 官网也解释了自己为啥不支持回滚。简单来说就是 Redis 开发者们觉得没必要支持回滚这样更简单便捷并且性能更好。Redis 开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。
![Redis 为什么不支持回滚](https://oss.javaguide.cn/github/javaguide/database/redis/redis-rollback.png)
你可以将 Redis 中的事务就理解为 **Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。**
除了不满足原子性之外,事务中的每条命令都会与 Redis 服务器进行网络交互,这是比较浪费资源的行为。明明一次批量执行多个命令就可以了,这种操作实在是看不懂。
因此Redis 事务是不建议在日常开发中使用的。
**相关 issue** :
- [issue452: 关于 Redis 事务不满足原子性的问题](https://github.com/Snailclimb/JavaGuide/issues/452) 。
- [Issue491:关于 redis 没有事务回滚?](https://github.com/Snailclimb/JavaGuide/issues/491)
- [issue#452: 关于 Redis 事务不满足原子性的问题](https://github.com/Snailclimb/JavaGuide/issues/452) 。
- [Issue#491:关于 Redis 没有事务回滚?](https://github.com/Snailclimb/JavaGuide/issues/491)
### Redis 事务支持持久性吗?
Redis 不同于 Memcached 的很重要一点就是Redis 支持持久化,而且支持 3 种持久化方式:
- 快照snapshottingRDB
- 只追加文件append-only file, AOF
- RDB 和 AOF 的混合持久化(Redis 4.0 新增)
与 RDB 持久化相比AOF 持久化的实时性更好。在 Redis 的配置文件中存在三种不同的 AOF 持久化方式( `fsync`策略),它们分别是:
```bash
appendfsync always #每次有数据修改发生时都会调用fsync函数同步AOF文件,fsync完成后线程返回,这样会严重降低Redis的速度
appendfsync everysec #每秒钟调用fsync函数同步一次AOF文件
appendfsync no #让操作系统决定何时进行同步一般为30秒一次
```
AOF 持久化的`fsync`策略为 no 、everysec 时都会存在数据丢失的情况 。always 下可以基本是可以满足持久性要求的,但性能太差,实际开发过程中不会使用。
因此Redis 事务的持久性也是没办法保证的。
### 如何解决 Redis 事务的缺陷?
@ -160,7 +184,7 @@ Redis 从 2.6 版本开始支持执行 Lua 脚本,它的功能和事务非常
另外Redis 7.0 新增了 [Redis functions](https://redis.io/docs/manual/programmability/functions-intro/) 特性,你可以将 Redis functions 看作是比 Lua 更强大的脚本。
## Redis 性能优化
## Redis 性能优化(重要)
### 使用批量操作减少网络传输
@ -173,7 +197,9 @@ Redis 从 2.6 版本开始支持执行 Lua 脚本,它的功能和事务非常
其中,第 1 步和第 4 步耗费时间之和称为 **Round Trip Time (RTT,往返时间)** ,也就是数据在网络上传输的时间。
**使用批量操作可以减少网络传输次数,进而有效减小网络开销,大幅减少 RTT。**
使用批量操作可以减少网络传输次数,进而有效减小网络开销,大幅减少 RTT。
另外,除了能减少 RTT 之外,发送一次命令的 socket I/O 成本也比较高(涉及上下文切换,存在`read()``write()`系统调用),批量操作还可以减少 socket I/O 成本。这个在官方对 pipeline 的介绍中有提到https://redis.io/docs/manual/pipelining/ 。
#### 原生批量操作命令
@ -206,19 +232,31 @@ Redis 中有一些原生支持批量操作的命令,比如:
原生批量操作命令和 pipeline 的是有区别的,使用的时候需要注意:
- 原生批量操作命令是原子操作pipeline 是非原子操作
- pipeline 可以打包不同的命令,原生批量操作命令不可以
- 原生批量操作命令是原子操作pipeline 是非原子操作
- pipeline 可以打包不同的命令,原生批量操作命令不可以
- 原生批量操作命令是 Redis 服务端支持实现的,而 pipeline 需要服务端和客户端的共同实现。
顺带补充一下 pipeline 和 Redis 事务的对比:
- 事务是原子操作pipeline 是非原子操作。两个不同的事务不会同时运行,而 pipeline 可以同时以交错方式执行。
- Redis 事务中每个命令都需要发送到服务端,而 Pipeline 只需要发送一次,请求次数更少。
> 事务可以看作是一个原子操作,但其实并不满足原子性。当我们提到 Redis 中的原子操作时主要指的是这个操作比如事务、Lua 脚本不会被其他操作比如其他事务、Lua 脚本)打扰,并不能完全保证这个操作中的所有写命令要么都执行要么都不执行。这主要也是因为 Redis 是不支持回滚操作。
![](https://oss.javaguide.cn/github/javaguide/database/redis/redis-pipeline-vs-transaction.png)
另外pipeline 不适用于执行顺序有依赖关系的一批命令。就比如说你需要将前一个命令的结果给后续的命令使用pipeline 就没办法满足你的需求了。对于这种需求,我们可以使用 **Lua 脚本**
#### Lua 脚本
Lua 脚本同样支持批量操作多条命令。一段 Lua 脚本可以视作一条命令执行,可以看作是原子操作。一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行,保证了操作不会被其他指令插入或打扰,这是 pipeline 所不具备的。
Lua 脚本同样支持批量操作多条命令。一段 Lua 脚本可以视作一条命令执行,可以看作是 **原子操作** 也就是说,一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行,保证了操作不会被其他指令插入或打扰,这是 pipeline 所不具备的。
并且Lua 脚本中支持一些简单的逻辑处理比如使用命令读取值并在 Lua 脚本中进行处理,这同样是 pipeline 所不具备的。
不过, Redis Cluster 下 Lua 脚本的原子操作也无法保证了,原因同样是无法保证所有的 key 都在同一个 **hash slot**(哈希槽)上。
不过, Lua 脚本依然存在下面这些缺陷:
- 如果 Lua 脚本运行时出错并中途结束,之后的操作不会进行,但是之前已经发生的写操作不会撤销,所以即使使用了 Lua 脚本,也不能实现类似数据库回滚的原子性。
- Redis Cluster 下 Lua 脚本的原子操作也无法保证了,原因同样是无法保证所有的 key 都在同一个 **hash slot**(哈希槽)上。
### 大量 key 集中过期问题
@ -293,7 +331,7 @@ Biggest string found '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28
**参考答案** [Redis 内存碎片详解](https://javaguide.cn/database/redis/redis-memory-fragmentation.html)。
## Redis 生产问题
## Redis 生产问题(重要)
### 缓存穿透
@ -480,4 +518,5 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB然后直接删
- 《Redis 开发与运维》
- 《Redis 设计与实现》
- Redis Transactions : https://redis.io/docs/manual/transactions/ 。
- Redis Transactions : https://redis.io/docs/manual/transactions/
- What is Redis Pipelinehttps://buildatscale.tech/what-is-redis-pipeline/

View File

@ -0,0 +1,107 @@
---
title: 从校招入职腾讯的四年工作总结
category: 技术文章精选集
author: pioneeryi
tag:
- 个人经历
---
程序员是一个流动性很大的职业,经常会有新面孔的到来,也经常会有老面孔的离开,有主动离开的,也有被动离职的。
再加上这几年卷得厉害,做的事更多了,拿到的却更少了,互联网好像也没有那么香了。
人来人往,变动无常的状态,其实也早已习惯。
打工人的唯一出路,无外乎精进自己的专业技能,提升自己的核心竞争力,这样无论有什么变动,走到哪里,都能有口饭吃。
今天分享一位博主,校招入职腾讯,工作四年后,离开的故事。
至于为什么离开,我也不清楚,可能是有其他更好的选择,或者是觉得当前的工作对自己的提升有限。
**下文中的“我”,指这位作者本人。**
> 原文地址https://zhuanlan.zhihu.com/p/602517682
研究生毕业后, 一直在腾讯工作,不知不觉就过了四年。个人本身没有刻意总结的习惯,以前只顾着往前奔跑了,忘了停下来思考总结。记得看过一个职业规划文档,说的三年一个阶段,五年一个阶段的说法,现在恰巧是四年,同时又从腾讯离开,该做一个总结了。
先对自己这四年做一个简单的评价吧:个人认为,没有完全的浪费和辜负这四年的光阴。为何要这么说了?因为我发现和别人对比,好像意义不大,比我混的好的人很多;比我混的差的人也不少。说到底,我只是一个普普通通的人,才不惊人,技不压众,接受自己的平凡,然后看自己做的,是否让自己满意就好。
下面具体谈几点吧我主要想聊下工作绩效EPC嫡系看法最后再谈下收获。
## 工作情况
我在腾讯内部没有转过岗但是做过的项目也还是比较丰富的包括BUGLY、分布式调用链Huskie)、众包系统SOHO)EPC度量系统。其中一些是对外的一些是内部系统可能有些大家不知道。还是比较感谢这些项目经历既有纯业务的系统也有偏框架的系统让我学到了不少知识。
接下来,简单介绍一下每个项目吧,毕竟每一个项目都付出了很多心血的:
BUGLY这是一个终端Crash联网上报的系统很多APP都接入了。Huskie这是一个基于zipkin搭建的分布式调用链跟踪项目。SOHO这是一个众包系统主要是将数据标准和语音采集任务众包出去让人家做。EPC度量系统这是研发效能度量系统主要是度量研发效能情况的。这里我谈一下对于业务开发的理解和认识很多人可能都跟我最开始一样有一个疑惑整天做业务开发如何成长换句话说就是说整天做CRUD如何成长我开始也有这样的疑惑后来我转变了观念。
我觉得对于系统的复杂度,可以粗略的分为技术复杂度和业务复杂度,对于业务系统,就是业务复杂度高一些,对于框架系统就是技术复杂度偏高一些。解决这两种复杂度,都具有很大的挑战。
此前做过的众包系统就是各种业务逻辑搞过去搞过来其实这就是业务复杂度高。为了解决这个问题我们开始探索和实践领域驱动DDD确实带来了一些帮助不至于系统那么混乱了。同时我觉得这个过程中自己对于DDD的感悟对于我后来的项目系统划分和设计以及开发都带来了帮助。
当然DDD不是银弹我也不是吹嘘它有多好只是了解了它后有时候设计和开发时能换一种思路。
可以发现,其实平时咱们做业务,想做好,其实也没那么容易,如果可以多探索多实践,将一些好的方法或思想或架构引入进来,与个人和业务都会有有帮助。
## 绩效情况
我在腾讯工作四年,腾讯半年考核一次,一共考核八次,回想了下,四年来的绩效情况为:三星,三星,五星,三星,五星,四星,四星,三星。统计一下, 四五星占比刚好一半。
![](https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/640.png)
PS还好以前有奖杯不然一点念想都没了。(现在腾讯似乎不发了)
印象比较深的是两次五星获得经历。第一次五星是工作的第二年那一年是在做众包项目因为项目本身难度不大因此我把一些精力投入到了团队的基础建设中帮团队搭建了java以及golang的项目脚手架又做了几次中心技术分享最终Leader觉得我表现比较突出因此给了我五星。看来主动一些与个人与团队都是有好处的最终也能获得一些回报。
第二次五星就是与EPC有关了。说一个搞笑的事我也是后来才知道的项目初期总监去汇报时给老板演示系统加载了很久指标才刷出来总监很不好意思的说正在优化过了一段时间又去汇报演示结果又很尴尬的刷了很久才出来总监无赖表示还是在优化。没想到自己曾经让总监这么丢脸哈哈。好吧说一下结果最终我自己写了一个查询引擎替换了Mondrian之后再也没有出现那种尴尬的情况了。随之而来也给了好绩效鼓励。做EPC度量项目我觉得自己成长很大比如抗压能力当你从零到一搭建一个系统时会有一个先扛住再优化的过程此外如果你的项目很重要尤其是数据相关那么任何一点问题都可能让你神经紧绷得想尽办法降低风险和故障。此外另一个不同的感受就是以前得项目我大多是开发者而这个系统我是Owner负责人当你Owner一个系统时你得时刻负责同时还需要思考系统的规划和方向此外还需要分配好需求和把控进度角色体验跟以前完全不一样。
## 谈谈EPC
很多人都骂EPC或者笑EPC作为度量平台核心开发者之一我来谈谈客观的看法。
其实EPC初衷是好的希望通过全方位多维度的研效指标来度量研发效能各环节的质量进而反推业务提升研发效能。然而最终在实践的过程中才发现客观条件并不支持工具还没建设好此外一味的追求指标数据使得下面的人想方设法让指标好看最终违背了初衷。
为什么说EPC好了其实如果你仔细了解下EPC你就会发现他是一套相当完善且比较先进的指标度量体系。覆盖了需求代码缺陷测试持续集成运营部署各个环节。
此外这个过程中虽然一些人和一些业务做弊但绝大多数业务还是做出了改变的比如微视那边的人反馈是以前的代码写的跟屎一样当有了EPC后代码质量好了很多。虽然最后微视还是亡了但是大厦将倾EPC是救不了的亡了也更不能怪EPC。
## 谈谈嫡系
大家都说腾讯,嫡系文化盛行。但其实我觉得在那个公司都一样吧。这也符合事物的基本规律,人们只相信自己信任并熟悉的人。作为领导,你难道会把把重要的事情交给自己不熟悉的人吗?
其实我也不知道我算不算嫡系,脉脉上有人问过”怎么知道自己算不算嫡系”,下面有一个回答,我觉得很妙:如果你不知道你是不是嫡系,那你就不是。哈哈,这么说来,我可能不是。
但另一方面,后来我负责了团队内很重要的事情,应该是中心内都算很重要的事,我独自负责一个方向,直接向总监汇报,似乎又有点像。
网上也有其他说法一针见血是不是嫡系就看钱到不到位这么说也有道理。我在7级时就发了股票自我感觉还是不错的。我当时以为不出意外的话我以后的钱途和发展是不是就会一帆风顺。不出意外就出了意外第二年EPC不达预期部门总经理和总监都被换了中心来了一个新的的总监。
好吧,又要重新建立信任了。再到后来,是不是嫡系已经不重要了,因为大环境不好,又加上裁员,大家主动的被动的差不多都走了。
总结一下,嫡系的存在,其实情有可原。怎么样成为嫡系了?其实我也不知道。不过,我觉得,与其思考怎么成为嫡系,不如思考怎么展现自己的价值和能力,当别人发现你的价值和能力了,那自然更多的机会就会给予你,有了机会,只要把握住了,那就有更多的福利了。
## 再谈收获
收获,什么叫做收获了?个人觉得无论是外在的物质,技能,职级;还是内在的感悟,认识,都算收获。
先说一些可量化的吧,我觉得有:
- 级别上,升上了九级,高级工程师。虽然大家都在说腾讯职级缩水,但是有没有高工的能力自己其实是知道的,我个人感觉,通过我这几年的努力,我算是达到了我当时认为的我需要在高工时达到的状态;
- 绩效上自我评价个人不是一个特别卷的人或者说不会为了卷而卷。但是如果我认定我应该把它做好得我的Owner意识以及负责态度我觉得还是可以的。最终在腾讯四年的绩效也还算过的去。再谈一些其他软技能方面:
**1、文档能力**
作为程序员,文档能力其实是一项很重要的能力。其实我也没觉得自己文档能力有多好,但是前后两任总监,都说我的文档不错,那看来,我可能在平均水准之上。
**2、明确方向**
最后,说一个更虚的,但是我觉得最有价值的收获: 我逐渐明确了,或者确定了以后的方向和路,那就是走数据开发。
其实,找到并确定一个目标很难,身边有清晰目标和方向的人很少,大多数是迷茫的。
前一段时间,跟人聊天,谈到职业规划,说是可以从两个角度思考:
- 选一个业务方向,比如电商,广告,不断地积累业务领域知识和业务相关技能,随着经验的不断积累,最终你就是这个领域的专家。
- 深入一个技术方向,不断钻研底层技术知识,这样就有希望成为此技术专家。坦白来说,虽然我深入研究并实践过领域驱动设计,也用来建模和解决了一些复杂业务问题,但是发自内心的,我其实更喜欢钻研技术,同时,我又对大数据很感兴趣。因此,我决定了,以后的方向,就做数据相关的工作。
腾讯的四年,是我的第一份工作经历,认识了很多厉害的人,学到了很多。最后自己主动离开,也算走的体面(即使损失了大礼包),还是感谢腾讯。

View File

@ -1,5 +1,5 @@
# 技术文章精选
# 程序人生
::: tip 这是一则或许对你有用的小广告
👉 欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](./../about-the-author/zhishixingqiu-two-years.md),干货很多!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
@ -7,7 +7,7 @@
👉 [《Java 面试指北》](./../zhuanlan/java-mian-shi-zhi-bei.md)持续更新完善中!这是一份教你如何更高效地准备面试的小册,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ......)、优质面经等内容。
:::
这里主要会收录一些我看到的优质技术文章,每一篇都值得你阅读 3 遍以上!
这里主要会收录一些我看到的和程序员密切相关的非技术类的优质文章,每一篇都值得你阅读 3 遍以上!常看常新!
## 练级攻略
@ -17,6 +17,7 @@
## 个人经历
- [从校招入职腾讯的四年工作总结](./personal-experience/four-year-work-in-tencent-summary.md)
- [我在滴滴和头条的两年后端研发工作经验分享](./personal-experience/two-years-of-back-end-develop--experience-in-didi&toutiao.md)
- [一个中科大差生的 8 年程序员工作总结](./personal-experience/8-years-programmer-work-summary.md)
- [华为 OD 275 天后,我进了腾讯!](./personal-experience/huawei-od-275-days.md)

View File

@ -9,7 +9,7 @@ category: 开源项目
👉 [《Java 面试指北》](./../zhuanlan/java-mian-shi-zhi-bei.md)持续更新完善中!这是一份教你如何更高效地准备面试的小册,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ......)、优质面经等内容。
:::
精选 Github 和 Gitee 上优质的 Java 开源项目.
精选 Github 和 Gitee 上优质的 Java 开源项目
欢迎大家在项目 [issues 区](https://github.com/CodingDocs/awesome-java/issues)推荐自己认可的 Java 开源项目,让我们共同维护一个优质的 Java 开源项目精选集!
@ -18,7 +18,7 @@ category: 开源项目
如果内容对你有帮助的话,欢迎给本项目点个 Star。我会用我的业余时间持续完善这份名单感谢
另外,我的公众号还会定期分享优质开源项目,每一期我都会精选 5 个高质量的 Java 开源项目。
另外,我的公众号还会定期分享优质开源项目,每月一期,每一期我都会精选 5 个高质量的 Java 开源项目。
目前已经更新到了第 16 期:
@ -38,6 +38,7 @@ category: 开源项目
14. [8.8k star这可能是我见过最强的开源支付系统](https://mp.weixin.qq.com/s/vfPSXtOgefwonbnP53KlOQ)
15. [31.2k!这是我见过最强的后台管理系统 ](https://mp.weixin.qq.com/s/esaivn2z_66CcrRJlDYLEA)
16. [14.3k star这是我见过最强的第三方登录工具库](https://mp.weixin.qq.com/s/6-TnCHUMEIFWQVl-pIWBOA)
17. [3.2k!这是我见过最强的消息推送平台!!](https://mp.weixin.qq.com/s/heag76H4UwZmr8oBY_2gcw)
推荐你在我的公众号“**JavaGuide**”回复“**开源**”在线阅读[「优质开源项目推荐」](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg2OTA0Njk0OA==&action=getalbum&album_id=1345382825083895808&scene=173&from_msgid=2247516459&from_itemidx=1&count=3&nolastread=1#wechat_redirect)系列。