Hello Coder


  • 首页

  • 归档

  • 标签

  • 搜索
close

gulp js压缩

发表于 2016-08-19

安装nodejs、gulp

  • 安装 nodejs
1
brew install node
  • 安装 gulp
1
npm install -g gulp

安装gulp插件

1
npm install gulp-htmlmin gulp-imagemin imagemin-pngcrush gulp-minify-css gulp-jshint gulp-uglify gulp-concat gulp-rename gulp-notify --save-dev

gulp配置文件

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//在你的项目根目录下创建gulpfile.js,代码如下:
// 引入 gulp
var gulp = require('gulp');
// 引入组件
var htmlmin = require('gulp-htmlmin'), //html压缩
imagemin = require('gulp-imagemin'),//图片压缩
pngcrush = require('imagemin-pngcrush'),
minifycss = require('gulp-minify-css'),//css压缩
jshint = require('gulp-jshint'),//js检测
uglify = require('gulp-uglify'),//js压缩
concat = require('gulp-concat'),//文件合并
rename = require('gulp-rename'),//文件更名
notify = require('gulp-notify');//提示信息
// 压缩html
gulp.task('html', function() {
return gulp.src('src/*.html')
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(gulp.dest('./dest'))
.pipe(notify({ message: 'html task ok' }));
});
// 压缩图片
gulp.task('img', function() {
return gulp.src('src/images/*')
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngcrush()]
}))
.pipe(gulp.dest('./dest/images/'))
.pipe(notify({ message: 'img task ok' }));
});
// 合并、压缩、重命名css
gulp.task('css', function() {
return gulp.src('src/css/*.css')
.pipe(concat('main.css'))
.pipe(gulp.dest('dest/css'))
.pipe(rename({ suffix: '.min' }))
.pipe(minifycss())
.pipe(gulp.dest('dest/css'))
.pipe(notify({ message: 'css task ok' }));
});
// 检查js
gulp.task('lint', function() {
return gulp.src('src/js/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(notify({ message: 'lint task ok' }));
});
// 合并、压缩js文件
gulp.task('js', function() {
return gulp.src('src/js/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('dest/js'))
.pipe(rename({ suffix: '.min' }))
.pipe(uglify())
.pipe(gulp.dest('dest/js'))
.pipe(notify({ message: 'js task ok' }));
});
// 默认任务
gulp.task('default', function(){
gulp.run('img', 'css', 'lint', 'js', 'html');
// 监听html文件变化
gulp.watch('src/*.html', function(){
gulp.run('html');
});
// Watch .css files
gulp.watch('src/css/*.css', ['css']);
// Watch .js files
gulp.watch('src/js/*.js', ['lint', 'js']);
// Watch image files
gulp.watch('src/images/*', ['img']);
});

B+树索引

发表于 2016-08-18

Mysql 索引原理

B-树

B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树
它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。下图是 B-树的简化图.

B-树有如下特点:

  • 所有键值分布在整颗树中;
  • 任何一个关键字出现且只出现在一个结点中;
  • 搜索有可能在非叶子结点结束;
  • 在关键字全集内做一次查找,性能逼近二分查找;

B+树

B+树是B-树的变体,也是一种多路搜索树, 它与B-树的不同之处在于:

所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的data)
为所有叶子结点增加了一个链指针

简化 B+树 如下图

为什么使用B-/B+ Tree

红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构。MySQL 是基于磁盘的数据库系统,索引往往以索引文件的形式存储的磁盘上,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。

局部性原理与磁盘预读

磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。

由于磁盘的存取速度与内存之间鸿沟(访问磁盘的成本大概是访问内存的十万倍左右),为了提高效率,要尽量减少磁盘I/O.磁盘往往不是严格按需读取,而是每次都会预读,磁盘读取完需要的数据,会顺序向后读一定长度的数据放入内存。而这样做的理论依据是计算机科学中著名的局部性原理:

  • 当一个数据被用到时,其附近的数据也通常会马上被使用
  • 程序运行期间所需要的数据通常比较集中

由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率.预读的长度一般为页(page)的整倍数。

MySQL(默认使用InnoDB引擎),将记录按照页的方式进行管理,每页大小默认为16K(这个值可以修改).linux 默认页大小为4K(每一次IO读取的数据称之为一页)

B-/+Tree索引的性能分析

实际实现B-Tree还需要使用如下技巧:

1
2
3
每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个结点只需一次I/O。
假设 B-Tree 的高度为 h,B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。
而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

为什么使用 B+树

  • B+树更适合外部存储,由于内节点无data域,一个结点可以存储更多的内结点,每个节点能索引的范围更大更精确,也意味着 B+树单次磁盘IO的信息量大于B-树,I/O效率更高。
  • Mysql是一种关系型数据库,区间访问是常见的一种情况,B+树叶节点增加的链指针,加强了区间访问性,可使用在范围区间查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找。

参考:

MySQL索引结构

MySQL索引背后的数据结构及算法原理

mysql 锁

发表于 2016-08-18

mysql 锁机制

数据库为了维护事务的性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。所以对于加锁的处理,可以说就是数据库对于事务处理的精髓所在。

悲观锁和乐观锁

  • 悲观锁

    正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

    在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。

  • 乐观锁

    相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

乐观锁,大多是基于数据版本( Version )记录机制实现。数据版本:即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

多版本的并发控制协议(以乐观锁为理论基础)

MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,现阶段几乎所有的RDBMS,都支持了MVCC。

在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

以MySQL InnoDB为例:

  • 快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)
    • select * from table where ?;
  • 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

    • select * from table where ? lock in share mode;
    • select * from table where ? for update;
    • insert into table values (…);
    • update table set ? where ?;
    • delete from table where ?;

    所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

