0%

官方给出的Redis性能数据是10W+QPS,在实际业务中,Redis很少会成为性能瓶颈,但是针对Redis的一些简单的性能分析方法,还是可以了解一下。

info命令

Redis自带的info命令,可以输出当前Redis各个维度的统计数据。

memory模块

info memory命令,可以输出内存相关的数据,例如:

阅读全文 »

Redis可以通过maxmemory配置,来设置占用的最大内存,如果不设置或者设置为0,那么在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。

如果设置了最大内存,那么当内存占用达到配置的上限时候,便会触发内存淘汰的策略,目前Redis支持如下几种内存淘汰策略:

  • noeviction:默认策略,对于写请求直接返回错误,DEL请求和部分特殊请求除外;
  • allkeys-lru:从所有key中使用LRU算法进行淘汰;
  • volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰;
  • allkeys-random:从所有key中随机淘汰数据;
  • volatile-random:从设置了过期时间的key中随机淘汰;
  • volatile-ttl:从设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰;
  • allkeys-lfu:4.0新增,从所有key中使用LFU算法进行淘汰;
  • volatile-lfu:4.0新增,从设置了过期时间的key中使用LFU算法进行淘汰。

在volatile-lru、volatile-random、volatile-ttl、volatile-lfu这四种策略下,如果没有key可以被淘汰,则和noeviction一样直接返回错误。

上面的策略中,除了默认策略外,其他策略可以分为两部分:key的范围(allkeys,volatile),算法(ttl,random,lru,lfu)。

阅读全文 »

Redis是一个纯内存的NoSQL,但是同时也支持数据持久化,当服务重启的时候,可以读取持久化的数据到内存中。目前Redis有三种持久化的机制,AOF,RDB,以及AOF+RDB混合的方式(4.0之前只有前两种)。

AOF(Append Only File)

Redis将所有的写操作添加至缓冲区(aof_buf),随后根据配置的不同策略,将缓冲区中的数据追加保存到AOF文件(appendonly.aof)中。如果顺序读取AOF文件,并执行读取到的命令,则可以得到一份跟当前Redis一致的数据。

阅读全文 »

Redis,一个高性能,纯内存的NoSQL数据库,官方给出的性能数据是10W+QPS,多用于各种高并发业务的缓存框架中。本文主要介绍Redis的一些基础数据结构。

Redis是一个key-value数据库,所有的key-value都存在一个大的字典里,数据结构定义在dict.h里,大致的结构如下:

阅读全文 »

AQS(AbstractQueuedSynchronizer),是JDK1.5之后出现一种抽象的同步编程框架,主要是利用一个共享资源变量state和一个CLH同步队列,来实现共享资源的竞争,线程的阻塞唤醒逻辑,以及阻塞线程的存储队列。ReentrantLock、CountDownLatch都是基于AQS来实现的。

CLH是一个虚拟的双向队列,队列仅仅保存节点之间的关联关系。在AQS中,即是将每个请求共享资源的线程封装成一个节点Node,然后保存节点之间的关联关系。

更直白一点描述,就是线程通过CAS去改变共享变量state,如果修改成功,则获取锁成功;如果修改失败,则线程进入等待队列中,等待被唤醒。

阅读全文 »

synchronized,Java中用来实现线程同步的一个关键字,有以下几个特性:

  • 互斥性:同时只能有一个线程持有某个锁,在持有期间,其他竞争线程需要等待锁的释放;
  • 可重入性:一个线程获取了一个锁之后,在释放锁之前,如果嵌套的代码中需要再次获取这个锁(必须是同一个锁对象),则此时不会因为锁没有释放而阻塞;
  • 数据可见性:被synchronized关键字保护的同步代码,会在进入时,从主存读取数据,执行完毕时将线程缓存的数据刷新到主存,因此可以解决多线程的数据可见性问题;
  • 重排序:由于as-if-serial,happens-before的概念约束,因此synchronized能够解决指令重排序的问题。

那么synchronized关键字的原理是啥呢?

阅读全文 »

CAS操作,全称为Compare And Swap,现在几乎所有的CPU指令都支持CAS的原子操作,他也是自旋锁,乐观锁的核心操作。

CAS操作的目的,是在一次原子操作中,实现如下功能:

  • 取出某个内存地址存储的值;
  • 比较取出的值是否与预期的值相同;
  • 如果相同则将这个值更新为新的值,不相同则不做更新。

我们用一个例子来解释CAS的适用场景,假设需要统计一个网站的访问次数,那么会有如下代码:

1
2
3
4
5
6
7
public class WebSta {
private volatile int count = 0;

public void clicked() {
count = count + 1;
}
}
阅读全文 »

在Java的线程生命周期中,一共有六种状态,定义在Thread.State枚举类型中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum State {
// A thread that has not yet started is in this state.
NEW,
// A thread executing in the Java virtual machine is in this state.
RUNNABLE,
// A thread that is blocked waiting for a monitor lock is in this state.
BLOCKED,
// A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
WAITING,
// A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
TIMED_WAITING,
// A thread that has exited is in this state.
TERMINATED;
}
阅读全文 »

并发编程离不开锁,而Java有各种各样的锁,并且根据锁的特性不同,适用的场景也不同,本文会对锁的一些概念进行介绍。

这些概念不光是适用于Java中的锁,也适用于其他各种需要用到锁的场景。

本文只讲概念,不涉及到细节,相关的实现细节在后续具体的某个锁的文章中会讲解。

乐观锁/悲观锁

乐观锁和悲观锁,是指看待并发问题的角度不同,从而引出的不同上锁时机的两种概念。

  • 悲观锁

    悲观锁认为,自己在修改某一个数据的时候,一定也会有其他并发的线程来修改数据,因此会在操作数据之前,就对数据进行上锁。

    例如synchronized/lock等都属于悲观锁的范畴,在执行数据修改之前就上锁,保证同时只有一个线程在修改数据。

阅读全文 »

Java代码,最先由javac指令编译成.class文件,然后被JVM加载到内存中,存放到JVM运行时数据区当中。需要被执行的时候,则由字节码执行引擎解析执行。

JIT即时编译

JVM的执行引擎中,有一个解释器,将字节码解析成对应的机器指令来执行,读取一条,解析一条,执行一条。这种方式自然要比可执行的二进制程序慢很多,为了解决这种效率问题,JVM引入了JIT技术,当JVM发现某段代码是一段热点代码(需要频繁执行)的时候,就会将这部分代码翻译成机器码,并进行优化,然后缓存起来,供下次直接使用。

阅读全文 »