關於MySQL5.7 幾天的總結(簡單分區 & json類型)


一開始,老板讓調整一下 innodb_buffer_pool_size 大小,因為這台機器內存大。

看了下內存,16G,再SQL下面命令,得到結果是4G。

SELECT @@innodb_buffer_pool_size; 

果斷vim /etc/my.cnf 

修改了 innodb_buffer_pool_size = 8G # (adjust value here, 50%-70% of total RAM) 

括號中內容是官方給的建議。

然后再執行下面的命令,調整到8G,這樣不用重啟mysql服務,即調整完畢。

SET GLOBAL innodb_buffer_pool_size=8589934592;  -- 8G

 

其它的幾個查詢:

--  show status like 'Threads%';   -- 連接數
--  show processlist;                             -- 查看連接,可以知道當前有哪些IP連接
--  select * from information_schema.processlist order by id;  -- 查看連接

 

MySQL 5.7 安裝

最近才知道, mysql從5.7版本開始,增加了新的字段類型: json 

所以在centos6.5上裝了個5.7版本作為平時測試用.

#----------------------------------------------------------------------#
# 修改yum源為aliyun 
# 先備份:
  mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
# 下載配置文件 注意 centos版本
  wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
  yum makecache  # 生成緩存
  # yum -y update  # 升級所有包同時也升級軟件和系統內核 不是必要
#----------------------------------------------------------------------#

### --------------------------------------------------------------------###
# 安裝 mysql 5.7版本 不同於低版本的安裝,稍微復雜。 

yum list installed | grep mysql  # 檢測系統是否自帶安裝mysql
yum -y remove mysql-libs.x86_64  # 刪除系統自帶的mysql及其依賴
# 給CentOS添加rpm源,並且選擇較新的源
wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm --no-check-certificate
yum install mysql-community-release-el6-5.noarch.rpm
vim /etc/yum.repos.d/mysql-community.repo
# 編輯此文件,將MySQL 5.7下的 enabled=1  低版本的改成 enabled=0

yum repolist enabled | grep mysql    # 檢查mysql57的源是否開啟

# 安裝mysql 服務器
yum install mysql-community-server
cat /var/log/mysqld.log | grep "password" # 找到臨時密碼
# 如果沒有找到密碼,則可能使用之前本機舊版本的密碼。

service mysqld start

chkconfig mysqld on
# mysql_secure_installation    # 設置安全選項 可以不做

mysql -uroot -p  # 輸入臨時密碼
SET PASSWORD = PASSWORD('newpassword'); # 設置新密碼
USE mysql;
-- update user set password=PASSWORD('newpassword') where user='root'; -- 5.7之后已經沒有password字段
grant all PRIVILEGES on *.* to 'root'@'%' IDENTIFIED BY 'newpassword' with grant option;
FLUSH PRIVILEGES;  --更改root密碼,授權遠程連接,生效。
如果錯誤:ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
則可以修改密碼安全規則,允許簡單密碼:
set global validate_password_policy=0;
set global validate_password_length=1;
# 如果遠程連接出錯:'performance_schema.session_variables' doesn't exist # 則操作以下命令並重啟服務后,再連接 
mysql_upgrade
-u root -p --force service mysqld restart

# 如果要修改數據庫實際存儲位置:則一定要在 /etc/my.cnf中指明以下兩處路徑
datadir=/db/mysql
socket=/db/mysql/mysql.sock

 CentOS 7,如果遠程連不上,可能需要防火牆開啟  3306端口。

firewall-cmd --permanent --add-port=3306/tcp  # 添加3306
systemctl restart firewalld.service    # 重啟服務
firewall-cmd --list-all      # 查看結果 

此外my.cnf 文件中最好在一開始就加 sql_mode  和  innodb_file_per_table=1

sql_mode是個很容易被忽視的變量,默認值是空值,在這種設置下是可以允許一些非法操作的,比如允許一些非法數據的插入。在生產環境必須將這個值設置為嚴格模式,所以開發、測試環境的數據庫也必須要設置,這樣在開發測試階段就可以發現問題。具體可參考: https://www.cnblogs.com/grimm/p/5752259.html

重要:innodb_file_per_table=1 是使用獨立表空間,從而避免共享表空間的 ibdata1文件過大問題,避免后續體積太大時引起麻煩。

[mysqld]
sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
innodb_file_per_table=1 # innodb 使用獨立的表空間, 避免datadir下 ibdata1 單文件過大

命令行操作備份與恢復,如果能熟練使用,也是方便實用的功能。

詳細可參考: https://www.cnblogs.com/linuxk/p/9371475.html

