What are the main optimization techniques you use in PySpark to resolve data skew and out-of-memory (OOM) errors?