Posted in 清文

2016 6月 7月 随笔

首先在6月,写了很多好多好玩的文案

4.pic

3.pic

2.pic

1.pic

…… 等等


2016-06-28 白领

到家了,看到镜子前深夜里自己的她,卸了装后完全看清了自己


2016-06-30 睡衣

只有睡衣穿久了 才真正有你的味道


2016-07-01 野鸭

野鸭,不像家禽那个肥大,也不像家禽那么绚丽。批着防御大自然的颜色,敏捷的钻水捕鱼


2016-07-10 随笔

双胞胎的孩子 在双人娃娃椅上 一个睡着 一个动着


2016-07-20 随笔

往往就是这种不太大的、微不足道的变化,对我们的未来有着深远影响— 蝴蝶效应


2016-08-01 走路

不看路走的人容易踩狗屎

8月开始了。。。

祝自己别踩狗屎

Posted in Java, 技术

Code片段 : .properties属性文件操作工具类 & JSON工具类

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“贵专” — 泥瓦匠

一、java.util.Properties API & 案例

java.util.Properties 是一个属性集合。常见的api有如下:

  • load(InputStream inStream)  从输入流中读取属性
  • getProperty(String key)  根据key,获取属性值
  • getOrDefault(Object key, V defaultValue) 根据key对象,获取属性值需要强转

首先在resources目录下增加/main/resources/fast.properties:

fast.framework.name=fast
fast.framework.author=bysocket
fast.framework.age=1

然后直接上代码PropertyUtil.java:

