简

人生短暂,学海无边,而大道至简。


  • 首页

  • 归档

  • 分类

  • 标签

Apache Bench与JVisualVM联调

发表于 2017-04-01 | 分类于 Tomcat
  • Apache Bench 负责模拟用户请求
  • JVisualVM负责观察Tomcat的Java虚拟机内存、线程、CPU的变化

启动Tomcat,再使用JVisualVM远程连接Tomcat,JVisualVM远程连接Tomcat。

JVisualVM

1
2
3
4
5
6
JDK发布的功能最强大的运行监视和故障处理工具,他可以做到:
显示虚拟机进程以及进程的配置、环境信息
监视应用程序的CPU、GC、堆、方法区以及线程的信息
dump以及分析堆转储快照
方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法
离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照、可以将快照发送开发者处进行BUG反馈

JVisualVM监控Tomcat

1、本地Tomcat:打开JVisualVm 既可以开始监控

2、远程Tomcat

2.1、配置服务器端Tomcat的JMX,在Tomcat的bin目录下的catalina.sh添加以下内容
20200331130248

2.2、客户端启动JVisualVM工具

右键”远程”–>“添加远程主机”–>在”主机名”处输入Tomcat所在服务器IP地址–>点击”确认”

右键刚刚创建的远程主机,选择”添加JMX连接,输入连接:192.168.214.150:11111;勾选”不要求SSL连接”,点击确认,点击创建成功的JMX连接。JVisualVM连接远程Tomcat成功

可以看到顶栏处有4个菜单,分别为:概述、监视、线程、抽样器

Tables Are
概述 包括JVM参数和系统相关属性等基本信息
监视 展示了CPU、堆、类装载、线程的活动情况
线程 展示所有的线程及各自的状态:运行、休眠、等待、驻留、监视
抽样器 细致的内存和CPU的实时监控

之后Apache Bench 负责模拟用户请求,再观察JVisualVm变化。

Tomcat7新的连接池 TOMCAT JDBC POOL

发表于 2017-04-01 | 分类于 Tomcat

Tomcat 在 7.0 以前的版本都是使用 commons-dbcp 做为连接池的实现,但是 dbcp具有问题,原因有:

  • dbcp 是单线程的,为了保证线程安全会锁整个连接池。
  • dbcp 性能不佳,特别是高度并发系统。
  • dbcp 太复杂,超过 60 个类。
  • dbcp 使用静态接口,必须使用给定的jre,在 JDK 1.6 编译有问题。

因此很多人会选择一些第三方的连接池组件:c3p0 , bonecp, druid等。为此,Tomcat 从 7.0 开始引入一个新的模块:Tomcat jdbc pool

  • tomcat jdbc pool 近乎兼容 dbcp ,性能更高,异步方式获取连接。
  • tomcat jdbc pool 是 tomcat 的一个模块,基于 tomcat JULI,使用 Tomcat 的日志框架。
    使用 javax.sql.PooledConnection 接口获取连接。支持高并发应用环境超简单,核心文件只有8个,更好的空闲连接处理机制;支持 JMX;支持 XA Connection。

详情请看https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html

tomcat jdbc pool 可在 Tomcat 中直接使用,也可以在独立的应用中使用。

使用方法

Tomcat 中项目直接使用

创建context.xml,放置于META-INF下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
name="test"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/ssm"
username="root"
password="123456"
maxActive="50"
maxIdle="20"
auth="Container"
maxWait="10000"
type="javax.sql.DataSource"
/>
</Context>

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
Context cxt = new InitialContext();//使用了JNDI本地目录访问
DataSource ds = (DataSource) cxt.lookup("java:comp/env/test");//complete/environment/test
Connection conn = ds.getConnection();//从连接池取连接
PreparedStatement ps = conn.prepareStatement("select * from user");
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getLong(1) + " "+rs.getString(2)+" "+rs.getInt(3) );
}
rs.close();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}

这种连接池当得到连接时,把连接对象状态改为:Active 关闭连接对象时,把连接对象归还给数据库连接池,把状态改变成:Idle

独立应用使用

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
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOExample {
public static void main(String[] args) throws Exception {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);


Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+
" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) try {con.close();}catch (Exception ignore) {}
}
}
}

Linux的项目Can't connect to X11 window server的问题

发表于 2017-04-01 | 分类于 Tomcat

在Linux,tomcat中,web程序使用了AWT绘图,在浏览的时候出现以下异常;

