Last Friday, whilst I was fiddling with innodb performance settings, I apparently managed to totally hose any innodb tables. My blag and my dspam signatures were, thankfully, the only ones that ate it. Of course, I didn’t have backups, so that kinda sucked. I restored the blag from the most recent backup I had (February 12th) and then recreated manually the few posts I knew were ahead of that.
I hacked up some backup scripts in ruby, I probably could have done it in pure bash, but I felt that this was cleaner.
I’ve got two scripts, one for mysql, and one for the postgresql database. They’re almost exactly the same, except for the differences in how to talk to the database, and the interfacing objects. They create one .sql file for each database, and that will automatically adapt when you add more databases. It will keep around 7 copies of each file, so if you run it daily, you’ll have a week’s worth of database backups. They’re bzip2 –best’d so that it doesn’t waste too much space.
#!/usr/bin/rubyrequire"mysql"require"fileutils"# designed to be run daily, will keep #{files} number of files aroundbackupDir="/srv/database/backup/mysql"username="SOMEUSER"password="PASSNERD"host="localhost"files=7FileUtils.mkdir_pbackupDircurrentDir=FileUtils.pwd()FileUtils.cd(backupDir)begindbh=Mysql.real_connect(host,username,password)result=dbh.query("show databases;")whilerow=result.fetch_rowdobegindbname=row[0]# do file rotation stuffsFileUtils.rm("#{dbname}.#{files}.sql.bz2",:force=>true)# never will complain(1..(files-1)).eachdo|a|n=files-aFileUtils.mv("#{dbname}.#{n}.sql.bz2","#{dbname}.#{n+1}.sql.bz2",:force=>true)#no errorsend# move the one without a number to 1.sql.bz2FileUtils.mv("#{dbname}.sql.bz2","#{dbname}.1.sql.bz2",:force=>true)#dump the sql!command="mysqldump -u #{username} -p'#{password}' -h #{host}#{dbname} | bzip2 --best > #{dbname}.sql.bz2"`#{command}`rescueMysql::Error=>err# note it and continue with the next databasep"Database: #{dbname}\n\terr"endendresult.freerescueMysql::Error=>eputs"Error code: #{e.errno}"puts"Error message: #{e.error}"puts"Error SQLSTATE: #{e.sqlstate}"ife.respond_to?("sqlstate")ensuredbh.closeifdbhFileUtils.cd(currentDir)end
#!/usr/bin/rubyrequire"pg"require"fileutils"# designed to be run daily, will keep #{files} number of files aroundbackupDir="/srv/database/backup/postgres"username="SOMEUSER"password="PASSNERD"host="localhost"files=7FileUtils.mkdir_pbackupDircurrentDir=FileUtils.pwd()FileUtils.cd(backupDir)beginconn=PGconn.new(:user=>username,:password=>password,:host=>host)result=conn.exec("select datname from pg_database")result.eachdo|row|begindbname=row['datname']if!(/template.*/=~dbname)# do file rotation stuffsFileUtils.rm("#{dbname}.#{files}.sql.bz2",:force=>true)# never will complain(1..(files-1)).eachdo|a|n=files-aFileUtils.mv("#{dbname}.#{n}.sql.bz2","#{dbname}.#{n+1}.sql.bz2",:force=>true)#no errorsend# move the one without a number to 1.sql.bz2FileUtils.mv("#{dbname}.sql.bz2","#{dbname}.1.sql.bz2",:force=>true)#dump the sql!command="PGPASSWORD='#{password}' pg_dump -U #{username} -h #{host}#{dbname} | bzip2 --best > #{dbname}.sql.bz2"`#{command}`endrescuePGError=>err# note it and continue with the next databasep"Database: #{dbname}\n\terr"endendresult.clearrescuePGError=>epeensureconn.closeifconnFileUtils.cd(currentDir)end