SQL JOIN语句中的ON和WHERE用法

SQL JOIN定义

  • INNER JOIN(内连接):取得两个表中存在连接匹配关系的记录。
  • LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录。
  • RIGHT JOIN(右连接):与 LEFT JOIN 相反,取得右表(table2)完全记录,即是左表(table1)并无匹配对应记录。

SQL JOIN 语法

SELECT ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditions1 WHERE conditions2

这里需要注意的是 ON 条件是在 JOIN 之前筛选数据,而 WHERE 是在 JOIN 之后筛选数据。

示例

CREATE TABLE `users` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `uid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;

users表

uid username
1 user1
2 user2
3 user3
4 user4
5 user5
6 user6

orders 表

id name uid
1 order1 1
2 order2 1
3 order3 1
4 order4 2
5 order5 3
6 order8 7
  • 不管 LEFT JOIN 的条件是什么,左侧始终都是左表的所有记录
-- 由于没有符合连接条件的记录,右侧记录全部为 NULL
SELECT * FROM orders t1 LEFT JOIN users t2 on 1 > 2;
  • 注意 ON 和 WHERE 的区别
-- ON 在 JOIN 之前筛选数据,符合条件的记录才进行连接
SELECT * FROM orders t1 LEFT JOIN users t2 on t1.uid = t2.uid AND t1.uid = 1;

-- WHERE 在连接完成之后筛选记录
SELECT * FROM orders t1 LEFT JOIN users t2 on t1.uid = t2.uid WHERE t1.uid = 1;

工具JsonPath

今天在看一个开源项目源码的时候,看到两个好东西,备份一下以后应该用得着

JsonPath

类似XPath,关于这个工具类的使用场景,举个例子,一个 json api 调用返回了一个复杂的对象,而我们只需要其中的一些信息,直接的做法是将字符串解析成 JSONObject 对象,然后根据对应的 key 一层一层剥开

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            // ...省略...
        ],
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}

假设我们只需要第一本书的价格,直接的做法

String json = IOUtils.toString(getClass().getResourceAsStream("json.txt"));
JSONObject j = JSONObject.fromObject(json);
JSONObject jStore = j.getJSONObject("store");
JSONArray jBooks = jStore.getJSONArray("book");
System.out.println(jBooks.getJSONObject(0).get("price"));

用 JsonPath 的方式,只需要一行代码(完整语法参考https://github.com/jayway/JsonPath)

double price = JsonPath.read(json, "$.store.book[0].price");
System.out.println(price);

List<String> books = JsonPath.read(json, "$.store.book[?(@.title=='Moby Dick')]");
System.out.println(books);

jsonschema2pojo

这个工具是将 json 类型的数据结构转换成 Java pojo,比如将上一个例子的 json 数据丢进去,生成出来的 pojo 代码如下(省略了getter/setter)

    -----------------------------------com.example.Bicycle.java-----------------------------------
    package com.example;

    public class Bicycle {
        public String color;
        public double price;
    }
    -----------------------------------com.example.Book.java-----------------------------------
    package com.example;

    public class Book {
        public String category;
        public String author;
        public String title;
        public double price;
        public String isbn;
    }
    -----------------------------------com.example.Store.java-----------------------------------
    package com.example;

    import java.util.ArrayList;
    import java.util.List;

    public class Store {
        public List<Book> book = new ArrayList<Book>();
        public Bicycle bicycle;
    }
    -----------------------------------com.example.Example.java-----------------------------------
    package com.example;

    public class Example {
        public Store store;
        public int expensive;
    }

参考资料

Java线上问题排查

场景

  • 前端机 apache/nginx
  • 后端 N 台 tomcat
  • 域名 http://your-site.com

用户反馈,your-site.com 访问特别慢,如何一步步排查定位问题?

大体思路

  • 确认问题
  • 定位问题
  • 修复问题

确认问题/重现问题

有时候并不是应用有问题,可能是用户自己的网络或者机器有问题,所以首先第一步要做的是确认问题是否存在

  • 确认用户反馈对应的页面/功能,参数等细节
  • 自己打开 your-site.com ,还原用户的请求
  • F12 查看网络面板,定位到慢的 URL

确认与前端机的网络连接状况

  • ping -i 0.2 -c 20 your-site.com 发送 20 个 ping 包之后停止, 查看 packet loss / rtt
  • curl 请求查看各个请求阶段的耗时细节
  • curl -w @curl-format.txt -s -o /dev/null http://your-site.com
# 输出 curl 执行细节
            time_namelookup:  %{time_namelookup}\n
               time_connect:  %{time_connect}\n
            time_appconnect:  %{time_appconnect}\n
           time_pretransfer:  %{time_pretransfer}\n
              time_redirect:  %{time_redirect}\n
         time_starttransfer:  %{time_starttransfer}\n
                            ----------\n
                 time_total:  %{time_total}\n

确认 nginx 前端机与后端机的连接情况

  • 查看前端机负载
  • 查看 nginx 日志,确认请求后端返回情况
  • 直连后端 tomcat 节点看问题是否存在

查看 tomcat 日志

  • 排查异常
  • 查看请求时间差
  • 查看对应的代码

查看服务器状态

  • top 主要查看 load / cpu / mem
  • free - m 查看空闲内存, 如果进程莫名被 kill,可以查看 dmesg 日志
  • iostat -d 1 关注是否有某块磁盘写入/读取异常
  • vmstat -n 1 关注 si/so(swap in/ swap out)
  • netstat -ant | awk '{print $6}' | sort | uniq -c | sort -rn 各 tcp 连接状态统计
  • netstat -ant | grep 'x.x.x.x:1024' | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -rn 统计某个服务端口的 ip 连接数量

查看 jvm 状态

  • 高 CPU 占用
    • jstack <pid> stack.txt 保存案发现场
      • 也可以使用 kill -3 <pid> 的方式生成 thread dump
    • top -H 查看具体占用 cpu 高的线程
    • 记录线程 id,printf "%x" id 转成 16 进制,通过 jstack 定位到异常的线程堆栈
    • jstack 30979 | grep -C 20 'printf "%x" 31000'
  • 高内存占用
    • jmap -dump:live,format=b,file=dump.bin <pid> 保存现场事后再用 MAT 分析
    • jmap -heap <pid> 查看 jvm 堆内存的使用情况
    • jmap -histo:live <pid> | more 查看存活状态的对象数量统计
    • jstat -gcutil -h50 <pid> 1000 具体字段意义可以查看 man jstat
    • jstat -gccause -h50 <pid> 1000 显示最后一次 gc 和当前一次 gc 的原因

数据库

  • show processlist 查看当前正在执行的查询列表
  • 查看慢查询日志
  • explain select ... 分析执行计划,针对执行计划优化语句

参考资料

scala语言学习-文件和正则表达式

Files文件操作

  • import scala.io.Source
  • Source.fromFile("myfile.txt", "UTF-8").getLines.toArray 获得文件所有的行
  • Source.fromFile("myfile.txt", "UTF-8").mkString 获得文件内容
  • Source.fromURL("http://horstmann.com", "UTF-8") 直接读取网址内容(这里不安全,没有地方设置timeout属性)
  • 写文本文件还是用java.io.PrintWriter:val out = new PrintWriter("numbers.txt")

文件的其他操作等用到了再去翻官方文档了

正则表达式

  • scala.util.matching.Regex
  • 字符串的r方法可以将字符串转换成正则表达式对象:val numPat = "[0-9]+".r
  • 可以用原生字符串的写法,避免混乱的转义字符val r = """\s+[0-9]\s+""".r
  • 正则表达式常用方法val r = """\s+""".r
    • r.split("hello world") // Array("hello", "world")
    • r.findAllIn("hello world") // Iterator
    • r.findFirstIn("hello world") // Optional[String]
    • numPattern.replaceFirstIn("99 bottles, 98 bottles", "XX")
    • numPattern.replaceAllIn("99 bottles, 98 bottles", "XX")
  • 分组匹配
// Groups are useful to get subexpressions of regular expressions. Add parentheses around the subexpressions that you want to extract, for example:
val numitemPattern = "([0-9]+) ([a-z]+)".r
// To match the groups, use the regular expression object as an “extractor” (see Chapter 14), like this:
val numitemPattern(num, item) = "99 bottles"
// Sets num to "99", item to "bottles"
// If you want to extract groups from multiple matches, use a for statement like this:
for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles"))
    process num and item

scala语言学习-Class

Class

  • 类的var属性自动获得gettersetter方法, val属性自动获得getter方法
  • 使用@BeanProperty val/var name来生成JavaBeangettersetter(var)方法
  • 主构造函数,这个构造函数的参数自动变成这个class的对应字段, class Person(val/var name: String, val/var age: Int){...}
  • 主构造函数的参数可以有默认值class Person(val name: String = "", val age: Int = 0)
  • 辅助构造函数?def this(name: String),需要调用主构造函数this(),或者另一个辅助构造函数
  • class不需要定义为public,一个Scala文件中可以有多个class定义,并且都是public
  • 只读属性
    • 如果构造之后不再修改,那么使用val name定义
    • 如果构造之后需要修改,那么使用private var name定义,另外还需要手动增加一个getter方法
  • 可以重新定义gettersetter
class Person {
    private var privateAge = 0 // Make private and rename,需要重新定义的时候,自定名就不能定为 age 了
    def age = privateAge // 重新定义getter
    def age_=(newValue: Int) { // 这里重新定义setter,注意这里的 _ 是和 age 一起的,并不是和 = 一起的
        if (newValue > privateAge) privateAge = newValue; // Can’t get younger
    }
}