1
2
3
4
java.lang.InternalError: Can't connect to X11 window server using 'localhost:10.0' as the value of the DISPLAY variable.
at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
at sun.awt.X11GraphicsEnvironment.access$000(X11GraphicsEnvironment.java:53)
`

CentOS系统,如果以init 5启动,显示是GUI方式运行,不会有这个异常,如果是纯命令模式init 3启动,就异常。

是因为awt绘图api会调用操作系统本地窗口资源绘图,windows对此支持很好,在linux下如果没有进到X window,AWT就不能绘图。

网上的解决方案如下:
1、在JVM中加入-Djava.awt.headless=true 对于tomcat ,可以修改catalina.sh,加入 CATALINA_OPTS=”$CATALINA_OPTS -Djava.awt.headless=true”
已验证,无效

2、修改tomcat的startup.sh
加入export CATALINA_OPTS=”-Djava.awt.headless=true”。
问题解决,init 3启动级别下,java awt没问题。

tomcat和运用集成--内嵌Tomcat的应用开发

发表于 2017-04-01 | 分类于 Tomcat

将tomcat作为应用的一部分集成到应用中,使得应用可以直接开启http服务,对外提供接口。此时应用程序不必再遵守j2ee中的文件目录格式要求。
此种方式改变了以往先部署tomcat容器,再按照j2ee标准目录格式部署应用的做法,开发人员无需搭建Tomcat的环境就可以使用内嵌式Tomcat进行开发,减少搭建J2EE容器环境的时间和开发时容器频繁启动所花时间,提高开发的效率。

Tomcat组成结构

代码创建Tomcat如下:

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
public class TomcatStarter {
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
//因为Tomcat启动时,需要指定它的工作目录
tomcat.setBaseDir("/temp");
//可获得一个Server实例,并可以设置此server的port值,在此端口上监听server关闭请求。
Server server = tomcat.getServer();
// server.setPort(8080);
//可获得service实例,其真实类型为StandardService,service实际为上述创建的server中的service;
Service service = tomcat.getService();
service.setName("Tomcat-embbeded");
//可获得一个connector实例,默认protocol为“HTTP/1.1”。
//此connector实际上为上述创建的service中的connector。
//用于设置监听的端口,即http请求时访问的端口。
Connector connector = tomcat.getConnector();
// connector.setPort(8888);
//创建context时,会将默认的engine、host一同创建,因此在TomcatStarter中可以忽略其创建。
//addContext中的两个参数分别是contextPath和docBase。
//contextPath:即网络请求是的路径;
//docBase:即在tomcatBaseDir/webapps目录下的目录,可以不包含任何文件,但必须存在
Context context = tomcat.addContext("", "/");
//用于向指定的context添加servlet。
//tomcat.addServlet("", "test", new MyServlet());
//用于完成servlet在context中的映射配置。
//这里同web.xml中的配置相似。
context.addServletMappingDecoded("/*", "test");
server.start();
server.await();

/**
* 注意:
* service、connector、engine、host的显式创建可以忽略。
* engine、host会在context创建时创建;
* server会在engine创建时创建;
* service会在server创建时创建;
* 如果没有显式创建connector,需要使用tomcat实例去设置port值,使用tomcat实例启动服务时,会创建connector;
*
* 简化后的TomcatStarter可以为:
* Tomcat tomcat = new Tomcat();
* tomcat.setPort(8080);
* Context context = tomcat.addContext("", "/");
* tomcat.addServlet("", "test", new MyServlet());
* context.addServletMappingDecoded("/*", "test");
* tomcat.start();
* tomcat.getServer().await();
*/

}
}

实例

文件结构:

依赖:

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>tomcat-dev</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.20</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util</artifactId>
<version>8.5.9</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>8.5.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

创建一个servlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DemoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.write("parameter:"+ req.getParameter("par"));
out.flush();
out.close();
}
}

创建Tomcat实例并且配置:

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
public class TomcatStarterDemo {
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
//设置端口和编码格式
Connector connector = new Connector();
connector.setURIEncoding("UTF-8");
connector.setPort(80);
tomcat.getService().addConnector(connector);
//配置servlet
Context context = tomcat.addContext("demo",null);
tomcat.addServlet(context,"demoServlet",new DemoServlet());
//servlet映射路径
context.addServletMappingDecoded("/demoServlet","demoServlet");

//设置webapps目录
String dir = System.getProperty("user.dir")+ File.separator;
System.out.println(dir);
tomcat.getHost().setAppBase(dir);
tomcat.addWebapp("","webapps");

tomcat.start();
//维持tomcat服务
tomcat.getServer().await();
}
}

运行:http://localhost/demo/demoServlet?par=hello

测试成功,在webapp下创建html,一样可以访问。

Tomcat热部署的三种方式

发表于 2017-04-01 | 分类于 Tomcat

热部署是指在你修改项目BUG的时候对JSP或JAVA类进行了修改在不重启WEB服务器前提下能让修改生效。但是对配置文件的修改除外!

1、直接把项目web文件夹放在webapps里。

2、在tomcat\conf\server.xml中的内部添加标签:

docBase:项目路径,可以使用绝对路径或相对路径,相对路径是相对于webapps
path:访问项目的路径,如:http://127.0.0.1:8080/demo1
reloadable:是否自动加载新增或改变的class文件.
debug属性与这个Engine关联的Logger记录的调试信息的详细程度。数字越大,输出越详细。如果没有指定,缺省为0。 也就是程序异常时写入日志文件里的详细程度。

3、第三种方式和第二种差不多,但是不是在Server.xml文件中添加Context标签,而是在
%tomcat_home%\conf\Catalina\localhost中添加一个XML文件,

如:demo1.xml,内容如下:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="D:\demo1\web" reloadable="true" />

只是缺少了path属性,如果你硬写个path="demo2"的话,是没有用的,tomcat不明确的告诉你:"我不认得", 这种方式服务器会使用xml文件的名字作为path属性的名字,SO访问地址为:http://localhost:8080/demo1

还有一种方式:使用第三方工具 http://www.zeroturnaround.com/jrebel/

1
2
3
4
5
6
7
8
9
10
11
12
1、解压缩包
2、执行Java -jar jrebel-setup.jar
3、会弹出一个安装界面(自己看,很好懂)
4、在MyEclipse中配置,找到服务器(如tomcat)的配置页面,在tomcat的jdk配置页面的Optional Java VM arguments输入框中加如下参数:-noverify -javaagent:D:\dev\JRebel\jrebel.jar -Drebel.dirs=D:\dev\apache-tomcat-6.0.20\webapps\nmr
第一个路径是jar包的绝对路径,第二个地址是说想让哪个目录下的类更改后不重启服务。
如果Optional java VM arguments输入框中有其他参数,用空格隔开。
5、发布项目,就能在控制台看到效果。

类就可以改变之后不用重启tomcat。目前流行的服务器都支持。详细看官方网站。
下载的压缩包文件名为jrebel-setup.zip (7.8 MB)

可参考:http://japankn.javaeye.com/blog/577931

MySQL索引管理和优化

发表于 2017-01-20 | 分类于 mysql

MySQL 中的索引简介

索引的优点

创建索引可以大大提高系统的查询性能。

第一、通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二、可以大大加快 数据的检索速度,这也是创建索引的最主要的原因。
第三、可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四、在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。

索引的缺点

增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢? 增加索引也有许多不利的一个方面:

第一、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二、索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。
第三、当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

什么样的字段适合创建索引

索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。

第一、在经常需要搜索的列上,可以加快搜索的速度;
第二、在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
第三、在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
第四、在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
第五、在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
第六、在经常使用在 WHERE 子句中的列上面创建索引,加快条件的判断速度。建立索引,一般按照 select 的 where 条件来建立,比如: select 的条件是 where f1 andf2,那么如果我们在字段 f1 或字段 f2 上建立索引是没有用的,只有在字段 f1 和 f2 上同时建立索引才有用

什么样的字段不适合创建索引

第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
第二,对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
第三,对于那些定义为 text, image 和 bit 数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
第四,当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

MySQL 中的索引种类

B-Tree 索引;Full-text 索引

MySQL 中的索引管理

在 MySQL 中,对索引的查看和删除操作是所有索引类型通用的。

普通索引

这是最基本的索引,它没有任何限制 MyIASM 中默认的 BTREE 类型的索引,也是我们大多数情况下用到的索引。
创建索引
CREATE INDEX index_name ON table_name (column(length))
ALTER TABLE table_name ADD INDEX index_name (column(length))
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY
KEY(id) , INDEX index_name (title(5)))
查看索引
SHOW INDEX FROM [table_name]
SHOW KEYS FROM [table_name] # 只在 MySQL 中可以使用 keys 关键字。
删除索引
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY

唯一索引

与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似

创建索引
CREATE UNIQUE INDEX index_name ON table_name (column(length))
ALTER TABLE table_name ADD UNIQUE index_name (column(length))
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY
KEY(id) , UNIQUE index_name (title(length)))

全文索引(FULLTEXT)

MySQL 从 3.23.23 版开始支持全文索引和全文检索,FULLTEXT 索引仅可用于 MyISAM表;他们可以从 CHAR、VARCHAR 或 TEXT 列中作为 CREATE TABLE 语句的一部分被创建,或是随后使用 ALTER TABLE 或 CREATE INDEX 被添加。对于较大的数据集,将你的资料输入一个没有 FULLTEXT 索引的表中,然后创建索引,其速度比把资料输入现有 FULLTEXT 索引的速度更为快。不过切记对于大容量的数据表,生成全文索引是一个非常消耗时间非常消耗硬盘空间的做法。

创建索引
CREATE FULLTEXT INDEX index_name ON table_name(column(length))
ALTER TABLE table_name ADD FULLTEXT index_name( column)
CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY
KEY(id) , FULLTEXT index_name (title))

组合索引(最左前缀)

CREATE TABLE article(id int not null, title varchar(255), time date);

平时用的 SQL 查询语句一般都有比较多的限制条件,所以为了进一步榨取 MySQL 的效率,就要考虑建立组合索引。例如上表中针对 title 和 time 建立一个组合索引:ALTER TABLEarticle ADD INDEX index_title_time (title(50),time(10))。建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:

-title,time -title

MySQL 中的索引优化

上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点。虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行 INSERT、UPDATE和 DELETE 次数大于查询次数时,放弃索引。因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。索引只是提高效率的一个因素,如果你的 MySQL 有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。

索引不会包含有 NULL 值的列

只要列中包含有 NULL 值都将不会被包含在索引中,组合索引中只要有一列含有 NULL值,那么这一列对于此组合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为 NULL。create table table_name(c1 varchar(32) default ‘0’)

使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个 CHAR(255)的列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作。
CREATE INDEX index_name ON table_name (column(length))

索引列排序

MySQL 查询只使用一个索引,因此如果 where 子句中已经使用了索引的话,那么 orderby 中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

like 语句操作

一般情况下不鼓励使用 like 操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引,而 like “aaa%”可以使用索引。

不要在列上进行运算

例如:select * from users where YEAR(adddate)<2007,将在每个行上进行运算,这将导致 索 引 失 效 而 进 行 全 表 扫 描 , 因 此 我 们 可 以 改 成 : select * from users where adddate<’2007-01-01′ 8 索引总结

总结

MySQL 只对以下操作符才使用索引:<,<=,,=,>,>=,between,in,以及某些时候的 like(不以通配符%或_开头的情形)。而理论上每张表里面最多可创建 16 个索引,不过除非是数据量真的很多,否则过多的使用索引也不是那么好玩的。

建议:一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

常见优化策略

避免全表扫描

对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

避免判断 null 值

应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null
可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询:select id from t where num=0

避免不等值判断

应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。

避免使用 or 逻辑

应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20

可以这样查询:
select id from t where num=10
union all
select id from t where num=20

慎用 in 和 not in 逻辑

in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t1 where num in(select id from t2 where id > 10)
此时外层查询会全表扫描,不使用索引。可以修改为:select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id
此时索引被使用,可以明显提升查询效率。

注意模糊查询

下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’ 模糊查询如果是必要条件时,可以使用 select id from t where name like ‘abc%’来实现模糊查询,此时索引将被使用。如果头匹配是必要逻辑,建议使用全文搜索引擎(Elastic search、Lucene、Solr 等)。

避免查询条件中字段计算

应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where num/2=100
应改为: select id from t where num=100*2

避免查询条件中对字段进行函数操作

应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where substring(name,1,3)='abc'--name 以 abc 开头的 id
应改为:select id from t where name like 'abc%' 9.9WHERE 子句"="左边注意点
不要在 where 子句中的"="左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

组合索引使用

在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

不要定义无异议的查询

不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)

exists

很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)

索引也可能失效

并不是所有索引对查询都有效,SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL 查询可能不会去利用索引,如一表中有字段 sex,male、female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。

表格字段类型选择

尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

尽可能的使用 varchar 代替 char ,因为首先可变长度字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

查询语法中的字段

任何地方都不要使用 select * from t ,用具体的字段列表代替”*”,不要返回用不到的任何字段。

索引无关优化

不使用*、尽量不使用 union,union all 等关键字、尽量不使用 or 关键字、尽量使用等值判断。
表连接建议不超过 5 个。如果超过 5 个,则考虑表格的设计。(互联网应用中)表连接方式使用外联优于内联。
外连接有基础数据存在。
如:A left join B,基础数据是 A。
A inner join B,没有基础数据的,先使用笛卡尔积完成全连接,在根据连接条件得到内连接结果集。
大数据量级的表格做分页查询时,如果页码数量过大,则使用子查询配合完成分页逻辑。
Select * from table limit 1000000, 10
Select * from table where id in (select pk from table limit 100000, 10)

MySQL数据库引擎介绍

发表于 2016-12-20 | 分类于 mysql

MySQL架构

20200420155948

MySQL 数据库引擎简介

ISAM(Indexed Sequential Access Method)

ISAM 是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM 的两个主要不足之处在于,它不支持事务处理,也不能够容错。如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把 ISAM 用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MYSQL 能够支持这样的备份应用程序。

注意:使用 ISAM 注意点:必须经常备份所有实时数据。

MyISAM

MyISAM 是 MySQL 的 ISAM 扩展格式(MySQL5.5 之前版本的缺省数据库引擎)数据库引擎。除了提供 ISAM 里所没有的索引和字段管理的大量功能,MyISAM 还使用一种表格锁定的机制,来优化多个并发的读写操作,其代价是你需要经常运行 OPTIMIZE TABLE 命令,来恢复被更新机制所浪费的空间。MyISAM 还有一些有用的扩展,例如用来修复数据库文件的 MyISAMCHK 工具和用来恢复浪费空间的 MyISAMPACK 工具。

MYISAM 强调了快速读取操作,这可能就是为什么 MySQL 受到了 WEB 开发如此青睐的主要原因:在 WEB 开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和 INTERNET 平台提供商只允许使用 MYISAM 格式。MyISAM 格式的一个重要缺陷就是不能在表损坏后恢复数据。MyISAM 引擎使用注意:必须经常使用 Optimize Table 命令清理空间;必须经常备份所有实时数据。工具有用来修复数据库文件的 myISAMCHK 工具和用来恢复浪费空间的MyISAMPACK 工具。不支持事务。数据越多,写操作效率越低。因为要维护数据和索引信息。(索引列越多,相对效率月底。)如果使用该数据库引擎,会生成三个文件:

.frm:表结构信息
.MYD:数据文件
.MYI:表的索引信息

InnoDB

InnoDB 数据库引擎都是造就 MySQL 灵活性的技术的直接产品,这项技术就是 MYSQL++API。在使用 MYSQL 的时候,你所面对的每一个挑战几乎都源于 ISAM 和 MyISAM 数据库引擎不支持事务处理(transaction process)也不支持外键。尽管要比 ISAM 和 MyISAM 引擎慢很多,但是 InnoDB 包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。是现在的 MySQL(5.5 以上版本)常用版本默认引擎MySQL 官方对 InnoDB 是这样解释的:InnoDB 给 MySQL 提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID 兼容)存储引擎。InnoDB 锁定在行级并且也在 SELECT 语句提供一个 Oracle 风格一致的非锁定读,这些特色增加了多用户部署的性能。没有在 InnoDB 中扩大锁定的需要,因为在 InnoDB 中行级锁定适合非常小的空间。InnoDB 也支持 FOREIGN KEY强制。在 SQL 查询中,你可以自由地将 InnoDB 类型的表与其它 MySQL 的表的类型混合起来,甚至在同一个查询中也可以混合。

InnoDB 是为处理巨大数据量时的最大性能设计,它的 CPU 效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。InnoDB 存储引擎被完全与 MySQL 服务器整合,InnoDB 存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB 存储它的表&索引在一个表空间中,表空间可以包含数个文件(或原始磁盘分区)。这与 MyISAM 表不同,比如在 MyISAM 表中每个表被存在分离的文件中。InnoDB 表可以是任何尺寸,即使在文件尺寸被限制为 2GB 的操作系统上。在 MySQL5.7 版本中,InnoDB 存储引擎管理的数据文件为两个:分别是 frm,idb 文件。

InnoDB 特点:

1)、支持事务
2)、数据多版本读取(InnoDB+MyISAM+ISAM)
3)、锁定机制的改进
4)、实现外键

innodb 与 myisam 区别

1
2
3
4
5
1. InnoDB 支持事务,MyISAM 不支持,对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条 SQL 语言放在 begin transaction 和 commit 之间,组成一个事务;
2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
3. InnoDB 是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
5. Innodb 不支持全文索引,而 MyISAM 支持全文索引,查询效率上 MyISAM 要高;

如何选择

1
2
3
4
1. 是否要支持事务,如果要请选择 innodb,如果不需要可以考虑 MyISAM
2. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用 InnoDB。
3. 系统崩溃后,MyISAM 恢复起来更困难,能否接受;
4. MySQL5.5 版本开始 Innodb 已经成为 Mysql 的默认引擎(之前是 MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用 InnoDB,至少不会差。

Memory 存储引擎

Memory 存储引擎,通过名字就很容易让人知道,他是一个将数据存储在内存中的存储引擎。Memory 存储引擎不会将任何数据存放到磁盘上,仅仅存放了一个表结构相关信息的.frm 文件在磁盘上面。所以一旦 MySQLCrash 或者主机 Crash 之后,Memory 的表就只剩下一个结构了。Memory 表支持索引,并且同时支持 Hash 和 B-Tree 两种格式的索引。由于是存放在内存中,所以 Memory 都是按照定长的空间来存储数据的,而且不支持 BLOB 和 TEXT类型的字段。Memory 存储引擎实现页级锁定。

linux上安装yum和使用

发表于 2016-06-21 | 分类于 linux

安装

通过wget命令下载安装

[root@localhost ~]# wget http://yum.baseurl.org/download/3.4/yum-3.4.3.tar.gz
--2019-02-17 07:22:41--  http://yum.baseurl.org/download/3.4/yum-3.4.3.tar.gz
Resolving yum.baseurl.org... 140.211.9.35
Connecting to yum.baseurl.org|140.211.9.35|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1140370 (1.1M) [application/x-gzip]
Saving to: "yum-3.4.3.tar.gz"

100%[======================================>] 1,140,370   23.3K/s   in 28s    

2019-02-17 07:23:09 (40.0 KB/s) - "yum-3.4.3.tar.gz" saved [1140370/1140370]

查看并且移动到tmp中解压

[root@localhost ~]# ls
anaconda-ks.cfg  Downloads           Music     Templates
Desktop          install.log         Pictures  Videos
Documents        install.log.syslog  Public    yum-3.4.3.tar.gz
[root@localhost ~]# mv yum-3.4.3.tar.gz /usr/local/tmp
[root@localhost ~]# ls /usr/local/tmp
apache-tomcat-7.0.68.tar.gz  mysql-5.6.31-linux-glibc2.5-x86_64.tar.gz
Demo.class                   yum-3.4.3.tar.gz
Demo.java
[root@localhost ~]# cd /usr/local/tmp
[root@localhost tmp]# tar -zxvf yum-3.4.3.tar.gz

进入解压的文件,运行安装yum

[root@localhost yum-3.4.3]# ./yummain.py install yum
Loaded plugins: fastestmirror, refresh-packagekit, security
Loading mirror speeds from cached hostfile
* base: mirrors.huaweicloud.com
* extras: mirrors.shu.edu.cn
* updates: mirrors.shu.edu.cn
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package yum.noarch 0:3.2.29-40.el6.centos will be updated
---> Package yum.noarch 0:3.2.29-81.el6.centos.0.1 will be an update
--> Processing Dependency: python-urlgrabber >= 3.9.1-10 for package: yum-3.2.29-81.el6.centos.0.1.noarch
--> Running transaction check
---> Package python-urlgrabber.noarch 0:3.9.1-9.el6 will be updated
---> Package python-urlgrabber.noarch 0:3.9.1-11.el6 will be an update
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
Package              Arch      Version                        Repository  Size
================================================================================
Updating:
yum                  noarch    3.2.29-81.el6.centos.0.1       updates    1.0 M
Updating for dependencies:
python-urlgrabber    noarch    3.9.1-11.el6                   base        86 k

Transaction Summary
================================================================================
Upgrade       2 Packages

Total download size: 1.1 M
Is this ok [y/N]: y
Downloading Packages:
(1/2): python-urlgrabber-3.9.1-11.el6.noarch.rpm         |  86 kB     00:00    
warning: rpmts_HdrFromFdno: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Public key for python-urlgrabber-3.9.1-11.el6.noarch.rpm is not installed
(2/2): yum-3.2.29-81.el6.centos.0.1.noarch.rpm           | 1.0 MB     00:03    
Public key for yum-3.2.29-81.el6.centos.0.1.noarch.rpm is not installed
--------------------------------------------------------------------------------
Total                                           274 kB/s | 1.1 MB     00:04    
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Importing GPG key 0xC105B9DE:
Userid : CentOS-6 Key (CentOS 6 Official Signing Key) <centos-6-key@centos.org>
Package: centos-release-6-5.el6.centos.11.1.x86_64 (@anaconda-CentOS-201311272149.x86_64/6.5)
From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Is this ok [y/N]: y
Running Transaction Check
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Updating   : python-urlgrabber-3.9.1-11.el6.noarch                        1/4
Updating   : yum-3.2.29-81.el6.centos.0.1.noarch                          2/4
Cleanup    : yum-3.2.29-40.el6.centos.noarch                              3/4
Cleanup    : python-urlgrabber-3.9.1-9.el6.noarch                         4/4

Updated:
yum.noarch 0:3.2.29-81.el6.centos.0.1                                        

Dependency Updated:
python-urlgrabber.noarch 0:3.9.1-11.el6                                      

Complete!
[root@localhost yum-3.4.3]# yum
Loaded plugins: fastestmirror, refresh-packagekit, security
You need to give some command
Usage: yum [options] COMMAND

List of Commands:

check          Check for problems in the rpmdb
check-update   Check for available package updates

至此,安装完成!!!!!!!!!!!!!

1.备份原始源

cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

2.创建新源

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo

  1. 重新缓存

yum clean all

yum makecache

使用yum来安装或卸载CentOS图形界面包

命令行模式安装图形界面
yum grouplist 检查已安装的组
yum groupinstall "X Window System"
yum groupinstall "GNOME Desktop Environment"
startx
或者init 5
就OK。
重新安装了带图形界面模式的centos系统,来实验yum方式卸载图形界面
图形界面想要卸载:
yum groupremove "GNOME Desktop Environment"
yum groupremove "X Window System"
以图形界面安装的linux 卸载图形界面之后会提示错误
init: Id "x" respawning too fast: disabled for 5 minutes
解决的办法是
vi /etc/inittab
找到这段文字 把这段文字中id后面的数字改为3 id:3:initdefault 让系统以文本界面启动
找到这段文字x:5:respawn:/etc/X11/prefdm -nodaemon注释掉!
注(此方法未证实能否保证服务器保持稳定!未在服务器上线实验过)

如何查看yum 安装的软件路径

比如查看yum install keepalived安装后的路径
第一步:
[root@localhost local]# rpm -qa|grep keepalived
keepalived-1.2.13-5.el6_6.x86_64
第二步得结果:
[root@localhost local]# rpm -ql keepalived-1.2.13-5.el6_6.x86_64
/etc/keepalived
/etc/keepalived/keepalived.conf
/etc/rc.d/init.d/keepalived
/etc/sysconfig/keepalived
/usr/bin/genhash
/usr/libexec/keepalived
/usr/sbin/keepalived
/usr/share/doc/keepalived-1.2.13
/usr/share/doc/keepalived-1.2.13/AUTHOR
/usr/share/doc/keepalived-1.2.13/CONTRIBUTORS
/usr/share/doc/keepalived-1.2.13/COPYING
/usr/share/doc/keepalived-1.2.13/ChangeLog
/usr/share/doc/keepalived-1.2.13/NOTE_vrrp_vmac.txt
/usr/share/doc/keepalived-1.2.13/README
/usr/share/doc/keepalived-1.2.13/TODO
/usr/share/doc/keepalived-1.2.13/VERSION
/usr/share/doc/keepalived-1.2.13/keepalived.conf.SYNOPSIS
/usr/share/doc/keepalived-1.2.13/samples
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.HTTP_GET.port
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.IPv6
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.SMTP_CHECK
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.SSL_GET
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.fwmark
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.inhibit
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.misc_check
/usr/share/doc/keepalived-1.2.13/samples/keepalived.conf.misc_check_arg

yum命令

1.使用YUM查找软件包
命令:yum search
2.列出所有可安装的软件包
命令:yum list
3.列出所有可更新的软件包
命令:yum list updates
4.列出所有已安装的软件包
命令:yum list installed
5.列出所有已安装但不在 Yum Repository 内的软件包
命令:yum list extras

6.列出所指定的软件包
命令:yum list XX
7.使用YUM获取软件包信息
命令:yum info XX
8.列出所有软件包的信息
命令:yum info
9.列出所有可更新的软件包信息
命令:yum info updates
10.列出所有已安装的软件包信息
命令:yum info installed
11.列出所有已安装但不在 Yum Repository 内的软件包信息
命令:yum info extras
12.列出软件包提供哪些文件
命令:yum provides

java网络编程

发表于 2016-05-11 | 分类于 java

1、Socket编程

Socket(套接字):封装着如端口号,ip地址,计算机名等信息的类。通过Socket我们可以和远程计算机通信。

网络通信模型

C/S Client/Server

客户端通航运行在用户的计算机上,客户端是一个特有的程序,实现特有的功能,连接服务器进行通信,谁发起连接谁是用户。
服务器端通常是等待客户端连接,提供功能服务并与之通信。

B/S

固定了客户端和通信协议和C/S结构。

通信协议:计算机通信的实质就是相互收发字节。那么按照一定的格式收发字节就是通信协议。

一个最简单的网络通信:

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
/**
* 创建客户端Socket
* Socket客户端类
* 构造的时候就会根据给定的服务端ip地址和服务端的端口号尝试连接
*/
try {
System.out.println("开始连接");
Socket socket = new Socket("172.16.3.33", 8088);
System.out.println("与服务端连接成功!");
/**
* 通过socket可以获取一组与服务器通信的输入输出流
* 我们对其包装就可以方便进行读写信息了。
* 通过socket拿到的是两个低级流(字节流)
*/
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
/**
* 向服务器发送字符,将字符输出流转换成缓冲字符输出流
*/
PrintWriter pw = new PrintWriter(out);
pw.println("你好服务器!");
pw.flush();
/**
* 收取服务器发送的字符串,包装为缓冲字符输入流
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
//读取服务器发回的信息

String info = reader.readLine();
System.out.println("服务端:"+info);
} catch (IOException e) {
//e.printStackTrace();
}
/**
* 服务端
* 服务器端打开socket等待客户端的连接
* 服务器端的Socket名称是ServerSocket
* 创建ServerSocket需要指定服务端口号
*/
public static void main(String[] args) {
try {
System.out.println("dddd");
ServerSocket server = new ServerSocket(8050);
/**
* 监听端口
* 等待客户端连接
* 等待客户端连接的方法accept()
* 该方法是一个阻塞方法,知道有客户端连接上该方法才会返回,返回的就是当前客户端的套接字。
* 从中我们可以知道客户端的ip等信息。
*
* accept()方法可以重复调用
*/
System.out.println("启动完毕,等待连接");
Socket client = server.accept();
System.out.println("有一个客户端和我连接了");
/**
* 通过socket获取输入流读取客户端的信息
*/
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();

BufferedReader reader = new BufferedReader(new InputStreamReader(in));
System.out.println("客户端:"+reader.readLine());

//向客户端发信息
PrintWriter pw = new PrintWriter(out);
pw.println("你好客户端");
pw.flush();
} catch (IOException e) {
//e.printStackTrace();
}
}

2、多线程Socket

Server端多线程:

服务器端无限循环接受客户端的访问,每连接都能茶圣一对新的Socket的实例。

为每个客户端连接创建一个独立线程,处理客户请求。

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
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(8088);
System.out.println("启动完毕,等待连接");

while (true) {
Socket client = server.accept();
if(client==null)continue;
System.out.println("与客户端连接成功"+client.getInetAddress().getHostAddress());
Handler handler = new Handler(client);//交给线程去处理
Thread t = new Thread(handler);
t.start();
}
} catch (IOException e) {
}
}
/**
* 与客户端通信线程
* 负责与某个特定的socket的客户端进行通信
* 每个线程实例负责一个客户端的通信
*/
private static class Handler implements Runnable {
/**
* 当前线程要通信的客户端socket
*/
private Socket client;
public Handler(Socket client) {
this.client = client;
}
public void run(){
try {
/** 通过socket获取客户端信息 */
InputStream in = this.client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
/** 死循环读取客户端信息 */

while (true) {
if (reader.readLine() == null)
return;
System.out.println("客户端"+ this.client.getInetAddress().getHostAddress() + ":" + reader.readLine());
}
} catch (Exception e) {
}
}
}

3、线程池

上面这种频繁的创建线程和销毁线程是非常消耗资源和性能的。

可以创建一些空的线程,将它们保存起来,当有任务需要并发执行时,我们取出一个空的线程来运行这个任务,当任务运行完毕后,在将线程收回,等待下次分配任务。

这样自始自终我们都只使用了开始创建的那些线程,并可以重复利用来节省性能开销。

JDK提供了线程池的管理器ExecutorService

通过Executors类的静态方法创建几个不同实现的线程池。

Executors.newCachedThreadPool();创建一个缓存线程池,当有任务的时候会检查线程池中是否有空线程,若有就使用它,若没有就创建新的线程。若长久没有使用的线程会自动回收。

Executors.newFixedThreadPool(int threads);创建一个可重用的,具有固定线程数的线程池。

Executors.newScheduledExecutor();创建只有一条线程的线程池,它可以在指定延迟后执行线程任务。

Executors.newSingleThreadExecutor();创建一个只有单线程的线程池,相当于Exceutors.newFixedThreadPool方法时传入参数1。里边维护着一个任务队列。

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
/**
* 创建一个线程池,具有50个
*/
ExecutorService threadPool =Executors.newFixedThreadPool(50);
try {
ServerSocket server = new ServerSocket(8088);
System.out.println("启动完毕,等待连接");
/** 将转发消息线程启动 */
SendMessageHandler sHandler = new SendMessageHandler();
Thread sendTh = new Thread(sHandler);
sendTh.start();

while (true) {
Socket client = server.accept();
System.out.println("与客户端连接成功"+client.getInetAddress().getHostAddress());
Handler handler = new Handler(client);//交给线程去处理
/**
* 将需要并发的任务(Runnable)交给线程池
* 让其分配空线程去运行该任务
* 若线程都在工作,那么登载,直到有空线程为止。
*/
threadPool.execute(handler);
//Thread t = new Thread(handler);
//t.start();
}
} catch (IOException e) {
}

4、双端队列

内部由两个队列实现,他们交替进行存取工作。这样至少可以保证2个线程同时进行存取操作。但是对于两个线程同时进行存或者取,还是要求同步的。

但是比单队列实现线程安全还是要快的。

BlockingDeque双端队列

实现:

1)ArrayBlockingDeque该类实现的构造方法要求我们传入一个整数,代表当前队列的长度。所以这个是一个固定大小的双端队列,存取原则为FIFO先入先出的原则。

当元素调用offer存入了队列时,若队列已满,那么可以设置延时等待,当超过了延时等待会返回存入失败。

2)LinkedBlockingDeque变长双端队列。长度不定,随着元素数量而增加,最大可以达到Integer.MAX_VALUE,重载构造方法可以传入一个整数,使之变为一个定长的队列。

3)PriorityBlockingDeque 这个和LinkedBlockingDeque相似,只不过是进去了自然排序后获取。

4)SynchronousQueue特殊的双端队列 存取步骤有要求,必须存一次取一次,交替进行。

例:

服务端

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* 创建一个静态集合保护所有客户端的数输入流
* 注意:因为这个集合被多个线程使用,所哟一集合要是安全的。
*/
static Vector<PrintWriter> allOut = new Vector<PrintWriter>();
/** 消息队列 */
static BlockingDeque<String> msgQueue = new LinkedBlockingDeque<String>();
public static void main(String[] args) {
/**
* 创建一个线程池,具有50个
*/
ExecutorService threadPool =Executors.newFixedThreadPool(50);
try {
ServerSocket server = new ServerSocket(8088);
System.out.println("启动完毕,等待连接");
/** 将转发消息线程启动 */
SendMessageHandler sHandler = new SendMessageHandler();
Thread sendTh = new Thread(sHandler);
sendTh.start();

while (true) {
Socket client = server.accept();
System.out.println("与客户端连接成功"+client.getInetAddress().getHostAddress());
Handler handler = new Handler(client);//交给线程去处理
/**
* 将需要并发的任务(Runnable)交给线程池
* 让其分配空线程去运行该任务
* 若线程都在工作,那么登载,直到有空线程为止。
*/
threadPool.execute(handler);
//Thread t = new Thread(handler);
//t.start();
}
} catch (IOException e) {
}
}
/**
* 与客户端通信线程
* 负责与某个特定的socket的客户端进行通信
* 每个线程实例负责一个客户端的通信
*/
private static class Handler implements Runnable {
/**
* 当前线程要通信的客户端socket
*/
private Socket client;

public Handler(Socket client) {
this.client = client;
}
public void run(){
PrintWriter pw = null;
try {
/** 通过socket获取客户端信息 */
InputStream in = this.client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
/**
* 将当前客户端的输出流保存到共享集合
*/
pw = new PrintWriter(client.getOutputStream());
allOut.add(pw);
/** 死循环读取客户端信息 */
while (true) {
String str = reader.readLine();
/**
* 将信息放入到消息队列
*/
if("q".equals(str)){
client.close();
}
msgQueue.offer(str);
/**
* arg1:存放的元素
* arg2:延时时间
* arg3:延时的时间单位
*
* 下面方法是向队列中存放元素,设置5秒超时,若超时了还没有放入队列这返回false
*/
boolean tf = msgQueue.offer(str, 5, TimeUnit.SECONDS);
}
} catch (Exception e) {
e.printStackTrace();
/**
* 抛出异常,我们应该将这个客户端的输出流从共享集合中删除
* 告诉其他线程不要再发信息了。
*/
allOut.remove(pw);
}
}
}
/**
* 转发消息
* 循环消息队列,将每一个消息通过所有客户端的输入流发送给客户端
* 做到广播的效果。
*/
public static class SendMessageHandler implements Runnable{
public void run() {
while (true) {
/** 循环将消息发送给每一个客户端 每50ms作一次*/
String str = null;
while ((str=msgQueue.poll())!=null) {//and msgQueue.poll()!=null
/** 获取所有客户端的输出流,将字符串输出 */
for (PrintWriter pw : allOut) {
pw.println(str);
pw.flush();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

客户端:

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
public static void main(String[] args) {
/**
* 创建客户端Socket
* Socket客户端类
* 构造的时候就会根据给定的服务端ip地址和服务端的端口号尝试连接
*/
try {
System.out.println("开始连接");
Socket socket = new Socket("172.16.3.14", 8088);
System.out.println("与服务端连接成功!");

//启动消息线程
Thread readerMsgTh = new Thread(new ReadMessageHandler(socket.getInputStream()));
readerMsgTh.start();

OutputStream out = socket.getOutputStream();
PrintWriter pw = new PrintWriter(out);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
pw.println(reader.readLine());
pw.flush();
}
} catch (IOException e) {
//e.printStackTrace();
}
}
/**
* 该线程用于从服务器读取信息。
*/
public static class ReadMessageHandler implements Runnable{
private InputStream in;//从服务端读取信息
public ReadMessageHandler(InputStream in) {
this.in = in;
}
public void run() {
try {
//将字节输入流转换为缓冲字符输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
System.out.println(reader.readLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

http://www.cnblogs.com/springcsc/archive/2009/12/03/1616413.html这里有一篇文章讲的比较细,把地址记录下来,供查询,感谢作者。

linux安装JavaEE环境JDK、Tomcat、Mysql

发表于 2016-04-21 | 分类于 linux

JDK

1、把压缩包上传到linux服务器目录/usr/local/tmp
这里使用FileZilla上传
2、解压压缩包:tar zxvf jdk-8u191-linux-x64.tar.gz
3、把解压后的文件复制到/usr/local/jdk8
cp -r jdk1.8.0_191 /usr/local/jdk8
4、配置环境变量,linux是通过修改环境变量配置文件来配置,使用vim编辑:
vim /etc/profile
在文件中添加环境变量信息:
#export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
export JAVA_HOME=/usr/local/jdk8
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar#jdk1.6以后不用配置classpath

20200421165530

5、解析文件或者重启系统
source /etc/profile
6、验证java环境:
[root@localhost ~]# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

Tomcat安装

1、把安装包上传到服务器/usr/local/tmp
[root@localhost local]# ls tmp
apache-tomcat-7.0.68.tar.gz  Demo.class  Demo.java  mysql-5.6.31-linux-glibc2.5-x86_64.tar.gz
2、解压
[root@localhost local]# tar -zxvf tmp/apache-tomcat-7.0.68.tar.gz
3、复制解压文件到/usr/local/tomcat7.0
命令:cp -r apache-tomcat-7.0.68 /usr/local/tomcat7.0
上一步直接把文件解压到local目录了,这一步直接省略。
[root@localhost local]# ls
apache-tomcat-7.0.68  bin  etc  games  include  jdk8  lib  lib64  libexec  sbin  share  src  tmp
4、添加环境变量vim /etc/profile
export TOMCAT_HOME=/usr/local/apache-tomcat-7.0.68
export CATALINA_HOME=/usr/local/apache-tomcat-7.0.68#tomcat启动主服务

20200421165616

运行配置:source /etc/profile
5、开放端口让通过防火墙(centos默认开放端口只有22)
[root@localhost local]# vim /etc/sysconfig/iptables
类似22端口,增加开放8080端口的配置:
-A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT
比如要开放连续的多个端口:8080:9900  #开放8080到9900之间的所有端口。
重启服务,使其端口配置生效。
[root@localhost local]# service iptables restart
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
iptables: Applying firewall rules:  
分别有:restart  start   stop

6、启动Tomcat
进入到Tomcat的bin目录
直接启动:./startup.sh
[root@localhost bin]# ./startup.sh
Using CATALINA_BASE:   /usr/local/apache-tomcat-7.0.68
Using CATALINA_HOME:   /usr/local/apache-tomcat-7.0.68
Using CATALINA_TMPDIR: /usr/local/apache-tomcat-7.0.68/temp
Using JRE_HOME:        /usr/local/jdk8
Using CLASSPATH:       /usr/local/apache-tomcat-7.0.68/bin/bootstrap.jar:/usr/local/apache-tomcat-7.0.68/bin/tomcat-juli.jar
Tomcat started.
[root@localhost bin]# ./shutdown.sh

启动并打印启动信息:
[root@localhost bin]# ./startup.sh & tailf /usr/local/apache-tomcat-7.0.68/logs/catalina.out
..............###显示启动过程的日志信息
INFO: Server startup in 1567 ms

MYSQL

CentOS上安装MySql官方推荐使用rpm在线安装的方式进行安装。但是使用在线安装的方式无法修改安装的路径,而且速度慢。

1、复制MySQL道服务器/usr/local/tmp

2、解压缩到/usr/local

[root@localhost local]# tar -zxvf tmp/mysql-5.6.31-linux-glibc2.5-x86_64.tar.gz
修改文件名太长
[root@localhost local]# mv mysql-5.6.31-linux-glibc2.5-x86_64 mysql-5.6.31
[root@localhost local]# ls
apache-tomcat-7.0.68  etc    include  lib    libexec       sbin   src
bin                   games  jdk8     lib64  mysql-5.6.31  share  tmp

3、创建用户和用户组

首先:单独为MySQL创建用户和用户组的目的是为了保证mysql服务的独立性,隔离风险;即便mysql服务被黑掉,得到了mysql用户权限,也不会影响整个系统的安全,因为读写不了其他文件。             
其次:所指定的用户是想要运行mysqld进程用户名。这个是可变的,不一定非要是mysql。设置这个用户以后,所有通过mysqld进程创建的文件都会属于这个用户。
添加名为mysql用户组
[root@localhost local]# groupadd mysql
创建用户mysql,并指定所属组为mysql
[root@localhost local]# useradd -r -g mysql mysql

4、赋权,让用户组合用户具有操作权限

一定要保证在需要赋予某个目录权限的这个目录中操作;.号表示本级目录。
变更mysql用户组具有操作本级目录的权限:
[root@localhost mysql-5.6.31]# chgrp -R mysql .
变更mysql用户具有操作本级目录的权限:
[root@localhost mysql-5.6.31]# chown -R mysql .
上面两条命令可以同时换成一条来完成操作:
chown -R mysql:mysql ./
mysql:mysql,第一个mysql是用户名,第二个mysql是组名
chown -R mysql:mysql /usr/local/mysql-5.6.31  #这种也可以

5、初始化(安装数据库注册服务)

判断/etc/my.cnf(mysql全局配置文件)是否存在,如果存在,则删除。
[root@localhost mysql-5.6.31]# ls /etc/my.cnf
/etc/my.cnf
[root@localhost mysql-5.6.31]# rm /etc/my.cnf
rm: remove regular file `/etc/my.cnf'? y
[root@localhost mysql-5.6.31]# ls /etc/my.cnf
ls: cannot access /etc/my.cnf: No such file or directory
执行初始化脚本文件
[root@localhost mysql-5.6.31]# ls scripts
mysql_install_db
[root@localhost mysql-5.6.31]# ./scripts/mysql_install_db --user=mysql #使用mysql用户执行该脚本

6、修改配置文件

配置my.cnf和启动文件,如没有特殊操作,直接复制mysql服务启动配置文件到/etc/下并重命名为my.cnf
[root@localhost mysql-5.6.31]#  cp support-files/my-default.cnf /etc/my.cnf
其实步骤5中的判断/etc/my.cnf可以不用,如果文件存在会提示是否覆盖?
[root@localhost mysql-5.6.31]#  cp support-files/my-default.cnf /etc/my.cnf
cp: overwrite `/etc/my.cnf'? y
配置启动服务文件
将mysql加入到可控制启动的服务的文件夹内,并命名mysql,就可以用service mysql start控制启动mysql
[root@localhost mysql-5.6.31]# cp support-files/mysql.server /etc/rc.d/init.d/mysql
    /etc/init.d 是/etc/rc.d/init.d的链接,在/etc/init.d添加一个文件会同步在/etc/rc.d/init.d下添加一个相同文件
测试出问题:
[root@localhost mysql-5.6.31]# service mysql-5.6.31 start
/etc/init.d/mysql-5.6.31: line 256: my_print_defaults: command not found
/etc/init.d/mysql-5.6.31: line 276: cd: /usr/local/mysql: No such file or directory
Starting MySQL ERROR! Couldn't find MySQL server (/usr/local/mysql/bin/mysqld_safe)

原因是我的MySQL文件夹带了版本信息,目录名不是默认mysql,解决办法在MySQL全局配置/etc/mycnf中设置MySQL的主目录。
[root@localhost init.d]# vim /etc/my.cnf  
basedir=/usr/local/mysql-5.6.31
datadir=/usr/local/mysql-5.6.31/data
或者
vim /etc/init.d/mysql
#   basedir=<path-to-mysql-installation-directory>
basedir=/usr/local/mysql-5.6.31
datadir=/usr/local/mysql-5.6.31/data
保存,再测试---通过
[root@localhost mysql-5.6.31]# service mysql start
Starting MySQL.. SUCCESS!

开机自启动mysql服务
让mysql服务加入到开机启动指令管理的服务列表中,前提是服务文件拷贝到init.d下
chkconfig --add mysql
显示服务列表:chkconfig --list
如果mysql服务的345都是no的话则执行:
chkconfig mysql on
否则执行如下命令:
chkconfig --level 345 mysql on
on : 针对的服务有等级限制,具体查询chkconfig详细
验证:reboot重启后,运行netstat -na | grep 3306有监听说明成功

7、命令行操作MySQL

以上配置没有问题,启动服务后可以通过命令进入MySQL命令行模式:
[root@localhost mysql-5.6.31]# mysql -u root -p
-bash: mysql: command not found
提示没有命令,需要添加软连接,把mysql中/bin/mysql添加到/usr/bin/中,类似windows中添加一个可执行文件的path或者快捷方式。
[root@localhost mysql-5.6.31]# ln -s /usr/local/mysql-5.6.31/bin/mysql /usr/bin/mysql
这样就可以使用mysql命令行了
[root@localhost mysql-5.6.31]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
...
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases ;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.11 sec)

8、设置root密码

如果忘记密码无法登陆,则先通过启动安全模式的方式进入到mysql后修改密码。
进入/etc/my.cnf 在[mysql]下添加skip-grant-tables 启动安全模式   vi /etc/my.cnf
重启服务:service mysql restart
登录mysql,输入密码时直接回车mysql -u root -p
修改mysql数据库中的user表的password,5.7版本以下才有这个列,5.7及其上密码列为authentication_string:mysql> use mysql
Database changed
mysql> update user set password=password('123456') where user='root';
刷新权限:
mysql> flush privileges;
Query OK, 0 rows affected (0.02 sec)
退出,重新连接,测试ok
[root@localhost mysql-5.6.31]# mysql -u root -p123456
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.

9、设置远程连接(外部访问数据库)

执行赋予权限命令
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
刷新权限:
mysql> flush privileges;
注:
grant 权限 on 数据库对象 to 用户
如果想让授权的用户,也可以将这些权限 grant 给其他用户,需要选项 "grant option"
%可能是localhost或192.168.0.%或192.168.%.%等等,所以直接使用%
    grant使用可以参考https://www.cnblogs.com/crxis/p/7044582.html

开启防火墙mysql3306端口的外部访问,CentOS升级到7之后,使用firewalld代替了原来的iptables。
[root@localhost mysql-5.6.31]# vim /etc/sysconfig/iptables
[root@localhost mysql-5.6.31]# service iptables restart
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
iptables: Applying firewall rules:                         [  OK  ]

外部测试:

20200421165727

10、一系列测试如下

mysql> show satabases;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'satabases' at line 1                 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.02 sec)

mysql> create database demo default character set utf8;
Query OK, 1 row affected (0.01 sec)

mysql> use demo
Database changed
mysql> create table user(id int(10) primary key auto_increment, name varchar(20));
Query OK, 0 rows affected (0.08 sec)

mysql> show tables;
+----------------+
| Tables_in_demo |
+----------------+
| user           |
+----------------+
1 row in set (0.00 sec)

mysql> insert into user values(default,'张三丰'),(default,'江小白');
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from user;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 张三丰    |
|  2 | 江小白    |
+----+-----------+
2 rows in set (0.00 sec)

11、在服务器上如何批量操作sql语句

拷贝sql脚本文件到服务器
在MySQL命令行中执行脚本文件:source /usr/local/tmp/xx.sql


在mysql有时执行了错误的update或者delete时导致大量数据错误恢复的办法。执行时没有开启事务,也没有对数据进行。这时就需要使用到sqlbinlog工具。

sqlbinlog需要开启,具体的打开方法就不说了。

使用sqlbinlog会产生bin文件,恢复就需要用到这些文件。文件中记录着数据库的所有操作。(此方法的操作是将数据库之前所执行的语句重新执行一次,以达到恢复效果)

具体步骤:

1,先找到bin文件,一般都是在mysql的data文件夹中,结尾以.00000X等形式结束。
2,寻找需要还原的时间点 使用语句   mysqlbinlog  文件名    例(MySQLbinlog xxbin.000001)来查看内容,然后找到对应的具体时间
3,导出sql语句,使用语句   文件名>sql文件路径   例(mysqlbinlog xxxbin,00001>>a.sql mysql -u root -p
如果需要指定时间导出--start--date -stop='' ' 来导出指定时间执行的语句例(sqlbinlog --start-stop='2015-11-22 10:00:00'  xxbin.000001>a.sql mysql -u root -p )这句意思是导出在2015-11-22 10点之前的语句,反之start是导出时间之后的。 和stop可以同时使用。
如果存在多个bin文件,则按照需要导出。
4,使用mysql将导出的语句执行一次。
上一页1…232425下一页
初晨

初晨

永远不要说你知道本质,更别说真相了。

249 日志
46 分类
109 标签
近期文章
  • WebSocket、Socket、TCP、HTTP区别
  • Springboot项目的接口防刷
  • 深入理解Volatile关键字及其实现原理
  • 使用vscode搭建个人笔记环境
  • HBase介绍安装与操作
© 2018 — 2020 Copyright
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4