spring整体架构说明
- spring framework
- spring session
- spring security
- spring data
- spring integration
- spring boot
npm install hexo -g #安装
npm update hexo -g #升级
hexo init #初始化
hexo n "我的博客" == hexo new "我的博客" #新建文章
hexo p == hexo publish
hexo g == hexo generate#生成
hexo s == hexo server #启动服务预览
hexo d == hexo deploy#部署
hexo server #Hexo 会监视文件变动并自动更新,您无须重启服务器。
hexo server -s #静态模式
hexo server -p 5000 #更改端口
hexo server -i 192.168.1.1 #自定义 IP
hexo clean #清除缓存 网页正常情况下可以忽略此条命令
hexo g #生成静态网页
hexo d #开始部署
hexo generate #使用 Hexo 生成静态文件快速而且简单
hexo generate --watch #监视文件变动
两个命令的作用是相同的
hexo generate --deploy
hexo deploy --generate
hexo deploy -g
hexo server -g
hexo publish [layout] <title>
hexo new "postName" #新建文章
hexo new page "pageName" #新建页面
hexo generate #生成静态页面至public目录
hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy #将.deploy目录部署到GitHub
hexo new [layout] <title>
hexo new photo "My Gallery"
hexo new "Hello World" --lang tw
变量 | 描述 |
---|---|
layout | 布局 |
title | 标题 |
date | 文件建立日期 |
title: 使用Hexo搭建个人博客
layout: post
date: 2014-03-03 19:07:43
comments: true
categories: Blog
tags: [Hexo]
keywords: Hexo, Blog
description: 生命在于折腾,又把博客折腾到Hexo了。给Hexo点赞。
hexo new photo "My Gallery"
变量 | 描述 |
---|---|
layout | 布局 |
title | 标题 |
date | 文件建立日期 |
以上是文章摘要 <!--more--> 以下是余下全文
hexo new page <title>
hexo new post <title>
变量 | 描述 |
---|---|
:title | 标题 |
:year | 建立的年份(4 位数) |
:month | 建立的月份(2 位数) |
:i_month | 建立的月份(去掉开头的零) |
:day | 建立的日期(2 位数) |
:i_day | 建立的日期(去掉开头的零) |
hexo n #写文章
hexo g #生成
hexo d #部署 #可与hexo g合并为 hexo d -g
##报错
ERROR Deployer not found: git
###解决方法
npm install hexo-deployer-git --save
hexo 3.0 部署类型不再是github,_config.yml 中修改
# Deployment
## Docs: http://hexo.io/docs/deployment.html
deploy:
type: git
repository: git@***.github.com:***/***.github.io.git
branch: master
MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。
一般来说开启二进制日志大概会有1%的性能损耗(参见MySQL官方中文手册 5.1.24版)。二进制有两个最重要的使用场景:
其一:MySQL Replication在Master端开启binlog,Mster把它的二进制日志传递给slaves来达到master-slave数据一致的目的。
其二:自然就是数据恢复了,通过使用mysqlbinlog工具来使恢复数据。
二进制日志包括两类文件:二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件,二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句)语句事件。
vi编辑打开mysql配置文件
# vi /usr/local/mysql/etc/my.cnf
在[mysqld] 区块
设置/添加 log-bin=mysql-bin 确认是打开状态(值 mysql-bin 是日志的基本名或前缀名);
重启mysqld服务使配置生效
# pkill mysqld
# /usr/local/mysql/bin/mysqld_safe --user=mysql &
登录服务器
# /usr/local/mysql/bin/mysql -uroot -p123456
mysql> show variables like 'log_%';
+----------------------------------------+---------------------------------------+
| Variable_name | Value |
+----------------------------------------+---------------------------------------+
| log_bin | ON | ------> ON表示已经开启binlog日志
| log_bin_basename | /usr/local/mysql/data/mysql-bin |
| log_bin_index | /usr/local/mysql/data/mysql-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| log_error | /usr/local/mysql/data/martin.err |
| log_output | FILE |
| log_queries_not_using_indexes | OFF |
| log_slave_updates | OFF |
| log_slow_admin_statements | OFF |
| log_slow_slave_statements | OFF |
| log_throttle_queries_not_using_indexes | 0 |
| log_warnings | 1 |
+----------------------------------------+---------------------------------------+
mysql> show master logs;
mysql> show master status;
mysql> flush logs;
注:每当mysqld服务重启时,会自动执行此命令,刷新binlog日志;在mysqldump备份数据时加 -F 选项也会刷新binlog日志;
mysql> reset master;
注: binlog是二进制文件,普通文件查看器cat more vi等都无法打开,必须使用自带的 mysqlbinlog 命令查看
binlog日志与数据库文件在同目录中(我的环境配置安装是选择在/usr/local/mysql/data中)
在MySQL5.5以下版本使用mysqlbinlog命令时如果报错,就加上 “--no-defaults”选项
# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000013
下面截取一个片段分析:(得知可以用–base64-output=DECODE-ROWS -v查看出来sql语句)
...............................................................................
# at 552
#131128 17:50:46 server id 1 end_log_pos 665 Query thread_id=11 exec_time=0 error_code=0 ---->执行时间:17:50:46;pos点:665
SET TIMESTAMP=1385632246/*!*/;
update zyyshop.stu set name='李四' where id=4 ---->执行的SQL
/*!*/;
# at 665
#131128 17:50:46 server id 1 end_log_pos 692 Xid = 1454 ---->执行时间:17:50:46;pos点:692
...............................................................................
注: server id 1 数据库主机的服务号;
end_log_pos 665 pos点
thread_id=11 线程号
mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];
选项解析:
IN 'log_name' 指定要查询的binlog文件名(不指定就是第一个binlog文件)
FROM pos 指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)
LIMIT [offset,] 偏移量(不指定就是0)
row_count 查询总条数(不指定就是所有行)
截取部分查询结果:
*************************** 20. row ***************************
Log_name: mysql-bin.000021 ----------------------------------------------> 查询的binlog日志文件名
Pos: 11197 ----------------------------------------------------------> pos起始点:
Event_type: Query ----------------------------------------------------------> 事件类型:Query
Server_id: 1 --------------------------------------------------------------> 标识是由哪台服务器执行的
End_log_pos: 11308 ----------------------------------------------------------> pos结束点:11308(即:下行的pos起始点)
Info: use `zyyshop`; INSERT INTO `team2` VALUES (0,345,'asdf8er5') ---> 执行的sql语句
*************************** 21. row ***************************
Log_name: mysql-bin.000021
Pos: 11308 ----------------------------------------------------------> pos起始点:11308(即:上行的pos结束点)
Event_type: Query
Server_id: 1
End_log_pos: 11417
Info: use `zyyshop`; /*!40000 ALTER TABLE `team2` ENABLE KEYS */
*************************** 22. row ***************************
Log_name: mysql-bin.000021
Pos: 11417
Event_type: Query
Server_id: 1
End_log_pos: 11510
Info: use `zyyshop`; DROP TABLE IF EXISTS `type`
这条语句可以将指定的binlog日志文件,分成有效事件行的方式返回,并可使用limit指定pos点的起始偏移,查询条数;
mysql> show binlog events\G;
mysql> show binlog events in 'mysql-bin.000021'\G;
mysql> show binlog events in 'mysql-bin.000021' from 8224\G;
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 10\G;
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 2,10\G;
将zyyshop数据库备份到 /root/BAK.zyyshop.sql 文件中:
# /usr/local/mysql/bin/mysqldump -uroot -p123456 -lF --log-error=/root/myDump.err -B zyyshop > /root/BAK.zyyshop.sql
......
大约过了若干分钟,备份完成了,我不用担心数据丢失了,因为我有备份了,嘎嘎~~~
由于我使用了-F选项,当备份工作刚开始时系统会刷新log日志,产生新的binlog日志来记录备份之后的数据库“增删改”操作,查看一下:
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000023 | 120 | | |
+------------------+----------+--------------+------------------+
也就是说, mysql-bin.000023 是用来记录4:00之后对数据库的所有“增删改”操作。
@ 比如:创建一个学生表并插入、修改了数据等等:
CREATE TABLE IF NOT EXISTS `tt` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(16) NOT NULL,
`sex` enum('m','w') NOT NULL DEFAULT 'm',
`age` tinyint(3) unsigned NOT NULL,
`classid` char(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
导入实验数据
mysql> insert into zyyshop.tt(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');
查看数据
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | xiaoer | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | lisi | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
中午时分又执行了修改数据操作
mysql> update zyyshop.tt set name='李四' where id=4;
mysql> update zyyshop.tt set name='小二' where id=2;
修改后的结果:
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | 小二 | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | 李四 | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
假设此时是下午18:00,莫名地执行了一条悲催的SQL语句,整个数据库都没了:
mysql> drop database zyyshop;
备份一下最后一个binlog日志文件:
# ll /usr/local/mysql/data | grep mysql-bin
# cp -v /usr/local/mysql/data/mysql-bin.000023 /root/
此时执行一次刷新日志索引操作,重新开始新的binlog日志记录文件,理论说 mysql-bin.000023 这个文件不会再有后续写入了(便于我们分析原因及查找pos点),以后所有数据库操作都会写入到下一个日志文件;
mysql> flush logs;
mysql> show master status;
方式一:使用mysqlbinlog读取binlog日志:
# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000023
方式二:登录服务器,并查看(推荐):
mysql> show binlog events in 'mysql-bin.000023';
以下为末尾片段:
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+
| mysql-bin.000023 | 922 | Xid | 1 | 953 | COMMIT /* xid=3820 */ |
| mysql-bin.000023 | 953 | Query | 1 | 1038 | BEGIN |
| mysql-bin.000023 | 1038 | Query | 1 | 1164 | use `zyyshop`; update zyyshop.tt set name='李四' where id=4|
| mysql-bin.000023 | 1164 | Xid | 1 | 1195 | COMMIT /* xid=3822 */ |
| mysql-bin.000023 | 1195 | Query | 1 | 1280 | BEGIN |
| mysql-bin.000023 | 1280 | Query | 1 | 1406 | use `zyyshop`; update zyyshop.tt set name='小二' where id=2|
| mysql-bin.000023 | 1406 | Xid | 1 | 1437 | COMMIT /* xid=3823 */ |
| mysql-bin.000023 | 1437 | Query | 1 | 1538 | drop database zyyshop |
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+
通过分析,造成数据库破坏的pos点区间是介于 1437--1538 之间,只要恢复到1437前就可。
# /usr/local/mysql/bin/mysql -uroot -p123456 -v < /root/BAK.zyyshop.sql;
注: 至此截至当日凌晨(4:00)前的备份数据都恢复了。
但今天一整天(4:00--18:00)的数据肿么办呢?就得从前文提到的 mysql-bin.000023 新日志做文章了......
恢复语法格式:
# mysqlbinlog mysql-bin.0000xx | mysql -u用户名 -p密码 数据库名
常用选项:
--start-position=953 起始pos点
--stop-position=1437 结束pos点
--start-datetime="2013-11-29 13:18:54" 起始时间点
--stop-datetime="2013-11-29 13:21:53" 结束时间点
--database=zyyshop 指定只恢复zyyshop数据库(一台主机上往往有多个数据库,只限本地log日志)
不常用选项:
-u --user=name Connect to the remote server as username.连接到远程主机的用户名
-p --password[=name] Password to connect to remote server.连接到远程主机的密码
-h --host=name Get the binlog from server.从远程主机上获取binlog日志
--read-from-remote-server Read binary logs from a MySQL server.从某个MySQL服务器上读取binlog日志
# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
@ --stop-position=953 pos结束点
注:此pos结束点介于“导入实验数据”与更新“name='李四'”之间,这样可以恢复到更改“name='李四'”之前的“导入测试数据”
# /usr/local/mysql/bin/mysqlbinlog --stop-position=953 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
在另一终端登录查看结果(成功恢复了):
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | xiaoer | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | lisi | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
更新 name='李四' 这条数据,日志区间是Pos[1038] --> End_log_pos[1164],按事务区间是:Pos[953] --> End_log_pos[1195];
更新 name='小二' 这条数据,日志区间是Pos[1280] --> End_log_pos[1406],按事务区间是:Pos[1195] --> End_log_pos[1437];
c1.单独恢复 name='李四' 这步操作,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1038 --stop-position=1164 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
也可以按事务区间单独恢复,如下:
# /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1195 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
c2.单独恢复 name='小二' 这步操作,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1280 --stop-position=1406 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
也可以按事务区间单独恢复,如下:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1195 --stop-position=1437 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
c3.将 name='李四'、name='小二' 多步操作一起恢复,需要按事务区间,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1437 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | 小二 | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | 李四 | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
比如,我把刚恢复的tt表删除掉,再用时间区间点恢复
mysql> drop table tt;
@ --start-datetime="2013-11-29 13:18:54" 起始时间点
@ --stop-datetime="2013-11-29 13:21:53" 结束时间点
# /usr/local/mysql/bin/mysqlbinlog --start-datetime="2013-11-29 13:18:54" --stop-datetime="2013-11-29 13:21:53" --database=zyyshop /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
总结:所谓恢复,就是让mysql将保存在binlog日志中指定段落区间的sql语句逐个重新执行一次而已。
数据源的分类,大体可以分为三类:结构化数据,半结构化数据,非结构化数据
首先,我们面临的数据源多而杂,有来自公司自有平台的数据,来自第三方现有的数据,来自通过爬取获取的数据。
自有平台的数据包括:自有系统中的数据和各个部门手动整理的历史数据
(1)自有系统的数据,存放在oracle数据库中,而我们抽取的数据统一放在一个数据平台,数据平台采用的数据库为mongodb。所以自有系统的数据采集,关键是如何从oracle到mongodb中。
oracle to oracle迁移方案
oracle to mongodb迁移方案
(2)自有数据,还有一部分是以csv或txt的形式存在
仅有自有的数据是不足以支撑业务需求的分析,所以收集第三方数据是必须的,第三方的数据来源就多种多样了,大体可以二类:来自数据库中的半结构化数据,来自文件的半结构化数据
(1)先将数据存入oracle或mysql,然后使用上述迁移方案完成数据的采集
(2)直接将获取的数据,使用工具导入到oracle
如果数据提供的是txt或csv文件,那么直接使用mongoimport导入mongodb
这一节,没多少要讲的。因为没有接触很深,但是后续是个必须的过程。使用python爬取各种数据,存储成csv或txt文件
爬取的文件,再使用mongodbimport导入mongodb中
由于要提供数据的可视化和搜索平台,建议使用ELK的技术栈,所以数据的收集使用Logstash
想要做数据分析,免费的growing IO。它的分析仅限于界面跳转的转化率,不能详细地分析业务数据。
我研究了一个需要埋点的产品,搞明白他们是在每个接口的调用埋点,将用户对接口的调用行为记录下来,进行分析。由于接口众多,每个接口的数据都不同。
可以充分利用hbase宽表的特性,在一行中定义一个通用的字段来标示当前行的数据类型,操作人,然后定义不同的字段来记录每一种数据。在插入数据的时候,每一行只插入当前类型和当前数据。由于hbase的宽表特性,可以容纳上百万列。可以将一家公司所有的接口访问数据都记录到一张无限大的表中,再配合辅助的用户表,就可以在各种纬度上分析用户的行为了。
分析了他们的表结构,我想用mongodb也可以做同样的事情,并且mongo比hbase好的地方在于,他入门门槛相对较低,然后在索引方面,检索的速度远比hbase那种查询要快多了。hbase只能要么按照主键范围查询,要么全表检索。为什么大的互联网公司都在推行hbase呢,这个是困扰我的地方。问了一个前腾讯员工,搞明白了两者的区别。
原因就在于写入的速度,hbase由于只维护一个主键,写入的速度要比mongodb这种要维护所有索引的数据库快多了。hbase占用两台机器能完成的事情,mongodb要占用更多的机器,每台机器按一年20000的费用,几百台下来就是一笔很大的费用。但是代价就是hbase记录下东西以后,只能事后通过全表检索或按照索引范围的方式进行整体分析,而不能对具体每个人的数据进行实时分析,更强调数据分析能力而不是实时数据查询能力,因此各有千秋吧。像用户行为分析的这种,一开始产品经理可能会具体看某一个人的数据,但是新鲜过后,只会看程序的分析结果了。因此从经济的角度出发,对于用户行为分析这种不需要实时数据的需求来说,hbase+mysql就可以用最经济的方式解决了。mongodb比较适合需要实时返回数据的大数据应用。
在大数据时代,数据研发人员总是想把各类数据采集到我们的数据仓库。最典型的方案是日志收集方案: flume采集文件,转发到kafka,再使用storm写到hdfs。但是实际场景中,我们的数据源不止文件,还有mysql这类db数据。
众所周知,mysql是可以开启binlog的,也就是说我们对db的每个操作都可以通过binlog解析得到。所以我们实时解析mysql的binlog文件,即可实时获取到db的各个变更事件,就可以实时地将insert的数据,像tail日志文件一样,以规范化的形式发送到我们后端的消息中间件。
本文不会拘泥于实现细节,只会列举几个注意点,避免后续人采坑。
binlog row模式
最需要支持的点:
mysql必须支持binlog,且必须是row模式。需要关注几个问题:
1.row模式的binlog是远大于其他模式,需要注意磁盘容量
2.从其他模式binlog(如mix)改为row模式,需要断开已有mysql的连接,需要dba及相关业务开发评估可行性。
3.不需要采集的库表要独立出去,不然大量无关binlog会影响采集器的性能,堵塞通道。(需要推动业务改)
4.row模式下日志变多,还有从库解析方式发生变化,可能会造成主从不一致(状态延迟)的情况,需要dba确认
不支持DDL,只是inset最好,就类似文件的append。update、delete都会增加后端的处理逻辑。
本身就是用于大数据处理的,不支持事务
建议末尾追加字段,只用简易字段(int,string)
binlog方案技术上没什么特别难点,重点还是运营的坑比较多
栈分为java虚拟机栈和本地方法栈
重点是Java虚拟机栈,它是线程私有的,生命周期与线程相同。
每个方法执行都会创建一个栈帧,用于存放局部变量表,操作栈,动态链接,方法出口等。每个方法从被调用,直到被执行完。对应着一个栈帧在虚拟机中从入栈到出栈的过程。
通常说的栈就是指局部变量表部分,存放编译期间可知的8种基本数据类型,及对象引用和指令地址。局部变量表是在编译期间完成分配,当进入一个方法时,这个栈中的局部变量分配内存大小是确定的。
会有两种异常StackOverFlowError和 OutOfMemoneyError。当线程请求栈深度大于虚拟机所允许的深度就会抛出StackOverFlowError错误;虚拟机栈动态扩展,当扩展无法申请到足够的内存空间时候,抛出OutOfMemoneyError。
本地方法栈为虚拟机使用到本地方法服务(native)
堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例。
堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区最要放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。
不过很多文章介绍分为3个区块,把方法区算着为永久代。这大概是基于Hotspot虚拟机划分,然后比如IBM j9就不存在永久代概论。不管怎么分区,都是存放对象实例。
会有异常OutOfMemoneyError
被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)
垃圾回收很少光顾这个区域,不过也是需要回收的,主要针对常量池回收,类型卸载。
常量池用于存放编译期生成的各种字节码和符号引用,常量池具有一定的动态性,里面可以存放编译期生成的常量;运行期间的常量也可以添加进入常量池中,比如string的intern()方法。
当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。
Java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都需要一个独立的程序计数器,所以它是线程私有的。
唯一一块Java虚拟机没有规定任何OutofMemoryError的区块。
1.JVM中堆空间可以分成三个大区,新生代、老年代、永久代。
2.新生代可以划分为三个区,Eden区,两个幸存区。
在JVM运行时,可以通过配置以下参数改变整个JVM堆的配置比例
1.JVM运行时堆的大小
-Xms 堆的最小值
-Xmx 堆空间的最大值
2.新生代堆空间大小调整
-XX:NewSize 新生代的最小值
-XX:MaxNewSize 新生代的最大值
-XX:NewRatio 设置新生代与老年代在堆空间的大小
-XX:SurvivorRatio 新生代中Eden所占区域的大小
3.永久代大小调整
-XX:MaxPermSize
4.其他
-XX:MaxTenuringThreshold, 设置将新生代对象转到老年代时需要经过多少次垃圾回收,但是仍然没有被回收
将内存平均分成A、B两块,算法过程:
优点:简单高效。
缺点:内存代价高,有效内存为占用内存的一半。
平均分成A/B块太浪费内存,采用Eden/S0/S1三个区更合理,空间比例为Eden:S0:S1==8:1:1,有效内存(即可分配新生对象的内存)是总内存的9/10。
算法过程:
Eden+S0可分配新生对象;
对Eden+S0进行垃圾收集,存活对象复制到S1。清理Eden+S0。一次新生代GC结束。
Eden+S1可分配新生对象;
对Eden+S1进行垃圾收集,存活对象复制到S0。清理Eden+S1。二次新生代GC结束。
goto 1。
默认Eden:S0:S1=8:1:1,因此,新生代中可以使用的内存空间大小占用新生代的9/10,那么有人就会问,为什么不直接分成两个区,一个区占9/10,另一个区占1/10,
这样做的原因大概有以下几种
1.S0与S1的区间明显较小,有效新生代空间为Eden+S0/S1,因此有效空间就大,增加了内存使用率
2.有利于对象代的计算,当一个对象在S0/S1中达到设置的XX:MaxTenuringThreshold值后,会将其分到老年代中,设想一下,如果没有S0/S1,直接分成两个区,该如何计算对象经过了多少次GC还没被释放,你可能会说,在对象里加一个计数器记录经过的GC次数,或者存在一张映射表记录对象和GC次数的关系,是的,可以,但是这样的话,会扫描整个新生代中的对象, 有了S0/S1我们就可以只扫描S0/S1区了~~~
#创建:
JVM遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。
如果没有,那必须先执行相应的类的加载过程。
对象所需内存的大小在类加载完成后便完全确定(对象内存布局),为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。
根据Java堆中是否规整有两种内存的分配方式:(Java堆是否规整由所采用的垃圾收集器是否带有压缩整理功能决定)。
指针碰撞(Bump the pointer)
Java堆中的内存是规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离。例如:Serial、ParNew等收集器。
空闲列表(Free List)
Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。例如:CMS这种基于Mark-Sweep算法的收集器。
对象创建在虚拟机中时非常频繁的行为,即使是仅仅修改一个指针指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
虚拟机采用CAS配上失败重试的方式保证更新操作的原子性
本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)
把内存分配的动作按照线程划分为在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存(TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配。只有TLAB用完并分配新的TLAB时,才需要同步锁定。
虚拟机将分配到的内存空间都初始化为零值(不包括对象头),如果使用了TLAB,这一工作过程也可以提前至TLAB分配时进行。
内存空间初始化保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
注意:类的成员变量可以不显示地初始化(Java虚拟机都会先自动给它初始化为默认值)。方法中的局部变量如果只负责接收一个表达式的值,可以不初始化,但是参与运算和直接输出等其它情况的局部变量需要初始化。
虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头之中。
在上面的工作都完成之后,从虚拟机的角度看,一个新的对象已经产生了。但是从Java程序的角度看,对象的创建才刚刚开始init()方法还没有执行,所有的字段都还是零。
所以,一般来说(由字节码中是否跟随invokespecial指令所决定),执行new指令之后会接着执行init()方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算产生出来。
器等一系列信息,当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,同时引
用计数器+1,当有新的引用时,引用计数器继续+1,而当其中一个引用销毁时,引用计数器-1,当引用计数器减
为0的时候,标志着这个对象已经没有引用了,可以回收了!但是这样会有一个问题:
当我们的代码出现这样的情况时:
a)ObjA.obj=ObjB
b)ObjB.obj=ObjA
这样的代码会产生如下引用情形objA指向objB,而ObjB又指向objA,这样当其他所有的引用都消失了之后,objA
和objB还有一个相互的引用,也就是说两个对象的引用计数器各为1,而实际上这两个对象都已经没有额外的引用,已经是垃圾了。
2.根搜索算法:
根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看做一张图,从一个节点GC Root开始,寻找对
应的引用节点,找到这个节点之后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节
点则被认为是没有被饮用到的节点,即无用的节点。
目前Java中可作为GC Root的对象有:
1.虚拟机栈中引用的对象(本地变量表)
2.方法区中静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中引用的对象(Native对象)。
java中存在的四种引用
(1)强引用:
只要引用存在,垃圾回收器永远不会回收。
(2)软引用
非必须引用,内存溢出之前进行回收,可以通过以下代码实现
Object obj=new Object();
SoftReference
obj=null;
sf.get();//有时会返回null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然这个对象被标记为需要回收的对象时,
则返回null;
软引用主要用于用户实现类似缓存的功能,在内存不足的情况下直接通过软引用取值,无需从繁忙的真实来源查
询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真实的来源查询这些数据。
(3)弱引用
第二次垃圾回收时回收,可以通过如下代码实现
Object obj=new Object();
WeakReference
obj=null;
wf.get();//有时会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,
将返回null。弱引用主要用于监控对象是否已经被标记为即将回收的垃圾,可以通过弱引用的isEnQueues方法
返回对象是否被垃圾回收器标记。
(4)虚引用
垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
Object obj=new Object();
PhantomReference
obj=null;
pf.get();//永远是返回null
pf.isEnQueued();//返回从内从中已经删除。
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null。
垃圾回收算法
标记-清除算法(Mark-Sweep)
从根节点开始标记所有可达对象,其余没有标记的即为垃圾对象,执行清除。但回收后的空间是不连续的。
标记-清除算法采用从根集合进行扫描,对存活的对象标记,标记完毕后,在扫描整个空间中未被标记的对象,进
行回收。
标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,
但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。
复制算法
复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象
比较少时,极为高效,但是带来的成本是需要一块内存交换空间进行对象的移动。也就是s0,s1等空间。
标记-整理法
标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时,在回收不存活的对象占用的空间后,
会将所有的存活对象网左端空闲空间移动,并更新相应的指针。标记-整理算法是在标记-清除算法的基础上,
又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。
串行垃圾回收器(Serial Garbage Collector)
并行垃圾回收器(Parallel Garbage Collector)
并发标记扫描垃圾回收器(CMS Garbage Collector)
G1垃圾回收器(G1 GarbageCollector)
并发标记垃圾回收使用多线程扫描堆内存,标记需要清理的实例并且清理被标记过的实例。并发标记垃圾回收器只会在下面两种情况持有应用程序所有线程。
当标记的引用对象在tenured区域;
在进行垃圾回收的时候,堆内存的数据被并发的改变。
相比并行垃圾回收器,并发标记扫描垃圾回收器使用更多的CPU来确保程序的吞吐量。如果我们可以为了更好的程序性能分配更多的CPU,那么并发标记上扫描垃圾回收器是更好的选择相比并发垃圾回收器。
通过JVM参数 XX:+USeParNewGC 打开并发标记扫描垃圾回收器。
G1垃圾回收器适用于堆内存很大的情况,他将堆内存分割成不同的区域,并且并发的对其进行垃圾回收。G1也可以在回收内存之后对剩余的堆内存空间进行压缩。并发扫描标记垃圾回收器在STW情况下压缩内存。G1垃圾回收会优先选择第一块垃圾最多的区域
通过JVM参数–XX:+UseG1GC 使用G1垃圾回收器
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。
这一定义既清晰又易于理解。但是,当发生Minor GC事件的时候,有一些有趣的地方需要注意到:
当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。
其中的真相就是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。
如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。
所以 Minor GC 的情况就相当清楚了——每次 Minor GC 会清理年轻代的内存。
大家应该注意到,目前,这些术语无论是在 JVM 规范还是在垃圾收集研究论文中都没有正式的定义。但是我们一看就知道这些在我们已经知道的基础之上做出的定义是正确的,
Minor GC 清理年轻带内存应该被设计得简单:
Major GC 是清理永久代。
Full GC 是清理整个堆空间—包括年轻代和永久代。
很不幸,实际上它还有点复杂且令人困惑。首先,许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。另一方面,许多现代垃圾收集机制会清理部分永久代空间,所以使用“cleaning”一词只是部分正确。
这使得我们不用去关心到底是叫 Major GC 还是 Full GC,大家应该关注当前的 GC 是否停止了所有应用程序的线程,还是能够并发的处理而不用停掉应用程序的线程。
#9. 类加载的五个过程:加载、验证、准备、解析、初始化。
加载:
在加载阶段,虚拟机主要完成三件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
3.在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口。
验证:
验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段:
1.文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。
2.元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。
3.字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。
4.符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。
准备:
准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:
pirvate static int size = 12;
那么在这个阶段,size的值为0,而不是12。 final修饰的类变量将会赋值成真实的值。
解析:
解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。
初始化:
在准备阶段,类变量已经经过一次初始化了,在这个阶段,则是根据程序员通过程序制定的计划去初始化类的变量和其他资源。这些资源有static{}块,构造函数,父类的初始化等。
至于使用和卸载阶段阶段,这里不再过多说明,使用过程就是根据程序定义的行为执行,卸载由GC完成。
类加载器按照层次,从顶层到底层,分为以下三种:
(1)启动类加载器(BootstrapClassLoader)
这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
(2)扩展类加载器(ExtensionClassLoader)
这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
(3)应用程序类加载器(ApplicationClassLoader)
这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载
类加载的双亲委派模型
双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是都使用组合关系来复用父加载器的代码
工作过程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,
只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
好处:
Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。
判断两个类是否相同是通过classloader.class这种方式进行的,所以哪怕是同一个class文件如果被两个classloader加载,那么他们也是不同的类。
实现自己的加载器
只需要继承ClassLoader,并覆盖findClass方法。
在调用loadClass方法时,会先根据委派模型在父加载器中加载,如果加载失败,则会调用自己的findClass方法来完成加载。
静态分派
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派,其典型应用是方法重载(根据参数的静态类型来定位目标方法)。
静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机执行的。
动态分派
在运行期根据实际类型确定方法执行版本。
默认的innodb引擎,开启了二进制日志,
对于事务性的操作,是要事物完成的时候写入二进制日志,事物提交之前,执行的写入性操作会被缓存起来,直到整个事物完成,mysqld进程会将整个事物写入二进制日志。
当事物开始的时候,会按照binlog_cache_size系统变量指定的值分配内容空间,如果指定的binlog_cache_size缓存空间不够,执行的事务性操作回滚并提示失败。
mysql> show variables like '%binlog%';
+--------------------------------------------+----------------------+
| Variable_name | Value |
+--------------------------------------------+----------------------+
| binlog_cache_size | 32768 |
| binlog_checksum | CRC32 |
| binlog_direct_non_transactional_updates | OFF |
| binlog_error_action | ABORT_SERVER |
| binlog_format | ROW |
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
| binlog_gtid_simple_recovery | ON |
| binlog_max_flush_queue_time | 0 |
| binlog_order_commits | ON |
| binlog_row_image | FULL |
| binlog_rows_query_log_events | OFF |
| binlog_stmt_cache_size | 32768 |
| binlog_transaction_dependency_history_size | 25000 |
| binlog_transaction_dependency_tracking | COMMIT_ORDER |
| innodb_api_enable_binlog | OFF |
| innodb_locks_unsafe_for_binlog | OFF |
| log_statements_unsafe_for_binlog | ON |
| max_binlog_cache_size | 18446744073709547520 |
| max_binlog_size | 1073741824 |
| max_binlog_stmt_cache_size | 18446744073709547520 |
| sync_binlog | 1 |
+--------------------------------------------+----------------------+
22 rows in set (0.46 sec)
mysql>
用来记录操作MySQL数据库中的写入性操作(增删改,但不包括查询),相当于sqlserver中的完整恢复模式下的事务日志文件。
1,用于复制,配置了主从复制的时候,主服务器会将其产生的二进制日志发送到slave端,slave端会利用这个二进制日志的信息在本地重做,实现主从同步
2,用户恢复,MySQL可以在全备和差异备份的基础上,利用二进制日志进行基于时间点或者事物Id的恢复操作。原理雷同于主从复制的日志重做。
1,开启二进制日志
开启二进制日志,需要指定一个log-bin参数的路径,比如:log_bin=/var/lib/mysql/mysql-bin
开始二进制日志之后会自动生成一个管理二进制日志的log_bin_index文件。log_bin选项也显示为on,也即开启了二进制日志。
log_bin=ON
log_bin_basename=/var/lib/mysql/mysql-bin
log_bin_index=/var/lib/mysql/mysql-bin.index
三个参数来指定,
第一个参数是打开binlog日志
第二个参数是binlog日志的基本文件名,后面会追加标识来表示每一个文件
第三个参数指定的是binlog文件的索引文件,这个文件管理了所有的binlog文件的目录
当然也有一种简单的配置,一个参数就可以搞定
log-bin=/var/lib/mysql/mysql-bin
这一个参数的作用和上面三个的作用是相同的,mysql会根据这个配置自动设置log_bin为on状态,自动设置log_bin_index文件为你指定的文件名后跟.index
这些配置完毕之后对于5.7以下版本应该是可以了,但是我们这个时候用的如果是5.7及以上版本的话,重启mysql服务会报错。这个时候我们必须还要指定一个参数
server-id=123454
随机指定一个不能和其他集群中机器重名的字符串,如果只有一台机器,那就可以随便指定了
有了上述的配置之后,我们就可以重新启动我们的mysql了
service mysqld restart
启动成功之后,我们可以登陆查看我们的配置是否起作用
show variables like ‘%log_bin%’
之所以不进行es聚合实时查询一个是查询数量过大,另一方面是实时查询要保存大量的原始日志,现在只有5台es data节点,不能承受这么大的原始日志量。原始日志保留一定的天数要进行删除。
当然也有的数据只是查询几天内的数据就直接用es的自身聚合能力了
#python部分脚本示例:
def main_statistic(domain,userId):
body = {
"query": {
"bool": {
"must": [
{
"term": {
"uriHost.raw": domain
}
}
]
}
},
"size": 0,
"aggs": {
"fileCount": {
"terms": {
"field": "mime.raw"
},
"aggs": {
"totalFileSize": {
"sum": {
"field": "repsize"
}
}
}
}
}
}
result = in_es.search(index=common_index.logstash_index,doc_type="fc_access",body=body)
name = result["aggregations"]["fileCount"]
buckets = name["buckets"]
for name_item in buckets:
name_key = name_item["key"]
doc_count = name_item["doc_count"]
totalFileSize = name_item["totalFileSize"]["value"]
if doc_count > 0:
browser_count_item = {
"_index": common_index.spark_portal_index,
"_type": "logstashIndexDF_filetype_totalsize",
"_source": {
"@timestamp": common_index.timestamp_attr,
"add_time": common_index.add_time_attr,
"uriHost": domain,
"userId": userId,
"mime": name_key,
"fileCount": doc_count,
"totalFileSize": totalFileSize
}
}
print browser_count_item
out_count_arr.append(browser_count_item)
# 这是按照用户分类进行数据填充的
browser_count_item_use = {
"_index": common_index.spark_portal_index,
"_type": "logstashIndexDF_filetype_totalsize_sum",
"_source": {
"@timestamp": common_index.timestamp_attr,
"add_time": common_index.add_time_attr,
"userId": userId,
"mime": name_key,
"fileCountSum": doc_count,
"totalFileSizeSum": totalFileSize
}
}
print browser_count_item_use
out_count_arr.append(browser_count_item_use)
def cacl_main(common_index_obj,domain_users):
global common_index
common_index = common_index_obj
global out_count_arr
out_count_arr = []
for domain_user_item in domain_users:
domain = domain_user_item["key"]
userId = domain_user_item["user_id"]
main_statistic(domain=domain, userId=userId)
if len(out_count_arr) > 300:
helpers.bulk(out_es, out_count_arr)
out_count_arr = []