mysqldump -uroot -p123456 -q --all-databases > /opt/all.sql # 備份所有庫
mysqldump -uroot -p123456 --databases db1 db2 db3 --add-drop-table |gzip > dbs.gz  # 備份指定庫
mysql -uroot -p123456 < /opt/all.sql   # 從文件恢復

 

以下示例是一個已經使用了很久的mariadb,未解決之前ibdata1單文件16G大小,解決后,變成每表一個ibd文件,合計11.6G大小。

# 1. 備份全部庫
mysqldump -uroot -p123456 --all-databases --add-drop-table > /opt/all.sql

# 2. vim /etc/my.cnf --[mysqld]下增加一行: 
innodb_file_per_table=1 # 3. 重啟服務驗證開啟 innodb_file_per_table
systemctl restart mariadb

mysql -uroot -p123456 
show variables like '%per_table%';  -- 得到 ON 即開啟成功
 
         
# 4. 刪除 ibdata1
cd /var/lib/mysql
rm -rf ib_logfile*
rm -rf ibdata1

# 5. 重啟服務並導入數據
systemctl restart mariadb
mysql -uroot -p123456 < /opt/all.sql

# 完成后還可以再重啟一次服務,並檢查數據是否正常。
# MariaDB是MySQL的繼續免費開源的分支。而mysql被oracle收購后已經商業化。所以,CentOS7,大家都使用 MariaDB

 

簡單的 range 分區

分區的范圍根據真實數據表來定,不必范圍過小,因為變成分區表會增加數據文件大小,增加磁盤占用。

必要時,也可以刪除分區重建分區。

-- 檢查 分區 是否開啟  找到 partition 是否 ACTIVE
-- SELECT PLUGIN_NAME AS NAME, PLUGIN_VERSION AS VERSION, PLUGIN_STATUS AS STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_TYPE='STORAGE ENGINE';

-- 分區名和行數 -- SELECT PARTITION_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'access_log'; -- 檢查分區狀態 -- ALTER TABLE history_hourly CHECK PARTITION all; -- 查看分區信息
-- select * from information_schema.partitions where TABLE_SCHEMA='mon_db' and table_name ='history' -- select * from information_schema.partitions where TABLE_SCHEMA='mon_db' and table_name ='alarm' -- 修改普通表為分區表 ( range 按月分區,分區條件要含蓋已存在的所有數據 )
-- ALTER TABLE history_10min PARTITION by RANGE(to_days(time)) ( -- PARTITION p201903 VALUES LESS THAN (to_days('20190401')), -- PARTITION p201904 VALUES LESS THAN (to_days('20190501')), -- PARTITION p201905 VALUES LESS THAN (to_days('20190601')), -- PARTITION p201906 VALUES LESS THAN (to_days('20190701')), -- PARTITION p201907 VALUES LESS THAN (to_days('20190801')) -- ) -- 為原有分區表追加分區 ( 注意LESS THAN()中的類型 )
-- ALTER TABLE history_hourly ADD PARTITION (PARTITION p201907 VALUES LESS THAN (to_days('20190801')))  -- PARTITION_EXPRESSION 是 to_days(time) -- ALTER TABLE history_minutely ADD PARTITION (PARTITION p201905 VALUES LESS THAN ('20190601'))      -- PARTITION_EXPRESSION 是 'time' -- 刪除表的所有分區: #不會丟失數據,變成普通表 -- ALTER TABLE history_10min REMOVE PARTITIONING -- 刪除指定分區( 所在分區數據也徹底刪除 )
-- alter table history_10min drop partition p201901; -- alter table history_daily drop partition p201908;

 

例:用node.js腳本 結合 crontab ,每月1號1點追加表分區

var mysql = require('mysql');
var moment = require('moment');
var config = require('./config');

var mysqlClient = mysql.createConnection(config.mysql);
mysqlClient.connect(function(error) { if (error) return; });

var partition_name = "p" + moment().add(1, 'M').format("YYYYMM");
var partition_date = moment().add(2, 'M').format("YYYYMM01");

var sql = "ALTER TABLE history_minutely ADD PARTITION (PARTITION " + partition_name + " VALUES LESS THAN ('" + partition_date + "'))";
mysqlClient.query(sql, function (error, results, fields) { if (error) {console.log("history_minutely:", error);} });

sql = "ALTER TABLE history_10min ADD PARTITION (PARTITION " + partition_name + " VALUES LESS THAN (TO_DAYS('" + partition_date + "')))";
mysqlClient.query(sql, function (error, results, fields) { if (error) {console.log("history_10min:", error);} });

