对solr, lucence的理解

是什么

lucence 是一套信息检索工具包, 使用lucence 能够更加方便地去构建一个查询应用

solr是一个给予lucence的成熟的企业级的搜索引擎框架, solr对lucence做了封装, 程序员的工作会少很多

可以做什么

在项目中, 如果遇到需要在海量数据中做查询的场景, 比如千万级别+, 这时候使用sql就比慢了, 这时候就可以用上solr, 比如商城中的商品查询, 比如酒店行业, 酒店的可用情况以及价格查询.

例子

去哪儿首页中, 想要查询A地区, 时间区间 B ~ C 内可用的 酒店情况, 实际上数据量是是非常大的, 20万商家 * 10 个渠道 * 4个房型 * 30天, 这种情况数据本身是千万级别的, 使用sql肯定会有性能上的 压力, 可以使用solr建立适当的索引,每种查询情况对应一种索引, 直接命中, 拿空间换时间, 提高查询效率. 具体的索引设置得查看相关文档, 结合具体的查询业务分析了.

怎么做

主要是建立更新全文索引, 使用solr提供的api做查询操作
https://www.ibm.com/developerworks/cn/java/j-solr1/index.html

pracitice

原理

相关

https://www.ibm.com/developerworks/cn/java/j-solr-lucene/index.html

ext_idea_tomcat.md

在使用idea的时候, 如果删除了一个类, 再重启tomcat, 该class的编译后的文件可能还在target里面, 会被再次加载在tomcat里面, 造成一些隐性问题

可以在删除类后clean下, 或者手动删一下二进制的class

股票市场总结

开户

券商开户, 接下来是具体的交易所/板块开户

板块

深圳

主板 000

主板-中小企业板 002

二板-创业板 300

新三板 83, 43 ignore

上海

主板 600

纳斯达克

海外上市

其他

四板 800, 360, 880, 66 等

科创板&&战略新兴板

condition

主板: 1990开始, 连续3年盈利, 累计盈利超过3000W, 1400+企业, 主要是国有企业

主板-中小企业板: 2004开始, 准入同主板, 700+企业, 主要是民营企业, 是细分行业的龙头

二板-创业板: 2009开始, 持续经营三年即可, 484+企业, 是成长比快的企业

纳斯达克等海外: 1994开始, 100+企业

老三板: 从主板退市的公司, 养老院, 一般不用来炒股

新三板: 2006年开始, 持续经营两年即可, 5000+成长期企业, 一般不用来炒股

双创板: 2016年开始,初创期企业, 一般不用来炒股

四板: 区域性的股票交易市场, N++, 不考虑炒股

科创板&&战略新兴板: 是主板和二板的连接, 2014年开始筹备, 体系不完整, 不考虑炒股

总结: 可以考虑的是主板, 主板中小板, 创业板, 新三板需要再了解

场内场外

场外: 新三板, 双创板, 区域性股票市场
场内: 主板, 中小板, 创业板

A, B股

都是在境内注册, 只是上市地址不一样
B股, 是给非大陆人用的
N 纽约上市
S 新加坡上市
H 香港上市

红筹, 蓝筹

红筹, 境外注册, 境外上市, 具有大陆特色, 比如中信富泰

红筹股具有稳定的盈利记录, 能顶起分派比较优厚的古稀, 业绩优良的普通股票, 又称为绩优股, 稳定收益就买蓝筹股

境内

香港 商业银行的汇丰控股, 华资背景的长江实业, 中资背景的中信富泰

美国

谈一谈这行代码 String s = new string ("ddd")

谈一谈这行代码 String s = new string (“ddd”), 它创建了几个对象

从常量谈起

java 中由 final 修饰的变量,就是常量, 有局部变量, 静态变量, 实例变量三种

常量池

  • 编译时的常量池

java编译后会生成class字节码, class字节码在魔数和版本号之后就是常量池, 常量池之后主要就是访问描述符号, 类父类, 接口的信息各种表了

在常量池里面存储两类信息, 字面量和符号引用. 字面量就是字符串, 符号引用 就是java类中各部分, 包括属性, 方法, 类, 接口的字符串描述. 在第一次使用某变量和方法时, 会从符号引用拿到对应的名字直接地址, 然后从方法区拿到对应的信息.

  • 运行时的常量池

java程序在运行时会有一个运行时常量池, 运行时常量池是方法区的一部分, 里面会加载编译时的静态常量池,加载的时机应该就是类生命周期中的连接中的解析阶段
解析阶段官方文档说法是将符号引用转化为直接引用,我的理解就是为java字节码中涉及到的各个信息在方法区分地址,再把符号引用指向对应的地址
加载
连接
准备 验证 解析
初始化

  • String的常量池

