Concurrency model for mysql data processing@rubyconf.tw 2012
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Concurrency model for mysql data processing@rubyconf.tw 2012

on

  • 1,375 views

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

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

Statistics

Views

Total Views
1,375
Views on SlideShare
1,367
Embed Views
8

Actions

Likes
1
Downloads
28
Comments
0

2 Embeds 8

https://twitter.com 7
https://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Concurrency model for mysql data processing@rubyconf.tw 2012 Presentation Transcript

  • 1. Concurrency Model for huge MySQL data Mu-Fan Teng(@ryudoawaru) @ RubyConf Taiwan 201212年12月8⽇日星期六
  • 2. 緣起 Background12年12月8⽇日星期六
  • 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. The purpose12年12月8⽇日星期六
  • 5. Transcoding to UTF812年12月8⽇日星期六
  • 6. Try12年12月8⽇日星期六
  • 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. Failed!12年12月8⽇日星期六
  • 9. Cause • Too big size for most text editor. • Many mis-encoding text.12年12月8⽇日星期六
  • 10. Let’s reinvent the wheel!12年12月8⽇日星期六
  • 11. The new work flow • Connect DB • Transcode • Output db rows to SQL insert statement. • Write SQL file12年12月8⽇日星期六
  • 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. Thanks for Ruby 1.9’s Awesome Encoding class which supports Big5-UAO.12年12月8⽇日星期六
  • 14. Reduces almost 80% of encoding problem.12年12月8⽇日星期六
  • 15. But the file size is too big to wait for transcoding!12年12月8⽇日星期六
  • 16. So I have to find the concurrency model to make it faster.12年12月8⽇日星期六
  • 17. Experiment Target • Test the difference of performance between thread and fork model.12年12月8⽇日星期六
  • 18. H&W Platform • 4 Cores Core2Quad CPU@2.5G • 8GB RAM • 1*SSD • MacOS 10.8 • MRI 1.9.3p19412年12月8⽇日星期六
  • 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. 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. Result of 100k*4 rows12年12月8⽇日星期六
  • 22. Thread12年12月8⽇日星期六
  • 23. Fork12年12月8⽇日星期六
  • 24. Fork12年12月8⽇日星期六
  • 25. Circumstance • Thread ‣ CPU utilization rate between 105 and 125 percent. • Fork ‣ The rate changes frequently between processes.12年12月8⽇日星期六
  • 26. GVL still effects12年12月8⽇日星期六
  • 27. Try again12年12月8⽇日星期六
  • 28. Decompose the steps to find how to skip GVL.12年12月8⽇日星期六
  • 29. Experiment No.212年12月8⽇日星期六
  • 30. Minify the process to query DB only.12年12月8⽇日星期六
  • 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. Result of 100k*4 rows12年12月8⽇日星期六
  • 33. It seems the Mysql2 Gem can skip GVL.12年12月8⽇日星期六
  • 34. Experiment NO.312年12月8⽇日星期六
  • 35. Limit the experiment to I/O operation only.12年12月8⽇日星期六
  • 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. 12年12月8⽇日星期六
  • 38. Result of 100k*4 rows12年12月8⽇日星期六
  • 39. Change I/O to different files.12年12月8⽇日星期六
  • 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. Result reversed12年12月8⽇日星期六
  • 42. Implement the same change to the first experiment.12年12月8⽇日星期六
  • 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. Dose not effect any12年12月8⽇日星期六
  • 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. There is no effective and 「all-around」 concurrency model.12年12月8⽇日星期六
  • 47. The small I/O can’t release GVL.12年12月8⽇日星期六
  • 48. Kosaki-san’s slide12年12月8⽇日星期六
  • 49. Matz is not a threading guy12年12月8⽇日星期六
  • 50. End12年12月8⽇日星期六