插入/更新/删除 操作,都归为当前读.如更新 操作,在数据库中的执行流程:

从图中,可以看到,一个Update操作的具体流程。当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

注:根据上图的交互,针对一条当前读的SQL语句,InnoDB与MySQL Server的交互,是一条一条进行的,因此,加锁也是一条一条进行的。先对一条满足条件的记录加锁,返回给MySQL Server,做一些DML操作(数据操纵语言DML:insert, update, delete);然后在读取下一条加锁,直至读取完毕。

阅读全文 »

java调试-jstack命令

发表于 2016-08-18

jstack命令使用

jstack可用于导出java运用程序的线程堆栈,其基本使用语法为:

1
jstack [-l] pid

-l 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.

常用用法

下面这段代码运行之后会出现死锁现象(因为线程1持有lock1,在等待lock2,线程2持有lock2在等待lock1,造成了循环等待,形成死锁):

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
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.zsr.test.lock;
/**
*
* Created by david.zhang
*/
public class TestDeadLock {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("线程1执行....");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock2) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("线程2执行...");
}
}
}
});
t1.start();
t2.start();
}
}

运行这段代码,然后使用jstack命令导出这个程序的线程堆栈信息:

1
nalideMacBook-Pro-4:public nali$ jstack -l 35072 > /Users/nali/deadlock.txt

