由于系统历史问题,在系统中有着许多的定时任务。小到每5分钟一次,大到每天一次。由于这种定时任务的无限增多,当达到时间的最小公倍数的时候就会许多个任务同时跑起来,这样就会让MySQL发生抖动。就会发现时不时的MySQL性能会变差。
1、可能是定时任务的SQL太复杂,这一点可以从慢SQL中看出。
2、发生这样的原因是每个定时任务并发的对数据库进行操作。
1、排查优化定时任务的SQL
2、将并发任务改成串行任务去执行。
这边我们讨论如何让定时任务串行执行。
要让并行的任务串行起来,这我就想到了MySQL的mutex。在对资源进行操作的时候需要对资源加上一把锁,如果有其他 线程/进程 需要对同一资源进行操作的时候需要等待上一个 线程/进程 释放 mutex。
但是,我们不可能去实现或使用MySQL中的mutex,这是我们就需要使用到 “分布式锁”,这边我更喜欢将他叫做 “全局资源锁”。因此我们就需要引入zookeeper,用他来实现我们的”全局资源锁”的功能。当定时任务执行的时候都需要获取这个”全局资源锁”,当定时任务执行完之后再释放”全局资源锁”,从而让其他并发的定时任务去争抢”全局资源锁”,并运行定时任务。
kazoo 是zookeeper的Python API,我使用它的理由是十分方便,比使用zookeeper java API(zkClient/dubbo)方便太多了。
这边我就不说需要如何搭建一个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、编写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’ : ‘trustauth.cn’ , ‘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’ : ‘trustauth.cn’ , ‘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 |
你也可以使用手动掉用acquire(获得锁)和release(释放锁)方法来实现。不过没有特殊情况我的编程风格不会这么干。
其实这只是变向的方法解决定时任务的问题。最重要的还是要和业务沟通,或其他技术实现定时任务内容(如定时任务是统计一些信息,是否可以使用storm等技术来代替)。从而减少对定时任务的使用。
文章转载来自:trustauth.cn