Jvm 为String设计了一个运行时的常量池, 也在方法区中, 每次创建新的字符串时, 会先从方法区中找, 没有就会新建一个放入方法区中的常量池, 有就会直接将引用指向那里.

  • java 语言层面的常量池

大部分包装类创建的时候如果是自动包装, 会调用 valueOf方法, 包装类内部会有一个静态类, 里面维护了一个数组做cache, 这样当有新的包装类对应的值需要创建对象的时候, 会先从内部的cache开始找, 有的话直接返回对应的引用, 如果没有在堆中生成新的对象

回到 String的创建

  • 谈一谈 String s = “ddd”

针对这种代码, java在编译器就会生成一个 ddd的字面量常量, 放入常量池中, 运行时, 类被加载时, 该常量和静态常量池的其他常量一起被放入方法区中

所以说 String s = “ddd”会创建一个对象, “ddd”是在类加载的时候创建的

  • 再谈 String s = new String(“ddd”);

首先, 编译期在字节码中会有一个常量池, 常量池前面一部分是字面量, ddd作为字面量会被保存在其中, 然后在运行时,
方法区中存在运行时常量池, 静态常量池中的内容会在类加载的时候存入方法区中的常量池中, 这个时候建立的第一个字面量对象ddd,
允许到该代码时创建第二个对象,放在堆中,但实际上还是指向的同一个字节数组

  • 方法区的回收

默认是不会开启回收的, 需要加参数开启

elk的坑

三个关键的变量

logstash 的type, 区分不同的日志

redis的Key, 区分不同的key在reidis中的存储

index, 存储在es中的索引

排查收集流程

收集到redis -> 消费redis -> 写入到目标机文件 + es

排查时发现redis时有时无, 说明redis消费成功了, 最后一步的问题

总结可以发现 应该从源头追溯, 看每一步是否成功, 是否有问题, 再往下追溯

nginx.md

总结 20180106

1, api应该一直保持内网服务

2, 前端请求后端的时候. 无论域是否一样,都可以通过node 或者nginx做转发

3, 无论api是否是内网服务, 总有一个api入口去拿到接口数据

另: 真正的内部服务是, 前端加一个node server层, 前端的接口在node上请求, 浏览器只请求页面, 不请求接口。 这样才能真正对外网隔离api服务.

遇到一个nginx转发的问题, 始终做不了二次代理

排查过程: google, 使用不同的配置尝试. 一直不行, 最后参考之前可以的情况排除变更项找到了正确的方式

坑:

nginx负载均衡时根据host去转发不同的请求, 这里的host是header里面的host, 在nginx 转发里面是这个: proxy_set_header Host $host;

nginx的error日志, 默认是不会打的, 比如前端资源不存在, 需要指定 error_log

教训:

在使用新的方案的时候, 要对方案抱有质疑的态度, 每一行都可能有问题

在新的方案有问题, 可以去排查新方案的每个不懂的技术参数是什么意思, 弄懂了, 自然问题就解决了

可以拿新的方案和旧的方案diff下, 看旧的方案时候可用, 新的方案变更了什么, 逐渐diff出问题

可以看具体的日志, 这里是nginx日志, 成功和失败都要看日志分析, 这是排查问题非常有效的手段

spring 注解事务的使用问题

怎么使用事务

1
2
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>

注意事项

在一个类内部的方法之间相互调用, 不会触发事务, 在类之间相互调用才会触发事务

只要在方法的最外层开始事务, 方法里面的执行体只要使用的是同样的session, 都会在方法的事务里面

eg:

method2不会触发事务

1
2
3
4
5
6
7
8
9
10
11
class A {
method1() {
method2();
}

@Transactional
method2(){
}
}


method2 会触发事务

1
2
3
4
5
6
7
8
9
10
11
12
13
class A{
B b;
method1() {
b.method2();
}

}

class B{
@Transactional
method2() {}
}

method1, method2 会触发事务, 且method2会使用method1的session, 因为transactional的默认传播属性propagation是REQUIRED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A{
B b;
@Transactional
method1() {
b.method2();
}

}

class B{
@Transactional
method2() {}
}

class C {
A a;
@Transactional
method3() {
a.method1();
}

}

支付事务使用分析

在掉外部支付接口时
1, 初始化支付状态
2, 调用外部接口
3, 更新本地数据状态

