OMG_By

沉心、静气、学习、总结、进步


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

HashMap详解

发表于 2019-06-02 | 分类于 JAVA

什么是HashMap

HashMap一个用于存储Key-Value键值对的集合,每一个键值对称之为Entry分散存储在一个数组中。这个数组每一个元首初始值都是Null。

HashMap的常用操作就是GET和PUT

PUT

我们在调用put方法的时候,会利用一个哈希函数来确定Entry的插入位置。为了解决哈希冲突的问题,HashMap采用链表法来解决这个问题。
注意:位置0上存放的一定是Null

1
2
3
4
5
6
7
8
9
10
11
12
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;

在遇到Hash冲突的时候,就将Entry以链表的形式给插入到相应的位置,这里需要注意的是:HashMap采用的是头插法的形式。这是因为设计者认为最新加入的Entry更有可能被访问。
此时HashMap的结构为数组加链表

阅读全文 »

Object的常用方法

发表于 2019-06-01 | 分类于 JAVA

Object类常见方法

Object是一个特殊的类,所有的类都会隐式的继承Object类。Object中主要有以下11个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
注:native方法:使用这个关键字说明这个方法是原生方法,底层使用C/C++实现,并且被编译成了DLL,由Java调用。函数实现在DLL中,JDK源码并不包含。

public final native Class<?> getClass()
用于返回当前运行时对象的Class对象,使用final关键字修饰,不允许子类重写。

public native int hashCode()
用于返回对象的哈希码,主要使用在哈希表中。

public boolean equals(Object obj)
用于比较2个对象的内存地址是否相等,String类对该方法进行了重写。

protected native Object clone() throws CloneNotSupportedException
用于创建当前对象的一份拷贝。Object本身并没有实现Cloneable接口,所以不重写clone方法并且进行调用就会发生CloneNotSupportedException异常。

public String toString()
默认返回类的名字@实例的哈希码的16进制的字符串,建议所有子类都重写该方法。

public final native void notify()
final方法,用于唤醒一个在此对象监视器上等待的线程。如果有多个线程在等待只会任意唤醒一个。

public final native void notifyAll()
用于唤醒该对象上所有的等待线程。

public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
暂停线程的执行,第二个超时时间需要增加nanos毫秒,第三个没有超时时间。

protected void finalize() throws Throwable { }
实例被垃圾回收器回收的时候触发的操作。

GET和POST的区别

发表于 2019-04-05 | 分类于 计算机基础

GET和POST请求的区别

GET和POST是HTTP请求的两种发送方式,其本质上并没有多大区别。HTTP的底层是TCP/IP协议,所以GET和POST底层实现也是TCP/IP协议,也就是GET和POST都是TCP连接,GET和POST做的事情都是一样的。如果在GET上加上request body,POST上加上url在技术上完全是可行的。

但是GET和POST在一些表现上还是有区别的。主要体现在以下几个方面:

  • GET请求在URL中传送的参数是有长度限制的,POST没有
  • GET会将参数直接显示在URL上,所以POST更为安全
  • GET参数传递通过URL,POST参数传递通过request body
  • GET请求参数记录会被保留在浏览器记录中,POST不会
  • POST支持多种编码方式
  • POST在回退的时候会再次提交数据,GET不会
  • 在某些浏览器下,GET只会发送一次数据包,而POST需要发送两次

三次握手和四次挥手

发表于 2019-04-04 | 分类于 计算机基础

三次握手流程

1
2
3
4
5
6
7
客户端->服务端: SYNC=1,seq=x
Note left of 客户端: SYCN_SEND
服务端-->客户端: SYNC=1,ACK=1,seq=y,ack=x+1
Note right of 服务端: SYNC_RECV
客户端->服务端: ACK=1,seq=x+1,ack=y+1
Note left of 客户端: ESTAB-LISHED
Note right of 服务端: ESTAB-LISHED

第一次握手:建立连接时,客户端发送sync包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;

第二次握手:服务器收到syn包,发送确认号(ack=x+1),同时自己也发送一个syn包(syn=y),即ACK+SYN包;服务器进入SYN_RECV状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1);发送完毕后,客户端和服务端都进入ESTABLISHED状态完成三次握手。

阅读全文 »

MySQL机制介绍之Change Buffer

发表于 2019-04-03 | 分类于 MySQL

MySQL的偷懒行为之Change Buffer

在使用MySQL的时候,只要MySQL服务给你返回成功信息后,得到你需要的结果,你并不关注MySQL的行为;但是其实在MySQL内部,其实有很多的特性和机制来延迟真正的持久化等耗时耗资源行为。下面我将为你介绍其中的一个特性:change buffer

简介(What)

The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool. The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), are merged later when the pages are loaded into the buffer pool by other read operations.

当需要改变二级索引的数据,并且该数据页不在内存缓冲池中;这个时候会直接向客户端返回修改成功的信息。MySQL并不实时的去更新磁盘数据,而是在之后一定条件下触发更新操作。这就是change buffer在作祟。

change buffer在MySQL5.5版本之前,只支持insert操作,所以最初被称为insert buffer。在之后的版本中,支持了更多的修改操作,所以后面改称为change buffer。

阅读全文 »

Redis的内存管理和优化

发表于 2019-01-25 | 分类于 Redis

Redis中的内存使用主要是数据使用内存+客户端连接使用内存+内存碎片。
其中数据内存占用的最多,优化的常用手段是合理的控制对象的生命周期以及正确的使用数据结构。客户端使用内存主要包含输出缓冲区等一些数据传输缓存。

Redis的数据结构

