Managing Data

September 29, 2012

MySQLConnect — Auditing, Online DDL, FK in Cluster and More

Filed under: 5.6,MySQLConnect,python — lstigile @ 11:31 pm
Tags:

There’s nothing like hearing directly from the developers.  As an Oracle employee, I’ve already learned a lot about MySQL 5.6 and other recent updates.  Nevertheless, I was impressed with a few new updates mentioned during the keynote.  Here’s a list of items that caught my attention (disclaimer: this is a partial list)

**Performance — with the group commit feature in 5.6 and other 5.6 performance improvements, the overhead of sync_binlog=1 is less than 10%.  In fact, with binlog enabled, 5.6 is faster than 5.5 without binlog enabled.

**Audit Extension — Oracle offers a dynamic plugin for enabling auditing for Sarbanes Oxley, PCI Compliance…  (note: this is a commercial feature which requires a subscription)

**Foreign Key support in MySQL Cluster — I find it ironic that at the same time NoSQL is becoming more popular; we’re also seeing very high demand for foreign key support in MySQL Cluster.  Cluster 7.3 (not GA, new milestone release (beta-level) ) now supports foreign keys, even if you’re using our NoSQL api.   Nice!

**Online ALTER TABLE (online DDL) for InnoDB — MySQL DBA’s are accustomed to all sorts of hurdles to change schema, including failing over to secondary servers during the change.  No more!  Add columns and indexes online.

Python connector GA– MySQL Workbench has been shipping with some very powerful python utilities.  My favorite is a batch of utilities for managing 5.6 replication servers, including a utility to automate failover to a slave — safely.  It uses the new 5.6 global transaction ID to ensure that the slave server is up-to-date.  Oracle just announced a MySQL python connector, supported by Oracle — and more importantly, heavily used by the Oracle MySQL team.

I also impressed by Edward Screven’s comment that Oracle has more developers working on MySQL than ever before–great evidence of Oracle’s investment in MySQL

September 29, 2010

MySQL Workbench Plugin: mforms example and slow query log statistics

Filed under: MySQL Workbench,python,Windows — lstigile @ 3:33 pm
Tags: , ,

As an update to my prior post, I’ve added a form to the workbench plugin.  Now, the user can select a slow query log file and generate statistics from it.  The plugin provides information to answer the following questions:

Figure 1. Sample plugin form

  • What type of queries run most often?
  • What type of queries are the slowest?
  • Which queries access the most rows?
  • Which queries send the most data?

The plugin scans the slow query log, aggregates similar queries, and provides summary statistics for each group.  It’s very similar to the mysqldumpslow perl utility, which is included in the mysql bin folder.  However, as a plugin, it’s easier to use on a Windows server as it doesn’t require you to install perl.

If you’re working to create a MySQL Workbench plugin, this mform code will help provide an example.

This form prompts the user for the output type of the slow query log (file or table), for the location of the slow query log file, and for the sort order: count, query_time, rows_examined, rows_sent. See Figure 1. Sample plugin form.

The plugin generates output similar to the following for queries in the slow query log:

Statement: DELETE FROM cache_form WHERE expire != DIGIT AND expire < DIGIT
count: 21
average time: 0:00:00.003677
slowest sql: DELETE FROM cache_form WHERE expire != 0 AND expire < 1281722403
slowest time: 0:00:00.075859
average rows sent: 0
average rows examined: 0
average lock time: 0:00:00.003625

Statement: SELECT data, created, headers, expire, serialized FROM cache_menu WHERE cid = ‘STRING’
count: 116
average time: 0:00:00.001269
slowest sql: SELECT data, created, headers, expire, serialized FROM cache_menu WHERE cid = ‘links:secondary-links:tree-data:5d6d3aaaaef5fba302ce62698fa37bbe’
slowest time: 0:00:00.024912<
average rows sent: 0
average rows examined: 0
average lock time: 0:00:00.000071

. . .
. . .
. . .


To add the plugin to your workbench install, create a file named slowquerystats_grt.py, click on the “show source” magnifying glass icon below, and copy the code block into the file.  From the Scripting menu in Workbench, call install Plugin/Module File and select the file to be installed.  This plugin requires Workbench version 2.7 or newer.  Once you’ve installed the plugin, you can run it by opening the SQL Editor and selecting Slow Log Stats from the Plugins|Utilities menu.

import os
import re
from wb import *
import grt
from operator import itemgetter
from mforms import newBox, newButton, newPanel, newTextBox, newRadioButton, newLabel, newSelector, newTextEntry, newCheckBox, SelectorCombobox
from mforms import Utilities, FileChooser
import mforms
import datetime

