目录
1、如何给IDEA设置炫酷的背景图片?
2、使用Python执行sql脚本
3、PL/SQL对分库或分表的sql查询
4、如何在spring的controller正确使用非静态成员变量?
5、解决mybatis使用中的实体类名称与表的字段名称不一致问题
6、Java中优雅的停止一个正在运行的线程
7、0.0.0.0,127.0.0.1,localhost的区别?
8、通过xxl-job每天凌晨0点定时重启第一台机器
9、Windows系统中的 win+r 常用查询
10、使用Redis统计网站的用户访问
只需以下几步,就可以轻松完成设置:
背景:最近在工作中经常使用 xxl-job,这是一个分布式的任务调度框架,可以并行的执行复杂的定时调度场景。不过,在正式启动 xxl-job 之前,需要将一些 sql 脚本在 MySQL 数据库跑一下。这里,我并不是复制 sql 脚本语句到数据库执行就完事了,而是想到了用 Python 写个脚本去执行 sql 脚本。
试想一下,如果执行sql脚本数据量较小时,直接复制到数据库客户端执行肯定最方便,但当有成百上千万行语句要执行时,通过Python处理无疑是比较合适的选择。实现步骤,有以下三步:
演示代码如下:
import pymysql'''database一定要存在,否则会报错 1049'''
def get_connection():print('开始连接数据库.....')connection = pymysql.connect(host="localhost", port=3306, user="root", --pwd自行填写,database="xxl_job")print('连接数据库ok')return connectiondef process_sql(filename, connection):with connection.cursor() as cursor:with open(filename, "r", encoding='utf-8') as f:sql_list = f.read().split(";")[0:-1]for sql in sql_list:if "\n" in sql:sql.replace("\n", " ")if " " in sql:sql.replace(" ", " ")sql_item = sql + ";"# print(sql_item)cursor.execute(sql_item)connection.commit()if __name__ == '__main__':con = get_connection()process_sql(r"D:\items\xxl-job\doc\db\tables_xxl_job.sql", con)print("sql执行成功!")
执行效果:
PL/SQL 是 Oracle 对 SQL 语言的过程化扩展,指在原有的增删查改的基础上,对 SQL 命令语言中增加了过程处理语句(比如分支,循环等),使 SQL 语言具有过程处理能力。
在数据库表的设计中可能会存在分库或分表的情况。假如有一张表 T_CHG_RECORD,它有16个分库 chg01~chg16,如何统计出该表的总数据量?最简单的方式是写16条 SQL 查询语句了,但肯定还有更高大上的方式,此时 PL/SQL 就可以排上用场,写法如下:
------ 先查CHG01~CHG09
declare v_count number;v_total number := 0;
beginfor idx in 1 .. 9 loopexecute immediate 'select count(*) from CHG0'|| idx ||'.T_CHG_RECORD ' into v_count;v_total := v_total + v_count;--dbms_output.put_line(idx ||':' || v_count);end loop;dbms_output.put_line('total:' || v_total);
end;------再查CHG10~CHG16
declare v_count number;v_total number := 0;
beginfor idx in 10 .. 16 loopexecute immediate 'select count(*) from CHG'|| idx ||'.T_CHG_RECORD ' into v_count;v_total := v_total + v_count;--dbms_output.put_line(idx ||':' || v_count);end loop;dbms_output.put_line('total:' || v_total);
end;
假如有一张表 T_ORDER_RECORD 做分表设计,分为64张表,此时使用 PL/SQL 查询64张表的总数据量就更容易了,写法如下:
---统计2023年之前的订单总数量
declarev_count number;v_total number := 0;
beginfor idx in 1 .. 64 loopexecute immediate 'select count(*) from ORDDB.T_ORDER_RECORD'|| idx ||' t where t.updatetime< to_timestamp(''2023-01-01 00:00:00.0'',''yyyy-MM-dd hh24:mi:ss.ff'')' into v_count;v_total := v_total + v_count;--dbms_output.put_line(idx ||':' || v_count);end loop;dbms_output.put_line('total:' || v_total);
end;
由于 PL/SQL 语法支持循环等逻辑,让查询分库/分表的语句就变得优雅简洁,execute immediate ...... into ...... 是查询的核心语句,根据自己的需求改造即可。
Spring的Controller默认是单例的,如果使用了静态非成员变量,且该变量不做同步处理的话,会引发线程安全问题。
举例说明:
@Controller
@Slf4j
public class ScopeTestController {private int num = 0;@RequestMapping("/testScope1")public void testScope1() {log.info("testScope1:{}", ++num);}@RequestMapping("/testScope2")public void testScope2() {log.info("testScope2:{}", ++num);}
}
首先访问 http://localhost:8080/testScope1,得到的答案是1;然后再访问 http://localhost:8080/testScope2,得到的答案是 2。
由此,就产生了线程安全问题了。为避免这种问题,最好不要在类里定义非静态成员变量。如果实在是需要定义该非静态成员变量,那如何正确使用呢?有两种方式:
第一种,使用多例模式,增加@Scope("prototype")注解
@Controller
@Scope("prototype")
@Slf4j
public class ScopeTestController {private int num = 0;@RequestMapping("/testScope1")public void testScope1() {log.info("testScope1:{}", ++num);}@RequestMapping("/testScope2")public void testScope2() {log.info("testScope2:{}", ++num);}
}
第二种,使用 ThreadLocal 保证变量不被修改
@Controller
@Slf4j
public class ThreadLocalController {// private int num = 0;private static final ThreadLocal num = ThreadLocal.withInitial(() -> 0);@RequestMapping("/test1")public void test1() {int value1 = num.get();if (value1 == 0){log.info("test1:{}", ++value1);}num.remove();}@RequestMapping("/test2")public void test2() {int value2 = num.get();if (value2 == 0){log.info("test2:{}", ++value2);}num.remove();}
}
这是个基础而常见的问题,解决方式有以下两种:
第一种,修改实体类名称,或数据库表的字段使用别名,保持二者名称一致(不推荐这种方式)。
第二种,使用 resultMap 标签,举例如下:
<1>. 既然优雅,肯定不能采用 kill 主进程而终止线程的超级暴力方式(不考虑)。
<2>. 调用 Thread 类中的 stop() 方法,这是一个被标记为废弃的线程终止方法,因为 stop 方法会释放锁并强制终止线程,可能造成执行一半的线程被终止,从而造成数据不一致问题(不推荐)。
<3>. 优雅停止线程的最佳实践是,调用 Thread 类的中断方法+中断判断方法(极力推荐)。
代码演示如下:
@Testpublic void testInterrupted() throws InterruptedException {Thread thread = new Thread() {@SneakyThrowspublic void run() {log.info("线程启动了...");//一直循环while (true) {log.info(String.valueOf(isInterrupted()));Thread.sleep(1000);//如果线程被中断,就退出死循环if (isInterrupted()) {log.info("线程结束了...");return;}}}};thread.start();Thread.sleep(2000); //等待2秒thread.interrupt(); //中断线程}
主要区别如下:
0.0.0.0地址,IPV4中被用于表示一个无效的,未知的或者不可用的目标。
127.0.0.1地址,是回环地址的一种(回环地址,指的是所有发往该类地址的数据包都应该被loop back),因此,可以通过使用ping 127.0.0.1 测试某台机器上的网络设备,操作系统或者TCP/IP实现是否工作正常。
localhost,准确的说是一个域名,而非IP地址,用于指代 this computer 或者 this host,可以用它来获取运行在本机上的网络服务。实际上,大多数机器地址都会将localhost映射到 IPV4的127.0.0.1地址,或IPV6的::1地址。
额外补充:
0.0.0.0地址:当一台主机还没被DHCP分配一个IP地址时,表示主机本身。用在服务端时,表示本机上的任意IPV4地址。用作路由时,它表示的是默认路由,即当路由表中没有找到完全匹配的路由的时候所对应的路由。
127.0.0.1地址:需要注意,所有带127网络号的地址都是回环地址,127.0.0.1只是其中一种回环地址。实际应用有,在大多数web容器测试中作为域名localhost所绑定的IP地址。在DDos攻击防御中,将域名指向127.0.0.1,让攻击者自己攻击自己。
localhost:作为域名,需要区分使用的是ipv4还是ipv6。大多数系统,绑定的是IPV4的127.0.0.1地址,或IPV6的::0地址。本机web测试中,一般会使用的是localhost域名。
背景:在做数据稽核项目时,有一台机器隔几天总是出现报错 "java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED"。
经过排查发现,基本原因:ES client 内部的调用链为 IOReactor->performRequestAsync的Listener -> onFailure,当短暂抖动触发 onFailure 中抛出异常时,最终导致整个 IO Reactor 不可用,后续请求都受影响,需要重启才能恢复。
因此,通过 xxl-job 配置两个定时任务,于每日凌晨0点定时重启报错机器(要保证凌晨0点左右该机器无定时任务在执行)。在 xxl-job 的 GLUE IDEA 编写脚本代码,做法如下:
每天定时重启第一台机器--kill
#!/bin/bash
echo "xxl-job: hello shell"ssh 用户名@机器IP < . /home/xxw/tmp/audit-kill.shecho "Good bye!"
exit 0
每天定时重启第一台机器--restart
#!/bin/bash
echo "xxl-job: hello shell"ssh 用户名@机器IP < . /home/xxw/tmp/audit-restart.shecho "Good bye!"
exit 0
涉及 shell 脚本:
audit-kill.sh
#!/bin/bashkill -9 `jps |grep 'xxxxx.jar' |awk '{print $1}'`
audit-restart.sh
#!/bin/bashecho "restart begin....."
sh xxxxx/cfg/start.sh start
echo "restart end....."
使用快捷键win+r,召唤出运行弹框,可以通过 Windows 系统自带的命令打开对应的内容面板。
按功能划分的话,可分为:
计算机管理
compmgmt.msc
-------------【计算机管理】下分项自带命令------------------
# 设备管理器: devmgmt.msc
# 磁盘管理: diskmgmt.msc
# 本地服务: services.msc
# 性能监视器: perfmon.msc
# 本地用户和组: lusrmgr.msc
# 事件查看器: eventvwr
计算机管理
# 任务管理器: taskmgr 一般使用快捷键ctrl+shift+esc
# 文件资源管理器: explorer 一般使用快捷键Win+E
# 注册表编辑器: regedit (删除某些程序的时候用的到)
# 证书管理器: certmgr.msc (查看电脑上的所有证书,不常用)
# 本地安全策略 secpol.msc (电脑安全方面的设置,不常用,win10家庭版无)
# 组策略编辑器 gpedit.msc (win10家庭版无)
控制面板
control
-------------【控制面板】下分项自带命令------------------
desk.cpl(桌面设置)
main.cpl(鼠标设置)
inetcpl.cpl(Internet属性)
ncpa.cpl(网络连接)
mmsys.cpl(声音和音频设备)
powercfg.cpl(电源选项)
sysdm.cpl(系统属性)
firewall.cpl(防火墙)
系统自带工具
calc(计算器)
osk(虚拟键盘)
write(写字板)
notepad(记事本)
mspaint(画图)
magnify(放大镜)
snippingtool(截图工具,支持无规则截图 )
mplayer2(简易widnows media player)
Sndvol(音量控制程序 )
关机命令
shutdown -s -t 0(在x秒后关机,x取值0-300,0表示立即关机)
shutdown -r -t 0(在x秒后重启,x取值0-300,0表示立即重启)
tsshutdn(60秒倒计时关机,win10家庭版无)
rononce -p(15秒倒计时关机,win10家庭版无)
以上绝大部分命令,都亲测实用~
有以下几种方案,各有优缺点,可根据实际场景需求选择。
第一种,使用hash数据类型(hset+hlen命令)。
使用hset命令,key=网站首页地址+当天日期,field的话,如果用户已登录存放用户ID,如果未登录则随机生成一个唯一标识表示用户ID,value默认设置1。最后统计某天的网站用户访问量时,使用hlen命令即可。
# 模拟用户访问网站首页的过程,第4条命令属于重复用户,不会被重复计入访问人数中的> hset http://123/index.html20230307 user_id1 1
1
> hset http://123/index.html20230307 user_id2 1
1
> hset http://123/index.html20230307 radom_id1 1
1
> hset http://123/index.html20230307 user_id2 1
0
> hlen http://123/index.html20230307
3
这种方式的优点:简单易实现,查询也方便,而且统计准确度高。但是,缺点也很明显:随着key越来越多占用内存会越大,可能出现性能瓶颈。
请注意,对于访问量不高的网站可以使用这种方式,像电商网站就不适用了!
第二种,使用bitmap结构。
bitmap通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。常用命令有:
正是如此,bitmap常用来做:用户签到、活跃用户、在线用户等功能。这里,setbit命令,key=网站首页地址+当天日期,offset=位的标识(标记1表示一个用户),value默认为1。使用bitcount命令,可以统计该网页每天的访问数量了。
# 模拟使用bitset统计用户访问网站首页的过程
> setbit http://123/index.html20230307 1 1
0
> hset http://123/index.html20230307 11 1
0
> hset http://123/index.html20230307 100 1
0
> hset http://123/index.html20230307 100 1
1
> bitcount http://123/index.html20230307
3
使用bitmap优势在于占用内存少,查询也方便,统计准确率高。不过,对于非登陆用户可能会映射到同一个用户ID,需要区分的话则会增加额外开销。
另外,bitmap适用于非稀疏的用户分布,用户过于稀疏可能比第一种方式更占用内存!
第三种,使用HyperLogLog概率算法。
Redis HyperLogLog 是用来做基数统计的算法,优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、且很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 它不能像集合那样,返回输入的各个元素。
当用户访问网站的时候,我们可以使用PFADD命令(Pfadd 命令将所有元素参数添加到 HyperLogLog 数据结构中,格式:PFADD key element [element ...]),设置对应的命令,最后我们只要通过PFCOUNT就能顺利计算出最终的结果,因为这个只是一个概率算法,所以可能存在0.81%的误差。
> pfadd http://123/index.html20230310 a b c d e f g h i j k l m n o p q
(integer) 1
>pfcount http://123/index.html20230310
(integer) 17
优点:占用内存极小,对于一个key,只需要12kb,对于电商网站这种超多用户的特别适用。
缺点:查询指定用户的时候,可能会出错,毕竟存的不是具体数据。总数也存在一定误差。它只能统计在线人数,不能实现其余的任何功能。这种方案仅仅只能统计出某个时间段在线人数的总量, 对在线用户的名单却无能为力,但是却挺节省内存的,对统计数据要求不多情况下 ,我们便可以考虑这种方案。
三月第二周总结梳理,就到这~