??Redis
1.Martin 表示,一个分布式系统,更像一个复杂的「野兽」,存在着你想不到的各种异常情况。
这些异常场景主要包括三大块,这也是分布式系统会遇到的三座大山:NPC。
- N:Network Delay, 络延迟
- P:Process Pause,进程暂停(GC)
- C:Clock Drift,时钟漂移
到这里我们再小结一下,基于 Redis 的实现分布式锁,前面遇到的问题,以及对应的解决方案:
- 死锁:设置过期时间
- 过期时间评估不好,锁提前过期:守护线程,自动续期
- 锁被别人释放:锁写入唯一标识,释放锁先检查标识,再释放
还有哪些问题场景,会危害 Redis 锁的安全性呢r> 之前分析的场景都是,锁在「单个」Redis 实例中可能产生的问题,并没有涉及到 Redis 的部署架构细节。
而我们在使用 Redis 时,一般会采用主从集群 + 哨兵的模式部署,这样做的好处在于,当主库异常宕机时,哨兵可以实现「故障自动切换」,把从库提升为主库,继续提供服务,以此保证可用性。
最后,用 Martin 在对于 Redlock 争论过后,写下的感悟来结尾:
“前人已经为我们创造出了许多伟大的成果:站在巨人的肩膀上,我们可以才得以构建更好的软件。无论如何,通过争论和检查它们是否经得起别人的详细审查,这是学习过程的一部分。但目标应该是获取知识,而不是为了说服别人,让别人相信你是对的。有时候,那只是意味着停下来,好好地想一想。”
- 统计一个位数组中非0二进制位的数量在数学上被称为”计算汉明重量”。
目前已知效率最好的通用算法为variable-precision SWAR算法,该算法通过一系列位移和位运算操作,可以在常数时间内计算多个字节的汉明重量,并且不需要使用任何额外的内存。
面试题:40亿qq 去重 1个G内存
1??有内存要求:
一个字节可以记录8个数是否存在(类似于计数排序),将QQ 对应的offset的值设置为1表示此数存在,遍历完40亿个QQ 后直接统计BITMAP上值为1的offset即可完成QQ 的去重。
2??无内存要求:
- 排序:① 首先将40亿个QQ 进行排序;② 从小到大遍历,跳过重复元素只取第一个元素。
- Set:将40亿个QQ 统统放进Set集合中,自动完成去重,
- redis中实现set用了两种结构:intset和hash table。 非数字或者大量数字时都会退化成hash table。 那么是否好的算法可以节省hash table的大小呢r> 其实早在1970年由Burton Howard Bloom提出的布隆过滤器(英语:Bloom Filter)。 它实际上是一个很长的二进制向量和一系列随机映射函数。 布隆过滤器可以用于检索一个元素是否在一个集合中。 它的优点是空间效率和查询时间都远远超过一般的算法, 缺点是有一定的误识别率和删除困难。
上面的使用多个hash方法来降低碰撞就是BloomFilter的核心思想。
BloomFilter 需要一个大的bitmap来存储。鉴于目前公司现状,最好的存储容器是redis。
-
哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而 Redis 去模拟关系型复杂查询开发困难且维护成本高。
-
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行 -
redis和memcached的一种代理实现就是Twemproxy
-
最后一个功能是事务,但 Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去。
-
Redis Cluster 使用分片机制,在内部分为 16384 个 slot 插槽,分布在所有 master 节点上,每个 master 节点负责一部分 slot。数据操作时按 key 做 CRC16 来计算在哪个 slot,由哪个 master 进行处理。数据的冗余是通过 slave 节点来保障。
-
因为慢日志中只记录一个命令真正操作内存数据的耗时,而 Redis 主动删除过期 key 的逻辑,是在命令真正执行之前执行的。
-
应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。
Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。
- 一般现代的服务器会有多个 CPU,而每个 CPU 又包含多个物理核心,每个物理核心又分为多个逻辑核心,每个物理核下的逻辑核共用 L1/L2 Cache。
不要让 Redis 进程只绑定在一个 CPU 逻辑核上,而是绑定在多个逻辑核心上,而且,绑定的多个逻辑核心最好是同一个物理核心,这样它们还可以共用 L1/L2 Cache。
- 操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。
- CPU 相关:使用复杂度过高命令、数据的持久化,都与耗费过多的 CPU 资源有关
- 内存相关:bigkey 内存的申请和释放、数据过期、数据淘汰、碎片整理、内存大页、内存写时复制都与内存息息相关
- 磁盘相关:数据持久化、AOF 刷盘策略,也会受到磁盘的影响
- 络相关:短连接、实例流量过载、 络流量过载,也会降低 Redis 性能
- 计算机系统:CPU 结构、内存分配,都属于最基础的计算机系统知识
- 操作系统:写时复制、内存大页、Swap、CPU 绑定,都属于操作系统层面的知识
-
Redis中只有 络请求模块和数据操作模块是单线程的。而其他的如持久化存储模块、集群支撑模块等是多线程的。
-
其中读写操作主要是涉及到的就是I/O操作,其中包括 络I/O和磁盘I/O。计算操作主要涉及到CPU。
-
Redis并没有在 络请求模块和数据操作模块中使用多线程模型,主要是基于以下四个原因:
- 1、Redis 操作基于内存,绝大多数操作的性能瓶颈不在 CPU
- 2、使用单线程模型,可维护性更高,开发,调试和维护的成本更低
- 3、单线程模型,避免了线程间切换带来的性能开销
- 4、在单线程中使用多路复用 I/O技术也能提升Redis的I/O利用率
-
Linux多路复用技术,就是多个进程的IO可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。
-
Redis 6.0中的多线程,也只是针对处理 络请求过程采用了多线程,而数据的读写命令,仍然是单线程处理的。
-
AOF 记录日志的方式被称为写后日志,也就是先执行命令再记录,而 MySQL 中的 redo log、binlog 等都是写前日志
-
首先,我们要明确一点,缓存不是更新,而应该是删除。(redis)
删除缓存有两种方式: -
先删除缓存,再更新数据库。解决方案是使用延迟双删。
-
先更新数据库,再删除缓存。解决方案是消息队列或者其他binlog同步,引入消息队列会带来更多的问题,并不推荐直接使用。(推荐)
-
lru 属性是创建对象的时候写入,对象被访问到时也会进行更新。正常人的思路就是最后决定要不要删除某一个键肯定是用当前时间戳减去 lru,差值最大的就优先被删除。但是 Redis 里面并不是这么做的,Redis 中维护了一个全局属性 lru_clock,这个属性是通过一个全局函数 serverCron 每隔 100 毫秒执行一次来更新的,记录的是当前 unix 时间戳。
最后决定删除的数据是通过 lru_clock 减去对象的 lru 属性而得出的。
这是因为这么做可以避免每次更新对象的 lru 属性的时候可以直接取全局属性,而不需要去调用系统函数来获取系统时间,从而提升效率(Redis 当中有很多这种细节考虑来提升性能,可以说是对性能尽可能的优化到极致)。
-
当我们采用 LFU 回收策略时,lru 属性的高 16 位用来记录访问时间(last decrement time:ldt,单位为分钟),低 8 位用来记录访问频率(logistic counter:logc),简称 counter。
-
为什么要使用时间来当做 ID 的一部分呢方面,我们要 满足 ID 自增 的属性,另一方面,也是为了 支持范围查找 的功能。由于 ID 和生成消息的时间有关,这样就使得在根据时间范围内查找时基本上是没有额外损耗的。
-
异步重试。什么是异步重试r> 其实就是把重试请求写到「消息队列」中,然后由专门的消费者来重试,直到成功。
-
要想做到强一致,最常见的方案是 2PC、3PC、Paxos、Raft 这类一致性协议,但它们的性能往往比较差,而且这些方案也比较复杂,还要考虑各种容错问题。
-
Redis端口是6379,这个端口 也不是随机选的,而是由手机键盘字母「MERZ」的位置决定的。「MERZ」在 Antirez 的朋友圈语言中是「愚蠢」的代名词
我写代码是为了更好的表达自我,这是艺术创作(乐趣),而不单单是为了把事情搞定(挣钱)。
我的目标更多的是为了追求美感,而最终能不能起到作用仅仅是附带的结果而已。我宁可大家认为我是一个糟糕的艺术家,也不希望只被看成一个优秀的程序员。
我们以优化代码为乐,我们相信编码是一件辛苦的工作,唯一对得起这辛苦的就是去享受它,如果我们在编码中失去了乐趣,那最好的解决办法就是停下来,我们决不会选择让Redis不好玩的开发模式。
-
使用过 Rabbitmq 的同学知道它使用起来有多复杂,发消息之前要创建 Exchange,再创建 Queue,还要将 Queue 和 Exchange 通过某种规则绑定起来,发消息的时候要指定 routing-key,还要控制头部信息。消费者在消费消息之前也要进行上面一系列的繁琐过程。
-
HyperLogLog 提供了两个指令 pfadd 和 pfcount,根据字面意义很好理解,一个是增加计数,一个是获取计数
它是 HyperLogLog 这个数据结构的发明人 Philippe Flajolet 的首字母缩写
-
我们平时用到的 HBase、Cassandra 还有 LevelDB、RocksDB 内部都有布隆过滤器结构,布隆过滤器可以显著降低数据库的 IO 请求数量。当用户来查询某个 row 时,可以先通过内存中的布隆过滤器过滤掉大量不存在的 row 请求,然后再去磁盘进行查询
-
·String / Hash 使用 MGET/MSET 替代 GET/SET,HMGET/HMSET 替代 HGET/HSET
·其它数据类型使用 Pipeline,打包一次性发送多个命令到服务端执行 -
调大 maxmemory:先修改从库,再修改主库
调小 maxmemory:先修改主库,再修改从库 -
·性能
- 可靠性
- 资源
- 运维
- 监控
- 安全
- redis用作消息队列,其在spring boot中的主要表现为一RedisTemplate.convertAndSend()方法和一个MessageListener接口。所以我们要在IOC容器中注入一个RedisTemplate和一个实现了MessageListener接口的类。
配置RedisTemplate的主要目的是配置序列化方式以解决乱码问题,同时合理配置序列化方式还能降低一点性能开销。
在将监听器添加到容器的配置的时候,RedisMessageListenerContainer类中有一个方法setTaskExecutor(Executor taskExecutor)可以为监听容器配置线程池。配置线程池以后,所有的线程都会由该线程池产生,由此,我们可以通过调节线程池来控制队列监听的速率。
redisLockRegistry.obtain(),其返回的是一个名为RedisLock的锁,这是一个私有内部类,它实现了Lock接口,因此我们不能从代码外部创建一个他的实例,只能通过obtian()方法来获取这个锁。
- 因此市面上大部分 RPC 框架都使用 TCP 协议,但也有少部分框架使用其他协议,比如 gRPC 就基于 HTTP2 来实现的。
数据编解码和 络传输可以有多种组合方式,比如常见的有:HTTP+JSON, Dubbo 协议+TCP 等。
- 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易 (流水 )实现.
-
事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
-
我接触和了解到的分布式事务大概分为:
- 2pc(两段式提交)
- 3pc(三段式提交)
- TCC(Try、Confirm、Cancel)
- 最大努力通知
- XA
- 本地消息表(ebay研发出的)
- 半消息/最终一致性(RocketMQ)
主要参考:
https://mp.weixin.qq.com/s/cQqc1r8jydbxMzav2FpPdA
文章知识点与官方知识档案匹配,可进一步学习相关知识MySQL入门技能树首页概览33031 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!