只有熟悉的了解数据结构的组成、特性、性能、边界条件等因素后,我们还能更好的分析该数据结构的使用场景和资源消耗情况。

在Redis当中,所有的对象都是通过(redisObject+具体的对象)形式存在的,所有的对象都被封装在redisObject中,redisObject有五个成员:对象类型(type)、底层编码(encoding)、lru(最近访问时间)、refcount(引用数)、*ptr(指向具体对象的指针)。该结构一共会占用16字节。

1
2
3
4
5
6
7
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;

Redis一共有五种基本类型(其他的特殊类型都是此基础上形成的)string、list、hash、set、zset。
V7Jart.md.jpg

每一种数据类型在底层的存储实现存在多种选择,根据实际情况选择合适的编码类型。在效率和资源之间做出合适的权衡。
V7JtxA.md.jpg

阅读全文 »

记一次磁盘空间不足

发表于 2018-12-29 | 分类于 Linux

问题描述:

在一台虚拟机机器上发现磁盘空间不足的情况,通过登录上去查看,发现是挂载盘占用磁盘资源太多,通过df命令查看,但是并找不到具体是哪个文件占用过多。
通过以下查找大文件命令也没有找到相应的大文件。

1
find . -type f -size +100M

原因

文件被删除后,并没有及时的释放磁盘资源,导致磁盘空间不足的情况。
通过以下命令,查看已经标记为删除但是没有释放的文件。对相应程序进行重启后解决

1
lsof|grep -i delete|less

Redis源码阅读(启动过程)

发表于 2018-11-15 | 分类于 Redis

Redis的启动函数main()在redis.c文件中。

初始化配置

在启动时会使用initServerConfig()函数进行初始化服务器配置工作,redisServer(redis.h)作为保存服务器配置的结构。 redisServer包含以下的一些Redis服务器的信息:

  • 一般服务器状态。
  • 一些服务器统计信息。
  • 各种链表结构,如保存客户端的链表等。
  • 配置文件及启动参数中的配置。
  • 主从复制的状态。
  • 持久化文件的参数和状态。


阅读全文 »

Redis对象结构

发表于 2018-11-10 | 分类于 Redis

object.c

object.c文件主要记录了Redis对象的创建和释放方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
robj *createEmbeddedStringObject(char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);
struct sdshdr *sh = (void*)(o+1);

o->type = REDIS_STRING;
o->encoding = REDIS_ENCODING_EMBSTR;
o->ptr = sh+1;
o->refcount = 1;
o->lru = LRU_CLOCK();

sh->len = len;
sh->free = 0;
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1);
}
return o;
}

从这个函数中,我们可以看出Redis在创建一个REDIS_ENCODING_EMBSTR编码的字符串对象时,是SDS连同redisObject一同创建的。也就是说这个字符串是不可以修改的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
robj *createStringObjectFromLongLong(long long value) {

robj *o;

// value 的大小符合 REDIS 共享整数的范围
// 那么返回一个共享对象
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
incrRefCount(shared.integers[value]);
o = shared.integers[value];

// 不符合共享范围,创建一个新的整数对象
} else {
// 值可以用 long 类型保存,
// 创建一个 REDIS_ENCODING_INT 编码的字符串对象
if (value >= LONG_MIN && value <= LONG_MAX) {
o = createObject(REDIS_STRING, NULL);
o->encoding = REDIS_ENCODING_INT;
o->ptr = (void*)((long)value);

// 值不能用 long 类型保存(long long 类型),将值转换为字符串,
// 并创建一个 REDIS_ENCODING_RAW 的字符串对象来保存值
} else {
o = createObject(REDIS_STRING,sdsfromlonglong(value));
}
}

return o;
}

这个函数是根据传入的整数值来创建一个字符串对象。从这里可以看到主要有三个判断:

  1. 如果设置的值符合Redis共享整数对象的范围时,是直接复用共享对象,并不会去创建一个新的对象。
  2. 如果传入的值是long类型的,这创建一个REDIS_ENCODING_INT编码类型的字符串对象。
  3. 如果传入的值为long long类型的,则创建REDIS_ENCODING_RAW编码类型的字符串对象。

debug命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Object command allows to inspect the internals of an Redis Object.
* Usage: OBJECT <verb> ... arguments ... */
void objectCommand(redisClient *c) {
robj *o;

// 返回对戏哪个的引用计数
if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyLongLong(c,o->refcount);

// 返回对象的编码
} else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyBulkCString(c,strEncoding(o->encoding));

// 返回对象的空闲时间
} else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
} else {
addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
}
}

Redis原理----主从复制

发表于 2018-10-26 | 分类于 Redis

很多企业都没有使用到Redis的集群,但是至少都做了主从。有了主从,当master挂掉的时候,运维让从库过来接管,服务就可以继续,否则master需要经过数据恢复和重启的过程,这就可能会拖很长时间,影响线上业务的持续服务。

在了解Redis主从复制之前,让我们先来理解一下现代分布式系统的理论基石–CAP原理。

CAP原理

CAP原理就好比分布式领域的牛顿定律,它是分布式存储的理论基石。自打CAP的论文发表之后,分布式存储中间件犹如雨后春笋一个一个涌现出来。

  • C-Consistent 一致性
  • A-Availability 可用性
  • P-Partition tolerance 分区容忍性

分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,这个网络断开的场景的专业词汇叫着「网络分区」。

阅读全文 »
1…567…14
OMG_By

OMG_By

133 日志
20 分类
36 标签
RSS
GitHub E-Mail
友链
  • 戎码人生
  • JosemyQAQ
  • Just do it !
  • ACM各大OJ题集
  • django大神博客
© 2020 OMG_By