/**
 * .properties属性文件操作工具类
 *
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);

    /** .properties属性文件名后缀 */
    public static final String PROPERTY_FILE_SUFFIX	= ".properties";

    /**
     * 根据属性文件名,获取属性
     *
     * @param propsFileName
     * @return
     */
    public static Properties getProperties(String propsFileName) {
        if (StringUtils.isEmpty(propsFileName))
            throw new IllegalArgumentException();

        Properties  properties  = new Properties();
        InputStream inputStream = null;

        try {

            try {
                /** 加入文件名后缀 */
                if (propsFileName.lastIndexOf(PROPERTY_FILE_SUFFIX) == -1) {
                    propsFileName += PROPERTY_FILE_SUFFIX;
                }

                inputStream = Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream(propsFileName);
                if (null != inputStream)
                    properties.load(inputStream);
            } finally {
                if ( null != inputStream) {
                    inputStream.close();
                }
            }

        } catch (IOException e) {
            LOGGER.error("加载属性文件出错!",e);
            throw new RuntimeException(e);
        }

        return properties;
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @return
     */
    public static String getString(Properties properties, String key){
        return properties.getProperty(key);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @return
     */
    public static String getStringOrDefault(Properties properties, String key, String defaultValue){
        return properties.getProperty(key,defaultValue);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @param <V>
     * @return
     */
    public static <V> V getOrDefault(Properties properties, String key, V defaultValue){
        return (V) properties.getOrDefault(key,defaultValue);
    }
}

UT如下:

/**
 * {@link PropertyUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtilTest {

    @Test
    public void testGetProperties() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = properties.getProperty("fast.framework.name");
        String authorName        = properties.getProperty("fast.framework.author");
        Object age               = properties.getOrDefault("fast.framework.age",10);
        Object defaultVal        = properties.getOrDefault("fast.framework.null",10);
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }

    @Test
    public void testGetString() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = PropertyUtil.getString(properties,"fast.framework.name");
        String authorName        = PropertyUtil.getString(properties,"fast.framework.author");
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
    }

    @Test
    public void testGetOrDefault() {
        Properties properties = PropertyUtil.getProperties("fast");
        Object age               = PropertyUtil.getOrDefault(properties,"fast.framework.age",10);
        Object defaultVal        = PropertyUtil.getOrDefault(properties,"fast.framework.null",10);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }
}

Run Console:

1
10
fast
bysocket
1
10
fast
bysocket

相关对应代码分享在 Github 主页


二、JACKSON 案例

首先,加个Maven 依赖:

                


                <!-- Jackson -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.13</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-jaxrs</artifactId>
			<version>1.9.13</version>
		</dependency>

 

然后直接上代码JSONUtil:

/**
 * JSON 工具类
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class);

    /**
     * 默认JSON类
     **/
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 将 Java 对象转换为 JSON 字符串
     *
     * @param object
     * @param <T>
     * @return
     */
    public static <T> String toJSONString(T object) {
        String jsonStr;
        try {
            jsonStr = mapper.writeValueAsString(object);
        } catch (Exception e) {
            LOGGER.error("Java Object Can't covert to JSON String!");
            throw new RuntimeException(e);
        }
        return jsonStr;
    }


    /**
     * 将 JSON 字符串转化为 Java 对象
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T toObject(String json, Class<T> clazz) {
        T object;
        try {
            object = mapper.readValue(json, clazz);
        } catch (Exception e) {
            LOGGER.error("JSON String Can't covert to Java Object!");
            throw new RuntimeException(e);
        }
        return object;
    }

}

UT如下:

/**
 * {@link JSONUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtilTest {

    @Test
    public void testToJSONString() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        Assert.assertEquals("{\"age\":1,\"name\":\"bysocket\",\"id\":33}", jsonStr);
    }

    @Test(expected = RuntimeException.class)
    public void testToJSONStringError() {
        JSONUtil.toJSONString(System.out);
    }

    @Test
    public void testToObject() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        JSONObject resultObject = JSONUtil.toObject(jsonStr, JSONObject.class);
        Assert.assertEquals(jsonObject.toString(), resultObject.toString());
    }

    @Test(expected = RuntimeException.class)
    public void testToObjectError() {
        JSONUtil.toObject("{int:1}", JSONObject.class);
    }
}

class JSONObject {
    int age;
    String name;
    Integer id;

    public JSONObject() {
    }

    public JSONObject(int age, String name, Integer id) {
        this.age = age;
        this.name = name;
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "JSONObject{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

Run Console(抛出了异常信息):

16/07/19 23:09:13 ERROR util.JSONUtil: JSON String Can't covert to Java Object!
16/07/19 23:09:13 ERROR util.JSONUtil: Java Object Can't covert to JSON String!

三、小结

相关对应代码分享在 Github 主页

请看到的Java小伙伴多交流多评论改进之。

参考 黄勇 smart

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in 技术, 算法

图解:二叉搜索树算法(BST)

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“岁月极美,在于它必然的流逝”
“春花 秋月 夏日 冬雪”
— 三毛

一、树 & 二叉树

是由节点和边构成,储存元素的集合。节点分根节点、父节点和子节点的概念。
如图:树深=4; 5是根节点;同样8与3的关系是父子节点关系。

1
二叉树binary tree,则加了“二叉”(binary),意思是在树中作区分。每个节点至多有两个子(child),left child & right child。二叉树在很多例子中使用,比如二叉树表示算术表达式。
如图:1/8是左节点;2/3是右节点;

2

二、二叉搜索树 BST

顾名思义,二叉树上又加了个搜索的限制。其要求:每个节点比其左子树元素大,比其右子树元素小。
如图:每个节点比它左子树的任意节点大,而且比它右子树的任意节点小

3

三、BST Java实现

直接上代码,对应代码分享在 Github 主页
BinarySearchTree.java

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索树(BST)实现
 *
 * Created by bysocket on 16/7/7.
 */
public class BinarySearchTree {
    /**
     * 根节点
     */
    public static TreeNode root;

    public BinarySearchTree() {
        this.root = null;
    }

    /**
     * 查找
     *      树深(N) O(lgN)
     *      1. 从root节点开始
     *      2. 比当前节点值小,则找其左节点
     *      3. 比当前节点值大,则找其右节点
     *      4. 与当前节点值相等,查找到返回TRUE
     *      5. 查找完毕未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

    /**
     * 插入
     *      1. 从root节点开始
     *      2. 如果root为空,root为插入值
     *      循环:
     *      3. 如果当前节点值大于插入值,找左节点
     *      4. 如果当前节点值小于插入值,找右节点
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增节点
        TreeNode newNode = new TreeNode(key);
        // 当前节点
        TreeNode current = root;
        // 上个节点
        TreeNode parent  = null;
        // 如果根节点为空
        if (current == null) {
            root = newNode;
            return newNode;
        }
        while (true) {
            parent = current;
            if (key < current.value) {
                current = current.left;
                if (current == null) {
                    parent.left = newNode;
                    return newNode;
                }
            } else {
                current = current.right;
                if (current == null) {
                    parent.right = newNode;
                    return newNode;
                }
            }
        }
    }

    /**
     * 删除节点
     *      1.找到删除节点
     *      2.如果删除节点左节点为空 , 右节点也为空;
     *      3.如果删除节点只有一个子节点 右节点 或者 左节点
     *      4.如果删除节点左右子节点都不为空
     * @param key
     * @return
     */
    public TreeNode delete (int key) {
        TreeNode parent  = root;
        TreeNode current = root;
        boolean isLeftChild = false;
        // 找到删除节点 及 是否在左子树
        while (current.value != key) {
            parent = current;
            if (current.value > key) {
                isLeftChild = true;
                current = current.left;
            } else {
                isLeftChild = false;
                current = current.right;
            }

            if (current == null) {
                return current;
            }
        }

        // 如果删除节点左节点为空 , 右节点也为空
        if (current.left == null && current.right == null) {
            if (current == root) {
                root = null;
            }
            // 在左子树
            if (isLeftChild == true) {
                parent.left = null;
            } else {
                parent.right = null;
            }
        }
        // 如果删除节点只有一个子节点 右节点 或者 左节点
        else if (current.right == null) {
            if (current == root) {
                root = current.left;
            } else if (isLeftChild) {
                parent.left = current.left;
            } else {
                parent.right = current.left;
            }

        }
        else if (current.left == null) {
            if (current == root) {
                root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right;
            } else {
                parent.right = current.right;
            }
        }
        // 如果删除节点左右子节点都不为空
        else if (current.left != null && current.right != null) {
            // 找到删除节点的后继者
            TreeNode successor = getDeleteSuccessor(current);
            if (current == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.left = successor;
            } else {
                parent.right = successor;
            }
            successor.left = current.left;
        }
        return current;
    }

    /**
     * 获取删除节点的后继者
     *      删除节点的后继者是在其右节点树种最小的节点
     * @param deleteNode
     * @return
     */
    public TreeNode getDeleteSuccessor(TreeNode deleteNode) {
        // 后继者
        TreeNode successor = null;
        TreeNode successorParent = null;
        TreeNode current = deleteNode.right;

        while (current != null) {
            successorParent = successor;
            successor = current;
            current = current.left;
        }

        // 检查后继者(不可能有左节点树)是否有右节点树
        // 如果它有右节点树,则替换后继者位置,加到后继者父亲节点的左节点.
        if (successor != deleteNode.right) {
            successorParent.left = successor.right;
            successor.right = deleteNode.right;
        }

        return successor;
    }

    public void toString(TreeNode root) {
        if (root != null) {
            toString(root.left);
            System.out.print("value = " + root.value + " -> ");
            toString(root.right);
        }
    }
}

/**
 * 节点
 */
class TreeNode {

    /**
     * 节点值
     */
    int value;

    /**
     * 左节点
     */
    TreeNode left;

    /**
     * 右节点
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

1. 节点数据结构
首先定义了节点的数据接口,节点分左节点和右节点及本身节点值。如图

4

代码如下:

/**
 * 节点
 */
class TreeNode {

    /**
     * 节点值
     */
    int value;

    /**
     * 左节点
     */
    TreeNode left;

    /**
     * 右节点
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

 

2. 插入
插入,和删除一样会引起二叉搜索树的动态变化。插入相对删处理逻辑相对简单些。如图插入的逻辑:

5
a. 从root节点开始
b.如果root为空,root为插入值
c.循环:
d.如果当前节点值大于插入值,找左节点
e.如果当前节点值小于插入值,找右节点
代码对应:

    /**
     * 插入
     *      1. 从root节点开始
     *      2. 如果root为空,root为插入值
     *      循环:
     *      3. 如果当前节点值大于插入值,找左节点
     *      4. 如果当前节点值小于插入值,找右节点
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增节点
        TreeNode newNode = new TreeNode(key);
        // 当前节点
        TreeNode current = root;
        // 上个节点
        TreeNode parent  = null;
        // 如果根节点为空
        if (current == null) {
            root = newNode;
            return newNode;
        }
        while (true) {
            parent = current;
            if (key < current.value) {
                current = current.left;
                if (current == null) {
                    parent.left = newNode;
                    return newNode;
                }
            } else {
                current = current.right;
                if (current == null) {
                    parent.right = newNode;
                    return newNode;
                }
            }
        }
    }

 

3.查找

其算法复杂度 : O(lgN),树深(N)。如图查找逻辑:

6
a.从root节点开始
b.比当前节点值小,则找其左节点
c.比当前节点值大,则找其右节点
d.与当前节点值相等,查找到返回TRUE
e.查找完毕未找到
代码对应:

    /**
     * 查找
     *      树深(N) O(lgN)
     *      1. 从root节点开始
     *      2. 比当前节点值小,则找其左节点
     *      3. 比当前节点值大,则找其右节点
     *      4. 与当前节点值相等,查找到返回TRUE
     *      5. 查找完毕未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

4. 删除
首先找到删除节点,其寻找方法:删除节点的后继者是在其右节点树种最小的节点。如图删除对应逻辑:7

结果为:

8
a.找到删除节点
b.如果删除节点左节点为空 , 右节点也为空;
c.如果删除节点只有一个子节点 右节点 或者 左节点
d.如果删除节点左右子节点都不为空
代码对应见上面完整代码。

 

案例测试代码如下,BinarySearchTreeTest.java

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索树(BST)测试案例 {@link BinarySearchTree}
 *
 * Created by bysocket on 16/7/10.
 */
public class BinarySearchTreeTest {

    public static void main(String[] args) {
        BinarySearchTree b = new BinarySearchTree();
        b.insert(3);b.insert(8);b.insert(1);b.insert(4);b.insert(6);
        b.insert(2);b.insert(10);b.insert(9);b.insert(20);b.insert(25);

        // 打印二叉树
        b.toString(b.root);
        System.out.println();

        // 是否存在节点值10
        TreeNode node01 = b.search(10);
        System.out.println("是否存在节点值为10 => " + node01.value);
        // 是否存在节点值11
        TreeNode node02 = b.search(11);
        System.out.println("是否存在节点值为11 => " + node02);

        // 删除节点8
        TreeNode node03 = b.delete(8);
        System.out.println("删除节点8 => " + node03.value);
        b.toString(b.root);


    }
}

运行结果如下:

value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 8 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> 
是否存在节点值为10 => 10
是否存在节点值为11 => null
删除节点8 => 8
value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 9 -> value = 10 -> value = 20 -> value = 25 ->

四、小结

与偶尔吃一碗“老坛酸菜牛肉面”一样的味道,品味一个算法,比如BST,的时候,总是那种说不出的味道。

树,二叉树的概念

BST算法

相关代码分享在 Github 主页

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in Working Skills, 技术

ApacheBench压测 那点事儿

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“我总是希望自己不要过于草率地去批评一个人” -《傲慢与偏见》

写代码,到处是指标和数据的。在写完接口,为了保证代码在一定压力下没问题,我们需要对接口进行压测。比如写完一模块的RestAPI,就需要用Web压测工具进行压测。压测工具有很多,新潮的getling,还有Old版的ApacheBench压测。

一、 什么是ApacheBench(AB)?

ApacheBench (ab) is a single-threaded command line computerprogram for measuring the performance of HTTP web servers.

AB是一个测试HTTP WEB服务器性能的单线程命令程序。实战才是真理,但需要学会怎么使用AB命令。

 

二、 AB命令参数

通过简单的指令 ab -h ,就可以得到AB命令参数列表。如下:

ab -h

Usage: ab [options] [http[s]://]hostname[:port]/path

Options are:

    -n requests     Number of requests to perform

    -c concurrency  Number of multiple requests to make at a time

    -t timelimit    Seconds to max. to spend on benchmarking

                    This implies -n 50000

    -s timeout      Seconds to max. wait for each response

                    Default is 30 seconds

    -b windowsize   Size of TCP send/receive buffer, in bytes

    -B address      Address to bind to when making outgoing connections

    -p postfile     File containing data to POST. Remember also to set -T

    -u putfile      File containing data to PUT. Remember also to set -T

    -T content-type Content-type header to use for POST/PUT data, eg.

                    'application/x-www-form-urlencoded'

                    Default is 'text/plain'

    -v verbosity    How much troubleshooting info to print

    -w              Print out results in HTML tables

    -i              Use HEAD instead of GET

    -x attributes   String to insert as table attributes

    -y attributes   String to insert as tr attributes

    -z attributes   String to insert as td or th attributes

    -C attribute    Add cookie, eg. 'Apache=1234'. (repeatable)

    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'

                    Inserted after all normal header lines. (repeatable)

    -A attribute    Add Basic WWW Authentication, the attributes

                    are a colon separated username and password.

    -P attribute    Add Basic Proxy Authentication, the attributes

                    are a colon separated username and password.

    -X proxy:port   Proxyserver and port number to use

    -V              Print version number and exit

    -k              Use HTTP KeepAlive feature

    -d              Do not show percentiles served table.

    -S              Do not show confidence estimators and warnings.

    -q              Do not show progress when doing more than 150 requests

    -l              Accept variable document length (use this for dynamic pages)

    -g filename     Output collected data to gnuplot format file.

    -e filename     Output CSV file with percentages served

    -r              Don't exit on socket receive errors.

    -m method       Method name

    -h              Display usage information (this message)

    -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)

    -f protocol     Specify SSL/TLS protocol

                    (SSL3, TLS1, TLS1.1, TLS1.2 or ALL)

常用命令参数如下:

-n 测试的请求数量

-c 同时请求数量(并发数)

-t 测试时间

以上一般搭配 -n & -c 或者 -n & -t

-k HTTP保持Keep-Alive方式(长连接)

-p POST方式下发送的数据

-T POST或PUT方式下的Content-type

 

三、简单例子

从常用命令参数中,我们用案例说话:

a. 同时10个请求,连续点击1000次(每次Request完毕自动销毁,重新请求)

ab -n 1000 -c 10 http://www.test.com/index.html

b. 同时10个请求,连续点击1000次,并保持Keep-Alive方式(保持长连接的方式)

ab -n 1000 -c 10 -k http://www.test.com/index.html

c. 同时10个请求,请求测试时间为20s

ab -c 10 -t 20 http://www.test.com/index.html

d. 将请求性能详情输出到CSV档文件

ab -e output.csv -n 1000 -c 10 http://www.test.com/index.html

out.csv: {请求序号 – 请求耗时ms}

e. 包含Post的数据的文件,例如POST发送JSON格式数据

ab -p p.json -T application/json -n 1000 -c 10 http://www.test.com/index.html

p.json:

{"mallId" : 1, "itemIds" : [9102,9101]}

四、返回参数详解

比如 我们拿简单案例第一个测试,得到了返回报告如下:

Server Software:        nginx/1.9.15

Server Hostname:        www.test.com

Server Port:            80

Document Path:          /index.html

Document Length:        161 bytes

Concurrency Level:      10

Time taken for tests:   5.760 seconds

Complete requests:      100

Failed requests:        0

Non-2xx responses:      100

Total transferred:      35200 bytes

HTML transferred:       16100 bytes

Requests per second:    17.36 [#/sec] (mean)

Time per request:       575.955 [ms] (mean)

Time per request:       57.595 [ms] (mean, across all concurrent requests)

Transfer rate:          5.97 [Kbytes/sec] received

Connection Times (ms)

              min  mean[+/-sd] median   max

Connect:      179  197  28.3    187     320

Processing:   179  240 236.8    186    2074

Waiting:      179  231 229.2    186    2073

Total:        360  437 238.4    374    2263

Percentage of the requests served within a certain time (ms)

  50%    374

  66%    381

  75%    418

  80%    427

  90%    513

  95%    567

  98%   1685

  99%   2263

 100%   2263 (longest request)

指标太多,我们要去看关键指标,比如

—-关键指标—-

Failed requests  失败的请求数。如果太多证明Web服务器稳定度不够

Request per second 每秒请求的次数。代表web服务器的承载量,即吞吐量(不考虑带宽限制)

Time per request:855.897 [ms] (mean)用户平均请求等待时间。可以通过1000ms/Time per request算出每秒的事务数 QPS ,mean是平均值

Time per request:85.590 [ms] (mean, across all concurrent requests) 服务器平均处理每一个并发请求的时间。可以1000ms/Time per request算出平均事务响应时间,mean是平均值

详细解释如下:

Server Software:  服务器名称(从http响应数据的头信息获取)

Server Hostname:  服务器Host名称(从http请求数据的头信息获取)

Server Port:      服务器端口

Document Path:    请求的根绝对路径, 默认为 /

Document Length:  响应数据的正文大小, 单位为 Byte

Concurrency Level: 并发用户数(命令中表现为-c后面跟着的参数)

Time taken for tests: 所有请求测试的总耗时(如果是 -t 参数,则为该数值;如果是 -n 请求,一般来说 值越大,耗时越多)

Complete requests: 总请求数(-n 参数值)

Failed requests:   失败的请求数(2XX失败的请求除外)

Write errors:      写入失败的数量

Total transferred: 数据传输量

HTML transferred:  所有请求的响应数据长度总和

Requests per second: 每秒请求的次数。代表web服务器的承载量,即吞吐量(不考虑带宽限制)

Time per request:  用户平均请求等待时间

Time per request:  服务器平均处理每一个并发请求的时间

Transfer rate:     请求在单位时间内从服务器获取的数据长度

五、机器Top命令 & 压测建议

压测中,我们要关注服务器的性能,一般28原则。即

1. 压测要将某个指标压到 80%左右。比如Linux服务器 top命令,实时查看服务器性能:

top - 15:22:08 up 41 days, 23:39,  1 user,  load average: 0.21, 0.05, 0.02

Tasks: 392 total,   1 running, 391 sleeping,   0 stopped,   0 zombie

Cpu0  :  1.0%us,  0.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  P COMMAND

24425 root      20   0 13728 1580  944 R  1.0  0.1   0:00.98 0 top

关键指标达到 80% ,得出报告最准确。

load average (0.21, 0.05, 0.02)- 系统负载,即 1分钟、5分钟、15分前的任务队列的平均长度,所以可以看第一个是否达到80%

Cpu0 CPU占用百分比

wa 等待输入输出的CPU时间百分比,即网络IO操作

2. 循序渐进:同时请求数阶梯式加进去。达到上面某个指标的域

3. 服务器压测服务器,不用多说

4. 排除带宽限制、多台服务器测试


如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

Posted in Spring, 技术

spring4.0 整合 Quartz 实现任务调度

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“一个人如何对待他的时间,决定他可以成为什么样的人” — 随笔

一、前言

  项目需求:

二维码推送到一体机上,给学生签到扫描用。然后需要的是 上课前20分钟 ,幸好在帮带我的学长做 p2p 的时候,接触过。自然 quartz 是首选。所以我就配置了下,搞了个小样例给大家。

二、正文

spring4.0 整合 Quartz 实现任务调度。这是期末项目的最后一篇,剩下到暑假吧。

    Quartz 介绍

    Quartz is a full-featured, open source job scheduling service that can be integrated with, or used along side virtually any Java application – from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs;
    Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。
核心概念
     Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节

三、实战

第一步 :spring、quartz 相应的jar包,添加到项目中(需要的call me)

quartz-2.2.1.jar以及spring的一些必要包

第二步:web.xml中配置spring

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>wmuitp</display-name>
      
      <!--Spring WebApplicationContext上下文,称为父上下文(父容器)-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!--Srping
      <listener>  
        <listener-class>  
            org.springframework.web.context.request.RequestContextListener  
        </listener-class>  
    </listener> 
     -->
     
    <!--加载spring的配置文件 -->  
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    
    <!--Spring MVC 配置 DispatcherServlet-->
    <servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>  
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!--filter配置,解决编码问题 --> 
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!--OpenSessionInViewFilter配置,解决延迟加载时Session会关闭的问题 -->  
    <filter>
        <filter-name>openSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- session过期时间:  20-->
    <session-config>   
        <session-timeout>20</session-timeout>
    </session-config>
    
    <!-- 错误界面  -->
    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/WEB-INF/error/500.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/error/500.jsp</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/error/404.jsp</location>
    </error-page>
    <error-page>
        <error-code>400</error-code>
        <location>/WEB-INF/error/400.jsp</location>
    </error-page>
</web-app>

#有些你不用的,就不要写了。

第三:在spring配置文件中配置quartz任务调度

<!--Quartz-->
            
    <!-- 集成方式:JobDetailFactoryBean,并且任务类需要继承QuartzJobBean-->
    <!-- 定义jobDetail -->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <!-- durability 表示任务完成之后是否依然保留到数据库,默认false   -->
        <property name="durability" value="true" />  
        <!--     目标类  /wmuitp/src/test/SpringQuartz.java-->
        <property name="jobClass" value="test.SpringQuartzTest"></property>
        
           <!--  在这个例子中,jobDataAsMap没有用,此目标类中接受的参数 ,若参数为service,则可以在此进行参数配置,类似struts2 -->
           <!--
        <property name="jobDataAsMap">  
            <map>  
                <entry key="service"><value>simple is the beat</value></entry>  
            </map>  
        </property>
            -->
    </bean>
    
    <!-- 定义simpleTrigger触发器 -->
    <!--     
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
        <property name="jobDetail" ref="jobDetail"></property>
        <property name="repeatCount">
            <value>8</value>
        </property>
        <property name="repeatInterval">
            <value>1000</value>
        </property>
        <property name="startDelay">
            <value>4</value>
        </property>
    </bean> 
    -->
    
    <!-- 另一种触发器是CornTrigger -->
     <bean id="cornTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
       <property name="jobDetail" ref="jobDetail"/>
       <!-- 每个10秒触发 -->
       <property name="cronExpression" value="0/10 * * * * ?"/>
    </bean> 
    
    <!-- 定义核心调度器 -->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
      <property name="triggers">
        <ref bean="cornTrigger"/>
      </property>
    </bean>
  #目标类
    <property name="jobClass" value="test.SpringQuartzTest"></property>

第四步:编写目标类

 

package test;

import java.util.Date;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class SpringQuartzTest extends QuartzJobBean
{

    /*业务实现*/
    public void work() {
        System.out.println("执行调度任务:"+new Date());
    }

    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        this.work();
    }
}
#需要继承QuartzJobBean

测试运行结果(这个很重要 能服众)

四、总结

 spring quartz实战

  

    

    http://url.cn/RzETYu 加入我的群 路上走来一步一个脚印,希望大家和我一起。


 

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in Working Skills, 技术

数据库必会必知 之 SQL四种语言:DDL DML DCL TCL

作者:泥瓦匠
今天群里面讨论,DDL 还是 DML,我这种小白还是总结下他们的区别吧。

1. DDL – Data Definition Language

数据库定义语言:定义数据库的结构。

其主要命令有CREATE,ALTER,DROP等,下面用例子详解。该语言不需要commit,因此慎重。

CREATE – to create objects in the database   在数据库创建对象

例:CREATE DATABASE test; // 创建一个名为test的数据库

ALTER – alters the structure of the database   修改数据库结构

例:ALTER TABLE test ADD birthday date; // 修改test表,新增date类型的birthday列

DROP – delete objects from the database   从数据库中删除对象

例:DROP DATABASE test;// 删除test数据库

还有其他的:

TRUNCATE – 截断表内容(开发期,还是挺常用的)

COMMENT – 为数据字典添加备注

 

2. DML – Data Manipulation Language

数据库操作语言:SQL中处理数据库中的数据

其主要命令有INSERT,UPDATE,DELETE等,这些例子大家常用就不一一介绍了。该语言需要commit。还有常用的 LOCK TABLE ,记得写过锁的博客 – 传送门

还有其他不熟悉的:

CALL – 调用一个PL/SQL或Java子程序
EXPLAIN PLAN – 解析分析数据访问路径

 

3. DCL – Data Control Language

数据库控制语言:授权,角色控制等

GRANT – 为用户赋予访问权限

REVOKE – 撤回授权权限

 

4. TCL – Transaction Control Language

事务控制语言

COMMIT – 保存已完成的工作

SAVEPOINT – 在事务中设置保存点,可以回滚到此处

ROLLBACK – 回滚

SET TRANSACTION – 改变事务选项

例子:Java中JDBC封装了对事务的支持。比如我们首先新建一个表:test

test.sql

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `city`
-- ----------------------------
DROP TABLE IF EXISTS `city`;
CREATE TABLE `city` (
  `id` int(11) NOT NULL DEFAULT '0' COMMENT '城市ID',
  `name` varchar(20) DEFAULT NULL COMMENT '名称',
  `state` varchar(20) DEFAULT NULL COMMENT '状态',
  `country` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

SET FOREIGN_KEY_CHECKS = 1;

JDBC事务回滚第一个例子 -JDBC数据库事务回滚:

/**
 * 描述:JDBC数据库事务回滚
 *
 * Created by bysocket on 16/6/6.
 */
public class TransactionRollBack extends BaseJDBC {

    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        try {
            // 加载数据库驱动
            Class.forName(DRIVER);
            // 数据库连接
            conn = DriverManager.getConnection(URL,USER,PWD);

            // 关闭自动提交的事务机制
            conn.setAutoCommit(false);
            // 设置事务隔离级别 SERIALIZABLE
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

            Statement stmt = conn.createStatement();
            int rows = stmt.executeUpdate("INSERT INTO city VALUES (3,'china',1,'cc')");
            rows = stmt.executeUpdate("UPDATE city set country = 'TAIWAN' WHERE id = 4");

            // 提交事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            // 回滚事务
            if (conn != null) {
                conn.rollback();
            }
        } finally {
            /** 关闭数据库连接 */
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

第 19 行:设置了事务隔离级别为 SERIALIZABLE 底层调用的是TCL语言的SET TRANSACTION

第 22 行:执行通过,插入数据

第 23 行:执行不通过,没有主键为4的记录,直接抛出异常

第 31 行:事务回滚,封装的就是 TCL 语句的ROLLBACK

休息下,一个例子不够,再来一个。代码都在github主页上。https://github.com/JeffLi1993/jee-component-learning

JDBC事务回滚第二个例子-JDBC数据库事务回滚,回滚到特定的保存点:

/**
 * 描述:JDBC数据库事务回滚,回滚到特定的保存点
 *
 * Created by bysocket on 16/6/6.
 */
public class TransactionRollBack2 extends BaseJDBC {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        Savepoint svpt = null;
        try {
            // 加载数据库驱动
            Class.forName(DRIVER);
            // 数据库连接
            conn = DriverManager.getConnection(URL,USER,PWD);

            // 关闭自动提交的事务机制
            conn.setAutoCommit(false);
            // 设置事务隔离级别 SERIALIZABLE
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

            Statement stmt = conn.createStatement();
            int rows = stmt.executeUpdate("INSERT INTO city VALUES (3,'china',1,'cc')");
            // 设置事务保存点
            svpt = conn.setSavepoint();
            rows = stmt.executeUpdate("UPDATE city set country = 'TAIWAN' WHERE id = 4");

            // 提交事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            // 回滚事务
            if (conn != null) {
                conn.rollback(svpt);
            }
        } finally {
            /** 关闭数据库连接 */
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

和第一个例子重复的就不提了。

第 9 行:声明了一个保存点

第 24 行:设置了保存点

第 33 行:回滚事务到该保存点

上面的代码涉及到的是 TCL语言中的 SAVEPOINT

 

最后来张图总结:(SELECT属于DQL哈。)

QQ20160612-0


如以上文章或链接对你有帮助的话,别忘了分享到朋友圈,让更多的人阅读这篇文章。

 

Posted in 清文

《大冰的小屋》 – 厦门

那是喧嚣的厦门曾厝垵,巷子最里面的《大冰的小屋》。上图:
1.pic
胖胖的那位简称胖子,内蒙人,流浪歌手。貌似在过天桥的时候已经注意到他在唱,转了下,他移到了大冰的小屋继续唱。原来是一个女生,胆大的歌手。唱完了听胖子唱回味无穷,和两个哥们,喝着风花雪月,听了若干小时。听出了

段子

胖子30多了,冷不丁和旁边孩子都6岁的大女生调侃。然后各种能逗乐,耐逗乐的段子。这是我脑子里有了一首歌赵雷《三十岁的女人》:

“三十岁了

光芒和激情已被岁月打磨

是不是一个人的生活

比两个人更快活”
眼前的胖子,他年龄却不是真正30了,我却觉得他依旧没有被生活迫着走,就像我看到那些和他调侃的大女人也有心里丝丝向外走的心。

行走

唱完一首,说了下故事。唱完了各个中国大陆,脚踏实地。唱来的钱献给了希望小学,献给了他感觉快乐的事。是快乐的事。我们多少被生活牵着走,财务自由才能做自己喜欢的事。有时候确实自己的欲望越来越大了罢了。所以今年我打算多走走,有空就去 哦也~

开心

胖子又开心的各种乐器show,各种搞怪show。开心唱一天,说喉咙哑了,还继续。为嘛为嘛,开心嘛。

体验

毕业去厦门,这胖子深有印象。听哥们看过大冰《阿弥陀佛 么么哒》的书说,不错。我赶紧入手了。体验着当下,体验这生活给我带来的what ever。体验珍惜每一天。比如爱,比如什么的。我的目标:不工作。但我如果工作,我会认真对待体验工作。
  
Posted in 技术

记博客挂了的事

wordpress 突然 告诉我:

建立数据库连接时出错

聪明的我,肯定知道。ssh 博客地址 — service mysql restart。没有重启解决不了的问题的。

可是,还是不行。。 最后定位谨记之

问题:wp_options 表挂了

修复之:repair table wp_options

Posted in Java, 技术

图解 & 深入浅出JavaWeb:事务必会必知

事务,大家所熟悉的事务(Transaction),基本上会就往Spring事务靠。其实Spring事务管理基于底层数据库本身的事务处理机制。数据库事务的基础,是掌握Spring事务管理的基础。这篇总结下数据库事务。

一、数据库事务

它的思想:we are 伐木累。就是多个SQL语句(一个团队),要么所有执行success,不然就fail。

它最终的目标:数据不会被破坏。即事务操作成功,数据的结果和业务期待的结果是一致的。这也就是ACID中的一致性(Consistency)。那什么是ACID呢?

 

二、ACID

上面是思想,牛人马上根据思想建模,DBMS中数据库事务满足4各特性,即原子性、一致性、隔离性和持久性。下面一一生动解释:

a)原子性

原子是物质的最小单元,即不可再分。

例如,以MySQL为例,每一个简单的 SQL 语句即包含在一个事务中,具有原子性。这时候有人问了,那多个SQL呢?

BEGIN TRANSACTION;

INSERT INTO `test`.`city` (`state`, `country`, `name`)

VALUES

('1', 'China', 'CHINA','错误语句多了个VALUE');

INSERT INTO `test`.`city` (`state`, `country`, `name`)

VALUES

('1', 'China', 'CHINA');

COMMIT;

 

结果:执行不通过。行3-5:为一个错误SQL。行6-8:是一个正确的SQL。它们各自被包裹在各自的隐式事务中,即Read Uncommited。T-all包裹了上面具有原子性的T-1和T-2,实现了更大的原子,如下图。

2

b)一致性

终极目标:数据不会被破坏。(这不是废话?确实有点)具体说,事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。举个栗子:两句UPDATE语句,从A账户转账到B账户,不管成功失败,A和B账户的总额是不变的。

c)隔离性

隔离:表示互不干扰。事务与事务之间无法干扰,即每个事务独立,不会交叉。这样可以让多个线程并发访问数据库。如图:

3

但是聪明的小伙伴知道,如果事务完全隔离,每次只允许一个事务能访问数据库,那其他都是阻塞。会非常慢。

但是聪明的小伙伴也知道,这样会造成数据的并发问题。(是的,在下面第三节讲)。

d)持久性

数据必须持久化到数据库(存储在磁盘)中。已提交的事务,即使在提交后数据库崩溃,重启数据库时也能够根据日志对未持久化的数据进行重执行操作。(同学会问,那没提交的事务呢?那就悲剧了(>﹏<))

小结:数据的一致性是最终目标,其他特性都是其要求或手段。

 

三、隔离性中的问题:脏读、不可重复读和幻读

对应上面的隔离性,事务并发访问的时候会出现:脏读、不可重复读和幻读。案例转自勇哥博客

脏读:A事务读取了B事务未提交的更改数据。一般数据库事务默认不允许该问题出现。

比如这里查询应该是1500,现在出现了脏读。

时间 事务 A(存款) 事务 B(取款)
T1 开始事务
T2 开始事务
T3 查询余额(1000 元)
T4 取出 1000 元(余额 0 元)
T5 查询余额(0 元)
T6 撤销事务(余额恢复为 1000 元)
T7 存入 500 元(余额 500 元)
T8 提交事务

不可重复读:A事务读取了B事务已提交的更改数据。

幻读:A事务读取了B事务提交的新增数据。

上面的案例脑补吧,主要还是看下面。

 

不可重复读和幻读区别:一个更改,一个新增数据。其实两个区别在于一个是新增(insert语句),处理幻读这个操作需要加表级别的锁,将整个表锁定,防止新增数据造成幻读。另一个则是更改(update delete),这时候避免这个情况只需要添加行级锁组织该行发生变化即可。

 

四、事务隔离级别

既要求高的隔离性(安全性),又要求高并发性。这种是不可能的任务。根据各种锁的操作机制出现了一个事务隔离级别。即相同情况下的输入,不同隔离级别结果不同。为啥了,当然是在并发性和安全性的抉择。如图:

6

按着图说的,根据程序的并发性和安全性的抉择。鱼和熊掌不可兼得也~ 但分布式的时候,可以吧安全性关键的单独分布式锁。

好了,案例说了很多下面代码实战。

 

ps: 休息下,泥瓦匠的代码都会这github上~ ,这段代码地址:https://github.com/JeffLi1993/jee-component-learning

 

五 、JDBC事务实战

下面利用MYSQL JDBC驱动连接MySQL,代码如下:

public class TransactionLevels extends BaseJDBC {
    public static void main(String[] args) {
        try {
            // 加载数据库驱动
            Class.forName(DRIVER);
            // 数据库连接
            Connection conn = DriverManager.getConnection(URL,USER,PWD);
            // 数据库元数据
            DatabaseMetaData metaData = conn.getMetaData();

            // 是否支持事务
            boolean isSupport = metaData.supportsTransactions();
            System.out.println(isSupport);
            // 是否支持的事务
            boolean isSupportLevel = metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE);
            System.out.println(isSupportLevel);
            // 获取默认事务
            int defaultIsolation = metaData.getDefaultTransactionIsolation();
            System.out.println(defaultIsolation);

            /** 关闭数据库连接 */
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

第 5、7行是连接数据库

第 9 行:获取数据库元数据,这是包含数据库连接信息

第 12 行:从元数据中,判断是否支持事务

第 15 行:从元数据中,判断是否支持事务级别 TRANSACTION_SERIALIZABLE

第 18 行:这里可以看出MySQL默认支持的事务级别是 READ_COMMITTED,默认会隔离脏读。

具体源码如下:

4

因此在安全性要求不高,支持高并发的情况下,选择MySQL默认事务等级。但在安全性极高,几乎不会出现高并发情况下,选择更高的事务等级。根据上小节的图一幕了然。

 

六、补充

关于事务,还有大家熟悉的Spring事务管理、具体数据库事务的实现,推荐一本书《MySQL技术内幕InnoDB存储引擎 》。

下一篇:ThreadLocal的工作机制,揭示Spring事务同步管理器的工作原理

如以上文章或链接对你有帮助的话,别忘了分享到朋友圈,让更多的人阅读这篇文章。

Posted in 清文

2016 4月 随笔

2016-04-11

今晚 我看到了没有雾霾最亮的夜晚

2016-04-16

屋顶上看明月 空气真好

还能听到几声乌鸦叫。

2016-04-18

2016-04-25

以前是人放狗看家,现在是狗牵着人散步

2016-04-29

我们想法不同

但走的路一样

走着、自由着

分散在城市的各个角落

05-02 我的生日于劳动节,我的目标却是不工作不劳动。哈哈 祝大家节日快乐


476be76fgw1f2h8nmf53wj20yj194alj