利用mysql "named lock"实现分布式系统的同步
2014-06-18 17:13
471 查看
问题描述
分布式系统要解决的一个核心技术问题就是系统级的同步问题,一个通用的解决方案是Apache ZooKeeper。但是ZooKeeper并不是在所有的场景下都适用,而且配置复杂,且由于需要跨进程间通信,性能也比较差。
一个常用的场景是读写数据库,且要保证数据库读写是同步的,如果数据库采用的是mysql,那么利用mysql的“named lock”就可以快速高效地实现系统级的同步。
考虑一个极端简化的员工信息输入系统:
在mysql中有一张employee表格,用来存储一个公司所有的员工信息:
而在指向mysql的每一台服务器上,都有一个程序生成employee数据并录入mysql,这个程序可以是web service,cron job,或者一个python脚本,唯一的要求是保证不能插入具有同样employee_id的记录到employee表。
Insert...Ignore 或 Insert...On Duplicate Key Update
这种方式最为简洁,效率也最高,但前提条件是:
对要求值唯一的column(s),必须定义了primary key或unique key来从table schema上保证其唯一性
Insert...Where Not Exists...
这种方式就突破了PK和Unique Key的限制,可以保证插入的数据在任何column(s)上唯一,但缺点是:
在高并发情况下容易发生Deadlock,因为嵌套query里会去锁定a range of rows
Get_Lock & Release_Lock
最后就是本文要重点介绍的mysql 'named lock',其用法和高级编程语言,如Java中的lock object类似,可以提供database-level的同步。其用法就是利用Get_Lock和Release_Lock分别获取或释放一个lock object,具体可参考mysql文档。
‘named lock’的使用有一套固定的模式:
提供一个stored procedure
Get_Lock
....需要同步的操作...
Release_Lock
从应用程序中将插入的数据作为IN参数传给stored procedure,并从OUT参数中获取结果和调式信息
例如:
这是在mysql+分布式应用场景中适用最广的一种同步机制,虽然性能会有些微下降,但在高并发情况下也不会出现死锁的情况。当然,如果在该分布式系统中并不是只有一个centralized mysql,而是有多个database server,并且需要在多个database server中保持数据一致性的话,这种同步机制就不适用了,只能考虑ZooKeepr之类的分布式同步系统,或者Cassandra之类的分布式数据库了。
分布式系统要解决的一个核心技术问题就是系统级的同步问题,一个通用的解决方案是Apache ZooKeeper。但是ZooKeeper并不是在所有的场景下都适用,而且配置复杂,且由于需要跨进程间通信,性能也比较差。
一个常用的场景是读写数据库,且要保证数据库读写是同步的,如果数据库采用的是mysql,那么利用mysql的“named lock”就可以快速高效地实现系统级的同步。
考虑一个极端简化的员工信息输入系统:
在mysql中有一张employee表格,用来存储一个公司所有的员工信息:
CREATE TABLE IF NOT EXISTS `employee` ( `employee_id` varchar(8) NOT NULL, `employee_name` varchar(128) NOT NULL, PRIMARY KEY (`employee_id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8
而在指向mysql的每一台服务器上,都有一个程序生成employee数据并录入mysql,这个程序可以是web service,cron job,或者一个python脚本,唯一的要求是保证不能插入具有同样employee_id的记录到employee表。
Insert...Ignore 或 Insert...On Duplicate Key Update
INSERT INTO employee ( employee_id, employee_name ) VALUES ('xxxx', 'yyyy') ON DUPLICATE KEY UPDATE employee_name = employee_name
这种方式最为简洁,效率也最高,但前提条件是:
对要求值唯一的column(s),必须定义了primary key或unique key来从table schema上保证其唯一性
Insert...Where Not Exists...
INSERT INTO employee ( employee_id, employee_name ) SELECT 'xxxx', 'yyyy' FROM DUAL WHERE NOT EXISTS ( SELECT 1 FROM employee WHERE employee_id = 'xxxx' )
这种方式就突破了PK和Unique Key的限制,可以保证插入的数据在任何column(s)上唯一,但缺点是:
在高并发情况下容易发生Deadlock,因为嵌套query里会去锁定a range of rows
Get_Lock & Release_Lock
最后就是本文要重点介绍的mysql 'named lock',其用法和高级编程语言,如Java中的lock object类似,可以提供database-level的同步。其用法就是利用Get_Lock和Release_Lock分别获取或释放一个lock object,具体可参考mysql文档。
‘named lock’的使用有一套固定的模式:
提供一个stored procedure
Get_Lock
....需要同步的操作...
Release_Lock
从应用程序中将插入的数据作为IN参数传给stored procedure,并从OUT参数中获取结果和调式信息
例如:
DELIMITER ; DROP PROCEDURE IF EXISTS `insert_employee_data`; DELIMITER // CREATE DEFINER=`xxxx`@`%` PROCEDURE `insert_employee_data`( IN inEmployeeID varchar(8), IN inEmployeeName varchar(128), OUT outStatus INT(11), -- -1 means Error; 0 means Skipped; 1 means Inserted OUT outErrMsg TEXT, -- Error message OUT outTraceInfo TEXT -- Trace message for debugging ) SP_LABEL:BEGIN -- 首先要注册一个Exit Handler,如果Stored Procedure执行过程中发生异常,则会在退出前执行Exit Handler DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN SET @lockObj = 'MYSQL_INSERT_EMPLOYEE_DATA'; SET @releaseLockTime = CURRENT_TIMESTAMP; SET @lockResult = RELEASE_LOCK(@lockObj); SET outTraceInfo = CONCAT(IFNULL(outTraceInfo, ''), ' SQLEXCEPTION handler: release_lock=', IFNULL(@lockResult, 'NULL')); SET outTraceInfo = CONCAT(outTraceInfo, '; release lock time: ', @releaseLockTime); SET outStatus = -1; GET DIAGNOSTICS CONDITION 1 outErrMsg = MESSAGE_TEXT; END; -- 定义一个Named Lock对象,注意不要和其它SP里用到的Named Lock重复,否则会相互干扰 -- 保证唯一的一个方法是将SP的名字作为Lock对象名字的一部分 SET @lockObj = 'MYSQL_INSERT_EMPLOYEE_DATA'; -- GET_LOCK。60表示最多等待60秒,如果失败,则返回0 SET @lockResult = GET_LOCK(@lockObj, 60); SET outTraceInfo = CONCAT('@lockResult=', IFNULL(@lockResult, 'NULL')); -- '0' means fails to get the lock in specified time limit; 'NULL' means error happens when trying to get the lock IF ((@lockResult IS NULL) OR (@lockResult = 0)) THEN SET outStatus = -1; SET outErrMsg = CONCAT('Fail to get in-memory lock object in 60 seconds', @lockObj); LEAVE SP_LABEL; END IF; SET @getLockTime = CURRENT_TIMESTAMP; SELECT 1 INTO employeeCount FROM employee WHERE employee_id = inEmployeeID LIMIT 1; SET outTraceInfo = CONCAT(outTraceInfo, '; employeeCount=', IFNULL(vmCount, 'NULL')); SET outStatus = 0; IF ((employeeCount IS NULL) OR (employeeCount = 0)) THEN SET outStatus = 1; INSERT INTO employee(employee_id, employee_name) VALUES (inEmployeeID, inEmployeeName); END IF; SET @releaseLockTime = CURRENT_TIMESTAMP; -- Release the lock SET @lockResult = RELEASE_LOCK(@lockObj); SET outTraceInfo = CONCAT(outTraceInfo, '; release_lock=', IFNULL(@lockResult, 'NULL')); SET outTraceInfo = CONCAT(outTraceInfo, '; lock time: (', @getLockTime , ') ~ (', @releaseLockTime, ')'); END; //
这是在mysql+分布式应用场景中适用最广的一种同步机制,虽然性能会有些微下降,但在高并发情况下也不会出现死锁的情况。当然,如果在该分布式系统中并不是只有一个centralized mysql,而是有多个database server,并且需要在多个database server中保持数据一致性的话,这种同步机制就不适用了,只能考虑ZooKeepr之类的分布式同步系统,或者Cassandra之类的分布式数据库了。
相关文章推荐
- 利用webservice和jms实现系统间的数据同步之三
- 利用readwritelock简单模拟实现多线程下cache的系统
- 分布式系统下利用 AOP 实现 session 身份验证的一种方案
- 利用WhiteHose一步步建立分布式系统的框架(七)--添加组件的操作并实现分布式系统
- 分布式系统笔记:利用zookeeper实现分布式leader节点选举
- Centos6系统利用rsync与ssh实现文件同步
- 利用ReentrantReadWriteLock实现缓存系统
- 利用Logstash实现ES和MySQL同步
- LINUX下使用elasticsearch-jdbc工具实现MySQL同步到ElasticSearch 以及linux 64位centos系统安装jdk1.8
- shiro+cas+spring-data-redis实现多系统单点登录和分布式项目的session同步
- Debian系统下利用vsdftpd+Mysql实现虚拟用户登录(Debian+vsftpd+Mysql)
- 利用log4j+mongodb实现分布式系统中日志统一管理
- 利用脚本实现mysql主库到备库数据同步(每五分钟同步一次增量)
- Spring Cloud分布式微服务系统中利用redssion实现分布式锁
- 利用webservice和jms实现系统间的数据同步之一
- 利用ZooKeeper服务实现分布式系统的Leader选举
- 利用log4j+mongodb实现分布式系统中日志统一管理
- 利用JAVA+MySQL实现学生选课系统
- MySQL(二):主从复制结构、半同步复制、双主复制结构、利用SSL实现安全的MySQL主从复制
- 分布式系统笔记:利用zookeeper实现分布式任务锁(Java)