ModuleInfo = DefineModule(name= "slowlogstats", author= "lstigile", version="0.5")
@ModuleInfo.plugin("slowlogstats", caption= "Slow Log Stats", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
#---------------------------------------------
#  Entry point, creates form instance, sets choice for sourcetype (table or file), file location
#  and sort order of results
#---------------------------------------------
def optionPrompt(qbuffer):

  lines = []

  sort = {"query_time":1, "count":0, "rows_sent":3,"rows_examined":4, "lock_time":2}

  f = LogParamFrm()

  editor= qbuffer.owner
  editor.addToOutput("\n", 1)

  if f.result == mforms.ResultOk:
    if f.rbfile.get_active():
      resultsets = LogFileResultset(f.logfile_path)
      ok= resultsets.open()
      tuples = parseSlowQueryTable(resultsets)
    else:
      editor= qbuffer.owner
      sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"
      resultsets = editor.executeScript(sql)
      tuples = parseSlowQueryTable(resultsets[0])

    if len(tuples) < 12:
      editor.addToOutput( tuples[0], 1)
      return 0

    tuples = sorted(tuples, key=itemgetter(sort[f.sort_method]), reverse=True)
    lines = printFormat(tuples)
    editor.addToOutput('\n'.join(lines), 0)

    if f.rbfile.get_active(): resultsets.close()

  return 0

#-----------------------------------
# format info to print to screen
#------------------------------------
def printFormat(tuples):
  lines=[]
  for item in tuples:
    (count, ttime, tlock, tsent, texam, maxtime, maxstmt, atime, asent, aexam, alock, statement ) = item
    lines.append('Statement: ' + statement + '\n' + 'count: ' + str(count) + '\n' +
                        'average time:' +  str(atime) + '\n' +
                        'slowest sql: ' + maxstmt + '\n' +
                        'slowest time: ' + str(maxtime) + '\n'
                        'average rows sent: ' + str(asent) + '\n'
                        'average rows examined: ' + str(aexam) + '\n'
                        'average lock time: ' + str(alock) + '\n\n'
                     )
  return lines

#----------------------------
def parseSlowQueryTable(result):
  row_dict = {}
  lines= []

  ok = result.goToFirstRow()

  if ok == 0:
    lines.append('invalid file format, User@Host not found in first 20 entries')
    return lines

  while ok:
    sql = result.stringFieldValueByName('sql_text')
    time = result.stringFieldValueByName('query_time')

    if time[9:]!='':
      time = datetime.timedelta(minutes=int(time[0:2]),
        seconds=int(time[3:5]), microseconds=int(time[6:]))
    else:
      time = datetime.timedelta(hours=int(time[0:2]), minutes=int(time[3:5]),
      seconds=int(time[6:8]))
    timelock = result.stringFieldValueByName('lock_time')
    if timelock[9:]!='':
      timelock = datetime.timedelta(minutes=int(timelock[0:2]),
        seconds=int(timelock[3:5]), microseconds=int(timelock[6:]))
    else:
      timelock = datetime.timedelta(hours=int(timelock[0:2]), minutes=int(timelock[3:5]),
        seconds=int(timelock[6:8]))
    sent = int(result.stringFieldValueByName('rows_sent'))

    examined = int(result.stringFieldValueByName('rows_examined'))

      #Use python regular expression engine to replace variables with STRING or
      #DIGIT  For readability, notes on expression are in comments at bottom of this
      #script.  Modify this expression if SQL statements are not correctly grouped.
    sql = re.sub(r'(?<=[\s\(,<>=][\'\"]).*?(?=[\'\"]\)$|[\'\"]$|[\'\"][\,\)]\s|[\'\"]\s[FROM|' +
                         'ORDER|GROUP|AND|WHERE])', 'STRING', sql)

      #  replace all digits with 'DIGIT'
    sql = re.sub(r'(?<=[-+=\s\(,<>])\d.*?(?=[\s\),\)]|$)','DIGIT', sql)

      #determine if SQL is in row_count dict, if not add statement and count, else increment
      #existing entries
    if not sql in row_dict:
      avgtime = time
      avgrowssent = sent
      avgrowsexamined = examined
      avglocktime = timelock
      totaltime = time
      maxsql = result.stringFieldValueByName('sql_text')
      row = [1,totaltime, timelock, sent, examined, time, maxsql, avgtime,
                   avgrowssent, avgrowsexamined, avglocktime, sql]
      row_dict[sql]=row
    else:
      (count, ttime, tlock, tsent, texam, maxtime, maxstmt, atime, asent, aexam,
           alock, sql) = tuple(row_dict[sql])
      count = count + 1
      ttime = ttime + time

      tlock = tlock + timelock
      tsent = tsent + sent
      texam = texam + examined

      if maxtime < time:
        maxtime = time
        maxstmt = result.stringFieldValueByName('sql_text')

      atime = ttime/count
      asent = tsent/count
      aexam = texam/count
      alock = tlock/count

      row = [count, ttime, tlock, tsent, texam, maxtime, maxstmt, atime, asent, aexam, alock, sql]
      row_dict[sql] = row

    ok = result.nextRow()

  for row in row_dict.values():
    lines.append(tuple(row))

  return tuple(lines)

#------function to format time coming from file into 00:00:00000"
def filetimeformat(s):
  parts = s.split('.')
  sformat = ('%(minutes)02d:%(seconds)02d:%(microseconds)05d'
                  %{'minutes':int(parts[0])//60,'seconds':int(parts[0])%60,
                  'microseconds':int(parts[1])})
  return sformat

########################################################################
##
##  LogFileResultset class
##
##
########################################################################

class LogFileResultset:
  def __init__(self, location ):
    self.location = location
    self.__user = ''

  def open(self):
    try:
      self.file = open(self.location, 'r')
      return 1
    except IOError, e:
      return "Error in file IO: ", e

  def goToFirstRow(self):
    ok = self.nextRow()
    return ok

  def __matchTest(self, pattern, text, groupno):
    match = re.search(pattern, text)
    if match:
      return match.group(groupno)
    else:
      return ''

  def nextRow(self):
    x = 0
    sqlline = []

    self.row = {}

    # walk through 20 lines looking for first instance of User@Host
    # if no entry, return 0
    while self.__user == '':
      x = x+1
      self.__user = self.__matchTest('#\sUser@Host:\s(.*)\n', self.file.readline(),1)
      if x == 20:
        return 0

    line = self.file.readline()

    query_time = self.__matchTest('Query_time:\s(.*)\s\sLock_time:\s(.*)\sRows_sent:\s(.*)' +
                                                      '\s\sRows_examined:\s(.*)', line,1)

    if query_time != '':
      query_time = filetimeformat(query_time)
    lock_time= self.__matchTest('Query_time:\s(.*)\s\sLock_time:\s(.*)\sRows_sent:\s(.*)' +
                                               '\s\sRows_examined:\s(.*)', line,2)
    if lock_time != '':
      lock_time = filetimeformat(lock_time)
    rows_sent= self.__matchTest('Query_time:\s(.*)\s\sLock_time:\s(.*)\sRows_sent:\s(.*)' +
                                                  '\s\sRows_examined:\s(.*)', line,3)
    rows_examined= self.__matchTest('Query_time:\s(.*)\s\sLock_time:\s(.*)\sRows_sent:\s(.*)' +
                                                         '\s\sRows_examined:\s(.*)', line,4)

    # if use databasename in log, the timestamp skips a line
    timestamp = self.__matchTest('SET\stimestamp=(\d*);', self.file.readline(),1)
    if timestamp == '':
      timestamp = self.__matchTest('SET\stimestamp=(\d*);', self.file.readline(),1)

    ##this pseudo do..until loop is designed to find the first User@Host entry, which marks a new record.
    ##When we hit User@Host, we save the record in a class variable since it belongs to the next
   ##record.
    while True:
      line = self.file.readline()
      sqlmatch = re.search(r'#\sTime:\s(.*)\n', line)
      if sqlmatch:  #skip Time: entry for SHOW STATUS type statements
        line = self.file.readline()

      sqlmatch = re.search(r'#\sUser@Host:\s(.*)\n', line)
      if sqlmatch:
        self.__user = sqlmatch.group(1)
        break
      if line == '':
        break
      sqlline.append(line)
    sql = ' '.join(sqlline)

    self.row = {'user': self.__user, 'query_time': query_time, 'lock_time':lock_time, 'rows_sent':rows_sent,
                  'rows_examined':rows_examined, 'timestamp':timestamp, 'sql_text':sql.rstrip(';\n')}

      #if any value is missing, empty the dict
    for r in self.row:
      if self.row[r] == '':
        empty = True
      else:  empty = False

    if empty:  return 0
    # if empty:  self.row.clear()

    return 1

  def close(self):
    self.file.close()

  def stringFieldValueByName(self, name):
    return self.row[name]

########################################################################
##
##  Form class
##
##
########################################################################

class LogParamFrm(mforms.Form):

  def __init__(self):
    mforms.Form.__init__(self, None)
    self.logfile_path = ''
    self.sort_method = 'query_time'
    self.result = None

    self.set_title("Log Parameters")

    #bx -- first box as container for others
    bx = mforms.newBox(False)
    bx.set_padding(12)
    bx.set_spacing(12)
    self.set_content(bx)

    #bx components
    lbtitle = mforms.newLabel()
    lbtitle.set_text("Slow Query Log Analyze Options")
    self.rbfile = newRadioButton(1)
    self.rbfile.add_clicked_callback(lambda: self.set_box())
    self.rbfile.set_text("Analyze log file on disk (output=file)")
    self.rbfile.set_active(True)
    self.lbdetail = newLabel("Select an option to analyze a log file for the slow_log table:")
    self.lbdetail.set_enabled(True)
#    self.lbdetail.set_style(mforms.SmallStyle)
    self.rbtable = newRadioButton(1)
    self.rbtable.add_clicked_callback(lambda: self.set_box())
    self.rbtable.set_text("Analyze slow_log table (output=table)")
    bx.add(self.lbdetail, False, True)
    bx.add(self.rbfile, False, True)
    bx.add(self.rbtable, False, True)
    bx.add(lbtitle, False, True)

    #bxfile_path -- list path selection and radio buttons
    bxfile_path = newBox(True)
    bxfile_path.set_spacing(4)
    self.tbfile = newTextEntry()
    self.lbfile = newLabel("   File Path")
    self.btfile = newButton()
    self.btfile.set_text("...")
    self.btfile.enable_internal_padding(False)
    self.btfile.set_enabled(True)
    self.btfile.add_clicked_callback(self.open_file_chooser)

    bxfile_path.add(self.lbfile, False, False)
    bxfile_path.add(self.tbfile, True, True)
    bxfile_path.add(self.btfile, False, False)
    bx.add(bxfile_path, False, True)

    #bxOptions -- sorting and other obtions
    bxoptions = mforms.newBox(True)
    bxoptions.set_spacing(4)
    bx.add(bxoptions, False, True)
    lbsort = newLabel("  Sort")
    self.cbsort = newSelector()
    self.cbsort.set_size(150, -1)
    sort_items = ("query_time", "count", "rows_sent","rows_examined", "lock_time")
    for item in sort_items:
      self.cbsort.add_item(item)
    self.cbsort.add_changed_callback(lambda: self.get_sort_method())
    bxoptions.add(lbsort, False, False)
    bxoptions.add(self.cbsort, False, False)

    #bxOK --- last items on window
    bxOK = mforms.newBox(True)
    bx.add(bxOK, False, True)
    bxOK.set_spacing(4)
    self.ok = mforms.newButton()
    self.ok.set_text("OK")
    self.ok.set_enabled(False)
    bxOK.add_end(self.ok, False, False)
    cancel = mforms.newButton()
    cancel.set_text("Cancel")
    bxOK.add_end(cancel, False, True)

    self.set_size(400, 300)
    self.center()

    self.result = self.run_modal(self.ok, cancel)

#-------open_file_chooser method---------
  def open_file_chooser(self):
    filechooser = FileChooser(mforms.OpenFile)
    filechooser.set_directory(self.logfile_path)
    if filechooser.run_modal():
      self.logfile_path = filechooser.get_path()
#      self.tbfile.set_value(self.logfile_path)
      self.tbfile.set_value(self.logfile_path)
      if self.logfile_path != '':
        self.ok.set_enabled(True)

#-------file path box visibility toggle method------
  def set_box(self):
    if self.rbfile.get_active():
      self.tbfile.show(True)
      self.lbfile.show(True)
      self.btfile.show(True)
      self.ok.set_enabled(True)
      self.ok.set_enabled(False)
    else:
      self.ok.set_enabled(True)
      self.tbfile.show(False)
      self.lbfile.show(False)
      self.btfile.show(False)

#---------------------------------------------------------------------------
  def get_sort_method(self):
    self.sort_method = self.cbsort.get_string_value()

September 15, 2010

MySQL Workbench Plugin: Slow Query Log Statistics

Filed under: MySQL Workbench,python — lstigile @ 2:09 pm
Tags: , ,

This is my first attempt at creating a plugin for MySQL Workbench.  As a first step, I’ve created a plugin that summarizes the slow query log if it’s output to the slow_log table, which is an option available in MySQL version 5.1 or newer.  It’s similar to the mysqldumpslow perl script, except that it doesn’t require perl, which should be more convenient on Windows.  In my next update, the plugin will provide the same summary statistics for the slow query log file on disk.

While the slow query log reports query time, lock time, rows sent and rows examined for each query; it’s often useful to group and aggregate similar queries for analysis.  For example, here’s a sample of the plugin output, which is sorted by count, after just a few clicks on a drupal6 site:

The plugin groups all similar statements by substituting “STRING” and “DIGIT” for SQL parameters.  It then lists the total count, average time, the slowest SQL statement (not parameterized), the slowest execution time, average rows sent, average rows examined and the average lock time.

With 5.1, microsecond logging is only supported for the slow query log if it is output to file on disk.  Nevertheless, you can still glean very useful information by sorting by “rows examined” and comparing it to the rows actually sent.  A high ratio of rows examined to rows sent may indicate improper indexing.  The following output is sorted by “rows examined”

REQUIREMENTS
MySQL 5.1
Slow Query Log Enabled,  log-out=TABLE
( http://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html )

MySQL Workbench 5.2.27 or new version. Prior versions had a bug related to the field name passed from the GRT.

CODE
To install the plugin, click on ”view source” and copy the contents into a file named slowquerystats_sort_grt.py on your local computer or download the file To install the plugin, select Scripting | Install Module | Script File from the main menu. Select the new plugin file and click open.

# Slow Log Stats custom plugin
# Sept 9, 2010
# This plugin groups similar queries and sorts them based on selection
# Requirements: Log slow queries = ON, log_output = TABLE.   Workbench version 5.2.27 or higher
#
from wb import *
import grt
import re
import datetime
from operator import itemgetter

# define this Python module as a GRT module
ModuleInfo = DefineModule(name= "SlowQueryStats", author= "lstigile", version="0.1")

@ModuleInfo.plugin("slowtablestats_time", caption= "Slow Query Table Stats by Time", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
def executeSlowTableStatsTime(qbuffer):
  lines=[]
  editor= qbuffer.owner
  editor.addToOutput("\n", 1)
  sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"

  resultsets= editor.executeScript(sql)

  tuples = parseSlowQueryTable(resultsets)
  tuples = sorted(tuples, key=itemgetter(2), reverse=True)

  lines = printFormat(tuples)

  editor.addToOutput("Slow Query Statistics By Time \n", 1)
  editor.addToOutput('\n'.join(lines), 0)
  return 0

@ModuleInfo.plugin("slowtablestats_rows_sent", caption= "Slow Query Table Stats by Rows Sent", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
def executeSlowTableStatsSent(qbuffer):
  lines=[]
  editor= qbuffer.owner
  editor.addToOutput("\n", 1)
  sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"

  resultsets= editor.executeScript(sql)

  tuples = parseSlowQueryTable(resultsets)
  tuples = sorted(tuples, key=itemgetter(5), reverse=True)

  lines = printFormat(tuples)

  editor.addToOutput("Slow Query Statistics By Rows Sent \n", 1)
  editor.addToOutput('\n'.join(lines), 0)
  return 0

@ModuleInfo.plugin("slowtablestats_rows_examined", caption= "Slow Query Table Stats by Rows Examined", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
def executeSlowTableStatsExamined(qbuffer):
  lines=[]
  editor= qbuffer.owner
  editor.addToOutput("\n", 1)
  sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"

  resultsets= editor.executeScript(sql)

  tuples = parseSlowQueryTable(resultsets)
  tuples = sorted(tuples, key=itemgetter(6), reverse=True)

  lines = printFormat(tuples)

  editor.addToOutput("Slow Query Statistics By Rows Examined \n", 1)
  editor.addToOutput('\n'.join(lines), 0)
  return 0

@ModuleInfo.plugin("slowtablestats_rows_locked", caption= "Slow Query Table Stats by Rows Locked", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
def executeSlowTableStatsLocked(qbuffer):
  lines=[]
  editor= qbuffer.owner
  editor.addToOutput("\n", 1)
  sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"

  resultsets= editor.executeScript(sql)

  tuples = parseSlowQueryTable(resultsets)
  tuples = sorted(tuples, key=itemgetter(7), reverse=True)

  lines = printFormat(tuples)

  editor.addToOutput("Slow Query Statistics By Rows Locked \n", 1)
  editor.addToOutput('\n'.join(lines), 0)
  return 0

@ModuleInfo.plugin("slowtablestats_count", caption= "Slow Query Table Stats by Count", input= [wbinputs.currentQueryBuffer()], pluginMenu= "SQL/Utilities")
@ModuleInfo.export(grt.INT, grt.classes.db_query_QueryBuffer)
def executeSlowLogStatsCount(qbuffer):
  lines=[]
  editor= qbuffer.owner
  sql = "SELECT * FROM mysql.slow_log order by start_time desc limit 10000;"
  resultsets= editor.executeScript(sql)
  editor.addToOutput("\n", 1)
  tuples = parseSlowQueryTable(resultsets)
  tuples = sorted(tuples, key=itemgetter(1), reverse=True)

  lines = printFormat(tuples)

  editor.addToOutput("Slow Query Statistics By Count \n", 1)
  editor.addToOutput('\n'.join(lines), 0)
  return 0

#-----------------------------------
# format info to print to screen
#------------------------------------
def printFormat(tuples):
  lines=[]
  for item in tuples:
    (statement, count, avg, max_statement, max_time, rows_sent, rows_examined, lock_time) = item
    lines.append('Statement: ' + statement + '\n' + 'count: ' + str(count) + '\n' +
                        'average time:' +  str(avg) + '\n' +
                        'slowest sql: ' + max_statement + '\n' +
                        'slowest time: ' + str(max_time) + '\n'
                        'average rows sent: ' + str(rows_sent) + '\n'
                        'average rows examined: ' + str(rows_examined) + '\n'
                        'average lock time: ' + str(lock_time) + '\n\n'
                     )
  return lines

#----------------------------
def parseSlowQueryTable(resultsets):
  for result in resultsets:
    row_count = {}
    totaltime = {}
    maxtime = {}
    avgtime = {}
    maxstmt = {}
    totalrowssent = {}
    avgrowssent = {}
    totalrowsexamined = {}
    avgrowsexamined = {}
    totallocktime = {}
    avglocktime = {}
    lines= []

    ok = result.goToFirstRow()

    while ok:
      sql = result.stringFieldValueByName('sql_text')
      time = result.stringFieldValueByName('query_time')
      time = datetime.timedelta(hours=int(time[0:2]), minutes=int(time[3:5]),
        seconds=int(time[6:9]))
      timelock = result.stringFieldValueByName('lock_time')
      timelock = datetime.timedelta(hours=int(timelock[0:2]), minutes=int(timelock[3:5]),
        seconds=int(timelock[6:9]))
      sent = int(result.stringFieldValueByName('rows_sent'))
      examined = int(result.stringFieldValueByName('rows_examined'))

      #Use python regular expression engine to replace variables with STRING or
      #DIGIT  For readability, notes on expression are in comments at bottom of this
      #script.  Modify this expression if SQL statements are not correctly grouped.
      sql = re.sub(r'(?<=[\s\(,<>=][\'\"]).*?(?=[\'\"]\)$|[\'\"]$|[\'\"][\,\)]\s|[\'\"]\s[ORDER|GROUP|AND|WHERE])','STRING', sql)

      #  replace all digits with 'DIGIT'
      sql = re.sub(r'(?<=[-+=\s\(,<>])\d.*?(?=[\s\),\)]|$)','DIGIT', sql)

      #determine if SQL is in row_count dict, if not add statement and count, else increment
      #existing entries
      if not sql in row_count:
        row_count[sql] = 1
        totaltime[sql] = time
        totallocktime[sql] = timelock
        totalrowssent[sql] = sent
        totalrowsexamined[sql] = examined
        maxtime[sql] = time
        maxstmt[sql] = result.stringFieldValueByName('sql_text')
      else:
        row_count[sql] = row_count[sql] + 1
        totaltime[sql] = totaltime[sql] + time
        totallocktime[sql] = totallocktime[sql] + timelock
        totalrowssent[sql] = totalrowssent[sql] + sent
        totalrowsexamined[sql] = totalrowsexamined[sql] + examined
        if maxtime[sql] < time:
          maxtime[sql]=time
          maxstmt[sql] = result.stringFieldValueByName('sql_text')

      avgtime[sql] = totaltime[sql]/row_count[sql]
      avgrowssent[sql] = totalrowssent[sql]/row_count[sql]
      avgrowsexamined[sql] = totalrowsexamined[sql]/row_count[sql]
      avglocktime[sql] = totallocktime[sql]/row_count[sql]

      ok= result.nextRow()

    for row in row_count:
      lines.append((row,row_count[row],avgtime[row],maxstmt[row],maxtime[row],
        avgrowssent[row],avgrowsexamined[row],avglocktime[row]))

#  list = [tuple(item.split(",")) for item in lines]
    tuples = tuple(lines)

  return tuples

###explanation of regular expression
    #  "?<="  search for one of the following but don't include it in the returned value:
    #  "[\s\\='\(<>]"  a space, single quote, paren, greater than or less than
    #  "[\'\"])" followed by a text deliminer of single or double quote
    #  ".*?"  return one or more words that match up to the next delimiter:
    #  "(?=[\'\"]$|[\'\"][\s\)])"  ?= means to not include the following filter in the return value
    #   search to the delimiter, but only stop if it's the end of the line or followed by a space
    #   or a paren.  This helps capture strings with a single-quote embedded within

RUN THE PLUGIN
To run the plugin, you’ll need to open the SQL Editor. For now, only the SQL Editor enables the plugin menu. To open the Editor, from the Workbench home page, select the option to “open Connection to Start Querying” or by selecting Database | Query Database from the main menu.

Once you’re in the SQL Editor, you should see the plugins under Plugins | Utilities

New Plugins Available From Menu

If you select one of the options and don’t see any results in the output screen, it’s likely that you haven’t output the slow query log to the slow_log table in the mysql database.  In order to run this plugin, you should see results when you execute “SELECT * FROM mysql.slow_log limit 5″
A few notes for other folks interested in developing plugins:
I found it much easier to develop the entire script in Python and then to convert it to a plugin.  In case it helps, the following includes my independent Python code:

mysqlexec.py

import sys
import MySQLdb

def myexec(sql):
  my_host = "hostIP"
  my_user = "user"
  my_pass = "pwd"
  my_db = "mysql"

  try:
    db = MySQLdb.connect(host=my_host, user=my_user, passwd=my_pass, db=my_db)
  except MySQLdb.Error, e:
    print "Error %d: %s" % (e.args[0], e.args[1])
    sys.exit (1)

  cursor = db.cursor()
#  print "in func line 17"
  cursor.execute(sql)
  resultset = cursor.fetchall()

  db.close()

  return resultset

slowquery.py

import mysqlexec
import re
import datetime
from operator import itemgetter

def slowlogstats(sql):
###def slowlogstats(qbuffer):
 ## editor= qbuffer.owner
 ## sql= qbuffer.selectedText or qbuffer.script
##  resultsets= editor.executeScript(sql)
###  editor.addToOutput("\n", 1)

  try:
    resultsets = mysqlexec.myexec(sql)
  except Exception, e:
    raise Exception("Error fetching log contents: %s" % e)

  row_count = {}
  totaltime = {}
  maxtime = {}
  maxstmt = {}
  avgtime = {}
  lines = []

  for result in resultsets:
    sql= result[0]
    time = result[1]

    ###explanation of regular expression
    #  "?<="  search for one of the following but don't include it in the returned value:
    #  "[\s\\='\(<>]"  a space, single quote, paren, greater than or less than
    #  "[\'\"])" followed by a text deliminer of single or double quote
    #  ".*?"  return one or more words that match up to the next delimiter:
    #  "(?=[\'\"]$|[\'\"][\s\)])"  ?= means to not include the following filter in the return value
    #               search to the delimiter, but only stop if it's the end of the line or followed by a space
    #               or a paren.  This helps capture strings with a single-quote embedded within
#    sql = re.sub(r'(?<=[\s\'\"\(,<>=][\'\"]).*?(?=[\'\"]$|[\'\"][\s\),\)])','STRING', sql)
    sql = re.sub(r'(?<=[\s\(,<>=][\'\"]).*?(?=[\'\"]\)$|[\'\"]$|[\'\"][\,\)]\s|[\'\"]\s[ORDER|GROUP|AND|WHERE])','STRING', sql)
      #  replace all digits with 'DIGIT'
    sql = re.sub(r'(?<=[-+=\s\(,<>])\d.*?(?=[\s\),\)]|$)','DIGIT', sql)

    if not sql in row_count:
      row_count[sql] = 1
      totaltime[sql] = time
      maxtime[sql] = time
      maxstmt[sql] = result[0]
    else:
      row_count[sql] = row_count[sql] + 1
      totaltime[sql] = totaltime[sql] + time
      if maxtime[sql] < time:
        maxtime[sql]=time
        maxstmt[sql] = result[0]

    avgtime[sql] = totaltime[sql]/row_count[sql]

  #sorted_sql = sorted(row_count, key=row_count.get, reverse=True)
  #sorted_sql = sorted(avgtime, key=avgtime.get, reverse=True)

  for row in row_count:
    lines.append((row,row_count[row],avgtime[row],maxstmt[row],maxtime[row]))

#  list = [tuple(item.split(",")) for item in lines]
  tuples = tuple(lines)

  return tuples

def main():
  lines=[]
  tuples = slowlogstats("SELECT sql_text, query_time FROM mysql.slow_log order by query_time DESC limit 5")

  tuples = sorted(tuples, key=itemgetter(1), reverse=True)
  for item in tuples:
    (statement, count, avg, max_statement, max_time) = item
    lines.append('Statement: ' + statement + '\n' + 'count: ' + str(count) + '\n' +
                        'average time:' +  str(avg) + '\n' +
                        'slowest sql: ' + max_statement + '\n' + 'slowest time: ' +
                        str(max_time) + '\n\n')
  print lines

if __name__ == '__main__':
  main()

Also, I borrowed heavily from existing plugins and modules including the plugin to execute a query to vertical text: http://wb.mysql.com/?p=677

Note: I’m new to Python, so I’d gladly accept any criticism regarding the script itself.

I hope someone finds this useful or is at least inspired to build their own plugin. I look forward to posting my next version to include a summary of the slow query log files on disk.

June 24, 2010

Sure-fire MySQL Install on Windows

Filed under: MySQL Install,Windows — lstigile @ 10:07 pm
Tags: , ,

Due to firewalls, virus scans, corporate security restrictions, or just plain bad luck; there are times when MySQL just won’t install on a specific Windows server.  Here’s a sure-fire install method.   Relax, I won’t have you run the msi installer again.

The first step is to remove directories from your previous install attempts.  Uninstall from the control panel.  Manually, rename or delete “C:\Program Files\MySQL\MySQL Server 5.1″.  Next, delete (or rename) the data directory.  Warning! If you have previously entered data into MySQL, deleting the data directory will delete data.  The directory is located at “C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.1\data”.   You may need to unhide the data directory.

Let’s get started.

#1.  Download the no-install MySQL Version for Windows.  Choose either “Windows (x86, 32-bit) ZIP Archive” or “Windows (x86, 64-bit) ZIP Archive” based on whether your system is 64 or 32-bit .

#2.  Unzip the files to “C:\Program Files\MySQL\MySQL Server 5.1″.

#3.  Use notepad or wordpad to create a my.ini file and save the file in the MySQL directory, “C:\Program Files\MySQL\MySQL Server 5.1″   Be careful that notepad doesn’t add a default “.txt” extension to the my.ini file ( http://support.microsoft.com/kb/253688 )

Add the following following to your my.ini file and save.

[mysqld]
##update basedir if you move the install location
basedir="C:/Program Files/MySQL/MySQL Server 5.1"

##update datadir if you move the data directory
datadir="C:/Program Files/MySQL/MySQL Server 5.1/data"

## increase to 40% of RAM if using MyISAM
key_buffer_size=32MB

## increase to 80% of available RAM for production usage
innodb_buffer_pool_size = 128MB

## may want to increase if write intensive
innodb_log_file_size = 64MB
innodb_log_buffer_size=8MB
table_cache=1024
thread_cache=16
query_cache_size=32M

Note the basedir and the datadir variables.  If you wish to install in a different location or put the data in a different location, update these values accordingly.

#3.  Create and start the service.    Open a command prompt, and enter the following:

"C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqld"  --install "MySQL 5.1" --defaults-file="C:\Program Files\MySQL\MySQL Server 5.1\my.ini"

This installs the server.  To start the service, type

net start "MySQL 5.1"

or start the service from the control panel.

You should be up and running.  If not, check your err log in “C:\Program Files\MySQL\MySQL Server 5.1\Data”

#4.  Connect from your client application and update your user accounts.  By default, there are 3 accounts: root@localhost, root@127.0.0.1 and an anonymous account.

The following commands log in with the mysql command-line client, remove the anonymous account and set the password for the root accounts.

At a command prompt, enter the following:

"c:\program files\mysql\mysql 5.1 server\bin\mysql" -uroot

This will log you into MySQL and you should have a mysql command prompt like mysql>

Here are the commands to delete your anonymous account and set passwords for your two root accounts.  When setting the password, substitute your desired password for “mypassword”.


mysql> DROP USER ""@localhost;
mysql> Set PASSWORD FOR "root"@"localhost" = password("mypassword");
mysql> Set PASSWORD FOR "root"@"127.0.0.1" = password("mypassword");
mysql> flush privileges;
mysql> exit

Feel free to post any questions/comments or issues to the comment section of this blog.  For more comprehensive instructions see http://dev.mysql.com/doc/refman/5.1/en/windows-install-archive.html

June 14, 2010

Enable MySQL Enterprise Plugin for Connector/NET

Figure 1. Source Location

In a prior post ( Trace SQL From Database to Source Code ), I showed how to enable SQL trace capabilities for java/MySQL application to trace SQL statements from the database to the exact line of code from which the statement was executed (see Figure 1).  In this post, I’ll enable SQL tracing in the sample C# application, which is included with the MySQL Connector/NET (MySQL’s ADO.NET provider ) install.

The following instructions assume that the MySQL Enterprise Agent and Monitor is already installed.  The Monitor is available for support customers at http://customer.mysql.com or a trial is available at http://www.mysql.com/trials .

Step #1.  Download and install the Connector/NET and the MySQL Enterprise Plugin for Connector/NET

Step #2.  Copy the plugin, MySQL.MonitorPlugin.dll to your application directory.  In my case, I’m building and debugging the sample application using Microsoft Visual C# 2008 Express Edition.  The MySQL.MonitorPlugin.dll needs to reside in the same directly as the application’s executable.  Copy the dll to C:\Program Files\MySQL\MySQL Connector Net 6.2.3\Samples\Table Editor\cs\bin\Debug

Step #3.  Open the sample project TableEditor.csproj ( C:\Program Files\MySQL\MySQL Connector Net 6.2.3\Samples\Table Editor\cs\TableEditor.csproj ) in Microsoft Visual Studio and add an App.config file to the project.  For this exercise, I’m using Microsoft Visual C# 2008 Express Edition.

To add the App.config file, select Solution Explorer on the View menu, right-click on TableEditor, the project name.  Point to Add, New Item, and choose Application Configuration File.

In Solution Explorer, double-click App.config to open the file.  Replace the contents of the file with the XML below.  Note: replace the items in brackets [ ] with the appropriate information for your server.  (Omit the brackets) Likewise, the UserID and Password should match the credentials that the agent uses to authenticate to the Monitor.

Copy and paste the following into App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="mysql" switchName="SourceSwitch"    switchType="System.Diagnostics.SourceSwitch">
        <listeners>
          <add name="EMTrace" type="MySql.EMTrace.EMTraceListener, MySql.MonitorPlugin"
                    initializeData=""
            Host="[http://yourServer.com:18080]"
            PostInterval="60"
            UserId="[agent]"
            Password="[mysql]"/>
          </listeners>
      </source>
    </sources>
    <switches>
      <!-- You can set the level at which tracing is to occur -->
      <add name="SourceSwitch" value="All"/>
    </switches>
  </system.diagnostics>
</configuration>

[Note: if you're following the readme or documentation, you'll notice that I omitted the <system.data> section.  I believe this to be an error in the documentation.  In my testing, it never works with the <system.data> entry.]

Step #4.  Modify the connection string to include “logging = true”

Here’s the full connection string that I used while running the sample application:

string connStr = String.Format(“server={0};user id={1}; password={2}; database=mysql; port=3307; pooling=false; logging=true”,server.Text, userid.Text, password.Text );

Step #5.  Compile the application ( Debugging | Start Debugging (F5) ).   On Form1, enter the appropriate Server, User ID and Password for the MySQL Server and toggle the databases and tables.  Watch the Output window, select Output from the View menu in the IDE, and you should start seeing SQL debug information, which will be passed to the MySQL Enterprise Monitor.  In the App.config, the PostInterval is set to 30, meaning that it will take 30 seconds before the data is sent to the monitor.

If you’re a .NET developer, please give it a try.  The query analyzer provides a great view into your application and the database.

June 11, 2010

Trace SQL From the Database to the Source Code with MySQL Enterprise Monitor

Filed under: Enterprise Monitor — lstigile @ 4:10 pm
Tags: , , , ,

OK, you found the problem SQL statement that was affecting your server’s performance, now where did it originate?

The new MySQL Enterprise Plugins for Connector/J and Connector/NET send query statistics, including the source location for each query, directly to the MySQL Enterprise Monitor.

Figure 1 is a screenshot of new source location feature.

Figure 1. Source Location

Figure 2 shows the standard query statistics, which are collected in the query analyzer.  In both cases, the statistics are gathered by the MySQL Connector and the Plugin, not MySQL proxy.

Figure 2. Query Analyzer

If you’re a MySQL Enterprise customer, you can download the new monitor and plugins from customer.mysql.com.  If you’d like to try it out, a 30 day trial is available at http://www.mysql.com/trials/

Please review the help file and readme for full instructions, but here are a few of the highlights for setting up monitoring for Java applications.  (I’ll provide further instructions for .NET in a future post.)

1.  Set up the MySQL Enterprise Monitor and Agent.  The agent resides on the MySQL production server and the monitor should reside on a non-production server within your environment.  Although the Plugin gathers the query statistics, the agent needs to run and connect to the monitor at least once so the Monitor can associate the data with the appropriate server.

2.  Download the following files from the customer portal (customer.mysql.com) and add them to your classpath:

  • Connector/J version 5.1.12 or newer
  • Plugin for Connector/J c-java-mysql-enterprise-plugin-1.0.0.42.jar

3. Change your JDBC url.  In my case, I changed it from the standard JDBC url, “jdbc:mysql:127.0.0.1:3306/sakila , to

“jdbc:mysql://127.0.0.1:3306/sakila?statementInterceptors=com.mysql.etools.jdbc.StatementPerformanceCounters& amp;serviceManagerUrl=http://[mymonitorURL.com]&serviceManagerUser=[myagentusername]& serviceManagerPassword=[myagentpassword]l”;

Full instructions are in the readme.  Substitute appropriate URL and credentials for the bracketed [ ] items above.


4. Finally, the mysql user account that is used by the application needs to have SELECT privilege to the mysql.inventory table.  This table includes a unique identifier which the monitor uses to match the data with the MySQL instance.  I executed the following:

mysql> GRANT SELECT ON mysql.inventory TO ‘elgato’@'%’;

I found it helpful to monitor the application log file for error messages.   The readme includes additional logging information.  In my tomcat test application, the log information was sent to /var/logs/tomcat6/daemon.log file.

I hope this is helpful information.  If you have any issues, feel free to post a comment on this blog post or post on the forums:  http://forums.mysql.com/list.php?166

December 2, 2009

Launch MySQL from the Command Line

Filed under: MySQL Install — lstigile @ 10:49 pm
Tags:

In my last blog entry, I discussed a “silent” install method for deploying MySQL on Windows.  However, the best way to distribute MySQL may be to not install it.  Instead, design the application to launch mysql from the command line.  With this method, you can deploy with two files, avoid registry entries, and hide database files from end-users and their IT staff.

[Note:  these instructions may also be useful if you can't get the MySQL installer to run on a particular Windows machine.  I find it easier to copy the system tables from an existing install of MySQL that was deployed with the Windows MSI installer.  Nevertheless, you could download the "without installer" MySQL zip file and use the mysql_system_tables.sql and mysql_system_table_data.sql to create your system tables.  I had to remove the "WHERE @had_user_table=0" from the mysql_system_table_data.sql to get it to work.  The SC.exe tool can be used to add it to the services]

The following is a minimum install example.  I prefer to run the full install using the Windows Essentials or the Windows MSI Installer download from http://dev.mysql.com/downloads/mysql/5.1.html#win32 .  Stop the service and use the files that were generated from the install to set up MySQL on your target machine.

Step 1.  Create an empty directory on the target server.  I created c:\min.  From an existing MySQL install, copy the following files into c:\min:

mysqld.exe

errmsg.sys

In my unmodified 5.1 version, mysqld.exe is 8.2 MB and errmsg.sys is 36K.  The following command will launch the full version of the server with only the aforementioned files:

Step 2.  c:\min>mysqld.exe --language=. --datadir=. --skip-grant-tables --bind-address=localhost

That’s it.  MySQL is running and you can now connect and create tables.

I used the following server options:

--language=.
This is an essential parameter.  Your install needs to find errmsg.sys to start.  I retrieved this file from the “share” folder in an existing installation.

--datadir=. This determines the location of the data directory.  In my simple example, I put the data files in the application directory.  You may prefer to specify a data directory in the recommended Windows application directory.

--skip-grant-tables This variable allows MySQL to load without the privileges table.  Anyone can connect without authenticating.   I’ve specified --bind-address for only local connections. If you’re deploying to a server in a secure location on a private network and MySQL is only running when your application is running, this may be acceptable.

--bind-address=localhost  This option determines the IP address for which to bind, it will only listen on localhost.  As an alternative, it’s possible to run MySQL on windows with named pipes or shared memory.

To force authentication, you will need to include the privilege tables.  In this case, create a data directory.  In my case I used c:\min\data .  Then, copy the mysql folder from an existing install into the data directory.  Then, start MySQL and specify the new data directory:

c:\min>mysqld.exe --language=. --datadir=./data

Since these are MyISAM files, MySQL will recognize the additional database and files.  Also, omit the --skip-grant-tables parameter.  Now, you’ll be prompted for a password.  It will be the same password that you specified when you ran the full install.

MySQL is running.  What’s next?  If you’re deploying an application, you’ll need to deploy your initial schema and data.  The simplest way would be to dump the schema and tables from your development machine and then load in the sql script on your target server.  Documentation for dumping a loading and database can be found here: http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html

There are many startup options and system variables which can be specified on the command line or the configuration file (--defaults-file).

Here are the appropriate links:

http://dev.mysql.com/doc/refman/5.1/en/server-options.html

http://dev.mysql.com/doc/refman/5.1/en/using-system-variables.html

If only using MyISAM, you might want to start MySQL with --skip-innodb.  You can use these options to make your installation more secure. You may want to change the default TCP/IP port.  If using InnoDB, you can set innodb-data-file-path to a different location, a more obscure filename and a different size.  This would prevent someone from copying the data to another computer and trying to access the data with a different application.

There are other ways to deploy MySQL with your application, including using the “silent” install method or using the deeply embedded library (libmysqld).  If you’d prefer that your customer has full visibility and access to MySQL, a “silent” install adds MySQL to the program files, service control panel and the Add/Remove program options.  If you’re developing a stand-alone application in C/C++, the deeply embedded library (libmysqld) provides excellent performance with a small footprint. Otherwise, your best option may be to  launch MySQL from the command line.  This method provides a simple and lightweight deployment with all the MySQL server capabilities.

November 2, 2009

Silent Install Instructions

Filed under: Uncategorized — lstigile @ 10:50 pm
Tags: , ,

This entry provides instructions for “silently” installing MySQL on a Microsoft Windows server.  I use the term “silent install” to describe the method of passing parameters to the Microsoft Installation Package (MSI) to bypass installation screens.  This allows you to deploy MySQL with your application and to install MySQL without end-user intervention.  The developer can define the install location, the appropriate storage engines, the root password and the server configuration.

Most application developers use some sort of software for creating an application installer.  Common tools include Install Shield, Wise Installation, InstallBuilder and Windows Installer.  These installation software tools can also be used to install MySQL by launching the MySQL MSI and the MySQL Instance configuration utility ( MySQLInstanceConfig.exe ) from the command-line.

To get started, download the Windows Essentials from http://dev.mysql.com/downloads.

Then, look for an option in your installation tool to execute the following:

C:\>msiexec /q /log install.txt /i mysql-advanced-5.1.32-win32.msi datadir=”c:\installs\myapp” installdir=”c:\installs\myapp”

The MSI installer will add MySQL to the start menu, add registry values and add MySQL to the add/remove programs option from the control panel.

To configure the server, prepare the my.ini, set the root password and start the service, you’ll need to execute the following:

C:\>MySQLInstanceConfig.exe -i -q “-lC:\mysql_install_log.txt” “-nMySQL Server 5.1.234” -pC:\installs\myapp”   -v5.1.234  “tc:\installs\myapp\my-small.ini” “-cC:\mytest.ini ServerType=DEVELOPMENT DatabaseType=MIXED ConnectionUsage=DSS Port=3311 ServiceName=MySQLCust RootPassword=1234

This installation method has the benefit of adding MySQL to the control panel and the startup menus.  It also installs the default mysql tables.  The only remaining task is to import your application tables into MySQL by running mysqldump ( http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html ) on your development server and then loading the dump files into the target server.

In a future blog entry, I’ll discuss launching the mysqld.exe directly without actually installing MySQL.

June 17, 2009

MySQL Windows Password Reset

Filed under: Uncategorized — lstigile @ 8:14 pm

If you receive a 1045 error, you may need to reset your password.  Here are the official instructions for resetting the password: http://dev.mysql.com/doc/refman/5.1/en/resetting-permissions.html

If you find these instructions confusing, here’s another method.  

1.  Find the my.ini file (my.cnf for linux).  On windows, the my.ini file is typically in your install directory: c:\Program Files\MySQL\MySQL Server 5.1\my.ini   Open the file with notepad (or any text editor) and add the following entry under the [mysqld] section:

skip-grant-tables

Then, save the file and restart the MySQL service.  This has disabled the grant tables so you should be able to log into mysql without any password ( no more 1045 errors ).  Now, we need to reset the password.  

2.  Log into MySQL with the MySQL command-line utility.  This can be found in the MySQL bin directory.  No user or password parameters are required since grant tables have been disabled.  Execute the following statement:

UPDATE mysql.user SET PASSWORD = PASSWORD(”) WHERE user = ‘root’ and host=’localhost’;

This example will replace the root password with an empty string or no password.  Feel free to substitute the appropriate password in the PASSWORD(”) function or leave it blank and change the password later.

3.  Remove “skip-grant-tables’ from the my.ini file and restart the MySQL service.

4.  Log into mysql.  Example: shell>mysql -u root

I hope this helps.

June 15, 2009

MySQL Windows Install Troubleshooting

Filed under: Uncategorized — lstigile @ 9:04 pm
Tags: , , , ,

If you’re having trouble installing MySQL 5.1 on a Windows server, I’d recommend the following process.  Don’t skip the first steps that describe the uninstall and removal of the previous install as this seems to clear up most issues.   For the uninstall, I’d recommend manually deleting data files.  Caution: deleting the data files will delete any data previously entered into MySQL.

1.  Remove previous MySQL installs (and remnants)

a.  Stop the MySQL service ( Start | Control Panel | Administrative Tools | Services)

Picture 2

b.  Remove MySQL using Windows Add or Remove Programs (Start | Control Panel | Add or Remove Programs)

Picture 3
c.  Clean up (delete) the data directory  (assuming that you don’t have data that you need) c:\documents and settings\all users\application data\mysql
 

Picture 4

 

d.  Clean up (delete) any remaining directories under program files c:\program files\MySQL\MySQL Server 5.1 (or whatever version you’re using)

 

————–

That’s it for the removal and clean up.  Now, we’ll begin the install.

 

2.  The MySQL install is a two step process: running the installer (.MSI) and then running the MySQL configuration utility (MySQLInstanceConfig.exe).  Theoretically, the first step should install the data files and program files and the second step should set up the service, set the root password and start the service.  The installer is designed to call the configuration utility automatically.   Instead, I recommend running each process separately.   

 

a.  Launch the installer from a command-line.  Here’s an example shell>msiexec /log c:\install.txt /i mysql-5.1.34-win32.msi 

 

Note: You’ll need to change the name of the .msi file to match the name of the version that you’re installing.  If you’re not used to working at the command-line, you’ll either need to change the directory to the location of the mysql installation file or specify the full path.  For example, you may need to execute:  msiexec /log c:\install.txt /i “c:\Documents and Settings\Administrator\Desktop\mysql-5.1.34-win32.msi”

 

Continue through the wizard until you reach the final screen which says, “Wizard Completed”  There’s a checkbox to “Configure the MySQL Server now”.  Uncheck the box and select Finish.

 

 

Uncheck the Configure box.

Uncheck the Configure box.

 When the install has finished, please check c:\documents and settings\all users\application data\mysql\mysql server 5.1\data .  You should see a mysql and test directory.  

 

Also, verify that files have been installed in c:\program files\MySQL\MySQL Server 5.1 

You should have a bin, Docs and share folder.  

 

b.  Navigate to c:\program files\MySQL\MySQL Server 5.1\bin and launch the MySQLInstanceConfig.exe from a command-line.  Here’s an example.

shell>MySQLInstanceConfig.exe “-lC:\mysql_install_log.txt” “

 

I suggest launching it from a command-line so that you can pass the -l parameter, which will create an install log in case you need to troubleshoot the problem.

 

Step through the wizard.  Make sure to remember the password that you assign in the wizard.

 

The install should complete successfully.

 

TROUBLESHOOTING

What if it didn’t work?

 

1.  Check the service from the control panel.  Is it running?  If so, try to log into MySQL.  I’ve seen instances where an error was received, but it completed successfully.  If it’s not running, try to start it and please post the complete error message.

 

2.  Check the error log.  It’s in C:\Documents and Settings\All Users\Application Data\MySQL\MySQL Server 5.1\data   It has an .err extension and you should be able to open it with notepad.  Ignore any errors about the innodb plugin not loading.  It’s a symptom, not the problem.  Look for an error about missing data files or a mismatch in file sizes.

 

3.  1045 error?  It should indicate Password: No or Password:yes.  If “yes”, it’s not a port issue, not a firewall issue — it’s a password issue.  It’s usually caused by not deleting out the data files from an old install.  The password is kept in the data directory and it’s likely that the password you entered during the previous install doesn’t match what you’re entering now.  You can either re-install after deleting all the files or try to reset your root password.  (resetting root password: http://dev.mysql.com/doc/refman/5.1/en/resetting-permissions.html )

 

4.  Still stuck?   In the instructions above, I specified c:\install.txt and c:\mysql_install_log.txt  Please check those and post the contents.


Next Page »

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.