sql = "ALTER TABLE history_hourly ADD PARTITION (PARTITION " + partition_name + " VALUES LESS THAN (TO_DAYS('" + partition_date + "')))";
mysqlClient.query(sql, function (error, results, fields) { if (error) {console.log("history_hourly:", error);} });

sql = "ALTER TABLE history_daily ADD PARTITION (PARTITION " + partition_name + " VALUES LESS THAN (TO_DAYS('" + partition_date + "')))";
mysqlClient.query(sql, function (error, results, fields) { if (error) {console.log("history_daily:", error);} });

mysqlClient.end();

crontab:

0 1 1 * * /opt/node/bin/node /opt/apps/partition/per_month.js >> /opt/logs/per_month.log

 

 

 

數據表的 JSON 字段

設計表的時候, 字段類型直接選json  就像平常選varchar一樣.

插入數據的時候, 需要轉成JSON_OBJECT 。 而JSON_ARRAY 用來將多個值存成數組。 如下面的示例:

SELECT mac, JSON_OBJECT('e1',JSON_ARRAY (round( AVG( value ->> '$.e1' ), 3 ),MIN( value ->> '$.e1' ),MAX( value ->> '$.e1' )),
'e2',JSON_ARRAY (round( AVG( value ->> '$.e2' ), 3 ),MIN( value ->> '$.e2' ),MAX( value ->> '$.e2'))) as json
FROM history WHERE mac ='522099e6660004' and time between '2018-08-07 00:00:00' AND '2018-08-08 00:00:00' GROUP BY mac

value是表中的字段名,e1、e2是此字段中json的key,  AVG/MIN/MAX不用多說了,大家都知道。 此句的作用是以某mac字段和時間為限定,分別取出e1的avg/min/max,然后賦給一個新的數組 “e1", 取出e2的avg/min/max,然后賦給一個新的數組 “e2",然后組成json對象,得到下面的結果。

{"e1": [25.568, 12, 121], "e2": [28.631, 12, 182]}

 

 取值的時候,取json里面的鍵,可以直接用SQL語句,像下面這么寫:

value -> '$.e1'     表示取出json型字段名為value中的 e1 健的值. 即使取出的數字,也帶有雙引號
value ->> '$.e1' 則取出數字不帶有雙引號, 但此時仍然不是數值類型. JSON_UNQUOTE(json_extract(json,'$.attr')) 也可以去掉引號.
value ->> '$.e1'+0 則會強制將取出的字符型數字轉為數值類型. CAST('123' AS SIGNED) 或 CONVERT('123',SIGNED)的函數也行,但是執行速度沒有直接 +0 快. (必須看起來的確是數字)

 

更新json字段時,使用JSON_REPLACE(字段,原key, 新值 ) 函數。

-- 將json字段中key為e31的數組,第0個和第1個交換,如果第0小於第1的話:
update `history_daily` set json = JSON_REPLACE(json,"$.e31[0]",round(json ->>"$.e31[1]",2), "$.e31[1]",round(json ->>"$.e31[0]",2)) where json ->> "$.e31[0]"*1 < json ->> "$.e31[1]" *1;

-- 將這個數組中第0位的小數大於3位的,只保留2位
update `history_daily` set json = JSON_REPLACE(json,"$.e31[0]",round(json ->>"$.e31[0]",2)) where LOCATE('.', json ->> "$.e31[0]") > 0 and LENGTH(SUBSTRING_INDEX(json ->> "$.e31[0]",'.' ,-1)) >3;

 

 

 python2.7 腳本,測試mysql

如果跑py腳本,沒有安裝環境,則依次執行以下命令。

或者參考更詳細的python升級2.7    https://www.cnblogs.com/frx9527/p/python27.html

yum install gcc gcc-c++ -y 

yum -y install mysql-devel 
 
yum install python-devel 
 
pip install --upgrade pip

pip install MySQL-python -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

 

以下腳本運行在python2.7   (python3以上執行命令 pip install mysql 也可以得到 MySQLdb 庫,我沒有測試)

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import MySQLdb, time, datetime, json
print(datetime.datetime.now())
HOST = 'node-6'

db = MySQLdb.connect(HOST, "root", "123456", "monitor_db")
cursor = db.cursor()


def get_date(str_date):
    t = time.strptime(str_date, '%Y-%m-%d')
    y, m, d = t[0:3]
    return datetime.datetime(y, m, d)


def insert_device_data(cursor, data):
    sql = "INSERT INTO history_daily (mac, time, json) VALUES (%s, %s, %s)"
    # print sql
    cursor.executemany(sql, data)


