Redis实现排行榜功能
背景 通常来说,榜单是指一定时间周期内以某个指标反序排列的 N 个元素 (id, score),即 topN 个元素,这里的 N,一般来说是个较小的常数,如 100。而根据时间周期的不同,可分为小时榜、日榜、周榜、月榜等。
以直播为例,在直播后端业务场景下,涉及榜单的包括但不限于直播间榜单、直播 PK 贡献榜单、粉丝团亲密度榜单、直播宠物好友榜单、直播宠物投喂饭团榜单、地区小时榜单。其中直播间榜单是日榜、直播 PK 贡献榜则是以 PK 时长为周期的榜单。
本文主要简述在不同需求场景下,如何使用 redis 对榜单数据进行存储,并尽可能保证榜单数据一致性。
概念
榜单,一系列榜单单元 <id, score> 构成,按 score 反序排列的有序列表。
榜单元素,id,用户于惟一标识榜单中某个数据单元,如用户 id;score,用于标识 id 的计分。
说明:后续示例中以 u 表示用户,topList 表示榜单,userScore 表示用户计分。
选择 redis 原因
redis 丰富的数据结构中 zset 就是有序列表。
redis ...
Redis 实战:Redis 在 Java 中的基本使用
Redis 实战:Redis 在 Java 中的基本使用1、使用 jedis 操作 redis1.1、Jedis 简介Jedis 是 Java 语言开发的 Redis 客户端工具包,用于 Java 语言与 Redis 数据进行交互。
Jedis 在 github 官网地址:https://github.com/redis/jedis#readme
Jedis 只是对 Redis 命令的封装,掌握 Redis 命令便可轻易上手 Jedis。
Jedis 遵循 RESP 协议规范开发,具有良好的通用性与可读性。
1.2、引入 jedis 的 Maven 依赖123456<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> ...
Java 锁介绍
乐观锁
乐观地认为并发访问不会造成数据冲突,只在更新时检查是否有冲突。乐观锁和 CAS 的关系可以用 “乐观锁是一种思想,CAS 是一种具体的实现” 来理解。
当使用 CAS 操作修改数据时,如果版本号不匹配或者其他线程已经修改了要操作的数据,CAS 会返回失败。这时候,程序可以再次尝试 CAS 操作,也就是进行自旋重试,直到 CAS 操作成功。
因此,CAS 操作已经内置了自旋重试的机制,避免了使用额外的自旋锁。
适用场景:适用于并发较低(高并发场景每次修改了去对比,还不如让加锁阻塞排队执行)、读多写少的场景,相信数据多数情况下不会发生冲突,只在更新时进行检查,以减少对共享资源的争用。
java 中常见悲观锁实现:可以使用 java.util. concurrent.atomic 包中的原子类,比如 AtomicInteger、AtomicLong 等,来实现 CAS 操作。
mysql 实现乐观锁:版本号、时间戳
悲观锁
悲观地认为并发访问会造成数据冲突,因此在访问共享资源之前就会进行加锁,确保同一时刻只有一个线程能够访问。
适用场景:适用于高并发、写多的场景,通过加锁保护共享资源 ...
深入理解 Java 虚拟机:Jvm 性能调优
深入理解 Java 虚拟机:Jvm 性能调优一、Jvm 性能调优简介到目前为止,我们已经对 Jvm 进行了简单的了解,知道了 Jvm 运行时各种各样的内存结构,各种垃圾回收机制以及各种对应的垃圾收集器及其配置。而我们整个 Jvm 系列的最终目标不当仅仅以了解基础理论为终点,理论总应作为实践的工具。接下来,我们开始了解 Java 性能优化的最后一环:Jvm 性能调优。
我们常说的 Jvm 性能调优实际上有着三个以及的目的:
根据需求进行 Jvm 规划和预调优;
优化运行 Jvm 运行环境(慢,卡顿);
解决 Jvm 运行过程中出现的各种问题(OOM)
二、根据需求目标进行 Jvm 调优规划而这一性能调优基本的步骤就是明确优化目标、发现性能瓶颈、性能调优、通过监控及数据统计工具获得数据、确认是否达到目标。或者笼统的概括为 以业务场景开始压力测试监控,查看调优结果 两步。
1、调优的目标首先我们要明白 Jvm 性能调优的形式方案并不是固定的,不同的应用有着不同的目标与不同的问题。而根据需求进行 Jvm 规划和预调优最先要明确的就是调优的目标。
下面是 Jvm 常提到的性能指标
...
深入理解 Java 虚拟机:Java 垃圾回收器
深入理解 Java 虚拟机:Java 垃圾回收器一、Jvm 垃圾回收器概述我们前面提到了,垃圾回收器的 回收的内容、回收的时机以及回收的方式,接下来我们来看 Java 垃圾回收器。如果垃圾回收算法是内存回收的方法论的话,那么垃圾回收器就是内存回收的具体实现了。
Jvm 的垃圾回收器根据场景和实现方式可以分为新生代回收器和老年代回收器,新生代回收器与老年代回收器可以搭配使用。
新生代回收器包括:Serial、ParNew 以及 Parallel Scavenge;
老年代回收器包括:Serial Old、Parallel Old 以及 CMS;
此外,Java7 update 4(第七版第四个更新升级包)之后引入了一个 G1 收集器。
Ps:不同垃圾回收器适合于不同的内存区域,有的两个垃圾回收器之间也可以配合使用!
二、新生代回收器1、Serial 收集器Serial 收集器是最基础且历史最悠久的垃圾收集器,作为单线程工作的收集器。Serial 会在它工作时要求暂停用户所有的其他线程(Stop-the-World 机制)。采用的是 “标记 - 复制” 算法。垃圾清理时, ...
深入理解 Java 虚拟机:Java 垃圾回收机制
深入理解 Java 虚拟机:Java 垃圾回收机制一、概述
Java 与 C++ 之间有一堵由内存动态分配和垃圾收集技术所围成的 “高墙”,墙外面的人想进去,墙里面的人却想出来
说起垃圾收集(Garbage Collection,下文简称 GC),有不少人把这项技术当作 Java 语言的伴生产物。事实上,垃圾收集的历史远远比 Java 久远,在 1960 年诞生于麻省理工学院的 Lisp 是第一门开始使用内存动态分配和垃圾收集技术的语言。
当 Lisp 还在胚胎时期是,其作者 John McCarthy 就思考过垃圾收集器需要完成的三件事情:
有哪些内存需要回收?
什么时候回收?
如何回收?
二、有哪些内存需要回收?——对象已死?在堆里面存放着 Java 世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还 “存活” 着,哪些已经“死去”(即不可能再被任何途径 使用的对象)。
1、引用计数算法引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设 ...
深入理解 Java 虚拟机:Java 运行内存结构
深入理解 Java 虚拟机:Java 运行内存结构一、JAVA 运行内存结构Jvm 执行 Java 程序时,会把它所管理的内存划分为若干个不同的数据区域。
其中一些数据区域是所有线程共享是,在 Jvm 启动时创建,在 Jvm 退出时销毁。如:方法区和堆。
还有一些数据区域是每个线程独有的,在线程启动时创建,在线程结束时销毁。如:程序计数器、虚拟机栈和本地方法栈。
下面介绍的是根据 Java 虚拟机规范定义的运行时数据区,单不同的虚拟机其运行时数据区定义也会有所不同。比如默认的 HotSpot在实现 JDK1.7 虚拟机规范时,其常量池的定义不在方法区中,而是移到了堆中;到了 HotSpot JDK1.8 中,则彻底移除了持久代(方法区)而使用 Metaspace(元数据区)来进行替代等等。
二、线程独有数据区域(Java 虚拟机规范定义的运行时数据区)1、程序计数器程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执 ...
深入理解 Java 虚拟机:Java 类的加载机制
深入理解 Java 虚拟机:Java 类的加载机制一、Java 类的加载机制1、 Jvm 结构组成Jvm 整体组成可分为四个部分:类加载器、运行时数据区(Runtime Data Area)、执行引擎(Execution Engine)、本地库接口(Native Interface)
类加载器:负责从字节码(Class)文件中,加载 class 信息到运行时数据区的方法区;
运行时数据区:存放 Jvm 在执行 Java 程序时相关数据的区域;
执行引擎:将字节码翻译成底层系统指令再交由 CPU 去执行;
本地库接口:执行过程中可能需要调用到其他语言(比如 C 语言)的本地接口。
PS:Javac 是收录于 Jdk 中的 Java 语言编译器。该工具可以将后缀名为 .java 的源文件编译为后缀名为 .class 的可以运行于 Java 虚拟机的字节码。
程序在被执行之前, Java 代码会被先转换成字节码(.class 文件), Jvm 首先通过一定的方式类加载器①(ClassLoader)把字节码文件加载到内存中运行时数据区②(Runtime Data Area) ...
Java 新特性:Stream 流式编程
Java 新特性:Stream 流式编程
Stream 流是 Java8 提供的新功能,是对集合对象功能的增强,能对集合对象进行各种非常便利、高效的聚合操作,或大批量数据操作。Stream 流以一种声明性方式处理数据集合,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。
Stream 流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算
1、Stream 流概述1.1、Stream 流简介Stream 流是 Java8 提供的新功能,是对集合对象功能的增强,能对集合对象进行各种非常便利、高效的聚合操作,或大批量数据操作。Stream 流以一种声明性方式处理数据集合,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。
Stream 流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据 ...
String、StringBuffer、StringBuilder 有什么区别?
典型回答首先,String 是 Java 语言中非常基础和重要的类,它提供了构造和管理字符串的各种基本逻辑。String 对象一旦创建,其值就不能被改变,这种特性称为不可变性(Immutable)。由于它是 final 类,无法被继承,所有的属性也是 final 的。这种不可变性使得 String 对象在多线程环境中可以安全地使用,但是它也意味着像字符串拼接这样的操作会生成许多临时的中间对象,从而可能影响性能。
其次,为了优化性能问题,尤其是在字符串频繁修改的场景下,Java 提供了 StringBuffer 类。StringBuffer 允许字符串的可变性,并且支持诸如 append 和 insert 等方法来修改字符串。最关键的是,StringBuffer 是线程安全的,它内部通过同步机制来保证多线程操作的一致性。但这也意味着每次操作可能涉及到锁机制,带来额外的性能开销。
最后,考虑到线程安全带来的性能代价,在 Java 1.5 中引入了 StringBuilder 类。StringBuilder 在功能上与 StringBuffer 类似,提供了相同的接口,但是它不是线程安全的。 ...