0%

  • 思考不完整

    • 逻辑上下文更新慢, 逻辑修改太多次后, 出现混乱, 失忆
    • 缺少完整的体系参考, 遗漏一些方面的思考
    • 疲劳, 思维变懒
  • 个人防御墙
    由于情绪, 偏见引起的和他人的对立面, 导致无意义的争执

沟通交流时说话不经过思考, 容易说出一些不恰当的话.

先思考, 将要说的话过一遍, 再说出来, 这样又觉得思维速度跟不上说话速度, 容易出现卡顿.

说话时, 先产生一个念头, 是自己需要表达给对方的一个思想. 需要在说出之前整理, 过滤, 确认符合情理逻辑, 再说出口.

有时候我们能很流畅的和别人交流, 是因为我们知道对方在想什么, 接下来会发生什么, 应该说些什么.

所以我们需要整理思考什么呢?

  • 人物
    对方正在做什么
    对方的背景, 知识面等
    对方需要什么信息
  • 情境
    身处的环境
    我的立场
  • 上下文逻辑
    已经表达过什么信息

我们需要掌握这些情况, 防止自己说完出洋相.
在还没掌握这些情况之前, 要么聆听, 要么提问获取信息之后再表达.

有气场的人一般有什么共同点?

  1. 理性
    沟通过程中非常冷静, 没有幅度太大的的情感波动和肢体语言, 说话匀速, 眼神坚定.
    通常都是微笑表达友善, 说话有条理, 思考过程中也是看着对方, 目光不会偏移到别处.

  2. 自信
    有成功的经验和信念支撑, 不会轻易让对方知道自己的短处.
    即使嘴上说的丧气的话, 表情和肢体语言上却没有太大变化, 似乎一切都在掌控范围内.
    自信源于自己成功处理过某些问题, 那么再遇到类似问题的时候就有底气. 如果一直把问题放在那里不处理, 在没有解决前与人谈及相关问题时, 就会有心虚的表现.

  3. 有阅历
    似乎一切事情对方都能说出几分道理.

  4. 仪态
    着装看上去给人干净整洁, 舒适随和感.
    站坐姿态大方得体.

1. Redis 是单线程的, 为什么快?

  1. 单线程没有线程上下文切换问题, 没有资源竞争
  2. 采用IO多路复用的方式并发处理任务
  3. 纯内存操作, 没有磁盘IO限制
  4. CPU 不是 Redis 的使用瓶颈, 一般内存和网络带宽才可能是.

2. Redis 和其他 key-value 存储有什么不同

  • 所有命令都是原子性操作
  • 数据结构多样, 且对使用者透明
  • 运行在内存中, 可以持久化到磁盘

3. Redis 数据结构有哪些?

  • string
  • hash 表, H开头的命令
  • list
  • set
  • zset 有序集合

4. Redis 慢怎么办?

延迟
  1. 查看慢日志
    1
    2
    3
    4
    config set slowlog-log-slower-than 0 # 设置慢日志门槛, 到过这个值就会记录到慢日志中, 单位微秒
    config set slowlog-man-len 100 # 存储的慢日志数量

    slowlog show
  2. 检查最大内存策略导致
    对大对象进行删除, 过期和淘汰操作 (由于最大内存策略导致) 都属于阻塞操作
    如果经常对大对象进行删除, 过期和淘汰操作, 可尝试把大对象拆分成多个小对象.
    1
    config get maxmemory-policy

5. Redis 分布式锁

  1. 加锁

SET $resource_id $token EX $expired_time NX

resource_id: 锁名称, 用于标识资源的唯一ID
token: 随机值, 如果锁需要重入, 或者手动释放锁, 这个值必须保持一致.
expired_time: 锁自动过期时间

  1. 解锁

不使用 DEL 命令来释放锁, 而是发送一个 Lua 脚本, 这个脚本只在客户端传入的值和键的口令串相匹配时, 才对键进行删除.
比较 token 是否一致, 如果一致则可以解锁, 如果不一致, 说明锁已经被其他客户端获取了, 不能再操作.

1
2
3
4
5
6
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end

这个脚本可以通过 EVAL ...script... 1 resource_id token 命令来调用.

6. 常见问题

6.1. 缓存穿透

缓存穿透是指查询一定不存在的数据, 由于缓存没有命中时需要查询数据库, 查不到数据则不写入缓存, 这将导致这个不存在的数据每次请求都需要去数据库查询, 造成缓存穿透.

解决办法:

  1. 对所有可能查询的参数以hash形式存储, 在控制层先进行校验, 不符合则丢弃.
  2. 采用布隆过滤器, 将所有可能存在的数据哈希到一个足够大的bitmap中, 一个一定不存在的数据会被这个bitmap拦截掉, 从而避免了对底层存储系统的查询压力.
  3. 也可以采用一个更为简单粗暴的方法, 如果一个查询返回的数据为空 (不管是数 据不存在, 还是系统故障) , 我们仍然把这个空结果进行缓存, 但它的过期时间会很短, 最长不超过五分钟.

6.2. 缓存雪崩

