Re: Implement a logging table; avoiding conflicting inserts
Given: MySQL 4.0.12, I need to implement a pageview log with a resolution of 1 day. I propose this table: CREATE TABLE `pageviews` ( `id` int(11) NOT NULL auto_increment, `date` date NOT NULL default '-00-00', `url` char(120) NOT NULL default '', `views` mediumint(9) NOT NULL default '0', PRIMARY KEY (`id`), UNIQUE KEY `date` (`date`,`url`), KEY `url` (`url`) ) TYPE=InnoDB; So that an update will look like: UPDATE pageviews SET views=views+1 WHERE date='DATE' AND url='ARTIST' Of course I need to INSERT the record if one does not match my WHERE. This would be easy if I had 4.1 -- INSERT ... ON DUPLICATE KEY UPDATE, I think -- but I do not. Would the REPLACE method work? David
RE: Implement a logging table; avoiding conflicting inserts
Given: MySQL 4.0.12, I need to implement a pageview log with a resolution of 1 day. .. Would the REPLACE method work? David Hmmm...as I read the docs, the LOCK IN SHARED MODE seemed to be the real key to this. I created a test script and ran: $ ab -n100 -c100 localhost/hits.php Where hits.php looks like: ?php /** * Import db connection parameters */ require $_SERVER['DOCUMENT_ROOT'] . '/generic/app_global.inc.php'; $err = null; ($date = $_GET['date']) or ($date = date('Y-m-d')); ($url = $_GET['url']) or ($url = $_SERVER['HTTP_REFERER']); /** * For testing, get a random date and URL */ $dates = array( '2007-09-11', '2007-09-12', '2007-09-13', ); $urls = array( 'URL A', 'URL B', 'URL C', ); shuffle($dates); shuffle($urls); $date = pos($dates); $url = pos($urls); /** * Connect */ $dblink = mysql_connect($page_options['host_main'],$page_options['host_main_user'] ,$page_options['host_main_pass']); mysql_select_db('articles',$dblink); /** * BEGIN TRANSACTION */ $rs = mysql_query('START TRANSACTION',$dblink); $debug = 'Begin Transaction said:'.mysql_error($dblink).''; error_log($debug.\n, 3, '/tmp/errors.log'); // see: http://dev.mysql.com/doc/refman/4.1/en/innodb-next-key-locking.html $sql = SELECT views FROM pageviews WHERE date='.mysql_escape_string($date).' AND url='.mysql_escape_string($url).' LOCK IN SHARE MODE; /** * If NO records are returned, we need to INSERT with our first pageview */ $rs = mysql_query($sql,$dblink); if( mysql_num_rows($rs) == 0 ) { $sql = INSERT INTO pageviews SET views=1, date='.mysql_escape_string($date).', url='.mysql_escape_string($url).'; } else { $sql = REPLACE INTO pageviews SET views=.(intval(mysql_result($rs,0,'views'))+1)., date='.mysql_escape_string($date).', url='.mysql_escape_string($url).'; //$sql = UPDATE pageviews SET views=views+1 WHERE date='.mysql_escape_string($date).' AND url='.mysql_escape_string($url).'; } echo $sql; $rs = mysql_query($sql,$dblink); /** * Barely error-checking... */ if ( mysql_affected_rows($dblink) != 1 ) { $err = mysql_error($dblink); error_log ($err.\n, 3, '/tmp/errors.log'); } mysql_query('COMMIT',$dblink); ? As you can see, I tried *both* the REPLACE INTO and UPDATE queries and received *very* strange results. I sum(views) and get roughly 115 views!! I expected 100 or less, but maybe I do NOT understand 'ab'. So, I added this: error_log('foo'.\n, 3, '/tmp/errors.log'); exit; At the top of my script, and ran: $ ab -n100 -c100 localhost/hits.php Again, expecting 100 'foo's -- I get roughly 160! What the hell? I guess I really *don't* understand ab... Thoughts? -- Wellington -- MySQL General Mailing List For list archives: http://lists.mysql.com/mysql To unsubscribe:http://lists.mysql.com/[EMAIL PROTECTED]
Re: Implement a logging table; avoiding conflicting inserts
Fan, Wellington wrote: Given: MySQL 4.0.12, I need to implement a pageview log with a resolution of 1 day. If you want to brute-force it, I think I would go this route: create table hits ( day date not null primary key, hitcount int unsigned not null, ); insert ignore into hits(day, hitcount) values (current_date, 0); update hits set hitcount = hitcount + 1 where day = current_date; No transactions. Your application logic can perhaps be smart and avoid the first query. But the transactional method with locking in share mode is probably going to have a lot more overhead and lower concurrency than my suggestion. Baron -- MySQL General Mailing List For list archives: http://lists.mysql.com/mysql To unsubscribe:http://lists.mysql.com/[EMAIL PROTECTED]
Implement a logging table; avoiding conflicting inserts
Hello Listies, Given: MySQL 4.0.12, I need to implement a pageview log with a resolution of 1 day. I propose this table: CREATE TABLE `pageviews` ( `id` int(11) NOT NULL auto_increment, `date` date NOT NULL default '-00-00', `url` char(120) NOT NULL default '', `views` mediumint(9) NOT NULL default '0', PRIMARY KEY (`id`), UNIQUE KEY `date` (`date`,`url`), KEY `url` (`url`) ) TYPE=InnoDB; So that an update will look like: UPDATE pageviews SET views=views+1 WHERE date='DATE' AND url='ARTIST' Of course I need to INSERT the record if one does not match my WHERE. This would be easy if I had 4.1 -- INSERT ... ON DUPLICATE KEY UPDATE, I think -- but I do not. So, how should I write my queries so that when a new day dawns, I don't have 2 connections racing to INSERT? I suspect I could do something like this (in PHP, line numbers added: 01 $link = connect2Db(); 02 $sql = SELECT * FROM pageviews WHERE date='DATE' AND url='ARTIST' LOCK IN SHARE MODE; 03 if ( mysql_num_rows(mysql_query($sql,$link)) ) { 04 // UPDATE 05 } else { 06 // INSERT 07 } Also, should I explicitly mysql_query('COMMIT',$link) on line 8? Thanks all! -- wellington -- MySQL General Mailing List For list archives: http://lists.mysql.com/mysql To unsubscribe:http://lists.mysql.com/[EMAIL PROTECTED]