redis如何避免数据倾斜
数据倾斜分类数据量倾斜在某些情况下,实例上的数据分布不均衡,某个实例上的数据特别多。
造成原因1、bigKey
bigkey 的 value 值很大(String 类型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。bigkey 的操作一般都会造成实例 IO 线程阻塞,如果 bigkey 的访问量比较大,就会影响到这个实例上的其它请求被处理的速度。
在生成数据时,尽量避免把过多的数据保存在同一个键值对中,如果bigKey是一个集合,我们可以把bigKey拆分成多个小的集合类型,分散保存在不同实例。
2、slot分配不均衡
如果集群运维人员没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。
比如说,集群中有5个实例,而实例1的硬件配置比较好,可能分配人员就给实例1多分配了几个Slot。但并不知道数据和Slot的对应关系,这种做法就有可能导致大量数据被映射到实例1的slot上。
在分配前,可以避免把过多的slot ...
Redis为什么可以支撑秒杀场景
秒杀场景的负载特征对支撑系统的要求特征一:瞬时并发访问很高。一般的数据库每秒可以支撑千级别的并发请求,而Redis的并发处理能力达到了万级别。所以当有大量请求涌入系统,我们可以使用Redis先拦截大部分请求,避免很多请求直接发到数据库。
特征二:读多写少,而且读操作是简单查询操作。一般的场景,需要先验证库存,然后再进行下单和商品购买,而查询库存这一操作也比较简单,适合使用Redis。
Redis 可以在秒杀场景的哪些环节发挥作用秒杀活动前在这个阶段,用户会不断刷新商品详情页,这会导致详情页的瞬时请求量剧增。这个阶段的应对方案,一般是尽量把商品详情页的页面元素静态化,然后使用 CDN 或是浏览器把这些静态化的元素缓存起来。这样一来,秒杀前的大量请求可以直接由 CDN 或是浏览器缓存服务,不会到达服务器端了,这就减轻了服务器端的压力。
秒杀活动开始简单来说这个阶段的操作有三个,库存查验、库存扣减和订单处理,其中查看库存的请求应该是最多的。所以我们可以使用Redis来保存库存数量,减少查询库存给数据库带来的压力。
除了查库存外,订单处理的操作可以放在后端处理。因为这时候只有少部分请求可以到达 ...
Redis集群方案之Codis
codis集群中包含的4个组件1、codis server:这是进行了二次开发的 Redis 实例,其中增加了额外的数据结构,支持数据迁移操作,主要负责处理具体的数据读写请求。
2、codis proxy:接收客户端请求,并把请求转发给 codis server。
3、Zookeeper 集群:保存集群元数据,例如数据位置信息和 codis proxy 信息。
4、codis dashboard 和 codis fe:共同组成了集群管理工具。其中,codis dashboard 负责执行集群管理工作,包括增删 codis server、codis proxy 和进行数据迁移。而 codis fe 负责提供 dashboard 的 Web 操作界面,便于我们直接在 Web 界面上进行集群管理。
Codis处理请求流程1、首先使用codis dashboard 设置 codis server 和 codis proxy 的访问地址。
2、客户端与coids proxy建立连接。codis proxy本身支持Redis的RESP交互协议,所以与codis proxy建立连接与原生Redis ...
Redis脑裂导致的数据丢失问题
一般的数据丢失在主从集群中发生数据丢失,最常见的原因就是主库的数据还没有同步到从库,结果主库发生了故障,等从库升级为主库后,未同步的数据就丢失了。
如果是这种情况,我们可以通过比对主从库上的复制进度差值来进行判断,也就是计算 master_repl_offset 和 slave_repl_offset 的差值。
脑裂导致的数据丢失所谓的脑裂,就是指在主从集群中,同时有两个主节点,它们都能接收写请求。
主从切换后,从库一旦升级为新主库,哨兵就会让原主库执行 slave of 命令,和新主库重新进行全量同步。而在全量同步执行的最后阶段,原主库需要清空本地的数据,加载新主库发送的 RDB 文件,这样一来,原主库在主从切换期间保存的新写数据就丢失了。
上述情况的发生,主要是由于误判造成的原主库假死,然后在执行主从切换的过程中,原主库还可以进行数据的处理,但是进行全量同步时这些数据并不会一起进行同步,而是会保存在缓存中,等到同步完发送,但是主从切换在执行的最后阶段会清空主库原来的数据,导致全量同步过程中新写入的数据丢失。
而误判的原因可能是原主库在执行cpu密集型的操作,导致无法相应哨兵的心跳检 ...
虚拟机类加载机制之类加载器
通过一个类的全限定名来获取描述该类的二进制字节流,实现这个动作的代码叫做类加载器。
类与类加载器对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。换句话说:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
这里的相等指:代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括了使用instanceof关键字做对象所属关系判定等各种情况。
双亲委派模型启动类加载器(Bootstrap Class Loader):这个类加载器负责加载存放在\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中。
...
Redis主从同步与故障切换的一些问题
主从数据不一致主从数据不一致,就是指客户端从从库中读取到的值和主库中的最新值并不一致。
比如主库和从库之前的数据都是20,此时一条修改命令将主库的值由20改为19,接着有一个查询走了从库,此时从库的值还是19。
产生原因:主从库间的命令复制是异步进行的。
具体来说,主库收到写命令,会发给从库,但是在写完主库后就会返回给客户端,并不会等到从库写完才返回给客户端。
从库命令滞后原因:
1、主从库间的网络可能会有传输延迟,所以从库不能及时地收到主库发送的命令,从库上执行同步命令的时间就会被延后。
2、从库收到命令,但此时从库因在执行其他复杂度高的命令而阻塞,无法执行同步命令。
解决办法:
1、采用更好的硬件,保证主从库间的网络连接状况良好。
2、监控主从复制的进度。
Redis 的 INFO replication 命令可以查看主库接收写命令的进度信息(master_repl_offset)和从库复制写命令的进度信息(slave_repl_offset),用 master_repl_offset 减去 slave_repl_offset,这样就能得到从库和主库间的复制进度差值了。
所以我们可 ...
事务机制之Redis能否实现ACID属性
什么是事务所谓的事务,就是指对数据进行读写的一系列操作。事务在执行时,会提供专门的属性保证,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),也就是 ACID 属性。
事务ACID属性要求首先来看原子性。原子性的要求很明确,就是一个事务中的多个操作必须都完成,或者都不完成。业务应用使用事务时,原子性也是最被看重的一个属性。
第二个属性是一致性。这个很容易理解,就是指数据库中的数据在事务执行前后是一致的。
第三个属性是隔离性。它要求数据库在执行一个事务时,其它操作无法存取到正在执行事务访问的数据。
最后一个属性是持久性。数据库执行事务后,数据的修改要被持久化保存下来。当数据库重启后,数据的值需要是被修改后的值。
Redis如何实现事务首先,客户端需要一个显式的命令开启事务,Redis里使用MULTI来手动开启一个事务。
第二步,客户端把事务中需要执行的具体操作发送给服务端。Redis接受到这些命令后会将他们都存到一个队列当中,并不会立即执行。
第三步向服务端发送提交事务请求,Redis使用EXEC,让数据库执 ...
虚拟机类加载机制之类加载过程
这篇文章我们会详细了解Java虚拟机中类加载的全过程,即加载、验证、准备、解析和初始化这五个阶段所执行的具体动作。
加载“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一个阶段,在这个阶段java虚拟机要做三件事:
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
针对第一件事,《Java虚拟机规范》并没有指定从哪里获取这个二进制字节流,例如可以在如下几种情况中获取:
1)从ZIP压缩包中读取,这很常见,最终成为日后JAR、EAR、WAR格式的基础。
2)从网络中获取,这种场景最典型的应用就是Web Applet。
3)运行时计算生成,这种场景使用得最多的就是动态代理技术。
4)由其他文件生成,典型场景是JSP应用,由JSP文件生成对应的Class文件。
5)……
相对于类加载过程的其他阶段,非数组类型的加载阶段(准确地说,是加载阶段 ...
虚拟机类加载机制之类加载时机
虚拟机类加载机制Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。
在Java语言中,类型的加载、连接和初始化过程都是在程序运行期间完成的,这会导致Java语言进行提前编译会面临额外的困难,也会让类加载时稍微增加一些性能开销,但是很灵活。例如,编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类。
用户可以通过Java预置的或自定义类加载器,让某个本地的应用程序在运行时从网络或其他地方上加载一个二进制流作为其程序代码的一部分。
本文以及后续所提到的“Class文件”也并非特指某个存在于具体磁盘中的文件,而应当是一串二进制字节流,无论其以何种形式存在,包括但不限于磁盘文件、网络、数据库、内存或者动态产生等。
类加载过程一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(U ...
如何使用Redis实现分布式锁
1、为什么要使用分布式锁当Redis的客户端只有一个时,可以通过在客户端加锁来控制并发写操作对共享数据的修改,也可以使用原子操作。
但是一个Redis往往不仅连接一个客户端,当有多个客户端需要并发修改数据时,这把锁加在客户端已经不起作用了。假如有3个客户端,在客户端加锁可以保证该客户端所处理的请求在同一时间内只有一个可以修改Redis数据,但是3个客户端意味着有3把锁,会出现同一时间内有3个客户端可以修改Redis数据。
所以,在分布式系统中,当有多个客户端需要获取锁时,我们需要分布式锁。此时,锁是保存在一个共享存储系统中的,可以被多个客户端共享访问和获取。
2、简单锁的设计先看单机上的锁。
对锁进行简化,我们可以用一个变量来表示。变量值为0,表示没有线程获取锁,变量值为1,表示已经有线程获取到了锁。
平时所说的加锁,解锁,其实就是该线程去检查这个变量,如果是0,就可以获取该锁,然后把变量值改为1。如果变量值本身就是1,那么就返回获取锁失败。除此之外,我们还需要知道哪一个线程获取了锁,所以还需要一个id来标识。一个最简单的锁伪代码可以设计如下:
12345678910111213141 ...