如果缓存集中在一段时间内失效, 发生大量的缓存穿透, 所有的查询都落在数据库上, 造成了缓存雪崩.

这个没有完美解决办法, 但可以分析用户行为, 尽量让失效时间点均匀分布.大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程 (进程) 写, 从而避免失效时大量的并发请求落到底层存储系统上.

解决办法:

  1. 在并发量不高的情况下, 可以使用加锁排队, 但是这样会影响系统吞吐量.
  2. 提前更新过期时间, 如果发现缓存即将失效, 则重新reload, 当然需要控制reload的数量
  3. 不同的key, 设置不同的过期时间, 让缓存失效的时间点尽量均匀

6.3. 缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据 (一般是缓存时间到期) , 这时由于并发用户特别多, 同时读缓存没读到数据, 又同时去数据库去取数据, 引起数据库压力瞬间增大, 造成过大压力

  1. 设置热点数据永远不过期.
  2. 查询时加互斥锁, 保证同一时间只有一个线程区查询; 没有获取到锁的线程则轮询等待获取数据

6.4. 缓存一致性

场景: 在分布式系统中, 有一个频繁更新的数据要做缓存, 该怎么处理?

方案: 先更新数据库, 之后直接淘汰内存. 那么其他线程查询缓存时, 发现没有命中, 则通过查询数据库获取并缓存结果.

  1. 使用直接淘汰, 而不是更新缓存, 是为了防止高并发下更新缓存的顺序问题; 而如果要保证更新的顺序性, 需要有更新数据的版本, data + version 同时更新, 会有原子性的问题.
  2. 查询数据库需要考虑缓存击穿问题. 通过分布式锁保证只有一个线程去查询, 其他线程自旋等待.
  3. 如果要考虑数据更新完后, 还没来得及淘汰缓存, 就服务崩溃的问题. 也就是说不能等到下次更新数据时更新缓存. 那么这种依赖事务的数据不要放在缓存中. 如果要保证缓存一致性, 需要保证业务能幂等重试.

6.5. 什么样的数据能放在缓存中

  1. 业务完全依赖缓存中的数据, 如果缓存中没有数据则无法工作, 或者能够通过查询数据库恢复的数据.
  2. 缓存数据不一致不会影响业务.

7. Resource

1. pagecache

页高速缓存, 简称页高缓.

pagecache 是通过将磁盘中的数据缓存到内存中, 从而减少磁盘I/O操作, 从而提高性能.

1.1. pagecache 的管理

linux 通过 radix tree 和两个双向链表管理 pagecache.

radix tree 是一种搜索树, 用于快速查找脏的 (dirty) 或回写的 (writeback) 页面.

active_list 和 inactive_list 两个双向链表, 用于回收 pagecache.
active_list 中的 page 被认为是 hot, 内存回收时不能释放; 之后在 inactive_list 中的 page 才可以回收.
首次缓存数据的 page 会被加入到 inactive_list 中, 已经在 inactive_list 中的 page 再次访问就会移入 active_list 中.
这两个链表都使用了伪 LRU 算法, 从尾部加入, 从头部移除.
如果 active_list 中 page 数量远远大于 inactive_list, 那么 active_list 头部的就会被移入 inactive_list 中, 从而维持两个链表的平衡.

1.2. 读

当内核发起一个读请求时, 首先检查请求的数据是否在 pagecache 中, 如果没有命中 (cache miss), 则从磁盘中读取数据, 并缓存起来, 之后读相同文件的请求就能命中 (cache hit) 了.
pagecache 可以只缓存文件的部分内容, 不需要把整个文件都缓存起来.

1.3. 写

pagecache 缓存的存储设备称为后备存储 (backing store);
当内核发起一个写请求, 会直接往 page 中写入, 后备存储中的内容不会直接更新. 内核会将被写入的 page 标记为 dirty, 之后内核会周期性的写回到磁盘上.

2. Resource

Linux内核学习笔记 (八) Page Cache与Page回写

1. 如何在不改变代码的情况下, 改变类中方法的行为?

  1. 组合, 继承, 模板方法

如果开发时就能知道有这样的需求, 就可以通过这些方法实现可扩展的类

  1. 动态代理

通过 AOP 技术, 在运行期间动态代理对象

  1. ClassLoader
  • 利用 ClassLoader 加载顺序的特性, 让别的实现类在原来的类加载前加载
  • 加载时, 让他指向别的实现类
  1. Instrumentation

JTI 的 Instrumentation, 通过独立于程序的 agent, 修改和替换类的实现

Good judgment comes from experience, and experience comes from bad judgment
正确的判断来自于丰富的经验, 而大多数经验又来自于错误的判断.

如何从被游戏支配中解脱? 如果救治拖延症?

  • 先要离开当前状态, 比如冥想, 运动, 让自己独处
    然后脑中思考现在有什么事情要做, 怎么做, 定制一个计划
    有了明确的目标和计划之后就能摆脱那些负面的状态了

  • 重复的事情做多了就没有成就感, 变成了固定任务. 那由于人性的懒惰, 就开始变动逃避这些固定任务.