打开导出的线程堆栈信息文件,文件末尾如下所示:

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
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007fe6d48432b8 (object 0x00000007d558e9d8, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007fe6d4844758 (object 0x00000007d558e9e8, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.zsr.test.lock.TestDeadLock$2.run(TestDeadLock.java:39)
- waiting to lock <0x00000007d558e9d8> (a java.lang.Object)
- locked <0x00000007d558e9e8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at com.zsr.test.lock.TestDeadLock$1.run(TestDeadLock.java:23)
- waiting to lock <0x00000007d558e9e8> (a java.lang.Object)
- locked <0x00000007d558e9d8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.

导出的线程堆栈文件中明确提示发现死锁,并且指明了死锁的原因。

jstack不仅能够导出线程堆栈,还能自动进行死锁检测,输出线程死锁原因。

java调试-jmap命令

发表于 2016-08-18

jmap命令使用

jmap是一个多功能的命令。它可以生成java程序的堆dump文件,也可以查看堆内对象实例的统计信息,查看ClassLoader的信息以及Finalizer队列。

常用用法

  • 导出对象统计信息

下面的命令生成PID为24205的java成粗的对象的统计信息,并输出到out.txt文件中:

1
nalideMacBook-Pro-4:~ nali$ jmap -histo 24205 > /Users/nali/out.txt

生成的文件如下:

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
num #instances #bytes class name
----------------------------------------------
1: 962520 23100480 java.lang.Long
2: 481260 19250400 com.ximalaya.test.Ip
3: 962779 15404464 java.lang.Integer
4: 481300 15401600 java.util.HashMap$Entry
5: 6362 9556232 [I
6: 19 7341744 [Ljava.util.HashMap$Entry;
7: 319 2174856 [Ljava.lang.Object;
8: 6060 781024 <methodKlass>
9: 6060 692032 <constMethodKlass>
10: 415 486208 <constantPoolKlass>
11: 378 296288 <constantPoolCacheKlass>
12: 415 285880 <instanceKlassKlass>
13: 1358 156272 [C
14: 573 94240 [B
15: 125 52928 <methodDataKlass>
16: 478 46680 java.lang.Class
17: 704 45152 [[I
18: 631 38168 [S
19: 1334 32016 java.lang.String
20: 46 25024 <objArrayKlassKlass>
21: 101 6464 java.net.URL
22: 79 5688 java.lang.reflect.Field
23: 8 4352 <typeArrayKlassKlass>
24: 96 3840 java.util.LinkedHashMap$Entry
25: 119 3808 java.util.Hashtable$Entry
26: 94 3760 java.lang.ref.SoftReference
27: 91 2912 java.util.concurrent.ConcurrentHashMap$HashEntry
28: 61 2432 [Ljava.lang.String;

从文件中可以看到,统计信息显示了内存中实例的数量和合计。

  • 导出程序堆快照

下面的命令导出PID为24205的java程序当前的堆快照:

1
2
3
nalideMacBook-Pro-4:~ nali$ jmap -dump:format=b,file=dump.bin 24205
Dumping heap to /Users/nali/dump.bin ...
Heap dump file created

该命令成功地将运用程序的当前的堆快照导出到了dump.bin文件,之后可以使用Visual VM,MAT等工具分析对快照文件。

  • 查看java 堆(heap)使用情况
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
nalideMacBook-Pro-4:~ nali$ jmap -heap 30192
Attaching to process ID 30192, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration: //堆内存初始化配置
//对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率
MinHeapFreeRatio = 0
//对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率
MaxHeapFreeRatio = 100
//对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
MaxHeapSize = 2147483648 (2048.0MB)
//对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
NewSize = 1310720 (1.25MB)
//对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
MaxNewSize = 17592186044415 MB
//对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
OldSize = 5439488 (5.1875MB)
//对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
NewRatio = 2
//对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
SurvivorRatio = 8
//对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小
PermSize = 21757952 (20.75MB)
//对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小
MaxPermSize = 85983232 (82.0MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space: //Eden区内存分布
//Eden区总容量
capacity = 136839168 (130.5MB)
//Eden区已使用
used = 62349088 (59.460723876953125MB)
//Eden区剩余容量
free = 74490080 (71.03927612304688MB)
//Eden区使用比率
45.56377308578784% used
From Space: //其中一个Survivor区的内存分布
capacity = 17301504 (16.5MB)
used = 17299704 (16.49828338623047MB)
free = 1800 (0.00171661376953125MB)
99.98959628018466% used
To Space: //另一个Survivor区的内存分布
capacity = 27262976 (26.0MB)
used = 0 (0.0MB)
free = 27262976 (26.0MB)
0.0% used
PS Old Generation //当前的Old区内存分布
capacity = 89128960 (85.0MB)
used = 35824408 (34.164817810058594MB)
free = 53304552 (50.835182189941406MB)
40.19390330595129% used
PS Perm Generation //当前的 “永生代” 内存分布
capacity = 22020096 (21.0MB)
used = 2801672 (2.6718826293945312MB)
free = 19218424 (18.32811737060547MB)
12.723250616164435% used
734 interned Strings occupying 47744 bytes.
1…252627…31
David

David

Develop Notes

155 日志
37 标签
GitHub Weibo
© 2016 - 2020 David
由 Hexo 强力驱动
主题 - NexT.Pisces