1.1. 前言
由于系统历史问题,在系统中有着许多的定时任务。小到每5分钟一次,大到每天一次。由于这种定时任务的无限增多,当达到时间的最小公倍数的时候就会许多个任务同时跑起来,这样就会让MySQL发生抖动。就会发现时不时的MySQL性能会变差。
1.2. 原因
1、可能是定时任务的SQL太复杂,这一点可以从慢SQL中看出。
2、发生这样的原因是每个定时任务并发的对数据库进行操作。
1.3. 解决思路
1、排查优化定时任务的SQL
2、将并发任务改成串行任务去执行。
这边我们讨论如何让定时任务串行执行。
1.4. zookeeper的引入
要让并行的任务串行起来,这我就想到了MySQL的mutex。在对资源进行操作的时候需要对资源加上一把锁,如果有其他 线程/进程 需要对同一资源进行操作的时候需要等待上一个 线程/进程 释放 mutex。
但是,我们不可能去实现或使用MySQL中的mutex,这是我们就需要使用到 "分布式锁",这边我更喜欢将他叫做 "全局资源锁"。因此我们就需要引入zookeeper,用他来实现我们的"全局资源锁"的功能。当定时任务执行的时候都需要获取这个"全局资源锁",当定时任务执行完之后再释放"全局资源锁",从而让其他并发的定时任务去争抢"全局资源锁",并运行定时任务。
1.5. kazoo的使用
kazoo 是zookeeper的Python API,我使用它的理由是十分方便,比使用zookeeper java API(zkClient/dubbo)方便太多了。
官网:https://kazoo.readthedocs.org/en/latest/
1.6. zookeeper环境
这边我就不说需要如何搭建一个zookeeper集群了有需要自行去 Google。或到zookeeper官网上学习。
我这边使用的是zookeeper单机伪集群
主要配置如下
1
2
3
|
server
.
1
=
192.168.1.233
:
2887
:
3887
server
.
2
=
192.168.1.233
:
2888
:
3888
server
.
3
=
192.168.1.233
:
2889
:
3889
|
数据库环境
1
2
3
4
5
6
7
8
9
10
11
12
|
USE
test
;
CREATE
TABLE
`
orders
`
(
`
order_id
`
int
(
11
)
DEFAULT
NULL
,
`
num
`
int
(
11
)
DEFAULT
NULL
,
`
user_id
`
int
(
11
)
DEFAULT
NULL
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8
;
INSERT
INTO
orders
VALUES
(
1
,
11111
,
10
)
,
(
2
,
22222
,
10
)
,
(
3
,
33333
,
10
)
,
(
4
,
44444
,
10
)
,
(
5
,
55555
,
10
)
;
|
1.7. 模拟步骤
1、编写lock_mysql_1.py文件对mysql进行SELECT操作,并sleep 30秒后释放锁。
2、编写lock_mysql_2.py文件对mysql进行SELECT操作,不用sleep。
3、运行lock_mysql_1.py,后马上运行op_mysql_2.py。
4、观察lock_mysql_2.py被阻塞(30秒)后执行效果。
lock_mysql_1.py中的代码
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Program: kazoo lock
# Author : HH
# Date : 2016-04-07
from
kazoo
.
client
import
KazooClient
from
kazoo
.
recipe
.
lock
import
Lock
import
mysql
.
connector
import
time
import
logging
import
sys
reload
(
sys
)
sys
.
setdefaultencoding
(
'utf-8'
)
logging
.
basicConfig
(
)
if
__name__
==
'__main__'
:
# zookeeper 链接参数
zk_config
=
{
'hosts'
:
'192.168.1.233:2181,192.168.1.233:2182,192.168.1.233:2183'
,
'timeout'
:
5000
,
'read_only'
:
False
,
}
# MySQL链接参数
mysql_conf
=
{
'host'
:
'localhost'
,
'port'
:
'3306'
,
'database'
:
'test'
,
'user'
:
'root'
,
'password'
:
'root'
}
# 创建zookeeper客户端
zk
=
KazooClient
(
*
*
zk_config
)
zk
.
start
(
)
# 设置zookeeper锁节点并返回锁
lock
=
zk
.
Lock
(
"/lock_mysql"
,
"lock_1"
)
with
lock
:
''
'加锁并对数据库进行操作'
''
# 查询数据SQL
sql
=
''
'SELECT * FROM orders'
''
conn
=
mysql
.
connector
.
connect
(
*
*
mysql_conf
)
cursor
=
conn
.
cursor
(
)
cursor
.
execute
(
sql
)
for
x
in
cursor
:
print
x
# 睡眠30秒
time
.
sleep
(
30
)
|
lock_mysql_2.py中的代码
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
37
38
39
40
41
42
43
44
45
46
47
48
|
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Program: kazoo lock
# Author : HH
# Date : 2016-04-07
from
kazoo
.
client
import
KazooClient
from
kazoo
.
recipe
.
lock
import
Lock
import
mysql
.
connector
import
time
import
logging
import
sys
reload
(
sys
)
sys
.
setdefaultencoding
(
'utf-8'
)
logging
.
basicConfig
(
)
if
__name__
==
'__main__'
:
# zookeeper 链接参数
zk_config
=
{
'hosts'
:
'192.168.1.233:2181,192.168.1.233:2182,192.168.1.233:2183'
,
'timeout'
:
5000
,
'read_only'
:
False
,
}
# MySQL链接参数
mysql_conf
=
{
'host'
:
'localhost'
,
'port'
:
'3306'
,
'database'
:
'test'
,
'user'
:
'root'
,
'password'
:
'root'
}
# 创建zookeeper客户端
zk
=
KazooClient
(
*
*
zk_config
)
zk
.
start
(
)
# 设置zookeeper锁节点并返回锁
lock
=
zk
.
Lock
(
"/lock_mysql"
,
"lock_2"
)
with
lock
:
''
'加锁并对数据库进行操作'
''
# 查询数据SQL
sql
=
''
'SELECT * FROM orders'
''
conn
=
mysql
.
connector
.
connect
(
*
*
mysql_conf
)
cursor
=
conn
.
cursor
(
)
cursor
.
execute
(
sql
)
for
x
in
cursor
:
print
x
|
在session1中执行 lock_mysql_1.py (睡眠30秒)
1
|
time
python
lock_mysql_1
.
py
|
在session2中执行lock_mysql_2.py 观察呗阻塞现象(没有睡眠当时也被阻塞了)
1
|
time
python
lock_mysql_2
.
py
|
1.8. 补充
你也可以使用手动掉用acquire(获得锁)和release(释放锁)方法来实现。不过没有特殊情况我的编程风格不会这么干。
1.9. 总结
其实这只是变向的方法解决定时任务的问题。最重要的还是要和业务沟通,或其他技术实现定时任务内容(如定时任务是统计一些信息,是否可以使用storm等技术来代替)。从而减少对定时任务的使用。
昵称:HH
QQ:275258836
ttlsa群交流沟通(QQ群②:6690706 QQ群③:168085569 QQ群④:415230207(新) 微信公众号:ttlsacom)
感觉本文内容不错,读后有收获?
逛逛衣服店,鼓励作者写出更好文章。
转载请注明:成长的对话 » 使用zookeeper让定时任务串行执行