Successfully reported this slideshow.
Your SlideShare is downloading. ×

Concurrency model for mysql data processing@rubyconf.tw 2012

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 50 Ad

More Related Content

Slideshows for you (20)

Advertisement

Similar to Concurrency model for mysql data processing@rubyconf.tw 2012 (20)

Advertisement

Recently uploaded (20)

Concurrency model for mysql data processing@rubyconf.tw 2012

  1. 1. Concurrency Model for huge MySQL data Mu-Fan Teng(@ryudoawaru) @ RubyConf Taiwan 2012 12年12月8⽇日星期六
  2. 2. 緣起 Background 12年12月8⽇日星期六
  3. 3. Legacy environment • A Mysql Database with 2.3gb data with Big5 charset and ISO-8859-1 encoding. • The biggest table in DB is 1.5gb. 12年12月8⽇日星期六
  4. 4. The purpose 12年12月8⽇日星期六
  5. 5. Transcoding to UTF8 12年12月8⽇日星期六
  6. 6. Try 12年12月8⽇日星期六
  7. 7. Work Flow 1. mysqldump with -default-character- set=latin1 parameter to generate SQL file. 2. Transcoding SQL file with tool like iconv/ bsdconv. 3. Edit transcoded SQL file to avoid 「slash」 problem. 4. Restore SQL file to new DB. 12年12月8⽇日星期六
  8. 8. Failed! 12年12月8⽇日星期六
  9. 9. Cause • Too big size for most text editor. • Many mis-encoding text. 12年12月8⽇日星期六
  10. 10. Let’s reinvent the wheel! 12年12月8⽇日星期六
  11. 11. The new work flow • Connect DB • Transcode • Output db rows to SQL insert statement. • Write SQL file 12年12月8⽇日星期六
  12. 12. CORES_COUNT = 4 LIMIT = ARGV[0].to_i || 10000 sqls = CORES_COUNT.times.map do |x| sprintf("SELECT * FROM cdb_posts ORDER BY pid LIMIT %d OFFSET %d;", LIMIT, (x * LIMIT)) end class String def to_my_val "'#{Mysql2::Client.escape self.force_encoding(‘Big5- UAO’).encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '??')}'" end end procs = sqls.map do |sql| Proc.new do |out| Mysql2::Client.new(database: DBNAME, reconnect: true, encoding: 'latin1').query(sql).each(as: :array) do |row| out.print "INSERT INTO `cdb_posts` VALUES (#{row.map(&:to_my_val).join(',')});n" end end end procs.each{|p| p.call(OUT)} 12年12月8⽇日星期六
  13. 13. Thanks for Ruby 1.9’s Awesome Encoding class which supports Big5-UAO. 12年12月8⽇日星期六
  14. 14. Reduces almost 80% of encoding problem. 12年12月8⽇日星期六
  15. 15. But the file size is too big to wait for transcoding! 12年12月8⽇日星期六
  16. 16. So I have to find the concurrency model to make it faster. 12年12月8⽇日星期六
  17. 17. Experiment Target • Test the difference of performance between thread and fork model. 12年12月8⽇日星期六
  18. 18. H&W Platform • 4 Cores Core2Quad CPU@2.5G • 8GB RAM • 1*SSD • MacOS 10.8 • MRI 1.9.3p194 12年12月8⽇日星期六
  19. 19. DBNAME = 'wwwfsc' CORES_COUNT = 4 ForceEncoding = 'Big5-UAO' LIMIT = ARGV[0].to_i || 10000 OUT = '/dev/null' sqls = CORES_COUNT.times.map do |x| sprintf("SELECT * FROM cdb_posts ORDER BY pid LIMIT %d OFFSET %d;", LIMIT, (x * LIMIT)) end class String def to_my_val "'#{Mysql2::Client.escape self.force_encoding(ForceEncoding).encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '??')}'" end end procs = sqls.map do |sql| Proc.new do |out| open(out,'w') do |io| Mysql2::Client.new(database: DBNAME, reconnect: true, encoding: 'latin1').query(sql).each(as: :array) do |row| io.print "INSERT INTO `cdb_posts` VALUES (#{row.map(&:to_my_val).join(',')});n" end end end end 12年12月8⽇日星期六
  20. 20. Benchmark.bm(15) do |x| x.report("Thread"){procs.map{|p| Thread.new{p.call(OUT)} }.each(&:join)} x.report("Fork"){procs.each{|p| fork{p.call(OUT)} }; Process.waitall} x.report("Normal"){procs.each{|p| p.call(OUT)}} end 12年12月8⽇日星期六
  21. 21. Result of 100k*4 rows 12年12月8⽇日星期六
  22. 22. Thread 12年12月8⽇日星期六
  23. 23. Fork 12年12月8⽇日星期六
  24. 24. Fork 12年12月8⽇日星期六
  25. 25. Circumstance • Thread ‣ CPU utilization rate between 105 and 125 percent. • Fork ‣ The rate changes frequently between processes. 12年12月8⽇日星期六
  26. 26. GVL still effects 12年12月8⽇日星期六
  27. 27. Try again 12年12月8⽇日星期六
  28. 28. Decompose the steps to find how to skip GVL. 12年12月8⽇日星期六
  29. 29. Experiment No.2 12年12月8⽇日星期六
  30. 30. Minify the process to query DB only. 12年12月8⽇日星期六
  31. 31. DBNAME = 'wwwfsc' CORES_COUNT = 4 limit = ARGV[0].to_i || 10000 sqls = CORES_COUNT.times.map do |x| sprintf("SELECT * FROM cdb_posts ORDER BY pid LIMIT %d OFFSET %d;", limit, (x * limit)) end procs = CORES_COUNT.times.map do |x| Proc.new do client = Mysql2::Client.new(database: DBNAME, reconnect: true) result = client.query(sqls[x]) end end Benchmark.bmbm(15) do |x| x.report("Thread"){procs.map{|p| Thread.new{p.call} }.each(&:join)} x.report("Fork"){procs.each{|p| fork{p.call} }; Process.waitall} x.report("Normal"){procs.each(&:call)} end 12年12月8⽇日星期六
  32. 32. Result of 100k*4 rows 12年12月8⽇日星期六
  33. 33. It seems the Mysql2 Gem can skip GVL. 12年12月8⽇日星期六
  34. 34. Experiment NO.3 12年12月8⽇日星期六
  35. 35. Limit the experiment to I/O operation only. 12年12月8⽇日星期六
  36. 36. client = Mysql2::Client.new(database: DBNAME, reconnect: true, encoding: 'latin1') sql_raws = sqls.map do |sql| arr = [] client.query(sql).each(as: :array) do |row| arr << "(#{row.map(&:to_my_val).join(',')})" end arr end procs = sql_raws.map do |arr| proc do io = open('/dev/null','w') io.write "INSERT INTO `cdb_posts` VALUES " io.write arr.join(',') io.write "n" io.close end end Benchmark.bm(15) do |x| x.report("Thread"){procs.map{|p| Thread.new{p.call} }.each(&:join)} x.report("Fork"){procs.each{|p| fork{p.call} }; Process.waitall} x.report("Normal"){procs.each{|p| p.call}} end 12年12月8⽇日星期六
  37. 37. 12年12月8⽇日星期六
  38. 38. Result of 100k*4 rows 12年12月8⽇日星期六
  39. 39. Change I/O to different files. 12年12月8⽇日星期六
  40. 40. procs = sql_raws.map do |arr| proc do io = Tempfile.new(SecureRandom.uuid)#open('/dev/null','w') puts io.path io.write "INSERT INTO `cdb_posts` VALUES " io.write arr.join(',') io.write "n" io.close end end 12年12月8⽇日星期六
  41. 41. Result reversed 12年12月8⽇日星期六
  42. 42. Implement the same change to the first experiment. 12年12月8⽇日星期六
  43. 43. procs = sqls.map do |sql| Proc.new do io = Tempfile.new(SecureRandom.uuid) Mysql2::Client.new(database: DBNAME, reconnect: true, encoding: 'latin1').query(sql).each(as: :array) do |row| io.write "INSERT INTO `cdb_posts` VALUES (#{row.map(&:to_my_val).join(',')});n" end io.close end end 12年12月8⽇日星期六
  44. 44. Dose not effect any 12年12月8⽇日星期六
  45. 45. Conclusion Thread fork normal MySQL2-read Fast Fast x Transcoding & Slow Very fast x iteration Write to the Very slow Slow Fast same I/O Write to the Fast Slow Fast different I/O 12年12月8⽇日星期六
  46. 46. There is no effective and 「all-around」 concurrency model. 12年12月8⽇日星期六
  47. 47. The small I/O can’t release GVL. 12年12月8⽇日星期六
  48. 48. Kosaki-san’s slide 12年12月8⽇日星期六
  49. 49. Matz is not a threading guy 12年12月8⽇日星期六
  50. 50. End 12年12月8⽇日星期六

×