第1, 3步分别保证各自的内部一致性, 需要使用事务
第2步如果失败要记日志, 推动第三步更新状态

嵌套事务的使用

如果被调用方是一个完整的业务B, 自己要记录其开始, 失败, 成功. 不能因为记录失败的时候失败就让该完整的事务回滚.
比如说先掉开始创建, 再调用失败, 记录成功出现异常, 结果导致创建也回滚了, 这种情况是不被允许的.
这个时候应该catch住. 然后记失败. 然后完成整个事务.

这个时候 业务B也不能因为调用方A的失败就失败, 不然B也没有记录了

所以B 应该是Required_new的传播属性, 而且通过返回参数给A返回状态.

综上, 在涉及到事务数据变更的地方, 都要考虑通过返回参数告诉调用方方法的执行情况, 而不是通过异常
同时, 子业务应该启动新的异常

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

// 如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.REQUIRED)
// 容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
// 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.SUPPORTS)

单个数据实体

最外层不用事务, 成功/失败的unit使用事务

Transaction rolled back because it has been marked as rollback-only

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
class A {
B b;
@Transactional
method1 {
try {
b.method2()
} catch(Exception e) {

}
}
}

class B {

@Transactional
method2(){

}
}

class C {
A a;
method3 {
a.method1()
}

在class C中调用a的方法, 如果在B中出现了异常, 再抛出异常, 这时候事务会由A发起的事务会 marked as rollback-only, 这个时候 method1的提交事务的时候就会抛出异常Transaction rolled back because it has been marked as rollback-only. 原因是由于spring的传播属性, b.method2 并没有开启新的事务, 依然在a.method1的事务里面。 在加了Transactional情况下, method2失败, 会标记事务已经回滚. 在method1 commit的时候自然就发现事务已经回滚了, 就会报错

resolve1: 这个时候可以在method2开启子事务, 这样就可以达到想要的目的. 但是要确认下是否能承受A 和 B不在同一个事务里的情况

1
2
@Transactional(propagation=Propagation.REQUIRES_NEW) 

resolve2:去掉A中的事务注解, 这个时候A中就没有事务了

resolve3:去掉B中的事务注解, 这个时候B中就没有事务了

子事务引起的可重复读问题(希望不可重复读)

可重复读的时候, 针对同一笔订单, 其他事务已经提交了更新, 但是该事务查询大的订单还是事务提交前的记录
http://blog.csdn.net/v123411739/article/details/39298127
http://www.cnblogs.com/zhoujinyi/p/3437475.html

原理

为什么会有以上的生效情况

什么情况下需要用事务呢 ?

读事务 单表事务 多表事务 分布式事务

spring mvc 在get中传入数组

使用相同的key, 传多个值

request:

1
array=1&array=2

服务器使用List接收, 可以放在对象里面, 使用 array
也可以放在方法体上面, 使用 @RequestParam(value = “array”, required = false) 接收

使用array[0], array[1], array[2]

request:

1
2
array[0]=1&array[1]=2

服务器使用List接收, 可以放在对象里面, 使用array
或者方法体里面 @RequestParam(value = “array[]”, required = false) 接收

也可以使用json

待补充

java 日期序列化, 反序列化的坑

背景知识

GMT是格林威治时间

CST 是北京标准时间, CST比GMT早8个小时。 一些工具类在格式化日期,会把日期转化成GMT时间再格式化。

使用jackson序列化日期存到cache中, 然后反序列回来, 发现反序列化后的日期比序列化时要小

1, 从数据库查到日期后, 默认是CST时区的,

2, 如果不设置, jackson 会默认把日期转化成 GMT时区对应的日期, 然后序列化成对应的字符串

3, 这样序列化后的日期字符串丢失了时区信息, 得到的日期字符串也不是我们想要的日期字符串了

4, 如果自定义了日期反序列化方式, 反序列化的时候会取系统默认的时区,也就是GMT时区,这时候日期就和序列化时不一致了

总结:

日期的序列化和反序列化时需要注意时区的设置, 如果设置了序列化日期, 在反序列化, 务必保证两次的时区一致

取时间戳计算后再根据时间戳创建时间比预期时间要快8小时 ?

问题代码:

以下算时间有问题吗, 有问题, 算出来的是GMT 0点的时间戳. 转换成CST就是 8:00了

1
2
3
4
5
6
7
8
9
10
11
    /**
* 获取当日日期
*
* @param date
* @return
*/
public static Date getDateShort(final Date date) {
final long l = date.getTime() / ONE_DAY;
return new Date(l * ONE_DAY);

}