使用zookeeper让定时任务串行执行

HH MySQL python使用zookeeper让定时任务串行执行已关闭评论15,2844字数 3324阅读11分4秒阅读模式

1.1. 前言

由于系统历史问题,在系统中有着许多的定时任务。小到每5分钟一次,大到每天一次。由于这种定时任务的无限增多,当达到时间的最小公倍数的时候就会许多个任务同时跑起来,这样就会让MySQL发生抖动。就会发现时不时的MySQL性能会变差。

1.2. 原因

1、可能是定时任务的SQL太复杂,这一点可以从慢SQL中看出。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

2、发生这样的原因是每个定时任务并发的对数据库进行操作。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

1.3. 解决思路

1、排查优化定时任务的SQL文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

2、将并发任务改成串行任务去执行。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

这边我们讨论如何让定时任务串行执行。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

1.4. zookeeper的引入

要让并行的任务串行起来,这我就想到了MySQL的mutex。在对资源进行操作的时候需要对资源加上一把锁,如果有其他 线程/进程 需要对同一资源进行操作的时候需要等待上一个 线程/进程 释放 mutex。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

但是,我们不可能去实现或使用MySQL中的mutex,这是我们就需要使用到 "分布式锁",这边我更喜欢将他叫做 "全局资源锁"。因此我们就需要引入zookeeper,用他来实现我们的"全局资源锁"的功能。当定时任务执行的时候都需要获取这个"全局资源锁",当定时任务执行完之后再释放"全局资源锁",从而让其他并发的定时任务去争抢"全局资源锁",并运行定时任务。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

1.5. kazoo的使用

kazoo 是zookeeper的Python API,我使用它的理由是十分方便,比使用zookeeper java API(zkClient/dubbo)方便太多了。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

官网:https://kazoo.readthedocs.org/en/latest/文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

1.6. zookeeper环境

这边我就不说需要如何搭建一个zookeeper集群了有需要自行去 Google。或到zookeeper官网上学习。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

我这边使用的是zookeeper单机伪集群文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

主要配置如下文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

server.1 = 192.168.1.233:2887:3887
server.2 = 192.168.1.233:2888:3888
server.3 = 192.168.1.233:2889:3889

数据库环境文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

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秒后释放锁。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

2、编写lock_mysql_2.py文件对mysql进行SELECT操作,不用sleep。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

3、运行lock_mysql_1.py,后马上运行op_mysql_2.py。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

4、观察lock_mysql_2.py被阻塞(30秒)后执行效果。文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

lock_mysql_1.py中的代码文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

#!/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中的代码文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

#!/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秒)文章源自运维生存时间-https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/

time python lock_mysql_1.py

在session2中执行lock_mysql_2.py 观察呗阻塞现象(没有睡眠当时也被阻塞了)

time python lock_mysql_2.py

1.8. 补充

你也可以使用手动掉用acquire(获得锁)和release(释放锁)方法来实现。不过没有特殊情况我的编程风格不会这么干。

1.9. 总结

其实这只是变向的方法解决定时任务的问题。最重要的还是要和业务沟通,或其他技术实现定时任务内容(如定时任务是统计一些信息,是否可以使用storm等技术来代替)。从而减少对定时任务的使用。

 

昵称:HH
QQ:275258836
ttlsa群交流沟通(QQ群②:6690706 QQ群③:168085569 QQ群④:415230207(新) 微信公众号:ttlsacom)

感觉本文内容不错,读后有收获?

逛逛衣服店,鼓励作者写出更好文章。

weinxin
我的微信
微信公众号
扫一扫关注运维生存时间公众号,获取最新技术文章~
HH
  • 本文由 发表于 16/08/2016 00:17:17
  • 转载请务必保留本文链接:https://www.ttlsa.com/mysql/use-zookeeper-let-timer-serial-execute/