Concurrency model for mysql data processing@rubyconf.tw 2012

1,774 views
1,681 views

Published on

Video: http://www.youtube.com/watch?v=3Oi_IjKDZtg

Published in: News & Politics
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,774
On SlideShare
0
From Embeds
0
Number of Embeds
82
Actions
Shares
0
Downloads
30
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Concurrency model for mysql data processing@rubyconf.tw 2012

  1. 1. Concurrency Model for huge MySQL data Mu-Fan Teng(@ryudoawaru) @ RubyConf Taiwan 201212年12月8⽇日星期六
  2. 2. 緣起 Background12年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 purpose12年12月8⽇日星期六
  5. 5. Transcoding to UTF812年12月8⽇日星期六
  6. 6. Try12年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 file12年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.3p19412年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 end12年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)}} end12年12月8⽇日星期六
  21. 21. Result of 100k*4 rows12年12月8⽇日星期六
  22. 22. Thread12年12月8⽇日星期六
  23. 23. Fork12年12月8⽇日星期六
  24. 24. Fork12年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 effects12年12月8⽇日星期六
  27. 27. Try again12年12月8⽇日星期六
  28. 28. Decompose the steps to find how to skip GVL.12年12月8⽇日星期六
  29. 29. Experiment No.212年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)} end12年12月8⽇日星期六
  32. 32. Result of 100k*4 rows12年12月8⽇日星期六
  33. 33. It seems the Mysql2 Gem can skip GVL.12年12月8⽇日星期六
  34. 34. Experiment NO.312年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}} end12年12月8⽇日星期六
  37. 37. 12年12月8⽇日星期六
  38. 38. Result of 100k*4 rows12年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 end12年12月8⽇日星期六
  41. 41. Result reversed12年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 end12年12月8⽇日星期六
  44. 44. Dose not effect any12年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/O12年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 slide12年12月8⽇日星期六
  49. 49. Matz is not a threading guy12年12月8⽇日星期六
  50. 50. End12年12月8⽇日星期六

×