def fetch_device_minutely_data(cursor, start, end, mac):
    try:
        sql = '''
                SELECT
                    mac,
                    CASE version %s 
                    END json 
                FROM
                    history 
                WHERE
                    mac ='%s' 
                    and time between '%s' AND '%s' 
                GROUP BY
                    mac,
                    version
            ''' % (jsonkey, mac, start, end)
        # print(sql)   # 壯觀的 case ... when

        cursor.execute(sql)
        data = cursor.fetchall()
        if data:
            for d in data:
                mac = d[0]
                json = d[1]
                device_data = []
                device_data.append((mac, start, json))
                insert_device_data(cursor, tuple(device_data))
    except MySQLdb.OperationalError, e:
        print(e)
        time.sleep(10)
        db = MySQLdb.connect(HOST, "root", "123456", "monitor_db")
        cursor = db.cursor()
        fetch_device_minutely_data(cursor, start, end, mac)


# 設備表 id , version
version_sql = 'SELECT id, version FROM device_version GROUP BY id, version'
cursor.execute(version_sql)
versions = cursor.fetchall()  # version 從2到10

jsonkey = ""
if versions:
    for version in versions:
        print (version[0])
        sensor_sql = '''
                SELECT s.sensor_key FROM device_version_sensor dvs,sensor s WHERE dvs.sensor_id = s.id AND dvs.device_version_id = '%d' 
            ''' % (version[0])  # e1  e2  e3  ...
        cursor.execute(sensor_sql)
        keys = cursor.fetchall()
        if keys:
            jsonkey += "WHEN " + str(int(version[1])) + " THEN JSON_OBJECT("
            for i, sensorKey in enumerate(keys):
                key = sensorKey[0]
                jsonkey += "'" + str(key) + "',JSON_ARRAY (round( AVG( value ->> '$." + str(
                    key) + "'+0 ), 3 ),MIN( value ->> '$." + str(
                    key) + "'+0 ), MAX( value ->> '$." + str(key) + "'+0 ))"
                if i != len(keys) - 1:
                    jsonkey += ","
            jsonkey += ")"


# mac 列表
sql_mac = 'SELECT DISTINCT mac FROM `history` where mac is not NULL';

cursor.execute(sql_mac)
mac_tuple = cursor.fetchall()  # version 從2到10

start_date = get_date((datetime.datetime.now() + datetime.timedelta(days=-6)).strftime("%Y-%m-%d"))
end_date = get_date((datetime.datetime.now()).strftime("%Y-%m-%d"))

days = (end_date - start_date).days

for i in range(days):
    start_day = start_date + datetime.timedelta(days=i)
    end_day = start_date + datetime.timedelta(days=i + 1)

    for mac in mac_tuple:
        # print (mac[0])
        fetch_device_minutely_data(cursor, start_day, end_day, mac[0])
        db.commit()

db.close()

print(datetime.datetime.now())

 

 

MySQL 一個簡單的定時任務 

SQL語句創建簡單的定時任務,每天凌晨執行前一天的數據匯總,求取表中每個城市的最大值。

對應地將json中的quality值由漢字轉換為數字,先存入臨時表,最后求取個最大值存放到 quality_daily 表中。如果SQL搞定沒問題,就沒必要麻煩python了。
  定義:

begin
-- 取出所有 quality
INSERT INTO quality_tmp ( time, quality, city_code ) SELECT
DATE_FORMAT( time, "%Y-%m-%d" ) AS time,
IF
    (
        aqi_json ->> "$.quality" = "優",
        1,
    IF
        (
            aqi_json ->> "$.quality" = "良",
            2,
        IF
            (
                aqi_json ->> "$.quality" = "輕度污染",
                3,
            IF
                ( aqi_json ->> "$.quality" = "中度污染", 
                4, 
                IF 
                ( aqi_json ->> "$.quality" = "重度污染",
                5,
                    IF(aqi_json ->> "$.quality" = "嚴重污染", 6, 0)) ) 
            ) 
        ) 
    ) AS quality,
    city_code 
FROM
    `hangzhou_aqi` 
WHERE
    time >= date_sub( curdate( ), INTERVAL 1 DAY ) 
    AND time < curdate( );
-- 求出最大值
INSERT into quality_daily(time,quality,city_code) 
SELECT time, max(quality), city_code from quality_tmp GROUP BY time,city_code ;
-- 清空臨時表
delete from quality_tmp;
end

 設定 計划 為每一天。

 

 

別忘了開啟計划任務功能:

-- show variables like '%sche%';

set global event_scheduler =1 ;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM