陈志军

物来顺应,未来不迎,当时不杂,既过不恋


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索

数据库索引优化实例

发表于 2018-03-23 | 分类于 数据库 | 阅读次数

数据库索引优化实例

索引对于查找小表或者需要处理表中大部分或全部行的大型表的查询不太重要,适合的才是最好的。

数据库索引的操作

  1. 快速找到匹配where条件的行结果;
  2. 如果有多个索引,MySQL通常会使用能检索除最少结果行的索引;
  3. 如果有多列索引,优化器可以使用索引的最左边的前缀来查找行;比如有col1,col2,col3三列索引,那么你就可以使用(col1),(col1,col2),(col1,col2,col3)三个索引;
  4. 如果有多表连接join的语句,mysql会将varchar(10)和char(10)看成同一个类型并且有效的使用索引;两个表使用的字符要一样比如utf8,和latin,就不行;
  5. 在查找最小值和最大值的时候,优化器会先优化你是否在where条件中有常量值;
  6. 索引排序会使用最左边的索引列前缀;
  7. 在某些情况下,可以优化查询来检索值,而不必咨询数据行

主键与外键优化

主键索引的查询优化得益于主键不能为NULL值。表的数据在物理存储上就被组织为基于主键列或多列进行快速查找和排序。如果你的表很大又非常重要,然而却没有声明主键,或者组合主键,那么你需要新建一个新的列使用auto-increment来作为主键。

外键优化,如果有一个大表,有许多列,可以考虑将表的列进行拆分成小表,并且用外键关联。每个小表都有主键,这样可以让查找数据更加快速,而需要数据的时候也可以是使用join关联查询。由于数据是分散的,查询可以有更少的I/O,并且会使用更少的缓存。因为相关的列在磁盘中存储在一块。

组合索引

建立一个表:

1
2
3
4
5
6
7
CREATE TABLE test (
id INT NOT NULL,
last_name CHAR(30) NOT NULL,
first_name CHAR(30) NOT NULL,
PRIMARY KEY (id),
INDEX name (last_name,first_name)
);

我们有组合索引name,在索引查询中使用最左列查询是会使用索引的。也就是如下面的列子:

1
2
3
4
5
6
7
8
9
10
11
12
SELECT * FROM test WHERE last_name='Widenius';

SELECT * FROM test
WHERE last_name='Widenius' AND first_name='Michael';

SELECT * FROM test
WHERE last_name='Widenius'
AND (first_name='Michael' OR first_name='Monty');

SELECT * FROM test
WHERE last_name='Widenius'
AND first_name >='M' AND first_name < 'N';

即只要有最左侧的索引列,那么索引就会生效。但是如果是单独使用的first_name,那么索引将不会生效,如下面的示例:

阅读全文 »

Java 枚举和注解总结

发表于 2018-03-22 | 分类于 Java | 阅读次数

Java 枚举和注解总结

枚举

没有枚举前我们基本上常量来定义值:

1
2
3
4
5
6
7
public interface Color{
public static final int GREEN = 0;
public static final int RED = 1;
....
....
....
}

如果有了枚举后我们会怎样了?

1
2
3
public enum Color{
GREEN,RED,BLACk,.......
}

代码是不是就清晰很多了?而且使用的时候输出的值我们是可以使用GREEN,是不是可以很明白的知道是什么颜色?

枚举的创建

枚举是继承自Enum,使用enum关键字。

枚举的使用场景

枚举适合在固定的常量下使用,比如四季,月份,星期;这种基本公认的而且不会有改变的场景下使用。

编译器中的枚举

编译器默认帮我们实现了很多枚举中的方法,比如equals(),hashCode(),toString,values(),valueOf(String)等。这些都是编译器帮我们做的。

枚举中的注意事项

  1. 不能使用static,final修饰枚举,因为它是隐式的final类型的;
  2. 因为是final类型,所以我们也就知道它是不能被继承的;
  3. 从Enum继承的clone是final类型的,枚举是不能重写clone方法的,并且Enum里面的clone方法直接抛出异常,所以enum是不能被clone的;
  4. enum中的ordinal是强依赖于枚举实例的定义顺序的,所以用ordinal来做判断顺序是不推荐的,因为只要在非最后加入实例,那么就会改变整体的顺序;如果是需要顺序可以自定义属性。
阅读全文 »

优先队列和堆排序

发表于 2018-03-21 | 分类于 算法 | 阅读次数

优先队列和堆排序

很多情况下,我们会对某个任务做优先级,比如手机系统应用肯定要比第三方应用要优先级要高。实现这种方式的数据结构需要满足:一个最优先的任务(最大或最小值),加入新任务(新元素);这种数据结构可以用优先队列。

优先队列是一种抽象数据类型,表示一组值和对这些值的操作。优先队列最重要的操作就是删除最大元素和插入元素。优先队列里面需要有哪些接口?

1
MaxPQ()

未完待续…

极光推送线下交流分享会

发表于 2018-03-17 | 分类于 随笔 | 阅读次数

极光推送线下交流分享会

周六参加了异常极光推送组织的线下架构师分享会。当然我只是去学习的。

阅读全文 »

希尔排序算法

发表于 2018-03-17 | 分类于 算法 | 阅读次数

希尔排序算法

这个算法是插入排序的基础上做的优化,它描叙与实现可以看这个插入排序。

希尔排序的思想是使间隔为h之间的元素都是有序的。
比如我们说有10个数字,首先我们使用间隔4,那么就是位置为 1,5,2,6,3,7,4,8,5,9;将这些组的相应**位置的值**,我们说的1-9是指的位置,而不是值哦。将这些组进行排序。之后我们再进行第二次分组,也就是将间隔的长度再缩小,假如这里缩小为2,那么第二次的间隔位置组就是:1,3,5,7,9,2,4,6,8`。这样循环比较进行交换之后,我们最终就可以将间隔降为1,这样我们就可以得到一个优化的优化后的插入排序了,也就是常说的希尔排序;

一般来说我们通常将长度设为2的幂除。也就是先用数组长度除2,再除2,再除2;

1
2
3
4
5
6
7
8
9
10
11

public static void (int[] arr){
for(int h=arr.length/2;h>0;h=h/2){
for(int i=gap;i<arr.length;i++){
for(int j=i;j<arr.length&&arr[j]<arr[j-h];j-=h){
swap(arr,j,j-h)
}
}
}
}

在<<算法>>第四版中看到另一种实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public static void sort(int[] arr){
int len = arr.length;
int h = 1;
while(h<arr.length/3){
h=h*3+1;
}

while(h>=1){
for(int i=h;i<len;i++){
for(int j=i;j<len&&a[j]<a[j-h];j-=h){
swap(arr,j,j-h);
}
}
h=h/3;
}
}

1
2
3
4
5
public static void swap(int[] arr,int j,int h){
arr[j] = arr[j]+arr[i];
arr[i] = arr[j]-arr[i];
arr[j] = arr[j]-arr[i];
}

Java 事务处理(包括spring事务管理)

发表于 2018-03-14 | 分类于 Java | 阅读次数

Java 事务处理(包括spring事务管理)

JDBC 的事务处理

Java 中的事务处理有三部分:

  1. 自动提交模式
  2. 事务隔离级别
  3. 保护点

其实实际上java中的事务处理最终依赖的是各数据库的事务处理实现。如果使用的数据库不支持事务,或者提供的数据库驱动程序没有支持事务,那么也是巧妇难为无米之炊。

事务自动提交的方式有:DML(DML),DDL(create),select 查询后结果集关闭,储存过程执行后。

事务隔离级别有:脏读;不可重复读;幻读。

保护点:部分事务回滚;选择性释放。

DriverManager–> Connection –> Statement –> ResultSet –> ResultSetMetaData。

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
Connection connection = null;
try {
connection = DriverManager.getConnection("");
//将事务的自动提交关系
connection.setAutoCommit(false);

/*
业务逻辑(操作数据库)处理
doService();
*/

//提交事务
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
//还原自动提交事务
connection.setAutoCommit(true);
//关闭connection
connection.close();
} catch (SQLException e1) {
e1.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

Connection connection = null;
Savepoint savepoint = null;
try {
connection = DriverManager.getConnection("");
savepoint = connection.setSavepoint();
connection.commit();
} catch (SQLException e) {

if(null!=connection){
try {
connection.rollback(savepoint);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();

} finally {
try {
connection.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}

Spring 的事务实现与原理

手受了伤,一个手打字不便。稍等,别忘记了。记录到
gtd里面去

今日为2018年3月26日

spring 事务管理有以下一些有点<摘自官网文档16节 Transaction Management>:

  1. 接口一致性编程模型,适用于Java Transaction API(JTA),JDBC,Hibernate,Java Persistence API(JPA),Java Data Objects(JDO)
  2. 支持声明式事务管理
  3. 编程式事务api接口简单
  4. 与Spring的数据访问抽象的完美集成

第一节还有全局事务管理和本地事务管理。还看到了EJB CMT(Container Managed Transaction).Spring 事务抽象接口为PlatformTransactionManager接口,这个是用到了策略模式,可以看到传递transactionStatus为参数。
2018-03-26-20-18-37

很多英文没看懂,明天继续啃。

数据库查询优化

发表于 2018-03-12 | 分类于 数据库 | 阅读次数

数据库查询优化

通常笛卡尔积的优化很明显,可以用字查询等来做。

查询的优化其实实际上很大部分都是来自索引的优化。我是这么认为的。我们很多时候都会查执行计划的方式来优化语句。而这里面我们看得最多的就是表是否使用了索引。其实如果数量量少的情况下,我们用全表扫描可能会比用索引扫描要好。但是一般情况下还是优化成索引查询比较好。使用的方法就是查执行计划。explain,explain extended。

另外还有一个特别有意思的语句优化: where name like 'Abc%'优化成:where name>='Abc' and name < 'Abcd'

本文来自《数据库查询优化器的艺术》-李海翔的读书摘抄。

今天先给自己挖个坑。等之后我再来填。7天之内必填完,今日是3月12日。3月19日填完此坑。

来填坑,今天是3月16日。

性能优化简介

谈优化,我们先的知道怎么去测量。我们优化的性能其实实际上指的就是响应时间。主要测量也是看时间花在哪里。响应时间一般指的是执行时间和等待时间。在进行优化之前我们必须先明白什么样的优化才值得我们去优化:

  1. 值得优化的查询,如果为了优化1%的查询去浪费20%的人力,那肯定是不合算的;
  2. 异常情况要优化,有异常当然要优化了;
  3. 丢失时间,执行一些语句,发现花费了莫名奇妙的很长一段时间,而且这段时间也没有任何日志记录;这也有点像异常情况,对待异常一定要明白为什么;
  4. 不要相信平均值,毕竟有时候峰值几十秒,然后其它时候几毫秒,这种均值肯定是不能信的

既然要测量,那当然要有可以测量的点了。比如说从慢查询日志,show status,show profile。下面先介绍下几个常用的测量点:

慢查询日志

慢查询日志可以通过设置:long_query_time=0来开启。慢查询日志是开销最低,精度最高的测量查询时间的工具。慢查询最大的担心就是消耗大量的磁盘空间,所以建议在只开启某一段时间进行收集即可。推荐一款工具:pt-query-digest。

show profile

开始profile的命令为:set profiling=1,开启之后,在服务器上执行的所有语句,都会测量其耗费的时间和其它一些查询执行状态变更相关的数据。当一条查询提交给服务器时候,此工具会记录信息到临时表,然后我们可以使用show profiles;来查询结果。当然你可以使用我下面的这个sql来查询:

1
2
3
4
5
6
7
8
9
10
11
12

set @query_id=1;
select state,sum(duration) as Total_R,
ROUND(
100*SUM(DURATION) /(Select sum(duration) from information_schema.profiling where query_id=@query_id),2) as Pct_R,
count(*) as Calls,
SUM(duration) /count(*) as 'R/CALL'
From information_schema.profiling
Where query_id=@query_id
Group by state
order by Total_R desc;

show status

该命令返回一些计数器。既有服务器级别的全局计数器,也有基于某个连接的会话级别的计数器。

另外还有一些命令:show processlist;show global status;这些命令可能需要在实际使用中再查了。不是我想说的重点。

优化过程

谈到优化,其实优化的点有很多,从数据库表开始设计,字段类型,字段索引设置都有很多的可优化之处,我们非专业DBA,但是可以多去尝试理解,至少靠自己,是最靠谱的。

数据类型优化

选择优化的数据类型

比如使用可以正确存储数据的最小数据类型。他们更快,因为更小的数据类型占用更少的磁盘,内存和CPU缓存,并且处理时需要的CPU周期也更少。

使用更简单的数据类型

整型比字符操作代价更低,使用内建类型代替字符串存储日期和时间,使用整型存储IP;

避免使用NULL

为null的列会使用更多的存储空间;null的列为索引,需要一个额外的存储空间;

另外还有些优化是选择具体数据类型,设计表时候,列不要太多,太多的列需要考虑分表;少关联多个表,多表查询,MySQL最多关联表61个。

索引优化

很多时候谈优化,最终都无法避免的就是说到索引优化。索引是存储引擎用来快速找到记录的一种数据结构。在MySQL中也成为key。MySQL中索引是存储在存储引擎中的,不是在服务器中。一般我们说的索引都是B-Tree索引,也就是B-Tree数据结构。B-Tree对索引的列是顺序组织存储的(这里可以看到数据结构的威力了!),索引对值进行排序是按照create table时列的顺序来的。B-Tree 索引适用于全键值,键值范围,键前缀值。

索引的种类有很多,除了上面说的B-tree索引,还有哈希索引,空间数据索引,全文索引。

索引的优点:快速定位到表的位置,减少服务器需要扫描的数量,帮助服务器避免排序和临时表,将随机I/O变成顺序I/O。不过别太迷信索引,在表数量少的情况下,全表扫描更高效。

索引查询:

  1. 全值匹配;
  2. 匹配最左前缀,多列索引匹配最左边的索引;
  3. 匹配列前缀,匹配某一列的值的开头部分;
  4. 匹配范围值;
  5. 精确匹配某一列并范围匹配其它列;
  6. 只访问索引的查询

限制:

  1. 如果不按照索引最左列开始查找,则无法使用索引;
  2. 不能跳过索引列:比如有三个索引列name,address,age;不能跳过address来查询age的索引。
  3. 如果某列为范围查询,其右侧列都无法使用索引查询;如有索引a,b,c;如果在查询中使用了 a between 1 and 15 and b =12,由于a使用了范围查询,a之后的索引b不会生效。

索引优化实例

链接地址:http://www.chenzhijun.top/2018/03/23/mysql-optimization-index/

java性能优化

发表于 2018-03-12 | 分类于 java | 阅读次数

Java性能优化

今日在图书馆看java编程思想,看了一下午,就起身到处走走。没想到走到了《java性能优化》这本书这里,本来不想拿起来的,后来想着为啥不去看看呢。就拿起来读了一些感兴趣的章节。
前面一章主要讲了一些优化的大纲,已经四个测试。我记得有一个微基准测试。实在无奈,回到家,具体的是那几个测试也忘记了。当时额外看得只有几章,都是挑的感兴趣的读的。有一章讲的是jdbc,又想起之前别人为我的一个问题:spring的事务是怎么实现的?我当时有点懵逼,其实仔细一想,java中链接数据库不就是通过jdbc来操作的么?那我回答jdbc怎么控制事务的是不是就可以了?可那会脑袋一片空,神之尴尬。
java性能优化里面也提到了jpa,Java persistence api。谈到对它进行优化的时候,讲了一些点,预处理语句和语句池。比如使用PreparedStatement而不是直接使用Statement。对一些处理也可以进行批处理。jdbc的事务是基于Connection的。事务的隔离模式有,开销从大到小:

1
2
3
4
Transaction_Serializable
Transaction_Repeatable_Read
Transaction_Read_Commited
Transaction_read_uncommited

其实这些就是数据的事务隔离级别,当然java里面还有一个NONE。其实这些事务控制还是根据各数据库提供商来控制的。mysql就提供了上面的四种事务隔离级别。事务也会造成一些问题比如:脏读,幻读,不可重复读。

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted) 是 是 是
不可重复读(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否

JPA的性能直接受底层jdbc驱动程序的影响,可以尝试减少写入的字段,读取更少的数据或一次读取更多的数据(延时加载)(什么是延时加载?有什么好处?怎么实现?)

事务分为两种:声明式事务管理,编程式事务管理。(spring的事务怎么实现?)

JPA 缓存

缓存分为全局缓存和实体管理器缓存,实体管理器中的缓存通常称为一级缓存L1,全局缓存又称为二级缓存。(spring data jpa中是否也是这样实现?)

实体管理器实例都有自己的缓存,它会在本地缓存事务中取得数据,在本地缓存事务中写入的数据。只有在事务提交时,这些缓存的数据才会发送到数据库。

JPA缓存只在通过逐渐去访问实体的时候才有效。实体管理器通过主键查找数据时,会先从L2缓存中找。

任何时候用流都记得用缓存流。

重温Java编程思想的一些感悟-20180310

发表于 2018-03-10 | 分类于 Java | 阅读次数

重温Java编程思想的一些感悟-20180310

人的懒惰,是思想上的懒惰

前言

今天说个有趣的事情,起床的时间是9点。周末9点起床其实也不算过分,但是吧。拖着拖着在床上又赖到了10点。然后找不到理由再拖下去了。321起床,洗脸刷牙。然后脑袋在思考了:现在要是去大学城吧,10点了。这过去就得半小时吧,然后吃午饭吧,花半小时吧,然后去图书馆路上走个20分钟吧,嗯,好像差不多就12点了。这样下去吧,晚上6点又要吃饭。等会中午说不定还要睡觉。那还去图书馆干嘛。在家里也一样的啊,反正一张大桌子,一个人,很安静。那我先去吃个肠粉当早午饭,吃完回来看书。嗯,出门记得把垃圾扔出去。嗯是的。故事就是这样开始的。当我拿着垃圾扔到楼下垃圾桶。看着太阳这么明媚,天气这么暖和,不知咋地,我竟然生出一种感觉,回家拿书包,去图书馆,而且一分钟也没有思考其它的。直接就又返回来。拿着书包,就出去了。吃完肠粉就去图书馆了。
最后得出一个结论:人啊,要想战胜懒惰,先的开始行动。

今日看了《Java编程思想》的多态,接口,内部类。总共花费蕃茄钟5.5个,当然这只是有效的蕃茄中,天知道我分心走神了几次。。。

多态

多态,继承,抽象统称为《面向对象三大核心系统》。多态传统上的用法有点像:基类的方法调用,子类的方法实现。它的作用就是消除类型之间的耦合关系。
了解多态就得先了解下绑定,绑定分为前期绑定和后期绑定。

前期绑定:默认的绑定方式,方法直接调用,在编译期就能知道。

后期绑定:根据运行时的对象的类型进行绑定。又称为动态绑定或者运行时绑定。

Java中除了static方法和final方法之外其它都是后期绑定。(private方法是final方法)。只有普通方法的调用是多态的,静态方法不能使用多态的。构造器实际上是static方法,不过是隐式的。酒席那个private的方法是final一样,也是隐式的。

动态绑定只有在运行时才知道,因为无法知道它是属于方法所在的那个类,还是属于那个类的子类。

编写构造器的一条准则:尽可能简单的方法是对象进入正常状态,如果可以的话,避免去调用其它方法。

使用继承还是使用组合?推荐使用组合。

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
class Actor{
public void act(){}
}

class HappyActor extends Actor{
public void act(){
System.out.print("Happy Actor");
}
}

class SadActor extends Actor{
public void act(){
System.out.print("Sad Actor");
}
}

class Stage{
private Actor actor = new HappyActor();

public void change(){
actor = new SadActor();
}

public void performPlay(){
actor.act();
}
}

public class Demo{
public static void main(String[] args){
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}

代码用到了状态模式,使用组合还是继承有一条通用准则:用继承表达行为间的差异,并用字段表达状态上的变化。代码中,通过继承获得了两个不同的类,用于表达act()方法的差异。stage通过使用组合的方式通过change()方法试自己的状态发生改变。状态的改变也产生了行为的改变。

一个子类向上转型成父类,总是安全的。但是父类向子类转型就不一定了,毕竟子类的实现可能有很多,直接强转可能会出错(ClassCastException)。

阅读全文 »

初级排序算法

发表于 2018-03-09 | 分类于 算法 | 阅读次数

初级排序算法

约定

首先我们约定几个工具方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
比较a是否小于b,如果a<b返回true
*/
public static boolean less(int a,int b){
if(a<b){
return true;
}
return false;
}

/**
交换数组中i,j的值的位置
*/
public static void exchange(int[] arr,int i,int j){
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}

选择排序

选择排序的思想是在一堆无序数字中,首先找出最小的数字,然后与数组第一个元素交换,然后再在剩下的元素中找最小的数字,与第二个交换。这样以此类推,直到整个排序完成。这个就是选择排序。选择排序的思想就是一直选择最小的那个值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
选择排序,在传入前请判断arr是否为null
*/
public static void selectSort(int[] arr){
int len = arr.length;
for(int i=0;i<len;i++){
int min = i;
for(int j=i+1;j<len;j++){
if(less(arr[j],arr[i])){
min=j;
}
}
exchange(arr,i,min);
}
}

选择排序的特点,我们可以看到,选择排序每次都是从开始位置,一直到最后的位置,而且每次都需要遍历。所以输入的值关系并没有影响它,比如你输入“1,2,3,4”和“4,1,3,2”所需要的时间是相等的,因为它每次都会起始位置开始比较。数据移动是固定的,跟待比较的值呈线性关系。输入N个值,移动N次。另外有一点,如果一次遍历下来最小值是它本身,那么它还是会和自己做一次交换。
选择排序的时间复杂度怎么计算了?我们可以看到比较次数为:(n-1)+(n-2)+(n-3)+(n-4)+…+3+2+1,也就是任意i的位置都需要进行一次交换和(n-i-1)次比较。可以由公式得出为 $ (n-1)+(n-2)+(n-3)+(n-4)+…+3+2+1 ~ N^2/2 $

插入排序

如果你喜欢打牌,那么你肯定就知道插入排序就是怎么样了。比如你抓牌的时候会将抓到的牌按照大小顺序来排列,当你手上的牌是一个已好的顺序时候,下次抓出的牌你会将它插入到相应的位置。这就是插入排序的原理了。在实际中,我们用数组的方式来表示的话,当插入的已有的顺序中间的时间,那么其它位置的牌将需要全部移动一个位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void insertSort(int[] arr){
int len = arr.length;

//为什么从1开始?,第0个位置相当于抓的第一张牌,做基础对比的。
for(int i=1;i<len;i++){
// i之前的数据,可以看作是已经排好序了
for(int j=i;j>0;j--){
if(less(arr[j],arr[j-1])){
exchange(arr,i,j);
}
}

}
}

插入排序如果是以下集中情况将会很有效:

  1. 数组中每个元素的位置离他们最终元素的位置都不远;
  2. 数组中只有几个元素的位置不正确;
  3. 一个有序的大数组接一个小数组。
1…91011…18

180 日志
41 分类
70 标签
RSS
GitHub GitEE
推荐阅读
  • 陈志军的个人站
© 2017 - 2023 陈志军
由 Hexo 强力驱动
主题 - NexT.Muse